@@ -0,0 +1,146 @@
//go:build unit
package service
import (
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/require"
)
func newAuthServiceForPendingOAuthTest ( ) * AuthService {
cfg := & config . Config {
JWT : config . JWTConfig {
Secret : "test-secret-pending-oauth" ,
ExpireHour : 1 ,
} ,
}
return NewAuthService ( nil , nil , nil , nil , cfg , nil , nil , nil , nil , nil , nil )
}
// TestVerifyPendingOAuthToken_ValidToken 验证正常签发的 pending token 可以被成功解析。
func TestVerifyPendingOAuthToken_ValidToken ( t * testing . T ) {
svc := newAuthServiceForPendingOAuthTest ( )
token , err := svc . CreatePendingOAuthToken ( "user@example.com" , "alice" )
require . NoError ( t , err )
require . NotEmpty ( t , token )
email , username , err := svc . VerifyPendingOAuthToken ( token )
require . NoError ( t , err )
require . Equal ( t , "user@example.com" , email )
require . Equal ( t , "alice" , username )
}
// TestVerifyPendingOAuthToken_RegularJWTRejected 用普通 access token 尝试验证,应返回 ErrInvalidToken。
func TestVerifyPendingOAuthToken_RegularJWTRejected ( t * testing . T ) {
svc := newAuthServiceForPendingOAuthTest ( )
// 签发一个普通 access token( JWTClaims,无 Purpose 字段)
accessToken , err := svc . GenerateToken ( & User {
ID : 1 ,
Email : "user@example.com" ,
Role : RoleUser ,
} )
require . NoError ( t , err )
_ , _ , err = svc . VerifyPendingOAuthToken ( accessToken )
require . ErrorIs ( t , err , ErrInvalidToken )
}
// TestVerifyPendingOAuthToken_WrongPurpose 手动构造 purpose 字段不匹配的 JWT,应返回 ErrInvalidToken。
func TestVerifyPendingOAuthToken_WrongPurpose ( t * testing . T ) {
svc := newAuthServiceForPendingOAuthTest ( )
now := time . Now ( )
claims := & pendingOAuthClaims {
Email : "user@example.com" ,
Username : "alice" ,
Purpose : "some_other_purpose" ,
RegisteredClaims : jwt . RegisteredClaims {
ExpiresAt : jwt . NewNumericDate ( now . Add ( 10 * time . Minute ) ) ,
IssuedAt : jwt . NewNumericDate ( now ) ,
NotBefore : jwt . NewNumericDate ( now ) ,
} ,
}
tok := jwt . NewWithClaims ( jwt . SigningMethodHS256 , claims )
tokenStr , err := tok . SignedString ( [ ] byte ( svc . cfg . JWT . Secret ) )
require . NoError ( t , err )
_ , _ , err = svc . VerifyPendingOAuthToken ( tokenStr )
require . ErrorIs ( t , err , ErrInvalidToken )
}
// TestVerifyPendingOAuthToken_MissingPurpose 手动构造无 purpose 字段的 JWT(模拟旧 token),应返回 ErrInvalidToken。
func TestVerifyPendingOAuthToken_MissingPurpose ( t * testing . T ) {
svc := newAuthServiceForPendingOAuthTest ( )
now := time . Now ( )
claims := & pendingOAuthClaims {
Email : "user@example.com" ,
Username : "alice" ,
Purpose : "" , // 旧 token 无此字段,反序列化后为零值
RegisteredClaims : jwt . RegisteredClaims {
ExpiresAt : jwt . NewNumericDate ( now . Add ( 10 * time . Minute ) ) ,
IssuedAt : jwt . NewNumericDate ( now ) ,
NotBefore : jwt . NewNumericDate ( now ) ,
} ,
}
tok := jwt . NewWithClaims ( jwt . SigningMethodHS256 , claims )
tokenStr , err := tok . SignedString ( [ ] byte ( svc . cfg . JWT . Secret ) )
require . NoError ( t , err )
_ , _ , err = svc . VerifyPendingOAuthToken ( tokenStr )
require . ErrorIs ( t , err , ErrInvalidToken )
}
// TestVerifyPendingOAuthToken_ExpiredToken 过期 token 应返回 ErrInvalidToken。
func TestVerifyPendingOAuthToken_ExpiredToken ( t * testing . T ) {
svc := newAuthServiceForPendingOAuthTest ( )
past := time . Now ( ) . Add ( - 1 * time . Hour )
claims := & pendingOAuthClaims {
Email : "user@example.com" ,
Username : "alice" ,
Purpose : pendingOAuthPurpose ,
RegisteredClaims : jwt . RegisteredClaims {
ExpiresAt : jwt . NewNumericDate ( past ) ,
IssuedAt : jwt . NewNumericDate ( past . Add ( - 10 * time . Minute ) ) ,
NotBefore : jwt . NewNumericDate ( past . Add ( - 10 * time . Minute ) ) ,
} ,
}
tok := jwt . NewWithClaims ( jwt . SigningMethodHS256 , claims )
tokenStr , err := tok . SignedString ( [ ] byte ( svc . cfg . JWT . Secret ) )
require . NoError ( t , err )
_ , _ , err = svc . VerifyPendingOAuthToken ( tokenStr )
require . ErrorIs ( t , err , ErrInvalidToken )
}
// TestVerifyPendingOAuthToken_WrongSecret 不同密钥签发的 token 应返回 ErrInvalidToken。
func TestVerifyPendingOAuthToken_WrongSecret ( t * testing . T ) {
other := NewAuthService ( nil , nil , nil , nil , & config . Config {
JWT : config . JWTConfig { Secret : "other-secret" } ,
} , nil , nil , nil , nil , nil , nil )
token , err := other . CreatePendingOAuthToken ( "user@example.com" , "alice" )
require . NoError ( t , err )
svc := newAuthServiceForPendingOAuthTest ( )
_ , _ , err = svc . VerifyPendingOAuthToken ( token )
require . ErrorIs ( t , err , ErrInvalidToken )
}
// TestVerifyPendingOAuthToken_TooLong 超长 token 应返回 ErrInvalidToken。
func TestVerifyPendingOAuthToken_TooLong ( t * testing . T ) {
svc := newAuthServiceForPendingOAuthTest ( )
giant := make ( [ ] byte , maxTokenLength + 1 )
for i := range giant {
giant [ i ] = 'a'
}
_ , _ , err := svc . VerifyPendingOAuthToken ( string ( giant ) )
require . ErrorIs ( t , err , ErrInvalidToken )
}