import Foundationimport Testing@testable import CodexBar@testable import CodexBarCore struct ClaudeResilienceTests { @Test func `suppresses single flake when prior data exists`() { var gate = ConsecutiveFailureGate() let firstFailure = gate.shouldSurfaceError(onFailureWithPriorData: true) let secondFailure = gate.shouldSurfaceError(onFailureWithPriorData: true) #expect(firstFailure == false) #expect(secondFailure == true) } @Test func `surfaces failure without prior data`() { var gate = ConsecutiveFailureGate() let shouldSurface = gate.shouldSurfaceError(onFailureWithPriorData: false) #expect(shouldSurface) } @Test func `resets after success`() { var gate = ConsecutiveFailureGate() _ = gate.shouldSurfaceError(onFailureWithPriorData: true) gate.recordSuccess() let shouldSurface = gate.shouldSurfaceError(onFailureWithPriorData: true) #expect(shouldSurface == false) } @Test func `timeout keeps prior Claude snapshot without surfacing repeated failure`() async throws { try await ClaudeOAuthCredentialsStore.withIsolatedCredentialsFileTrackingForTesting { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) let fileURL = tempDir.appendingPathComponent("missing-credentials.json") try await ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) { let (store, prior) = try await MainActor.run { let settings = Self.makeSettingsStore(suite: "ClaudeResilienceTests-timeout-cache") settings.refreshFrequency = .manual settings.statusChecksEnabled = false settings.claudeUsageDataSource = .cli let metadata = ProviderRegistry.shared.metadata for provider in UsageProvider.allCases { try settings.setProviderEnabled( provider: provider, metadata: #require(metadata[provider]), enabled: provider == .claude) } let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings, startupBehavior: .testing, environmentBase: [:]) let prior = UsageSnapshot( primary: RateWindow(usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil), secondary: RateWindow( usedPercent: 34, windowMinutes: 10080, resetsAt: nil, resetDescription: nil), updatedAt: Date(timeIntervalSince1970: 1_800_000_000), identity: ProviderIdentitySnapshot( providerID: .claude, accountEmail: "claude@example.com", accountOrganization: nil, loginMethod: "Pro")) store._setSnapshotForTesting(prior, provider: .claude) let baseSpec = try #require(store.providerSpecs[.claude]) let descriptor = ProviderDescriptor( id: .claude, metadata: baseSpec.descriptor.metadata, branding: baseSpec.descriptor.branding, tokenCost: baseSpec.descriptor.tokenCost, fetchPlan: ProviderFetchPlan( sourceModes: [.cli], pipeline: ProviderFetchPipeline { _ in [TimeoutFetchStrategy()] }), cli: baseSpec.descriptor.cli) store.providerSpecs[.claude] = ProviderSpec( style: baseSpec.style, isEnabled: baseSpec.isEnabled, descriptor: descriptor, makeFetchContext: baseSpec.makeFetchContext) return (store, prior) } await store.refreshProvider(.claude) let firstResult = await MainActor.run { ( updatedAt: store.snapshot(for: .claude)?.updatedAt, hasError: store.error(for: .claude) != nil) } #expect(firstResult.updatedAt == prior.updatedAt) #expect(!firstResult.hasError) await store.refreshProvider(.claude) let secondResult = await MainActor.run { ( updatedAt: store.snapshot(for: .claude)?.updatedAt, hasError: store.error(for: .claude) != nil) } #expect(secondResult.updatedAt == prior.updatedAt) #expect(!secondResult.hasError) } } } @Test func `repeated non probe transient failure still surfaces`() async throws { try await ClaudeOAuthCredentialsStore.withIsolatedCredentialsFileTrackingForTesting { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) let fileURL = tempDir.appendingPathComponent("missing-credentials.json") try await ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) { let (store, prior) = try await MainActor.run { let settings = Self.makeSettingsStore(suite: "ClaudeResilienceTests-network-cache") settings.refreshFrequency = .manual settings.statusChecksEnabled = false settings.claudeUsageDataSource = .cli let metadata = ProviderRegistry.shared.metadata for provider in UsageProvider.allCases { try settings.setProviderEnabled( provider: provider, metadata: #require(metadata[provider]), enabled: provider == .claude) } let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings, startupBehavior: .testing, environmentBase: [:]) let prior = UsageSnapshot( primary: RateWindow(usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil), secondary: nil, updatedAt: Date(timeIntervalSince1970: 1_800_000_000), identity: ProviderIdentitySnapshot( providerID: .claude, accountEmail: "claude@example.com", accountOrganization: nil, loginMethod: "Pro")) store._setSnapshotForTesting(prior, provider: .claude) let baseSpec = try #require(store.providerSpecs[.claude]) let descriptor = ProviderDescriptor( id: .claude, metadata: baseSpec.descriptor.metadata, branding: baseSpec.descriptor.branding, tokenCost: baseSpec.descriptor.tokenCost, fetchPlan: ProviderFetchPlan( sourceModes: [.cli], pipeline: ProviderFetchPipeline { _ in [NetworkLostFetchStrategy()] }), cli: baseSpec.descriptor.cli) store.providerSpecs[.claude] = ProviderSpec( style: baseSpec.style, isEnabled: baseSpec.isEnabled, descriptor: descriptor, makeFetchContext: baseSpec.makeFetchContext) return (store, prior) } await store.refreshProvider(.claude) let firstResult = await MainActor.run { ( updatedAt: store.snapshot(for: .claude)?.updatedAt, hasError: store.error(for: .claude) != nil) } #expect(firstResult.updatedAt == prior.updatedAt) #expect(!firstResult.hasError) await store.refreshProvider(.claude) let secondResult = await MainActor.run { ( updatedAt: store.snapshot(for: .claude)?.updatedAt, hasError: store.error(for: .claude) != nil) } #expect(secondResult.updatedAt == prior.updatedAt) #expect(secondResult.hasError) } } } @Test func `credentials change clears prior Claude snapshot for non transient failure`() async throws { try await KeychainCacheStore.withServiceOverrideForTesting("com.steipete.codexbar.cache.tests.\(UUID())") { KeychainCacheStore.setTestStoreForTesting(true) defer { KeychainCacheStore.setTestStoreForTesting(false) } try await ClaudeOAuthCredentialsStore.withIsolatedCredentialsFileTrackingForTesting { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) let fileURL = tempDir.appendingPathComponent("credentials.json") try Data("{}".utf8).write(to: fileURL) try await ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) { #expect(ClaudeOAuthCredentialsStore.invalidateCacheIfCredentialsFileChanged()) let store = try await MainActor.run { let settings = Self.makeSettingsStore(suite: "ClaudeResilienceTests-auth-change") settings.refreshFrequency = .manual settings.statusChecksEnabled = false settings.claudeUsageDataSource = .cli let metadata = ProviderRegistry.shared.metadata for provider in UsageProvider.allCases { try settings.setProviderEnabled( provider: provider, metadata: #require(metadata[provider]), enabled: provider == .claude) } let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings, startupBehavior: .testing, environmentBase: [:]) let prior = UsageSnapshot( primary: RateWindow( usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil), secondary: nil, updatedAt: Date(timeIntervalSince1970: 1_800_000_000), identity: ProviderIdentitySnapshot( providerID: .claude, accountEmail: "old@example.com", accountOrganization: nil, loginMethod: "Pro")) store._setSnapshotForTesting(prior, provider: .claude) let baseSpec = try #require(store.providerSpecs[.claude]) let descriptor = ProviderDescriptor( id: .claude, metadata: baseSpec.descriptor.metadata, branding: baseSpec.descriptor.branding, tokenCost: baseSpec.descriptor.tokenCost, fetchPlan: ProviderFetchPlan( sourceModes: [.cli], pipeline: ProviderFetchPipeline { _ in [AuthFailureFetchStrategy(credentialsFileURL: fileURL)] }), cli: baseSpec.descriptor.cli) store.providerSpecs[.claude] = ProviderSpec( style: baseSpec.style, isEnabled: baseSpec.isEnabled, descriptor: descriptor, makeFetchContext: baseSpec.makeFetchContext) return store } await store.refreshProvider(.claude) let result = await MainActor.run { ( hasSnapshot: store.snapshot(for: .claude) != nil, hasError: store.error(for: .claude) != nil) } #expect(!result.hasSnapshot) #expect(result.hasError) } } } } @Test func `credentials change clears prior Claude snapshot for transient failure`() async throws { try await KeychainCacheStore.withServiceOverrideForTesting("com.steipete.codexbar.cache.tests.\(UUID())") { KeychainCacheStore.setTestStoreForTesting(true) defer { KeychainCacheStore.setTestStoreForTesting(false) } try await ClaudeOAuthCredentialsStore.withIsolatedCredentialsFileTrackingForTesting { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) let fileURL = tempDir.appendingPathComponent("credentials.json") try Data("{}".utf8).write(to: fileURL) try await ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) { #expect(ClaudeOAuthCredentialsStore.invalidateCacheIfCredentialsFileChanged()) let store = try await MainActor.run { let settings = Self.makeSettingsStore(suite: "ClaudeResilienceTests-transient-auth-change") settings.refreshFrequency = .manual settings.statusChecksEnabled = false settings.claudeUsageDataSource = .cli let metadata = ProviderRegistry.shared.metadata for provider in UsageProvider.allCases { try settings.setProviderEnabled( provider: provider, metadata: #require(metadata[provider]), enabled: provider == .claude) } let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings, startupBehavior: .testing, environmentBase: [:]) let prior = UsageSnapshot( primary: RateWindow( usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil), secondary: nil, updatedAt: Date(timeIntervalSince1970: 1_800_000_000), identity: ProviderIdentitySnapshot( providerID: .claude, accountEmail: "old@example.com", accountOrganization: nil, loginMethod: "Pro")) store._setSnapshotForTesting(prior, provider: .claude) let baseSpec = try #require(store.providerSpecs[.claude]) let descriptor = ProviderDescriptor( id: .claude, metadata: baseSpec.descriptor.metadata, branding: baseSpec.descriptor.branding, tokenCost: baseSpec.descriptor.tokenCost, fetchPlan: ProviderFetchPlan( sourceModes: [.cli], pipeline: ProviderFetchPipeline { _ in [TransientFailureAfterCredentialSwapFetchStrategy(credentialsFileURL: fileURL)] }), cli: baseSpec.descriptor.cli) store.providerSpecs[.claude] = ProviderSpec( style: baseSpec.style, isEnabled: baseSpec.isEnabled, descriptor: descriptor, makeFetchContext: baseSpec.makeFetchContext) return store } await store.refreshProvider(.claude) let result = await MainActor.run { ( hasSnapshot: store.snapshot(for: .claude) != nil, hasError: store.error(for: .claude) != nil) } #expect(!result.hasSnapshot) #expect(result.hasError) } } } } @Test func `keychain change clears prior Claude snapshot for transient failure`() async throws { try await KeychainCacheStore.withServiceOverrideForTesting("com.steipete.codexbar.cache.tests.\(UUID())") { KeychainCacheStore.setTestStoreForTesting(true) defer { KeychainCacheStore.setTestStoreForTesting(false) } let storedFingerprint = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprint( modifiedAt: 1, createdAt: 1, persistentRefHash: "old") let currentFingerprint = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprint( modifiedAt: 2, createdAt: 1, persistentRefHash: "new") let fingerprintStore = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprintStore( fingerprint: storedFingerprint) try await ClaudeOAuthCredentialsStore.withClaudeKeychainFingerprintStoreOverrideForTesting( fingerprintStore) { try await ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.always) { try await ClaudeOAuthCredentialsStore.withKeychainAccessOverrideForTesting(false) { try await KeychainAccessGate.withTaskOverrideForTesting(false) { try await ClaudeOAuthKeychainAccessGate.withShouldAllowPromptOverrideForTesting(true) { try await ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting( data: nil, fingerprint: currentFingerprint) { try await ClaudeOAuthCredentialsStore .withIsolatedCredentialsFileTrackingForTesting { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory( at: tempDir, withIntermediateDirectories: true) let fileURL = tempDir.appendingPathComponent("missing-credentials.json") try await ClaudeOAuthCredentialsStore .withCredentialsURLOverrideForTesting(fileURL) { let store = try await MainActor.run { let settings = Self.makeSettingsStore( suite: "ClaudeResilienceTests-keychain-auth-change") settings.refreshFrequency = .manual settings.statusChecksEnabled = false settings.claudeUsageDataSource = .cli let metadata = ProviderRegistry.shared.metadata for provider in UsageProvider.allCases { try settings.setProviderEnabled( provider: provider, metadata: #require(metadata[provider]), enabled: provider == .claude) } let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings, startupBehavior: .testing, environmentBase: [:]) let prior = UsageSnapshot( primary: RateWindow( usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil), secondary: nil, updatedAt: Date(timeIntervalSince1970: 1_800_000_000), identity: ProviderIdentitySnapshot( providerID: .claude, accountEmail: "old@example.com", accountOrganization: nil, loginMethod: "Pro")) store._setSnapshotForTesting(prior, provider: .claude) let baseSpec = try #require(store.providerSpecs[.claude]) let descriptor = ProviderDescriptor( id: .claude, metadata: baseSpec.descriptor.metadata, branding: baseSpec.descriptor.branding, tokenCost: baseSpec.descriptor.tokenCost, fetchPlan: ProviderFetchPlan( sourceModes: [.cli], pipeline: ProviderFetchPipeline { _ in [TimeoutFetchStrategy()] }), cli: baseSpec.descriptor.cli) store.providerSpecs[.claude] = ProviderSpec( style: baseSpec.style, isEnabled: baseSpec.isEnabled, descriptor: descriptor, makeFetchContext: baseSpec.makeFetchContext) return store } await store.refreshProvider(.claude) let result = await MainActor.run { ( hasSnapshot: store.snapshot(for: .claude) != nil, hasError: store.error(for: .claude) != nil) } #expect(!result.hasSnapshot) #expect(result.hasError) } } } } } } } } } } @Test func `keychain removal clears prior Claude snapshot for transient failure`() async throws { try await KeychainCacheStore.withServiceOverrideForTesting("com.steipete.codexbar.cache.tests.\(UUID())") { KeychainCacheStore.setTestStoreForTesting(true) defer { KeychainCacheStore.setTestStoreForTesting(false) } let storedFingerprint = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprint( modifiedAt: 1, createdAt: 1, persistentRefHash: "old") let fingerprintStore = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprintStore( fingerprint: storedFingerprint) let keychainStore = ClaudeOAuthCredentialsStore.ClaudeKeychainOverrideStore(data: nil, fingerprint: nil) try await ClaudeOAuthCredentialsStore.withClaudeKeychainFingerprintStoreOverrideForTesting( fingerprintStore) { try await ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.always) {Lines 1–500 of 1148. Load more
import Foundation import Testing @testable import CodexBar @testable import CodexBarCore struct ClaudeResilienceTests { @Test func `suppresses single flake when prior data exists`() { var gate = ConsecutiveFailureGate() let firstFailure = gate.shouldSurfaceError(onFailureWithPriorData: true) let secondFailure = gate.shouldSurfaceError(onFailureWithPriorData: true) #expect(firstFailure == false) #expect(secondFailure == true) } @Test func `surfaces failure without prior data`() { var gate = ConsecutiveFailureGate() let shouldSurface = gate.shouldSurfaceError(onFailureWithPriorData: false) #expect(shouldSurface) } @Test func `resets after success`() { var gate = ConsecutiveFailureGate() _ = gate.shouldSurfaceError(onFailureWithPriorData: true) gate.recordSuccess() let shouldSurface = gate.shouldSurfaceError(onFailureWithPriorData: true) #expect(shouldSurface == false) } @Test func `timeout keeps prior Claude snapshot without surfacing repeated failure`() async throws { try await ClaudeOAuthCredentialsStore.withIsolatedCredentialsFileTrackingForTesting { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) let fileURL = tempDir.appendingPathComponent("missing-credentials.json") try await ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) { let (store, prior) = try await MainActor.run { let settings = Self.makeSettingsStore(suite: "ClaudeResilienceTests-timeout-cache") settings.refreshFrequency = .manual settings.statusChecksEnabled = false settings.claudeUsageDataSource = .cli let metadata = ProviderRegistry.shared.metadata for provider in UsageProvider.allCases { try settings.setProviderEnabled( provider: provider, metadata: #require(metadata[provider]), enabled: provider == .claude) } let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings, startupBehavior: .testing, environmentBase: [:]) let prior = UsageSnapshot( primary: RateWindow(usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil), secondary: RateWindow( usedPercent: 34, windowMinutes: 10080, resetsAt: nil, resetDescription: nil), updatedAt: Date(timeIntervalSince1970: 1_800_000_000), identity: ProviderIdentitySnapshot( providerID: .claude, accountEmail: "claude@example.com", accountOrganization: nil, loginMethod: "Pro")) store._setSnapshotForTesting(prior, provider: .claude) let baseSpec = try #require(store.providerSpecs[.claude]) let descriptor = ProviderDescriptor( id: .claude, metadata: baseSpec.descriptor.metadata, branding: baseSpec.descriptor.branding, tokenCost: baseSpec.descriptor.tokenCost, fetchPlan: ProviderFetchPlan( sourceModes: [.cli], pipeline: ProviderFetchPipeline { _ in [TimeoutFetchStrategy()] }), cli: baseSpec.descriptor.cli) store.providerSpecs[.claude] = ProviderSpec( style: baseSpec.style, isEnabled: baseSpec.isEnabled, descriptor: descriptor, makeFetchContext: baseSpec.makeFetchContext) return (store, prior) } await store.refreshProvider(.claude) let firstResult = await MainActor.run { ( updatedAt: store.snapshot(for: .claude)?.updatedAt, hasError: store.error(for: .claude) != nil) } #expect(firstResult.updatedAt == prior.updatedAt) #expect(!firstResult.hasError) await store.refreshProvider(.claude) let secondResult = await MainActor.run { ( updatedAt: store.snapshot(for: .claude)?.updatedAt, hasError: store.error(for: .claude) != nil) } #expect(secondResult.updatedAt == prior.updatedAt) #expect(!secondResult.hasError) } } } @Test func `repeated non probe transient failure still surfaces`() async throws { try await ClaudeOAuthCredentialsStore.withIsolatedCredentialsFileTrackingForTesting { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) let fileURL = tempDir.appendingPathComponent("missing-credentials.json") try await ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) { let (store, prior) = try await MainActor.run { let settings = Self.makeSettingsStore(suite: "ClaudeResilienceTests-network-cache") settings.refreshFrequency = .manual settings.statusChecksEnabled = false settings.claudeUsageDataSource = .cli let metadata = ProviderRegistry.shared.metadata for provider in UsageProvider.allCases { try settings.setProviderEnabled( provider: provider, metadata: #require(metadata[provider]), enabled: provider == .claude) } let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings, startupBehavior: .testing, environmentBase: [:]) let prior = UsageSnapshot( primary: RateWindow(usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil), secondary: nil, updatedAt: Date(timeIntervalSince1970: 1_800_000_000), identity: ProviderIdentitySnapshot( providerID: .claude, accountEmail: "claude@example.com", accountOrganization: nil, loginMethod: "Pro")) store._setSnapshotForTesting(prior, provider: .claude) let baseSpec = try #require(store.providerSpecs[.claude]) let descriptor = ProviderDescriptor( id: .claude, metadata: baseSpec.descriptor.metadata, branding: baseSpec.descriptor.branding, tokenCost: baseSpec.descriptor.tokenCost, fetchPlan: ProviderFetchPlan( sourceModes: [.cli], pipeline: ProviderFetchPipeline { _ in [NetworkLostFetchStrategy()] }), cli: baseSpec.descriptor.cli) store.providerSpecs[.claude] = ProviderSpec( style: baseSpec.style, isEnabled: baseSpec.isEnabled, descriptor: descriptor, makeFetchContext: baseSpec.makeFetchContext) return (store, prior) } await store.refreshProvider(.claude) let firstResult = await MainActor.run { ( updatedAt: store.snapshot(for: .claude)?.updatedAt, hasError: store.error(for: .claude) != nil) } #expect(firstResult.updatedAt == prior.updatedAt) #expect(!firstResult.hasError) await store.refreshProvider(.claude) let secondResult = await MainActor.run { ( updatedAt: store.snapshot(for: .claude)?.updatedAt, hasError: store.error(for: .claude) != nil) } #expect(secondResult.updatedAt == prior.updatedAt) #expect(secondResult.hasError) } } } @Test func `credentials change clears prior Claude snapshot for non transient failure`() async throws { try await KeychainCacheStore.withServiceOverrideForTesting("com.steipete.codexbar.cache.tests.\(UUID())") { KeychainCacheStore.setTestStoreForTesting(true) defer { KeychainCacheStore.setTestStoreForTesting(false) } try await ClaudeOAuthCredentialsStore.withIsolatedCredentialsFileTrackingForTesting { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) let fileURL = tempDir.appendingPathComponent("credentials.json") try Data("{}".utf8).write(to: fileURL) try await ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) { #expect(ClaudeOAuthCredentialsStore.invalidateCacheIfCredentialsFileChanged()) let store = try await MainActor.run { let settings = Self.makeSettingsStore(suite: "ClaudeResilienceTests-auth-change") settings.refreshFrequency = .manual settings.statusChecksEnabled = false settings.claudeUsageDataSource = .cli let metadata = ProviderRegistry.shared.metadata for provider in UsageProvider.allCases { try settings.setProviderEnabled( provider: provider, metadata: #require(metadata[provider]), enabled: provider == .claude) } let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings, startupBehavior: .testing, environmentBase: [:]) let prior = UsageSnapshot( primary: RateWindow( usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil), secondary: nil, updatedAt: Date(timeIntervalSince1970: 1_800_000_000), identity: ProviderIdentitySnapshot( providerID: .claude, accountEmail: "old@example.com", accountOrganization: nil, loginMethod: "Pro")) store._setSnapshotForTesting(prior, provider: .claude) let baseSpec = try #require(store.providerSpecs[.claude]) let descriptor = ProviderDescriptor( id: .claude, metadata: baseSpec.descriptor.metadata, branding: baseSpec.descriptor.branding, tokenCost: baseSpec.descriptor.tokenCost, fetchPlan: ProviderFetchPlan( sourceModes: [.cli], pipeline: ProviderFetchPipeline { _ in [AuthFailureFetchStrategy(credentialsFileURL: fileURL)] }), cli: baseSpec.descriptor.cli) store.providerSpecs[.claude] = ProviderSpec( style: baseSpec.style, isEnabled: baseSpec.isEnabled, descriptor: descriptor, makeFetchContext: baseSpec.makeFetchContext) return store } await store.refreshProvider(.claude) let result = await MainActor.run { ( hasSnapshot: store.snapshot(for: .claude) != nil, hasError: store.error(for: .claude) != nil) } #expect(!result.hasSnapshot) #expect(result.hasError) } } } } @Test func `credentials change clears prior Claude snapshot for transient failure`() async throws { try await KeychainCacheStore.withServiceOverrideForTesting("com.steipete.codexbar.cache.tests.\(UUID())") { KeychainCacheStore.setTestStoreForTesting(true) defer { KeychainCacheStore.setTestStoreForTesting(false) } try await ClaudeOAuthCredentialsStore.withIsolatedCredentialsFileTrackingForTesting { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) let fileURL = tempDir.appendingPathComponent("credentials.json") try Data("{}".utf8).write(to: fileURL) try await ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) { #expect(ClaudeOAuthCredentialsStore.invalidateCacheIfCredentialsFileChanged()) let store = try await MainActor.run { let settings = Self.makeSettingsStore(suite: "ClaudeResilienceTests-transient-auth-change") settings.refreshFrequency = .manual settings.statusChecksEnabled = false settings.claudeUsageDataSource = .cli let metadata = ProviderRegistry.shared.metadata for provider in UsageProvider.allCases { try settings.setProviderEnabled( provider: provider, metadata: #require(metadata[provider]), enabled: provider == .claude) } let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings, startupBehavior: .testing, environmentBase: [:]) let prior = UsageSnapshot( primary: RateWindow( usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil), secondary: nil, updatedAt: Date(timeIntervalSince1970: 1_800_000_000), identity: ProviderIdentitySnapshot( providerID: .claude, accountEmail: "old@example.com", accountOrganization: nil, loginMethod: "Pro")) store._setSnapshotForTesting(prior, provider: .claude) let baseSpec = try #require(store.providerSpecs[.claude]) let descriptor = ProviderDescriptor( id: .claude, metadata: baseSpec.descriptor.metadata, branding: baseSpec.descriptor.branding, tokenCost: baseSpec.descriptor.tokenCost, fetchPlan: ProviderFetchPlan( sourceModes: [.cli], pipeline: ProviderFetchPipeline { _ in [TransientFailureAfterCredentialSwapFetchStrategy(credentialsFileURL: fileURL)] }), cli: baseSpec.descriptor.cli) store.providerSpecs[.claude] = ProviderSpec( style: baseSpec.style, isEnabled: baseSpec.isEnabled, descriptor: descriptor, makeFetchContext: baseSpec.makeFetchContext) return store } await store.refreshProvider(.claude) let result = await MainActor.run { ( hasSnapshot: store.snapshot(for: .claude) != nil, hasError: store.error(for: .claude) != nil) } #expect(!result.hasSnapshot) #expect(result.hasError) } } } } @Test func `keychain change clears prior Claude snapshot for transient failure`() async throws { try await KeychainCacheStore.withServiceOverrideForTesting("com.steipete.codexbar.cache.tests.\(UUID())") { KeychainCacheStore.setTestStoreForTesting(true) defer { KeychainCacheStore.setTestStoreForTesting(false) } let storedFingerprint = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprint( modifiedAt: 1, createdAt: 1, persistentRefHash: "old") let currentFingerprint = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprint( modifiedAt: 2, createdAt: 1, persistentRefHash: "new") let fingerprintStore = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprintStore( fingerprint: storedFingerprint) try await ClaudeOAuthCredentialsStore.withClaudeKeychainFingerprintStoreOverrideForTesting( fingerprintStore) { try await ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.always) { try await ClaudeOAuthCredentialsStore.withKeychainAccessOverrideForTesting(false) { try await KeychainAccessGate.withTaskOverrideForTesting(false) { try await ClaudeOAuthKeychainAccessGate.withShouldAllowPromptOverrideForTesting(true) { try await ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting( data: nil, fingerprint: currentFingerprint) { try await ClaudeOAuthCredentialsStore .withIsolatedCredentialsFileTrackingForTesting { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory( at: tempDir, withIntermediateDirectories: true) let fileURL = tempDir.appendingPathComponent("missing-credentials.json") try await ClaudeOAuthCredentialsStore .withCredentialsURLOverrideForTesting(fileURL) { let store = try await MainActor.run { let settings = Self.makeSettingsStore( suite: "ClaudeResilienceTests-keychain-auth-change") settings.refreshFrequency = .manual settings.statusChecksEnabled = false settings.claudeUsageDataSource = .cli let metadata = ProviderRegistry.shared.metadata for provider in UsageProvider.allCases { try settings.setProviderEnabled( provider: provider, metadata: #require(metadata[provider]), enabled: provider == .claude) } let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings, startupBehavior: .testing, environmentBase: [:]) let prior = UsageSnapshot( primary: RateWindow( usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil), secondary: nil, updatedAt: Date(timeIntervalSince1970: 1_800_000_000), identity: ProviderIdentitySnapshot( providerID: .claude, accountEmail: "old@example.com", accountOrganization: nil, loginMethod: "Pro")) store._setSnapshotForTesting(prior, provider: .claude) let baseSpec = try #require(store.providerSpecs[.claude]) let descriptor = ProviderDescriptor( id: .claude, metadata: baseSpec.descriptor.metadata, branding: baseSpec.descriptor.branding, tokenCost: baseSpec.descriptor.tokenCost, fetchPlan: ProviderFetchPlan( sourceModes: [.cli], pipeline: ProviderFetchPipeline { _ in [TimeoutFetchStrategy()] }), cli: baseSpec.descriptor.cli) store.providerSpecs[.claude] = ProviderSpec( style: baseSpec.style, isEnabled: baseSpec.isEnabled, descriptor: descriptor, makeFetchContext: baseSpec.makeFetchContext) return store } await store.refreshProvider(.claude) let result = await MainActor.run { ( hasSnapshot: store.snapshot(for: .claude) != nil, hasError: store.error(for: .claude) != nil) } #expect(!result.hasSnapshot) #expect(result.hasError) } } } } } } } } } } @Test func `keychain removal clears prior Claude snapshot for transient failure`() async throws { try await KeychainCacheStore.withServiceOverrideForTesting("com.steipete.codexbar.cache.tests.\(UUID())") { KeychainCacheStore.setTestStoreForTesting(true) defer { KeychainCacheStore.setTestStoreForTesting(false) } let storedFingerprint = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprint( modifiedAt: 1, createdAt: 1, persistentRefHash: "old") let fingerprintStore = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprintStore( fingerprint: storedFingerprint) let keychainStore = ClaudeOAuthCredentialsStore.ClaudeKeychainOverrideStore(data: nil, fingerprint: nil) try await ClaudeOAuthCredentialsStore.withClaudeKeychainFingerprintStoreOverrideForTesting( fingerprintStore) { try await ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.always) {