//go:build test // +build test package user import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "crussell/db" "crussell/mw" "crussell/testutils/fixtures" "github.com/go-chi/chi/v5" ) // makePatchTestsRequest builds a request for /api/admin/users/{user_id}/patch-tests[/{test_id}]. func makePatchTestsRequest(handler http.HandlerFunc, method, path string, body interface{}, userID, role string) *httptest.ResponseRecorder { req := httptest.NewRequest(method, path, nil) prefix := "/api/admin/users/" suffix := strings.TrimPrefix(path, prefix) // "USERID/patch-tests" or "USERID/patch-tests/TESTID" parts := strings.SplitN(suffix, "/", 3) // parts[0] = user_id, parts[1] = "patch-tests", parts[2] = test_id (optional) rctx := chi.NewRouteContext() if len(parts) > 0 { rctx.URLParams.Add("user_id", parts[0]) } if len(parts) > 2 { rctx.URLParams.Add("test_id", parts[2]) } ctx := context.WithValue(req.Context(), chi.RouteCtxKey, rctx) ctx = context.WithValue(ctx, mw.UserIDKey, userID) ctx = context.WithValue(ctx, mw.UserRoleKey, role) req = req.WithContext(ctx) w := httptest.NewRecorder() handler(w, req) return w } // ============================================================================= // GetUserPatchTestsHandler Tests // ============================================================================= func TestGetUserPatchTests_Empty(t *testing.T) { resetTestData(t) userID, err := fixtures.CreateTestUser(db.DB) if err != nil { t.Fatalf("failed to create user: %v", err) } w := makePatchTestsRequest(GetUserPatchTestsHandler, "GET", "/api/admin/users/"+userID+"/patch-tests", nil, userID, "verified_email") if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d. body: %s", w.Code, w.Body.String()) } var tests []UserPatchTest if err := json.Unmarshal(w.Body.Bytes(), &tests); err != nil { t.Fatalf("failed to parse response: %v", err) } if len(tests) != 0 { t.Errorf("expected empty list, got %d items", len(tests)) } } func TestGetUserPatchTests_WithRecords(t *testing.T) { resetTestData(t) userID, err := fixtures.CreateTestUser(db.DB) if err != nil { t.Fatalf("failed to create user: %v", err) } // Create a patch test and record var patchTestID string err = db.DB.QueryRow(context.Background(), ` INSERT INTO patch_tests (name, description, expiry_months) VALUES ('Patch Test A', 'Test description', 6) RETURNING id `).Scan(&patchTestID) if err != nil { t.Fatalf("failed to create patch test: %v", err) } _, err = db.DB.Exec(context.Background(), ` INSERT INTO user_patch_tests (user_id, patch_test_id, tested_at) VALUES ($1, $2, NOW()) `, userID, patchTestID) if err != nil { t.Fatalf("failed to create user patch test: %v", err) } w := makePatchTestsRequest(GetUserPatchTestsHandler, "GET", "/api/admin/users/"+userID+"/patch-tests", nil, userID, "verified_email") if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d. body: %s", w.Code, w.Body.String()) } var tests []UserPatchTest if err := json.Unmarshal(w.Body.Bytes(), &tests); err != nil { t.Fatalf("failed to parse response: %v", err) } if len(tests) != 1 { t.Fatalf("expected 1 patch test, got %d", len(tests)) } if tests[0].PatchTestName != "Patch Test A" { t.Errorf("expected 'Patch Test A', got %q", tests[0].PatchTestName) } } func TestGetUserPatchTests_InvalidUserID(t *testing.T) { w := makePatchTestsRequest(GetUserPatchTestsHandler, "GET", "/api/admin/users/invalid/patch-tests", nil, "admin001", "admin") if w.Code != http.StatusNotFound { t.Errorf("expected 404 for invalid user ID, got %d", w.Code) } } // ============================================================================= // DeletePatchTestHandler Tests // ============================================================================= func TestDeletePatchTest_HappyPath(t *testing.T) { resetTestData(t) userID, err := fixtures.CreateTestUser(db.DB) if err != nil { t.Fatalf("failed to create user: %v", err) } var patchTestID string err = db.DB.QueryRow(context.Background(), ` INSERT INTO patch_tests (name, description, expiry_months) VALUES ('Patch Test', 'Desc', 6) RETURNING id `).Scan(&patchTestID) if err != nil { t.Fatalf("failed to create patch test: %v", err) } var userPatchTestID string err = db.DB.QueryRow(context.Background(), ` INSERT INTO user_patch_tests (user_id, patch_test_id, tested_at) VALUES ($1, $2, NOW()) RETURNING id `, userID, patchTestID).Scan(&userPatchTestID) if err != nil { t.Fatalf("failed to create user patch test: %v", err) } w := makePatchTestsRequest(DeletePatchTestHandler, "DELETE", "/api/admin/users/"+userID+"/patch-tests/"+userPatchTestID, nil, userID, "verified_email") if w.Code != http.StatusNoContent { t.Fatalf("expected 204, got %d. body: %s", w.Code, w.Body.String()) } // Verify deleted var count int err = db.DB.QueryRow(context.Background(), "SELECT COUNT(*) FROM user_patch_tests WHERE id = $1", userPatchTestID).Scan(&count) if err != nil { t.Fatalf("failed to check: %v", err) } if count != 0 { t.Errorf("expected record to be deleted, count=%d", count) } } func TestDeletePatchTest_NotFound(t *testing.T) { resetTestData(t) userID, err := fixtures.CreateTestUser(db.DB) if err != nil { t.Fatalf("failed to create user: %v", err) } w := makePatchTestsRequest(DeletePatchTestHandler, "DELETE", "/api/admin/users/"+userID+"/patch-tests/99999", nil, "admin001", "admin") if w.Code != http.StatusNotFound { t.Errorf("expected 404 for nonexistent patch test, got %d", w.Code) } } func TestDeletePatchTest_InvalidUserID(t *testing.T) { w := makePatchTestsRequest(DeletePatchTestHandler, "DELETE", "/api/admin/users/invalid/patch-tests/1", nil, "admin001", "admin") if w.Code != http.StatusNotFound { t.Errorf("expected 404 for invalid user ID, got %d", w.Code) } } func TestDeletePatchTest_InvalidTestID(t *testing.T) { w := makePatchTestsRequest(DeletePatchTestHandler, "DELETE", "/api/admin/users/validuserid/patch-tests/invalid", nil, "admin001", "admin") if w.Code != http.StatusNotFound { t.Errorf("expected 404 for invalid test ID, got %d", w.Code) } }