测试修复: - 修复集成测试中的重复键冲突问题 - 移除 JSON 中多余的尾随逗号 - 新增 inprocess_transport_test.go - 更新 haiku 模型映射测试用例 数据库迁移: - 026: 运营指标聚合表 - 027: 使用量与计费一致性约束
229 lines
5.0 KiB
Go
229 lines
5.0 KiB
Go
//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)
|
||
})
|
||
}
|
||
}
|