package auth import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGeneratePair_ReturnsNonEmptyTokens(t *testing.T) { tm := NewTokenManager("test-secret-key", 15*time.Minute, 7*24*time.Hour) access, refresh, err := tm.GeneratePair("user-123", "alice") require.NoError(t, err) assert.NotEmpty(t, access) assert.NotEmpty(t, refresh) assert.NotEqual(t, access, refresh) } func TestValidateAccess_ValidToken(t *testing.T) { tm := NewTokenManager("test-secret-key", 15*time.Minute, 7*24*time.Hour) access, _, err := tm.GeneratePair("user-123", "alice") require.NoError(t, err) claims, err := tm.ValidateAccess(access) require.NoError(t, err) assert.Equal(t, "user-123", claims.UserID) assert.Equal(t, "alice", claims.Username) assert.Equal(t, "camtalk", claims.Issuer) } func TestValidateRefresh_ValidToken(t *testing.T) { tm := NewTokenManager("test-secret-key", 15*time.Minute, 7*24*time.Hour) _, refresh, err := tm.GeneratePair("user-123", "alice") require.NoError(t, err) claims, err := tm.ValidateRefresh(refresh) require.NoError(t, err) assert.Equal(t, "user-123", claims.UserID) assert.Equal(t, "alice", claims.Username) assert.NotEmpty(t, claims.ID) // refresh token 应含唯一 ID } func TestValidateAccess_ExpiredToken(t *testing.T) { // 使用极短的 TTL tm := NewTokenManager("test-secret-key", -1*time.Second, -1*time.Second) access, _, err := tm.GeneratePair("user-123", "alice") require.NoError(t, err) _, err = tm.ValidateAccess(access) assert.ErrorIs(t, err, ErrInvalidToken) } func TestValidateAccess_WrongSecret(t *testing.T) { tm1 := NewTokenManager("secret-1", 15*time.Minute, 7*24*time.Hour) tm2 := NewTokenManager("secret-2", 15*time.Minute, 7*24*time.Hour) access, _, err := tm1.GeneratePair("user-123", "alice") require.NoError(t, err) _, err = tm2.ValidateAccess(access) assert.ErrorIs(t, err, ErrInvalidToken) } func TestValidateAccess_InvalidFormat(t *testing.T) { tm := NewTokenManager("test-secret-key", 15*time.Minute, 7*24*time.Hour) _, err := tm.ValidateAccess("not-a-valid-token") assert.ErrorIs(t, err, ErrInvalidToken) } func TestValidateAccess_EmptyString(t *testing.T) { tm := NewTokenManager("test-secret-key", 15*time.Minute, 7*24*time.Hour) _, err := tm.ValidateAccess("") assert.ErrorIs(t, err, ErrInvalidToken) } func TestHashToken_Deterministic(t *testing.T) { hash1 := HashToken("some-token-value") hash2 := HashToken("some-token-value") assert.Equal(t, hash1, hash2) assert.Len(t, hash1, 64) // SHA256 hex = 64 chars } func TestHashToken_DifferentInputsDifferentHashes(t *testing.T) { hash1 := HashToken("token-a") hash2 := HashToken("token-b") assert.NotEqual(t, hash1, hash2) } func TestValidateRefresh_ExpiredToken(t *testing.T) { tm := NewTokenManager("test-secret-key", -1*time.Minute, -1*time.Minute) _, refresh, err := tm.GeneratePair("user-123", "alice") require.NoError(t, err) _, err = tm.ValidateRefresh(refresh) assert.ErrorIs(t, err, ErrInvalidToken) } func TestGeneratePair_TokenClaimsContainCorrectExpiry(t *testing.T) { accessTTL := 15 * time.Minute refreshTTL := 7 * 24 * time.Hour tm := NewTokenManager("test-secret-key", accessTTL, refreshTTL) before := time.Now() access, refresh, err := tm.GeneratePair("user-123", "alice") require.NoError(t, err) after := time.Now() // 校验 access token 有效期范围 accessClaims, err := tm.ValidateAccess(access) require.NoError(t, err) assert.True(t, accessClaims.ExpiresAt.Time.After(before.Add(accessTTL).Add(-1*time.Second))) assert.True(t, accessClaims.ExpiresAt.Time.Before(after.Add(accessTTL).Add(1*time.Second))) // 校验 refresh token 有效期范围 refreshClaims, err := tm.ValidateRefresh(refresh) require.NoError(t, err) assert.True(t, refreshClaims.ExpiresAt.Time.After(before.Add(refreshTTL).Add(-1*time.Second))) assert.True(t, refreshClaims.ExpiresAt.Time.Before(after.Add(refreshTTL).Add(1*time.Second))) }