mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
code-mode: extend test coverage to lock in cell lifecycle (#28468)
This PR establishes the intended behavior as an executable contract before a refactor of the cell runtime begins. It also fixes cases where a second observer or termination request could replace an existing response channel and leave the original caller unresolved. ### Behavior codified - A cell can yield output and subsequently resume to completion. - A caller can run a cell until it has no immediately runnable work, receive its accumulated output and outstanding tool-call IDs, and then resume the same cell when the awaited work is available. - Each cell admits one active observer: - a second observer receives an explicit busy error - the existing observer remains registered and is not displaced - A natural result (conclusion of the js module) that has already reached the cell controller wins over a later termination request. - Otherwise, termination preempts execution and resolves both: - the active observer, if present - the caller requesting termination - Repeated termination requests are rejected while termination is already in progress. - Terminal responses are sent only after outstanding callback work has been handled: - natural completion drains notifications and cancels outstanding tool calls - termination cancels and drains both notification and tool callbacks. - Cell removal and cell_closed notification happen after callback cleanup
This commit is contained in:
@@ -1411,7 +1411,7 @@ text("phase 3");
|
||||
|
||||
#[cfg_attr(windows, ignore = "no exec_command on Windows")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn code_mode_yield_timeout_works_for_busy_loop() -> Result<()> {
|
||||
async fn code_mode_yield_and_termination_are_not_starved_by_runtime_output() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = responses::start_mock_server().await;
|
||||
@@ -1420,8 +1420,12 @@ async fn code_mode_yield_timeout_works_for_busy_loop() -> Result<()> {
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
let code = r#"// @exec: {"yield_time_ms": 100}
|
||||
text("phase 1");
|
||||
// Exact controller arbitration is covered by deterministic code-mode contract tests. Keep
|
||||
// this end-to-end load bounded while exercising a substantial runtime output backlog.
|
||||
let code = r#"// @exec: {"yield_time_ms": 0, "max_output_tokens": 16}
|
||||
for (let index = 0; index < 16_384; index++) {
|
||||
text(`event ${index}`);
|
||||
}
|
||||
while (true) {}
|
||||
"#;
|
||||
|
||||
@@ -1451,7 +1455,7 @@ while (true) {}
|
||||
|
||||
let first_request = first_completion.single_request();
|
||||
let first_items = custom_tool_output_items(&first_request, "call-1");
|
||||
assert_eq!(first_items.len(), 2);
|
||||
assert_eq!(first_items.len(), 1);
|
||||
assert_regex_match(
|
||||
concat!(
|
||||
r"(?s)\A",
|
||||
@@ -1459,7 +1463,6 @@ while (true) {}
|
||||
),
|
||||
text_item(&first_items, /*index*/ 0),
|
||||
);
|
||||
assert_eq!(text_item(&first_items, /*index*/ 1), "phase 1");
|
||||
let cell_id = extract_running_cell_id(text_item(&first_items, /*index*/ 0));
|
||||
|
||||
responses::mount_sse_once(
|
||||
@@ -1491,7 +1494,7 @@ while (true) {}
|
||||
|
||||
let second_request = second_completion.single_request();
|
||||
let second_items = function_tool_output_items(&second_request, "call-2");
|
||||
assert_eq!(second_items.len(), 1);
|
||||
assert!(!second_items.is_empty());
|
||||
assert_regex_match(
|
||||
concat!(
|
||||
r"(?s)\A",
|
||||
|
||||
Reference in New Issue
Block a user