This commit is contained in:
Ahmed Ibrahim
2025-10-23 07:52:05 -07:00
parent d830ec9678
commit bb26652be1
6 changed files with 172 additions and 145 deletions

View File

@@ -35,6 +35,22 @@ impl ResponseMock {
pub fn requests(&self) -> Vec<ResponsesRequest> {
self.requests.lock().unwrap().clone()
}
/// Returns true if any captured request contains a `function_call` with the
/// provided `call_id`.
pub fn saw_function_call(&self, call_id: &str) -> bool {
self.requests()
.iter()
.any(|req| req.has_function_call(call_id))
}
/// Returns the `output` string for a matching `function_call_output` with
/// the provided `call_id`, searching across all captured requests.
pub fn function_call_output_text(&self, call_id: &str) -> Option<String> {
self.requests()
.iter()
.find_map(|req| req.function_call_output_text(call_id))
}
}
#[derive(Debug, Clone)]
@@ -70,6 +86,27 @@ impl ResponsesRequest {
.unwrap_or_else(|| panic!("function call output {call_id} item not found in request"))
}
/// Returns true if this request's `input` contains a `function_call` with
/// the specified `call_id`.
pub fn has_function_call(&self, call_id: &str) -> bool {
self.input().iter().any(|item| {
item.get("type").and_then(Value::as_str) == Some("function_call")
&& item.get("call_id").and_then(Value::as_str) == Some(call_id)
})
}
/// If present, returns the `output` string of the `function_call_output`
/// entry matching `call_id` in this request's `input`.
pub fn function_call_output_text(&self, call_id: &str) -> Option<String> {
let item = self.input().iter().find(|item| {
item.get("type").and_then(Value::as_str) == Some("function_call_output")
&& item.get("call_id").and_then(Value::as_str) == Some(call_id)
})?;
item.get("output")
.and_then(Value::as_str)
.map(str::to_string)
}
pub fn header(&self, name: &str) -> Option<String> {
self.0
.headers

View File

@@ -105,7 +105,7 @@ async fn interrupt_tool_records_history_entries() {
let fixture = test_codex().build(&server).await.unwrap();
let codex = Arc::clone(&fixture.codex);
let wait_timeout = Duration::from_secs(5);
let wait_timeout = Duration::from_secs(0.1);
codex
.submit(Op::UserInput {
@@ -150,42 +150,18 @@ async fn interrupt_tool_records_history_entries() {
let requests = response_mock.requests();
assert!(
requests.len() >= 2,
"expected at least two calls to the responses API"
requests.len() == 2,
"expected two calls to the responses API, got {}",
requests.len()
);
let mut call_seen = false;
let mut abort_seen = false;
for request in requests {
let input = request.input();
for window in input.windows(2) {
let current = &window[0];
let next = &window[1];
if current.get("type").and_then(|v| v.as_str()) == Some("function_call")
&& current.get("call_id").and_then(|v| v.as_str()) == Some(call_id)
{
call_seen = true;
if next.get("type").and_then(|v| v.as_str()) == Some("function_call_output")
&& next.get("call_id").and_then(|v| v.as_str()) == Some(call_id)
{
let content_matches =
next.get("output").and_then(serde_json::Value::as_str) == Some("aborted");
if content_matches {
abort_seen = true;
break;
}
}
}
}
if call_seen && abort_seen {
break;
}
}
assert!(call_seen, "function call not recorded in responses payload");
assert!(
abort_seen,
response_mock.saw_function_call(call_id),
"function call not recorded in responses payload"
);
assert_eq!(
response_mock.function_call_output_text(call_id).as_deref(),
Some("aborted"),
"aborted function call output not recorded in responses payload"
);
}