package main import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/google/uuid" "github.com/stretchr/testify/assert" ) // TestLibraryManagementEndpoints tests library management operations func TestLibraryManagementEndpoints(t *testing.T) { libraryID := uuid.New() t.Run("POST /api/libraries - Create library without admin role", func(t *testing.T) { payload := map[string]interface{}{ "name": "New Library", "description": "Test description", "type": "ebooks", } jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/api/libraries", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer user-token") req.Header.Set("X-User-Role", "user") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userRole := r.Header.Get("X-User-Role") if userRole != "admin" { w.WriteHeader(http.StatusForbidden) w.Write([]byte(`{"error":"admin access required"}`)) return } w.WriteHeader(http.StatusCreated) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusForbidden, rr.Code) }) t.Run("POST /api/libraries - Create library with invalid type", func(t *testing.T) { payload := map[string]interface{}{ "name": "New Library", "description": "Test description", "type": "invalid-type", } jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/api/libraries", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer admin-token") req.Header.Set("X-User-Role", "admin") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&req); err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"invalid request"}`)) return } libType := req["type"].(string) if libType != "ebooks" && libType != "comics" && libType != "manga" { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"invalid library type"}`)) return } w.WriteHeader(http.StatusCreated) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusBadRequest, rr.Code) }) t.Run("POST /api/libraries - Create library with missing required fields", func(t *testing.T) { payload := map[string]interface{}{ "description": "Test description", } jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/api/libraries", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer admin-token") req.Header.Set("X-User-Role", "admin") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&req); err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"invalid request"}`)) return } if _, ok := req["name"]; !ok { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"name is required"}`)) return } if _, ok := req["type"]; !ok { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"type is required"}`)) return } w.WriteHeader(http.StatusCreated) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusBadRequest, rr.Code) }) t.Run("GET /api/libraries/:id - Get library with invalid UUID", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/libraries/invalid-uuid", nil) req.Header.Set("Authorization", "Bearer admin-token") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"invalid library id"}`)) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusBadRequest, rr.Code) }) t.Run("GET /api/libraries/:id - Get non-existent library", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/libraries/"+uuid.New().String(), nil) req.Header.Set("Authorization", "Bearer admin-token") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) w.Write([]byte(`{"error":"library not found"}`)) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusNotFound, rr.Code) }) t.Run("PUT /api/libraries/:id - Update library without admin role", func(t *testing.T) { payload := map[string]interface{}{ "name": "Updated Library", "description": "Updated description", } jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("PUT", "/api/libraries/"+libraryID.String(), bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer user-token") req.Header.Set("X-User-Role", "user") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userRole := r.Header.Get("X-User-Role") if userRole != "admin" { w.WriteHeader(http.StatusForbidden) w.Write([]byte(`{"error":"admin access required"}`)) return } w.WriteHeader(http.StatusOK) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusForbidden, rr.Code) }) t.Run("DELETE /api/libraries/:id - Delete library without admin role", func(t *testing.T) { req := httptest.NewRequest("DELETE", "/api/libraries/"+libraryID.String(), nil) req.Header.Set("Authorization", "Bearer user-token") req.Header.Set("X-User-Role", "user") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userRole := r.Header.Get("X-User-Role") if userRole != "admin" { w.WriteHeader(http.StatusForbidden) w.Write([]byte(`{"error":"admin access required"}`)) return } w.WriteHeader(http.StatusNoContent) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusForbidden, rr.Code) }) t.Run("DELETE /api/libraries/:id - Delete library with invalid UUID", func(t *testing.T) { req := httptest.NewRequest("DELETE", "/api/libraries/invalid-uuid", nil) req.Header.Set("Authorization", "Bearer admin-token") req.Header.Set("X-User-Role", "admin") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"invalid library id"}`)) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusBadRequest, rr.Code) }) } // TestLibraryFolders tests library folder management func TestLibraryFolders(t *testing.T) { libraryID := uuid.New() t.Run("POST /api/libraries/:id/folders - Add folder without admin role", func(t *testing.T) { payload := map[string]interface{}{ "folder_path": "/path/to/folder", } jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/api/libraries/"+libraryID.String()+"/folders", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer user-token") req.Header.Set("X-User-Role", "user") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userRole := r.Header.Get("X-User-Role") if userRole != "admin" { w.WriteHeader(http.StatusForbidden) w.Write([]byte(`{"error":"admin access required"}`)) return } w.WriteHeader(http.StatusCreated) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusForbidden, rr.Code) }) t.Run("POST /api/libraries/:id/folders - Add folder with invalid library ID", func(t *testing.T) { payload := map[string]interface{}{ "folder_path": "/path/to/folder", } jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/api/libraries/invalid-uuid/folders", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer admin-token") req.Header.Set("X-User-Role", "admin") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"invalid library id"}`)) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusBadRequest, rr.Code) }) t.Run("POST /api/libraries/:id/folders - Add folder with missing path", func(t *testing.T) { payload := map[string]interface{}{} jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/api/libraries/"+libraryID.String()+"/folders", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer admin-token") req.Header.Set("X-User-Role", "admin") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&req); err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"invalid request"}`)) return } if _, ok := req["folder_path"]; !ok { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"folder_path is required"}`)) return } w.WriteHeader(http.StatusCreated) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusBadRequest, rr.Code) }) t.Run("GET /api/libraries/:id/folders - Get folders without admin role", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/libraries/"+libraryID.String()+"/folders", nil) req.Header.Set("Authorization", "Bearer user-token") req.Header.Set("X-User-Role", "user") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userRole := r.Header.Get("X-User-Role") if userRole != "admin" { w.WriteHeader(http.StatusForbidden) w.Write([]byte(`{"error":"admin access required"}`)) return } w.WriteHeader(http.StatusOK) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusForbidden, rr.Code) }) t.Run("DELETE /api/libraries/:id/folders - Delete folder without admin role", func(t *testing.T) { payload := map[string]interface{}{ "folder_path": "/path/to/folder", } jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("DELETE", "/api/libraries/"+libraryID.String()+"/folders", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer user-token") req.Header.Set("X-User-Role", "user") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userRole := r.Header.Get("X-User-Role") if userRole != "admin" { w.WriteHeader(http.StatusForbidden) w.Write([]byte(`{"error":"admin access required"}`)) return } w.WriteHeader(http.StatusNoContent) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusForbidden, rr.Code) }) } // TestLibraryVisibility tests library visibility controls func TestLibraryVisibility(t *testing.T) { libraryID := uuid.New() userID := uuid.New() t.Run("POST /api/libraries/visibility - Set visibility without auth", func(t *testing.T) { payload := map[string]interface{}{ "library_id": libraryID.String(), "is_visible": true, } jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/api/libraries/visibility", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") if authHeader == "" { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(`{"message":"missing or malformed jwt"}`)) return } w.WriteHeader(http.StatusOK) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusUnauthorized, rr.Code) }) t.Run("POST /api/libraries/visibility - Set visibility with invalid library ID", func(t *testing.T) { payload := map[string]interface{}{ "library_id": "invalid-uuid", "is_visible": true, } jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/api/libraries/visibility", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer valid-token") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&req); err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"invalid request"}`)) return } libID := req["library_id"].(string) if _, err := uuid.Parse(libID); err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"invalid library id"}`)) return } w.WriteHeader(http.StatusOK) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusBadRequest, rr.Code) }) t.Run("POST /api/libraries/visibility - Set visibility successfully", func(t *testing.T) { payload := map[string]interface{}{ "library_id": libraryID.String(), "is_visible": true, } jsonData, _ := json.Marshal(payload) req := httptest.NewRequest("POST", "/api/libraries/visibility", bytes.NewBuffer(jsonData)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer valid-token") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { visibility := map[string]interface{}{ "id": uuid.New().String(), "user_id": userID.String(), "library_id": libraryID.String(), "is_visible": true, } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(visibility) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code) }) t.Run("GET /api/libraries/visible - Get visible libraries without auth", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/libraries/visible", nil) rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") if authHeader == "" { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(`{"message":"missing or malformed jwt"}`)) return } w.WriteHeader(http.StatusOK) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusUnauthorized, rr.Code) }) t.Run("GET /api/libraries/visible - Get visible libraries with auth", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/libraries/visible", nil) req.Header.Set("Authorization", "Bearer valid-token") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { libraries := []map[string]interface{}{ { "id": libraryID.String(), "name": "Visible Library", "is_visible": true, }, } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(libraries) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code) }) } // TestLibraryStats tests library statistics func TestLibraryStats(t *testing.T) { libraryID := uuid.New() t.Run("GET /api/libraries/:id/stats - Get stats without admin role", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/libraries/"+libraryID.String()+"/stats", nil) req.Header.Set("Authorization", "Bearer user-token") req.Header.Set("X-User-Role", "user") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userRole := r.Header.Get("X-User-Role") if userRole != "admin" { w.WriteHeader(http.StatusForbidden) w.Write([]byte(`{"error":"admin access required"}`)) return } w.WriteHeader(http.StatusOK) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusForbidden, rr.Code) }) t.Run("GET /api/libraries/:id/stats - Get stats with invalid library ID", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/libraries/invalid-uuid/stats", nil) req.Header.Set("Authorization", "Bearer admin-token") req.Header.Set("X-User-Role", "admin") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error":"invalid library id"}`)) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusBadRequest, rr.Code) }) t.Run("GET /api/libraries/:id/stats - Get stats successfully", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/libraries/"+libraryID.String()+"/stats", nil) req.Header.Set("Authorization", "Bearer admin-token") req.Header.Set("X-User-Role", "admin") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { stats := map[string]interface{}{ "media_count": 42, "total_size": 1024000, } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(stats) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code) }) } // TestLibraryTypes tests library type retrieval func TestLibraryTypes(t *testing.T) { t.Run("GET /api/libraries/types - Get all library types", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/libraries/types", nil) req.Header.Set("Authorization", "Bearer valid-token") rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { types := []map[string]interface{}{ { "id": uuid.New().String(), "name": "ebooks", "description": "Ebook files including EPUB, PDF, MOBI, etc.", "allowed_extensions": []string{".epub", ".pdf", ".mobi"}, }, { "id": uuid.New().String(), "name": "comics", "description": "Comic book archives and image formats", "allowed_extensions": []string{".cbz", ".cbr", ".cb7", ".cbt", ".epub", ".pdf"}, }, } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(types) }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code) assert.Contains(t, rr.Body.String(), "ebooks") assert.Contains(t, rr.Body.String(), "comics") }) }