2025-11-28 23:30:49 +08:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-12-02 19:43:39 +08:00
|
|
|
|
"carrotskin/internal/model"
|
2025-12-02 22:52:33 +08:00
|
|
|
|
"context"
|
2025-12-02 19:43:39 +08:00
|
|
|
|
"fmt"
|
2025-11-28 23:30:49 +08:00
|
|
|
|
"testing"
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
"go.uber.org/zap"
|
2025-11-28 23:30:49 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenService_Constants 测试Token服务相关常量
|
|
|
|
|
|
func TestTokenService_Constants(t *testing.T) {
|
2025-12-02 22:52:33 +08:00
|
|
|
|
// 内部常量已私有化,通过服务行为间接测试
|
|
|
|
|
|
t.Skip("Token constants are now private - test through service behavior instead")
|
2025-11-28 23:30:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenService_Validation 测试Token验证逻辑
|
|
|
|
|
|
func TestTokenService_Validation(t *testing.T) {
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
accessToken string
|
|
|
|
|
|
wantValid bool
|
|
|
|
|
|
}{
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "空token无效",
|
|
|
|
|
|
accessToken: "",
|
|
|
|
|
|
wantValid: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "非空token可能有效",
|
|
|
|
|
|
accessToken: "valid-token-string",
|
|
|
|
|
|
wantValid: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
|
// 测试空token检查逻辑
|
|
|
|
|
|
isValid := tt.accessToken != ""
|
|
|
|
|
|
if isValid != tt.wantValid {
|
|
|
|
|
|
t.Errorf("Token validation failed: got %v, want %v", isValid, tt.wantValid)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenService_ClientTokenLogic 测试ClientToken逻辑
|
|
|
|
|
|
func TestTokenService_ClientTokenLogic(t *testing.T) {
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
clientToken string
|
|
|
|
|
|
shouldGenerate bool
|
|
|
|
|
|
}{
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "空的clientToken应该生成新的",
|
|
|
|
|
|
clientToken: "",
|
|
|
|
|
|
shouldGenerate: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "非空的clientToken应该使用提供的",
|
|
|
|
|
|
clientToken: "existing-client-token",
|
|
|
|
|
|
shouldGenerate: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
|
shouldGenerate := tt.clientToken == ""
|
|
|
|
|
|
if shouldGenerate != tt.shouldGenerate {
|
|
|
|
|
|
t.Errorf("ClientToken logic failed: got %v, want %v", shouldGenerate, tt.shouldGenerate)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenService_ProfileSelection 测试Profile选择逻辑
|
|
|
|
|
|
func TestTokenService_ProfileSelection(t *testing.T) {
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
profileCount int
|
|
|
|
|
|
shouldAutoSelect bool
|
|
|
|
|
|
}{
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "只有一个profile时自动选择",
|
|
|
|
|
|
profileCount: 1,
|
|
|
|
|
|
shouldAutoSelect: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "多个profile时不自动选择",
|
|
|
|
|
|
profileCount: 2,
|
|
|
|
|
|
shouldAutoSelect: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "没有profile时不自动选择",
|
|
|
|
|
|
profileCount: 0,
|
|
|
|
|
|
shouldAutoSelect: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
|
shouldAutoSelect := tt.profileCount == 1
|
|
|
|
|
|
if shouldAutoSelect != tt.shouldAutoSelect {
|
|
|
|
|
|
t.Errorf("Profile selection logic failed: got %v, want %v", shouldAutoSelect, tt.shouldAutoSelect)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenService_CleanupLogic 测试清理逻辑
|
|
|
|
|
|
func TestTokenService_CleanupLogic(t *testing.T) {
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
tokenCount int
|
|
|
|
|
|
maxCount int
|
|
|
|
|
|
shouldCleanup bool
|
|
|
|
|
|
cleanupCount int
|
|
|
|
|
|
}{
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "token数量未超过上限,不需要清理",
|
|
|
|
|
|
tokenCount: 5,
|
|
|
|
|
|
maxCount: 10,
|
|
|
|
|
|
shouldCleanup: false,
|
|
|
|
|
|
cleanupCount: 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "token数量超过上限,需要清理",
|
|
|
|
|
|
tokenCount: 15,
|
|
|
|
|
|
maxCount: 10,
|
|
|
|
|
|
shouldCleanup: true,
|
|
|
|
|
|
cleanupCount: 5,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "token数量等于上限,不需要清理",
|
|
|
|
|
|
tokenCount: 10,
|
|
|
|
|
|
maxCount: 10,
|
|
|
|
|
|
shouldCleanup: false,
|
|
|
|
|
|
cleanupCount: 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
|
shouldCleanup := tt.tokenCount > tt.maxCount
|
|
|
|
|
|
if shouldCleanup != tt.shouldCleanup {
|
|
|
|
|
|
t.Errorf("Cleanup decision failed: got %v, want %v", shouldCleanup, tt.shouldCleanup)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if shouldCleanup {
|
|
|
|
|
|
expectedCleanupCount := tt.tokenCount - tt.maxCount
|
|
|
|
|
|
if expectedCleanupCount != tt.cleanupCount {
|
|
|
|
|
|
t.Errorf("Cleanup count failed: got %d, want %d", expectedCleanupCount, tt.cleanupCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenService_UserIDValidation 测试UserID验证
|
|
|
|
|
|
func TestTokenService_UserIDValidation(t *testing.T) {
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
userID int64
|
|
|
|
|
|
isValid bool
|
|
|
|
|
|
}{
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "有效的UserID",
|
|
|
|
|
|
userID: 1,
|
|
|
|
|
|
isValid: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "UserID为0时无效",
|
|
|
|
|
|
userID: 0,
|
|
|
|
|
|
isValid: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "负数UserID无效",
|
|
|
|
|
|
userID: -1,
|
|
|
|
|
|
isValid: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
|
isValid := tt.userID > 0
|
|
|
|
|
|
if isValid != tt.isValid {
|
|
|
|
|
|
t.Errorf("UserID validation failed: got %v, want %v", isValid, tt.isValid)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
// 使用 Mock 的集成测试
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenServiceImpl_Create 测试创建Token
|
|
|
|
|
|
func TestTokenServiceImpl_Create(t *testing.T) {
|
|
|
|
|
|
tokenRepo := NewMockTokenRepository()
|
|
|
|
|
|
profileRepo := NewMockProfileRepository()
|
|
|
|
|
|
logger := zap.NewNop()
|
|
|
|
|
|
|
|
|
|
|
|
// 预置Profile
|
|
|
|
|
|
testProfile := &model.Profile{
|
2025-12-07 20:51:20 +08:00
|
|
|
|
UUID: "test-profile-uuid",
|
|
|
|
|
|
UserID: 1,
|
|
|
|
|
|
Name: "TestProfile",
|
2025-12-02 19:43:39 +08:00
|
|
|
|
}
|
2025-12-03 15:27:12 +08:00
|
|
|
|
_ = profileRepo.Create(context.Background(), testProfile)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
tokenService := NewTokenService(tokenRepo, profileRepo, logger)
|
|
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
userID int64
|
|
|
|
|
|
uuid string
|
|
|
|
|
|
clientToken string
|
|
|
|
|
|
wantErr bool
|
|
|
|
|
|
}{
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "正常创建Token(指定UUID)",
|
|
|
|
|
|
userID: 1,
|
|
|
|
|
|
uuid: "test-profile-uuid",
|
|
|
|
|
|
clientToken: "client-token-1",
|
|
|
|
|
|
wantErr: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "正常创建Token(空clientToken)",
|
|
|
|
|
|
userID: 1,
|
|
|
|
|
|
uuid: "test-profile-uuid",
|
|
|
|
|
|
clientToken: "",
|
|
|
|
|
|
wantErr: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
2025-12-02 22:52:33 +08:00
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
_, _, accessToken, clientToken, err := tokenService.Create(ctx, tt.userID, tt.uuid, tt.clientToken)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
if tt.wantErr {
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
t.Error("期望返回错误,但实际没有错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Errorf("不期望返回错误: %v", err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if accessToken == "" {
|
|
|
|
|
|
t.Error("accessToken不应为空")
|
|
|
|
|
|
}
|
|
|
|
|
|
if clientToken == "" {
|
|
|
|
|
|
t.Error("clientToken不应为空")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenServiceImpl_Validate 测试验证Token
|
|
|
|
|
|
func TestTokenServiceImpl_Validate(t *testing.T) {
|
|
|
|
|
|
tokenRepo := NewMockTokenRepository()
|
|
|
|
|
|
profileRepo := NewMockProfileRepository()
|
|
|
|
|
|
logger := zap.NewNop()
|
|
|
|
|
|
|
|
|
|
|
|
// 预置Token
|
|
|
|
|
|
testToken := &model.Token{
|
|
|
|
|
|
AccessToken: "valid-access-token",
|
|
|
|
|
|
ClientToken: "valid-client-token",
|
|
|
|
|
|
UserID: 1,
|
|
|
|
|
|
ProfileId: "test-profile-uuid",
|
|
|
|
|
|
Usable: true,
|
|
|
|
|
|
}
|
2025-12-03 15:27:12 +08:00
|
|
|
|
_ = tokenRepo.Create(context.Background(), testToken)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
tokenService := NewTokenService(tokenRepo, profileRepo, logger)
|
|
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
accessToken string
|
|
|
|
|
|
clientToken string
|
|
|
|
|
|
wantValid bool
|
|
|
|
|
|
}{
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "有效Token(完全匹配)",
|
|
|
|
|
|
accessToken: "valid-access-token",
|
|
|
|
|
|
clientToken: "valid-client-token",
|
|
|
|
|
|
wantValid: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "有效Token(只检查accessToken)",
|
|
|
|
|
|
accessToken: "valid-access-token",
|
|
|
|
|
|
clientToken: "",
|
|
|
|
|
|
wantValid: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "无效Token(accessToken不存在)",
|
|
|
|
|
|
accessToken: "invalid-access-token",
|
|
|
|
|
|
clientToken: "",
|
|
|
|
|
|
wantValid: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "无效Token(clientToken不匹配)",
|
|
|
|
|
|
accessToken: "valid-access-token",
|
|
|
|
|
|
clientToken: "wrong-client-token",
|
|
|
|
|
|
wantValid: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
2025-12-02 22:52:33 +08:00
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
isValid := tokenService.Validate(ctx, tt.accessToken, tt.clientToken)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
if isValid != tt.wantValid {
|
|
|
|
|
|
t.Errorf("Token验证结果不匹配: got %v, want %v", isValid, tt.wantValid)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenServiceImpl_Invalidate 测试注销Token
|
|
|
|
|
|
func TestTokenServiceImpl_Invalidate(t *testing.T) {
|
|
|
|
|
|
tokenRepo := NewMockTokenRepository()
|
|
|
|
|
|
profileRepo := NewMockProfileRepository()
|
|
|
|
|
|
logger := zap.NewNop()
|
|
|
|
|
|
|
|
|
|
|
|
// 预置Token
|
|
|
|
|
|
testToken := &model.Token{
|
|
|
|
|
|
AccessToken: "token-to-invalidate",
|
|
|
|
|
|
ClientToken: "client-token",
|
|
|
|
|
|
UserID: 1,
|
|
|
|
|
|
ProfileId: "test-profile-uuid",
|
|
|
|
|
|
Usable: true,
|
|
|
|
|
|
}
|
2025-12-03 15:27:12 +08:00
|
|
|
|
_ = tokenRepo.Create(context.Background(), testToken)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
tokenService := NewTokenService(tokenRepo, profileRepo, logger)
|
|
|
|
|
|
|
2025-12-02 22:52:33 +08:00
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// 验证Token存在
|
2025-12-02 22:52:33 +08:00
|
|
|
|
isValid := tokenService.Validate(ctx, "token-to-invalidate", "")
|
2025-12-02 19:43:39 +08:00
|
|
|
|
if !isValid {
|
|
|
|
|
|
t.Error("Token应该有效")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 注销Token
|
2025-12-02 22:52:33 +08:00
|
|
|
|
tokenService.Invalidate(ctx, "token-to-invalidate")
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 验证Token已失效(从repo中删除)
|
2025-12-03 15:27:12 +08:00
|
|
|
|
_, err := tokenRepo.FindByAccessToken(context.Background(), "token-to-invalidate")
|
2025-12-02 19:43:39 +08:00
|
|
|
|
if err == nil {
|
|
|
|
|
|
t.Error("Token应该已被删除")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenServiceImpl_InvalidateUserTokens 测试注销用户所有Token
|
|
|
|
|
|
func TestTokenServiceImpl_InvalidateUserTokens(t *testing.T) {
|
|
|
|
|
|
tokenRepo := NewMockTokenRepository()
|
|
|
|
|
|
profileRepo := NewMockProfileRepository()
|
|
|
|
|
|
logger := zap.NewNop()
|
|
|
|
|
|
|
|
|
|
|
|
// 预置多个Token
|
|
|
|
|
|
for i := 1; i <= 3; i++ {
|
2025-12-03 15:27:12 +08:00
|
|
|
|
_ = tokenRepo.Create(context.Background(), &model.Token{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
AccessToken: fmt.Sprintf("user1-token-%d", i),
|
|
|
|
|
|
ClientToken: "client-token",
|
|
|
|
|
|
UserID: 1,
|
|
|
|
|
|
ProfileId: "test-profile-uuid",
|
|
|
|
|
|
Usable: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-12-03 15:27:12 +08:00
|
|
|
|
_ = tokenRepo.Create(context.Background(), &model.Token{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
AccessToken: "user2-token-1",
|
|
|
|
|
|
ClientToken: "client-token",
|
|
|
|
|
|
UserID: 2,
|
|
|
|
|
|
ProfileId: "test-profile-uuid-2",
|
|
|
|
|
|
Usable: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
tokenService := NewTokenService(tokenRepo, profileRepo, logger)
|
|
|
|
|
|
|
2025-12-02 22:52:33 +08:00
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// 注销用户1的所有Token
|
2025-12-02 22:52:33 +08:00
|
|
|
|
tokenService.InvalidateUserTokens(ctx, 1)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 验证用户1的Token已失效
|
2025-12-03 15:27:12 +08:00
|
|
|
|
tokens, _ := tokenRepo.GetByUserID(context.Background(), 1)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
if len(tokens) > 0 {
|
|
|
|
|
|
t.Errorf("用户1的Token应该全部被删除,但还剩 %d 个", len(tokens))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证用户2的Token仍然存在
|
2025-12-03 15:27:12 +08:00
|
|
|
|
tokens2, _ := tokenRepo.GetByUserID(context.Background(), 2)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
if len(tokens2) != 1 {
|
|
|
|
|
|
t.Errorf("用户2的Token应该仍然存在,期望1个,实际 %d 个", len(tokens2))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenServiceImpl_Refresh 覆盖 Refresh 的主要分支
|
|
|
|
|
|
func TestTokenServiceImpl_Refresh(t *testing.T) {
|
|
|
|
|
|
tokenRepo := NewMockTokenRepository()
|
|
|
|
|
|
profileRepo := NewMockProfileRepository()
|
|
|
|
|
|
logger := zap.NewNop()
|
|
|
|
|
|
|
|
|
|
|
|
// 预置 Profile 与 Token
|
|
|
|
|
|
profile := &model.Profile{
|
|
|
|
|
|
UUID: "profile-uuid",
|
|
|
|
|
|
UserID: 1,
|
|
|
|
|
|
}
|
2025-12-03 15:27:12 +08:00
|
|
|
|
_ = profileRepo.Create(context.Background(), profile)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
oldToken := &model.Token{
|
|
|
|
|
|
AccessToken: "old-token",
|
|
|
|
|
|
ClientToken: "client-token",
|
|
|
|
|
|
UserID: 1,
|
|
|
|
|
|
ProfileId: "",
|
|
|
|
|
|
Usable: true,
|
|
|
|
|
|
}
|
2025-12-03 15:27:12 +08:00
|
|
|
|
_ = tokenRepo.Create(context.Background(), oldToken)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
tokenService := NewTokenService(tokenRepo, profileRepo, logger)
|
|
|
|
|
|
|
2025-12-02 22:52:33 +08:00
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// 正常刷新,不指定 profile
|
2025-12-02 22:52:33 +08:00
|
|
|
|
newAccess, client, err := tokenService.Refresh(ctx, "old-token", "client-token", "")
|
2025-12-02 19:43:39 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("Refresh 正常情况失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if newAccess == "" || client != "client-token" {
|
|
|
|
|
|
t.Fatalf("Refresh 返回值异常: access=%s, client=%s", newAccess, client)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// accessToken 为空
|
2025-12-02 22:52:33 +08:00
|
|
|
|
if _, _, err := tokenService.Refresh(ctx, "", "client-token", ""); err == nil {
|
2025-12-02 19:43:39 +08:00
|
|
|
|
t.Fatalf("Refresh 在 accessToken 为空时应返回错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenServiceImpl_GetByAccessToken 封装 GetUUIDByAccessToken / GetUserIDByAccessToken
|
|
|
|
|
|
func TestTokenServiceImpl_GetByAccessToken(t *testing.T) {
|
|
|
|
|
|
tokenRepo := NewMockTokenRepository()
|
|
|
|
|
|
profileRepo := NewMockProfileRepository()
|
|
|
|
|
|
logger := zap.NewNop()
|
|
|
|
|
|
|
|
|
|
|
|
token := &model.Token{
|
|
|
|
|
|
AccessToken: "token-1",
|
|
|
|
|
|
UserID: 42,
|
|
|
|
|
|
ProfileId: "profile-42",
|
|
|
|
|
|
Usable: true,
|
|
|
|
|
|
}
|
2025-12-03 15:27:12 +08:00
|
|
|
|
_ = tokenRepo.Create(context.Background(), token)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
tokenService := NewTokenService(tokenRepo, profileRepo, logger)
|
|
|
|
|
|
|
2025-12-02 22:52:33 +08:00
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
|
|
uuid, err := tokenService.GetUUIDByAccessToken(ctx, "token-1")
|
2025-12-02 19:43:39 +08:00
|
|
|
|
if err != nil || uuid != "profile-42" {
|
|
|
|
|
|
t.Fatalf("GetUUIDByAccessToken 返回错误: uuid=%s, err=%v", uuid, err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-02 22:52:33 +08:00
|
|
|
|
uid, err := tokenService.GetUserIDByAccessToken(ctx, "token-1")
|
2025-12-02 19:43:39 +08:00
|
|
|
|
if err != nil || uid != 42 {
|
|
|
|
|
|
t.Fatalf("GetUserIDByAccessToken 返回错误: uid=%d, err=%v", uid, err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestTokenServiceImpl_validateProfileByUserID 直接测试内部校验逻辑
|
|
|
|
|
|
func TestTokenServiceImpl_validateProfileByUserID(t *testing.T) {
|
|
|
|
|
|
tokenRepo := NewMockTokenRepository()
|
|
|
|
|
|
profileRepo := NewMockProfileRepository()
|
|
|
|
|
|
logger := zap.NewNop()
|
|
|
|
|
|
|
2025-12-02 22:52:33 +08:00
|
|
|
|
svc := &tokenService{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
tokenRepo: tokenRepo,
|
|
|
|
|
|
profileRepo: profileRepo,
|
|
|
|
|
|
logger: logger,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 预置 Profile
|
|
|
|
|
|
profile := &model.Profile{
|
|
|
|
|
|
UUID: "p-1",
|
|
|
|
|
|
UserID: 1,
|
|
|
|
|
|
}
|
2025-12-03 15:27:12 +08:00
|
|
|
|
_ = profileRepo.Create(context.Background(), profile)
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 参数非法
|
2025-12-03 15:27:12 +08:00
|
|
|
|
if ok, err := svc.validateProfileByUserID(context.Background(), 0, ""); err == nil || ok {
|
2025-12-02 19:43:39 +08:00
|
|
|
|
t.Fatalf("validateProfileByUserID 在参数非法时应返回错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Profile 不存在
|
2025-12-03 15:27:12 +08:00
|
|
|
|
if ok, err := svc.validateProfileByUserID(context.Background(), 1, "not-exists"); err == nil || ok {
|
2025-12-02 19:43:39 +08:00
|
|
|
|
t.Fatalf("validateProfileByUserID 在 Profile 不存在时应返回错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 用户与 Profile 匹配
|
2025-12-03 15:27:12 +08:00
|
|
|
|
if ok, err := svc.validateProfileByUserID(context.Background(), 1, "p-1"); err != nil || !ok {
|
2025-12-02 19:43:39 +08:00
|
|
|
|
t.Fatalf("validateProfileByUserID 匹配时应返回 true, err=%v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 用户与 Profile 不匹配
|
2025-12-03 15:27:12 +08:00
|
|
|
|
if ok, err := svc.validateProfileByUserID(context.Background(), 2, "p-1"); err != nil || ok {
|
2025-12-02 19:43:39 +08:00
|
|
|
|
t.Fatalf("validateProfileByUserID 不匹配时应返回 false, err=%v", err)
|
|
|
|
|
|
}
|
2025-12-02 22:52:33 +08:00
|
|
|
|
}
|