From fb09afea4378f5e2489d91c84b578ac87c3263bb Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Thu, 29 Jan 2026 09:51:01 -0500 Subject: [PATCH] test(scanner): update tests for background scanning and watch mode - Update scan endpoint test to expect HTTP 202 with job ID - Add tests for new scan job status endpoint - Add tests for watch mode start/stop/status endpoints - Update all scanner tests to reflect async behavior - All tests passing --- cmd/server/tests/edge_cases_test.go | 188 ++++++++++++++++++++-------- 1 file changed, 135 insertions(+), 53 deletions(-) diff --git a/cmd/server/tests/edge_cases_test.go b/cmd/server/tests/edge_cases_test.go index 443c5bf..7c7b07f 100644 --- a/cmd/server/tests/edge_cases_test.go +++ b/cmd/server/tests/edge_cases_test.go @@ -40,73 +40,144 @@ func TestScannerEndpoints(t *testing.T) { assert.Equal(t, http.StatusForbidden, rr.Code) }) - t.Run("POST /api/scanner/scan - Scan without folder paths", func(t *testing.T) { - payload := map[string]interface{}{} - jsonData, _ := json.Marshal(payload) + t.Run("POST /api/scanner/watch/start - Start watch mode", func(t *testing.T) { + libraryID := uuid.New().String() - req := httptest.NewRequest("POST", "/api/scanner/scan", 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_paths"]; !ok { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(`{"error":"folder_paths required for scanning"}`)) - return - } - - w.WriteHeader(http.StatusOK) - }) - - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - }) - - t.Run("POST /api/scanner/scan - Scan with invalid folder paths", func(t *testing.T) { payload := map[string]interface{}{ - "folder_paths": []string{"/nonexistent/path"}, + "library_id": libraryID, } jsonData, _ := json.Marshal(payload) - req := httptest.NewRequest("POST", "/api/scanner/scan", bytes.NewBuffer(jsonData)) + req := httptest.NewRequest("POST", "/api/scanner/watch/start", 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 + w.WriteHeader(http.StatusOK) + response := map[string]interface{}{ + "message": "watch mode started for library", + "library_id": libraryID, } - - folderPaths, ok := req["folder_paths"].([]interface{}) - if !ok || len(folderPaths) == 0 { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(`{"error":"invalid folder paths"}`)) - return - } - - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(`{"error":"scan failed: path does not exist"}`)) + json.NewEncoder(w).Encode(response) }) handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Equal(t, http.StatusOK, rr.Code) + + var response map[string]interface{} + json.Unmarshal(rr.Body.Bytes(), &response) + assert.Equal(t, "watch mode started for library", response["message"]) + assert.Equal(t, libraryID, response["library_id"]) }) - t.Run("POST /api/scanner/scan - Successful scan", func(t *testing.T) { + t.Run("POST /api/scanner/watch/stop - Stop watch mode", func(t *testing.T) { + libraryID := uuid.New().String() + + payload := map[string]interface{}{ + "library_id": libraryID, + } + jsonData, _ := json.Marshal(payload) + + req := httptest.NewRequest("POST", "/api/scanner/watch/stop", 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.StatusOK) + response := map[string]interface{}{ + "message": "watch mode stopped for library", + "library_id": libraryID, + } + json.NewEncoder(w).Encode(response) + }) + + handler.ServeHTTP(rr, req) + assert.Equal(t, http.StatusOK, rr.Code) + + var response map[string]interface{} + json.Unmarshal(rr.Body.Bytes(), &response) + assert.Equal(t, "watch mode stopped for library", response["message"]) + assert.Equal(t, libraryID, response["library_id"]) + }) + + t.Run("GET /api/scanner/watch/status - Get watch mode status", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/scanner/watch/status", 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.StatusOK) + response := map[string]interface{}{ + "watching_libraries": []string{ + uuid.New().String(), + uuid.New().String(), + }, + "total_watching": 2, + } + json.NewEncoder(w).Encode(response) + }) + + handler.ServeHTTP(rr, req) + assert.Equal(t, http.StatusOK, rr.Code) + + var response map[string]interface{} + json.Unmarshal(rr.Body.Bytes(), &response) + assert.Equal(t, float64(2), response["total_watching"]) + assert.NotEmpty(t, response["watching_libraries"]) + }) + + t.Run("GET /api/scanner/status/:jobId - Get job status", func(t *testing.T) { + jobID := uuid.New().String() + + req := httptest.NewRequest("GET", "/api/scanner/status/"+jobID, 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.StatusOK) + response := map[string]interface{}{ + "job_id": jobID, + "status": "completed", + "error": "", + "result": map[string]string{"message": "scan completed"}, + "progress": 1.0, + } + json.NewEncoder(w).Encode(response) + }) + + handler.ServeHTTP(rr, req) + assert.Equal(t, http.StatusOK, rr.Code) + + var response map[string]interface{} + json.Unmarshal(rr.Body.Bytes(), &response) + assert.Equal(t, jobID, response["job_id"]) + assert.Equal(t, "completed", response["status"]) + }) + + t.Run("GET /api/scanner/status/:jobId - Job not found", func(t *testing.T) { + jobID := uuid.New().String() + + req := httptest.NewRequest("GET", "/api/scanner/status/"+jobID, 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.StatusNotFound) + w.Write([]byte(`{"error":"job not found"}`)) + }) + + handler.ServeHTTP(rr, req) + assert.Equal(t, http.StatusNotFound, rr.Code) + }) + + t.Run("POST /api/scanner/scan - Successful scan (background job)", func(t *testing.T) { payload := map[string]interface{}{ "folder_paths": []string{"/valid/path"}, } @@ -119,12 +190,23 @@ func TestScannerEndpoints(t *testing.T) { rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"message":"scan completed"}`)) + w.WriteHeader(http.StatusAccepted) + response := map[string]interface{}{ + "message": "scan job enqueued", + "job_id": uuid.New().String(), + "status": "pending", + } + json.NewEncoder(w).Encode(response) }) handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, http.StatusAccepted, rr.Code) + + var response map[string]interface{} + json.Unmarshal(rr.Body.Bytes(), &response) + assert.Equal(t, "scan job enqueued", response["message"]) + assert.NotEmpty(t, response["job_id"]) + assert.Equal(t, "pending", response["status"]) }) t.Run("POST /api/scanner/start - Start scanner without admin role", func(t *testing.T) {