162 lines
6.0 KiB
Swift
162 lines
6.0 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import CodexBarCore
|
|
|
|
struct CodexOAuthResetCreditFetchTests {
|
|
@Test
|
|
func `app enrichment can rescue reset-credit-only O auth usage`() throws {
|
|
let json = #"{"rate_limit":{"primary_window":null,"secondary_window":null}}"#
|
|
let result = try CodexOAuthFetchStrategy._mapResultForTesting(
|
|
Data(json.utf8),
|
|
credentials: Self.credentials(),
|
|
allowEmptyUsageForResetCreditEnrichment: true)
|
|
|
|
#expect(result.usage.primary == nil)
|
|
#expect(result.usage.secondary == nil)
|
|
#expect(result.usage.codexResetCredits == nil)
|
|
#expect(result.credits == nil)
|
|
#expect(result.strategyID == "codex.oauth")
|
|
}
|
|
|
|
@Test
|
|
func `app defers reset credit GET while CLI attempts it once on failure`() async throws {
|
|
let credentials = Self.credentials()
|
|
let recorder = CodexOAuthResetCreditFetchRecorder()
|
|
let fetcher: @Sendable (CodexOAuthCredentials) async throws -> CodexRateLimitResetCreditsSnapshot = { _ in
|
|
await recorder.recordRequest()
|
|
throw CodexOAuthFetchError.serverError(500, nil)
|
|
}
|
|
|
|
let appResult = try await CodexOAuthFetchStrategy._fetchResetCreditsForTesting(
|
|
context: Self.context(runtime: .app),
|
|
credentials: credentials,
|
|
fetcher: fetcher)
|
|
#expect(appResult == nil)
|
|
#expect(await recorder.requestCount() == 0)
|
|
|
|
let cliResult = try await CodexOAuthFetchStrategy._fetchResetCreditsForTesting(
|
|
context: Self.context(runtime: .cli),
|
|
credentials: credentials,
|
|
fetcher: fetcher)
|
|
#expect(cliResult == nil)
|
|
#expect(await recorder.requestCount() == 1)
|
|
}
|
|
|
|
@Test
|
|
func `CLI reset credit GET preserves cancellation without retry`() async throws {
|
|
let recorder = CodexOAuthResetCreditFetchRecorder()
|
|
|
|
await #expect(throws: CancellationError.self) {
|
|
_ = try await CodexOAuthFetchStrategy._fetchResetCreditsForTesting(
|
|
context: Self.context(runtime: .cli),
|
|
credentials: Self.credentials(),
|
|
fetcher: { _ in
|
|
await recorder.recordRequest()
|
|
throw CancellationError()
|
|
})
|
|
}
|
|
#expect(await recorder.requestCount() == 1)
|
|
}
|
|
|
|
@Test
|
|
func `reset credit inventory only O auth payload still returns usage result`() throws {
|
|
let json = #"{"rate_limit":{"primary_window":null,"secondary_window":null}}"#
|
|
let now = Date()
|
|
let resetCredits = CodexRateLimitResetCreditsSnapshot(
|
|
credits: [
|
|
CodexRateLimitResetCredit(
|
|
id: "available-no-expiry",
|
|
resetType: "codex_rate_limits",
|
|
status: .available,
|
|
grantedAt: now,
|
|
expiresAt: nil,
|
|
redeemStartedAt: nil,
|
|
redeemedAt: nil,
|
|
title: nil,
|
|
description: nil),
|
|
],
|
|
availableCount: 1,
|
|
updatedAt: now)
|
|
|
|
let result = try CodexOAuthFetchStrategy._mapResultForTesting(
|
|
Data(json.utf8),
|
|
credentials: Self.credentials(),
|
|
resetCredits: resetCredits)
|
|
|
|
#expect(result.usage.primary == nil)
|
|
#expect(result.usage.secondary == nil)
|
|
#expect(result.usage.codexResetCredits?.availableInventory(at: now).count == 1)
|
|
#expect(result.credits == nil)
|
|
#expect(result.sourceLabel == "oauth")
|
|
}
|
|
|
|
@Test
|
|
func `empty reset credits do not mask missing O auth usage`() {
|
|
let json = #"{"rate_limit":{"primary_window":null,"secondary_window":null}}"#
|
|
let resetCredits = CodexRateLimitResetCreditsSnapshot(
|
|
credits: [],
|
|
availableCount: 0,
|
|
updatedAt: Date())
|
|
|
|
#expect(throws: UsageError.self) {
|
|
try CodexOAuthFetchStrategy._mapResultForTesting(
|
|
Data(json.utf8),
|
|
credentials: Self.credentials(),
|
|
resetCredits: resetCredits)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `O auth strategy defers app inventory and CLI follows credits flag`() {
|
|
let appContext = Self.context(runtime: .app, includeCredits: false, includeOptionalUsage: false)
|
|
let cliNoCreditsContext = Self.context(runtime: .cli, includeCredits: false, includeOptionalUsage: true)
|
|
let cliCreditsContext = Self.context(runtime: .cli, includeCredits: true, includeOptionalUsage: false)
|
|
|
|
#expect(CodexOAuthFetchStrategy._shouldFetchResetCreditsForTesting(appContext) == false)
|
|
#expect(CodexOAuthFetchStrategy._shouldFetchResetCreditsForTesting(cliNoCreditsContext) == false)
|
|
#expect(CodexOAuthFetchStrategy._shouldFetchResetCreditsForTesting(cliCreditsContext))
|
|
}
|
|
|
|
private static func context(
|
|
runtime: ProviderRuntime,
|
|
includeCredits: Bool = true,
|
|
includeOptionalUsage: Bool = false) -> ProviderFetchContext
|
|
{
|
|
let browserDetection = BrowserDetection(cacheTTL: 0)
|
|
return ProviderFetchContext(
|
|
runtime: runtime,
|
|
sourceMode: .auto,
|
|
includeCredits: includeCredits,
|
|
includeOptionalUsage: includeOptionalUsage,
|
|
webTimeout: 60,
|
|
webDebugDumpHTML: false,
|
|
verbose: false,
|
|
env: [:],
|
|
settings: nil,
|
|
fetcher: UsageFetcher(),
|
|
claudeFetcher: ClaudeUsageFetcher(browserDetection: browserDetection),
|
|
browserDetection: browserDetection)
|
|
}
|
|
|
|
private static func credentials() -> CodexOAuthCredentials {
|
|
CodexOAuthCredentials(
|
|
accessToken: "access",
|
|
refreshToken: "refresh",
|
|
idToken: nil,
|
|
accountId: "account-123",
|
|
lastRefresh: Date())
|
|
}
|
|
}
|
|
|
|
private actor CodexOAuthResetCreditFetchRecorder {
|
|
private var count = 0
|
|
|
|
func recordRequest() {
|
|
self.count += 1
|
|
}
|
|
|
|
func requestCount() -> Int {
|
|
self.count
|
|
}
|
|
}
|