package auth import ( "testing" "time" "perms-system-server/internal/middleware" "github.com/golang-jwt/jwt/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const testSecret = "test-jwt-secret-key" // TC-0217: secret="s", expire=3600, userId=1, username="u", productCode="p", memberType="M", perms=["a"] func TestGenerateAccessToken(t *testing.T) { tests := []struct { name string secret string expire int64 userId int64 username string productCode string memberType string perms []string }{ { name: "normal generation", secret: testSecret, expire: 3600, userId: 1, username: "admin", productCode: "p1", memberType: "ADMIN", perms: []string{"perm_a", "perm_b"}, }, { name: "empty perms", secret: testSecret, expire: 3600, userId: 2, username: "user1", productCode: "p1", memberType: "MEMBER", perms: nil, }, { name: "empty productCode", secret: testSecret, expire: 3600, userId: 3, username: "user2", productCode: "", memberType: "", perms: []string{}, }, { name: "super admin", secret: testSecret, expire: 7200, userId: 100, username: "super", productCode: "p1", memberType: "SUPER_ADMIN", perms: []string{"all_perm_1", "all_perm_2", "all_perm_3"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tokenStr, err := GenerateAccessToken(tt.secret, tt.expire, tt.userId, tt.username, tt.productCode, tt.memberType, tt.perms) require.NoError(t, err) assert.NotEmpty(t, tokenStr) token, err := jwt.ParseWithClaims(tokenStr, &middleware.Claims{}, func(token *jwt.Token) (interface{}, error) { return []byte(tt.secret), nil }) require.NoError(t, err) assert.True(t, token.Valid) claims, ok := token.Claims.(*middleware.Claims) require.True(t, ok) assert.Equal(t, tt.userId, claims.UserId) assert.Equal(t, tt.username, claims.Username) assert.Equal(t, tt.productCode, claims.ProductCode) assert.Equal(t, tt.memberType, claims.MemberType) if tt.perms == nil { assert.Nil(t, claims.Perms) } else { assert.Equal(t, tt.perms, claims.Perms) } }) } } // TC-0221: expireSeconds=1, sleep 2s func TestGenerateAccessToken_Expiry(t *testing.T) { tokenStr, err := GenerateAccessToken(testSecret, 1, 1, "u", "", "", nil) require.NoError(t, err) time.Sleep(2 * time.Second) _, err = jwt.ParseWithClaims(tokenStr, &middleware.Claims{}, func(token *jwt.Token) (interface{}, error) { return []byte(testSecret), nil }) assert.Error(t, err) assert.Contains(t, err.Error(), "token is expired") } // TC-0222: secret="s", expire=86400, userId=1, productCode="p" func TestGenerateRefreshToken(t *testing.T) { tests := []struct { name string secret string expire int64 userId int64 productCode string }{ {"normal", testSecret, 86400, 1, "p1"}, {"empty productCode", testSecret, 86400, 2, ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tokenStr, err := GenerateRefreshToken(tt.secret, tt.expire, tt.userId, tt.productCode) require.NoError(t, err) assert.NotEmpty(t, tokenStr) claims, err := ParseRefreshToken(tokenStr, tt.secret) require.NoError(t, err) assert.Equal(t, tt.userId, claims.UserId) assert.Equal(t, tt.productCode, claims.ProductCode) }) } } // TC-0225: 有效token+正确secret func TestParseRefreshToken(t *testing.T) { validToken, err := GenerateRefreshToken(testSecret, 3600, 42, "prod") require.NoError(t, err) t.Run("valid token", func(t *testing.T) { claims, err := ParseRefreshToken(validToken, testSecret) require.NoError(t, err) assert.Equal(t, int64(42), claims.UserId) assert.Equal(t, "prod", claims.ProductCode) }) t.Run("wrong secret", func(t *testing.T) { _, err := ParseRefreshToken(validToken, "wrong-secret") assert.Error(t, err) }) t.Run("invalid token string", func(t *testing.T) { _, err := ParseRefreshToken("not-a-valid-token", testSecret) assert.Error(t, err) }) t.Run("empty token", func(t *testing.T) { _, err := ParseRefreshToken("", testSecret) assert.Error(t, err) }) t.Run("expired token", func(t *testing.T) { expiredToken, err := GenerateRefreshToken(testSecret, 1, 1, "p") require.NoError(t, err) time.Sleep(2 * time.Second) _, err = ParseRefreshToken(expiredToken, testSecret) assert.Error(t, err) }) // TC-0230: AccessToken误用 — TokenType校验拒绝 t.Run("access token used as refresh - should be rejected", func(t *testing.T) { accessToken, err := GenerateAccessToken(testSecret, 3600, 1, "u", "p", "M", []string{"a"}) require.NoError(t, err) _, err = ParseRefreshToken(accessToken, testSecret) assert.Error(t, err, "BUG-002: access token 不应被 ParseRefreshToken 接受,应通过 TokenType 字段区分") }) } // TC-0219: secret="" func TestGenerateAccessToken_EmptySecret(t *testing.T) { tokenStr, err := GenerateAccessToken("", 3600, 1, "u", "p", "M", []string{"a"}) require.NoError(t, err) assert.NotEmpty(t, tokenStr) token, err := jwt.ParseWithClaims(tokenStr, &middleware.Claims{}, func(token *jwt.Token) (interface{}, error) { return []byte(""), nil }) require.NoError(t, err) assert.True(t, token.Valid) claims, ok := token.Claims.(*middleware.Claims) require.True(t, ok) assert.Equal(t, int64(1), claims.UserId) }