Files
claw/tests/scene_registry_test.rs
木炎 96c3bf1dee feat: route staged scene skills through runtime
Add registry-driven scene routing and multi-root skill loading so fault-details and 95598 scene skills can be triggered from natural language while still running through the browser-backed runtime.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 16:17:17 +08:00

224 lines
6.8 KiB
Rust

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(&registry, "请帮我导出故障明细").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(&registry, "帮我看一下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(&registry, "想看市指那边的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(&registry, "今天上海天气怎么样").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(&registry, "请同时处理导出故障明细和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);
}
}