package auth import ( "encoding/json" "io" "net/http/httptest" "testing" "github.com/gofiber/fiber/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func setupTestApp() (*fiber.App, *JWTMiddleware, *MockAuthService) { app := fiber.New() authService, _ := NewMockAuthService("test-service") middleware := NewJWTMiddleware(authService) return app, middleware, authService } func TestJWTMiddleware_Authenticate(t *testing.T) { app, middleware, authService := setupTestApp() // Generate a valid token validToken, err := authService.GenerateToken("user123", "testuser", "test@example.com", []string{"player"}) require.NoError(t, err) app.Get("/protected", middleware.Authenticate(), func(c *fiber.Ctx) error { user, _ := GetUserFromContext(c) return c.JSON(fiber.Map{"user": user.Username}) }) t.Run("Allows request with valid token", func(t *testing.T) { req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set(AuthorizationHeader, "Bearer "+validToken) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) var response map[string]interface{} err = json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, "testuser", response["user"]) }) t.Run("Rejects request without Authorization header", func(t *testing.T) { req := httptest.NewRequest("GET", "/protected", nil) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, StatusUnauthorized, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) var response map[string]interface{} err = json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, ErrAuthHeaderRequired, response["error"]) }) t.Run("Rejects request with invalid Authorization header format", func(t *testing.T) { req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set(AuthorizationHeader, "Token "+validToken) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, StatusUnauthorized, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) var response map[string]interface{} err = json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, ErrInvalidAuthHeaderFormat, response["error"]) }) t.Run("Rejects request with invalid token", func(t *testing.T) { req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set(AuthorizationHeader, "Bearer invalid-token") resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, StatusUnauthorized, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) var response map[string]interface{} err = json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, ErrInvalidToken, response["error"]) assert.NotNil(t, response["details"]) }) } func TestJWTMiddleware_RequireRole(t *testing.T) { app, middleware, authService := setupTestApp() // Generate tokens with different roles adminToken, err := authService.GenerateToken("admin1", "admin", "admin@test.com", []string{"admin", "player"}) require.NoError(t, err) playerToken, err := authService.GenerateToken("player1", "player", "player@test.com", []string{"player"}) require.NoError(t, err) app.Get("/admin", middleware.Authenticate(), middleware.RequireRole("admin"), func(c *fiber.Ctx) error { return c.JSON(fiber.Map{"message": "admin access"}) }) t.Run("Allows access when user has required role", func(t *testing.T) { req := httptest.NewRequest("GET", "/admin", nil) req.Header.Set(AuthorizationHeader, "Bearer "+adminToken) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) }) t.Run("Denies access when user doesn't have required role", func(t *testing.T) { req := httptest.NewRequest("GET", "/admin", nil) req.Header.Set(AuthorizationHeader, "Bearer "+playerToken) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, StatusForbidden, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) var response map[string]interface{} err = json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, ErrInsufficientPermissions, response["error"]) assert.Equal(t, "admin", response["required_role"]) assert.Contains(t, response["user_roles"], "player") }) } func TestJWTMiddleware_RequireAnyRole(t *testing.T) { app, middleware, authService := setupTestApp() // Generate tokens with different roles adminToken, err := authService.GenerateToken("admin1", "admin", "admin@test.com", []string{"admin"}) require.NoError(t, err) playerToken, err := authService.GenerateToken("player1", "player", "player@test.com", []string{"player"}) require.NoError(t, err) guestToken, err := authService.GenerateToken("guest1", "guest", "guest@test.com", []string{"guest"}) require.NoError(t, err) app.Get("/restricted", middleware.Authenticate(), middleware.RequireAnyRole("admin", "player"), func(c *fiber.Ctx) error { return c.JSON(fiber.Map{"message": "access granted"}) }) t.Run("Allows access when user has admin role", func(t *testing.T) { req := httptest.NewRequest("GET", "/restricted", nil) req.Header.Set(AuthorizationHeader, "Bearer "+adminToken) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) }) t.Run("Allows access when user has player role", func(t *testing.T) { req := httptest.NewRequest("GET", "/restricted", nil) req.Header.Set(AuthorizationHeader, "Bearer "+playerToken) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) }) t.Run("Denies access when user doesn't have any required role", func(t *testing.T) { req := httptest.NewRequest("GET", "/restricted", nil) req.Header.Set(AuthorizationHeader, "Bearer "+guestToken) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, StatusForbidden, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) var response map[string]interface{} err = json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, ErrInsufficientPermissions, response["error"]) assert.Contains(t, response["required_roles"], "admin") assert.Contains(t, response["required_roles"], "player") assert.Contains(t, response["user_roles"], "guest") }) } func TestJWTMiddleware_Optional(t *testing.T) { app, middleware, authService := setupTestApp() // Generate a valid token validToken, err := authService.GenerateToken("user123", "testuser", "test@example.com", []string{"player"}) require.NoError(t, err) app.Get("/public", middleware.Optional(), func(c *fiber.Ctx) error { if IsAuthenticated(c) { user, _ := GetUserFromContext(c) return c.JSON(fiber.Map{"authenticated": true, "user": user.Username}) } return c.JSON(fiber.Map{"authenticated": false, "user": "anonymous"}) }) t.Run("Sets user context when valid token is provided", func(t *testing.T) { req := httptest.NewRequest("GET", "/public", nil) req.Header.Set(AuthorizationHeader, "Bearer "+validToken) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) var response map[string]interface{} err = json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, true, response["authenticated"]) assert.Equal(t, "testuser", response["user"]) }) t.Run("Allows request without token", func(t *testing.T) { req := httptest.NewRequest("GET", "/public", nil) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) var response map[string]interface{} err = json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, false, response["authenticated"]) assert.Equal(t, "anonymous", response["user"]) }) t.Run("Allows request with invalid token format", func(t *testing.T) { req := httptest.NewRequest("GET", "/public", nil) req.Header.Set(AuthorizationHeader, "Token invalid-format") resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) var response map[string]interface{} err = json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, false, response["authenticated"]) assert.Equal(t, "anonymous", response["user"]) }) t.Run("Allows request with invalid token", func(t *testing.T) { req := httptest.NewRequest("GET", "/public", nil) req.Header.Set(AuthorizationHeader, "Bearer invalid-token") resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) var response map[string]interface{} err = json.Unmarshal(body, &response) require.NoError(t, err) assert.Equal(t, false, response["authenticated"]) assert.Equal(t, "anonymous", response["user"]) }) } func TestNewJWTMiddleware(t *testing.T) { t.Run("Creates new JWT middleware", func(t *testing.T) { authService, err := NewMockAuthService("test-service") require.NoError(t, err) middleware := NewJWTMiddleware(authService) assert.NotNil(t, middleware) assert.Equal(t, authService, middleware.authService) }) }