Skip to content

Commit 8deef1e

Browse files
committed
Fix dashboard day keys and flaky tests
1 parent eb02364 commit 8deef1e

File tree

6 files changed

+71
-46
lines changed

6 files changed

+71
-46
lines changed

Sources/CodexBarCore/OpenAIWeb/OpenAIDashboardScrapeScript.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ let openAIDashboardScrapeScript = """
203203
};
204204
const dayKeyFromPayload = (payload) => {
205205
if (!payload || typeof payload !== 'object') return null;
206+
const localDayKeyForDate = (date) => {
207+
const year = date.getFullYear();
208+
const month = String(date.getMonth() + 1).padStart(2, '0');
209+
const day = String(date.getDate()).padStart(2, '0');
210+
return `${year}-${month}-${day}`;
211+
};
206212
const keys = ['day', 'date', 'name', 'label', 'x', 'time', 'timestamp'];
207213
for (const k of keys) {
208214
const v = payload[k];
@@ -215,7 +221,7 @@ let openAIDashboardScrapeScript = """
215221
if (typeof v === 'number' && Number.isFinite(v) && (k === 'timestamp' || k === 'time' || k === 'x')) {
216222
try {
217223
const d = new Date(v);
218-
if (!isNaN(d.getTime())) return d.toISOString().slice(0, 10);
224+
if (!isNaN(d.getTime())) return localDayKeyForDate(d);
219225
} catch {}
220226
}
221227
}

Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials+TestingOverrides.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ extension ClaudeOAuthCredentialsStore {
187187
}
188188
}
189189

190+
static func currentSecurityCLIReadOverrideForTesting() -> SecurityCLIReadOverride? {
191+
self.taskSecurityCLIReadOverride ?? self.securityCLIReadOverride
192+
}
193+
190194
static func withSecurityCLIReadAccountOverrideForTesting<T>(
191195
_ account: String?,
192196
operation: () throws -> T) rethrows -> T

Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthDelegatedRefreshCoordinator.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,25 @@ public enum ClaudeOAuthDelegatedRefreshCoordinator {
5555
// Detached to avoid inheriting the caller's executor context (e.g. MainActor) and cancellation state.
5656
let readStrategy = ClaudeOAuthKeychainReadStrategyPreference.current()
5757
let keychainAccessDisabled = KeychainAccessGate.isDisabled
58+
#if DEBUG
59+
let securityCLIReadOverride = ClaudeOAuthCredentialsStore.currentSecurityCLIReadOverrideForTesting()
60+
#endif
5861
let task = Task.detached(priority: .utility) {
62+
#if DEBUG
63+
return await ClaudeOAuthCredentialsStore.withSecurityCLIReadOverrideForTesting(securityCLIReadOverride) {
64+
await self.performAttempt(
65+
now: now,
66+
timeout: timeout,
67+
readStrategy: readStrategy,
68+
keychainAccessDisabled: keychainAccessDisabled)
69+
}
70+
#else
5971
await self.performAttempt(
6072
now: now,
6173
timeout: timeout,
6274
readStrategy: readStrategy,
6375
keychainAccessDisabled: keychainAccessDisabled)
76+
#endif
6477
}
6578
self.inFlightAttemptID = attemptID
6679
self.inFlightTask = task

Tests/CodexBarTests/ClaudeOAuthDelegatedRefreshCoordinatorTests.swift

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -255,11 +255,11 @@ struct ClaudeOAuthDelegatedRefreshCoordinatorTests {
255255
let securityData = self.makeCredentialsData(
256256
accessToken: "security-token-a",
257257
expiresAt: Date(timeIntervalSinceNow: 3600))
258-
ClaudeOAuthCredentialsStore.setSecurityCLIReadOverrideForTesting(.data(securityData))
259-
defer { ClaudeOAuthCredentialsStore.setSecurityCLIReadOverrideForTesting(nil) }
260-
let outcome = await ClaudeOAuthDelegatedRefreshCoordinator.attempt(
261-
now: Date(timeIntervalSince1970: 60000),
262-
timeout: 0.1)
258+
let outcome = await ClaudeOAuthCredentialsStore.withSecurityCLIReadOverrideForTesting(.data(securityData)) {
259+
await ClaudeOAuthDelegatedRefreshCoordinator.attempt(
260+
now: Date(timeIntervalSince1970: 60000),
261+
timeout: 0.1)
262+
}
263263

264264
guard case .attemptedFailed = outcome else {
265265
Issue.record("Expected .attemptedFailed outcome")
@@ -325,12 +325,13 @@ struct ClaudeOAuthDelegatedRefreshCoordinatorTests {
325325
ClaudeOAuthDelegatedRefreshCoordinator.setTouchAuthPathOverrideForTesting { _ in
326326
dataBox.store(afterData)
327327
}
328-
ClaudeOAuthCredentialsStore.setSecurityCLIReadOverrideForTesting(.dynamic { _ in dataBox.load() })
329-
defer { ClaudeOAuthCredentialsStore.setSecurityCLIReadOverrideForTesting(nil) }
330-
331-
let outcome = await ClaudeOAuthDelegatedRefreshCoordinator.attempt(
332-
now: Date(timeIntervalSince1970: 61000),
333-
timeout: 0.1)
328+
let outcome = await ClaudeOAuthCredentialsStore.withSecurityCLIReadOverrideForTesting(.dynamic { _ in
329+
dataBox.load()
330+
}) {
331+
await ClaudeOAuthDelegatedRefreshCoordinator.attempt(
332+
now: Date(timeIntervalSince1970: 61000),
333+
timeout: 0.1)
334+
}
334335

335336
#expect(outcome == .attemptedSucceeded)
336337
#expect(fingerprintCounter.count < 1)
@@ -390,12 +391,13 @@ struct ClaudeOAuthDelegatedRefreshCoordinatorTests {
390391
ClaudeOAuthDelegatedRefreshCoordinator.setTouchAuthPathOverrideForTesting { _ in
391392
dataBox.store(afterData)
392393
}
393-
ClaudeOAuthCredentialsStore.setSecurityCLIReadOverrideForTesting(.dynamic { _ in dataBox.load() })
394-
defer { ClaudeOAuthCredentialsStore.setSecurityCLIReadOverrideForTesting(nil) }
395-
396-
let outcome = await ClaudeOAuthDelegatedRefreshCoordinator.attempt(
397-
now: Date(timeIntervalSince1970: 61500),
398-
timeout: 0.1)
394+
let outcome = await ClaudeOAuthCredentialsStore.withSecurityCLIReadOverrideForTesting(.dynamic { _ in
395+
dataBox.load()
396+
}) {
397+
await ClaudeOAuthDelegatedRefreshCoordinator.attempt(
398+
now: Date(timeIntervalSince1970: 61500),
399+
timeout: 0.1)
400+
}
399401

400402
guard case .attemptedFailed = outcome else {
401403
Issue.record("Expected .attemptedFailed outcome when baseline is unavailable")
@@ -428,15 +430,15 @@ struct ClaudeOAuthDelegatedRefreshCoordinatorTests {
428430
expiresAt: Date(timeIntervalSinceNow: 3600))
429431
ClaudeOAuthDelegatedRefreshCoordinator.setCLIAvailableOverrideForTesting(true)
430432
ClaudeOAuthDelegatedRefreshCoordinator.setTouchAuthPathOverrideForTesting { _ in }
431-
ClaudeOAuthCredentialsStore.setSecurityCLIReadOverrideForTesting(.dynamic { _ in
433+
let outcome = await ClaudeOAuthCredentialsStore.withSecurityCLIReadOverrideForTesting(.dynamic { _ in
432434
securityReadCounter.increment()
433435
return securityData
434-
})
435-
defer { ClaudeOAuthCredentialsStore.setSecurityCLIReadOverrideForTesting(nil) }
436-
let outcome = await KeychainAccessGate.withTaskOverrideForTesting(true) {
437-
await ClaudeOAuthDelegatedRefreshCoordinator.attempt(
438-
now: Date(timeIntervalSince1970: 62000),
439-
timeout: 0.1)
436+
}) {
437+
await KeychainAccessGate.withTaskOverrideForTesting(true) {
438+
await ClaudeOAuthDelegatedRefreshCoordinator.attempt(
439+
now: Date(timeIntervalSince1970: 62000),
440+
timeout: 0.1)
441+
}
440442
}
441443

442444
guard case .attemptedFailed = outcome else {

Tests/CodexBarTests/HistoricalUsagePaceTestSupport.swift

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,7 @@ import Testing
44
@testable import CodexBar
55

66
extension HistoricalUsagePaceTests {
7-
private static let utcTimeZone: TimeZone = {
8-
if let zone = TimeZone(identifier: "UTC") {
9-
return zone
10-
}
11-
if let zone = TimeZone(secondsFromGMT: 0) {
12-
return zone
13-
}
14-
return TimeZone.current
15-
}()
7+
private static let dashboardTimeZone: TimeZone = .current
168

179
static func linearCurve(end: Double) -> [Double] {
1810
let clampedEnd = max(0, min(100, end))
@@ -45,10 +37,10 @@ extension HistoricalUsagePaceTests {
4537
overridesByDayOffset: [Int: Double] = [:]) -> [OpenAIDashboardDailyBreakdown]
4638
{
4739
var calendar = Calendar(identifier: .gregorian)
48-
calendar.timeZone = Self.utcTimeZone
40+
calendar.timeZone = Self.dashboardTimeZone
4941
let formatter = DateFormatter()
5042
formatter.locale = Locale(identifier: "en_US_POSIX")
51-
formatter.timeZone = Self.utcTimeZone
43+
formatter.timeZone = Self.dashboardTimeZone
5244
formatter.dateFormat = "yyyy-MM-dd"
5345

5446
let endDay = calendar.startOfDay(for: endDate)
@@ -65,10 +57,10 @@ extension HistoricalUsagePaceTests {
6557

6658
static func gregorianDate(year: Int, month: Int, day: Int, hour: Int) -> Date {
6759
var calendar = Calendar(identifier: .gregorian)
68-
calendar.timeZone = Self.utcTimeZone
60+
calendar.timeZone = Self.dashboardTimeZone
6961
var components = DateComponents()
7062
components.calendar = calendar
71-
components.timeZone = Self.utcTimeZone
63+
components.timeZone = Self.dashboardTimeZone
7264
components.year = year
7365
components.month = month
7466
components.day = day
@@ -91,10 +83,10 @@ extension HistoricalUsagePaceTests {
9183
return nil
9284
}
9385
var calendar = Calendar(identifier: .gregorian)
94-
calendar.timeZone = Self.utcTimeZone
86+
calendar.timeZone = Self.dashboardTimeZone
9587
var dateComponents = DateComponents()
9688
dateComponents.calendar = calendar
97-
dateComponents.timeZone = Self.utcTimeZone
89+
dateComponents.timeZone = Self.dashboardTimeZone
9890
dateComponents.year = year
9991
dateComponents.month = month
10092
dateComponents.day = day

Tests/CodexBarTests/OpenAIDashboardNavigationDelegateTests.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,25 @@ struct OpenAIDashboardNavigationDelegateTests {
6161
}
6262
}
6363

64-
@MainActor
6564
@Test("navigation timeout fails with timed out error")
6665
func navigationTimeoutFailsWithTimedOutError() async {
67-
var result: Result<Void, Error>?
68-
let delegate = NavigationDelegate { result = $0 }
66+
final class DelegateBox: @unchecked Sendable {
67+
var delegate: NavigationDelegate?
68+
}
6969

70-
delegate.armTimeout(seconds: 0.01)
71-
try? await Task.sleep(for: .milliseconds(30))
70+
let result = await withCheckedContinuation { (continuation: CheckedContinuation<Result<Void, Error>, Never>) in
71+
Task { @MainActor in
72+
let box = DelegateBox()
73+
box.delegate = NavigationDelegate { result in
74+
continuation.resume(returning: result)
75+
box.delegate = nil
76+
}
77+
box.delegate?.armTimeout(seconds: 0.01)
78+
}
79+
}
7280

7381
switch result {
74-
case let .failure(error as URLError)?:
82+
case let .failure(error as URLError):
7583
#expect(error.code == .timedOut)
7684
default:
7785
#expect(Bool(false))

0 commit comments

Comments
 (0)