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

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