- Remove manual device ID generation, use database-generated IDs - Add comprehensive test helpers (setupTestServer, loginTestUser, getTestUserID) - Add cleanup step for existing test users in integration tests - Fix UUID parsing from database responses
162 lines
5.0 KiB
Go
162 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"bookmann/internal/config"
|
|
"bookmann/internal/database"
|
|
"bookmann/internal/handlers"
|
|
ratelimit "bookmann/internal/middleware"
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"github.com/labstack/echo/v4"
|
|
echomiddleware "github.com/labstack/echo/v4/middleware"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Helper functions for testing
|
|
func containsPrefix(s, prefix string) bool {
|
|
return len(s) >= len(prefix) && s[:len(prefix)] == prefix
|
|
}
|
|
|
|
func contains(s, substr string) bool {
|
|
return strings.Contains(s, substr)
|
|
}
|
|
|
|
func trimSpace(s string) string {
|
|
return strings.TrimSpace(s)
|
|
}
|
|
|
|
// setupTestServer creates a test server with a test database
|
|
// Returns: (*httptest.Server, *database.Queries, *config.Config, *handlers.Handler)
|
|
func setupTestServer(t *testing.T) (*httptest.Server, *database.Queries, *config.Config, *handlers.Handler) {
|
|
// Load test configuration
|
|
cfg := &config.Config{
|
|
ServerPort: "0", // Use random port for tests
|
|
BaseURL: "http://localhost",
|
|
DatabaseHost: "localhost",
|
|
DatabasePort: "5432",
|
|
DatabaseUser: "postgres",
|
|
DatabasePassword: "password",
|
|
DatabaseName: "bookmann",
|
|
JWTSecret: "test-secret-key",
|
|
UploadPath: "./test-uploads",
|
|
TestMode: true,
|
|
RateLimitEnabled: false,
|
|
RequestsPerMinute: 1000,
|
|
}
|
|
|
|
// Connect to test database
|
|
dbPool, err := pgxpool.New(context.Background(), cfg.DatabaseURL())
|
|
require.NoError(t, err, "Failed to connect to test database")
|
|
|
|
queries := database.New(dbPool)
|
|
|
|
// Create login attempt tracker
|
|
loginAttemptTracker := ratelimit.NewLoginAttemptTracker(5, 15*time.Minute, 5*time.Minute)
|
|
|
|
// Create handlers
|
|
authHandler := handlers.NewAuthHandler(queries, cfg.JWTSecret, loginAttemptTracker)
|
|
deviceHandler := handlers.NewDeviceHandler(queries, cfg.JWTSecret, cfg)
|
|
|
|
// Create Echo instance
|
|
e := echo.New()
|
|
|
|
// Middleware
|
|
e.Use(echomiddleware.Logger())
|
|
e.Use(echomiddleware.Recover())
|
|
e.Use(echomiddleware.CORS())
|
|
|
|
// Setup routes
|
|
protected := e.Group("/api")
|
|
h := handlers.SetupRoutes(protected, queries)
|
|
|
|
// Device management routes (public - for registration)
|
|
e.POST("/api/devices/register", deviceHandler.InitiateRegistration)
|
|
e.POST("/api/devices/register/status", deviceHandler.CheckRegistrationStatus)
|
|
e.GET("/devices/approve/:registration_id", deviceHandler.ApproveDevice)
|
|
e.POST("/devices/reject/:registration_id", deviceHandler.RejectDevice)
|
|
|
|
// 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)
|
|
|
|
// 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)
|
|
|
|
// Return server, queries, config, and handler
|
|
return ts, queries, cfg, h
|
|
}
|
|
|
|
// loginTestUser logs in a test user and returns the JWT token
|
|
func loginTestUser(t *testing.T, ts *httptest.Server) string {
|
|
loginRequest := map[string]interface{}{
|
|
"login": "testuser@example.com",
|
|
"password": "testpass123",
|
|
}
|
|
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 test user")
|
|
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, "Response should contain access_token")
|
|
require.NotEmpty(t, token, "Token should not be empty")
|
|
|
|
return token
|
|
}
|
|
|
|
// getTestUserID retrieves the test user ID from the database
|
|
func getTestUserID(t *testing.T, db *database.Queries) uuid.UUID {
|
|
// Try to get the test user by email
|
|
user, err := db.GetUserByEmail(context.Background(), "testuser@example.com")
|
|
if err == nil {
|
|
userUUID, err := uuid.FromBytes(user.ID.Bytes[:])
|
|
require.NoError(t, err, "Failed to parse user UUID")
|
|
return userUUID
|
|
}
|
|
|
|
// If user doesn't exist, create one
|
|
passwordHash := "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy" // "testpass123" hashed
|
|
|
|
newUser, err := db.CreateUser(context.Background(), database.CreateUserParams{
|
|
Email: "testuser@example.com",
|
|
Username: "testuser",
|
|
PasswordHash: passwordHash,
|
|
FirstName: pgtype.Text{String: "Test", Valid: true},
|
|
LastName: pgtype.Text{String: "User", Valid: true},
|
|
Role: "user",
|
|
})
|
|
require.NoError(t, err, "Failed to create test user")
|
|
|
|
userUUID, err := uuid.FromBytes(newUser.ID.Bytes[:])
|
|
require.NoError(t, err, "Failed to parse user UUID")
|
|
return userUUID
|
|
}
|