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.

321 lines
9.4 KiB
Go

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)
})
}