feat: add generated scene skill platform hardening
This commit is contained in:
248
tests/scene_registry_test.rs
Normal file
248
tests/scene_registry_test.rs
Normal file
@@ -0,0 +1,248 @@
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use sgclaw::compat::scene_platform::registry::{load_scene_registry, SceneRegistryError};
|
||||
use uuid::Uuid;
|
||||
|
||||
fn temp_root(prefix: &str) -> PathBuf {
|
||||
let root = std::env::temp_dir().join(format!("{prefix}-{}", Uuid::new_v4()));
|
||||
fs::create_dir_all(&root).unwrap();
|
||||
root
|
||||
}
|
||||
|
||||
fn write_skill(
|
||||
root: &Path,
|
||||
skill_name: &str,
|
||||
skill_toml: &str,
|
||||
scene_toml: Option<&str>,
|
||||
) -> PathBuf {
|
||||
let skill_root = root.join(skill_name);
|
||||
fs::create_dir_all(&skill_root).unwrap();
|
||||
fs::write(skill_root.join("SKILL.toml"), skill_toml).unwrap();
|
||||
if let Some(scene_toml) = scene_toml {
|
||||
fs::write(skill_root.join("scene.toml"), scene_toml).unwrap();
|
||||
}
|
||||
skill_root
|
||||
}
|
||||
|
||||
fn browser_script_skill_toml(skill_name: &str, tool_name: &str, tool_kind: &str) -> String {
|
||||
format!(
|
||||
r#"[skill]
|
||||
name = "{skill_name}"
|
||||
description = "test skill"
|
||||
version = "0.1.0"
|
||||
|
||||
[[tools]]
|
||||
name = "{tool_name}"
|
||||
description = "test tool"
|
||||
kind = "{tool_kind}"
|
||||
command = "scripts/{tool_name}.js"
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
fn scene_toml(
|
||||
scene_id: &str,
|
||||
skill_name: &str,
|
||||
tool_name: &str,
|
||||
schema_version: &str,
|
||||
kind: &str,
|
||||
) -> String {
|
||||
format!(
|
||||
r#"[scene]
|
||||
id = "{scene_id}"
|
||||
skill = "{skill_name}"
|
||||
tool = "{tool_name}"
|
||||
kind = "{kind}"
|
||||
version = "0.1.0"
|
||||
category = "report_collection"
|
||||
|
||||
[manifest]
|
||||
schema_version = "{schema_version}"
|
||||
|
||||
[bootstrap]
|
||||
expected_domain = "20.76.57.61"
|
||||
target_url = "http://20.76.57.61:18080/gsllys/tqLinelossStatis/tqQualifyRateMonitor"
|
||||
requires_target_page = true
|
||||
|
||||
[deterministic]
|
||||
suffix = "。。。"
|
||||
include_keywords = ["线损"]
|
||||
exclude_keywords = ["知乎"]
|
||||
|
||||
[[params]]
|
||||
name = "org"
|
||||
resolver = "dictionary_entity"
|
||||
required = true
|
||||
prompt_missing = "缺少供电单位"
|
||||
prompt_ambiguous = "供电单位存在歧义"
|
||||
|
||||
[params.resolver_config]
|
||||
dictionary_ref = "references/org-dictionary.json"
|
||||
output_label_field = "org_label"
|
||||
output_code_field = "org_code"
|
||||
|
||||
[artifact]
|
||||
type = "report-artifact"
|
||||
success_status = ["ok", "partial", "empty"]
|
||||
failure_status = ["blocked", "error"]
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_loads_scene_manifest_from_skill_root() {
|
||||
let skills_root = temp_root("sgclaw-scene-registry");
|
||||
write_skill(
|
||||
&skills_root,
|
||||
"tq-lineloss-report",
|
||||
&browser_script_skill_toml("tq-lineloss-report", "collect_lineloss", "browser_script"),
|
||||
Some(&scene_toml(
|
||||
"tq-lineloss-report",
|
||||
"tq-lineloss-report",
|
||||
"collect_lineloss",
|
||||
"1",
|
||||
"browser_script",
|
||||
)),
|
||||
);
|
||||
|
||||
let registry = load_scene_registry(&skills_root).unwrap();
|
||||
|
||||
assert_eq!(registry.len(), 1);
|
||||
assert_eq!(registry[0].manifest.scene.id, "tq-lineloss-report");
|
||||
assert_eq!(
|
||||
registry[0].skill_root,
|
||||
skills_root.join("tq-lineloss-report")
|
||||
);
|
||||
assert!(registry[0].skill_root.join("scene.toml").exists());
|
||||
assert!(!registry[0]
|
||||
.skill_root
|
||||
.to_string_lossy()
|
||||
.contains("skill_staging/scenes"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_rejects_duplicate_scene_ids_with_both_paths_in_error() {
|
||||
let skills_root = temp_root("sgclaw-scene-registry-dup");
|
||||
let first = write_skill(
|
||||
&skills_root,
|
||||
"skill-a",
|
||||
&browser_script_skill_toml("skill-a", "collect_a", "browser_script"),
|
||||
Some(&scene_toml(
|
||||
"duplicate-scene",
|
||||
"skill-a",
|
||||
"collect_a",
|
||||
"1",
|
||||
"browser_script",
|
||||
)),
|
||||
);
|
||||
let second = write_skill(
|
||||
&skills_root,
|
||||
"skill-b",
|
||||
&browser_script_skill_toml("skill-b", "collect_b", "browser_script"),
|
||||
Some(&scene_toml(
|
||||
"duplicate-scene",
|
||||
"skill-b",
|
||||
"collect_b",
|
||||
"1",
|
||||
"browser_script",
|
||||
)),
|
||||
);
|
||||
|
||||
let err = load_scene_registry(&skills_root).expect_err("duplicate scene ids should fail");
|
||||
let message = err.to_string();
|
||||
|
||||
assert!(matches!(err, SceneRegistryError::DuplicateSceneId { .. }));
|
||||
assert!(message.contains(&first.join("scene.toml").display().to_string()));
|
||||
assert!(message.contains(&second.join("scene.toml").display().to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_rejects_unknown_manifest_schema_version() {
|
||||
let skills_root = temp_root("sgclaw-scene-registry-schema");
|
||||
write_skill(
|
||||
&skills_root,
|
||||
"tq-lineloss-report",
|
||||
&browser_script_skill_toml("tq-lineloss-report", "collect_lineloss", "browser_script"),
|
||||
Some(&scene_toml(
|
||||
"tq-lineloss-report",
|
||||
"tq-lineloss-report",
|
||||
"collect_lineloss",
|
||||
"999",
|
||||
"browser_script",
|
||||
)),
|
||||
);
|
||||
|
||||
let err = load_scene_registry(&skills_root).expect_err("unknown schema version should fail");
|
||||
let message = err.to_string();
|
||||
|
||||
assert!(matches!(
|
||||
err,
|
||||
SceneRegistryError::UnsupportedSchemaVersion { .. }
|
||||
));
|
||||
assert!(message.contains("999"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_rejects_non_browser_script_scene_tool_in_v1() {
|
||||
let skills_root = temp_root("sgclaw-scene-registry-kind");
|
||||
write_skill(
|
||||
&skills_root,
|
||||
"shell-scene",
|
||||
&browser_script_skill_toml("shell-scene", "collect_shell", "shell"),
|
||||
Some(&scene_toml(
|
||||
"shell-scene",
|
||||
"shell-scene",
|
||||
"collect_shell",
|
||||
"1",
|
||||
"shell",
|
||||
)),
|
||||
);
|
||||
|
||||
let err =
|
||||
load_scene_registry(&skills_root).expect_err("non browser_script scenes should fail in v1");
|
||||
let message = err.to_string();
|
||||
|
||||
assert!(matches!(
|
||||
err,
|
||||
SceneRegistryError::UnsupportedSceneKind { .. }
|
||||
));
|
||||
assert!(message.contains("browser_script"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn committed_lineloss_sample_package_is_registration_ready() {
|
||||
let skills_root = Path::new("examples/generated_scene_platform/skills");
|
||||
|
||||
let registry = load_scene_registry(skills_root).unwrap();
|
||||
let entry = registry
|
||||
.iter()
|
||||
.find(|entry| entry.manifest.scene.id == "tq-lineloss-report")
|
||||
.expect("committed line-loss sample package should be registered");
|
||||
|
||||
assert_eq!(entry.manifest.scene.skill, "tq-lineloss-report");
|
||||
assert_eq!(entry.manifest.scene.tool, "collect_lineloss");
|
||||
assert_eq!(entry.manifest.scene.kind, "browser_script");
|
||||
assert_eq!(entry.manifest.scene.category, "report_collection");
|
||||
assert_eq!(entry.manifest.bootstrap.expected_domain, "20.76.57.61");
|
||||
assert_eq!(
|
||||
entry.manifest.bootstrap.target_url,
|
||||
"http://20.76.57.61:18080/gsllys/tqLinelossStatis/tqQualifyRateMonitor"
|
||||
);
|
||||
assert!(entry
|
||||
.manifest
|
||||
.deterministic
|
||||
.include_keywords
|
||||
.iter()
|
||||
.any(|keyword| keyword == "统计分析"));
|
||||
assert!(entry
|
||||
.skill_root
|
||||
.join("references")
|
||||
.join("org-dictionary.json")
|
||||
.exists());
|
||||
assert!(entry
|
||||
.skill_root
|
||||
.join("scripts")
|
||||
.join("collect_lineloss.js")
|
||||
.exists());
|
||||
}
|
||||
Reference in New Issue
Block a user