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

120 lines
3.8 KiB
Swift

import Testing
@testable import CodexBarCore
struct ProviderCandidateRetryRunnerTests {
private enum TestError: Error, Equatable {
case retryable(Int)
case nonRetryable(Int)
}
@Test
func `retries then succeeds`() async throws {
let candidates = [1, 2, 3]
var attempted: [Int] = []
var retried: [Int] = []
let output = try await ProviderCandidateRetryRunner.run(
candidates,
shouldRetry: { error in
if case TestError.retryable = error {
return true
}
return false
},
onRetry: { candidate, _ in
retried.append(candidate)
},
attempt: { candidate in
attempted.append(candidate)
guard candidate == 3 else {
throw TestError.retryable(candidate)
}
return candidate * 10
})
#expect(output == 30)
#expect(attempted == [1, 2, 3])
#expect(retried == [1, 2])
}
@Test
func `non retryable fails immediately`() async {
let candidates = [1, 2, 3]
var attempted: [Int] = []
var retried: [Int] = []
do {
_ = try await ProviderCandidateRetryRunner.run(
candidates,
shouldRetry: { error in
if case TestError.retryable = error {
return true
}
return false
},
onRetry: { candidate, _ in
retried.append(candidate)
},
attempt: { candidate in
attempted.append(candidate)
throw TestError.nonRetryable(candidate)
})
Issue.record("Expected TestError.nonRetryable")
} catch let error as TestError {
#expect(error == .nonRetryable(1))
#expect(attempted == [1])
#expect(retried.isEmpty)
} catch {
Issue.record("Expected TestError.nonRetryable(1), got \(error)")
}
}
@Test
func `exhausted retryable throws last error`() async {
let candidates = [1, 2]
var attempted: [Int] = []
var retried: [Int] = []
do {
_ = try await ProviderCandidateRetryRunner.run(
candidates,
shouldRetry: { error in
if case TestError.retryable = error {
return true
}
return false
},
onRetry: { candidate, _ in
retried.append(candidate)
},
attempt: { candidate in
attempted.append(candidate)
throw TestError.retryable(candidate)
})
Issue.record("Expected TestError.retryable")
} catch let error as TestError {
#expect(error == .retryable(2))
#expect(attempted == [1, 2])
#expect(retried == [1])
} catch {
Issue.record("Expected TestError.retryable(2), got \(error)")
}
}
@Test
func `empty candidates throws no candidates`() async {
do {
let candidates: [Int] = []
_ = try await ProviderCandidateRetryRunner.run(
candidates,
shouldRetry: { _ in true },
attempt: { _ in 1 })
Issue.record("Expected ProviderCandidateRetryRunnerError.noCandidates")
} catch ProviderCandidateRetryRunnerError.noCandidates {
// expected
} catch {
Issue.record("Expected ProviderCandidateRetryRunnerError.noCandidates, got \(error)")
}
}
}