Files
bookhoard/cmd/server/tests/scan_settings_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

183 lines
5.7 KiB
Go

package main
import (
"bytes"
"encoding/json"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestScanSettings_GetSettings(t *testing.T) {
setup := setupTestServer(t)
t.Run("Get settings as admin", func(t *testing.T) {
token := setup.Token
req, _ := http.NewRequest("GET", setup.Server.URL+"/api/libraries/scan-settings", nil)
req.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
var response map[string]interface{}
json.NewDecoder(resp.Body).Decode(&response)
resp.Body.Close()
assert.Contains(t, response, "scan_poll_interval_seconds")
assert.Contains(t, response, "auto_scan_enabled")
// Validate scan_poll_interval_seconds is present and valid (don't hardcode value)
interval, ok := response["scan_poll_interval_seconds"].(float64)
require.True(t, ok, "scan_poll_interval_seconds must be a number")
assert.Greater(t, interval, float64(0), "scan_poll_interval_seconds must be positive")
// Validate auto_scan_enabled is present and a boolean
autoScan, ok := response["auto_scan_enabled"].(bool)
require.True(t, ok, "auto_scan_enabled must be a boolean")
assert.NotNil(t, autoScan, "auto_scan_enabled must not be nil")
})
}
func TestScanSettings_UpdateSettings(t *testing.T) {
setup := setupTestServer(t)
t.Run("Update scan_poll_interval_seconds", func(t *testing.T) {
token := setup.Token
updateReq := map[string]interface{}{
"scan_poll_interval_seconds": 45,
"auto_scan_enabled": true,
}
body, _ := json.Marshal(updateReq)
req, _ := http.NewRequest("PUT", setup.Server.URL+"/api/libraries/scan-settings", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
var response map[string]interface{}
json.NewDecoder(resp.Body).Decode(&response)
resp.Body.Close()
assert.Equal(t, float64(45), response["scan_poll_interval_seconds"])
assert.Equal(t, true, response["auto_scan_enabled"])
getReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/libraries/scan-settings", nil)
getReq.Header.Set("Authorization", "Bearer "+token)
getResp, _ := client.Do(getReq)
var getResponse map[string]interface{}
json.NewDecoder(getResp.Body).Decode(&getResponse)
getResp.Body.Close()
assert.Equal(t, float64(45), getResponse["scan_poll_interval_seconds"])
})
t.Run("Update auto_scan_enabled to false", func(t *testing.T) {
token := setup.Token
updateReq := map[string]interface{}{
"scan_poll_interval_seconds": 30,
"auto_scan_enabled": false,
}
body, _ := json.Marshal(updateReq)
req, _ := http.NewRequest("PUT", setup.Server.URL+"/api/libraries/scan-settings", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
var response map[string]interface{}
json.NewDecoder(resp.Body).Decode(&response)
resp.Body.Close()
assert.Equal(t, false, response["auto_scan_enabled"])
})
t.Run("Validation rejects values below minimum", func(t *testing.T) {
token := setup.Token
updateReq := map[string]interface{}{
"scan_poll_interval_seconds": 0,
"auto_scan_enabled": true,
}
body, _ := json.Marshal(updateReq)
req, _ := http.NewRequest("PUT", setup.Server.URL+"/api/libraries/scan-settings", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
t.Run("Validation rejects values above maximum", func(t *testing.T) {
token := setup.Token
updateReq := map[string]interface{}{
"scan_poll_interval_seconds": 4000,
"auto_scan_enabled": true,
}
body, _ := json.Marshal(updateReq)
req, _ := http.NewRequest("PUT", setup.Server.URL+"/api/libraries/scan-settings", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
})
}
func TestScanSettings_RequireAdmin(t *testing.T) {
setup := setupTestServer(t)
t.Run("Get settings without auth returns 401", func(t *testing.T) {
req, _ := http.NewRequest("GET", setup.Server.URL+"/api/libraries/scan-settings", nil)
client := &http.Client{}
resp, _ := client.Do(req)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("Get settings as non-admin returns 403", func(t *testing.T) {
token := setup.RegularToken
req, _ := http.NewRequest("GET", setup.Server.URL+"/api/libraries/scan-settings", nil)
req.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
resp, _ := client.Do(req)
assert.Equal(t, http.StatusForbidden, resp.StatusCode)
})
t.Run("Update settings without auth returns 401", func(t *testing.T) {
updateReq := map[string]interface{}{
"scan_poll_interval_seconds": 30,
"auto_scan_enabled": true,
}
body, _ := json.Marshal(updateReq)
req, _ := http.NewRequest("PUT", setup.Server.URL+"/api/libraries/scan-settings", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}