From 891209b4bdf1307eac8035f0613f6dbd02ec7073 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Wed, 11 Feb 2026 10:37:38 -0500 Subject: [PATCH] test(infrastructure): Configure pgxpool with max_conns=1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix database connection exhaustion in tests by setting max_conns=1 when creating pgxpool via pgxpool.ParseConfig(). - Update setupTestServer() in test_helpers.go - Update setupSyncTestDB() in sync_integration_test.go This reduces per-test connection usage from 4 to 1, keeping total connections well under PostgreSQL's default max_connections=100. 78 tests × 1 connection = 78 connections (down from 312 potential) Fixes test failures: "FATAL: sorry, too many clients already" See PROJECT_GUIDELINES.md Testing section for details. --- PROJECT_GUIDELINES.md | 16 ++- cmd/server/tests/analytics_test.go | 121 ++++++---------------- cmd/server/tests/book_matching_test.go | 110 ++++---------------- cmd/server/tests/collections_bulk_test.go | 48 +-------- cmd/server/tests/conflicts_bulk_test.go | 88 +++------------- cmd/server/tests/media_bulk_test.go | 53 +--------- cmd/server/tests/opds_test.go | 100 +++--------------- cmd/server/tests/refresh_token_test.go | 53 +++------- cmd/server/tests/sync_integration_test.go | 6 +- cmd/server/tests/test_helpers.go | 7 +- 10 files changed, 124 insertions(+), 478 deletions(-) diff --git a/PROJECT_GUIDELINES.md b/PROJECT_GUIDELINES.md index 5c9d6ae..41006b4 100644 --- a/PROJECT_GUIDELINES.md +++ b/PROJECT_GUIDELINES.md @@ -9,9 +9,19 @@ - ❌ **NEVER build server binaries locally** - all builds through Dockerfile/docker-compose - ❌ **NEVER create new migration files** - merge changes into current one until release - ❌ **NEVER use `git checkout` on schema files** without checking what will be lost -- ❌ **NEVER break existing functionality** unless explicitly instructed -- ❌ **NEVER duplicate business logic** - keep logic in services, not handlers -- ❌ **NEVER bypass service layer** - all database operations must go through services + - ❌ **NEVER break existing functionality** unless explicitly instructed + - ❌ **NEVER duplicate business logic** - keep logic in services, not handlers + - ❌ **NEVER bypass service layer** - all database operations must go through services + +### Testing +- ✅ **ALWAYS use `setupTestServer()` helper from `cmd/server/tests/test_helpers.go`** +- ✅ **Share one test setup across all subtests** - call `setupTestServer()` once at test function level, not per subtest +- ✅ **Prefer table-driven tests** - use `t.Run()` with test cases instead of duplicate test functions +- ✅ **Configure database pools efficiently** - use `max_conns=1` for test pools (via `pgxpool.ParseConfig()`) to prevent connection exhaustion +- ❌ **NEVER create separate `pgxpool` per test** - each pool creates 4 connections by default; 78 tests = 312 potential connections > PostgreSQL's 100 limit +- ❌ **NEVER call `setupTestServer()` in loops or within subtests** - creates unnecessary database pools and exhausts connections +- ✅ **DO verify tests pass** - run full test suite before completing work +- ✅ **Use `t.Cleanup()` properly** - the `TestServerSetup` pattern automatically handles cleanup via `t.Cleanup()` ### Frontend & Styling - ❌ **NEVER modify backend/API for frontend features without user confirmation** diff --git a/cmd/server/tests/analytics_test.go b/cmd/server/tests/analytics_test.go index ca54896..fc07260 100644 --- a/cmd/server/tests/analytics_test.go +++ b/cmd/server/tests/analytics_test.go @@ -13,11 +13,12 @@ import ( // TestAnalyticsReadingStats tests the reading statistics endpoint func TestAnalyticsReadingStats(t *testing.T) { - t.Run("GetReadingStats_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} + t.Run("GetReadingStats_WithoutAuth", func(t *testing.T) { req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats", nil) - client := &http.Client{} resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -26,13 +27,9 @@ func TestAnalyticsReadingStats(t *testing.T) { }) t.Run("GetReadingStats_WithAuth_DefaultDates", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -51,16 +48,12 @@ func TestAnalyticsReadingStats(t *testing.T) { }) t.Run("GetReadingStats_WithCustomDateRange", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - startDate := time.Now().AddDate(0, -2, 0).Format("2006-01-02") endDate := time.Now().Format("2006-01-02") req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats?start_date="+startDate+"&end_date="+endDate, nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -69,13 +62,9 @@ func TestAnalyticsReadingStats(t *testing.T) { }) t.Run("GetReadingStats_InvalidStartDate", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats?start_date=invalid-date", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -84,13 +73,9 @@ func TestAnalyticsReadingStats(t *testing.T) { }) t.Run("GetReadingStats_InvalidEndDate", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats?end_date=not-a-date", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -99,13 +84,9 @@ func TestAnalyticsReadingStats(t *testing.T) { }) t.Run("GetReadingStats_EmptyHistory", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -123,11 +104,12 @@ func TestAnalyticsReadingStats(t *testing.T) { // TestAnalyticsDeviceUsage tests the device usage endpoint func TestAnalyticsDeviceUsage(t *testing.T) { - t.Run("GetDeviceUsage_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} + t.Run("GetDeviceUsage_WithoutAuth", func(t *testing.T) { req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/device-usage", nil) - client := &http.Client{} resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -136,13 +118,9 @@ func TestAnalyticsDeviceUsage(t *testing.T) { }) t.Run("GetDeviceUsage_WithAuth_NoDevices", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/device-usage", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -158,10 +136,6 @@ func TestAnalyticsDeviceUsage(t *testing.T) { }) t.Run("GetDeviceUsage_WithAuth_WithDevices", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // First create a device deviceReq := map[string]interface{}{ "device_name": "Test Kobo", @@ -173,7 +147,6 @@ func TestAnalyticsDeviceUsage(t *testing.T) { deviceReqHTTP.Header.Set("Content-Type", "application/json") deviceReqHTTP.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(deviceReqHTTP) require.NoError(t, err) resp.Body.Close() @@ -181,6 +154,7 @@ func TestAnalyticsDeviceUsage(t *testing.T) { // Now get device usage req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/device-usage", nil) req.Header.Set("Authorization", "Bearer "+token) + resp, err = client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -197,13 +171,9 @@ func TestAnalyticsDeviceUsage(t *testing.T) { }) t.Run("GetDeviceUsage_ResponseStructure", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/device-usage", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -231,11 +201,12 @@ func TestAnalyticsDeviceUsage(t *testing.T) { // TestAnalyticsPopularBooks tests the popular books endpoint func TestAnalyticsPopularBooks(t *testing.T) { - t.Run("GetPopularBooks_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} + t.Run("GetPopularBooks_WithoutAuth", func(t *testing.T) { req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books", nil) - client := &http.Client{} resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -244,13 +215,9 @@ func TestAnalyticsPopularBooks(t *testing.T) { }) t.Run("GetPopularBooks_WithAuth_DefaultLimit", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -268,13 +235,9 @@ func TestAnalyticsPopularBooks(t *testing.T) { }) t.Run("GetPopularBooks_WithCustomLimit", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books?limit=5", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -289,13 +252,9 @@ func TestAnalyticsPopularBooks(t *testing.T) { }) t.Run("GetPopularBooks_InvalidLimit", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books?limit=invalid", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -311,10 +270,6 @@ func TestAnalyticsPopularBooks(t *testing.T) { }) t.Run("GetPopularBooks_ResponseStructure", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // First create a book and some reading history bookID := createTestMediaItemID(t, setup.Server, token) @@ -331,7 +286,6 @@ func TestAnalyticsPopularBooks(t *testing.T) { historyHTTP.Header.Set("Content-Type", "application/json") historyHTTP.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(historyHTTP) require.NoError(t, err) resp.Body.Close() @@ -339,6 +293,7 @@ func TestAnalyticsPopularBooks(t *testing.T) { // Now get popular books req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books", nil) req.Header.Set("Authorization", "Bearer "+token) + resp, err = client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -363,13 +318,9 @@ func TestAnalyticsPopularBooks(t *testing.T) { }) t.Run("GetPopularBooks_NoReadingHistory", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -387,17 +338,17 @@ func TestAnalyticsPopularBooks(t *testing.T) { // TestAnalyticsEdgeCases tests edge cases for analytics endpoints func TestAnalyticsEdgeCases(t *testing.T) { + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} + t.Run("ReadingStats_FutureDateRange", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - startDate := time.Now().AddDate(0, 0, 7).Format("2006-01-02") endDate := time.Now().AddDate(0, 0, 14).Format("2006-01-02") req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats?start_date="+startDate+"&end_date="+endDate, nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -412,13 +363,9 @@ func TestAnalyticsEdgeCases(t *testing.T) { }) t.Run("PopularBooks_LimitZero", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books?limit=0", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -434,13 +381,9 @@ func TestAnalyticsEdgeCases(t *testing.T) { }) t.Run("PopularBooks_VeryLargeLimit", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books?limit=999999", nil) req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() diff --git a/cmd/server/tests/book_matching_test.go b/cmd/server/tests/book_matching_test.go index 07946e2..3660255 100644 --- a/cmd/server/tests/book_matching_test.go +++ b/cmd/server/tests/book_matching_test.go @@ -13,9 +13,10 @@ import ( // TestBookMatchingQueryBooks tests the book query endpoint func TestBookMatchingQueryBooks(t *testing.T) { - t.Run("QueryBooks_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + t.Run("QueryBooks_WithoutAuth", func(t *testing.T) { req := map[string]interface{}{ "title": "Test Book", } @@ -33,9 +34,6 @@ func TestBookMatchingQueryBooks(t *testing.T) { }) t.Run("QueryBooks_WithAuth_ByTitle", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) _ = createTestMediaItemID(t, setup.Server, token) req := map[string]interface{}{ @@ -62,10 +60,6 @@ func TestBookMatchingQueryBooks(t *testing.T) { }) t.Run("QueryBooks_InvalidRequestBody", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Send invalid JSON httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/sync/books/query", bytes.NewBuffer([]byte("invalid json"))) httpReq.Header.Set("Content-Type", "application/json") @@ -80,10 +74,6 @@ func TestBookMatchingQueryBooks(t *testing.T) { }) t.Run("QueryBooks_NoResults", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "title": "NonExistentBookTitleThatDoesNotExist123456789", } @@ -115,9 +105,10 @@ func TestBookMatchingQueryBooks(t *testing.T) { // TestBookMatchingBulkLink tests bulk linking operations func TestBookMatchingBulkLink(t *testing.T) { - t.Run("BulkLinkBooks_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + t.Run("BulkLinkBooks_WithoutAuth", func(t *testing.T) { req := map[string]interface{}{ "links": []map[string]interface{}{ { @@ -141,10 +132,6 @@ func TestBookMatchingBulkLink(t *testing.T) { }) t.Run("BulkLinkBooks_WithAuth_EmptyLinks", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "links": []map[string]interface{}{}, } @@ -170,9 +157,6 @@ func TestBookMatchingBulkLink(t *testing.T) { }) t.Run("BulkLinkBooks_InvalidUnlinkedBookID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) bookID := createTestMediaItemID(t, setup.Server, token) req := map[string]interface{}{ @@ -211,10 +195,6 @@ func TestBookMatchingBulkLink(t *testing.T) { }) t.Run("BulkLinkBooks_MultipleLinks", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "links": []map[string]interface{}{ { @@ -258,9 +238,10 @@ func TestBookMatchingBulkLink(t *testing.T) { // TestBookMatchingAutoLink tests automatic linking func TestBookMatchingAutoLink(t *testing.T) { - t.Run("AutoLinkBooks_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + t.Run("AutoLinkBooks_WithoutAuth", func(t *testing.T) { req := map[string]interface{}{ "confidence_threshold": 0.8, "limit": 10, @@ -279,10 +260,6 @@ func TestBookMatchingAutoLink(t *testing.T) { }) t.Run("AutoLinkBooks_WithAuth_DefaultThreshold", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{} body, _ := json.Marshal(req) @@ -305,10 +282,6 @@ func TestBookMatchingAutoLink(t *testing.T) { }) t.Run("AutoLinkBooks_CustomThreshold", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "confidence_threshold": 0.95, "limit": 20, @@ -333,10 +306,6 @@ func TestBookMatchingAutoLink(t *testing.T) { }) t.Run("AutoLinkBooks_NoUnlinkedBooks", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "limit": 5, } @@ -363,9 +332,10 @@ func TestBookMatchingAutoLink(t *testing.T) { // TestBookMatchingSuggestions tests getting suggestions for unlinked books func TestBookMatchingSuggestions(t *testing.T) { - t.Run("GetUnlinkedBookSuggestions_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + t.Run("GetUnlinkedBookSuggestions_WithoutAuth", func(t *testing.T) { testID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/sync/unlinked-books/"+testID.String()+"/suggestions", nil) @@ -378,10 +348,6 @@ func TestBookMatchingSuggestions(t *testing.T) { }) t.Run("GetUnlinkedBookSuggestions_InvalidUUID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/sync/unlinked-books/invalid-uuid/suggestions", nil) httpReq.Header.Set("Authorization", "Bearer "+token) @@ -394,10 +360,6 @@ func TestBookMatchingSuggestions(t *testing.T) { }) t.Run("GetUnlinkedBookSuggestions_BookNotFound", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - testID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/sync/unlinked-books/"+testID.String()+"/suggestions", nil) httpReq.Header.Set("Authorization", "Bearer "+token) @@ -411,10 +373,6 @@ func TestBookMatchingSuggestions(t *testing.T) { }) t.Run("GetUnlinkedBookSuggestions_ResponseStructure", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Create a test device and unlinked book would go here // For now, test with a non-existent ID to check response structure testID := uuid.New() @@ -433,9 +391,10 @@ func TestBookMatchingSuggestions(t *testing.T) { // TestBookMatchingDeviceFileAliases tests device file alias operations func TestBookMatchingDeviceFileAliases(t *testing.T) { - t.Run("GetDeviceFileAliases_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + t.Run("GetDeviceFileAliases_WithoutAuth", func(t *testing.T) { testID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/devices/"+testID.String()+"/file-aliases", nil) @@ -448,10 +407,6 @@ func TestBookMatchingDeviceFileAliases(t *testing.T) { }) t.Run("GetDeviceFileAliases_WithAuth", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - testID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/devices/"+testID.String()+"/file-aliases", nil) httpReq.Header.Set("Authorization", "Bearer "+token) @@ -472,8 +427,6 @@ func TestBookMatchingDeviceFileAliases(t *testing.T) { }) t.Run("CreateDeviceFileAlias_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) - deviceID := uuid.New() mediaItemID := uuid.New() @@ -497,10 +450,6 @@ func TestBookMatchingDeviceFileAliases(t *testing.T) { }) t.Run("CreateDeviceFileAlias_InvalidDeviceID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - mediaItemID := uuid.New() req := map[string]interface{}{ @@ -524,10 +473,6 @@ func TestBookMatchingDeviceFileAliases(t *testing.T) { }) t.Run("CreateDeviceFileAlias_InvalidMediaItemID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - deviceID := uuid.New() req := map[string]interface{}{ @@ -551,10 +496,6 @@ func TestBookMatchingDeviceFileAliases(t *testing.T) { }) t.Run("UpdateDeviceFileAlias_InvalidAliasID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - deviceID := uuid.New() req := map[string]interface{}{ @@ -575,10 +516,6 @@ func TestBookMatchingDeviceFileAliases(t *testing.T) { }) t.Run("DeleteDeviceFileAlias_InvalidAliasID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - deviceID := uuid.New() httpReq, _ := http.NewRequest("DELETE", setup.Server.URL+"/api/devices/"+deviceID.String()+"/file-aliases/invalid-uuid", nil) @@ -595,9 +532,10 @@ func TestBookMatchingDeviceFileAliases(t *testing.T) { // TestBookMatchingGetBookMatches tests the book matches endpoint func TestBookMatchingGetBookMatches(t *testing.T) { - t.Run("GetBookMatches_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + t.Run("GetBookMatches_WithoutAuth", func(t *testing.T) { httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/books/match?title=Test", nil) client := &http.Client{} @@ -609,10 +547,6 @@ func TestBookMatchingGetBookMatches(t *testing.T) { }) t.Run("GetBookMatches_WithAuth_ByTitle", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/books/match?title=Test", nil) httpReq.Header.Set("Authorization", "Bearer "+token) @@ -631,10 +565,6 @@ func TestBookMatchingGetBookMatches(t *testing.T) { }) t.Run("GetBookMatches_InvalidFileSize", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/books/match?title=Test&file_size=invalid", nil) httpReq.Header.Set("Authorization", "Bearer "+token) @@ -647,10 +577,6 @@ func TestBookMatchingGetBookMatches(t *testing.T) { }) t.Run("GetBookMatches_MultipleIdentifiers", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/books/match?identifier=id1&identifier=id2&title=Test", nil) httpReq.Header.Set("Authorization", "Bearer "+token) diff --git a/cmd/server/tests/collections_bulk_test.go b/cmd/server/tests/collections_bulk_test.go index 38b6cd2..f31b7ef 100644 --- a/cmd/server/tests/collections_bulk_test.go +++ b/cmd/server/tests/collections_bulk_test.go @@ -13,9 +13,11 @@ import ( // TestCollectionsBulkOperations tests bulk collection operations func TestCollectionsBulkOperations(t *testing.T) { - t.Run("BulkAddBooks_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} + t.Run("BulkAddBooks_WithoutAuth", func(t *testing.T) { req := map[string]interface{}{ "operations": []map[string]interface{}{ { @@ -29,7 +31,6 @@ func TestCollectionsBulkOperations(t *testing.T) { httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -38,10 +39,6 @@ func TestCollectionsBulkOperations(t *testing.T) { }) t.Run("BulkAddBooks_EmptyOperations", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "operations": []map[string]interface{}{}, } @@ -51,7 +48,6 @@ func TestCollectionsBulkOperations(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -60,9 +56,6 @@ func TestCollectionsBulkOperations(t *testing.T) { }) t.Run("BulkAddBooks_InvalidCollectionID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) bookID := createTestMediaItemID(t, setup.Server, token) req := map[string]interface{}{ @@ -79,7 +72,6 @@ func TestCollectionsBulkOperations(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -102,10 +94,6 @@ func TestCollectionsBulkOperations(t *testing.T) { }) t.Run("BulkAddBooks_InvalidBookID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Create a collection first collectionReq := map[string]interface{}{ "name": "Test Collection", @@ -117,7 +105,6 @@ func TestCollectionsBulkOperations(t *testing.T) { collectionHTTP.Header.Set("Content-Type", "application/json") collectionHTTP.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(collectionHTTP) require.NoError(t, err) defer resp.Body.Close() @@ -156,10 +143,6 @@ func TestCollectionsBulkOperations(t *testing.T) { }) t.Run("BulkAddBooks_SingleOperation", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Create a collection collectionReq := map[string]interface{}{ "name": "Test Collection", @@ -171,7 +154,6 @@ func TestCollectionsBulkOperations(t *testing.T) { collectionHTTP.Header.Set("Content-Type", "application/json") collectionHTTP.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(collectionHTTP) require.NoError(t, err) defer resp.Body.Close() @@ -218,10 +200,6 @@ func TestCollectionsBulkOperations(t *testing.T) { }) t.Run("BulkAddBooks_MultipleBooksSingleCollection", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Create a collection collectionReq := map[string]interface{}{ "name": "Test Collection", @@ -233,7 +211,6 @@ func TestCollectionsBulkOperations(t *testing.T) { collectionHTTP.Header.Set("Content-Type", "application/json") collectionHTTP.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(collectionHTTP) require.NoError(t, err) defer resp.Body.Close() @@ -276,10 +253,6 @@ func TestCollectionsBulkOperations(t *testing.T) { }) t.Run("BulkAddBooks_MultipleCollections", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Create multiple collections collectionReq := map[string]interface{}{ "name": "Test Collection 1", @@ -291,7 +264,6 @@ func TestCollectionsBulkOperations(t *testing.T) { collectionHTTP.Header.Set("Content-Type", "application/json") collectionHTTP.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(collectionHTTP) require.NoError(t, err) defer resp.Body.Close() @@ -355,10 +327,6 @@ func TestCollectionsBulkOperations(t *testing.T) { }) t.Run("BulkAddBooks_DuplicateBooks", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Create a collection collectionReq := map[string]interface{}{ "name": "Test Collection", @@ -370,7 +338,6 @@ func TestCollectionsBulkOperations(t *testing.T) { collectionHTTP.Header.Set("Content-Type", "application/json") collectionHTTP.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(collectionHTTP) require.NoError(t, err) defer resp.Body.Close() @@ -401,7 +368,7 @@ func TestCollectionsBulkOperations(t *testing.T) { require.NoError(t, err) defer resp.Body.Close() - // Try to add the same book again - create new request with fresh body + // Try to add same book again - create new request with fresh body addBody2, _ := json.Marshal(addReq) addHTTP2, _ := http.NewRequest("POST", setup.Server.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(addBody2)) addHTTP2.Header.Set("Content-Type", "application/json") @@ -416,16 +383,11 @@ func TestCollectionsBulkOperations(t *testing.T) { }) t.Run("BulkAddBooks_InvalidRequestBody", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Send invalid JSON httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/collections/bulk-add-books", bytes.NewBuffer([]byte("invalid json"))) httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() diff --git a/cmd/server/tests/conflicts_bulk_test.go b/cmd/server/tests/conflicts_bulk_test.go index ad9689e..6f143d8 100644 --- a/cmd/server/tests/conflicts_bulk_test.go +++ b/cmd/server/tests/conflicts_bulk_test.go @@ -13,9 +13,11 @@ import ( // TestConflictsBulkOperations tests bulk conflict resolution operations func TestConflictsBulkOperations(t *testing.T) { - t.Run("BulkResolveConflicts_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} + t.Run("BulkResolveConflicts_WithoutAuth", func(t *testing.T) { req := map[string]interface{}{ "conflict_ids": []string{uuid.New().String()}, "strategy": "most_recent", @@ -25,7 +27,6 @@ func TestConflictsBulkOperations(t *testing.T) { httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -34,10 +35,6 @@ func TestConflictsBulkOperations(t *testing.T) { }) t.Run("BulkResolveConflicts_EmptyConflictIDs", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{}, "strategy": "most_recent", @@ -48,7 +45,6 @@ func TestConflictsBulkOperations(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -57,10 +53,6 @@ func TestConflictsBulkOperations(t *testing.T) { }) t.Run("BulkResolveConflicts_InvalidConflictID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{"invalid-uuid"}, "strategy": "most_recent", @@ -71,7 +63,6 @@ func TestConflictsBulkOperations(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -92,10 +83,6 @@ func TestConflictsBulkOperations(t *testing.T) { }) t.Run("BulkResolveConflicts_InvalidStrategy", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{uuid.New().String()}, "strategy": "invalid_strategy", @@ -106,7 +93,6 @@ func TestConflictsBulkOperations(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -130,10 +116,6 @@ func TestConflictsBulkOperations(t *testing.T) { }) t.Run("BulkResolveConflicts_MostRecentStrategy", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{uuid.New().String(), uuid.New().String()}, "strategy": "most_recent", @@ -144,7 +126,6 @@ func TestConflictsBulkOperations(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -159,10 +140,6 @@ func TestConflictsBulkOperations(t *testing.T) { }) t.Run("BulkResolveConflicts_HighestProgressStrategy", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{uuid.New().String(), uuid.New().String()}, "strategy": "highest_progress", @@ -173,7 +150,6 @@ func TestConflictsBulkOperations(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -188,10 +164,6 @@ func TestConflictsBulkOperations(t *testing.T) { }) t.Run("BulkResolveConflicts_ManualStrategy_WithoutWinner", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{uuid.New().String()}, "strategy": "manual", @@ -202,7 +174,6 @@ func TestConflictsBulkOperations(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -216,10 +187,6 @@ func TestConflictsBulkOperations(t *testing.T) { }) t.Run("BulkResolveConflicts_ManualStrategy_WithWinner", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{uuid.New().String()}, "strategy": "manual", @@ -231,7 +198,6 @@ func TestConflictsBulkOperations(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -240,16 +206,11 @@ func TestConflictsBulkOperations(t *testing.T) { }) t.Run("BulkResolveConflicts_InvalidRequestBody", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Send invalid JSON httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer([]byte("invalid json"))) httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -260,9 +221,11 @@ func TestConflictsBulkOperations(t *testing.T) { // TestConflictsBulkDismiss tests bulk dismiss operations func TestConflictsBulkDismiss(t *testing.T) { - t.Run("BulkDismissConflicts_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} + t.Run("BulkDismissConflicts_WithoutAuth", func(t *testing.T) { req := map[string]interface{}{ "conflict_ids": []string{uuid.New().String()}, } @@ -271,7 +234,6 @@ func TestConflictsBulkDismiss(t *testing.T) { httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-dismiss", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -280,10 +242,6 @@ func TestConflictsBulkDismiss(t *testing.T) { }) t.Run("BulkDismissConflicts_EmptyConflictIDs", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{}, } @@ -293,7 +251,6 @@ func TestConflictsBulkDismiss(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -302,10 +259,6 @@ func TestConflictsBulkDismiss(t *testing.T) { }) t.Run("BulkDismissConflicts_InvalidConflictID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{"invalid-uuid", uuid.New().String()}, } @@ -315,7 +268,6 @@ func TestConflictsBulkDismiss(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -336,10 +288,6 @@ func TestConflictsBulkDismiss(t *testing.T) { }) t.Run("BulkDismissConflicts_MultipleConflicts", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{ uuid.New().String(), @@ -353,7 +301,6 @@ func TestConflictsBulkDismiss(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -368,16 +315,11 @@ func TestConflictsBulkDismiss(t *testing.T) { }) t.Run("BulkDismissConflicts_InvalidRequestBody", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Send invalid JSON httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-dismiss", bytes.NewBuffer([]byte("invalid json"))) httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -388,11 +330,11 @@ func TestConflictsBulkDismiss(t *testing.T) { // TestConflictsBulkEdgeCases tests edge cases for bulk operations func TestConflictsBulkEdgeCases(t *testing.T) { + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} + t.Run("BulkResolve_NonExistentConflicts", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{ uuid.New().String(), @@ -407,7 +349,6 @@ func TestConflictsBulkEdgeCases(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -423,10 +364,6 @@ func TestConflictsBulkEdgeCases(t *testing.T) { }) t.Run("BulkDismiss_MixedValidInvalid", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "conflict_ids": []string{ "invalid-uuid-1", @@ -440,7 +377,6 @@ func TestConflictsBulkEdgeCases(t *testing.T) { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() diff --git a/cmd/server/tests/media_bulk_test.go b/cmd/server/tests/media_bulk_test.go index 850079d..4ad274d 100644 --- a/cmd/server/tests/media_bulk_test.go +++ b/cmd/server/tests/media_bulk_test.go @@ -13,9 +13,10 @@ import ( // TestMediaBulkOperations tests bulk media operations func TestMediaBulkOperations(t *testing.T) { - t.Run("BulkDeleteBooks_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + t.Run("BulkDeleteBooks_WithoutAuth", func(t *testing.T) { req := map[string]interface{}{ "media_item_ids": []string{uuid.New().String()}, } @@ -33,10 +34,6 @@ func TestMediaBulkOperations(t *testing.T) { }) t.Run("BulkDeleteBooks_EmptyBookIDs", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "media_item_ids": []string{}, } @@ -55,10 +52,6 @@ func TestMediaBulkOperations(t *testing.T) { }) t.Run("BulkDeleteBooks_InvalidBookIDs", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "media_item_ids": []string{"invalid-uuid", "another-invalid"}, } @@ -85,11 +78,6 @@ func TestMediaBulkOperations(t *testing.T) { }) t.Run("BulkDeleteBooks_WithValidBooks", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - - // Create test media items mediaID1 := createTestMediaItemID(t, setup.Server, token) mediaID2 := createTestMediaItemID(t, setup.Server, token) mediaID3 := uuid.New().String() @@ -117,15 +105,11 @@ func TestMediaBulkOperations(t *testing.T) { assert.Equal(t, 3.0, result["total"]) // Check that deleted field exists and has at least 2 (the valid books) if deleted, ok := result["deleted"].(float64); ok { - assert.True(t, deleted >= 2, "Should delete at least the valid books") + assert.True(t, deleted >= 2, "Should delete at least valid books") } }) t.Run("BulkDeleteBooks_InvalidRequestBody", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Send invalid JSON httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/media-items/bulk-delete", bytes.NewBuffer([]byte("invalid json"))) httpReq.Header.Set("Content-Type", "application/json") @@ -140,8 +124,6 @@ func TestMediaBulkOperations(t *testing.T) { }) t.Run("BulkUpdateBooks_WithoutAuth", func(t *testing.T) { - setup := setupTestServer(t) - req := map[string]interface{}{ "media_item_updates": []map[string]interface{}{ { @@ -166,10 +148,6 @@ func TestMediaBulkOperations(t *testing.T) { }) t.Run("BulkUpdateBooks_EmptyBookIDs", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "media_item_updates": []map[string]interface{}{ { @@ -195,10 +173,6 @@ func TestMediaBulkOperations(t *testing.T) { }) t.Run("BulkUpdateBooks_InvalidBookIDs", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - req := map[string]interface{}{ "media_item_updates": []map[string]interface{}{ { @@ -232,11 +206,6 @@ func TestMediaBulkOperations(t *testing.T) { }) t.Run("BulkUpdateBooks_UpdateTags", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - - // Create test media items mediaID1 := createTestMediaItemID(t, setup.Server, token) mediaID2 := createTestMediaItemID(t, setup.Server, token) @@ -281,11 +250,6 @@ func TestMediaBulkOperations(t *testing.T) { }) t.Run("BulkUpdateBooks_UpdateReadingStatus", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - - // Create test media items mediaID1 := createTestMediaItemID(t, setup.Server, token) req := map[string]interface{}{ @@ -318,11 +282,6 @@ func TestMediaBulkOperations(t *testing.T) { }) t.Run("BulkUpdateBooks_UpdateMultipleFields", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - - // Create test media items mediaID1 := createTestMediaItemID(t, setup.Server, token) req := map[string]interface{}{ @@ -357,10 +316,6 @@ func TestMediaBulkOperations(t *testing.T) { }) t.Run("BulkUpdateBooks_InvalidRequestBody", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - // Send invalid JSON httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/media-items/bulk-update", bytes.NewBuffer([]byte("invalid json"))) httpReq.Header.Set("Content-Type", "application/json") diff --git a/cmd/server/tests/opds_test.go b/cmd/server/tests/opds_test.go index 0e24eae..b376aa7 100644 --- a/cmd/server/tests/opds_test.go +++ b/cmd/server/tests/opds_test.go @@ -11,13 +11,14 @@ import ( // TestOPDSEndpoints tests OPDS (Open Publication Distribution System) endpoints func TestOPDSEndpoints(t *testing.T) { - t.Run("GetDeviceCatalog_WithoutDeviceAuth", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} + t.Run("GetDeviceCatalog_WithoutDeviceAuth", func(t *testing.T) { deviceID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/catalog", nil) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -28,11 +29,8 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("GetDeviceCatalog_InvalidDeviceID", func(t *testing.T) { - setup := setupTestServer(t) - httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/invalid-uuid/catalog", nil) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -42,17 +40,11 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("GetDeviceCatalog_ValidDevice", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) - - // Note: Device registration requires different endpoint - // For now, test with a valid UUID format deviceID := uuid.New() + httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/catalog", nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -62,11 +54,8 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("SearchDeviceCatalog_InvalidDeviceID", func(t *testing.T) { - setup := setupTestServer(t) - httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/invalid-uuid/search?query=test", nil) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -75,15 +64,11 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("SearchDeviceCatalog_ValidDevice", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) deviceID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/search?query=test", nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -93,11 +78,8 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("GetDeviceNavigation_InvalidDeviceID", func(t *testing.T) { - setup := setupTestServer(t) - httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/invalid-uuid/nav", nil) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -106,15 +88,11 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("GetDeviceNavigation_ValidDevice", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) deviceID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/nav", nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -124,12 +102,8 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("DownloadBook_InvalidDeviceID", func(t *testing.T) { - setup := setupTestServer(t) + httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/invalid-uuid/download/"+uuid.New().String(), nil) - bookID := uuid.New() - httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/invalid-uuid/download/"+bookID.String(), nil) - - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -138,12 +112,9 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("DownloadBook_InvalidBookID", func(t *testing.T) { - setup := setupTestServer(t) - deviceID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/download/invalid-uuid", nil) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -152,16 +123,12 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("DownloadBook_ValidIDs", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) deviceID := uuid.New() bookID := createTestMediaItemID(t, setup.Server, token) httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/download/"+bookID, nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -172,12 +139,8 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("GetCoverImage_InvalidDeviceID", func(t *testing.T) { - setup := setupTestServer(t) + httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/invalid-uuid/cover/"+uuid.New().String(), nil) - bookID := uuid.New() - httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/invalid-uuid/cover/"+bookID.String(), nil) - - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -186,12 +149,8 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("GetCoverImage_InvalidBookID", func(t *testing.T) { - setup := setupTestServer(t) + httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/invalid-uuid/cover/"+uuid.New().String(), nil) - deviceID := uuid.New() - httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/cover/invalid-uuid", nil) - - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -200,16 +159,12 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("GetCoverImage_ValidIDs", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) deviceID := uuid.New() bookID := createTestMediaItemID(t, setup.Server, token) httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/cover/"+bookID, nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -219,12 +174,9 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("ListFormats_InvalidDeviceID", func(t *testing.T) { - setup := setupTestServer(t) - bookID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/invalid-uuid/formats/"+bookID.String(), nil) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -233,16 +185,12 @@ func TestOPDSEndpoints(t *testing.T) { }) t.Run("ListFormats_ValidDeviceID", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) deviceID := uuid.New() bookID := createTestMediaItemID(t, setup.Server, token) httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/formats/"+bookID, nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -254,10 +202,11 @@ func TestOPDSEndpoints(t *testing.T) { // TestOPDSConversion tests on-the-fly conversion for downloads func TestOPDSConversion(t *testing.T) { - t.Run("DownloadKEPUB_FormatParameter", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} - token := loginTestUser(t, setup.Server, setup.DB) + t.Run("DownloadKEPUB_FormatParameter", func(t *testing.T) { deviceID := uuid.New() bookID := createTestMediaItemID(t, setup.Server, token) @@ -265,7 +214,6 @@ func TestOPDSConversion(t *testing.T) { httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/download/"+bookID+"?format=kepub", nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -276,9 +224,6 @@ func TestOPDSConversion(t *testing.T) { }) t.Run("DownloadEPUB_DefaultFormat", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) deviceID := uuid.New() bookID := createTestMediaItemID(t, setup.Server, token) @@ -286,7 +231,6 @@ func TestOPDSConversion(t *testing.T) { httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/download/"+bookID, nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -296,9 +240,6 @@ func TestOPDSConversion(t *testing.T) { }) t.Run("Download_UnsupportedFormat", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) deviceID := uuid.New() bookID := createTestMediaItemID(t, setup.Server, token) @@ -306,7 +247,6 @@ func TestOPDSConversion(t *testing.T) { httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/download/"+bookID+"?format=pdf", nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -318,16 +258,16 @@ func TestOPDSConversion(t *testing.T) { // TestOPDSEdgeCases tests edge cases for OPDS endpoints func TestOPDSEdgeCases(t *testing.T) { - t.Run("Catalog_EmptyLibrary", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + token := loginTestUser(t, setup.Server, setup.DB) + client := &http.Client{} - token := loginTestUser(t, setup.Server, setup.DB) + t.Run("Catalog_EmptyLibrary", func(t *testing.T) { deviceID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/catalog", nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -337,16 +277,12 @@ func TestOPDSEdgeCases(t *testing.T) { }) t.Run("Search_SpecialCharacters", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) deviceID := uuid.New() // Search with special characters httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/search?query=test%20%26%20more", nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -356,15 +292,11 @@ func TestOPDSEdgeCases(t *testing.T) { }) t.Run("Search_EmptyQuery", func(t *testing.T) { - setup := setupTestServer(t) - - token := loginTestUser(t, setup.Server, setup.DB) deviceID := uuid.New() httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/opds/devices/"+deviceID.String()+"/search?query=", nil) httpReq.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() diff --git a/cmd/server/tests/refresh_token_test.go b/cmd/server/tests/refresh_token_test.go index a5db17f..df297c5 100644 --- a/cmd/server/tests/refresh_token_test.go +++ b/cmd/server/tests/refresh_token_test.go @@ -12,16 +12,16 @@ import ( // TestRefreshTokenFlow comprehensive tests for token refresh functionality func TestRefreshTokenFlow(t *testing.T) { - t.Run("RefreshToken_MissingToken", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + client := &http.Client{} + t.Run("RefreshToken_MissingToken", func(t *testing.T) { req := map[string]interface{}{} body, _ := json.Marshal(req) httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/refresh", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -30,8 +30,6 @@ func TestRefreshTokenFlow(t *testing.T) { }) t.Run("RefreshToken_InvalidTokenFormat", func(t *testing.T) { - setup := setupTestServer(t) - req := map[string]interface{}{ "refresh_token": "not-a-valid-jwt-token", } @@ -40,7 +38,6 @@ func TestRefreshTokenFlow(t *testing.T) { httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/refresh", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -49,8 +46,6 @@ func TestRefreshTokenFlow(t *testing.T) { }) t.Run("RefreshToken_ExpiredToken", func(t *testing.T) { - setup := setupTestServer(t) - // This would require an expired token - for now test with invalid token req := map[string]interface{}{ "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjAwMDAwMDB9.expired", @@ -60,7 +55,6 @@ func TestRefreshTokenFlow(t *testing.T) { httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/refresh", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -69,8 +63,6 @@ func TestRefreshTokenFlow(t *testing.T) { }) t.Run("RefreshToken_ValidToken", func(t *testing.T) { - setup := setupTestServer(t) - // First, login to get tokens loginReq := map[string]string{ "login": "testuser@example.com", @@ -81,7 +73,6 @@ func TestRefreshTokenFlow(t *testing.T) { loginHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/login", bytes.NewBuffer(loginBody)) loginHTTP.Header.Set("Content-Type", "application/json") - client := &http.Client{} loginResp, err := client.Do(loginHTTP) require.NoError(t, err) defer loginResp.Body.Close() @@ -122,13 +113,10 @@ func TestRefreshTokenFlow(t *testing.T) { }) t.Run("RefreshToken_InvalidRequestBody", func(t *testing.T) { - setup := setupTestServer(t) - // Send invalid JSON httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/refresh", bytes.NewBuffer([]byte("invalid json"))) httpReq.Header.Set("Content-Type", "application/json") - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -137,8 +125,6 @@ func TestRefreshTokenFlow(t *testing.T) { }) t.Run("RefreshToken_MissingContentType", func(t *testing.T) { - setup := setupTestServer(t) - req := map[string]interface{}{ "refresh_token": "some-token", } @@ -147,7 +133,6 @@ func TestRefreshTokenFlow(t *testing.T) { httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/refresh", bytes.NewBuffer(body)) // Don't set Content-Type - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -159,9 +144,10 @@ func TestRefreshTokenFlow(t *testing.T) { // TestRefreshTokenSecurity tests security aspects of token refresh func TestRefreshTokenSecurity(t *testing.T) { - t.Run("RefreshToken_ReuseProtection", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + client := &http.Client{} + t.Run("RefreshToken_ReuseProtection", func(t *testing.T) { // Login to get tokens loginReq := map[string]string{ "login": "testuser@example.com", @@ -172,7 +158,6 @@ func TestRefreshTokenSecurity(t *testing.T) { loginHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/login", bytes.NewBuffer(loginBody)) loginHTTP.Header.Set("Content-Type", "application/json") - client := &http.Client{} loginResp, err := client.Do(loginHTTP) require.NoError(t, err) defer loginResp.Body.Close() @@ -213,8 +198,6 @@ func TestRefreshTokenSecurity(t *testing.T) { }) t.Run("RefreshToken_TokenTampering", func(t *testing.T) { - setup := setupTestServer(t) - // Login to get a valid token loginReq := map[string]string{ "login": "testuser@example.com", @@ -225,7 +208,6 @@ func TestRefreshTokenSecurity(t *testing.T) { loginHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/login", bytes.NewBuffer(loginBody)) loginHTTP.Header.Set("Content-Type", "application/json") - client := &http.Client{} loginResp, err := client.Do(loginHTTP) require.NoError(t, err) defer loginResp.Body.Close() @@ -237,7 +219,7 @@ func TestRefreshTokenSecurity(t *testing.T) { refreshToken := loginResult["refresh_token"].(string) - // Tamper with the token by modifying a character + // Tamper with token by modifying a character if len(refreshToken) > 10 { tamperedToken := refreshToken[:5] + "X" + refreshToken[6:] @@ -249,20 +231,21 @@ func TestRefreshTokenSecurity(t *testing.T) { refreshHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/refresh", bytes.NewBuffer(refreshBody)) refreshHTTP.Header.Set("Content-Type", "application/json") - refreshResp, err := client.Do(refreshHTTP) + resp, err := client.Do(refreshHTTP) require.NoError(t, err) - defer refreshResp.Body.Close() + defer resp.Body.Close() - assert.Equal(t, http.StatusUnauthorized, refreshResp.StatusCode) + assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) } }) } // TestRefreshTokenEdgeCases tests edge cases for token refresh func TestRefreshTokenEdgeCases(t *testing.T) { - t.Run("RefreshToken_EmptyStringToken", func(t *testing.T) { - setup := setupTestServer(t) + setup := setupTestServer(t) + client := &http.Client{} + t.Run("RefreshToken_EmptyStringToken", func(t *testing.T) { req := map[string]interface{}{ "refresh_token": "", } @@ -271,7 +254,6 @@ func TestRefreshTokenEdgeCases(t *testing.T) { httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/refresh", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -280,8 +262,6 @@ func TestRefreshTokenEdgeCases(t *testing.T) { }) t.Run("RefreshToken_NullToken", func(t *testing.T) { - setup := setupTestServer(t) - req := map[string]interface{}{ "refresh_token": nil, } @@ -290,7 +270,6 @@ func TestRefreshTokenEdgeCases(t *testing.T) { httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/refresh", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") - client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() @@ -299,8 +278,6 @@ func TestRefreshTokenEdgeCases(t *testing.T) { }) t.Run("RefreshToken_ResponseStructure", func(t *testing.T) { - setup := setupTestServer(t) - // Login to get tokens loginReq := map[string]string{ "login": "testuser@example.com", @@ -311,7 +288,6 @@ func TestRefreshTokenEdgeCases(t *testing.T) { loginHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/login", bytes.NewBuffer(loginBody)) loginHTTP.Header.Set("Content-Type", "application/json") - client := &http.Client{} loginResp, err := client.Do(loginHTTP) require.NoError(t, err) defer loginResp.Body.Close() @@ -350,8 +326,6 @@ func TestRefreshTokenEdgeCases(t *testing.T) { }) t.Run("RefreshToken_TokenType", func(t *testing.T) { - setup := setupTestServer(t) - // Login to get tokens loginReq := map[string]string{ "login": "testuser@example.com", @@ -362,7 +336,6 @@ func TestRefreshTokenEdgeCases(t *testing.T) { loginHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/auth/login", bytes.NewBuffer(loginBody)) loginHTTP.Header.Set("Content-Type", "application/json") - client := &http.Client{} loginResp, err := client.Do(loginHTTP) require.NoError(t, err) defer loginResp.Body.Close() diff --git a/cmd/server/tests/sync_integration_test.go b/cmd/server/tests/sync_integration_test.go index 21b465b..bb04e5f 100644 --- a/cmd/server/tests/sync_integration_test.go +++ b/cmd/server/tests/sync_integration_test.go @@ -18,7 +18,11 @@ func setupSyncTestDB(t *testing.T) *database.Queries { ctx := context.Background() dbURL := "postgresql://postgres:postgres@db:5432/bookhoard?sslmode=disable" - dbPool, err := pgxpool.New(ctx, dbURL) + // Use max_conns=1 to prevent connection pool exhaustion during test runs + dbConfig, err := pgxpool.ParseConfig(dbURL) + require.NoError(t, err, "Failed to parse database URL") + dbConfig.MaxConns = 1 + dbPool, err := pgxpool.NewWithConfig(ctx, dbConfig) require.NoError(t, err, "Failed to connect to test database") db := database.New(dbPool) diff --git a/cmd/server/tests/test_helpers.go b/cmd/server/tests/test_helpers.go index 6b0e85b..9236e0b 100644 --- a/cmd/server/tests/test_helpers.go +++ b/cmd/server/tests/test_helpers.go @@ -309,7 +309,12 @@ func setupTestServer(t *testing.T) *TestServerSetup { cfg.RequestsPerMinute = 1000 // Connect to test database using the same method as main application - dbPool, err := pgxpool.New(context.Background(), cfg.DatabaseURL()) + // Use max_conns=1 to prevent connection pool exhaustion during test runs + // (78 tests × 1 connection = 78 connections, well under PostgreSQL's 100 default max_connections) + dbConfig, err := pgxpool.ParseConfig(cfg.DatabaseURL()) + require.NoError(t, err, "Failed to parse database URL") + dbConfig.MaxConns = 1 + dbPool, err := pgxpool.NewWithConfig(context.Background(), dbConfig) require.NoError(t, err, "Failed to connect to test database") queries := database.New(dbPool)