Files
steipete--codexbar/Tests/CodexBarTests/CodexResetCreditOutcomeTests.swift
2026-07-13 12:22:33 +08:00

272 lines
10 KiB
Swift

import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
struct CodexResetCreditOutcomeTests {
@Test
func `supplemental inventory skips stale credentials without issuing a request`() async throws {
let recorder = ResetCreditRequestRecorder()
let result = try await UsageStore._fetchCodexResetCreditsForTesting(
credentials: Self.credentials(lastRefresh: .distantPast),
request: { accessToken, accountID, environment in
await recorder.record(accessToken: accessToken, accountID: accountID, environment: environment)
return Self.resetSnapshot(id: "unexpected", now: Date())
})
#expect(result == nil)
#expect(await recorder.count() == 0)
}
@Test
func `supplemental inventory uses fresh credentials for one read only request`() async throws {
let recorder = ResetCreditRequestRecorder()
let now = Date()
let expected = Self.resetSnapshot(id: "fresh", now: now)
let result = try await UsageStore._fetchCodexResetCreditsForTesting(
credentials: Self.credentials(lastRefresh: now),
env: ["CODEX_HOME": "/tmp/account-a"],
request: { accessToken, accountID, environment in
await recorder.record(accessToken: accessToken, accountID: accountID, environment: environment)
return expected
})
#expect(result == expected)
#expect(await recorder.count() == 1)
#expect(await recorder.lastAccessToken() == "access")
#expect(await recorder.lastAccountID() == "account-123")
#expect(await recorder.lastEnvironment()["CODEX_HOME"] == "/tmp/account-a")
}
@Test
func `embedded OAuth inventory prevents a duplicate supplemental GET`() async throws {
let now = Date(timeIntervalSince1970: 1_781_726_400)
let embedded = Self.resetSnapshot(id: "embedded", now: now)
let recorder = ResetCreditFetchRecorder()
let outcome = await UsageStore.attachingCodexResetCreditsIfNeeded(
to: Self.outcome(resetCredits: embedded, now: now),
env: ["CODEX_HOME": "/tmp/account-a"],
fetcher: { env in
await recorder.record(env)
return Self.resetSnapshot(id: "supplemental", now: now)
})
#expect(try Self.usage(from: outcome).codexResetCredits == embedded)
#expect(await recorder.environments().isEmpty)
}
@Test
func `supplemental inventory uses each scoped account environment once`() async throws {
let now = Date(timeIntervalSince1970: 1_781_726_400)
let recorder = ResetCreditFetchRecorder()
let fetcher: UsageStore.CodexResetCreditsFetcher = { env in
await recorder.record(env)
let home = env["CODEX_HOME"] ?? "missing"
return Self.resetSnapshot(id: home, now: now)
}
let first = await UsageStore.attachingCodexResetCreditsIfNeeded(
to: Self.outcome(resetCredits: nil, now: now),
env: ["CODEX_HOME": "/tmp/account-a"],
fetcher: fetcher)
let second = await UsageStore.attachingCodexResetCreditsIfNeeded(
to: Self.outcome(resetCredits: nil, now: now),
env: ["CODEX_HOME": "/tmp/account-b"],
fetcher: fetcher)
#expect(try Self.usage(from: first).codexResetCredits?.credits.first?.id ==
Self.resetSnapshot(id: "/tmp/account-a", now: now).credits.first?.id)
#expect(try Self.usage(from: second).codexResetCredits?.credits.first?.id ==
Self.resetSnapshot(id: "/tmp/account-b", now: now).credits.first?.id)
#expect(await recorder.environments().compactMap { $0["CODEX_HOME"] } == [
"/tmp/account-a",
"/tmp/account-b",
])
}
@Test
func `failed supplemental GET clears inventory on a successful usage refresh`() async throws {
let now = Date(timeIntervalSince1970: 1_781_726_400)
let recorder = ResetCreditFetchRecorder()
let outcome = await UsageStore.attachingCodexResetCreditsIfNeeded(
to: Self.outcome(resetCredits: nil, now: now),
env: ["CODEX_HOME": "/tmp/account-a"],
fetcher: { env in
await recorder.record(env)
throw ResetCreditFetchTestError.failed
})
#expect(try Self.usage(from: outcome).codexResetCredits == nil)
#expect(await recorder.environments().count == 1)
}
@Test
func `single failed GET restores failure for reset-credit-only O auth usage`() async {
let now = Date(timeIntervalSince1970: 1_781_726_400)
let recorder = ResetCreditFetchRecorder()
let outcome = await UsageStore.attachingCodexResetCreditsIfNeeded(
to: Self.outcome(resetCredits: nil, now: now, primary: nil, strategyID: "codex.oauth"),
env: ["CODEX_HOME": "/tmp/account-a"],
fetcher: { env in
await recorder.record(env)
throw ResetCreditFetchTestError.failed
})
guard case let .failure(error) = outcome.result else {
Issue.record("Expected no-data failure")
return
}
#expect(error is UsageError)
#expect(await recorder.environments().count == 1)
}
@Test
func `single GET rescues reset-credit-only O auth usage`() async throws {
let now = Date(timeIntervalSince1970: 1_781_726_400)
let recorder = ResetCreditFetchRecorder()
let resetCredits = Self.resetSnapshot(id: "rescued", now: now)
let outcome = await UsageStore.attachingCodexResetCreditsIfNeeded(
to: Self.outcome(resetCredits: nil, now: now, primary: nil, strategyID: "codex.oauth"),
env: ["CODEX_HOME": "/tmp/account-a"],
fetcher: { env in
await recorder.record(env)
return resetCredits
})
#expect(try Self.usage(from: outcome).codexResetCredits == resetCredits)
#expect(await recorder.environments().count == 1)
}
@Test
func `supplemental GET cancellation remains a cancelled provider outcome`() async {
let now = Date(timeIntervalSince1970: 1_781_726_400)
let outcome = await UsageStore.attachingCodexResetCreditsIfNeeded(
to: Self.outcome(resetCredits: nil, now: now),
env: ["CODEX_HOME": "/tmp/account-a"],
fetcher: { _ in throw CancellationError() })
guard case let .failure(error) = outcome.result else {
Issue.record("Expected cancellation failure")
return
}
#expect(error is CancellationError)
}
@Test
func `display preference does not strip embedded inventory or issue a duplicate GET`() async throws {
let now = Date(timeIntervalSince1970: 1_781_726_400)
let recorder = ResetCreditFetchRecorder()
let outcome = await UsageStore.attachingCodexResetCreditsIfNeeded(
to: Self.outcome(resetCredits: Self.resetSnapshot(id: "embedded", now: now), now: now),
env: ["CODEX_HOME": "/tmp/account-a"],
fetcher: { env in
await recorder.record(env)
return Self.resetSnapshot(id: "supplemental", now: now)
})
#expect(try Self.usage(from: outcome).codexResetCredits == Self.resetSnapshot(id: "embedded", now: now))
#expect(await recorder.environments().isEmpty)
}
private static func outcome(
resetCredits: CodexRateLimitResetCreditsSnapshot?,
now: Date,
primary: RateWindow? = nil,
strategyID: String = "test") -> ProviderFetchOutcome
{
let resolvedPrimary = strategyID == "codex.oauth" ? primary : primary ?? RateWindow(
usedPercent: 25,
windowMinutes: 300,
resetsAt: now.addingTimeInterval(3600),
resetDescription: nil)
return ProviderFetchOutcome(
result: .success(ProviderFetchResult(
usage: UsageSnapshot(
primary: resolvedPrimary,
secondary: nil,
codexResetCredits: resetCredits,
updatedAt: now),
credits: nil,
dashboard: nil,
sourceLabel: "test",
strategyID: strategyID,
strategyKind: .cli)),
attempts: [])
}
private static func resetSnapshot(id: String, now: Date) -> CodexRateLimitResetCreditsSnapshot {
CodexRateLimitResetCreditsSnapshot(
credits: [CodexRateLimitResetCredit(
id: id,
resetType: "codex_rate_limits",
status: .available,
grantedAt: now,
expiresAt: now.addingTimeInterval(86400),
redeemStartedAt: nil,
redeemedAt: nil,
title: nil,
description: nil)],
availableCount: 1,
updatedAt: now)
}
private static func credentials(lastRefresh: Date?) -> CodexOAuthCredentials {
CodexOAuthCredentials(
accessToken: "access",
refreshToken: "refresh",
idToken: nil,
accountId: "account-123",
lastRefresh: lastRefresh)
}
private static func usage(from outcome: ProviderFetchOutcome) throws -> UsageSnapshot {
switch outcome.result {
case let .success(result):
result.usage
case let .failure(error):
throw error
}
}
}
private actor ResetCreditRequestRecorder {
private var requests: [(accessToken: String, accountID: String?, environment: [String: String])] = []
func record(accessToken: String, accountID: String?, environment: [String: String]) {
self.requests.append((accessToken, accountID, environment))
}
func count() -> Int {
self.requests.count
}
func lastAccessToken() -> String? {
self.requests.last?.accessToken
}
func lastAccountID() -> String? {
self.requests.last?.accountID
}
func lastEnvironment() -> [String: String] {
self.requests.last?.environment ?? [:]
}
}
private actor ResetCreditFetchRecorder {
private var capturedEnvironments: [[String: String]] = []
func record(_ env: [String: String]) {
self.capturedEnvironments.append(env)
}
func environments() -> [[String: String]] {
self.capturedEnvironments
}
}
private enum ResetCreditFetchTestError: Error {
case failed
}