Files
sub2api/backend/internal/service/token_refresher_test.go
shaw 81213f2324 refactor(service): 统一时间戳解析,支持多种格式
新增 Account.GetCredentialAsTime 方法,统一处理凭证中的时间戳字段,
兼容 RFC3339 字符串、Unix 时间戳字符串和数字类型。

- 重构 Claude/Gemini/Antigravity TokenRefresher.NeedsRefresh
- 移除重复的 parseExpiresAt/parseAntigravityExpiresAt 函数
- 简化 GetOpenAITokenExpiresAt 实现
- 新增 RFC3339 格式单元测试用例
2025-12-31 16:25:45 +08:00

229 lines
5.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build unit
package service
import (
"strconv"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestClaudeTokenRefresher_NeedsRefresh(t *testing.T) {
refresher := &ClaudeTokenRefresher{}
refreshWindow := 30 * time.Minute
tests := []struct {
name string
credentials map[string]any
wantRefresh bool
}{
{
name: "expires_at as string - expired",
credentials: map[string]any{
"expires_at": "1000", // 1970-01-01 00:16:40 UTC, 已过期
},
wantRefresh: true,
},
{
name: "expires_at as float64 - expired",
credentials: map[string]any{
"expires_at": float64(1000), // 数字类型,已过期
},
wantRefresh: true,
},
{
name: "expires_at as RFC3339 - expired",
credentials: map[string]any{
"expires_at": "1970-01-01T00:00:00Z", // RFC3339 格式,已过期
},
wantRefresh: true,
},
{
name: "expires_at as string - far future",
credentials: map[string]any{
"expires_at": "9999999999", // 远未来
},
wantRefresh: false,
},
{
name: "expires_at as float64 - far future",
credentials: map[string]any{
"expires_at": float64(9999999999), // 远未来,数字类型
},
wantRefresh: false,
},
{
name: "expires_at as RFC3339 - far future",
credentials: map[string]any{
"expires_at": "2099-12-31T23:59:59Z", // RFC3339 格式,远未来
},
wantRefresh: false,
},
{
name: "expires_at missing",
credentials: map[string]any{},
wantRefresh: false,
},
{
name: "expires_at is nil",
credentials: map[string]any{
"expires_at": nil,
},
wantRefresh: false,
},
{
name: "expires_at is invalid string",
credentials: map[string]any{
"expires_at": "invalid",
},
wantRefresh: false,
},
{
name: "credentials is nil",
credentials: nil,
wantRefresh: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
account := &Account{
Platform: PlatformAnthropic,
Type: AccountTypeOAuth,
Credentials: tt.credentials,
}
got := refresher.NeedsRefresh(account, refreshWindow)
require.Equal(t, tt.wantRefresh, got)
})
}
}
func TestClaudeTokenRefresher_NeedsRefresh_WithinWindow(t *testing.T) {
refresher := &ClaudeTokenRefresher{}
refreshWindow := 30 * time.Minute
// 设置一个在刷新窗口内的时间(当前时间 + 15分钟
expiresAt := time.Now().Add(15 * time.Minute).Unix()
tests := []struct {
name string
credentials map[string]any
}{
{
name: "string type - within refresh window",
credentials: map[string]any{
"expires_at": strconv.FormatInt(expiresAt, 10),
},
},
{
name: "float64 type - within refresh window",
credentials: map[string]any{
"expires_at": float64(expiresAt),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
account := &Account{
Platform: PlatformAnthropic,
Type: AccountTypeOAuth,
Credentials: tt.credentials,
}
got := refresher.NeedsRefresh(account, refreshWindow)
require.True(t, got, "should need refresh when within window")
})
}
}
func TestClaudeTokenRefresher_NeedsRefresh_OutsideWindow(t *testing.T) {
refresher := &ClaudeTokenRefresher{}
refreshWindow := 30 * time.Minute
// 设置一个在刷新窗口外的时间(当前时间 + 1小时
expiresAt := time.Now().Add(1 * time.Hour).Unix()
tests := []struct {
name string
credentials map[string]any
}{
{
name: "string type - outside refresh window",
credentials: map[string]any{
"expires_at": strconv.FormatInt(expiresAt, 10),
},
},
{
name: "float64 type - outside refresh window",
credentials: map[string]any{
"expires_at": float64(expiresAt),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
account := &Account{
Platform: PlatformAnthropic,
Type: AccountTypeOAuth,
Credentials: tt.credentials,
}
got := refresher.NeedsRefresh(account, refreshWindow)
require.False(t, got, "should not need refresh when outside window")
})
}
}
func TestClaudeTokenRefresher_CanRefresh(t *testing.T) {
refresher := &ClaudeTokenRefresher{}
tests := []struct {
name string
platform string
accType string
want bool
}{
{
name: "anthropic oauth - can refresh",
platform: PlatformAnthropic,
accType: AccountTypeOAuth,
want: true,
},
{
name: "anthropic api-key - cannot refresh",
platform: PlatformAnthropic,
accType: AccountTypeApiKey,
want: false,
},
{
name: "openai oauth - cannot refresh",
platform: PlatformOpenAI,
accType: AccountTypeOAuth,
want: false,
},
{
name: "gemini oauth - cannot refresh",
platform: PlatformGemini,
accType: AccountTypeOAuth,
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
account := &Account{
Platform: tt.platform,
Type: tt.accType,
}
got := refresher.CanRefresh(account)
require.Equal(t, tt.want, got)
})
}
}