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)") } } }