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

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
}
}