fix(codex): 修复额度窗口过期展示并补齐高覆盖测试
- 后端新增绝对重置时间字段计算(codex_5h_reset_at/codex_7d_reset_at) - 前端统一窗口解析逻辑:绝对时间优先,updated_at+seconds 回退,过期自动归零 - 新增后端与前端单元测试,覆盖关键边界与异常场景
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCodexSnapshotBaseTime(t *testing.T) {
|
||||
fallback := time.Date(2026, 2, 20, 9, 0, 0, 0, time.UTC)
|
||||
|
||||
t.Run("nil snapshot uses fallback", func(t *testing.T) {
|
||||
got := codexSnapshotBaseTime(nil, fallback)
|
||||
if !got.Equal(fallback) {
|
||||
t.Fatalf("got %v, want fallback %v", got, fallback)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty updatedAt uses fallback", func(t *testing.T) {
|
||||
got := codexSnapshotBaseTime(&OpenAICodexUsageSnapshot{}, fallback)
|
||||
if !got.Equal(fallback) {
|
||||
t.Fatalf("got %v, want fallback %v", got, fallback)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("valid updatedAt wins", func(t *testing.T) {
|
||||
got := codexSnapshotBaseTime(&OpenAICodexUsageSnapshot{UpdatedAt: "2026-02-16T10:00:00Z"}, fallback)
|
||||
want := time.Date(2026, 2, 16, 10, 0, 0, 0, time.UTC)
|
||||
if !got.Equal(want) {
|
||||
t.Fatalf("got %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid updatedAt uses fallback", func(t *testing.T) {
|
||||
got := codexSnapshotBaseTime(&OpenAICodexUsageSnapshot{UpdatedAt: "invalid"}, fallback)
|
||||
if !got.Equal(fallback) {
|
||||
t.Fatalf("got %v, want fallback %v", got, fallback)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCodexResetAtRFC3339(t *testing.T) {
|
||||
base := time.Date(2026, 2, 16, 10, 0, 0, 0, time.UTC)
|
||||
|
||||
t.Run("nil reset returns nil", func(t *testing.T) {
|
||||
if got := codexResetAtRFC3339(base, nil); got != nil {
|
||||
t.Fatalf("expected nil, got %v", *got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("positive seconds", func(t *testing.T) {
|
||||
sec := 90
|
||||
got := codexResetAtRFC3339(base, &sec)
|
||||
if got == nil {
|
||||
t.Fatal("expected non-nil")
|
||||
}
|
||||
if *got != "2026-02-16T10:01:30Z" {
|
||||
t.Fatalf("got %s, want %s", *got, "2026-02-16T10:01:30Z")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("negative seconds clamp to base", func(t *testing.T) {
|
||||
sec := -3
|
||||
got := codexResetAtRFC3339(base, &sec)
|
||||
if got == nil {
|
||||
t.Fatal("expected non-nil")
|
||||
}
|
||||
if *got != "2026-02-16T10:00:00Z" {
|
||||
t.Fatalf("got %s, want %s", *got, "2026-02-16T10:00:00Z")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildCodexUsageExtraUpdates_UsesSnapshotUpdatedAt(t *testing.T) {
|
||||
primaryUsed := 88.0
|
||||
primaryReset := 86400
|
||||
primaryWindow := 10080
|
||||
secondaryUsed := 12.0
|
||||
secondaryReset := 3600
|
||||
secondaryWindow := 300
|
||||
|
||||
snapshot := &OpenAICodexUsageSnapshot{
|
||||
PrimaryUsedPercent: &primaryUsed,
|
||||
PrimaryResetAfterSeconds: &primaryReset,
|
||||
PrimaryWindowMinutes: &primaryWindow,
|
||||
SecondaryUsedPercent: &secondaryUsed,
|
||||
SecondaryResetAfterSeconds: &secondaryReset,
|
||||
SecondaryWindowMinutes: &secondaryWindow,
|
||||
UpdatedAt: "2026-02-16T10:00:00Z",
|
||||
}
|
||||
|
||||
updates := buildCodexUsageExtraUpdates(snapshot, time.Date(2026, 2, 20, 8, 0, 0, 0, time.UTC))
|
||||
if updates == nil {
|
||||
t.Fatal("expected non-nil updates")
|
||||
}
|
||||
|
||||
if got := updates["codex_usage_updated_at"]; got != "2026-02-16T10:00:00Z" {
|
||||
t.Fatalf("codex_usage_updated_at = %v, want %s", got, "2026-02-16T10:00:00Z")
|
||||
}
|
||||
if got := updates["codex_5h_reset_at"]; got != "2026-02-16T11:00:00Z" {
|
||||
t.Fatalf("codex_5h_reset_at = %v, want %s", got, "2026-02-16T11:00:00Z")
|
||||
}
|
||||
if got := updates["codex_7d_reset_at"]; got != "2026-02-17T10:00:00Z" {
|
||||
t.Fatalf("codex_7d_reset_at = %v, want %s", got, "2026-02-17T10:00:00Z")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCodexUsageExtraUpdates_FallbackToNowWhenUpdatedAtInvalid(t *testing.T) {
|
||||
primaryUsed := 15.0
|
||||
primaryReset := 30
|
||||
primaryWindow := 300
|
||||
|
||||
fallbackNow := time.Date(2026, 2, 20, 8, 30, 0, 0, time.UTC)
|
||||
snapshot := &OpenAICodexUsageSnapshot{
|
||||
PrimaryUsedPercent: &primaryUsed,
|
||||
PrimaryResetAfterSeconds: &primaryReset,
|
||||
PrimaryWindowMinutes: &primaryWindow,
|
||||
UpdatedAt: "invalid-time",
|
||||
}
|
||||
|
||||
updates := buildCodexUsageExtraUpdates(snapshot, fallbackNow)
|
||||
if updates == nil {
|
||||
t.Fatal("expected non-nil updates")
|
||||
}
|
||||
|
||||
if got := updates["codex_usage_updated_at"]; got != "2026-02-20T08:30:00Z" {
|
||||
t.Fatalf("codex_usage_updated_at = %v, want %s", got, "2026-02-20T08:30:00Z")
|
||||
}
|
||||
if got := updates["codex_5h_reset_at"]; got != "2026-02-20T08:30:30Z" {
|
||||
t.Fatalf("codex_5h_reset_at = %v, want %s", got, "2026-02-20T08:30:30Z")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCodexUsageExtraUpdates_ClampNegativeResetSeconds(t *testing.T) {
|
||||
primaryUsed := 90.0
|
||||
primaryReset := 7200
|
||||
primaryWindow := 10080
|
||||
secondaryUsed := 100.0
|
||||
secondaryReset := -15
|
||||
secondaryWindow := 300
|
||||
|
||||
snapshot := &OpenAICodexUsageSnapshot{
|
||||
PrimaryUsedPercent: &primaryUsed,
|
||||
PrimaryResetAfterSeconds: &primaryReset,
|
||||
PrimaryWindowMinutes: &primaryWindow,
|
||||
SecondaryUsedPercent: &secondaryUsed,
|
||||
SecondaryResetAfterSeconds: &secondaryReset,
|
||||
SecondaryWindowMinutes: &secondaryWindow,
|
||||
UpdatedAt: "2026-02-16T10:00:00Z",
|
||||
}
|
||||
|
||||
updates := buildCodexUsageExtraUpdates(snapshot, time.Time{})
|
||||
if updates == nil {
|
||||
t.Fatal("expected non-nil updates")
|
||||
}
|
||||
|
||||
if got := updates["codex_5h_reset_after_seconds"]; got != -15 {
|
||||
t.Fatalf("codex_5h_reset_after_seconds = %v, want %d", got, -15)
|
||||
}
|
||||
if got := updates["codex_5h_reset_at"]; got != "2026-02-16T10:00:00Z" {
|
||||
t.Fatalf("codex_5h_reset_at = %v, want %s", got, "2026-02-16T10:00:00Z")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCodexUsageExtraUpdates_NilSnapshot(t *testing.T) {
|
||||
if got := buildCodexUsageExtraUpdates(nil, time.Now()); got != nil {
|
||||
t.Fatalf("expected nil updates, got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCodexUsageExtraUpdates_WithoutNormalizedWindowFields(t *testing.T) {
|
||||
primaryUsed := 42.0
|
||||
fallbackNow := time.Date(2026, 2, 20, 9, 15, 0, 0, time.UTC)
|
||||
snapshot := &OpenAICodexUsageSnapshot{
|
||||
PrimaryUsedPercent: &primaryUsed,
|
||||
UpdatedAt: "",
|
||||
}
|
||||
|
||||
updates := buildCodexUsageExtraUpdates(snapshot, fallbackNow)
|
||||
if updates == nil {
|
||||
t.Fatal("expected non-nil updates")
|
||||
}
|
||||
|
||||
if got := updates["codex_usage_updated_at"]; got != "2026-02-20T09:15:00Z" {
|
||||
t.Fatalf("codex_usage_updated_at = %v, want %s", got, "2026-02-20T09:15:00Z")
|
||||
}
|
||||
if _, ok := updates["codex_5h_reset_at"]; ok {
|
||||
t.Fatalf("did not expect codex_5h_reset_at in updates: %v", updates["codex_5h_reset_at"])
|
||||
}
|
||||
if _, ok := updates["codex_7d_reset_at"]; ok {
|
||||
t.Fatalf("did not expect codex_7d_reset_at in updates: %v", updates["codex_7d_reset_at"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user