import Foundation import Testing @testable import CodexBarCore #if os(macOS) /// Regression tests for #474: verify that CLI timeout errors trigger fallback /// to the web strategy instead of stalling the refresh cycle. struct AugmentCLIFetchStrategyFallbackTests { private struct StubClaudeFetcher: ClaudeUsageFetching { func loadLatestUsage(model _: String) async throws -> ClaudeUsageSnapshot { throw ClaudeUsageError.parseFailed("stub") } func debugRawProbe(model _: String) async -> String { "stub" } func detectVersion() -> String? { nil } } private func makeContext(sourceMode: ProviderSourceMode = .auto) -> ProviderFetchContext { let env: [String: String] = [:] return ProviderFetchContext( runtime: .app, sourceMode: sourceMode, includeCredits: false, webTimeout: 1, webDebugDumpHTML: false, verbose: false, env: env, settings: nil, fetcher: UsageFetcher(environment: env), claudeFetcher: StubClaudeFetcher(), browserDetection: BrowserDetection(cacheTTL: 0)) } // SubprocessRunnerError is not an AuggieCLIError, so it hits the default // fallback=true path — the desired behavior for infrastructure errors. @Test func `timeout error falls back to web`() { let strategy = AugmentCLIFetchStrategy() let context = self.makeContext() let error = SubprocessRunnerError.timedOut("auggie-account-status") #expect(strategy.shouldFallback(on: error, context: context) == true) } @Test func `binary not found falls back to web`() { let strategy = AugmentCLIFetchStrategy() let context = self.makeContext() let error = SubprocessRunnerError.binaryNotFound("/usr/local/bin/auggie") #expect(strategy.shouldFallback(on: error, context: context) == true) } @Test func `launch failed falls back to web`() { let strategy = AugmentCLIFetchStrategy() let context = self.makeContext() let error = SubprocessRunnerError.launchFailed("permission denied") #expect(strategy.shouldFallback(on: error, context: context) == true) } @Test func `not authenticated falls back to web`() { let strategy = AugmentCLIFetchStrategy() let context = self.makeContext() #expect(strategy.shouldFallback(on: AuggieCLIError.notAuthenticated, context: context) == true) } @Test func `no output falls back to web`() { let strategy = AugmentCLIFetchStrategy() let context = self.makeContext() #expect(strategy.shouldFallback(on: AuggieCLIError.noOutput, context: context) == true) } @Test func `parse error falls back to web`() { let strategy = AugmentCLIFetchStrategy() let context = self.makeContext() #expect(strategy.shouldFallback(on: AuggieCLIError.parseError("bad data"), context: context) == true) } @Test func `non zero exit falls back to web`() { let strategy = AugmentCLIFetchStrategy() let context = self.makeContext() let error = SubprocessRunnerError.nonZeroExit(code: 1, stderr: "crash") #expect(strategy.shouldFallback(on: error, context: context) == true) } } #endif