120 lines
3.8 KiB
Swift
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)")
|
|
}
|
|
}
|
|
}
|