Files
steipete--codexbar/Tests/CodexBarTests/UsageStoreWidgetSnapshotTests.swift
2026-07-13 12:22:33 +08:00

547 lines
24 KiB
Swift

import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
@MainActor
struct UsageStoreWidgetSnapshotTests {
@Test
func `widget snapshot preserves raw Codex windows for timeline projection`() async throws {
let suite = "UsageStoreWidgetSnapshotTests-codex-weekly-cap"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let now = Date()
store._setSnapshotForTesting(
UsageSnapshot(
primary: RateWindow(
usedPercent: 1,
windowMinutes: 300,
resetsAt: now.addingTimeInterval(1800),
resetDescription: nil),
secondary: RateWindow(
usedPercent: 100,
windowMinutes: 10080,
resetsAt: now.addingTimeInterval(3600),
resetDescription: nil),
updatedAt: now.addingTimeInterval(-7200)),
provider: .codex)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "codex-weekly-cap-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .codex })
#expect(entry.usageRows?.map(\.id) == ["session", "weekly"])
#expect(entry.usageRows?.compactMap(\.percentLeft) == [99, 0])
#expect(entry.usageRows?.first?.window?.usedPercent == 1)
#expect(entry.usageRows?.last?.window?.resetsAt == now.addingTimeInterval(3600))
}
@Test
func `widget snapshot includes Kimi subscription quota rows`() async throws {
let suite = "UsageStoreWidgetSnapshotTests-kimi-subscription-rows"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let snapshot = UsageSnapshot(
primary: RateWindow(usedPercent: 25, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
secondary: RateWindow(usedPercent: 50, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
tertiary: nil,
extraRateWindows: [
NamedRateWindow(
id: "kimi-code-7d",
title: "Code 7-day",
window: RateWindow(
usedPercent: 10,
windowMinutes: 7 * 24 * 60,
resetsAt: nil,
resetDescription: nil)),
NamedRateWindow(
id: "kimi-future-quota",
title: "Future quota",
window: RateWindow(
usedPercent: 5,
windowMinutes: nil,
resetsAt: nil,
resetDescription: nil)),
NamedRateWindow(
id: "kimi-monthly",
title: "Monthly",
window: RateWindow(
usedPercent: 75,
windowMinutes: nil,
resetsAt: nil,
resetDescription: nil)),
],
updatedAt: Date(),
identity: ProviderIdentitySnapshot(
providerID: .kimi,
accountEmail: nil,
accountOrganization: nil,
loginMethod: nil))
store._setSnapshotForTesting(snapshot, provider: .kimi)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "kimi-subscription-rows-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .kimi })
// Widgets preserve persisted lane order; menu-only presentation may reorder these lanes.
#expect(entry.usageRows?.map(\.id) == ["primary", "secondary", "kimi-monthly", "kimi-code-7d"])
#expect(entry.usageRows?.map(\.title) == ["Weekly", "Rate Limit", "Monthly", "Code 7-day"])
#expect(entry.usageRows?.compactMap(\.percentLeft) == [75, 50, 25, 90])
}
@Test
func `widget snapshot includes antigravity grouped usage rows`() async throws {
let suite = "UsageStoreWidgetSnapshotTests-antigravity-grouped"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
settings.usageBarsShowUsed = true
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let snapshot = UsageSnapshot(
primary: RateWindow(usedPercent: 10, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
secondary: RateWindow(usedPercent: 20, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
tertiary: RateWindow(usedPercent: 30, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
updatedAt: Date(),
identity: ProviderIdentitySnapshot(
providerID: .antigravity,
accountEmail: nil,
accountOrganization: nil,
loginMethod: "Pro"))
store._setSnapshotForTesting(snapshot, provider: .antigravity)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "antigravity-grouped-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .antigravity })
#expect(widgetSnapshots.last?.usageBarsShowUsed == true)
#expect(entry.usageRows?.map(\.id) == ["primary", "secondary"])
#expect(entry.usageRows?.map(\.title) == ["Gemini Models", "Claude and GPT"])
#expect(entry.usageRows?.compactMap(\.percentLeft) == [90, 80])
}
@Test
func `widget snapshot includes antigravity quota summary rows`() async throws {
let suite = "UsageStoreWidgetSnapshotTests-antigravity-quota-summary"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let snapshot = UsageSnapshot(
primary: RateWindow(usedPercent: 27, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
secondary: nil,
tertiary: nil,
extraRateWindows: [
NamedRateWindow(
id: "antigravity-quota-summary-gemini-5h",
title: "Gemini Models Five Hour Limit",
window: RateWindow(usedPercent: 9, windowMinutes: 300, resetsAt: nil, resetDescription: nil)),
NamedRateWindow(
id: "antigravity-quota-summary-gemini-weekly",
title: "Gemini Models Weekly Limit",
window: RateWindow(usedPercent: 18, windowMinutes: 10080, resetsAt: nil, resetDescription: nil)),
NamedRateWindow(
id: "antigravity-quota-summary-3p-5h",
title: "Claude and GPT models Five Hour Limit",
window: RateWindow(usedPercent: 27, windowMinutes: 300, resetsAt: nil, resetDescription: nil)),
NamedRateWindow(
id: "antigravity-quota-summary-3p-weekly",
title: "Claude and GPT models Weekly Limit",
window: RateWindow(usedPercent: 36, windowMinutes: 10080, resetsAt: nil, resetDescription: nil)),
],
updatedAt: Date(),
identity: ProviderIdentitySnapshot(
providerID: .antigravity,
accountEmail: nil,
accountOrganization: nil,
loginMethod: "Pro"))
store._setSnapshotForTesting(snapshot, provider: .antigravity)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "antigravity-quota-summary-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .antigravity })
#expect(entry.usageRows?.map(\.title) == [
"Gemini Models Five Hour Limit",
"Gemini Models Weekly Limit",
"Claude and GPT models Five Hour Limit",
"Claude and GPT models Weekly Limit",
])
#expect(entry.usageRows?.compactMap(\.percentLeft) == [91, 82, 73, 64])
}
@Test
func `widget snapshot labels antigravity compact fallback with model name`() async throws {
let suite = "UsageStoreWidgetSnapshotTests-antigravity-compact-fallback"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let snapshot = try AntigravityStatusSnapshot(
modelQuotas: [
AntigravityModelQuota(
label: "Experimental Model",
modelId: "MODEL_PLACEHOLDER_NEW",
remainingFraction: 0.36,
resetTime: nil,
resetDescription: nil),
],
accountEmail: nil,
accountPlan: nil,
source: .local)
.toUsageSnapshot()
store._setSnapshotForTesting(snapshot, provider: .antigravity)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "antigravity-compact-fallback-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .antigravity })
#expect(entry.primary == nil)
#expect(entry.usageRows?.map(\.id) == ["antigravity-compact-fallback-MODEL_PLACEHOLDER_NEW"])
#expect(entry.usageRows?.map(\.title) == ["Experimental Model"])
#expect(entry.usageRows?.compactMap(\.percentLeft) == [36])
}
@Test
func `widget snapshot excludes mimo balance from quota rows`() async throws {
let suite = "UsageStoreWidgetSnapshotTests-mimo-balance"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let snapshot = MiMoUsageSnapshot(
balance: 25.51,
currency: "USD",
updatedAt: Date())
.toUsageSnapshot()
store._setSnapshotForTesting(snapshot, provider: .mimo)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "mimo-balance-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .mimo })
#expect(entry.primary == nil)
#expect(entry.secondary == nil)
#expect(entry.usageRows?.isEmpty == true)
}
@Test
func `widget snapshot keeps Claude local cost without quota data`() async throws {
let suite = "UsageStoreWidgetSnapshotTests-claude-local-cost-only"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let updatedAt = Date(timeIntervalSince1970: 1_800_000_000)
store._setTokenSnapshotForTesting(
CostUsageTokenSnapshot(
sessionTokens: 4200,
sessionCostUSD: 1.25,
last30DaysTokens: 42000,
last30DaysCostUSD: 12.50,
daily: [],
updatedAt: updatedAt),
provider: .claude)
store._setSnapshotForTesting(
UsageSnapshot(
primary: RateWindow(usedPercent: 30, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
secondary: nil,
updatedAt: updatedAt,
identity: ProviderIdentitySnapshot(
providerID: .codex,
accountEmail: nil,
accountOrganization: nil,
loginMethod: nil)),
provider: .codex)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "claude-local-cost-only-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .claude })
#expect(entry.updatedAt == updatedAt)
#expect(entry.primary == nil)
#expect(entry.secondary == nil)
#expect(entry.usageRows?.isEmpty == true)
#expect(entry.tokenUsage?.sessionTokens == 4200)
#expect(entry.tokenUsage?.last30DaysTokens == 42000)
}
@Test(arguments: [true, false])
func `widget snapshot respects extra usage visibility for Devin`(_ showsExtraUsage: Bool) async throws {
let suite = "UsageStoreWidgetSnapshotTests-devin-extra-usage-\(showsExtraUsage)"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
settings.showOptionalCreditsAndExtraUsage = showsExtraUsage
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let updatedAt = Date(timeIntervalSince1970: 1_800_000_000)
store._setSnapshotForTesting(
UsageSnapshot(
primary: nil,
secondary: nil,
providerCost: ProviderCostSnapshot(
used: 48,
limit: 0,
currencyCode: "USD",
period: "Extra usage balance",
updatedAt: updatedAt),
updatedAt: updatedAt,
identity: ProviderIdentitySnapshot(
providerID: .devin,
accountEmail: nil,
accountOrganization: nil,
loginMethod: nil)),
provider: .devin)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "devin-extra-usage-visibility-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .devin })
#expect((entry.providerCost != nil) == showsExtraUsage)
}
@Test
func `widget snapshot carries token usage age separately from entry freshness`() async throws {
let suite = "UsageStoreWidgetSnapshotTests-token-usage-age"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let entryUpdatedAt = Date(timeIntervalSince1970: 1_800_000_000)
let tokenUpdatedAt = entryUpdatedAt.addingTimeInterval(-45 * 60)
store._setSnapshotForTesting(
UsageSnapshot(
primary: RateWindow(usedPercent: 30, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
secondary: nil,
updatedAt: entryUpdatedAt,
identity: ProviderIdentitySnapshot(
providerID: .claude,
accountEmail: nil,
accountOrganization: nil,
loginMethod: nil)),
provider: .claude)
store._setTokenSnapshotForTesting(
CostUsageTokenSnapshot(
sessionTokens: 4200,
sessionCostUSD: 1.25,
last30DaysTokens: 42000,
last30DaysCostUSD: 12.50,
daily: [],
updatedAt: tokenUpdatedAt),
provider: .claude)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "token-usage-age-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .claude })
#expect(entry.updatedAt == entryUpdatedAt)
#expect(entry.tokenUsage?.updatedAt == tokenUpdatedAt)
#expect(entry.tokenUsage?.isStale(comparedTo: entry.updatedAt) == true)
}
@Test
func `widget snapshot labels legacy Cursor request quota as Requests`() async throws {
let suite = "UsageStoreWidgetSnapshotTests-cursor-requests-label"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
store._setSnapshotForTesting(
UsageSnapshot(
primary: RateWindow(usedPercent: 40, windowMinutes: 43200, resetsAt: nil, resetDescription: nil),
secondary: nil,
tertiary: nil,
cursorRequests: CursorRequestUsage(used: 200, limit: 500),
updatedAt: Date()),
provider: .cursor)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "cursor-requests-label-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .cursor })
#expect(entry.usageRows?.map(\.id) == ["primary"])
#expect(entry.usageRows?.map(\.title) == ["Requests"])
}
@Test
func `widget snapshot keeps Cursor Total label for token based plans`() async throws {
let suite = "UsageStoreWidgetSnapshotTests-cursor-total-label"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
store._setSnapshotForTesting(
UsageSnapshot(
primary: RateWindow(usedPercent: 10, windowMinutes: 43200, resetsAt: nil, resetDescription: nil),
secondary: RateWindow(usedPercent: 20, windowMinutes: 43200, resetsAt: nil, resetDescription: nil),
tertiary: RateWindow(usedPercent: 0, windowMinutes: 43200, resetsAt: nil, resetDescription: nil),
updatedAt: Date()),
provider: .cursor)
var widgetSnapshots: [WidgetSnapshot] = []
store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
defer { store._test_widgetSnapshotSaveOverride = nil }
store.persistWidgetSnapshot(reason: "cursor-total-label-test")
await store.widgetSnapshotPersistTask?.value
let entry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .cursor })
#expect(entry.usageRows?.map(\.title) == ["Total", "Auto", "API"])
}
}