Files
ccs/macos-bar/Sources/CCSBarCheck/main.swift
T
Tam Nhu Tran df967792b9 feat(bar-app): bar-first quota card with sane time formatting
Replace the prose window lines with an aligned per-window bar list (5h /
weekly / Opus / Sonnet), colored by headroom, with the binding window
highlighted and a compact reset chip. Humanize durations beyond a day
(Nd Nh instead of raw hours) and only show the burn-rate pace clause when a
window is projected to exhaust before it resets, so a meaningless 'resets in
44h' / '~110h left' projection no longer appears.
2026-06-09 15:07:08 -04:00

1267 lines
57 KiB
Swift

import Foundation
import CCSBarCore
// Lightweight assert harness used in place of XCTest (unavailable without a
// full Xcode install). Run with `swift run ccs-bar-check`; exits non-zero on
// any failure so it works as a CI/test gate on a CommandLineTools toolchain.
var failures = 0
func check(_ condition: Bool, _ message: String) {
if condition {
print("[OK] \(message)")
} else {
print("[X] \(message)")
failures += 1
}
}
// Recording transport so CCSBarClient can be exercised without a live server.
final class RequestRecorder: @unchecked Sendable {
var lastRequest: URLRequest?
var responseData = Data("[]".utf8)
var status = 200
}
struct RecordingTransport: HTTPTransport {
let recorder: RequestRecorder
func send(_ request: URLRequest) async throws -> (Data, HTTPURLResponse) {
recorder.lastRequest = request
let http = HTTPURLResponse(
url: request.url!, statusCode: recorder.status, httpVersion: nil, headerFields: nil)!
return (recorder.responseData, http)
}
}
// MARK: BarSummaryRow decoding (mixed snake_case / camelCase keys)
let summaryJSON = """
[
{
"account_id": "alice@example.com",
"provider": "agy",
"displayName": "Alice (Ultra)",
"tier": "ultra",
"paused": false,
"quota_percentage": 82.4,
"quotaStatus": "ok",
"next_reset": "2026-06-08T00:00:00Z",
"is_default": true,
"last_activity_at": "2026-04-29T10:00:00Z",
"today_cost": 3.2,
"health": "ok",
"cached": true,
"fetchedAt": "2026-06-07T19:00:00Z",
"needsReauth": false
},
{
"account_id": "bob@example.com",
"provider": "codex",
"displayName": null,
"tier": null,
"paused": true,
"quota_percentage": null,
"quotaStatus": "error",
"next_reset": null,
"is_default": false,
"last_activity_at": null,
"today_cost": null,
"health": "error",
"cached": false,
"fetchedAt": null,
"needsReauth": true
}
]
"""
do {
let rows = try JSONDecoder().decode([BarSummaryRow].self, from: Data(summaryJSON.utf8))
check(rows.count == 2, "decodes two rows")
check(rows[0].accountId == "alice@example.com", "maps account_id")
check(rows[0].quotaPercentage == 82.4, "maps quota_percentage")
check(rows[0].quotaStatus == "ok", "maps quotaStatus (ok)")
check(rows[0].isDefault == true, "maps is_default")
check(rows[0].lastActivityAt == "2026-04-29T10:00:00Z", "maps last_activity_at")
check(rows[0].todayCost == 3.2, "maps today_cost")
check(rows[0].id == "agy:alice@example.com", "stable id is provider:account")
check(rows[1].quotaPercentage == nil, "null quota decodes to nil")
check(rows[1].quotaStatus == "error", "maps quotaStatus (error)")
check(rows[1].isDefault == false, "is_default false decodes")
check(rows[1].lastActivityAt == nil, "null last_activity_at decodes to nil")
check(rows[1].paused == true, "maps paused")
check(rows[1].needsReauth == true, "maps needsReauth")
check(rows[1].healthDot == "X", "error -> X dot")
check(rows[0].healthDot == "OK", "ok -> OK dot")
}
// An "unsupported" provider (ghcp/kiro) decodes with quotaStatus "unsupported"
// and health "ok" — the backend's key correctness fix (no permanent orange dot).
do {
let unsupportedJSON = """
[{
"account_id": "copilot-kaitranntt", "provider": "ghcp", "displayName": "copilot-kaitranntt",
"tier": null, "paused": false, "quota_percentage": null, "quotaStatus": "unsupported",
"next_reset": null, "is_default": true, "last_activity_at": null, "today_cost": null,
"health": "ok", "cached": false, "fetchedAt": "2026-06-08T16:40:00.000Z", "needsReauth": false
}]
"""
let rows = try JSONDecoder().decode([BarSummaryRow].self, from: Data(unsupportedJSON.utf8))
check(rows[0].quotaStatus == "unsupported", "ghcp decodes quotaStatus unsupported")
check(rows[0].health == "ok", "unsupported provider is health ok (no orange dot)")
}
// MARK: BarDiscovery loading
let tmp = NSTemporaryDirectory() + "ccs-bar-check-\(ProcessInfo.processInfo.globallyUniqueString)"
let ccsDir = tmp + "/.ccs"
try? FileManager.default.createDirectory(atPath: ccsDir, withIntermediateDirectories: true)
let barJSON = """
{ "baseUrl": "http://127.0.0.1:3210", "port": 3210, "authMode": "loopback" }
"""
try? barJSON.write(toFile: ccsDir + "/bar.json", atomically: true, encoding: .utf8)
switch BarDiscovery.load(home: tmp) {
case .success(let d):
check(d.port == 3210, "discovery reads port")
check(d.authMode == "loopback", "discovery reads authMode")
check(d.resolvedURL?.absoluteString == "http://127.0.0.1:3210", "resolvedURL from baseUrl")
case .failure(let e):
check(false, "discovery load failed: \(e)")
}
let missingHome = NSTemporaryDirectory() + "ccs-bar-check-missing-\(ProcessInfo.processInfo.globallyUniqueString)"
switch BarDiscovery.load(home: missingHome) {
case .failure(.missing):
check(true, "absent bar.json -> .missing (offline state)")
case .success:
check(false, "expected .missing for absent file")
case .failure(let e):
check(false, "expected .missing, got \(e)")
}
// MARK: BarFormatting
// Tri-state quota label: never a bare "--".
check(BarFormatting.quotaLabel(percentage: 82.4, status: "ok") == "82%", "ok+pct -> NN%")
check(
BarFormatting.quotaLabel(percentage: nil, status: "unsupported") == "no quota",
"unsupported -> 'no quota'")
check(
BarFormatting.quotaLabel(percentage: nil, status: "error") == "quota ?", "error -> 'quota ?'")
check(BarFormatting.costLabel(3.2) == "$3.20", "cost label formats")
check(BarFormatting.costLabel(0) == "", "zero cost hidden")
check(BarFormatting.costLabel(nil) == "", "nil cost hidden")
// MARK: statusTitle fallback chain (NEVER a bare "--", NEVER a lifetime figure when today==0)
// Shared analytics fixtures for the chain.
func mkAnalytics(allTimeCost: Double, todayCost: Double = 0) -> BarAnalytics {
BarAnalytics(
today: .init(cost: todayCost, requests: todayCost > 0 ? 10 : 0),
last7d: .init(cost: 0, requests: 0),
last30d: .init(cost: 0, requests: 0),
allTime: .init(cost: allTimeCost, requests: 5314),
byDay: [],
topModels: [],
topModelsWindow: "all",
lastActivityAt: "2026-04-29T10:00:00Z",
daysSinceLastActivity: 40,
hasRecentData: false,
generatedAt: "2026-06-08T13:00:00Z")
}
// (1) QUOTA wins: a quota row yields "<provider> NN%".
do {
let rows = try JSONDecoder().decode([BarSummaryRow].self, from: Data(summaryJSON.utf8))
let title = BarFormatting.statusTitle(rows: rows, analytics: mkAnalytics(allTimeCost: 2609.1))
check(title == "agy 82%", "title (1) quota wins -> 'agy 82%'")
}
// (1) lowest-remaining among quota rows is chosen; unsupported/error skipped.
let quotaMix = [
BarSummaryRow(accountId: "a", provider: "agy", quotaPercentage: 90, quotaStatus: "ok"),
BarSummaryRow(accountId: "b", provider: "agy", quotaPercentage: 12, quotaStatus: "ok"),
BarSummaryRow(accountId: "c", provider: "ghcp", quotaStatus: "unsupported"),
]
let mixTitle = BarFormatting.statusTitle(rows: quotaMix, analytics: nil)
check(mixTitle == "agy 12%", "title features lowest-remaining quota, skips unsupported")
check(!mixTitle.contains("--"), "quota-mix title never contains '--'")
// (2) TODAY COST wins when no quota but analytics.today.cost > 0.
// The title reads the fresh analytics aggregate, not per-row today_cost.
let todayRows = [
BarSummaryRow(accountId: "a", provider: "ghcp", quotaStatus: "unsupported"),
]
let todayAnalytics = mkAnalytics(allTimeCost: 40844, todayCost: 3.2)
check(
BarFormatting.statusTitle(rows: todayRows, analytics: todayAnalytics) == "$3.20",
"title (2) today cost from analytics -> '$3.20'")
// (2) today cost zero with non-zero all-time must NOT produce a "~$..." lifetime
// figure — the all-time step was removed to prevent confusion with live spend.
let staleRows = [
BarSummaryRow(accountId: "a", provider: "ghcp", quotaStatus: "unsupported", todayCost: 0),
BarSummaryRow(accountId: "b", provider: "kiro", quotaStatus: "unsupported", todayCost: nil),
]
let staleAnalytics = mkAnalytics(allTimeCost: 2609.1, todayCost: 0)
let staleTitle = BarFormatting.statusTitle(rows: staleRows, analytics: staleAnalytics)
check(!staleTitle.contains("~$"), "today==0 title never shows a '~$' lifetime figure")
check(!staleTitle.contains("2.6k"), "today==0 title never shows the all-time dollar amount")
check(!staleTitle.contains("--"), "stale title never contains '--'")
// With no quota and today==0 and 2 active rows, chain falls to COUNT.
check(staleTitle == "CCS 2", "title (3) no quota + today==0 falls to active count 'CCS 2'")
// (3) COUNT / ATTENTION when no quota and no today spend.
let reauthRows = [
BarSummaryRow(accountId: "a", provider: "ghcp", quotaStatus: "error", needsReauth: true),
BarSummaryRow(accountId: "b", provider: "kiro", quotaStatus: "unsupported"),
]
check(
BarFormatting.statusTitle(rows: reauthRows, analytics: mkAnalytics(allTimeCost: 0)) == "CCS 1!",
"title (3) reauth attention -> 'CCS 1!'")
let activeRows = [
BarSummaryRow(accountId: "a", provider: "ghcp", quotaStatus: "unsupported"),
BarSummaryRow(accountId: "b", provider: "kiro", paused: true, quotaStatus: "unsupported"),
]
check(
BarFormatting.statusTitle(rows: activeRows, analytics: nil) == "CCS 1",
"title (3) active count -> 'CCS 1'")
// (4) empty rows -> "CCS".
check(BarFormatting.statusTitle(rows: [], analytics: nil) == "CCS", "title (4) empty -> 'CCS'")
// All-empty-quota fixture: title never a bare "--".
check(
!BarFormatting.statusTitle(rows: activeRows, analytics: nil).contains("--"),
"no-quota title never contains a bare '--'")
// leadRow is deterministic with no quota: the is_default row, not rows.first.
let leadRows = [
BarSummaryRow(accountId: "z", provider: "ghcp", quotaStatus: "unsupported", isDefault: false),
BarSummaryRow(accountId: "a", provider: "kiro", quotaStatus: "unsupported", isDefault: true),
]
check(BarFormatting.leadRow(leadRows)?.accountId == "a", "leadRow prefers is_default row")
// MARK: RefreshDebouncer (arms at decision time)
var deb = RefreshDebouncer(interval: 15)
let t0 = Date(timeIntervalSince1970: 1000)
check(deb.shouldRefresh(now: t0), "first force-refresh proceeds")
check(!deb.shouldRefresh(now: t0.addingTimeInterval(5)), "within 15s blocked")
check(!deb.shouldRefresh(now: t0.addingTimeInterval(14.9)), "just before window end blocked")
check(deb.shouldRefresh(now: t0.addingTimeInterval(15)), "at/after 15s proceeds")
// MARK: CCSBarClient (recording transport)
let recorder = RequestRecorder()
recorder.responseData = Data(summaryJSON.utf8)
let client = CCSBarClient(
baseURL: URL(string: "http://127.0.0.1:3210")!,
transport: RecordingTransport(recorder: recorder)
)
do {
let rows = try await client.summary(refresh: true)
check(rows.count == 2, "client.summary decodes rows")
check(
recorder.lastRequest?.url?.query?.contains("refresh=true") == true,
"summary(refresh: true) adds ?refresh=true")
} catch {
check(false, "client.summary threw: \(error)")
}
recorder.responseData = Data("{}".utf8)
recorder.lastRequest = nil
do {
try await client.pause(provider: "agy", accountId: "alice@example.com")
check(recorder.lastRequest?.httpMethod == "POST", "pause is POST")
check(
recorder.lastRequest?.url?.path.hasSuffix("bulk-pause") == true,
"pause hits bulk-pause endpoint")
} catch {
check(false, "pause threw: \(error)")
}
recorder.lastRequest = nil
do {
try await client.tierLock(provider: "agy", tier: nil)
let body = recorder.lastRequest?.httpBody ?? Data()
let obj = (try? JSONSerialization.jsonObject(with: body)) as? [String: Any]
check(obj?["tier"] is NSNull, "tierLock(nil) serializes tier: null")
check((obj?["provider"] as? String) == "agy", "tierLock sends provider")
} catch {
check(false, "tierLock threw: \(error)")
}
recorder.status = 409
do {
_ = try await client.summary(refresh: false)
check(false, "non-200 summary should throw")
} catch CCSBarClientError.httpStatus(let code) {
check(code == 409, "non-200 -> httpStatus(409)")
} catch {
check(false, "wrong error type: \(error)")
}
recorder.status = 200
// Analytics formatting + decode.
check(BarFormatting.money(0) == "$0.00", "money shows zero")
check(BarFormatting.money(2609.1) == "$2.6k", "money compacts thousands")
check(BarFormatting.count(5314) == "5.3k", "count compacts thousands")
// Mirrors the stale-snapshot reality: recent windows zero, all-time populated,
// hasRecentData false, last-active in late April, 30-entry zero-filled byDay.
func buildByDayJSON(count: Int) -> String {
(0..<count).map { i in "{\"date\":\"2026-05-\(String(format: "%02d", i + 1))\",\"cost\":0,\"requests\":0}" }
.joined(separator: ",")
}
let analyticsJSON = """
{"today":{"cost":0,"requests":0},"last7d":{"cost":0,"requests":0},
"last30d":{"cost":0,"requests":0},"allTime":{"cost":2609.1,"requests":5314},
"byDay":[\(buildByDayJSON(count: 30))],
"topModels":[{"model":"gpt-5.4","cost":1253.65,"requests":1306}],
"topModelsWindow":"all","lastActivityAt":"2026-04-29T10:00:00.000Z",
"daysSinceLastActivity":40,"hasRecentData":false,
"generatedAt":"2026-06-08T13:00:00.000Z"}
""".data(using: .utf8)!
do {
let a = try JSONDecoder().decode(BarAnalytics.self, from: analyticsJSON)
check(a.allTime.requests == 5314, "analytics decodes all-time requests")
check(a.topModels.first?.model == "gpt-5.4", "analytics decodes top model")
check(a.topModelsWindow == "all", "analytics decodes window")
check(a.byDay.count == 30, "analytics decodes 30-day byDay series")
check(a.lastActivityAt == "2026-04-29T10:00:00.000Z", "analytics decodes lastActivityAt")
check(a.daysSinceLastActivity == 40, "analytics decodes daysSinceLastActivity")
check(a.hasRecentData == false, "analytics decodes hasRecentData (false on idle)")
// bySurface absent from JSON → defaults to empty array (resilient decoding).
check(a.bySurface.isEmpty, "analytics bySurface defaults to [] when absent from payload")
}
// MARK: BarAnalyticsSurface decoding
// Verify bySurface round-trips correctly with the exact field names the backend
// sends: `source`, `surface`, `cost`, `requests` (all camelCase / exact match).
let surfaceAnalyticsJSON = """
{"today":{"cost":1963.70,"requests":13103},"last7d":{"cost":1963.70,"requests":13103},
"last30d":{"cost":35620.0,"requests":70973},"allTime":{"cost":40844.0,"requests":91207},
"byDay":[\(buildByDayJSON(count: 30))],
"topModels":[],"topModelsWindow":"30d",
"hasRecentData":true,"generatedAt":"2026-06-09T00:00:00.000Z",
"bySurface":[
{"source":"custom-parser","surface":"Claude Code","cost":33656.21,"requests":57870},
{"source":"codex-native","surface":"Codex","cost":1963.70,"requests":13103}
]}
""".data(using: .utf8)!
do {
let a = try JSONDecoder().decode(BarAnalytics.self, from: surfaceAnalyticsJSON)
check(a.bySurface.count == 2, "bySurface decodes 2 surfaces")
check(a.bySurface[0].surface == "Claude Code", "bySurface[0].surface decodes")
check(a.bySurface[0].source == "custom-parser", "bySurface[0].source decodes")
check(a.bySurface[0].cost == 33656.21, "bySurface[0].cost decodes")
check(a.bySurface[0].requests == 57870, "bySurface[0].requests decodes")
check(a.bySurface[1].surface == "Codex", "bySurface[1].surface decodes")
check(a.bySurface[1].cost == 1963.70, "bySurface[1].cost decodes")
// Stable identity for SwiftUI ForEach: surface name is the id.
check(a.bySurface[0].id == "Claude Code", "bySurface[0].id == surface name")
// Today cost from the surface-bearing payload.
check(a.today.cost == 1963.70, "surface analytics decodes today.cost")
check(a.hasRecentData == true, "surface analytics decodes hasRecentData true")
} catch {
check(false, "surface analytics decode threw: \(error)")
}
// MARK: monthToDate decode (calendar MTD field, resilient default)
do {
// Older payload WITHOUT monthToDate → decodes to a zero window (back-compat).
let a = try JSONDecoder().decode(BarAnalytics.self, from: analyticsJSON)
check(a.monthToDate.cost == 0, "monthToDate defaults to 0 cost when absent from payload")
check(a.monthToDate.requests == 0, "monthToDate defaults to 0 requests when absent")
}
do {
// Newer payload WITH monthToDate → decodes the real value, distinct from last30d.
let mtdJSON = """
{"today":{"cost":0,"requests":0},"last7d":{"cost":0,"requests":0},
"last30d":{"cost":35620.0,"requests":70973},"monthToDate":{"cost":1200.5,"requests":4096},
"allTime":{"cost":40844.0,"requests":91207},
"byDay":[\(buildByDayJSON(count: 30))],
"topModels":[],"topModelsWindow":"30d","hasRecentData":true,
"generatedAt":"2026-06-09T00:00:00.000Z"}
""".data(using: .utf8)!
let a = try JSONDecoder().decode(BarAnalytics.self, from: mtdJSON)
check(a.monthToDate.cost == 1200.5, "monthToDate decodes its own cost")
check(a.monthToDate.requests == 4096, "monthToDate decodes its own requests")
check(a.monthToDate.cost != a.last30d.cost, "monthToDate is distinct from rolling last30d")
}
// MARK: compactDuration three-tier formatting
check(BarQuotaGauge.compactDuration(minutes: 35) == "35m", "compactDuration <1h -> minutes only")
check(BarQuotaGauge.compactDuration(minutes: 60) == "1h 0m", "compactDuration 1h exactly")
check(BarQuotaGauge.compactDuration(minutes: 195) == "3h 15m", "compactDuration hours+minutes")
// 44h 38m = 2678 min → 1d 20h (44%24=20, 44/24=1)
check(BarQuotaGauge.compactDuration(minutes: 2678) == "1d 20h", "compactDuration 44h38m -> '1d 20h'")
// 110h 27m = 6627 min → 4d 14h (110/24=4, 110%24=14)
check(BarQuotaGauge.compactDuration(minutes: 6627) == "4d 14h", "compactDuration 110h27m -> '4d 14h'")
check(BarQuotaGauge.compactDuration(minutes: 1440) == "1d 0h", "compactDuration exactly 24h -> '1d 0h'")
// MARK: BarQuotaGauge band + fillFraction + countdown
check(BarQuotaGauge.band(percentage: 82, status: "ok") == .green, "band >50 -> green")
check(BarQuotaGauge.band(percentage: 51, status: "ok") == .green, "band 51 -> green (boundary)")
check(BarQuotaGauge.band(percentage: 50, status: "ok") == .yellow, "band 50 -> yellow (boundary)")
check(BarQuotaGauge.band(percentage: 21, status: "ok") == .yellow, "band 21 -> yellow")
check(BarQuotaGauge.band(percentage: 20, status: "ok") == .orange, "band 20 -> orange (boundary)")
check(BarQuotaGauge.band(percentage: 11, status: "ok") == .orange, "band 11 -> orange")
check(BarQuotaGauge.band(percentage: 10, status: "ok") == .red, "band 10 -> red (boundary)")
check(BarQuotaGauge.band(percentage: 0, status: "ok") == .red, "band 0 -> red")
check(BarQuotaGauge.band(percentage: nil, status: "ok") == .none, "band nil pct -> none")
check(BarQuotaGauge.band(percentage: 50, status: "unsupported") == .none, "band unsupported -> none")
check(BarQuotaGauge.band(percentage: 50, status: "error") == .none, "band error -> none")
check(BarQuotaGauge.fillFraction(percentage: 82, status: "ok") == 0.82, "fillFraction 82 -> 0.82")
check(BarQuotaGauge.fillFraction(percentage: 0, status: "ok") == 0.0, "fillFraction 0 -> 0")
check(BarQuotaGauge.fillFraction(percentage: 150, status: "ok") == 1.0, "fillFraction clamps >100 to 1")
check(BarQuotaGauge.fillFraction(percentage: nil, status: "ok") == nil, "fillFraction nil pct -> nil")
check(
BarQuotaGauge.fillFraction(percentage: 50, status: "unsupported") == nil,
"fillFraction unsupported -> nil")
do {
let now = Date(timeIntervalSince1970: 1_000_000)
let in3h12m = now.addingTimeInterval(3 * 3600 + 12 * 60)
let iso = ISO8601DateFormatter().string(from: in3h12m)
check(
BarQuotaGauge.resetCountdown(nextReset: iso, now: now) == "resets in 3h 12m",
"resetCountdown formats hours+minutes")
let in12m = now.addingTimeInterval(12 * 60)
let iso12 = ISO8601DateFormatter().string(from: in12m)
check(
BarQuotaGauge.resetCountdown(nextReset: iso12, now: now) == "resets in 12m",
"resetCountdown formats minutes-only")
// Days tier: >=24h -> "Nd Nh" — 44h 38m -> "1d 20h", 110h 27m -> "4d 14h".
let in44h38m = now.addingTimeInterval(44 * 3600 + 38 * 60)
let iso44h38m = ISO8601DateFormatter().string(from: in44h38m)
check(
BarQuotaGauge.resetCountdown(nextReset: iso44h38m, now: now) == "resets in 1d 20h",
"resetCountdown >=24h -> days tier '1d 20h'")
let in110h27m = now.addingTimeInterval(110 * 3600 + 27 * 60)
let iso110h27m = ISO8601DateFormatter().string(from: in110h27m)
check(
BarQuotaGauge.resetCountdown(nextReset: iso110h27m, now: now) == "resets in 4d 14h",
"resetCountdown large duration '4d 14h'")
let past = now.addingTimeInterval(-60)
let isoPast = ISO8601DateFormatter().string(from: past)
check(
BarQuotaGauge.resetCountdown(nextReset: isoPast, now: now) == "resets soon",
"resetCountdown past -> 'resets soon'")
check(BarQuotaGauge.resetCountdown(nextReset: nil, now: now) == nil, "resetCountdown nil -> nil")
check(
BarQuotaGauge.resetCountdown(nextReset: "not-a-date", now: now) == nil,
"resetCountdown unparseable -> nil")
}
// MARK: Glance-mode title resolution (lifetime dollar NEVER appears)
do {
let allTimeBig = mkAnalytics(allTimeCost: 40844, todayCost: 0) // allTime is the largest window
let lifetime = BarFormatting.money(allTimeBig.allTime.cost)
let rows = [
BarSummaryRow(accountId: "a", provider: "ghcp", quotaStatus: "unsupported"),
BarSummaryRow(accountId: "b", provider: "kiro", quotaStatus: "unsupported"),
]
// For EVERY mode, the title must never be the lifetime figure.
for mode in BarGlanceMode.allCases {
let t = BarFormatting.statusTitle(rows: rows, analytics: allTimeBig, mode: mode)
check(t != lifetime, "glance mode \(mode.rawValue): title never == lifetime dollar")
}
// .todaySpend leads with today when > 0, else falls back to .auto chain.
let todayA = mkAnalytics(allTimeCost: 40844, todayCost: 3.2)
check(
BarFormatting.statusTitle(rows: rows, analytics: todayA, mode: .todaySpend) == "$3.20",
"glance .todaySpend leads with today's spend")
check(
BarFormatting.statusTitle(rows: rows, analytics: allTimeBig, mode: .todaySpend) == "CCS 2",
"glance .todaySpend falls back to .auto when today==0")
// .monthSpend leads with monthToDate when > 0, else falls back.
func mkMonth(_ mtd: Double, today: Double = 0) -> BarAnalytics {
BarAnalytics(
today: .init(cost: today, requests: 0),
last7d: .init(cost: 0, requests: 0),
last30d: .init(cost: 35000, requests: 0),
monthToDate: .init(cost: mtd, requests: 0),
allTime: .init(cost: 40844, requests: 0),
byDay: [], topModels: [], topModelsWindow: "30d",
generatedAt: "2026-06-09T00:00:00Z")
}
check(
BarFormatting.statusTitle(rows: rows, analytics: mkMonth(1200.5), mode: .monthSpend)
== BarFormatting.money(1200.5),
"glance .monthSpend leads with calendar MTD")
check(
BarFormatting.statusTitle(rows: rows, analytics: mkMonth(0), mode: .monthSpend) == "CCS 2",
"glance .monthSpend falls back to .auto when MTD==0")
// MTD mode must NOT leak last30d (35000) even though it is far larger.
let mtdTitle = BarFormatting.statusTitle(rows: rows, analytics: mkMonth(1200.5), mode: .monthSpend)
check(!mtdTitle.contains("35"), "glance .monthSpend never shows last30d figure")
// .lowestQuota leads with the lowest ok-quota row, else falls back.
let quotaRows = [
BarSummaryRow(accountId: "a", provider: "agy", quotaPercentage: 90, quotaStatus: "ok"),
BarSummaryRow(accountId: "b", provider: "agy", quotaPercentage: 12, quotaStatus: "ok"),
]
check(
BarFormatting.statusTitle(rows: quotaRows, analytics: nil, mode: .lowestQuota) == "agy 12%",
"glance .lowestQuota leads with lowest remaining quota")
check(
BarFormatting.statusTitle(rows: rows, analytics: allTimeBig, mode: .lowestQuota) == "CCS 2",
"glance .lowestQuota falls back to .auto with no ok-quota rows")
// .accountCount = non-paused count, never appends "!".
let pausedMix = [
BarSummaryRow(accountId: "a", provider: "ghcp", quotaStatus: "unsupported"),
BarSummaryRow(accountId: "b", provider: "kiro", paused: true, quotaStatus: "unsupported"),
]
check(
BarFormatting.statusTitle(rows: pausedMix, analytics: nil, mode: .accountCount) == "CCS 1",
"glance .accountCount = non-paused count")
let allPaused = [
BarSummaryRow(accountId: "a", provider: "ghcp", paused: true, quotaStatus: "unsupported"),
BarSummaryRow(accountId: "b", provider: "kiro", paused: true, quotaStatus: "unsupported"),
]
check(
BarFormatting.statusTitle(rows: allPaused, analytics: nil, mode: .accountCount) == "CCS 2",
"glance .accountCount falls back to total when all paused")
let reauthCount = [
BarSummaryRow(accountId: "a", provider: "ghcp", quotaStatus: "error", needsReauth: true)
]
check(
!BarFormatting.statusTitle(rows: reauthCount, analytics: nil, mode: .accountCount).contains("!"),
"glance .accountCount never appends attention '!'")
}
// MARK: BarAlertPrefsStore dictionary codec + level parsing
do {
// Empty dict -> all defaults.
let p = BarAlertPrefsStore.decode(from: [:])
check(p.quotaEnabled == true, "prefs default quotaEnabled true")
check(p.quotaLevels == [20, 10, 0], "prefs default quota levels 20,10,0")
check(p.dailyCapUSD == 500, "prefs default daily cap 500")
check(p.monthCapUSD == 10000, "prefs default month cap 10000")
check(p.glanceMode == .auto, "prefs default glance mode auto")
// Round-trip encode -> decode is identity.
let custom = BarAlertPrefs(
quotaEnabled: false, quotaLevels: [5, 25, 50], dailyCapUSD: 12.5, monthCapUSD: 2000,
glanceMode: .monthSpend)
let back = BarAlertPrefsStore.decode(from: BarAlertPrefsStore.encode(custom))
check(back.quotaEnabled == false, "prefs round-trip quotaEnabled")
check(back.quotaLevels == [50, 25, 5], "prefs round-trip levels sorted desc")
check(back.dailyCapUSD == 12.5, "prefs round-trip daily cap")
check(back.glanceMode == .monthSpend, "prefs round-trip glance mode")
// Level parsing: clamp, dedupe, sort desc; garbage tokens dropped.
check(
BarAlertPrefsStore.parseQuotaLevels("0, 10, 200, 10, -5, foo") == [100, 10, 0],
"parseQuotaLevels clamps/dedupes/sorts and drops garbage")
}
// MARK: BarAlertEngine — pure rule engine
let engineNow = Date(timeIntervalSince1970: 1_700_000_000) // fixed clock
let utc = { () -> Calendar in
var c = Calendar(identifier: .gregorian)
c.timeZone = TimeZone(identifier: "UTC")!
return c
}()
func mkSpendAnalytics(today: Double, month: Double) -> BarAnalytics {
BarAnalytics(
today: .init(cost: today, requests: 0),
last7d: .init(cost: 0, requests: 0),
last30d: .init(cost: 99999, requests: 0), // intentionally huge to prove it's unused
monthToDate: .init(cost: month, requests: 0),
allTime: .init(cost: 999999, requests: 0), // intentionally huge to prove it's unused
byDay: [], topModels: [], topModelsWindow: "30d",
generatedAt: "2026-06-09T00:00:00Z")
}
// (A) Threshold cross: quota dropping below levels fires exactly ONE notif at the
// most-severe crossed level (L10 for remaining 5 with levels [20,10,0]).
do {
let rows = [
BarSummaryRow(
accountId: "a", provider: "agy", quotaPercentage: 5, quotaStatus: "ok",
nextReset: "2026-07-01T00:00:00Z")
]
let prefs = BarAlertPrefs(quotaLevels: [20, 10, 0])
let ev = BarAlertEngine.evaluate(
rows: rows, analytics: nil, prefs: prefs, priorFiredKeys: [], now: engineNow, calendar: utc)
let quotaNotifs = ev.toDeliver.filter { $0.kind == .quotaRemainingBelow }
check(quotaNotifs.count == 1, "quota cross fires exactly ONE notif (most-severe level)")
check(quotaNotifs.first?.id.hasSuffix("|L10") == true, "quota fires at most-severe crossed L10")
check(quotaNotifs.first?.body.contains("5%") == true, "quota body reports remaining %")
}
// (B) dailySpendAbove strict > ; silent at ==cap ; driven by today (not last30d/allTime).
do {
let prefs = BarAlertPrefs(dailyCapUSD: 50)
let over = BarAlertEngine.evaluate(
rows: [], analytics: mkSpendAnalytics(today: 50.01, month: 0), prefs: prefs,
priorFiredKeys: [], now: engineNow, calendar: utc)
check(
over.toDeliver.contains { $0.kind == .dailySpendAbove }, "daily spend fires when today > cap")
let atCap = BarAlertEngine.evaluate(
rows: [], analytics: mkSpendAnalytics(today: 50, month: 0), prefs: prefs,
priorFiredKeys: [], now: engineNow, calendar: utc)
check(
!atCap.toDeliver.contains { $0.kind == .dailySpendAbove },
"daily spend silent at == cap (strict >)")
}
// (C) monthSpendAbove driven by monthToDate ONLY — huge last30d/allTime must not trigger.
do {
let prefs = BarAlertPrefs(monthCapUSD: 1000)
// monthToDate UNDER cap but last30d (99999) and allTime (999999) huge → must NOT fire.
let under = BarAlertEngine.evaluate(
rows: [], analytics: mkSpendAnalytics(today: 0, month: 500), prefs: prefs,
priorFiredKeys: [], now: engineNow, calendar: utc)
check(
!under.toDeliver.contains { $0.kind == .monthSpendAbove },
"month alert ignores last30d/allTime — silent when MTD under cap")
let over = BarAlertEngine.evaluate(
rows: [], analytics: mkSpendAnalytics(today: 0, month: 1500), prefs: prefs,
priorFiredKeys: [], now: engineNow, calendar: utc)
check(
over.toDeliver.contains { $0.kind == .monthSpendAbove }, "month alert fires on MTD > cap")
}
// (D) Dedupe: a second identical evaluate with same now + same firedKeys → empty toDeliver.
do {
let rows = [
BarSummaryRow(accountId: "a", provider: "agy", quotaPercentage: 5, quotaStatus: "ok",
nextReset: "2026-07-01T00:00:00Z")
]
let prefs = BarAlertPrefs(dailyCapUSD: 10)
let a1 = BarAlertEngine.evaluate(
rows: rows, analytics: mkSpendAnalytics(today: 100, month: 5000), prefs: prefs,
priorFiredKeys: [], now: engineNow, calendar: utc)
check(!a1.toDeliver.isEmpty, "first poll delivers notifs")
let a2 = BarAlertEngine.evaluate(
rows: rows, analytics: mkSpendAnalytics(today: 100, month: 5000), prefs: prefs,
priorFiredKeys: a1.firedKeys, now: engineNow, calendar: utc)
check(a2.toDeliver.isEmpty, "second identical poll delivers nothing (dedupe)")
check(a2.firedKeys == a1.firedKeys, "fired keys stable across identical polls")
}
// (E) Re-arm period: +1 day re-fires daily; +1 month re-fires month; new nextReset re-fires quota.
do {
let prefs = BarAlertPrefs(dailyCapUSD: 10, monthCapUSD: 100)
let day0 = mkSpendAnalytics(today: 100, month: 500)
let p0 = BarAlertEngine.evaluate(
rows: [], analytics: day0, prefs: prefs, priorFiredKeys: [], now: engineNow, calendar: utc)
let nextDay = engineNow.addingTimeInterval(24 * 3600)
let p1 = BarAlertEngine.evaluate(
rows: [], analytics: day0, prefs: prefs, priorFiredKeys: p0.firedKeys, now: nextDay,
calendar: utc)
check(p1.toDeliver.contains { $0.kind == .dailySpendAbove }, "daily re-fires next calendar day")
let nextMonth = engineNow.addingTimeInterval(40 * 24 * 3600)
let p2 = BarAlertEngine.evaluate(
rows: [], analytics: day0, prefs: prefs, priorFiredKeys: p0.firedKeys, now: nextMonth,
calendar: utc)
check(p2.toDeliver.contains { $0.kind == .monthSpendAbove }, "month re-fires next calendar month")
// New nextReset string => new quota bucket => quota re-fires.
let q0rows = [
BarSummaryRow(accountId: "a", provider: "agy", quotaPercentage: 5, quotaStatus: "ok",
nextReset: "2026-07-01T00:00:00Z")
]
let q0 = BarAlertEngine.evaluate(
rows: q0rows, analytics: nil, prefs: BarAlertPrefs(quotaLevels: [10]), priorFiredKeys: [],
now: engineNow, calendar: utc)
let q1rows = [
BarSummaryRow(accountId: "a", provider: "agy", quotaPercentage: 5, quotaStatus: "ok",
nextReset: "2026-08-01T00:00:00Z")
]
let q1 = BarAlertEngine.evaluate(
rows: q1rows, analytics: nil, prefs: BarAlertPrefs(quotaLevels: [10]),
priorFiredKeys: q0.firedKeys, now: engineNow, calendar: utc)
check(
q1.toDeliver.contains { $0.kind == .quotaRemainingBelow },
"quota re-fires on a new next_reset bucket")
}
// (F) Re-arm clears-then-recurs: reauth true->fires->key set; false->key dropped; true->re-fires.
do {
let onRows = [BarSummaryRow(accountId: "a", provider: "agy", needsReauth: true)]
let offRows = [BarSummaryRow(accountId: "a", provider: "agy", needsReauth: false)]
let s1 = BarAlertEngine.evaluate(
rows: onRows, analytics: nil, prefs: BarAlertPrefs(), priorFiredKeys: [], now: engineNow,
calendar: utc)
check(s1.toDeliver.contains { $0.kind == .reauthNeeded }, "reauth fires when needsReauth true")
check(
s1.firedKeys.contains { $0.hasPrefix("reauthNeeded|") }, "reauth key present after firing")
let s2 = BarAlertEngine.evaluate(
rows: offRows, analytics: nil, prefs: BarAlertPrefs(), priorFiredKeys: s1.firedKeys,
now: engineNow, calendar: utc)
check(
!s2.firedKeys.contains { $0.hasPrefix("reauthNeeded|") },
"reauth key dropped when condition clears")
let s3 = BarAlertEngine.evaluate(
rows: onRows, analytics: nil, prefs: BarAlertPrefs(), priorFiredKeys: s2.firedKeys,
now: engineNow, calendar: utc)
check(
s3.toDeliver.contains { $0.kind == .reauthNeeded }, "reauth re-fires after clear")
// Same clears-then-recurs behavior for accountCooldownOrPaused on `paused`.
let pOn = [BarSummaryRow(accountId: "a", provider: "agy", paused: true)]
let pOff = [BarSummaryRow(accountId: "a", provider: "agy", paused: false)]
let c1 = BarAlertEngine.evaluate(
rows: pOn, analytics: nil, prefs: BarAlertPrefs(), priorFiredKeys: [], now: engineNow,
calendar: utc)
check(
c1.toDeliver.contains { $0.kind == .accountCooldownOrPaused }, "cooldown/paused fires on paused")
let c2 = BarAlertEngine.evaluate(
rows: pOff, analytics: nil, prefs: BarAlertPrefs(), priorFiredKeys: c1.firedKeys, now: engineNow,
calendar: utc)
check(
!c2.firedKeys.contains { $0.hasPrefix("accountCooldownOrPaused|") },
"cooldown/paused key dropped when unpaused")
}
// (G) Prune / bounded: after day+month+reset rollover and account churn, fired set
// holds only current-bucket keys and no keys for absent accounts.
do {
let prefs = BarAlertPrefs(quotaLevels: [10], dailyCapUSD: 10, monthCapUSD: 100)
let rowsT0 = [
BarSummaryRow(accountId: "a", provider: "agy", quotaPercentage: 5, quotaStatus: "ok",
nextReset: "2026-07-01T00:00:00Z", needsReauth: true)
]
let t0 = BarAlertEngine.evaluate(
rows: rowsT0, analytics: mkSpendAnalytics(today: 100, month: 500), prefs: prefs,
priorFiredKeys: [], now: engineNow, calendar: utc)
check(t0.firedKeys.count >= 3, "t0 accumulates quota+daily+month+reauth keys")
// Roll a full month forward AND drop account "a", introduce account "b".
let later = engineNow.addingTimeInterval(40 * 24 * 3600)
let rowsT1 = [BarSummaryRow(accountId: "b", provider: "kiro", quotaStatus: "unsupported")]
let t1 = BarAlertEngine.evaluate(
rows: rowsT1, analytics: mkSpendAnalytics(today: 0, month: 0), prefs: prefs,
priorFiredKeys: t0.firedKeys, now: later, calendar: utc)
check(
!t1.firedKeys.contains { $0.contains("|agy:a|") || $0.hasSuffix("|agy:a|on") },
"prune drops keys for departed account a")
// Stale daily/month/quota buckets from t0 are gone (bucket rolled), so the set
// collapses to only what's currently true (account b has no alerts) → empty.
check(t1.firedKeys.isEmpty, "prune collapses stale-bucket + absent-account keys (bounded)")
}
// (H) Deterministic order: shuffled rows produce notifs in stable id order.
do {
let rows = [
BarSummaryRow(accountId: "z", provider: "agy", needsReauth: true),
BarSummaryRow(accountId: "a", provider: "agy", needsReauth: true),
]
let ev = BarAlertEngine.evaluate(
rows: rows, analytics: nil, prefs: BarAlertPrefs(), priorFiredKeys: [], now: engineNow,
calendar: utc)
let reauthIds = ev.toDeliver.filter { $0.kind == .reauthNeeded }.map { $0.id }
check(
reauthIds == reauthIds.sorted(), "engine emits notifs in stable account-id order")
}
// (I) Delivery wrapper (structural): RecordingNotifier (Core protocol) gets exactly
// the toDeliver ids; a repeat poll delivers nothing.
final class RecordingNotifier: NotificationDelivering, @unchecked Sendable {
var delivered: [String] = []
func deliver(_ n: BarNotification) { delivered.append(n.id) }
}
do {
let notifier = RecordingNotifier()
let rows = [BarSummaryRow(accountId: "a", provider: "agy", needsReauth: true)]
let ev = BarAlertEngine.evaluate(
rows: rows, analytics: nil, prefs: BarAlertPrefs(), priorFiredKeys: [], now: engineNow,
calendar: utc)
for n in ev.toDeliver { notifier.deliver(n) }
check(notifier.delivered == ev.toDeliver.map { $0.id }, "notifier delivered ids == toDeliver ids")
let ev2 = BarAlertEngine.evaluate(
rows: rows, analytics: nil, prefs: BarAlertPrefs(), priorFiredKeys: ev.firedKeys, now: engineNow,
calendar: utc)
let before = notifier.delivered.count
for n in ev2.toDeliver { notifier.deliver(n) }
check(notifier.delivered.count == before, "repeat poll delivers nothing through notifier")
}
// (J) Disabled rule: quota disabled => no quota notif even on a deep cross.
do {
let rows = [
BarSummaryRow(accountId: "a", provider: "agy", quotaPercentage: 1, quotaStatus: "ok",
nextReset: "2026-07-01T00:00:00Z")
]
let ev = BarAlertEngine.evaluate(
rows: rows, analytics: nil, prefs: BarAlertPrefs(quotaEnabled: false), priorFiredKeys: [],
now: engineNow, calendar: utc)
check(
!ev.toDeliver.contains { $0.kind == .quotaRemainingBelow },
"disabled quota rule never fires")
}
// MARK: Native subscription rows (Claude Code / Codex)
//
// The backend folds first-party subscription quota into /api/bar/summary as
// ordinary BarSummaryRow values (provider "claude-code" / "codex"). They must
// decode, drive the Tier-1 gauge + alert engine with ZERO new engine code, and
// group/label distinctly from CLIProxy pool accounts.
// (N1) Decode the exact native-row payload the collector emits.
let nativeJSON = """
[
{
"account_id": "claude-code", "provider": "claude-code", "displayName": "Claude Code",
"tier": "max", "paused": false, "quota_percentage": 15, "quotaStatus": "ok",
"next_reset": "2026-06-09T20:00:00.000Z", "is_default": false, "last_activity_at": null,
"today_cost": null, "health": "ok", "cached": true, "fetchedAt": "2026-06-09T13:00:00.000Z",
"needsReauth": false
},
{
"account_id": "codex", "provider": "codex", "displayName": "Codex",
"tier": "pro", "paused": false, "quota_percentage": 52, "quotaStatus": "ok",
"next_reset": "2026-06-09T19:00:00.000Z", "is_default": false, "last_activity_at": null,
"today_cost": null, "health": "ok", "cached": false, "fetchedAt": "2026-06-09T13:00:00.000Z",
"needsReauth": false
}
]
"""
do {
let rows = try JSONDecoder().decode([BarSummaryRow].self, from: Data(nativeJSON.utf8))
check(rows.count == 2, "native: decodes claude-code + codex rows")
check(rows[0].provider == "claude-code", "native: claude-code provider decodes")
check(rows[0].id == "claude-code:claude-code", "native: claude-code stable id")
check(rows[0].tier == "max", "native: claude-code tier decodes (max)")
check(rows[0].quotaStatus == "ok", "native: claude-code quotaStatus ok")
check(rows[0].todayCost == nil, "native: today_cost null decodes to nil (honest, not fake $0)")
check(rows[1].provider == "codex", "native: codex provider decodes")
check(rows[1].tier == "pro", "native: codex tier decodes (pro)")
}
// (N2) Native rows drive the Tier-1 gauge with no new code: an "ok" claude-code
// row at 15% lands in the orange band with a 0.15 fill.
do {
let claude = BarSummaryRow(
accountId: "claude-code", provider: "claude-code", displayName: "Claude Code",
tier: "max", quotaPercentage: 15, quotaStatus: "ok",
nextReset: "2026-06-09T20:00:00Z")
check(
BarQuotaGauge.band(percentage: claude.quotaPercentage, status: claude.quotaStatus) == .orange,
"native: claude-code 15% -> orange gauge band")
check(
BarQuotaGauge.fillFraction(percentage: claude.quotaPercentage, status: claude.quotaStatus)
== 0.15,
"native: claude-code 15% -> 0.15 gauge fill")
}
// (N3) Native rows feed the alert engine with no new code: a claude-code row at 8%
// with levels [20,10,0] fires quotaRemainingBelow scoped to its id.
do {
let rows = [
BarSummaryRow(
accountId: "claude-code", provider: "claude-code", displayName: "Claude Code",
tier: "max", quotaPercentage: 8, quotaStatus: "ok",
nextReset: "2026-06-09T20:00:00Z")
]
let ev = BarAlertEngine.evaluate(
rows: rows, analytics: nil, prefs: BarAlertPrefs(quotaLevels: [20, 10, 0]),
priorFiredKeys: [], now: engineNow, calendar: utc)
let quota = ev.toDeliver.filter { $0.kind == .quotaRemainingBelow }
check(quota.count == 1, "native: claude-code low quota fires exactly one alert")
check(
quota.first?.id.contains("|claude-code:claude-code|") == true,
"native: quota alert scoped to claude-code:claude-code")
check(quota.first?.body.contains("Claude Code") == true, "native: alert names 'Claude Code'")
}
// (N4) Native reauth: a claude-code row that needs reauth fires reauthNeeded
// (the 401-from-usage-endpoint path on the backend).
do {
let rows = [
BarSummaryRow(
accountId: "claude-code", provider: "claude-code", displayName: "Claude Code",
quotaStatus: "error", health: "error", needsReauth: true)
]
let ev = BarAlertEngine.evaluate(
rows: rows, analytics: nil, prefs: BarAlertPrefs(), priorFiredKeys: [], now: engineNow,
calendar: utc)
check(
ev.toDeliver.contains { $0.kind == .reauthNeeded },
"native: claude-code needsReauth fires reauthNeeded alert")
}
// (N5) Friendly provider labels + subscription classification.
check(
BarFormatting.providerLabel("claude-code") == "Claude Code",
"native: providerLabel maps claude-code -> 'Claude Code'")
check(
BarFormatting.providerLabel("codex") == "Codex", "native: providerLabel maps codex -> 'Codex'")
check(BarFormatting.providerLabel("agy") == "agy", "native: providerLabel passes CLIProxy keys through")
check(
BarFormatting.isNativeSubscription(provider: "claude-code"),
"native: claude-code is a native subscription")
check(
BarFormatting.isNativeSubscription(provider: "codex"), "native: codex is a native subscription")
check(
!BarFormatting.isNativeSubscription(provider: "agy"),
"native: agy (CLIProxy pool) is NOT a native subscription")
// (N6) Grouping: a mixed list splits into native subscriptions (top) and pool
// accounts, preserving backend order within each group.
do {
let mixed = [
BarSummaryRow(accountId: "pool-a", provider: "agy", quotaPercentage: 80, quotaStatus: "ok"),
BarSummaryRow(
accountId: "claude-code", provider: "claude-code", quotaPercentage: 40, quotaStatus: "ok"),
BarSummaryRow(accountId: "pool-b", provider: "ghcp", quotaStatus: "unsupported"),
BarSummaryRow(accountId: "codex", provider: "codex", quotaPercentage: 52, quotaStatus: "ok"),
]
let parts = BarFormatting.partitionSubscriptions(mixed)
check(parts.subscriptions.count == 2, "native: partition pulls 2 subscriptions")
check(parts.pool.count == 2, "native: partition leaves 2 pool accounts")
check(
parts.subscriptions.map { $0.provider } == ["claude-code", "codex"],
"native: subscriptions keep backend order (claude-code, codex)")
check(
parts.pool.map { $0.provider } == ["agy", "ghcp"],
"native: pool keeps backend order (agy, ghcp)")
}
// (N7) Pool-only list does NOT get split (single "Accounts" header path): both
// groups non-empty is the only trigger for the two-section render.
do {
let poolOnly = [
BarSummaryRow(accountId: "a", provider: "agy", quotaPercentage: 70, quotaStatus: "ok"),
BarSummaryRow(accountId: "b", provider: "ghcp", quotaStatus: "unsupported"),
]
let parts = BarFormatting.partitionSubscriptions(poolOnly)
check(parts.subscriptions.isEmpty, "native: pool-only list has no subscriptions to split out")
check(parts.pool.count == 2, "native: pool-only list keeps all rows in pool group")
}
// MARK: Per-window quota detail decode (resilient, backward compatible)
do {
let nativeJSON = """
[
{
"account_id": "claude-code",
"provider": "claude-code",
"displayName": "Claude Code",
"tier": "max",
"paused": false,
"quota_percentage": 44,
"quotaStatus": "ok",
"next_reset": "2026-06-09T20:00:00.000Z",
"is_default": false,
"last_activity_at": null,
"today_cost": null,
"health": "ok",
"cached": false,
"fetchedAt": "2026-06-09T14:00:00.000Z",
"needsReauth": false,
"quota_windows": [
{ "key": "five_hour", "label": "5h", "usedPercent": 56, "remainingPercent": 44, "resetAt": "2026-06-09T20:00:00.000Z", "windowMinutes": 300 },
{ "key": "seven_day", "label": "week", "usedPercent": 30, "remainingPercent": 70, "resetAt": "2026-06-15T00:00:00.000Z", "windowMinutes": 10080 },
{ "key": "seven_day_opus", "label": "Opus · week", "usedPercent": 25, "remainingPercent": 75, "resetAt": "2026-06-15T00:00:00.000Z", "windowMinutes": 10080 },
{ "key": "seven_day_sonnet", "label": "Sonnet · week", "usedPercent": 60, "remainingPercent": 40, "resetAt": "2026-06-15T00:00:00.000Z", "windowMinutes": 10080 }
]
},
{
"account_id": "codex",
"provider": "codex",
"displayName": "Codex",
"tier": "pro",
"paused": false,
"quota_percentage": 70,
"quotaStatus": "ok",
"next_reset": "2026-06-09T19:00:00.000Z",
"is_default": false,
"last_activity_at": null,
"today_cost": null,
"health": "warning",
"cached": false,
"fetchedAt": "2026-06-09T14:00:00.000Z",
"needsReauth": false,
"quota_windows": [
{ "key": "five_hour", "label": "5h", "usedPercent": 19, "remainingPercent": 81, "resetAt": "2026-06-09T19:00:00.000Z", "windowMinutes": 300 }
],
"stale_as_of": "2026-06-06T11:00:00.000Z"
}
]
"""
do {
let rows = try JSONDecoder().decode([BarSummaryRow].self, from: Data(nativeJSON.utf8))
check(rows.count == 2, "qw: decodes two native rows")
let claude = rows[0]
check(claude.quotaWindows?.count == 4, "qw: claude decodes 4 windows")
check(claude.quotaWindows?[0].key == "five_hour", "qw: first window key five_hour")
check(claude.quotaWindows?[0].label == "5h", "qw: first window label 5h")
check(claude.quotaWindows?[0].usedPercent == 56, "qw: usedPercent decodes")
check(claude.quotaWindows?[0].remainingPercent == 44, "qw: remainingPercent decodes")
check(claude.quotaWindows?[0].windowMinutes == 300, "qw: windowMinutes 300 decodes")
check(
claude.quotaWindows?[0].resetAt == "2026-06-09T20:00:00.000Z", "qw: resetAt decodes (ISO)")
check(
claude.quotaWindows?[2].key == "seven_day_opus", "qw: opus window present (Max)")
check(
claude.quotaWindows?[3].key == "seven_day_sonnet", "qw: sonnet window present (Max)")
check(claude.staleAsOf == nil, "qw: claude (fresh) has nil staleAsOf")
check(claude.quotaWindows?[0].id == "five_hour", "qw: window id is its key")
let codex = rows[1]
check(codex.quotaWindows?.count == 1, "qw: codex decodes 1 window")
check(codex.staleAsOf == "2026-06-06T11:00:00.000Z", "qw: codex staleAsOf decodes (stale)")
} catch {
check(false, "qw: native decode failed: \(error)")
}
}
// Legacy payload WITHOUT the two native-only keys still decodes (quotaWindows
// and staleAsOf -> nil). This is the backward-compatibility guarantee.
do {
let legacyJSON = """
[
{
"account_id": "alice@example.com",
"provider": "agy",
"displayName": "Alice",
"tier": "ultra",
"paused": false,
"quota_percentage": 80,
"quotaStatus": "ok",
"next_reset": null,
"is_default": true,
"last_activity_at": null,
"today_cost": null,
"health": "ok",
"cached": true,
"fetchedAt": "2026-06-07T19:00:00Z",
"needsReauth": false
}
]
"""
do {
let rows = try JSONDecoder().decode([BarSummaryRow].self, from: Data(legacyJSON.utf8))
check(rows.count == 1, "qw-legacy: legacy row decodes")
check(rows[0].quotaWindows == nil, "qw-legacy: missing quota_windows -> nil")
check(rows[0].staleAsOf == nil, "qw-legacy: missing stale_as_of -> nil")
} catch {
check(false, "qw-legacy: legacy decode failed: \(error)")
}
}
// MARK: burnMinutesRemaining (linear single-window projection)
do {
// Mid-window pace: a 300-min window resets in 150 min -> elapsed = 150 min.
// usedPercent = 50 -> rate = 50/150 = 1/3 %/min -> remaining 50% / (1/3) = 150.
let now = Date(timeIntervalSince1970: 1_700_000_000)
let resetIn150 = now.addingTimeInterval(150 * 60)
let t = BarQuotaGauge.burnMinutesRemaining(
usedPercent: 50, resetAt: resetIn150, windowMinutes: 300, now: now)
check(t == 150, "burn: mid-window 50% over 150min elapsed -> 150 min remaining")
// Near-zero usage -> nil ("plenty"); avoids an absurd projection.
let lowUse = BarQuotaGauge.burnMinutesRemaining(
usedPercent: 0.5, resetAt: resetIn150, windowMinutes: 300, now: now)
check(lowUse == nil, "burn: near-zero usage -> nil (plenty)")
// Exhausted -> 0 ("limit reached").
let exhausted = BarQuotaGauge.burnMinutesRemaining(
usedPercent: 100, resetAt: resetIn150, windowMinutes: 300, now: now)
check(exhausted == 0, "burn: usedPercent>=100 -> 0 (limit reached)")
// Unknown window length or reset -> nil (omit pace).
check(
BarQuotaGauge.burnMinutesRemaining(
usedPercent: 50, resetAt: resetIn150, windowMinutes: nil, now: now) == nil,
"burn: nil windowMinutes -> nil")
check(
BarQuotaGauge.burnMinutesRemaining(
usedPercent: 50, resetAt: nil, windowMinutes: 300, now: now) == nil,
"burn: nil resetAt -> nil")
// elapsed <= 0 (reset already further out than the full window) -> nil.
let resetTooFar = now.addingTimeInterval(400 * 60)
check(
BarQuotaGauge.burnMinutesRemaining(
usedPercent: 50, resetAt: resetTooFar, windowMinutes: 300, now: now) == nil,
"burn: elapsed<=0 -> nil")
}
// MARK: selectBindingWindow
do {
let now = Date(timeIntervalSince1970: 1_700_000_000)
_ = now
let fiveHour = QuotaWindowDetail(
key: "five_hour", label: "5h", usedPercent: 56, remainingPercent: 44, windowMinutes: 300)
let week = QuotaWindowDetail(
key: "seven_day", label: "week", usedPercent: 30, remainingPercent: 70, windowMinutes: 10080)
let opus = QuotaWindowDetail(
key: "seven_day_opus", label: "Opus · week", usedPercent: 75, remainingPercent: 25,
windowMinutes: 10080)
let binding = BarQuotaGauge.selectBindingWindow([fiveHour, week, opus])
check(binding?.key == "seven_day_opus", "binding: lowest remaining (opus 25%) wins")
// Tie on remaining% -> shorter window first.
let weekTie = QuotaWindowDetail(
key: "seven_day", label: "week", usedPercent: 60, remainingPercent: 40, windowMinutes: 10080)
let fiveTie = QuotaWindowDetail(
key: "five_hour", label: "5h", usedPercent: 60, remainingPercent: 40, windowMinutes: 300)
let tieBinding = BarQuotaGauge.selectBindingWindow([weekTie, fiveTie])
check(tieBinding?.key == "five_hour", "binding: remaining tie -> shorter window (5h) wins")
check(BarQuotaGauge.selectBindingWindow([]) == nil, "binding: empty input -> nil")
}
// MARK: paceClause phrasing
do {
let now = Date(timeIntervalSince1970: 1_700_000_000)
// At-risk scenario: window=300min, reset in 200min → elapsed=100min.
// usedPercent=75 → burn = (100-75)*100/75 ≈ 33 min. 33 < 200 → at-risk → shows pace.
let resetIn200 = ISO8601DateFormatter().string(from: now.addingTimeInterval(200 * 60))
let finite = BarQuotaGauge.paceClause(
usedPercent: 75, remainingPercent: 25, resetAt: resetIn200, windowMinutes: 300, now: now)
check(finite?.hasPrefix("~") == true && finite?.hasSuffix("left at this pace") == true,
"pace: at-risk (burn < reset) -> shows pace clause")
// NOT at-risk: burn > minutesToReset — pace must be nil (the core fix).
// window=300min, reset in 50min → elapsed=250min. usedPercent=50 →
// burn = (100-50)*250/50 = 250 min. 250 > 50 → NOT at risk → nil.
let resetIn50 = ISO8601DateFormatter().string(from: now.addingTimeInterval(50 * 60))
let notAtRisk = BarQuotaGauge.paceClause(
usedPercent: 50, remainingPercent: 50, resetAt: resetIn50, windowMinutes: 300, now: now)
check(notAtRisk == nil, "pace: burn > reset (not at risk) -> nil (omit — core bug fix)")
// Lots of headroom -> "plenty at this pace".
let resetIn150 = ISO8601DateFormatter().string(from: now.addingTimeInterval(150 * 60))
let plenty = BarQuotaGauge.paceClause(
usedPercent: 10, remainingPercent: 90, resetAt: resetIn150, windowMinutes: 300, now: now)
check(plenty == "plenty at this pace", "pace: >=85% remaining -> 'plenty at this pace'")
// Near-zero usage -> "plenty at this pace" (burn nil but window known).
let plentyLow = BarQuotaGauge.paceClause(
usedPercent: 0.5, remainingPercent: 99.5, resetAt: resetIn150, windowMinutes: 300, now: now)
check(plentyLow == "plenty at this pace", "pace: near-zero usage -> 'plenty at this pace'")
// Exhausted -> "limit reached, resets in ...".
let reached = BarQuotaGauge.paceClause(
usedPercent: 100, remainingPercent: 0, resetAt: resetIn150, windowMinutes: 300, now: now)
check(
reached?.hasPrefix("limit reached, resets in") == true,
"pace: 0% remaining -> 'limit reached, resets in ...'")
// < 5 min floor -> limit-reached path, never a scary "~0m".
// window=300min, reset in 150min → elapsed=150min. usedPercent=99 →
// burn = (100-99)*150/99 ≈ 1.5 min → floor hit → "limit reached".
let nearFloor = BarQuotaGauge.paceClause(
usedPercent: 99, remainingPercent: 1, resetAt: resetIn150, windowMinutes: 300, now: now)
check(
nearFloor?.hasPrefix("limit reached") == true && !(nearFloor?.contains("~0m") ?? true),
"pace: <5m floor -> limit-reached, never '~0m'")
// Unknown window -> nil (omit).
let unknown = BarQuotaGauge.paceClause(
usedPercent: 50, remainingPercent: 50, resetAt: nil, windowMinutes: nil, now: now)
check(unknown == nil, "pace: unknown window -> nil (omit)")
// resetAt in the past -> nil pace.
let pastReset = ISO8601DateFormatter().string(from: now.addingTimeInterval(-60))
let past = BarQuotaGauge.paceClause(
usedPercent: 50, remainingPercent: 50, resetAt: pastReset, windowMinutes: 300, now: now)
check(past == nil, "pace: past resetAt -> nil pace")
}
// MARK: atRisk boolean
do {
let now = Date(timeIntervalSince1970: 1_700_000_000)
// At-risk: burn(33m) < minutesToReset(200m) → true.
let resetIn200 = ISO8601DateFormatter().string(from: now.addingTimeInterval(200 * 60))
let isAtRisk = BarQuotaGauge.atRisk(
usedPercent: 75, remainingPercent: 25, resetAt: resetIn200, windowMinutes: 300, now: now)
check(isAtRisk == true, "atRisk: burn < reset -> true")
// NOT at-risk: burn(250m) > minutesToReset(50m) → false.
let resetIn50 = ISO8601DateFormatter().string(from: now.addingTimeInterval(50 * 60))
let notAtRisk = BarQuotaGauge.atRisk(
usedPercent: 50, remainingPercent: 50, resetAt: resetIn50, windowMinutes: 300, now: now)
check(notAtRisk == false, "atRisk: burn > reset -> false")
// Exhausted (remaining=0) → false (handled by limit-reached path, not atRisk).
let resetIn150 = ISO8601DateFormatter().string(from: now.addingTimeInterval(150 * 60))
check(
BarQuotaGauge.atRisk(
usedPercent: 100, remainingPercent: 0, resetAt: resetIn150, windowMinutes: 300, now: now)
== false,
"atRisk: exhausted -> false")
// Unknown window → false.
check(
BarQuotaGauge.atRisk(
usedPercent: 75, remainingPercent: 25, resetAt: nil, windowMinutes: nil, now: now)
== false,
"atRisk: unknown window -> false")
// Large weekly burn in screenshot scenario: weekly window resets in ~9h 2m (542 min),
// usedPercent=59 over elapsed=(10080-542)=9538 min → burn ≈ (41*9538)/59 ≈ 6626 min.
// 6626 > 542 → NOT at risk (this was the nonsensical "~110h left" shown to user).
let resetIn542m = ISO8601DateFormatter().string(from: now.addingTimeInterval(542 * 60))
let weeklyNotAtRisk = BarQuotaGauge.atRisk(
usedPercent: 59, remainingPercent: 41, resetAt: resetIn542m, windowMinutes: 10080, now: now)
check(weeklyNotAtRisk == false,
"atRisk: weekly window resetting in 9h, burn projecting 100+h -> false (screenshot fix)")
}
// MARK: headroomLeader
do {
func subRow(_ provider: String, _ name: String, remaining: Double) -> BarSummaryRow {
BarSummaryRow(
accountId: provider, provider: provider, displayName: name, quotaStatus: "ok",
quotaWindows: [
QuotaWindowDetail(
key: "five_hour", label: "5h", usedPercent: 100 - remaining,
remainingPercent: remaining, windowMinutes: 300)
])
}
let claude = subRow("claude-code", "Claude Code", remaining: 44)
let codex = subRow("codex", "Codex", remaining: 81)
let errorRow = BarSummaryRow(
accountId: "broken", provider: "claude-code", displayName: "Broken", quotaStatus: "error")
let leader = BarQuotaGauge.headroomLeader([claude, codex, errorRow])
check(leader?.label == "Codex", "headroom: highest-binding-remaining (Codex 81%) leads")
check(leader?.remainingPercent == 81, "headroom: leader carries its remaining%")
// Error/reauth rows (no binding window) are excluded from the count.
let single = BarQuotaGauge.headroomLeader([claude, errorRow])
check(single == nil, "headroom: <2 eligible subscriptions -> nil")
check(BarQuotaGauge.headroomLeader([]) == nil, "headroom: empty -> nil")
}
// cleanup
try? FileManager.default.removeItem(atPath: tmp)
if failures > 0 {
print("\nFAILED: \(failures) check(s)")
exit(1)
} else {
print("\nALL CHECKS PASSED")
exit(0)
}