Files
bookhoard/cmd/server/tests/queue_test.go
T
john-okeefe b2a3955c1e fix(tests): complete TestServerSetup migration for remaining test files
Finish migrating all test files to the new TestServerSetup pattern
introduced by the goroutine cleanup refactoring. This resolves all
remaining compilation errors in the test suite.

Changes:
- device_cap_test.go: Fix undefined ts references (7 instances)
  * Replace ts.URL with setup.Server.URL in all test functions
  * Fix URL references in t.Run subtest closures

- queue_test.go: Fix undefined db and helper function issues (5 instances)
  * Replace db.CreateDevice with setup.DB.CreateDevice
  * Fix loginAdminUser() to use ts/db parameters instead of setup
  * Fix loginUserWithID() to use ts parameter instead of setup

- websocket_test.go: Convert 5 tests to new TestServerSetup pattern
  * Replace old pattern (ts, queries, _) with new pattern (setup)
  * Update all resource references to use setup.Server and setup.DB
  * Fix getTestUserID calls to include t parameter

Build Impact:
- All compilation errors resolved
- Integration tests now compile successfully
- No functional changes to test logic

Related: TestServerSetup cleanup pattern (TEST_CLEANUP_PATTERN.md)
2026-02-10 13:24:08 -05:00

277 lines
9.1 KiB
Go

package main
import (
"bookhoard/internal/database"
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestListAllQueueItems_Admin(t *testing.T) {
setup := setupTestServer(t)
token := loginAdminUser(t, setup.Server, setup.DB)
req := httptest.NewRequest("GET", "/api/queue/items", nil)
req.Header.Set("Authorization", "Bearer "+token)
rec := httptest.NewRecorder()
setup.Server.Config.Handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code, "Should list all queue items")
var response []interface{}
json.Unmarshal(rec.Body.Bytes(), &response)
assert.NotNil(t, response, "Should have queue items array")
}
func TestGetDeviceQueueStats(t *testing.T) {
setup := setupTestServer(t)
token := loginTestUser(t, setup.Server, setup.DB)
userID := getTestUserID(t, setup.DB)
deviceToken := fmt.Sprintf("dev_%s", uuid.New().String())
device, err := setup.DB.CreateDevice(context.Background(), database.CreateDeviceParams{
UserID: pgtype.UUID{Bytes: [16]byte(userID), Valid: true},
DeviceName: "Test Device",
DeviceType: "koreader",
DeviceIdentifier: "test-device-123",
AuthToken: deviceToken,
SyncEnabled: pgtype.Bool{Bool: true, Valid: true},
AutoSync: pgtype.Bool{Bool: true, Valid: true},
DeviceMetadata: []byte("{}"),
})
require.NoError(t, err, "Should create device")
deviceIDBytes := device.ID.Bytes[0:16]
deviceID, err := uuid.FromBytes(deviceIDBytes)
require.NoError(t, err, "Should parse device ID")
req := httptest.NewRequest("GET", fmt.Sprintf("/api/queue/devices/%s/stats", deviceID.String()), nil)
req.Header.Set("Authorization", "Bearer "+token)
rec := httptest.NewRecorder()
setup.Server.Config.Handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code, "Should get device queue stats")
var response map[string]interface{}
json.Unmarshal(rec.Body.Bytes(), &response)
assert.Equal(t, deviceID.String(), response["device_id"], "Should match device ID")
}
func TestListDeviceQueueItems(t *testing.T) {
setup := setupTestServer(t)
token := loginTestUser(t, setup.Server, setup.DB)
userID := getTestUserID(t, setup.DB)
deviceToken := fmt.Sprintf("dev_%s", uuid.New().String())
device, err := setup.DB.CreateDevice(context.Background(), database.CreateDeviceParams{
UserID: pgtype.UUID{Bytes: [16]byte(userID), Valid: true},
DeviceName: "Test Device",
DeviceType: "koreader",
DeviceIdentifier: "test-device-123",
AuthToken: deviceToken,
SyncEnabled: pgtype.Bool{Bool: true, Valid: true},
AutoSync: pgtype.Bool{Bool: true, Valid: true},
DeviceMetadata: []byte("{}"),
})
require.NoError(t, err, "Should create device")
deviceIDBytes := device.ID.Bytes[0:16]
deviceID, err := uuid.FromBytes(deviceIDBytes)
require.NoError(t, err, "Should parse device ID")
req := httptest.NewRequest("GET", fmt.Sprintf("/api/queue/devices/%s/items", deviceID.String()), nil)
req.Header.Set("Authorization", "Bearer "+token)
rec := httptest.NewRecorder()
setup.Server.Config.Handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code, "Should list device queue items")
var response []interface{}
json.Unmarshal(rec.Body.Bytes(), &response)
assert.NotNil(t, response, "Should have queue items array")
}
func TestRetryQueueItem(t *testing.T) {
setup := setupTestServer(t)
token := loginTestUser(t, setup.Server, setup.DB)
itemID := uuid.New().String()
req := httptest.NewRequest("POST", fmt.Sprintf("/api/queue/items/%s/retry", itemID), nil)
req.Header.Set("Authorization", "Bearer "+token)
rec := httptest.NewRecorder()
setup.Server.Config.Handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code, "Should retry queue item")
}
func TestDeleteQueueItem(t *testing.T) {
setup := setupTestServer(t)
token := loginTestUser(t, setup.Server, setup.DB)
itemID := uuid.New().String()
req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/queue/items/%s", itemID), nil)
req.Header.Set("Authorization", "Bearer "+token)
rec := httptest.NewRecorder()
setup.Server.Config.Handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code, "Should delete queue item")
}
func TestClearDeviceQueue(t *testing.T) {
setup := setupTestServer(t)
token := loginTestUser(t, setup.Server, setup.DB)
userID := getTestUserID(t, setup.DB)
deviceToken := fmt.Sprintf("dev_%s", uuid.New().String())
device, err := setup.DB.CreateDevice(context.Background(), database.CreateDeviceParams{
UserID: pgtype.UUID{Bytes: [16]byte(userID), Valid: true},
DeviceName: "Test Device",
DeviceType: "koreader",
DeviceIdentifier: "test-device-123",
AuthToken: deviceToken,
SyncEnabled: pgtype.Bool{Bool: true, Valid: true},
AutoSync: pgtype.Bool{Bool: true, Valid: true},
DeviceMetadata: []byte("{}"),
})
require.NoError(t, err, "Should create device")
deviceIDBytes := device.ID.Bytes[0:16]
deviceID, err := uuid.FromBytes(deviceIDBytes)
require.NoError(t, err, "Should parse device ID")
req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/queue/devices/%s/clear", deviceID.String()), nil)
req.Header.Set("Authorization", "Bearer "+token)
rec := httptest.NewRecorder()
setup.Server.Config.Handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code, "Should clear device queue")
}
func TestQueueEndpoints_Unauthorized(t *testing.T) {
setup := setupTestServer(t)
tests := []struct {
name string
method string
endpoint string
body []byte
expectedStatus int
}{
{"ListAllQueueItems", "GET", "/api/queue/items", nil, http.StatusUnauthorized},
{"GetDeviceQueueStats", "GET", "/api/queue/devices/test-device-id/stats", nil, http.StatusUnauthorized},
{"ListDeviceQueueItems", "GET", "/api/queue/devices/test-device-id/items", nil, http.StatusUnauthorized},
{"RetryQueueItem", "POST", "/api/queue/items/test-item-id/retry", nil, http.StatusUnauthorized},
{"DeleteQueueItem", "DELETE", "/api/queue/items/test-item-id", nil, http.StatusUnauthorized},
{"ClearDeviceQueue", "DELETE", "/api/queue/devices/test-device-id/clear", nil, http.StatusUnauthorized},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(tt.method, tt.endpoint, bytes.NewReader(tt.body))
rec := httptest.NewRecorder()
setup.Server.Config.Handler.ServeHTTP(rec, req)
assert.Equal(t, tt.expectedStatus, rec.Code, "Should require authentication")
})
}
}
func loginAdminUser(t *testing.T, ts *httptest.Server, db *database.Queries) string {
passwordHash := "$2a$10$rKvZ.HZx3lLJ6IQCpH1lOukQ/xU8j5cH8mYhPY5YGfXllq5hG8y0Ou"
adminUser, err := db.CreateUser(context.Background(), database.CreateUserParams{
Email: "admin@example.com",
Username: "admin",
PasswordHash: passwordHash,
FirstName: pgtype.Text{String: "Admin", Valid: true},
LastName: pgtype.Text{String: "User", Valid: true},
Role: "admin",
Theme: pgtype.Text{String: "tokyo-night", Valid: true},
})
if err == nil {
userUUID, err := uuid.FromBytes(adminUser.ID.Bytes[:])
require.NoError(t, err, "Should parse admin user UUID")
return loginUserWithID(t, ts, db, userUUID, "admin@example.com", "Test@Pass123!")
}
user, err := db.GetUserByEmail(context.Background(), "admin@example.com")
if err == nil {
userUUID, err := uuid.FromBytes(user.ID.Bytes[:])
require.NoError(t, err, "Should parse admin user UUID")
return loginUserWithID(t, ts, db, userUUID, "admin@example.com", "Test@Pass123!")
}
_, err = db.ListUsers(context.Background())
if err == nil {
return loginTestUser(t, ts, db)
}
loginRequest := map[string]interface{}{
"login": "testuser@example.com",
"password": "Test@Pass123!",
}
body, _ := json.Marshal(loginRequest)
req, _ := http.NewRequest("POST", ts.URL+"/api/auth/login", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err, "Failed to login")
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
token, ok := result["access_token"].(string)
require.True(t, ok, "Should have access_token")
return token
}
func loginUserWithID(t *testing.T, ts *httptest.Server, db *database.Queries, userID uuid.UUID, email, password string) string {
loginRequest := map[string]interface{}{
"login": email,
"password": password,
}
body, _ := json.Marshal(loginRequest)
req, _ := http.NewRequest("POST", ts.URL+"/api/auth/login", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err, "Failed to login")
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode, "Login should succeed")
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
token, ok := result["access_token"].(string)
require.True(t, ok, "Should have access_token")
require.NotEmpty(t, token, "Access token should not be empty")
return token
}