You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
8.1 KiB
Go
306 lines
8.1 KiB
Go
package tests
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/testcontainers/testcontainers-go"
|
|
"github.com/testcontainers/testcontainers-go/modules/postgres"
|
|
"knowfoolery/backend/shared/auth"
|
|
"knowfoolery/backend/shared/database"
|
|
"knowfoolery/backend/services/game-service/internal/services"
|
|
)
|
|
|
|
// TestEnvironment holds the test environment setup
|
|
type TestEnvironment struct {
|
|
DB *database.Client
|
|
AuthService *auth.MockAuthService
|
|
GameService *services.GameService
|
|
App *fiber.App
|
|
AdminToken string
|
|
PlayerToken string
|
|
ContainerCleanup func()
|
|
}
|
|
|
|
// SetupTestEnvironment creates a test environment with PostgreSQL container
|
|
func SetupTestEnvironment(t *testing.T) *TestEnvironment {
|
|
ctx := context.Background()
|
|
|
|
// Try to start PostgreSQL container (fallback to SQLite if fails)
|
|
var postgresContainer testcontainers.Container
|
|
postgresContainer, err := postgres.RunContainer(ctx,
|
|
testcontainers.WithImage("postgres:15-alpine"),
|
|
postgres.WithDatabase("knowfoolery_test"),
|
|
postgres.WithUsername("test_user"),
|
|
postgres.WithPassword("test_password"),
|
|
)
|
|
if err != nil {
|
|
t.Logf("Failed to start postgres container, using SQLite: %v", err)
|
|
}
|
|
|
|
// Create database client
|
|
dbConfig := database.Config{
|
|
Driver: "postgres",
|
|
Host: "localhost",
|
|
Port: 5432, // Will be overridden by connection string parsing
|
|
User: "test_user",
|
|
Password: "test_password",
|
|
Database: "knowfoolery_test",
|
|
SSLMode: "disable",
|
|
}
|
|
|
|
// For simplicity, create a simple connection (in real implementation, parse connStr)
|
|
db, err := database.NewClient(dbConfig)
|
|
if err != nil {
|
|
// If postgres fails, fallback to SQLite for testing
|
|
t.Logf("PostgreSQL connection failed, using SQLite: %v", err)
|
|
dbConfig = database.Config{
|
|
Driver: "sqlite3",
|
|
Database: ":memory:",
|
|
}
|
|
db, err = database.NewClient(dbConfig)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create database client: %v", err)
|
|
}
|
|
}
|
|
|
|
// Create tables
|
|
if err := db.CreateTables(ctx); err != nil {
|
|
t.Fatalf("Failed to create tables: %v", err)
|
|
}
|
|
|
|
// Initialize services
|
|
authService, err := auth.NewMockAuthService("test-service")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create auth service: %v", err)
|
|
}
|
|
|
|
gameService := services.NewGameService(db)
|
|
|
|
// Create test tokens
|
|
tokens := authService.CreateMockUsers()
|
|
|
|
// Create Fiber app for testing
|
|
app := fiber.New(fiber.Config{
|
|
DisableStartupMessage: true,
|
|
})
|
|
|
|
// Add basic routes for testing
|
|
app.Get("/health", func(c *fiber.Ctx) error {
|
|
return c.JSON(fiber.Map{"status": "healthy"})
|
|
})
|
|
|
|
// Cleanup function
|
|
cleanup := func() {
|
|
db.Close()
|
|
if postgresContainer != nil {
|
|
postgresContainer.Terminate(ctx)
|
|
}
|
|
}
|
|
|
|
return &TestEnvironment{
|
|
DB: db,
|
|
AuthService: authService,
|
|
GameService: gameService,
|
|
App: app,
|
|
AdminToken: tokens["admin"],
|
|
PlayerToken: tokens["player"],
|
|
ContainerCleanup: cleanup,
|
|
}
|
|
}
|
|
|
|
// TestHealthEndpoint tests the health check endpoint
|
|
func TestHealthEndpoint(t *testing.T) {
|
|
env := SetupTestEnvironment(t)
|
|
defer env.ContainerCleanup()
|
|
|
|
// Test health endpoint
|
|
req, err := http.NewRequest("GET", "/health", nil)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create request: %v", err)
|
|
}
|
|
|
|
resp, err := env.App.Test(req)
|
|
if err != nil {
|
|
t.Fatalf("Failed to test request: %v", err)
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
t.Errorf("Expected status 200, got %d", resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
// TestDatabaseConnection tests that we can connect to the database
|
|
func TestDatabaseConnection(t *testing.T) {
|
|
env := SetupTestEnvironment(t)
|
|
defer env.ContainerCleanup()
|
|
|
|
// Test database health
|
|
ctx := context.Background()
|
|
if err := env.DB.Health(ctx); err != nil {
|
|
t.Errorf("Database health check failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestAuthTokenGeneration tests mock authentication token generation
|
|
func TestAuthTokenGeneration(t *testing.T) {
|
|
env := SetupTestEnvironment(t)
|
|
defer env.ContainerCleanup()
|
|
|
|
// Validate admin token
|
|
ctx := context.Background()
|
|
adminClaims, err := env.AuthService.ValidateToken(ctx, env.AdminToken)
|
|
if err != nil {
|
|
t.Errorf("Failed to validate admin token: %v", err)
|
|
}
|
|
|
|
if adminClaims.UserID != "admin-1" {
|
|
t.Errorf("Expected admin user ID 'admin-1', got '%s'", adminClaims.UserID)
|
|
}
|
|
|
|
// Check admin has admin role
|
|
hasAdminRole := false
|
|
for _, role := range adminClaims.Roles {
|
|
if role == "admin" {
|
|
hasAdminRole = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !hasAdminRole {
|
|
t.Error("Admin token should have admin role")
|
|
}
|
|
|
|
// Validate player token
|
|
playerClaims, err := env.AuthService.ValidateToken(ctx, env.PlayerToken)
|
|
if err != nil {
|
|
t.Errorf("Failed to validate player token: %v", err)
|
|
}
|
|
|
|
if playerClaims.UserID != "player-1" {
|
|
t.Errorf("Expected player user ID 'player-1', got '%s'", playerClaims.UserID)
|
|
}
|
|
}
|
|
|
|
// TestGameSessionCreation tests creating a game session
|
|
func TestGameSessionCreation(t *testing.T) {
|
|
env := SetupTestEnvironment(t)
|
|
defer env.ContainerCleanup()
|
|
|
|
ctx := context.Background()
|
|
userID := "test-user-1"
|
|
|
|
// Create a game session
|
|
session, err := env.GameService.CreateSession(ctx, "Test Player", &userID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create session: %v", err)
|
|
}
|
|
|
|
if session.PlayerName != "Test Player" {
|
|
t.Errorf("Expected player name 'Test Player', got '%s'", session.PlayerName)
|
|
}
|
|
|
|
if session.UserID == nil || *session.UserID != userID {
|
|
t.Errorf("Expected user ID '%s', got %v", userID, session.UserID)
|
|
}
|
|
|
|
if session.Status != "active" {
|
|
t.Errorf("Expected status 'active', got '%s'", session.Status)
|
|
}
|
|
|
|
// Retrieve the session
|
|
retrievedSession, err := env.GameService.GetSessionByID(ctx, session.ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to retrieve session: %v", err)
|
|
}
|
|
|
|
if retrievedSession.ID != session.ID {
|
|
t.Errorf("Retrieved session ID doesn't match: expected '%s', got '%s'", session.ID, retrievedSession.ID)
|
|
}
|
|
}
|
|
|
|
// TestRandomQuestionRetrieval tests retrieving random questions
|
|
func TestRandomQuestionRetrieval(t *testing.T) {
|
|
env := SetupTestEnvironment(t)
|
|
defer env.ContainerCleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Note: This test will only work if we have sample data in the database
|
|
// For now, it tests that the function doesn't crash
|
|
question, err := env.GameService.GetRandomQuestion(ctx, "", "")
|
|
|
|
// We expect this to fail since we haven't inserted test data yet
|
|
if err == nil {
|
|
t.Logf("Got random question: %s", question.Text)
|
|
} else {
|
|
t.Logf("No questions found (expected for empty database): %v", err)
|
|
}
|
|
}
|
|
|
|
// TestAPIEndpointsWithAuth tests API endpoints with authentication
|
|
func TestAPIEndpointsWithAuth(t *testing.T) {
|
|
env := SetupTestEnvironment(t)
|
|
defer env.ContainerCleanup()
|
|
|
|
middleware := auth.NewJWTMiddleware(env.AuthService)
|
|
|
|
// Add authenticated route
|
|
api := env.App.Group("/api/v1")
|
|
api.Use(middleware.Optional())
|
|
|
|
api.Get("/test", func(c *fiber.Ctx) error {
|
|
user, err := auth.GetUserFromContext(c)
|
|
if err != nil {
|
|
return c.JSON(fiber.Map{"authenticated": false})
|
|
}
|
|
return c.JSON(fiber.Map{
|
|
"authenticated": true,
|
|
"user": user.Username,
|
|
"roles": user.Roles,
|
|
})
|
|
})
|
|
|
|
// Test without authentication
|
|
req, err := http.NewRequest("GET", "/api/v1/test", nil)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create request: %v", err)
|
|
}
|
|
|
|
resp, err := env.App.Test(req)
|
|
if err != nil {
|
|
t.Fatalf("Failed to test request: %v", err)
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
t.Errorf("Expected status 200, got %d", resp.StatusCode)
|
|
}
|
|
|
|
// Test with authentication
|
|
authReq, err := http.NewRequest("GET", "/api/v1/test", nil)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create authenticated request: %v", err)
|
|
}
|
|
authReq.Header.Set("Authorization", "Bearer "+env.PlayerToken)
|
|
|
|
authResp, err := env.App.Test(authReq)
|
|
if err != nil {
|
|
t.Fatalf("Failed to test authenticated request: %v", err)
|
|
}
|
|
|
|
if authResp.StatusCode != 200 {
|
|
t.Errorf("Expected status 200 for authenticated request, got %d", authResp.StatusCode)
|
|
}
|
|
}
|
|
|
|
// Helper function to parse JSON response
|
|
func parseJSONResponse(t *testing.T, resp *http.Response) map[string]interface{} {
|
|
var result map[string]interface{}
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
t.Fatalf("Failed to parse JSON response: %v", err)
|
|
}
|
|
return result
|
|
} |