Files
2026-07-13 12:22:33 +08:00

843 lines
33 KiB
Swift

import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
struct StatusProbeTests {
@Test
func `parse codex status`() throws {
let sample = """
Model: gpt
Credits: 980 credits
5h limit: [#####] 75% left
Weekly limit: [##] 25% left
"""
let snap = try CodexStatusProbe.parse(text: sample)
#expect(snap.credits == 980)
#expect(snap.fiveHourPercentLeft == 75)
#expect(snap.weeklyPercentLeft == 25)
}
@Test
func `parse codex status with ansi and resets`() throws {
let now = try #require(
Calendar(identifier: .gregorian).date(from: DateComponents(
timeZone: TimeZone.current,
year: 2026,
month: 11,
day: 26,
hour: 8,
minute: 0)))
let sample = """
\u{001B}[38;5;245mCredits:\u{001B}[0m 557 credits
5h limit: [█████ ] 50% left (resets 09:01)
Weekly limit: [███████ ] 85% left (resets 04:01 on 27 Nov)
"""
let snap = try CodexStatusProbe.parse(text: sample, now: now)
#expect(snap.credits == 557)
#expect(snap.fiveHourPercentLeft == 50)
#expect(snap.weeklyPercentLeft == 85)
#expect(snap.fiveHourResetsAt == Calendar(identifier: .gregorian).date(from: DateComponents(
timeZone: TimeZone.current,
year: 2026,
month: 11,
day: 26,
hour: 9,
minute: 1)))
#expect(snap.weeklyResetsAt == Calendar(identifier: .gregorian).date(from: DateComponents(
timeZone: TimeZone.current,
year: 2026,
month: 11,
day: 27,
hour: 4,
minute: 1)))
}
@Test
func `parse codex status with weekly only line`() throws {
let sample = """
Model: gpt
Credits: 980 credits
Weekly limit: [##] 25% left
"""
let snap = try CodexStatusProbe.parse(text: sample)
#expect(snap.credits == 980)
#expect(snap.fiveHourPercentLeft == nil)
#expect(snap.weeklyPercentLeft == 25)
}
@Test
func `parse codex monthly credit limit`() throws {
let now = try #require(
Calendar(identifier: .gregorian).date(from: DateComponents(
timeZone: TimeZone.current,
year: 2026,
month: 6,
day: 23,
hour: 12,
minute: 0)))
let sample = """
Model: codex-status-fixture
Monthly credit limit: [██████████████████░░] 92% left (resets 08:00 on 1 Jul)
7,761 of 100,000 credits used
"""
let snap = try CodexStatusProbe.parse(text: sample, now: now)
#expect(snap.codexCreditLimit?.limit == 100_000)
#expect(snap.codexCreditLimit?.used == 7761)
#expect(snap.codexCreditLimit?.remaining == 92239)
#expect(snap.codexCreditLimit?.remainingPercent == 92)
#expect(snap.codexCreditLimit?.resetsAt == Calendar(identifier: .gregorian).date(from: DateComponents(
timeZone: TimeZone.current,
year: 2026,
month: 7,
day: 1,
hour: 8,
minute: 0)))
}
@Test
func `parse claude status`() throws {
let sample = """
Settings: Status Config Usage (tab to cycle)
Current session
1% used (Resets 5am (Europe/Vienna))
Current week (all models)
1% used (Resets Dec 2 at 12am (Europe/Vienna))
Current week (Sonnet only)
1% used (Resets Dec 2 at 12am (Europe/Vienna))
Nov 24, 2025 update:
We've increased your limits and removed the Opus cap,
so you can use Opus 4.5 up to your overall limit.
Sonnet now has its own limit—it's set to match your previous overall limit,
so you can use just as much as before.
Account: user@example.com
Org: Example Org
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 99)
#expect(snap.weeklyPercentLeft == 99)
#expect(snap.opusPercentLeft == 99)
#expect(snap.accountEmail == "user@example.com")
#expect(snap.accountOrganization == "Example Org")
#expect(snap.primaryResetDescription == "Resets 5am (Europe/Vienna)")
#expect(snap.secondaryResetDescription == "Resets Dec 2 at 12am (Europe/Vienna)")
#expect(snap.opusResetDescription == "Resets Dec 2 at 12am (Europe/Vienna)")
}
@Test
func `parse claude status with ANSI`() throws {
let sample = """
\u{001B}[35mCurrent session\u{001B}[0m
40% used (Resets 11am)
Current week (all models)
10% used (Resets Nov 27)
Current week (Sonnet only)
0% used (Resets Nov 27)
Account: user@example.com
Org: ACME
\u{001B}[0m
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 60)
#expect(snap.weeklyPercentLeft == 90)
#expect(snap.opusPercentLeft == 100)
#expect(snap.primaryResetDescription == "Resets 11am")
#expect(snap.secondaryResetDescription == "Resets Nov 27")
#expect(snap.opusResetDescription == "Resets Nov 27")
}
@Test
func `parse claude status legacy opus label`() throws {
let sample = """
Current session
12% used (Resets 11am)
Current week (all models)
55% used (Resets Nov 21)
Current week (Opus)
5% used (Resets Nov 21)
Account: user@example.com
Org: Example Org
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 88)
#expect(snap.weeklyPercentLeft == 45)
#expect(snap.opusPercentLeft == 95)
#expect(snap.primaryResetDescription == "Resets 11am")
#expect(snap.secondaryResetDescription == "Resets Nov 21")
#expect(snap.opusResetDescription == "Resets Nov 21")
}
@Test
func `parse claude status remaining keyword`() throws {
let sample = """
Current session
12% remaining (Resets 11am)
Current week (all models)
40% remaining (Resets Nov 21)
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 12)
#expect(snap.weeklyPercentLeft == 40)
}
@Test
func `parse claude status enterprise session only`() throws {
let sample = """
Current session
█ 2% used
Resets 3pm (Europe/Vienna)
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 98)
#expect(snap.weeklyPercentLeft == nil)
#expect(snap.primaryResetDescription == "Resets 3pm (Europe/Vienna)")
#expect(snap.secondaryResetDescription == nil)
}
@Test
func `parse claude status reset mappings with CR line endings`() throws {
let sample =
"Current session\r" +
"██████████████████████████████████████████████████ 17% used\r" +
"Resets 12:59pm (Europe/Paris)\r" +
"Current week (all models)\r" +
"██████████████████████████████████████████████████ 4% used\r" +
"Resets Dec 24 at 3:59pm (Europe/Paris)\r" +
"Current week (Sonnet only)\r" +
"██████████████████████████████████████████████████ 3% used\r" +
"Resets Dec 23 at 3:59am (Europe/Paris)\r"
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 83)
#expect(snap.weeklyPercentLeft == 96)
#expect(snap.opusPercentLeft == 97)
#expect(snap.primaryResetDescription == "Resets 12:59pm (Europe/Paris)")
#expect(snap.secondaryResetDescription == "Resets Dec 24 at 3:59pm (Europe/Paris)")
#expect(snap.opusResetDescription == "Resets Dec 23 at 3:59am (Europe/Paris)")
}
@Test
func `parse claude status reset mappings does not promote weekly reset to session`() throws {
let sample = """
Current session
██████████████████████████████████████████████████ 17% used
Current week (all models)
██████████████████████████████████████████████████ 4% used
Resets Dec 24 at 3:59pm (Europe/Paris)
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 83)
#expect(snap.weeklyPercentLeft == 96)
#expect(snap.primaryResetDescription == nil)
#expect(snap.secondaryResetDescription == "Resets Dec 24 at 3:59pm (Europe/Paris)")
}
@Test
func `parse claude status with plan and ansi noise`() throws {
let sample = """
Settings: Status Config Usage
Login method: \u{001B}[22mClaude Max Account\u{001B}[0m
Account: user@example.com
Org: ACME
"""
// Only care about login/identity; include minimal usage lines to satisfy parser.
let text = """
Current session
10% used
Current week (all models)
20% used
Current week (Opus)
30% used
\(sample)
"""
let snap = try ClaudeStatusProbe.parse(text: text)
#expect(snap.loginMethod == "Max")
#expect(snap.accountEmail == "user@example.com")
#expect(snap.accountOrganization == "ACME")
}
@Test
func `parse claude status with extra usage section`() throws {
let sample = """
Settings: Status Config Usage (tab to cycle)
Current session
▌ 1% used
Resets 3:59pm (Europe/Helsinki)
Current week (all models)
▌ 1% used
Resets Jan 2, 2026, 10:59pm (Europe/Helsinki)
Current week (Sonnet only)
0% used
Extra usage
Extra usage not enabled • /extra-usage to enable
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 99)
#expect(snap.weeklyPercentLeft == 99)
#expect(snap.opusPercentLeft == 100)
#expect(snap.primaryResetDescription == "Resets 3:59pm (Europe/Helsinki)")
#expect(snap.secondaryResetDescription == "Resets Jan 2, 2026, 10:59pm (Europe/Helsinki)")
}
@Test
func `parse claude status ignores status bar context percent`() throws {
let sample = """
Claude Code v2.1.29
22:47 | | Opus 4.5 | default | ░░░░░░░░░░ 0% ◯ /ide for Visual Studio Code
Settings: Status Config Usage (tab to cycle)
Loading usage data…
Esc to cancel
Curretsession
███████▌15%used
Resets 11:30pm (Asia/Calcutta)
Current week (all models)
█▌ 3% used
Resets Feb 12 at 1:30pm (Asia/Calcutta)
Current week (Sonnet only)
▌ 1% used
Resets Feb 12 at 1:30pm (Asia/Calcutta)
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 85)
#expect(snap.weeklyPercentLeft == 97)
#expect(snap.opusPercentLeft == 99)
}
@Test
func `parse claude status loading panel surfaces loading stall`() {
let sample = """
Claude Code v2.1.29
22:47 | | Opus 4.5 | default | ░░░░░░░░░░ 0% ◯ /ide for Visual Studio Code
Settings: Status Config Usage (tab to cycle)
Loading usage data…
Esc to cancel
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail while /usage is still loading")
} catch let ClaudeStatusProbeError.parseFailed(message) {
#expect(message.lowercased().contains("loading"))
return
} catch ClaudeStatusProbeError.timedOut {
return
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func `parse claude retained usage panel classifies latest loading panel`() {
let sample = """
Settings: Status Config Usage (tab to cycle)
Current session
███████▌15%used
Resets 11:30pm (Asia/Calcutta)
Current week (all models)
█▌ 3% used
Resets Feb 12 at 1:30pm (Asia/Calcutta)
Settings: Status Config Usage (tab to cycle)
Loading usage data…
Esc to cancel
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail while the latest /usage panel is still loading")
} catch let ClaudeStatusProbeError.parseFailed(message) {
#expect(message.lowercased().contains("loading"))
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func `parse claude status status only output does not fallback to zero`() {
let sample = """
Claude Code v2.1.32
01:07 | | Opus 4.6 | default | ░░░░░░░░░░ 0% left
Status: Partially Degraded Service
/status
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail when /usage windows are missing")
} catch ClaudeStatusProbeError.parseFailed {
return
} catch ClaudeStatusProbeError.timedOut {
return
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func `parse claude status placeholder usage window does not use status bar percent`() {
let sample = """
Claude Code v2.1.32
01:07 | | Opus 4.6 | default | ░░░░░░░░░░ 0% left
Settings: Status Config Usage
Current session
Current week (all models)
Current week (Sonnet only)
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail when only status-bar percentages are present")
} catch ClaudeStatusProbeError.parseFailed {
return
} catch ClaudeStatusProbeError.timedOut {
return
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func `parse claude status compact markers still parse`() throws {
let sample = """
Settings:StatusConfigUsage(←/→ortabtocycle)
Loadingusagedata…
Curretsession
███6%used
Resets4:29am(Asia/Calcutta)
Currentweek(allmodels)
██4%used
ResetsFeb12at1:29pm(Asia/Calcutta)
Currentweek(Sonnetonly)
▌1%used
ResetsFeb12at1:29pm(Asia/Calcutta)
"""
let snap = try ClaudeStatusProbe.parse(text: sample)
#expect(snap.sessionPercentLeft == 94)
#expect(snap.weeklyPercentLeft == 96)
#expect(snap.opusPercentLeft == 99)
#expect(snap.secondaryResetDescription == "ResetsFeb12at1:29pm(Asia/Calcutta)")
#expect(snap.opusResetDescription == "ResetsFeb12at1:29pm(Asia/Calcutta)")
}
@Test
func `parse claude status with bracket plan noise no esc`() throws {
let sample = """
Login method: [22m Claude Max Account
Account: user@example.com
"""
let text = """
Current session
10% used
Current week (all models)
20% used
Current week (Opus)
30% used
\(sample)
"""
let snap = try ClaudeStatusProbe.parse(text: text)
#expect(snap.loginMethod == "Max")
}
@Test
func `surfaces claude token expired`() {
let sample = """
Settings: Status Config Usage
Error: Failed to load usage data: {"type":"error","error":{"type":"authentication_error",
"message":"OAuth token has expired. Please obtain a new token or refresh your existing token.",
"details":{"error_visibility":"user_facing","error_code":"token_expired"}},\
"request_id":"req_123"}
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail for auth error")
} catch let ClaudeStatusProbeError.parseFailed(message) {
let lower = message.lowercased()
#expect(lower.contains("token"))
#expect(lower.contains("login"))
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func `surfaces claude rate limited compact usage error`() {
let sample = """
Settings:StatusConfigUsage(←/→ortabtocycle)
Error:Failedtoloadusagedata:{"error":{"message":"Ratelimited.Pleasetryagainlater.","type":"rate_limit_error"}}
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail for rate limiting")
} catch let ClaudeStatusProbeError.parseFailed(message) {
let lower = message.lowercased()
#expect(lower.contains("rate"))
#expect(lower.contains("limit"))
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func `surfaces claude folder trust prompt`() {
let sample = """
Do you trust the files in this folder?
/Users/example/project
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail for folder trust prompt")
} catch let ClaudeStatusProbeError.parseFailed(message) {
#expect(message.lowercased().contains("trust"))
#expect(message.contains("/Users/example/project"))
#expect(message.contains("cd \"/Users/example/project\" && claude"))
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func `surfaces claude folder trust prompt with CRLF and spaces`() {
let sample = "Do you trust the files in this folder?\r\n\r\n/Users/example/My Project\r\n"
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail for folder trust prompt")
} catch let ClaudeStatusProbeError.parseFailed(message) {
#expect(message.contains("/Users/example/My Project"))
#expect(message.contains("cd \"/Users/example/My Project\" && claude"))
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func `surfaces claude folder trust prompt without folder path`() {
let sample = """
Do you trust the files in this folder?
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail for folder trust prompt")
} catch let ClaudeStatusProbeError.parseFailed(message) {
let lower = message.lowercased()
#expect(lower.contains("trust"))
#expect(lower.contains("auto-accept"))
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func `surfaces claude subscription notice without quota data`() {
let sample = """
You are currently using your subscription to power your Claude Code usage
"""
do {
_ = try ClaudeStatusProbe.parse(text: sample)
#expect(Bool(false), "Parsing should fail for subscription notice without quota data")
} catch let ClaudeStatusProbeError.parseFailed(message) {
let lower = message.lowercased()
#expect(lower.contains("subscription"))
#expect(!lower.contains("still loading"))
#expect(ClaudeStatusProbe.isSubscriptionQuotaUnavailableDescription(message))
let errorDescription = ClaudeStatusProbeError.parseFailed(message).localizedDescription
#expect(UsageLimitsAvailability.resolve(
provider: .claude,
snapshot: nil,
lastErrorDescription: errorDescription) == .unavailable)
} catch {
#expect(Bool(false), "Unexpected error: \(error)")
}
}
@Test
func `parse claude status subscription notice is distinct from loading stall`() {
let subscriptionOnly = "You are currently using your subscription to power your Claude Code usage"
let loadingOnly = """
Settings: Status Config Usage (tab to cycle)
Loading usage data…
Esc to cancel
"""
do {
_ = try ClaudeStatusProbe.parse(text: subscriptionOnly)
#expect(Bool(false), "Subscription notice should fail parsing")
} catch let ClaudeStatusProbeError.parseFailed(subMessage) {
#expect(!subMessage.lowercased().contains("still loading"))
} catch {
#expect(Bool(false), "Unexpected error for subscription: \(error)")
}
do {
_ = try ClaudeStatusProbe.parse(text: loadingOnly)
#expect(Bool(false), "Loading panel should fail parsing")
} catch let ClaudeStatusProbeError.parseFailed(loadMessage) {
#expect(loadMessage.lowercased().contains("loading"))
} catch {
#expect(Bool(false), "Unexpected error for loading: \(error)")
}
}
@Test
func `parse claude status mixed loading and subscription notice surfaces subscription error`() {
// PTY capture containing both an intermediate "Loading usage data…" panel and the final
// Claude CLI 2.1.148 subscription notice. The subscription error must be surfaced, not
// the still-loading stall, so the UI shows the precise subscription message.
let mixedCapture = """
Settings: Status Config Usage (tab to cycle)
Loading usage data…
Esc to cancel
You are currently using your subscription to power your Claude Code usage
"""
do {
_ = try ClaudeStatusProbe.parse(text: mixedCapture)
#expect(Bool(false), "Parsing should fail for mixed loading+subscription capture")
} catch let ClaudeStatusProbeError.parseFailed(message) {
let lower = message.lowercased()
#expect(lower.contains("subscription"))
#expect(!lower.contains("still loading"))
} catch {
#expect(Bool(false), "Unexpected error for mixed capture: \(error)")
}
}
@Test
func `uses the five hour window to resolve stale claude reset times`() throws {
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = try #require(TimeZone(identifier: "UTC"))
let cases: [(now: DateComponents, text: String, expected: DateComponents)] = [
(
DateComponents(year: 2026, month: 7, day: 9, hour: 15),
"Resets 3pm (UTC)",
DateComponents(year: 2026, month: 7, day: 9, hour: 15)),
(
DateComponents(year: 2026, month: 7, day: 9, hour: 15, minute: 5),
"Resets 3pm (UTC)",
DateComponents(year: 2026, month: 7, day: 9, hour: 15, minute: 0)),
(
DateComponents(year: 2026, month: 7, day: 9, hour: 20),
"Resets 3pm (UTC)",
DateComponents(year: 2026, month: 7, day: 9, hour: 15)),
(
DateComponents(year: 2026, month: 7, day: 9, hour: 23, minute: 59),
"Resets 12:01am (UTC)",
DateComponents(year: 2026, month: 7, day: 10, hour: 0, minute: 1)),
(
DateComponents(year: 2026, month: 7, day: 10, hour: 0, minute: 1),
"Resets 11:59pm (UTC)",
DateComponents(year: 2026, month: 7, day: 9, hour: 23, minute: 59)),
]
for item in cases {
let now = try #require(calendar.date(from: item.now))
let parsed = ClaudeStatusProbe.parseResetDate(
from: item.text,
now: now,
expectedWindow: 5 * 60 * 60)
#expect(parsed == calendar.date(from: item.expected), "Failed session-window resolution: \(item.text)")
}
}
@Test
func `parses claude reset date and time`() throws {
let now = Date(timeIntervalSince1970: 1_733_690_000)
let parsed = ClaudeStatusProbe.parseResetDate(from: "Resets Dec 9, 8:59am (Europe/Helsinki)", now: now)
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = try #require(TimeZone(identifier: "Europe/Helsinki"))
let expected = calendar.date(from: DateComponents(
year: calendar.component(.year, from: now),
month: 12,
day: 9,
hour: 8,
minute: 59,
second: 0))
#expect(parsed == expected)
}
@Test
func `uses the weekly window to resolve stale claude reset dates`() throws {
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = try #require(TimeZone(identifier: "UTC"))
let cases: [(now: DateComponents, text: String, expected: DateComponents)] = [
(
DateComponents(year: 2026, month: 12, day: 31, hour: 23),
"Resets Jan 2, 3:15am (UTC)",
DateComponents(year: 2027, month: 1, day: 2, hour: 3, minute: 15)),
(
DateComponents(year: 2026, month: 12, day: 31, hour: 23),
"Resets Jan 2, 3am (UTC)",
DateComponents(year: 2027, month: 1, day: 2, hour: 3, minute: 0)),
(
DateComponents(year: 2027, month: 1, day: 1, hour: 0, minute: 5),
"Resets Dec 31, 11:59pm (UTC)",
DateComponents(year: 2026, month: 12, day: 31, hour: 23, minute: 59)),
(
DateComponents(year: 2027, month: 1, day: 1, hour: 0, minute: 5),
"Resets Dec 31, 11pm (UTC)",
DateComponents(year: 2026, month: 12, day: 31, hour: 23, minute: 0)),
(
DateComponents(year: 2026, month: 7, day: 9, hour: 15, minute: 5),
"Resets Jul 9, 3:00pm (UTC)",
DateComponents(year: 2026, month: 7, day: 9, hour: 15, minute: 0)),
]
for item in cases {
let now = try #require(calendar.date(from: item.now))
let parsed = ClaudeStatusProbe.parseResetDate(
from: item.text,
now: now,
expectedWindow: 7 * 24 * 60 * 60)
#expect(parsed == calendar.date(from: item.expected), "Failed weekly-window resolution: \(item.text)")
}
}
@Test
func `public claude reset parser remains forward looking`() throws {
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = try #require(TimeZone(identifier: "UTC"))
let cases: [(now: DateComponents, text: String, expected: DateComponents)] = [
(
DateComponents(year: 2026, month: 7, day: 9, hour: 8),
"Resets 9pm (UTC)",
DateComponents(year: 2026, month: 7, day: 9, hour: 21)),
(
DateComponents(year: 2026, month: 7, day: 9, hour: 20),
"Resets 1pm (UTC)",
DateComponents(year: 2026, month: 7, day: 10, hour: 13)),
(
DateComponents(year: 2026, month: 7, day: 9, hour: 12),
"Resets Jul 1, 9am (UTC)",
DateComponents(year: 2027, month: 7, day: 1, hour: 9)),
]
for item in cases {
let now = try #require(calendar.date(from: item.now))
let parsed = ClaudeStatusProbe.parseResetDate(from: item.text, now: now)
#expect(parsed == calendar.date(from: item.expected), "Failed future resolution: \(item.text)")
}
}
@Test
func `stale same day claude reset renders resets now`() throws {
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = try #require(TimeZone(identifier: "UTC"))
let now = try #require(calendar.date(from: DateComponents(
year: 2026, month: 7, day: 9, hour: 15, minute: 5, second: 0)))
let resetText = "Resets Jul 9, 3:00pm (UTC)"
let resetDate = try #require(ClaudeStatusProbe.parseResetDate(
from: resetText,
now: now,
expectedWindow: 5 * 60 * 60))
let window = RateWindow(
usedPercent: 73,
windowMinutes: 5 * 60,
resetsAt: resetDate,
resetDescription: resetText)
#expect(UsageFormatter.resetLine(for: window, style: .countdown, now: now) == "Resets now")
}
@Test
func `parses claude reset with dot separated time`() throws {
let now = Date(timeIntervalSince1970: 1_733_690_000)
let parsed = ClaudeStatusProbe.parseResetDate(from: "Resets Dec 9 at 5.27am (UTC)", now: now)
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = try #require(TimeZone(identifier: "UTC"))
let expected = calendar.date(from: DateComponents(year: 2024, month: 12, day: 9, hour: 5, minute: 27))
#expect(parsed == expected)
}
@Test
func `parses claude reset with compact times`() throws {
let now = Date(timeIntervalSince1970: 1_733_690_000)
let parsedTimeOnly = ClaudeStatusProbe.parseResetDate(from: "Resets 1pm (UTC)", now: now)
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = try #require(TimeZone(identifier: "UTC"))
let sameDay = try #require(calendar.date(bySettingHour: 13, minute: 0, second: 0, of: now))
let expected = try #require(calendar.date(byAdding: .day, value: 1, to: sameDay))
#expect(parsedTimeOnly == expected)
let parsedDateTime = ClaudeStatusProbe.parseResetDate(from: "Resets Dec 9, 9am", now: now)
calendar.timeZone = TimeZone.current
let dateExpected = calendar.date(from: DateComponents(
year: calendar.component(.year, from: now),
month: 12,
day: 9,
hour: 9,
minute: 0,
second: 0))
#expect(parsedDateTime == dateExpected)
}
@Test
func `parses claude reset with compact date and time no spaces`() throws {
let now = Date(timeIntervalSince1970: 1_773_097_200) // Mar 10, 2026 12:00:00 UTC
let parsed = ClaudeStatusProbe.parseResetDate(from: "ResetsMar13at12:30pm(Asia/Calcutta)", now: now)
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = try #require(TimeZone(identifier: "Asia/Calcutta"))
let expected = calendar.date(from: DateComponents(year: 2026, month: 3, day: 13, hour: 12, minute: 30))
#expect(parsed == expected)
}
@Test
func `live codex status`() async throws {
guard ProcessInfo.processInfo.environment["LIVE_CODEX_STATUS"] == "1" else { return }
let probe = CodexStatusProbe()
do {
let snap = try await probe.fetch()
let summary = """
Live Codex status:
\(snap.rawText)
values: 5h \(snap.fiveHourPercentLeft ?? -1)% left,
weekly \(snap.weeklyPercentLeft ?? -1)% left,
credits \(snap.credits ?? -1)
"""
print(summary)
} catch {
// Dump raw PTY text to help debug.
let runner = TTYCommandRunner()
let res = try runner.run(
binary: "codex",
send: "/status\n",
options: .init(rows: 60, cols: 200, timeout: 12))
print("RAW CODEX PTY OUTPUT BEGIN\n\(res.text)\nRAW CODEX PTY OUTPUT END")
let clean = TextParsing.stripANSICodes(res.text)
print("CLEAN CODEX OUTPUT BEGIN\n\(clean)\nCLEAN CODEX OUTPUT END")
let five = TextParsing.firstInt(pattern: #"5h limit[^\n]*?([0-9]{1,3})%\s+left"#, text: clean) ?? -1
let week = TextParsing.firstInt(pattern: #"Weekly limit[^\n]*?([0-9]{1,3})%\s+left"#, text: clean) ?? -1
let credits = TextParsing.firstNumber(pattern: #"Credits:\s*([0-9][0-9.,]*)"#, text: clean) ?? -1
print("Parsed probes => 5h \(five)% weekly \(week)% credits \(credits)")
throw error
}
}
}