测试纯静态美术产物验证合同

新增无 package.json 的 art-asset-plan 回归场景

约束自主验证仅走静态 smoke 与浏览器验收链路

记录当前错误暴露 project.verify 的失败证据
This commit is contained in:
AIGameCreator App
2026-07-25 20:08:21 +08:00
parent 006846e667
commit 655aa0e2f6
@@ -2919,3 +2919,134 @@ async fn autonomous_game_build_verified_revision_forces_response_only_delivery()
fs::remove_dir_all(root).ok();
}
#[tokio::test]
async fn autonomous_static_art_asset_verification_does_not_require_node_project() {
let root = unique_project_path();
init_local_game_project_at(
&root,
"project-autonomous-static-art-verification",
"自主构建纯静态美术验证测试",
)
.expect("project init");
assert!(
!root.join("package.json").exists(),
"static HTML fixture must not gain a Node project manifest"
);
fs::write(
root.join("assets/manifest.art.json"),
br#"{"assets":[{"path":"assets/art-spritesheet.png","kind":"art-spritesheet"}]}"#,
)
.expect("write static art manifest");
register_canvas_visual_asset_fixture(&root, "assets/art-spritesheet.png", "art-spritesheet");
let child_run_id = "autonomous-static-art-verification-child";
let task = "为纯静态 HTML 游戏生成并登记 assets/manifest.art.json 与 assets/art-spritesheet.png;项目不使用 Node 或 npm。";
let response_arguments = serde_json::json!({
"response": "美术清单和精灵图已经生成,可以交付。"
})
.to_string();
let smoke_arguments = serde_json::json!({
"reason": "使用静态 smoke 验证当前纯静态项目 revision",
"input": {"commandId": "game.static_smoke"}
})
.to_string();
let (sender, receiver) = mpsc::channel();
let base_url = spawn_mock_llm_raw_responses_with_capture(
vec![
native_agent_tool_plan_chat_response(
"call-autonomous-static-art-response",
AGENT_RUNTIME_RESPOND_FUNCTION_NAME,
response_arguments,
),
native_agent_tool_plan_chat_response(
"call-autonomous-static-art-smoke",
&native_runtime_function_name("command.run_limited")
.expect("limited command function"),
smoke_arguments,
),
],
Some(sender),
);
let _config_guard = write_test_local_config(format!(
r#"{{
"agentLlm": {{
"art-asset-plan": {{
"apiKey": "autonomous-static-art-verification-key",
"baseUrl": {base_url:?},
"model": "autonomous-static-art-verification-model",
"apiKind": "openai_chat",
"maxRetries": 0
}}
}}
}}"#
));
let runtime = bind_autonomous_specialist_runtime_for_test(
&root,
"autonomous-static-art-verification-parent",
"art-asset-plan",
child_run_id,
task,
);
prepare_agent_runtime_project_mutation_locked(
&root,
"art-asset-plan",
child_run_id,
"canvas.asset_generate",
)
.expect("record generated art mutation");
let observations = vec![AgentRuntimeToolObservation {
tool: "canvas.asset_generate".to_string(),
status: "ok".to_string(),
summary: "已生成 assets/manifest.art.json 与 assets/art-spritesheet.png".to_string(),
detail: None,
}];
let plan = request_game_creator_agent_background_tool_plan_for_test(
&root,
"art-asset-plan",
&runtime.session_id,
child_run_id,
task,
&observations,
2,
0,
)
.await
.expect("repair static art delivery into static verification")
.expect("static smoke verification plan");
assert!(plan.response.is_empty());
assert_eq!(plan.actions.len(), 1);
assert_eq!(plan.actions[0].tool, "command.run_limited");
assert_eq!(plan.actions[0].input["commandId"], "game.static_smoke");
receiver
.recv_timeout(Duration::from_secs(2))
.expect("initial static art response request");
let repair_request = receiver
.recv_timeout(Duration::from_secs(2))
.expect("static art verification-only repair request");
let repair_function_names = captured_native_function_names_for_test(&repair_request);
assert_eq!(
repair_function_names,
BTreeSet::from([
native_runtime_function_name("command.run_limited").expect("static smoke function")
]),
"pure static asset delivery must not expose package.json-backed project.verify"
);
let repair_request_json = mock_http_request_json(&repair_request);
let repair_instruction = repair_request_json["messages"]
.as_array()
.and_then(|messages| messages.last())
.and_then(|message| message.get("content"))
.and_then(serde_json::Value::as_str)
.expect("static art repair instruction");
assert!(repair_instruction.contains("game.static_smoke"));
assert!(repair_instruction.contains("preview.validate"));
assert!(!repair_instruction.contains("project.verify"));
assert!(!repair_instruction.contains("package.json"));
assert!(!repair_instruction.contains("Node"));
assert!(receiver.recv_timeout(Duration::from_millis(200)).is_err());
fs::remove_dir_all(root).ok();
}