use std::fs; use std::path::{Path, PathBuf}; use sgclaw::runtime::{ load_scene_registry_from_root, match_scene_instruction_in_registry, DispatchMode, }; use uuid::Uuid; #[test] fn scene_registry_loads_first_slice_dispatch_policies() { let root = TempSceneRoot::new(); write_first_slice_scenes(&root); let registry = load_scene_registry_from_root(root.path()); let fault_details = registry .iter() .find(|entry| entry.id == "fault-details-report") .expect("fault-details-report scene should load"); assert_eq!(fault_details.dispatch_mode, DispatchMode::DirectBrowser); assert_eq!(fault_details.expected_domain, "sgcc.example.invalid"); assert_eq!(fault_details.skill_package, "fault-details-report"); assert_eq!(fault_details.skill_tool, "collect_fault_details"); let repair_dispatch = registry .iter() .find(|entry| entry.id == "95598-repair-city-dispatch") .expect("95598-repair-city-dispatch scene should load"); assert_eq!(repair_dispatch.dispatch_mode, DispatchMode::AgentBrowser); assert_eq!(repair_dispatch.skill_package, "95598-repair-city-dispatch"); assert_eq!(repair_dispatch.skill_tool, "collect_repair_orders"); } #[test] fn scene_registry_matches_fault_details_natural_language_instruction() { let root = TempSceneRoot::new(); write_first_slice_scenes(&root); let registry = load_scene_registry_from_root(root.path()); let matched = match_scene_instruction_in_registry(®istry, "请帮我导出故障明细").expect("scene should match"); assert_eq!(matched.id, "fault-details-report"); assert_eq!(matched.dispatch_mode, DispatchMode::DirectBrowser); } #[test] fn scene_registry_matches_city_dispatch_natural_language_instruction() { let root = TempSceneRoot::new(); write_first_slice_scenes(&root); let registry = load_scene_registry_from_root(root.path()); let matched = match_scene_instruction_in_registry(®istry, "帮我看一下95598抢修市指监测") .expect("scene should match"); assert_eq!(matched.id, "95598-repair-city-dispatch"); assert_eq!(matched.dispatch_mode, DispatchMode::AgentBrowser); } #[test] fn scene_registry_matches_rephrased_instruction_via_alias_terms() { let root = TempSceneRoot::new(); write_first_slice_scenes(&root); let registry = load_scene_registry_from_root(root.path()); let matched = match_scene_instruction_in_registry(®istry, "想看市指那边的95598抢修队列") .expect("scene should match"); assert_eq!(matched.id, "95598-repair-city-dispatch"); } #[test] fn scene_registry_returns_none_for_unrelated_instruction() { let root = TempSceneRoot::new(); write_first_slice_scenes(&root); let registry = load_scene_registry_from_root(root.path()); assert!(match_scene_instruction_in_registry(®istry, "今天上海天气怎么样").is_none()); } #[test] fn scene_registry_ignores_missing_or_broken_scene_files() { let root = TempSceneRoot::new(); root.write_scene( "fault-details-report", r#"{ "id": "fault-details-report", "name": "故障明细", "summary": "查询故障明细行并生成结构化报表。", "inputs": ["period"], "outputs": ["report-artifact"], "tags": ["fault", "report"], "skill": { "package": "fault-details-report", "tool": "collect_fault_details", "artifact_type": "report-artifact" } }"#, ); root.write_scene("95598-repair-city-dispatch", "{ broken json"); let registry = load_scene_registry_from_root(root.path()); assert_eq!(registry.len(), 1); assert_eq!(registry[0].id, "fault-details-report"); assert_eq!(registry[0].dispatch_mode, DispatchMode::DirectBrowser); } #[test] fn scene_registry_ignores_mismatched_scene_metadata_id() { let root = TempSceneRoot::new(); root.write_scene( "fault-details-report", r#"{ "id": "wrong-scene-id", "name": "故障明细", "summary": "查询故障明细行并生成结构化报表。", "inputs": ["period"], "outputs": ["report-artifact"], "tags": ["fault", "report"], "skill": { "package": "fault-details-report", "tool": "collect_fault_details", "artifact_type": "report-artifact" } }"#, ); root.write_scene( "95598-repair-city-dispatch", r#"{ "id": "95598-repair-city-dispatch", "name": "95598抢修市指监测", "summary": "采集95598抢修市指监测列表。", "inputs": ["period"], "outputs": ["repair-orders"], "tags": ["95598", "repair", "dispatch"], "skill": { "package": "95598-repair-city-dispatch", "tool": "collect_repair_orders", "artifact_type": "repair-orders" } }"#, ); let registry = load_scene_registry_from_root(root.path()); assert_eq!(registry.len(), 1); assert_eq!(registry[0].id, "95598-repair-city-dispatch"); } #[test] fn scene_registry_returns_none_for_ambiguous_instruction() { let root = TempSceneRoot::new(); write_first_slice_scenes(&root); let registry = load_scene_registry_from_root(root.path()); assert!( match_scene_instruction_in_registry(®istry, "请同时处理导出故障明细和95598抢修市指监测").is_none() ); } struct TempSceneRoot { root: PathBuf, } impl TempSceneRoot { fn new() -> Self { let root = std::env::temp_dir().join(format!("scene-registry-test-{}", Uuid::new_v4())); fs::create_dir_all(root.join("scenes")).expect("temp scene root should be created"); Self { root } } fn path(&self) -> &Path { &self.root } fn write_scene(&self, scene_id: &str, contents: &str) { let scene_dir = self.root.join("scenes").join(scene_id); fs::create_dir_all(&scene_dir).expect("scene directory should be created"); fs::write(scene_dir.join("scene.json"), contents).expect("scene file should be written"); } } fn write_first_slice_scenes(root: &TempSceneRoot) { root.write_scene( "fault-details-report", r#"{ "id": "fault-details-report", "name": "故障明细", "summary": "查询故障明细行并生成结构化报表。", "inputs": ["period"], "outputs": ["report-artifact"], "tags": ["fault", "report"], "skill": { "package": "fault-details-report", "tool": "collect_fault_details", "artifact_type": "report-artifact" } }"#, ); root.write_scene( "95598-repair-city-dispatch", r#"{ "id": "95598-repair-city-dispatch", "name": "95598抢修市指监测", "summary": "采集95598抢修市指监测列表。", "inputs": ["period"], "outputs": ["repair-orders"], "tags": ["95598", "repair", "dispatch"], "skill": { "package": "95598-repair-city-dispatch", "tool": "collect_repair_orders", "artifact_type": "repair-orders" } }"#, ); } impl Drop for TempSceneRoot { fn drop(&mut self) { let _ = fs::remove_dir_all(&self.root); } }