Files
bookhoard/cmd/server/tests/fsnotify_integration_test.go
T
john-okeefe b700f64624 fix: remove redundant defer setup.Close() calls to enable library cleanup
Problem:
Tests were calling `defer setup.Close()` which was interfering with the
library cleanup added in the previous commit. The execution order was:

1. setupTestServer() registers t.Cleanup() with library deletion code
2. Test calls defer setup.Close()
3. Test finishes:
   - defer setup.Close() runs FIRST → closes DB pool
   - t.Cleanup() runs SECOND → tries to delete libraries but DB is closed!

This prevented "Job Status Test Library" and other test libraries from
being cleaned up, leaving residual data in the database after tests.

Root Cause:
The setupTestServer() function already handles cleanup via t.Cleanup(),
which calls setup.Close() at the end. The explicit defer calls were
redundant and caused the database pool to close before library cleanup
could execute.

Solution:
Removed all 17 occurrences of `defer setup.Close()` from test files:
- worker_test.go: 4 tests
- jobs_test.go: 7 tests
- scan_settings_integration_test.go: 3 tests
- library_browse_test.go: 1 test
- goroutine_leak_test.go: 1 test
- fsnotify_integration_test.go: 1 test

Now setupTestServer()'s t.Cleanup() function properly:
1. Deletes "test" libraries (while DB is still connected)
2. Then calls setup.Close() to close connections

This ensures all test libraries are cleaned up, leaving a clean database
after `make test-integration` completes.

Files changed:
- cmd/server/tests/worker_test.go: Removed 4 defer calls
- cmd/server/tests/jobs_test.go: Removed 7 defer calls
- cmd/server/tests/scan_settings_integration_test.go: Removed 3 defer calls
- cmd/server/tests/library_browse_test.go: Removed 1 defer call
- cmd/server/tests/goroutine_leak_test.go: Removed 1 defer call
- cmd/server/tests/fsnotify_integration_test.go: Removed 1 defer call
2026-03-24 20:55:37 -04:00

116 lines
4.0 KiB
Go

package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFSNotify_BulkFileDetection(t *testing.T) {
setup := setupTestServer(t)
t.Run("Detects multiple files added simultaneously", func(t *testing.T) {
token := setup.Token
tmpDir := t.TempDir()
// Create library
createLibReq := map[string]interface{}{
"name": "bulk-test-library",
"type": "ebooks",
}
libBody, _ := json.Marshal(createLibReq)
libReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/libraries", bytes.NewBuffer(libBody))
libReq.Header.Set("Content-Type", "application/json")
libReq.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
libResp, err := client.Do(libReq)
require.NoError(t, err)
defer libResp.Body.Close()
require.Equal(t, http.StatusCreated, libResp.StatusCode)
var libResult map[string]interface{}
json.NewDecoder(libResp.Body).Decode(&libResult)
libraryID := libResult["id"].(string)
// Add folder to library
folderURL := fmt.Sprintf("%s/api/libraries/%s/folders", setup.Server.URL, libraryID)
folderReq := map[string]interface{}{
"folder_path": tmpDir,
}
folderBody, _ := json.Marshal(folderReq)
folderHTTPReq, _ := http.NewRequest("POST", folderURL, bytes.NewBuffer(folderBody))
folderHTTPReq.Header.Set("Content-Type", "application/json")
folderHTTPReq.Header.Set("Authorization", "Bearer "+token)
folderResp, err := client.Do(folderHTTPReq)
require.NoError(t, err)
defer folderResp.Body.Close()
require.Equal(t, http.StatusCreated, folderResp.StatusCode, "Folder should be added to library")
// Create 20 test files simultaneously
for i := 0; i < 20; i++ {
fileName := filepath.Join(tmpDir, fmt.Sprintf("book%d.epub", i))
err := os.WriteFile(fileName, []byte(fmt.Sprintf("test %d", i)), 0644)
require.NoError(t, err)
}
// Trigger library scan
scanURL := fmt.Sprintf("%s/api/libraries/%s/scan", setup.Server.URL, libraryID)
scanHTTPReq, _ := http.NewRequest("POST", scanURL, nil)
scanHTTPReq.Header.Set("Authorization", "Bearer "+token)
scanResp, err := client.Do(scanHTTPReq)
require.NoError(t, err)
defer scanResp.Body.Close()
require.Equal(t, http.StatusAccepted, scanResp.StatusCode, "Scan should be accepted")
var scanResponse map[string]interface{}
json.NewDecoder(scanResp.Body).Decode(&scanResponse)
jobID, ok := scanResponse["job_id"].(string)
require.True(t, ok, "job_id should be string")
require.NotEmpty(t, jobID, "job_id should not be empty")
// Wait for scan job to complete
time.Sleep(3 * time.Second)
for i := 0; i < 30; i++ {
if i > 0 {
time.Sleep(1 * time.Second)
}
statusURL := fmt.Sprintf("%s/api/scanner/status/%s", setup.Server.URL, jobID)
statusReq, _ := http.NewRequest("GET", statusURL, nil)
statusReq.Header.Set("Authorization", "Bearer "+token)
statusResp, err := client.Do(statusReq)
require.NoError(t, err)
if statusResp.StatusCode == http.StatusNotFound {
statusResp.Body.Close()
break // Job completed
}
var status map[string]interface{}
json.NewDecoder(statusResp.Body).Decode(&status)
statusResp.Body.Close()
if status["status"] == "completed" || status["status"] == "failed" {
statusResp.Body.Close()
break
}
}
// Check items
req, _ := http.NewRequest("GET", setup.Server.URL+"/api/libraries/"+libraryID+"/media-items", nil)
req.Header.Set("Authorization", "Bearer "+token)
itemsResp, err := client.Do(req)
require.NoError(t, err)
defer itemsResp.Body.Close()
var itemsResult map[string]interface{}
json.NewDecoder(itemsResp.Body).Decode(&itemsResult)
items, ok := itemsResult["data"].([]interface{})
if !ok || items == nil {
items = []interface{}{} // Handle nil or wrong type
}
assert.GreaterOrEqual(t, len(items), 20, "Should detect all 20 files")
// Cleanup
deleteReq, _ := http.NewRequest("DELETE", setup.Server.URL+"/api/libraries/"+libraryID, nil)
deleteReq.Header.Set("Authorization", "Bearer "+token)
client.Do(deleteReq)
})
}