修复智能体运行时测试夹具竞态
让回执测试显式持有并等待父子执行通道释放 隔离异步后台任务与临时项目销毁之间的锁竞争 固定非阻塞 HTTP 夹具读取语义并保留有界超时
This commit is contained in:
@@ -5707,6 +5707,9 @@ mod canvas_generation_tests {
|
||||
};
|
||||
|
||||
fn read_test_http_request(stream: &mut std::net::TcpStream) -> String {
|
||||
stream
|
||||
.set_nonblocking(false)
|
||||
.expect("set request stream blocking");
|
||||
stream
|
||||
.set_read_timeout(Some(Duration::from_secs(10)))
|
||||
.expect("set request read timeout");
|
||||
|
||||
@@ -254,11 +254,17 @@ fn persist_game_chat_main_asset_audit_and_route(
|
||||
.expect("persist game-chat main asset route");
|
||||
}
|
||||
|
||||
fn game_chat_main_art_child_fixture(
|
||||
fn game_chat_main_art_child_fixture_with_lane(
|
||||
root: &Path,
|
||||
target_agent_id: &str,
|
||||
missing_slots: &[&str],
|
||||
) -> (AgentRuntimeState, AgentRuntimeState, String) {
|
||||
hold_child_lane: bool,
|
||||
) -> (
|
||||
AgentRuntimeState,
|
||||
AgentRuntimeState,
|
||||
String,
|
||||
Option<AgentRuntimeTaskLock>,
|
||||
) {
|
||||
init_local_game_project_at(root, "game-chat-art-child", "单主美术委派")
|
||||
.expect("init game-chat art child project");
|
||||
let root_run_id = "game-chat-main-art-root";
|
||||
@@ -310,6 +316,11 @@ fn game_chat_main_art_child_fixture(
|
||||
)
|
||||
.expect("persist game-chat main route");
|
||||
|
||||
let child_lane = hold_child_lane.then(|| {
|
||||
try_acquire_game_creator_agent_runtime_task_lock(root, target_agent_id)
|
||||
.expect("acquire delegated art child lane")
|
||||
.expect("delegated art child lane available")
|
||||
});
|
||||
let action_id = format!("game-chat-main-delegate-{target_agent_id}");
|
||||
let observation = observe_agent_runtime_agent_delegate(
|
||||
root,
|
||||
@@ -335,11 +346,27 @@ fn game_chat_main_art_child_fixture(
|
||||
)
|
||||
.expect("read delegated art child")
|
||||
.expect("delegated art child exists");
|
||||
(
|
||||
main,
|
||||
agent_runtime_state_from_task_record(&child),
|
||||
delegation_id,
|
||||
)
|
||||
let mut child = agent_runtime_state_from_task_record(&child);
|
||||
if child_lane.is_some() {
|
||||
child.status = "running".to_string();
|
||||
child.phase = "planning".to_string();
|
||||
child.current_action = "执行受限美术子任务".to_string();
|
||||
append_game_creator_agent_runtime_task(root, &child)
|
||||
.expect("persist running delegated art child");
|
||||
write_game_creator_agent_runtime_state(root, &child)
|
||||
.expect("persist running delegated art runtime");
|
||||
}
|
||||
(main, child, delegation_id, child_lane)
|
||||
}
|
||||
|
||||
fn game_chat_main_art_child_fixture(
|
||||
root: &Path,
|
||||
target_agent_id: &str,
|
||||
missing_slots: &[&str],
|
||||
) -> (AgentRuntimeState, AgentRuntimeState, String) {
|
||||
let (main, child, delegation_id, _child_lane) =
|
||||
game_chat_main_art_child_fixture_with_lane(root, target_agent_id, missing_slots, false);
|
||||
(main, child, delegation_id)
|
||||
}
|
||||
|
||||
fn game_chat_main_without_asset_audit_fixture(root: &Path) -> String {
|
||||
@@ -360,8 +387,12 @@ fn game_chat_main_without_asset_audit_fixture(root: &Path) -> String {
|
||||
async fn game_chat_main_agent_delegates_only_real_missing_art_and_limits_children_to_assets() {
|
||||
let temporary = tempfile::tempdir().expect("create game-chat art child root");
|
||||
let root = temporary.path().join("project");
|
||||
let (_main, child, _delegation_id) =
|
||||
game_chat_main_art_child_fixture(&root, "art-asset-plan", &["core-spritesheet"]);
|
||||
let (_main, child, _delegation_id, _child_lane) = game_chat_main_art_child_fixture_with_lane(
|
||||
&root,
|
||||
"art-asset-plan",
|
||||
&["core-spritesheet"],
|
||||
true,
|
||||
);
|
||||
assert_eq!(child.agent_id, "art-asset-plan");
|
||||
assert_eq!(child.source, "agent-delegate");
|
||||
assert_eq!(child.parent_agent_id.as_deref(), Some("code-prototype"));
|
||||
@@ -684,8 +715,14 @@ fn game_chat_main_agent_rejects_unneeded_or_wrong_art_delegation() {
|
||||
fn game_chat_art_receipt_wakes_the_same_code_prototype_run() {
|
||||
let temporary = tempfile::tempdir().expect("create game-chat receipt wake root");
|
||||
let root = temporary.path().join("project");
|
||||
let (mut main, mut child, delegation_id) =
|
||||
game_chat_main_art_child_fixture(&root, "art-asset-plan", &["core-spritesheet"]);
|
||||
let (mut main, mut child, delegation_id, child_lane) =
|
||||
game_chat_main_art_child_fixture_with_lane(
|
||||
&root,
|
||||
"art-asset-plan",
|
||||
&["core-spritesheet"],
|
||||
true,
|
||||
);
|
||||
let _child_lane = child_lane.expect("delegated art child lane is held");
|
||||
main.status = "running".to_string();
|
||||
main.phase = "waiting-for-delegate-receipts".to_string();
|
||||
main.current_action = "等待临时美术 Agent 回执".to_string();
|
||||
@@ -705,6 +742,9 @@ fn game_chat_art_receipt_wakes_the_same_code_prototype_run() {
|
||||
)
|
||||
.expect("read completed art child")
|
||||
.expect("completed art child exists");
|
||||
let parent_lane = try_acquire_game_creator_agent_runtime_task_lock(&root, "code-prototype")
|
||||
.expect("acquire waiting main-agent execution lane")
|
||||
.expect("waiting main-agent execution lane is free");
|
||||
publish_game_creator_agent_delegate_result(
|
||||
&root,
|
||||
&child_task,
|
||||
@@ -759,6 +799,7 @@ fn game_chat_art_receipt_wakes_the_same_code_prototype_run() {
|
||||
assert_eq!(duplicate.status, "failed", "{duplicate:?}");
|
||||
assert!(duplicate.summary.contains("最多委派一次"));
|
||||
|
||||
drop(parent_lane);
|
||||
let waiting = read_latest_game_creator_agent_runtime_task_by_run_id(
|
||||
&root,
|
||||
"code-prototype",
|
||||
@@ -768,6 +809,19 @@ fn game_chat_art_receipt_wakes_the_same_code_prototype_run() {
|
||||
.expect("waiting main task exists");
|
||||
let wake_started = wake_waiting_static_delegate_parent_run_at(&root, &waiting)
|
||||
.expect("wake same main run after art receipt");
|
||||
let release_deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
|
||||
loop {
|
||||
if game_creator_agent_runtime_task_lock_is_available(&root, "code-prototype")
|
||||
.expect("probe resumed main-agent lane release")
|
||||
{
|
||||
break;
|
||||
}
|
||||
assert!(
|
||||
std::time::Instant::now() < release_deadline,
|
||||
"resumed main-agent lane did not release before fixture teardown"
|
||||
);
|
||||
std::thread::sleep(std::time::Duration::from_millis(20));
|
||||
}
|
||||
let resumed = read_all_game_creator_agent_runtime_tasks(&game_creator_agent_runtime_task_path(
|
||||
&root,
|
||||
"code-prototype",
|
||||
@@ -783,7 +837,10 @@ fn game_chat_art_receipt_wakes_the_same_code_prototype_run() {
|
||||
|
||||
#[test]
|
||||
fn autonomous_parent_waits_for_active_child_while_registered_derived_visuals_need_repair() {
|
||||
let root = std::env::temp_dir().join(format!(
|
||||
let temporary_root = std::env::temp_dir()
|
||||
.canonicalize()
|
||||
.expect("canonicalize system temporary directory");
|
||||
let root = temporary_root.join(format!(
|
||||
"genarrative-agent-main-loop-legacy-{}-{}",
|
||||
std::process::id(),
|
||||
std::time::SystemTime::now()
|
||||
|
||||
+4
@@ -1084,6 +1084,10 @@ fn game_chat_pure_continue_inherits_failed_root_semantics_and_manifest_progress(
|
||||
"继续完成继承的现有游戏目标",
|
||||
)
|
||||
.expect("persist continued root Supervisor decision");
|
||||
let _code_prototype_lane =
|
||||
try_acquire_game_creator_agent_runtime_task_lock(&root, "code-prototype")
|
||||
.expect("acquire continued main-agent execution lane")
|
||||
.expect("continued main-agent execution lane is free");
|
||||
let scheduled = schedule_autonomous_game_build_ready_tasks_at(
|
||||
&root,
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
|
||||
Reference in New Issue
Block a user