Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// sessionWindowSyncRepo 记录 syncActiveToPassive 触发的所有写操作。
|
||||
type sessionWindowSyncRepo struct {
|
||||
AccountRepository
|
||||
|
||||
mu sync.Mutex
|
||||
extraUpdates []map[string]any
|
||||
sessionWindowEnds []sessionWindowEndCall
|
||||
}
|
||||
|
||||
type sessionWindowEndCall struct {
|
||||
AccountID int64
|
||||
End time.Time
|
||||
}
|
||||
|
||||
func (r *sessionWindowSyncRepo) UpdateExtra(_ context.Context, _ int64, updates map[string]any) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
copied := make(map[string]any, len(updates))
|
||||
for k, v := range updates {
|
||||
copied[k] = v
|
||||
}
|
||||
r.extraUpdates = append(r.extraUpdates, copied)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *sessionWindowSyncRepo) UpdateSessionWindowEnd(_ context.Context, id int64, end time.Time) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.sessionWindowEnds = append(r.sessionWindowEnds, sessionWindowEndCall{AccountID: id, End: end})
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestEstimateSetupTokenUsage_ExpiredWindowZeroes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
past := time.Now().Add(-2 * time.Hour)
|
||||
svc := &AccountUsageService{}
|
||||
info := svc.estimateSetupTokenUsage(&Account{
|
||||
SessionWindowEnd: &past,
|
||||
Extra: map[string]any{
|
||||
"session_window_utilization": 0.53,
|
||||
},
|
||||
})
|
||||
|
||||
if info.FiveHour == nil {
|
||||
t.Fatal("expected non-nil FiveHour info")
|
||||
}
|
||||
if info.FiveHour.Utilization != 0 {
|
||||
t.Fatalf("expected Utilization=0 for expired window, got %v", info.FiveHour.Utilization)
|
||||
}
|
||||
if info.FiveHour.ResetsAt != nil {
|
||||
t.Fatalf("expected ResetsAt=nil for expired window, got %v", info.FiveHour.ResetsAt)
|
||||
}
|
||||
if info.FiveHour.RemainingSeconds != 0 {
|
||||
t.Fatalf("expected RemainingSeconds=0 for expired window, got %v", info.FiveHour.RemainingSeconds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEstimateSetupTokenUsage_ActiveWindowPreservesUtilization(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
future := time.Now().Add(3 * time.Hour)
|
||||
svc := &AccountUsageService{}
|
||||
info := svc.estimateSetupTokenUsage(&Account{
|
||||
SessionWindowEnd: &future,
|
||||
Extra: map[string]any{
|
||||
"session_window_utilization": 0.53,
|
||||
},
|
||||
})
|
||||
|
||||
if info.FiveHour == nil {
|
||||
t.Fatal("expected non-nil FiveHour info")
|
||||
}
|
||||
if info.FiveHour.Utilization != 53 {
|
||||
t.Fatalf("expected Utilization=53, got %v", info.FiveHour.Utilization)
|
||||
}
|
||||
if info.FiveHour.ResetsAt == nil || !info.FiveHour.ResetsAt.Equal(future) {
|
||||
t.Fatalf("expected ResetsAt=%v, got %v", future, info.FiveHour.ResetsAt)
|
||||
}
|
||||
if info.FiveHour.RemainingSeconds <= 0 {
|
||||
t.Fatalf("expected positive RemainingSeconds, got %v", info.FiveHour.RemainingSeconds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncActiveToPassive_WritesFiveHourSessionWindowEnd(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
repo := &sessionWindowSyncRepo{}
|
||||
svc := &AccountUsageService{accountRepo: repo}
|
||||
resetsAt := time.Now().Add(3 * time.Hour).UTC().Truncate(time.Second)
|
||||
svc.syncActiveToPassive(context.Background(), 42, &UsageInfo{
|
||||
FiveHour: &UsageProgress{
|
||||
Utilization: 53,
|
||||
ResetsAt: &resetsAt,
|
||||
},
|
||||
})
|
||||
|
||||
repo.mu.Lock()
|
||||
defer repo.mu.Unlock()
|
||||
if len(repo.sessionWindowEnds) != 1 {
|
||||
t.Fatalf("expected 1 UpdateSessionWindowEnd call, got %d", len(repo.sessionWindowEnds))
|
||||
}
|
||||
call := repo.sessionWindowEnds[0]
|
||||
if call.AccountID != 42 {
|
||||
t.Fatalf("expected AccountID=42, got %d", call.AccountID)
|
||||
}
|
||||
if !call.End.Equal(resetsAt) {
|
||||
t.Fatalf("expected End=%v, got %v", resetsAt, call.End)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncActiveToPassive_SkipsSessionWindowEndWhenResetMissing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
repo := &sessionWindowSyncRepo{}
|
||||
svc := &AccountUsageService{accountRepo: repo}
|
||||
svc.syncActiveToPassive(context.Background(), 99, &UsageInfo{
|
||||
FiveHour: &UsageProgress{Utilization: 10},
|
||||
})
|
||||
|
||||
repo.mu.Lock()
|
||||
defer repo.mu.Unlock()
|
||||
if len(repo.sessionWindowEnds) != 0 {
|
||||
t.Fatalf("expected no UpdateSessionWindowEnd calls when ResetsAt is nil, got %d", len(repo.sessionWindowEnds))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user