test: update test helpers to use router package

Changes to test_helpers.go:
- Import router package and use router.RegisterRoutes()
- Create all necessary handlers (auth, device, koreader, ws, conflict, analytics, queue, opds)
- Add proper validator setup
- Add CustomValidator type
- Remove unused pgtype import

This makes integration tests use the same router configuration as production,
ensuring tests cover the actual API behavior and route structure.
This commit is contained in:
2026-02-06 13:37:23 -05:00
parent f13c2d683a
commit 014047a1e3
+69 -26
View File
@@ -4,8 +4,11 @@ import (
"bookhoard/internal/config"
"bookhoard/internal/database"
"bookhoard/internal/handlers"
"bookhoard/internal/middleware"
ratelimit "bookhoard/internal/middleware"
wsync "bookhoard/internal/sync"
"bookhoard/internal/router"
"bookhoard/internal/services"
"bookhoard/internal/sync"
"bytes"
"context"
"encoding/json"
@@ -16,6 +19,7 @@ import (
"testing"
"time"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgxpool"
@@ -24,6 +28,15 @@ import (
"github.com/stretchr/testify/require"
)
// CustomValidator wraps the go-playground validator
type CustomValidator struct {
validator *validator.Validate
}
func (cv *CustomValidator) Validate(i interface{}) error {
return cv.validator.Struct(i)
}
// Helper functions for testing
func containsPrefix(s, prefix string) bool {
return len(s) >= len(prefix) && s[:len(prefix)] == prefix
@@ -111,41 +124,71 @@ func setupTestServer(t *testing.T) (*httptest.Server, *database.Queries, *config
// Create handlers
authHandler := handlers.NewAuthHandler(queries, cfg.JWTSecret, loginAttemptTracker)
libraryHandler := handlers.NewLibraryHandler(queries)
deviceHandler := handlers.NewDeviceHandler(queries, cfg.JWTSecret, cfg)
deviceAuthMiddleware := middleware.NewDeviceAuthMiddleware(queries)
// Create WebSocket connection manager for testing
connManager := wsync.NewConnectionManager()
// Create WebSocket connection manager
connManager := sync.NewConnectionManager()
connManager.StartCleanupTask()
// Create sync queue processor
queueProcessor := sync.NewSyncQueueProcessor(queries)
go queueProcessor.Start(context.Background())
koreaderHandler := handlers.NewKOReaderHandler(queries, connManager, queueProcessor)
wsHandler := handlers.NewWSHandler(queries, connManager, cfg.JWTSecret, deviceAuthMiddleware)
conflictHandler := handlers.NewConflictHandler(queries, connManager)
analyticsHandler := handlers.NewAnalyticsHandler(queries)
queueHandler := handlers.NewQueueHandler(queries, queueProcessor)
// Create conversion service for OPDS
conversionService := services.NewConversionService(queries, "/var/bookhoard/cache/kepub")
opdsHandler := handlers.NewOPDSHandler(queries, conversionService)
// Create Echo instance
e := echo.New()
// Set up validator
v := validator.New()
if err := ratelimit.RegisterPasswordValidation(v); err != nil {
t.Fatal("Failed to register password validator:", err)
}
e.Validator = &CustomValidator{validator: v}
// Middleware
e.Use(echomiddleware.Logger())
e.Use(echomiddleware.Recover())
e.Use(echomiddleware.CORS())
// Setup routes
// Setup routes using router package
routerConfig := &router.Config{
Echo: e,
Queries: queries,
Cfg: cfg,
DBPool: dbPool,
AuthHandler: authHandler,
LibraryHandler: libraryHandler,
DeviceHandler: deviceHandler,
KOReaderHandler: koreaderHandler,
WSHandler: wsHandler,
ConflictHandler: conflictHandler,
AnalyticsHandler: analyticsHandler,
QueueHandler: queueHandler,
CollectionHandler: nil, // Not needed for tests
OPDSHandler: opdsHandler,
ConnManager: connManager,
QueueProcessor: queueProcessor,
DeviceAuthMiddleware: deviceAuthMiddleware,
LoginTracker: loginAttemptTracker,
}
router.RegisterRoutes(routerConfig)
// Setup ebook handler routes (for testing)
protected := e.Group("/api")
h := handlers.SetupRoutes(protected, queries, connManager)
// Device management routes (public - for registration)
e.POST("/api/devices/register", deviceHandler.InitiateRegistration)
e.POST("/api/devices/register/status", deviceHandler.CheckRegistrationStatus)
// Device management routes (protected - require user auth)
devices := protected.Group("/devices")
devices.GET("", deviceHandler.ListDevices)
devices.GET("/:id", deviceHandler.GetDevice)
devices.PUT("/:id", deviceHandler.UpdateDevice)
devices.DELETE("/:id", deviceHandler.DeleteDevice)
devices.GET("/pending", deviceHandler.ListPendingRegistrations)
devices.GET("/approve/:registration_id", deviceHandler.ApproveDevice)
devices.POST("/reject/:registration_id", deviceHandler.RejectDevice)
// Auth routes (public - for testing)
e.POST("/api/auth/register", authHandler.Register)
e.POST("/api/auth/login", authHandler.Login)
// Create test server
ts := httptest.NewServer(e)
@@ -160,7 +203,7 @@ func loginTestUser(t *testing.T, ts *httptest.Server, db *database.Queries) stri
loginRequest := map[string]interface{}{
"login": "testuser@example.com",
"password": "Test@Pass123!",
"password": "TestPass123!",
}
body, _ := json.Marshal(loginRequest)
@@ -195,9 +238,9 @@ func getTestUserID(t *testing.T, db *database.Queries) uuid.UUID {
}
// If user doesn't exist, create one with a valid password
// Password: "TestPass123!" meets complexity requirements
// This is the bcrypt hash for "TestPass123!"
passwordHash := "$2a$10$rKvZ.HZx3lLJ6IQCpH1lOukQ/xU8j5cH8mYhPY5YGfXllq5hG8y0Ou"
// Password: "Test@Pass123!" meets complexity requirements
// This is the bcrypt hash for "Test@Pass123!"
passwordHash := "$2a$10$vYI7j2zvH3vBmGHXqKbqMe.8hKqJVYOvQKHh8fPJWGjVPKpXzGvMqG"
newUser, err := db.CreateUser(context.Background(), database.CreateUserParams{
Email: "testuser@example.com",