Files
steipete--codexbar/TestsLinux/ClaudeOAuthDelegatedRefreshLinuxTests.swift
2026-07-13 12:22:33 +08:00

168 lines
6.0 KiB
Swift

import Foundation
import Testing
@testable import CodexBarCore
@Suite(.serialized)
struct ClaudeOAuthDelegatedRefreshLinuxTests {
private actor Counter {
private var value = 0
func increment() {
self.value += 1
}
func current() -> Int {
self.value
}
}
private actor VersionDetectionCapture {
private var value: Bool?
func record(_ value: Bool) {
self.value = value
}
func current() -> Bool? {
self.value
}
}
@Test
func cliOAuthSkipsVersionDetectionWhileAppPreservesIt() async throws {
#expect(try await self.detectsClaudeVersion(runtime: .cli) == false)
#expect(try await self.detectsClaudeVersion(runtime: .app) == true)
}
@Test
func cliOAuthDoesNotDelegateRefreshEvenForUserAction() async {
let result = await self.runDelegatedRefresh(
runtime: .cli,
interaction: .userInitiated,
promptMode: .always)
#expect(result.attempts == 0)
#expect(result.message.contains("CodexBar CLI does not launch Claude"))
}
@Test
func appOAuthPreservesUserInitiatedDelegatedRefresh() async {
let result = await self.runDelegatedRefresh(
runtime: .app,
interaction: .userInitiated,
promptMode: .onlyOnUserAction)
#expect(result.attempts == 1)
#expect(result.message.contains("still unavailable after delegated Claude CLI refresh"))
}
@Test
func appOAuthBackgroundRespectsPlatformKeychainPromptPolicy() async {
let result = await self.runDelegatedRefresh(
runtime: .app,
interaction: .background,
promptMode: .onlyOnUserAction)
#expect(result.attempts == 0)
#expect(result.message.contains("background repair is suppressed"))
}
private func runDelegatedRefresh(
runtime: ProviderRuntime,
interaction: ProviderInteraction,
promptMode: ClaudeOAuthKeychainPromptMode) async -> (attempts: Int, message: String)
{
let counter = Counter()
let fetcher = ClaudeUsageFetcher(
browserDetection: BrowserDetection(cacheTTL: 0),
environment: [:],
runtime: runtime,
dataSource: .oauth)
let credentialsOverride: @Sendable (
[String: String],
Bool,
Bool) async throws -> ClaudeOAuthCredentials = { _, _, _ in
throw ClaudeOAuthCredentialsError.refreshDelegatedToClaudeCLI
}
let delegatedOverride: @Sendable (
Date,
TimeInterval,
[String: String]) async -> ClaudeOAuthDelegatedRefreshCoordinator.Outcome = { _, _, _ in
await counter.increment()
return .attemptedSucceeded
}
do {
_ = try await ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(promptMode) {
try await ProviderInteractionContext.$current.withValue(interaction) {
try await ClaudeUsageFetcher.$loadOAuthCredentialsOverride
.withValue(credentialsOverride) {
try await ClaudeUsageFetcher.$delegatedRefreshAttemptOverride
.withValue(delegatedOverride) {
try await fetcher.loadLatestUsage(model: "sonnet")
}
}
}
}
Issue.record("Expected delegated-refresh path to fail with mocked stale credentials")
return (await counter.current(), "")
} catch let error as ClaudeUsageError {
guard case let .oauthFailed(message) = error else {
Issue.record("Expected ClaudeUsageError.oauthFailed, got \(error)")
return (await counter.current(), "")
}
return (await counter.current(), message)
} catch {
Issue.record("Expected ClaudeUsageError, got \(error)")
return (await counter.current(), "")
}
}
private func detectsClaudeVersion(runtime: ProviderRuntime) async throws -> Bool {
let capture = VersionDetectionCapture()
let fetcher = ClaudeUsageFetcher(
browserDetection: BrowserDetection(cacheTTL: 0),
environment: [:],
runtime: runtime,
dataSource: .oauth)
let credentialsOverride: @Sendable (
[String: String],
Bool,
Bool) async throws -> ClaudeOAuthCredentials = { _, _, _ in
ClaudeOAuthCredentials(
accessToken: "access-token",
refreshToken: nil,
expiresAt: Date(timeIntervalSinceNow: 3600),
scopes: ["user:profile"],
rateLimitTier: nil)
}
let fetchOverride: @Sendable (String, Bool) async throws -> OAuthUsageResponse = {
_, detectClaudeVersion in
await capture.record(detectClaudeVersion)
return try Self.makeOAuthUsageResponse()
}
_ = try await ClaudeUsageFetcher.$loadOAuthCredentialsOverride.withValue(credentialsOverride) {
try await ClaudeUsageFetcher.$fetchOAuthUsageOverride.withValue(fetchOverride) {
try await fetcher.loadLatestUsage(model: "sonnet")
}
}
guard let value = await capture.current() else {
Issue.record("Expected OAuth fetch to report its version-detection policy")
return false
}
return value
}
private static func makeOAuthUsageResponse() throws -> OAuthUsageResponse {
let json = """
{
"five_hour": { "utilization": 7, "resets_at": "2025-12-23T16:00:00.000Z" },
"seven_day": { "utilization": 21, "resets_at": "2025-12-29T23:00:00.000Z" }
}
"""
return try ClaudeOAuthUsageFetcher._decodeUsageResponseForTesting(Data(json.utf8))
}
}