use wiremock::matchers::method;
use wiremock::{Mock, MockServer, ResponseTemplate};
use yoagent::agent::Agent;
use yoagent::provider::{ModelConfig, OpenAiResponsesProvider};
use yoagent::retry::RetryConfig;
fn quick_retry() -> RetryConfig {
RetryConfig {
max_retries: 3,
initial_delay_ms: 1,
backoff_multiplier: 1.0,
max_delay_ms: 5,
}
}
async fn run_and_count(body: &'static str) -> usize {
let server = MockServer::start().await;
Mock::given(method("POST"))
.respond_with(ResponseTemplate::new(200).set_body_raw(body, "text/event-stream"))
.mount(&server)
.await;
let mut mc = ModelConfig::openai("gpt-5.5", "GPT-5.5");
mc.base_url = server.uri();
let mut agent = Agent::from_provider(OpenAiResponsesProvider, mc)
.with_api_key("test-key")
.with_retry_config(quick_retry());
let mut rx = agent.prompt("hi").await;
while rx.recv().await.is_some() {}
agent.finish().await;
server.received_requests().await.unwrap().len()
}
#[tokio::test]
async fn response_incomplete_is_terminal_not_retried() {
let body = "event: response.output_text.delta\n\
data: {\"type\":\"response.output_text.delta\",\"delta\":\"hello\"}\n\n\
event: response.incomplete\n\
data: {\"type\":\"response.incomplete\",\"response\":{\"usage\":{\"input_tokens\":5,\"output_tokens\":1,\"total_tokens\":6}}}\n\n";
assert_eq!(
run_and_count(body).await,
1,
"response.incomplete must end the turn; retrying re-bills a generation \
the server already completed"
);
}
#[tokio::test]
async fn response_failed_is_terminal_not_retried() {
let body = "event: response.failed\n\
data: {\"type\":\"response.failed\",\"response\":{\"error\":{\"message\":\"content policy\"}}}\n\n";
assert_eq!(
run_and_count(body).await,
1,
"response.failed must surface as an error, not be retried"
);
}