From e455af6b2d79da6eefbe844247ad4df78a5a6595 Mon Sep 17 00:00:00 2001 From: hhs <386998068@qq.com> Date: Sun, 14 Jun 2026 17:17:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BC=96=E5=86=99=20TokenManager=20?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/auth/jwt_test.go | 127 ++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 backend/internal/auth/jwt_test.go diff --git a/backend/internal/auth/jwt_test.go b/backend/internal/auth/jwt_test.go new file mode 100644 index 0000000..35b12fa --- /dev/null +++ b/backend/internal/auth/jwt_test.go @@ -0,0 +1,127 @@ +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))) +}