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

119 lines
4.5 KiB
Swift

import Foundation
import Testing
@testable import CodexBarCore
struct ClaudeSourcePlannerTests {
@Test
func `app auto plan preserves ordered steps and reasons`() {
let plan = ClaudeSourcePlanner.resolve(input: ClaudeSourcePlanningInput(
runtime: .app,
selectedDataSource: .auto,
webExtrasEnabled: false,
hasWebSession: true,
hasCLI: true,
hasOAuthCredentials: true))
#expect(plan.orderedSteps.map(\.dataSource) == [.oauth, .cli, .web])
#expect(plan.orderedSteps.map(\.inclusionReason) == [
.appAutoPreferredOAuth,
.appAutoFallbackCLI,
.appAutoFallbackWeb,
])
#expect(plan.availableSteps.map(\.dataSource) == [.oauth, .cli, .web])
#expect(plan.preferredStep?.dataSource == .oauth)
}
@Test
func `CLI auto plan preserves ordered steps and reasons`() {
let plan = ClaudeSourcePlanner.resolve(input: ClaudeSourcePlanningInput(
runtime: .cli,
selectedDataSource: .auto,
webExtrasEnabled: false,
hasWebSession: true,
hasCLI: true,
hasOAuthCredentials: false))
#expect(plan.orderedSteps.map(\.dataSource) == [.web, .cli])
#expect(plan.orderedSteps.map(\.inclusionReason) == [
.cliAutoPreferredWeb,
.cliAutoFallbackCLI,
])
#expect(plan.preferredStep?.dataSource == .web)
}
@Test
func `explicit mode plan is single step`() {
let plan = ClaudeSourcePlanner.resolve(input: ClaudeSourcePlanningInput(
runtime: .app,
selectedDataSource: .cli,
webExtrasEnabled: true,
hasWebSession: false,
hasCLI: true,
hasOAuthCredentials: false))
#expect(plan.orderedSteps.count == 1)
#expect(plan.orderedSteps.first?.dataSource == .cli)
#expect(plan.orderedSteps.first?.inclusionReason == .explicitSourceSelection)
#expect(plan.compatibilityStrategy == ClaudeUsageStrategy(dataSource: .cli, useWebExtras: true))
}
@Test
func `app auto CLI fallback reports web extras like runtime`() {
let plan = ClaudeSourcePlanner.resolve(input: ClaudeSourcePlanningInput(
runtime: .app,
selectedDataSource: .auto,
webExtrasEnabled: true,
hasWebSession: false,
hasCLI: true,
hasOAuthCredentials: false))
#expect(plan.preferredStep?.dataSource == .cli)
#expect(plan.compatibilityStrategy == ClaudeUsageStrategy(dataSource: .cli, useWebExtras: true))
}
@Test
func `no source planner output is deterministic`() {
let input = ClaudeSourcePlanningInput(
runtime: .app,
selectedDataSource: .auto,
webExtrasEnabled: false,
hasWebSession: false,
hasCLI: false,
hasOAuthCredentials: false)
let plan = ClaudeSourcePlanner.resolve(input: input)
#expect(plan.orderedSteps.map(\.dataSource) == [.oauth, .cli, .web])
#expect(plan.availableSteps.isEmpty)
#expect(plan.isNoSourceAvailable)
#expect(plan.preferredStep == nil)
#expect(plan.executionSteps.isEmpty)
#expect(plan.debugLines() == [
"planner_order=oauth→cli→web",
"planner_selected=none",
"planner_no_source=true",
"planner_step.oauth=unavailable reason=app-auto-preferred-oauth",
"planner_step.cli=unavailable reason=app-auto-fallback-cli",
"planner_step.web=unavailable reason=app-auto-fallback-web",
])
}
@Test
func `CLI resolver falls back to PATH when Claude CLI path override is invalid`() throws {
let tempDir = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString, isDirectory: true)
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
let binaryURL = tempDir.appendingPathComponent("claude")
try Data("#!/bin/sh\nexit 0\n".utf8).write(to: binaryURL)
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: binaryURL.path)
let resolved = ClaudeCLIResolver.resolvedBinaryPath(
environment: [
"CLAUDE_CLI_PATH": "/definitely/missing/claude",
"PATH": tempDir.path,
],
loginPATH: nil)
#expect(resolved == binaryURL.path)
}
}