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.
275 lines
8.6 KiB
Go
275 lines
8.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNewMockAuthService(t *testing.T) {
|
|
t.Run("Creates new mock auth service", func(t *testing.T) {
|
|
service, err := NewMockAuthService("test-service")
|
|
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, service)
|
|
assert.Equal(t, "test-service", service.issuer)
|
|
assert.Len(t, service.secretKey, 32)
|
|
})
|
|
}
|
|
|
|
func TestMockAuthService_GenerateToken(t *testing.T) {
|
|
service, err := NewMockAuthService("test-service")
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Generates valid JWT token", func(t *testing.T) {
|
|
userID := "user123"
|
|
username := "testuser"
|
|
email := "test@example.com"
|
|
roles := []string{"player", "admin"}
|
|
|
|
tokenString, err := service.GenerateToken(userID, username, email, roles)
|
|
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, tokenString)
|
|
|
|
// Verify the token has the expected structure
|
|
parts := strings.Split(tokenString, ".")
|
|
assert.Len(t, parts, 3) // header.payload.signature
|
|
})
|
|
|
|
t.Run("Generated token contains correct claims", func(t *testing.T) {
|
|
userID := "user123"
|
|
username := "testuser"
|
|
email := "test@example.com"
|
|
roles := []string{"player"}
|
|
|
|
tokenString, err := service.GenerateToken(userID, username, email, roles)
|
|
require.NoError(t, err)
|
|
|
|
// Parse and verify claims
|
|
claims, err := service.ValidateToken(context.Background(), tokenString)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, userID, claims.UserID)
|
|
assert.Equal(t, username, claims.Username)
|
|
assert.Equal(t, email, claims.Email)
|
|
assert.Equal(t, roles, claims.Roles)
|
|
assert.Equal(t, "test-service", claims.Issuer)
|
|
assert.Equal(t, userID, claims.Subject)
|
|
assert.Contains(t, claims.Audience, "knowfoolery")
|
|
})
|
|
|
|
t.Run("Generated token expires in 24 hours", func(t *testing.T) {
|
|
tokenString, err := service.GenerateToken("user1", "test", "test@example.com", []string{"player"})
|
|
require.NoError(t, err)
|
|
|
|
claims, err := service.ValidateToken(context.Background(), tokenString)
|
|
require.NoError(t, err)
|
|
|
|
now := time.Now()
|
|
expected := now.Add(24 * time.Hour)
|
|
|
|
// Allow for some time drift (within 1 minute)
|
|
assert.WithinDuration(t, expected, claims.ExpiresAt.Time, time.Minute)
|
|
})
|
|
|
|
t.Run("Generates different tokens for different users", func(t *testing.T) {
|
|
token1, err1 := service.GenerateToken("user1", "test1", "test1@example.com", []string{"player"})
|
|
token2, err2 := service.GenerateToken("user2", "test2", "test2@example.com", []string{"admin"})
|
|
|
|
require.NoError(t, err1)
|
|
require.NoError(t, err2)
|
|
assert.NotEqual(t, token1, token2)
|
|
})
|
|
}
|
|
|
|
func TestMockAuthService_ValidateToken(t *testing.T) {
|
|
service, err := NewMockAuthService("test-service")
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Validates correct token successfully", func(t *testing.T) {
|
|
userID := "user123"
|
|
username := "testuser"
|
|
email := "test@example.com"
|
|
roles := []string{"player", "admin"}
|
|
|
|
tokenString, err := service.GenerateToken(userID, username, email, roles)
|
|
require.NoError(t, err)
|
|
|
|
claims, err := service.ValidateToken(context.Background(), tokenString)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, userID, claims.UserID)
|
|
assert.Equal(t, username, claims.Username)
|
|
assert.Equal(t, email, claims.Email)
|
|
assert.Equal(t, roles, claims.Roles)
|
|
})
|
|
|
|
t.Run("Rejects malformed token", func(t *testing.T) {
|
|
claims, err := service.ValidateToken(context.Background(), "invalid.token.format")
|
|
|
|
require.Error(t, err)
|
|
assert.Nil(t, claims)
|
|
assert.Contains(t, err.Error(), "failed to parse token")
|
|
})
|
|
|
|
t.Run("Rejects token with wrong signature", func(t *testing.T) {
|
|
// Create token with different service (different secret)
|
|
otherService, err := NewMockAuthService("other-service")
|
|
require.NoError(t, err)
|
|
|
|
tokenString, err := otherService.GenerateToken("user1", "test", "test@example.com", []string{"player"})
|
|
require.NoError(t, err)
|
|
|
|
// Try to validate with original service
|
|
claims, err := service.ValidateToken(context.Background(), tokenString)
|
|
|
|
require.Error(t, err)
|
|
assert.Nil(t, claims)
|
|
assert.Contains(t, err.Error(), "failed to parse token")
|
|
})
|
|
|
|
t.Run("Rejects expired token", func(t *testing.T) {
|
|
// Create a token that's already expired
|
|
now := time.Now()
|
|
pastTime := now.Add(-25 * time.Hour) // Expired 1 hour ago
|
|
|
|
claims := Claims{
|
|
UserID: "user123",
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
Roles: []string{"player"},
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
Issuer: service.issuer,
|
|
Subject: "user123",
|
|
Audience: []string{"knowfoolery"},
|
|
ExpiresAt: jwt.NewNumericDate(pastTime),
|
|
NotBefore: jwt.NewNumericDate(pastTime.Add(-24 * time.Hour)),
|
|
IssuedAt: jwt.NewNumericDate(pastTime.Add(-24 * time.Hour)),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, err := token.SignedString(service.secretKey)
|
|
require.NoError(t, err)
|
|
|
|
// Try to validate expired token
|
|
validClaims, err := service.ValidateToken(context.Background(), tokenString)
|
|
|
|
require.Error(t, err)
|
|
assert.Nil(t, validClaims)
|
|
assert.Contains(t, err.Error(), "failed to parse token")
|
|
})
|
|
|
|
t.Run("Rejects token with wrong signing method", func(t *testing.T) {
|
|
// We can't easily create a token with wrong signing method without proper keys,
|
|
// so just create a malformed token to test the validation
|
|
malformedToken := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.INVALID"
|
|
|
|
validClaims, err := service.ValidateToken(context.Background(), malformedToken)
|
|
|
|
require.Error(t, err)
|
|
assert.Nil(t, validClaims)
|
|
})
|
|
}
|
|
|
|
func TestMockAuthService_CreateMockUsers(t *testing.T) {
|
|
service, err := NewMockAuthService("test-service")
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Creates mock user tokens", func(t *testing.T) {
|
|
tokens := service.CreateMockUsers()
|
|
|
|
assert.Len(t, tokens, 3)
|
|
assert.Contains(t, tokens, "admin")
|
|
assert.Contains(t, tokens, "player")
|
|
assert.Contains(t, tokens, "player2")
|
|
|
|
// Verify each token is valid
|
|
for userType, tokenString := range tokens {
|
|
claims, err := service.ValidateToken(context.Background(), tokenString)
|
|
require.NoError(t, err, "Token for %s should be valid", userType)
|
|
assert.NotEmpty(t, claims.UserID)
|
|
assert.NotEmpty(t, claims.Username)
|
|
assert.NotEmpty(t, claims.Email)
|
|
assert.NotEmpty(t, claims.Roles)
|
|
}
|
|
})
|
|
|
|
t.Run("Admin token has admin role", func(t *testing.T) {
|
|
tokens := service.CreateMockUsers()
|
|
adminToken := tokens["admin"]
|
|
|
|
claims, err := service.ValidateToken(context.Background(), adminToken)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, claims.Roles, "admin")
|
|
assert.Contains(t, claims.Roles, "player")
|
|
assert.Equal(t, "admin", claims.Username)
|
|
})
|
|
|
|
t.Run("Player tokens have player role", func(t *testing.T) {
|
|
tokens := service.CreateMockUsers()
|
|
|
|
for _, playerKey := range []string{"player", "player2"} {
|
|
playerToken := tokens[playerKey]
|
|
claims, err := service.ValidateToken(context.Background(), playerToken)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, claims.Roles, "player")
|
|
assert.NotContains(t, claims.Roles, "admin")
|
|
}
|
|
})
|
|
|
|
t.Run("Creates different tokens each time", func(t *testing.T) {
|
|
tokens1 := service.CreateMockUsers()
|
|
time.Sleep(time.Second + time.Millisecond) // Ensure different issued-at time
|
|
tokens2 := service.CreateMockUsers()
|
|
|
|
for userType := range tokens1 {
|
|
assert.NotEqual(t, tokens1[userType], tokens2[userType],
|
|
"Tokens for %s should be different between calls", userType)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMockAuthService_GetSecretHex(t *testing.T) {
|
|
service, err := NewMockAuthService("test-service")
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Returns hex encoded secret key", func(t *testing.T) {
|
|
hexSecret := service.GetSecretHex()
|
|
|
|
assert.NotEmpty(t, hexSecret)
|
|
assert.Len(t, hexSecret, 64) // 32 bytes * 2 hex chars per byte
|
|
|
|
// Verify it's valid hex
|
|
for _, char := range hexSecret {
|
|
assert.True(t,
|
|
(char >= '0' && char <= '9') || (char >= 'a' && char <= 'f') || (char >= 'A' && char <= 'F'),
|
|
"Character %c should be valid hex", char)
|
|
}
|
|
})
|
|
|
|
t.Run("Returns consistent secret key", func(t *testing.T) {
|
|
hex1 := service.GetSecretHex()
|
|
hex2 := service.GetSecretHex()
|
|
|
|
assert.Equal(t, hex1, hex2, "Secret key should be consistent")
|
|
})
|
|
|
|
t.Run("Different services have different secrets", func(t *testing.T) {
|
|
service2, err := NewMockAuthService("other-service")
|
|
require.NoError(t, err)
|
|
|
|
hex1 := service.GetSecretHex()
|
|
hex2 := service2.GetSecretHex()
|
|
|
|
assert.NotEqual(t, hex1, hex2, "Different services should have different secrets")
|
|
})
|
|
} |