1572 lines
58 KiB
Swift
1572 lines
58 KiB
Swift
import CodexBarCore
|
|
import Foundation
|
|
import SwiftUI
|
|
import Testing
|
|
@testable import CodexBar
|
|
|
|
struct OverviewMenuCardVisibilityTests {
|
|
@Test
|
|
func `overview hides cards that only contain an error`() throws {
|
|
let metadata = try #require(ProviderDefaults.metadata[.cursor])
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .cursor,
|
|
metadata: metadata,
|
|
snapshot: nil,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: "No Cursor session found.",
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: Date()))
|
|
|
|
#expect(model.isOverviewErrorOnly)
|
|
}
|
|
|
|
@Test
|
|
func `overview keeps cards with graceful unavailable placeholders`() throws {
|
|
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .codex,
|
|
metadata: metadata,
|
|
snapshot: nil,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: "user@example.com", plan: "pro"),
|
|
isRefreshing: false,
|
|
lastError: UsageError.noRateLimitsFound.errorDescription,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: Date()))
|
|
|
|
#expect(model.placeholder == "Limits not available")
|
|
#expect(!model.isOverviewErrorOnly)
|
|
}
|
|
|
|
@Test
|
|
func `claude subscription-only quota keeps local cost content`() throws {
|
|
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
let now = Date(timeIntervalSince1970: 1_800_000_000)
|
|
let tokenSnapshot = CostUsageTokenSnapshot(
|
|
sessionTokens: 4200,
|
|
sessionCostUSD: 1.25,
|
|
last30DaysTokens: 42000,
|
|
last30DaysCostUSD: 12.50,
|
|
daily: [],
|
|
updatedAt: now)
|
|
let quotaError = ClaudeStatusProbeError.parseFailed(
|
|
ClaudeStatusProbe.subscriptionQuotaUnavailableDescription).localizedDescription
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .claude,
|
|
metadata: metadata,
|
|
snapshot: nil,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: tokenSnapshot,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: quotaError,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: true,
|
|
tokenCostMenuSectionEnabled: true,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.placeholder == "Limits not available")
|
|
#expect(model.subtitleStyle == .info)
|
|
#expect(model.tokenUsage != nil)
|
|
#expect(model.metrics.isEmpty)
|
|
#expect(!model.isOverviewErrorOnly)
|
|
}
|
|
}
|
|
|
|
struct ProviderInlineDashboardModelTests {
|
|
@Test
|
|
func `kimi model orders rate limit before weekly quota`() throws {
|
|
let now = Date(timeIntervalSince1970: 1_800_000_000)
|
|
let metadata = try #require(ProviderDefaults.metadata[.kimi])
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(
|
|
usedPercent: 18.3,
|
|
windowMinutes: nil,
|
|
resetsAt: now.addingTimeInterval(4 * 24 * 60 * 60),
|
|
resetDescription: "375/2048 requests"),
|
|
secondary: RateWindow(
|
|
usedPercent: 9.5,
|
|
windowMinutes: 300,
|
|
resetsAt: now.addingTimeInterval(4 * 60 * 60),
|
|
resetDescription: "Rate: 19/200 per 5 hours"),
|
|
updatedAt: now)
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .kimi,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.metrics.map(\.id) == ["secondary", "primary"])
|
|
#expect(model.metrics.map(\.title) == ["Rate Limit", "Weekly"])
|
|
}
|
|
|
|
@Test
|
|
func `openrouter period usage gets inline dashboard`() throws {
|
|
let now = Date(timeIntervalSince1970: 1_700_179_200)
|
|
let metadata = try #require(ProviderDefaults.metadata[.openrouter])
|
|
let usage = OpenRouterUsageSnapshot(
|
|
totalCredits: 100,
|
|
totalUsage: 40,
|
|
balance: 60,
|
|
usedPercent: 40,
|
|
keyDataFetched: true,
|
|
keyLimit: 25,
|
|
keyUsage: 10,
|
|
keyUsageDaily: 1.25,
|
|
keyUsageWeekly: 7.5,
|
|
keyUsageMonthly: 18.75,
|
|
rateLimit: OpenRouterRateLimit(requests: 100, interval: "10s"),
|
|
updatedAt: now)
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .openrouter,
|
|
metadata: metadata,
|
|
snapshot: usage.toUsageSnapshot(),
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.inlineUsageDashboard?.kpis.first?.value == "$60.00")
|
|
#expect(model.inlineUsageDashboard?.points.map(\.label) == ["Today", "Week", "Month"])
|
|
#expect(model.inlineUsageDashboard?.detailLines.contains("Rate limit: 100 / 10s") == true)
|
|
}
|
|
|
|
@Test
|
|
func `local cost history gets inline dashboard`() throws {
|
|
let now = Date(timeIntervalSince1970: 1_700_179_200)
|
|
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
let daily = [
|
|
CostUsageDailyReport.Entry(
|
|
date: "2023-11-14",
|
|
inputTokens: 100,
|
|
outputTokens: 50,
|
|
totalTokens: 150,
|
|
costUSD: 0.12,
|
|
modelsUsed: ["claude-sonnet-4"],
|
|
modelBreakdowns: [
|
|
CostUsageDailyReport.ModelBreakdown(
|
|
modelName: "claude-sonnet-4",
|
|
costUSD: 0.12,
|
|
totalTokens: 150),
|
|
]),
|
|
CostUsageDailyReport.Entry(
|
|
date: "2023-11-15",
|
|
inputTokens: 200,
|
|
outputTokens: 75,
|
|
totalTokens: 275,
|
|
costUSD: 0.25,
|
|
modelsUsed: ["claude-opus-4"],
|
|
modelBreakdowns: [
|
|
CostUsageDailyReport.ModelBreakdown(
|
|
modelName: "claude-opus-4",
|
|
costUSD: 0.25,
|
|
totalTokens: 275),
|
|
]),
|
|
]
|
|
let tokenSnapshot = CostUsageTokenSnapshot(
|
|
sessionTokens: 275,
|
|
sessionCostUSD: 0.25,
|
|
last30DaysTokens: 425,
|
|
last30DaysCostUSD: 0.37,
|
|
daily: daily,
|
|
updatedAt: now)
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .claude,
|
|
metadata: metadata,
|
|
snapshot: UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 10, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
|
|
secondary: nil,
|
|
updatedAt: now),
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: tokenSnapshot,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: true,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.inlineUsageDashboard?.kpis.first?.value == "$0.25")
|
|
#expect(model.inlineUsageDashboard?.points.count == 2)
|
|
#expect(model.inlineUsageDashboard?.detailLines.contains { $0.contains("claude-opus-4") } == true)
|
|
#expect(model.tokenUsage?.sessionLine.contains("$0.25") == true)
|
|
#expect(model.tokenUsage?.monthLine.contains("$0.37") == true)
|
|
}
|
|
|
|
@Test
|
|
func `mistral daily buckets get inline dashboard`() throws {
|
|
let now = Date(timeIntervalSince1970: 1_700_179_200)
|
|
let metadata = try #require(ProviderDefaults.metadata[.mistral])
|
|
let snapshot = MistralUsageSnapshot(
|
|
totalCost: 1.5,
|
|
currency: "EUR",
|
|
currencySymbol: "€",
|
|
totalInputTokens: 100,
|
|
totalOutputTokens: 50,
|
|
totalCachedTokens: 0,
|
|
modelCount: 1,
|
|
daily: [
|
|
MistralDailyUsageBucket(
|
|
day: "2023-11-14",
|
|
cost: 1.5,
|
|
inputTokens: 100,
|
|
cachedTokens: 0,
|
|
outputTokens: 50,
|
|
models: [
|
|
MistralDailyUsageBucket.ModelBreakdown(
|
|
name: "mistral-large",
|
|
cost: 1.5,
|
|
inputTokens: 100,
|
|
cachedTokens: 0,
|
|
outputTokens: 50),
|
|
]),
|
|
],
|
|
startDate: nil,
|
|
endDate: nil,
|
|
updatedAt: now)
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .mistral,
|
|
metadata: metadata,
|
|
snapshot: snapshot.toUsageSnapshot(),
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
tokenCostInlineDashboardEnabled: true,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.inlineUsageDashboard?.kpis.first?.value == "€1.50")
|
|
#expect(model.inlineUsageDashboard?.points.first?.accessibilityValue == "2023-11-14: €1.50")
|
|
#expect(model.inlineUsageDashboard?.detailLines.contains("Top model: mistral-large") == true)
|
|
}
|
|
|
|
@Test
|
|
func `mistral billing usage can show cost card summary`() throws {
|
|
let now = Date(timeIntervalSince1970: 1_700_179_200)
|
|
let metadata = try #require(ProviderDefaults.metadata[.mistral])
|
|
let snapshot = MistralUsageSnapshot(
|
|
totalCost: 1.5,
|
|
currency: "EUR",
|
|
currencySymbol: "€",
|
|
totalInputTokens: 100,
|
|
totalOutputTokens: 50,
|
|
totalCachedTokens: 25,
|
|
modelCount: 1,
|
|
daily: [
|
|
MistralDailyUsageBucket(
|
|
day: "2023-11-14",
|
|
cost: 1.5,
|
|
inputTokens: 100,
|
|
cachedTokens: 25,
|
|
outputTokens: 50,
|
|
models: [
|
|
MistralDailyUsageBucket.ModelBreakdown(
|
|
name: "mistral-large",
|
|
cost: 1.5,
|
|
inputTokens: 100,
|
|
cachedTokens: 25,
|
|
outputTokens: 50),
|
|
]),
|
|
],
|
|
startDate: nil,
|
|
endDate: nil,
|
|
updatedAt: now)
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .mistral,
|
|
metadata: metadata,
|
|
snapshot: snapshot.toUsageSnapshot(),
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: snapshot.toCostUsageTokenSnapshot(historyDays: 30),
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: true,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(ProviderDescriptorRegistry.descriptor(for: .mistral).tokenCost.supportsTokenCost)
|
|
#expect(model.tokenUsage?.sessionLine == "Latest billing day (Nov 14): €1.50 · 175 tokens")
|
|
#expect(model.tokenUsage?.monthLine == "This month: €1.50 · 175 tokens")
|
|
#expect(model.tokenUsage?.hintLine == "Reported by Mistral billing usage.")
|
|
}
|
|
|
|
@Test
|
|
func `zai hourly usage gets inline dashboard`() throws {
|
|
let now = try #require(Self.zaiDate("2023-11-15 12:00"))
|
|
let metadata = try #require(ProviderDefaults.metadata[.zai])
|
|
let usage = ZaiUsageSnapshot(
|
|
tokenLimit: nil,
|
|
timeLimit: nil,
|
|
planName: "Pro",
|
|
modelUsage: ZaiModelUsageData(
|
|
xTime: ["2023-11-14 12:00", "2023-11-15 12:00"],
|
|
modelDataList: [
|
|
ZaiModelDataItem(modelName: "glm-4.5", tokensUsage: [100, 200]),
|
|
]),
|
|
updatedAt: now)
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .zai,
|
|
metadata: metadata,
|
|
snapshot: usage.toUsageSnapshot(),
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.inlineUsageDashboard?.kpis.first?.value == "300")
|
|
#expect(model.inlineUsageDashboard?.points.map(\.label) == ["12", "12"])
|
|
#expect(Set(model.inlineUsageDashboard?.points.map(\.id) ?? []).count == 2)
|
|
#expect(model.inlineUsageDashboard?.detailLines.contains("Top model: glm-4.5") == true)
|
|
}
|
|
|
|
private static func zaiDate(_ text: String) -> Date? {
|
|
let formatter = DateFormatter()
|
|
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
formatter.dateFormat = "yyyy-MM-dd HH:mm"
|
|
return formatter.date(from: text)
|
|
}
|
|
}
|
|
|
|
struct FactoryMenuCardModelTests {
|
|
@Test
|
|
func `factory token rate billing uses time window labels`() throws {
|
|
let now = Date()
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
|
|
secondary: RateWindow(usedPercent: 34, windowMinutes: 10080, resetsAt: nil, resetDescription: nil),
|
|
tertiary: RateWindow(usedPercent: 56, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
|
|
updatedAt: now,
|
|
identity: nil)
|
|
let metadata = try #require(ProviderDefaults.metadata[.factory])
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .factory,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: true,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.metrics.map(\.title) == ["5-hour", "Weekly", "Monthly"])
|
|
}
|
|
|
|
@Test
|
|
func `factory legacy billing keeps pool labels`() throws {
|
|
let now = Date()
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 12, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
|
|
secondary: RateWindow(usedPercent: 34, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
|
|
updatedAt: now,
|
|
identity: nil)
|
|
let metadata = try #require(ProviderDefaults.metadata[.factory])
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .factory,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: true,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.metrics.map(\.title) == ["Standard", "Premium"])
|
|
}
|
|
|
|
@Test
|
|
func `factory extra usage balance renders as optional balance`() throws {
|
|
let now = Date()
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
|
|
secondary: RateWindow(usedPercent: 34, windowMinutes: 10080, resetsAt: nil, resetDescription: nil),
|
|
tertiary: RateWindow(usedPercent: 56, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
|
|
providerCost: ProviderCostSnapshot(
|
|
used: 25,
|
|
limit: 0,
|
|
currencyCode: "USD",
|
|
period: "Extra usage balance",
|
|
updatedAt: now),
|
|
updatedAt: now,
|
|
identity: nil)
|
|
let metadata = try #require(ProviderDefaults.metadata[.factory])
|
|
|
|
let visible = UsageMenuCardView.Model.make(.init(
|
|
provider: .factory,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: true,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
#expect(visible.providerCost?.title == "Extra usage")
|
|
#expect(visible.providerCost?.spendLine == "Balance: $25.00")
|
|
#expect(visible.providerCost?.percentUsed == nil)
|
|
#expect(visible.providerCost?.percentLine == nil)
|
|
|
|
let hidden = UsageMenuCardView.Model.make(.init(
|
|
provider: .factory,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: true,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: false,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
#expect(hidden.providerCost == nil)
|
|
}
|
|
}
|
|
|
|
struct MiniMaxMenuCardModelTests {
|
|
@Test
|
|
func `minimax service metrics use codex aligned quota copy`() throws {
|
|
let now = Date()
|
|
let minimax = MiniMaxUsageSnapshot(
|
|
planName: "Max",
|
|
availablePrompts: nil,
|
|
currentPrompts: nil,
|
|
remainingPrompts: nil,
|
|
windowMinutes: nil,
|
|
usedPercent: nil,
|
|
resetsAt: nil,
|
|
updatedAt: now,
|
|
services: [
|
|
MiniMaxServiceUsage(
|
|
serviceType: "text-generation",
|
|
windowType: "5 hours",
|
|
timeRange: "10:00-15:00(UTC+8)",
|
|
usage: 2,
|
|
limit: 10,
|
|
percent: 20,
|
|
resetsAt: now.addingTimeInterval(3600),
|
|
resetDescription: "Resets in 1 hour"),
|
|
])
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 20, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
|
|
secondary: nil,
|
|
minimaxUsage: minimax,
|
|
updatedAt: now,
|
|
identity: ProviderIdentitySnapshot(
|
|
providerID: .minimax,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: "Max"))
|
|
let metadata = try #require(ProviderDefaults.metadata[.minimax])
|
|
|
|
let used = UsageMenuCardView.Model.make(.init(
|
|
provider: .minimax,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: true,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(used.metrics.first?.title == "Text Generation")
|
|
#expect(used.metrics.first?.detailLeftText == "Usage: 2 / 10")
|
|
#expect(used.metrics.first?.detailRightText == nil)
|
|
#expect(used.metrics.first?.detailText == nil)
|
|
#expect(used.metrics.first?.percent == 20)
|
|
#expect(used.metrics.first?.cardStyle == false)
|
|
}
|
|
|
|
@Test
|
|
func `text generation badge uses real window type when multiple windows exist`() throws {
|
|
let now = Date()
|
|
let minimax = MiniMaxUsageSnapshot(
|
|
planName: "Max",
|
|
availablePrompts: nil,
|
|
currentPrompts: nil,
|
|
remainingPrompts: nil,
|
|
windowMinutes: nil,
|
|
usedPercent: nil,
|
|
resetsAt: nil,
|
|
updatedAt: now,
|
|
services: [
|
|
MiniMaxServiceUsage(
|
|
serviceType: "text-generation",
|
|
windowType: "Today",
|
|
timeRange: "2026/05/16 00:00 - 2026/05/17 00:00",
|
|
usage: 2,
|
|
limit: 10,
|
|
percent: 20,
|
|
resetsAt: now.addingTimeInterval(3600),
|
|
resetDescription: "Resets in 1 hour"),
|
|
MiniMaxServiceUsage(
|
|
serviceType: "text-generation",
|
|
windowType: "Weekly",
|
|
timeRange: "05/11 00:00 - 05/18 00:00(UTC+8)",
|
|
usage: 20,
|
|
limit: 100,
|
|
percent: 20,
|
|
resetsAt: now.addingTimeInterval(7200),
|
|
resetDescription: "Resets in 2 hours"),
|
|
])
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 20, windowMinutes: 1440, resetsAt: nil, resetDescription: nil),
|
|
secondary: nil,
|
|
minimaxUsage: minimax,
|
|
updatedAt: now,
|
|
identity: ProviderIdentitySnapshot(
|
|
providerID: .minimax,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: "Max"))
|
|
let metadata = try #require(ProviderDefaults.metadata[.minimax])
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .minimax,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: true,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.metrics.count == 2)
|
|
#expect(model.metrics[0].title == "Text Generation · Today")
|
|
#expect(model.metrics[1].title == "Text Generation · Weekly")
|
|
}
|
|
|
|
@Test
|
|
func `minimax token plan model shows weekly quota and points balance`() throws {
|
|
let now = Date()
|
|
let minimax = MiniMaxUsageSnapshot(
|
|
planName: "Token Plan · TokenPlanPlus-年度会员",
|
|
availablePrompts: nil,
|
|
currentPrompts: nil,
|
|
remainingPrompts: nil,
|
|
windowMinutes: nil,
|
|
usedPercent: nil,
|
|
resetsAt: nil,
|
|
updatedAt: now,
|
|
services: [
|
|
MiniMaxServiceUsage(
|
|
serviceType: "text-generation",
|
|
windowType: "5 hours",
|
|
timeRange: "10:00-15:00(UTC+8)",
|
|
usage: 4,
|
|
limit: 100,
|
|
percent: 4,
|
|
resetsAt: now.addingTimeInterval(4 * 3600),
|
|
resetDescription: "Resets in 4 hours"),
|
|
MiniMaxServiceUsage(
|
|
serviceType: "text-generation",
|
|
windowType: "Weekly",
|
|
timeRange: "06/01 00:00 - 06/08 00:00(UTC+8)",
|
|
usage: 1,
|
|
limit: 100,
|
|
percent: 1,
|
|
resetsAt: now.addingTimeInterval(6 * 24 * 3600),
|
|
resetDescription: "Resets in 6 days"),
|
|
],
|
|
pointsBalance: 14000,
|
|
subscriptionRenewsAt: Date(timeIntervalSince1970: 1_810_569_600))
|
|
let snapshot = minimax.toUsageSnapshot()
|
|
let metadata = try #require(ProviderDefaults.metadata[.minimax])
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .minimax,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: true,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.planText == "Plus")
|
|
#expect(model.metrics[0].title == "Text Generation · 5h")
|
|
#expect(model.metrics[1].title == "Text Generation · Weekly")
|
|
#expect(model.metrics[0].detailLeftText == "Usage: 4 / 100")
|
|
#expect(model.metrics[1].detailLeftText == "Usage: 1 / 100")
|
|
#expect(model.metrics[0].detailRightText == nil)
|
|
#expect(model.metrics[1].detailRightText == nil)
|
|
#expect(model.metrics[0].detailText == nil)
|
|
#expect(model.metrics[1].detailText == nil)
|
|
#expect(model.metrics[0].cardStyle == false)
|
|
#expect(model.metrics[1].cardStyle == false)
|
|
#expect(model.providerCost?.title == "Credits")
|
|
#expect(model.providerCost?.spendLine == "Balance: 14000")
|
|
#expect(model.usageNotes == [String(format: L("Renews: %@"), minimaxRenewDate(1_810_569_600))])
|
|
}
|
|
}
|
|
|
|
struct ClaudeMenuCardCostTests {
|
|
@Test
|
|
func `claude extra usage labels monthly denominator as cap`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 0, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
providerCost: ProviderCostSnapshot(
|
|
used: 5,
|
|
limit: 20,
|
|
currencyCode: "USD",
|
|
period: "Monthly cap",
|
|
updatedAt: now),
|
|
updatedAt: now,
|
|
identity: nil)
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .claude,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.providerCost?.spendLine == "Monthly cap: $5.00 / $20.00")
|
|
}
|
|
}
|
|
|
|
struct MenuCardModelTests {
|
|
@Test
|
|
func `builds metrics using remaining percent`() throws {
|
|
let now = Date()
|
|
let identity = ProviderIdentitySnapshot(
|
|
providerID: .codex,
|
|
accountEmail: "codex@example.com",
|
|
accountOrganization: nil,
|
|
loginMethod: "Plus Plan")
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(
|
|
usedPercent: 22,
|
|
windowMinutes: 300,
|
|
resetsAt: now.addingTimeInterval(3000),
|
|
resetDescription: nil),
|
|
secondary: RateWindow(
|
|
usedPercent: 40,
|
|
windowMinutes: 10080,
|
|
resetsAt: now.addingTimeInterval(6000),
|
|
resetDescription: nil),
|
|
updatedAt: now,
|
|
identity: identity)
|
|
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
let updatedSnap = try UsageSnapshot(
|
|
primary: snapshot.primary,
|
|
secondary: RateWindow(
|
|
usedPercent: #require(snapshot.secondary?.usedPercent),
|
|
windowMinutes: #require(snapshot.secondary?.windowMinutes),
|
|
resetsAt: now.addingTimeInterval(3600),
|
|
resetDescription: nil),
|
|
tertiary: snapshot.tertiary,
|
|
updatedAt: now,
|
|
identity: identity)
|
|
let codexProjection = CodexConsumerProjection.make(
|
|
surface: .liveCard,
|
|
context: CodexConsumerProjection.Context(
|
|
snapshot: updatedSnap,
|
|
rawUsageError: nil,
|
|
liveCredits: nil,
|
|
rawCreditsError: nil,
|
|
liveDashboard: nil,
|
|
rawDashboardError: nil,
|
|
dashboardAttachmentAuthorized: false,
|
|
dashboardRequiresLogin: false,
|
|
now: now))
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .codex,
|
|
metadata: metadata,
|
|
snapshot: updatedSnap,
|
|
codexProjection: codexProjection,
|
|
credits: CreditsSnapshot(remaining: 12, events: [], updatedAt: now),
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: "codex@example.com", plan: "Plus Plan"),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
quotaWarningThresholds: [.session: [50, 20], .weekly: [25, 0]],
|
|
now: now))
|
|
|
|
#expect(model.providerName == "Codex")
|
|
#expect(model.metrics.count == 2)
|
|
#expect(model.metrics.first?.percent == 78)
|
|
#expect(model.metrics.first?.warningMarkerPercents == [50, 20])
|
|
#expect(model.metrics[1].warningMarkerPercents == [25])
|
|
#expect(model.planText == "Plus")
|
|
#expect(model.subtitleText.hasPrefix("Updated"))
|
|
#expect(model.progressColor != Color.clear)
|
|
#expect(model.metrics[1].resetText?.isEmpty == false)
|
|
}
|
|
|
|
@Test
|
|
func `claude model hides weekly when unavailable`() throws {
|
|
let now = Date()
|
|
let identity = ProviderIdentitySnapshot(
|
|
providerID: .claude,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: "Max")
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(
|
|
usedPercent: 2,
|
|
windowMinutes: nil,
|
|
resetsAt: now.addingTimeInterval(3600),
|
|
resetDescription: nil),
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
updatedAt: now,
|
|
identity: identity)
|
|
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .claude,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: "codex@example.com", plan: "plus"),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.metrics.count == 1)
|
|
#expect(model.metrics.first?.title == "Session")
|
|
#expect(model.planText == "Max")
|
|
}
|
|
|
|
@Test
|
|
func `claude model includes routines bar when present`() throws {
|
|
let now = Date()
|
|
let identity = ProviderIdentitySnapshot(
|
|
providerID: .claude,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: "Max")
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(
|
|
usedPercent: 2,
|
|
windowMinutes: nil,
|
|
resetsAt: now.addingTimeInterval(3600),
|
|
resetDescription: nil),
|
|
secondary: RateWindow(
|
|
usedPercent: 8,
|
|
windowMinutes: 10080,
|
|
resetsAt: now.addingTimeInterval(7200),
|
|
resetDescription: nil),
|
|
tertiary: RateWindow(
|
|
usedPercent: 16,
|
|
windowMinutes: 10080,
|
|
resetsAt: now.addingTimeInterval(7800),
|
|
resetDescription: nil),
|
|
extraRateWindows: [
|
|
NamedRateWindow(
|
|
id: "claude-routines",
|
|
title: "Daily Routines",
|
|
window: RateWindow(
|
|
usedPercent: 7,
|
|
windowMinutes: 10080,
|
|
resetsAt: now.addingTimeInterval(9200),
|
|
resetDescription: nil)),
|
|
],
|
|
updatedAt: now,
|
|
identity: identity)
|
|
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .claude,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: "codex@example.com", plan: "plus"),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.metrics.map(\.title) == ["Session", "Weekly", "Sonnet", "Daily Routines"])
|
|
}
|
|
|
|
@Test
|
|
func `shows error subtitle when present`() throws {
|
|
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .codex,
|
|
metadata: metadata,
|
|
snapshot: nil,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: "Probe failed for Codex",
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: Date()))
|
|
|
|
#expect(model.subtitleStyle == .error)
|
|
#expect(model.subtitleText.contains("Probe failed"))
|
|
#expect(model.placeholder == nil)
|
|
}
|
|
|
|
@Test
|
|
func `cost section includes last30 days tokens`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 0, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
updatedAt: now)
|
|
let tokenSnapshot = CostUsageTokenSnapshot(
|
|
sessionTokens: 123,
|
|
sessionCostUSD: 1.23,
|
|
last30DaysTokens: 456,
|
|
last30DaysCostUSD: 78.9,
|
|
daily: [],
|
|
updatedAt: now)
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .codex,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: tokenSnapshot,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: true,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.tokenUsage?.monthLine.contains("456") == true)
|
|
#expect(model.tokenUsage?.monthLine.contains("tokens") == true)
|
|
#expect(model.tokenUsage?.hintLine == "Estimated from local Codex logs for the selected account.")
|
|
}
|
|
|
|
@Test
|
|
func `claude model does not leak codex plan`() throws {
|
|
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .claude,
|
|
metadata: metadata,
|
|
snapshot: nil,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: "codex@example.com", plan: "plus"),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: Date()))
|
|
|
|
#expect(model.planText == nil)
|
|
#expect(model.email.isEmpty)
|
|
}
|
|
|
|
@Test
|
|
func `hides claude extra usage when disabled`() throws {
|
|
let now = Date()
|
|
let identity = ProviderIdentitySnapshot(
|
|
providerID: .claude,
|
|
accountEmail: "claude@example.com",
|
|
accountOrganization: nil,
|
|
loginMethod: nil)
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 0, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
providerCost: ProviderCostSnapshot(used: 12, limit: 200, currencyCode: "USD", updatedAt: now),
|
|
updatedAt: now,
|
|
identity: identity)
|
|
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .claude,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: false,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.providerCost == nil)
|
|
}
|
|
|
|
@Test
|
|
@MainActor
|
|
func `open router model uses API key quota bar and quota detail`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.openrouter])
|
|
let snapshot = OpenRouterUsageSnapshot(
|
|
totalCredits: 50,
|
|
totalUsage: 45.3895596325,
|
|
balance: 4.6104403675,
|
|
usedPercent: 90.779119265,
|
|
keyLimit: 20,
|
|
keyUsage: 0.5,
|
|
rateLimit: nil,
|
|
updatedAt: now).toUsageSnapshot()
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .openrouter,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.creditsText == nil)
|
|
#expect(model.metrics.count == 1)
|
|
#expect(model.usageNotes.isEmpty)
|
|
let metric = try #require(model.metrics.first)
|
|
let popupTitle = UsageMenuCardView.popupMetricTitle(
|
|
provider: .openrouter,
|
|
metric: metric)
|
|
#expect(popupTitle == "API key limit")
|
|
#expect(metric.resetText == "$19.50/$20.00 left")
|
|
#expect(metric.detailRightText == nil)
|
|
}
|
|
|
|
@Test
|
|
func `open router model without key limit shows text only summary`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.openrouter])
|
|
let snapshot = OpenRouterUsageSnapshot(
|
|
totalCredits: 50,
|
|
totalUsage: 45.3895596325,
|
|
balance: 4.6104403675,
|
|
usedPercent: 90.779119265,
|
|
keyDataFetched: true,
|
|
keyLimit: nil,
|
|
keyUsage: nil,
|
|
rateLimit: nil,
|
|
updatedAt: now).toUsageSnapshot()
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .openrouter,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.metrics.isEmpty)
|
|
#expect(model.creditsText == nil)
|
|
#expect(model.placeholder == nil)
|
|
#expect(model.usageNotes == ["No limit set for the API key"])
|
|
}
|
|
|
|
@Test
|
|
func `open router model when key fetch unavailable shows unavailable note`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.openrouter])
|
|
let snapshot = OpenRouterUsageSnapshot(
|
|
totalCredits: 50,
|
|
totalUsage: 45.3895596325,
|
|
balance: 4.6104403675,
|
|
usedPercent: 90.779119265,
|
|
keyDataFetched: false,
|
|
keyLimit: nil,
|
|
keyUsage: nil,
|
|
rateLimit: nil,
|
|
updatedAt: now).toUsageSnapshot()
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .openrouter,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.metrics.isEmpty)
|
|
#expect(model.usageNotes == ["API key limit unavailable right now"])
|
|
}
|
|
|
|
@Test
|
|
func `hides email when personal info hidden`() throws {
|
|
let now = Date()
|
|
let identity = ProviderIdentitySnapshot(
|
|
providerID: .codex,
|
|
accountEmail: "codex@example.com",
|
|
accountOrganization: nil,
|
|
loginMethod: nil)
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 0, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
updatedAt: now,
|
|
identity: identity)
|
|
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .codex,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: "OpenAI dashboard signed in as codex@example.com.",
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: "codex@example.com", plan: nil),
|
|
isRefreshing: false,
|
|
lastError: "OpenAI dashboard signed in as codex@example.com.",
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: true,
|
|
now: now))
|
|
|
|
#expect(model.email.isEmpty)
|
|
#expect(model.subtitleText.contains("codex@example.com") == false)
|
|
#expect(model.creditsHintCopyText?.isEmpty == true)
|
|
#expect(model.creditsHintText?.contains("codex@example.com") == false)
|
|
}
|
|
|
|
@Test
|
|
func `kilo model splits pass and activity and shows fallback note`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.kilo])
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(
|
|
usedPercent: 40,
|
|
windowMinutes: nil,
|
|
resetsAt: nil,
|
|
resetDescription: "40/100 credits"),
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
updatedAt: now,
|
|
identity: ProviderIdentitySnapshot(
|
|
providerID: .kilo,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: "Kilo Pass Pro · Auto top-up: visa"))
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .kilo,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
sourceLabel: "cli",
|
|
kiloAutoMode: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.planText == "Kilo Pass Pro")
|
|
#expect(model.usageNotes.contains("Auto top-up: visa"))
|
|
#expect(model.usageNotes.contains("Using CLI fallback"))
|
|
}
|
|
|
|
@Test
|
|
func `kilo model treats auto top up only login as activity`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.kilo])
|
|
let snapshot = UsageSnapshot(
|
|
primary: nil,
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
updatedAt: now,
|
|
identity: ProviderIdentitySnapshot(
|
|
providerID: .kilo,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: "Auto top-up: off"))
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .kilo,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(model.planText == nil)
|
|
#expect(model.usageNotes.contains("Auto top-up: off"))
|
|
}
|
|
|
|
@Test
|
|
func `kilo model does not show fallback note when not auto to CLI`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.kilo])
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(
|
|
usedPercent: 40,
|
|
windowMinutes: nil,
|
|
resetsAt: nil,
|
|
resetDescription: "40/100 credits"),
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
updatedAt: now,
|
|
identity: ProviderIdentitySnapshot(
|
|
providerID: .kilo,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: "Kilo Pass Pro · Auto top-up: visa"))
|
|
|
|
let apiModel = UsageMenuCardView.Model.make(.init(
|
|
provider: .kilo,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
sourceLabel: "api",
|
|
kiloAutoMode: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
let nonAutoModel = UsageMenuCardView.Model.make(.init(
|
|
provider: .kilo,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
sourceLabel: "cli",
|
|
kiloAutoMode: false,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
#expect(!apiModel.usageNotes.contains("Using CLI fallback"))
|
|
#expect(!nonAutoModel.usageNotes.contains("Using CLI fallback"))
|
|
}
|
|
|
|
@Test
|
|
func `kilo model shows primary detail when reset date missing`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.kilo])
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(
|
|
usedPercent: 10,
|
|
windowMinutes: nil,
|
|
resetsAt: nil,
|
|
resetDescription: "10/100 credits"),
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
updatedAt: now,
|
|
identity: ProviderIdentitySnapshot(
|
|
providerID: .kilo,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: "Kilo Pass Pro"))
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .kilo,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
let primary = try #require(model.metrics.first)
|
|
#expect(primary.resetText == nil)
|
|
#expect(primary.detailText == "10/100 credits")
|
|
}
|
|
|
|
@Test
|
|
func `kilo model keeps zero total edge state visible`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.kilo])
|
|
let snapshot = KiloUsageSnapshot(
|
|
creditsUsed: 0,
|
|
creditsTotal: 0,
|
|
creditsRemaining: 0,
|
|
planName: "Kilo Pass Pro",
|
|
autoTopUpEnabled: true,
|
|
autoTopUpMethod: "visa",
|
|
updatedAt: now).toUsageSnapshot()
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .kilo,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
let primary = try #require(model.metrics.first)
|
|
#expect(primary.percent == 0)
|
|
#expect(primary.detailText == "0/0 credits")
|
|
#expect(model.placeholder == nil)
|
|
}
|
|
|
|
@Test
|
|
func `warp model shows primary detail when reset date missing`() throws {
|
|
let now = Date()
|
|
let identity = ProviderIdentitySnapshot(
|
|
providerID: .warp,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: nil)
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(
|
|
usedPercent: 10,
|
|
windowMinutes: nil,
|
|
resetsAt: nil,
|
|
resetDescription: "10/100 credits"),
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
updatedAt: now,
|
|
identity: identity)
|
|
let metadata = try #require(ProviderDefaults.metadata[.warp])
|
|
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .warp,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: true,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
let primary = try #require(model.metrics.first)
|
|
#expect(primary.resetText == nil)
|
|
#expect(primary.detailText == "10/100 credits")
|
|
}
|
|
}
|