mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-12 18:14:36 +00:00
The Codex plan tab reused the shared connect, no-credentials and reconnect views whose copy was hardcoded for Claude, so the Codex tab showed Connect Claude subscription. Worse, the no-credentials Try Again button always re-ran the Claude bootstrap. The three views now take title, message and action from the call site. The oauth usage endpoint gained a limits array carrying model-scoped weekly buckets (currently Fable). Parse weekly_scoped entries generically by their display name and show them in the Claude hover popover and the plan tab, with the usual pace projection. Parser now has test coverage using the captured live response shape.
77 lines
3.6 KiB
Swift
77 lines
3.6 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import CodeBurnMenubar
|
|
|
|
@Suite("Claude usage response parsing")
|
|
struct ClaudeSubscriptionParsingTests {
|
|
// Shape captured from the live oauth/usage endpoint (2026-07): named
|
|
// windows plus a `limits` array carrying model-scoped weekly buckets.
|
|
private let liveShape = """
|
|
{
|
|
"five_hour": { "utilization": 13.0, "resets_at": "2026-07-02T22:09:59.599633+00:00", "limit_dollars": null, "used_dollars": null, "remaining_dollars": null },
|
|
"seven_day": { "utilization": 63.0, "resets_at": "2026-07-03T06:59:59.599658+00:00", "limit_dollars": null, "used_dollars": null, "remaining_dollars": null },
|
|
"seven_day_oauth_apps": null,
|
|
"seven_day_opus": null,
|
|
"seven_day_sonnet": null,
|
|
"extra_usage": { "is_enabled": false },
|
|
"limits": [
|
|
{ "kind": "session", "group": "session", "percent": 13, "severity": "normal", "resets_at": "2026-07-02T22:09:59.456907+00:00", "scope": null, "is_active": false },
|
|
{ "kind": "weekly_all", "group": "weekly", "percent": 63, "severity": "normal", "resets_at": "2026-07-03T06:59:59.456926+00:00", "scope": null, "is_active": false },
|
|
{ "kind": "weekly_scoped", "group": "weekly", "percent": 94, "severity": "critical", "resets_at": "2026-07-03T06:59:59.457220+00:00", "scope": { "model": { "id": null, "display_name": "Fable" }, "surface": null }, "is_active": true }
|
|
]
|
|
}
|
|
"""
|
|
|
|
@Test("model-scoped weekly bucket surfaces with its display name")
|
|
func scopedWeeklyParsed() throws {
|
|
let usage = try ClaudeSubscriptionService.parseUsage(Data(liveShape.utf8), rawTier: "max_20x")
|
|
#expect(usage.scopedWeekly.count == 1)
|
|
let fable = try #require(usage.scopedWeekly.first)
|
|
#expect(fable.label == "Fable")
|
|
#expect(fable.percent == 94)
|
|
#expect(fable.resetsAt != nil)
|
|
}
|
|
|
|
@Test("named windows still map alongside limits")
|
|
func namedWindowsUnaffected() throws {
|
|
let usage = try ClaudeSubscriptionService.parseUsage(Data(liveShape.utf8), rawTier: "max_20x")
|
|
#expect(usage.fiveHourPercent == 13.0)
|
|
#expect(usage.sevenDayPercent == 63.0)
|
|
#expect(usage.sevenDayOpusPercent == nil)
|
|
#expect(usage.sevenDaySonnetPercent == nil)
|
|
#expect(usage.tier == .max20x)
|
|
}
|
|
|
|
@Test("session and weekly_all limits are not duplicated as scoped rows")
|
|
func unscopedKindsSkipped() throws {
|
|
let usage = try ClaudeSubscriptionService.parseUsage(Data(liveShape.utf8), rawTier: nil)
|
|
#expect(!usage.scopedWeekly.contains { $0.percent == 13 || $0.percent == 63 })
|
|
}
|
|
|
|
@Test("response without a limits array parses with no scoped windows")
|
|
func missingLimitsIsBackCompat() throws {
|
|
let old = """
|
|
{
|
|
"five_hour": { "utilization": 40.0, "resets_at": "2026-07-02T22:00:00+00:00" },
|
|
"seven_day": { "utilization": 12.5, "resets_at": "2026-07-03T07:00:00+00:00" }
|
|
}
|
|
"""
|
|
let usage = try ClaudeSubscriptionService.parseUsage(Data(old.utf8), rawTier: "pro")
|
|
#expect(usage.scopedWeekly.isEmpty)
|
|
#expect(usage.fiveHourPercent == 40.0)
|
|
#expect(usage.tier == .pro)
|
|
}
|
|
|
|
@Test("weekly_scoped without a model display name is skipped")
|
|
func scopedWithoutNameSkipped() throws {
|
|
let body = """
|
|
{
|
|
"limits": [
|
|
{ "kind": "weekly_scoped", "percent": 50, "resets_at": "2026-07-03T07:00:00+00:00", "scope": { "model": null } }
|
|
]
|
|
}
|
|
"""
|
|
let usage = try ClaudeSubscriptionService.parseUsage(Data(body.utf8), rawTier: nil)
|
|
#expect(usage.scopedWeekly.isEmpty)
|
|
}
|
|
}
|