use std::fs; use std::path::{Path, PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; use sgclaw::compat::scene_platform::registry::load_scene_registry; use sgclaw::generated_scene::analyzer::{analyze_scene_source, analyze_scene_source_with_hint, SceneKind, ToolKind}; use sgclaw::generated_scene::generator::{generate_scene_package, GenerateSceneRequest}; #[test] fn analyzer_classifies_supported_report_collection_source() { let analysis = analyze_scene_source(Path::new( "tests/fixtures/generated_scene/report_collection", )) .unwrap(); assert_eq!(analysis.scene_kind, SceneKind::ReportCollection); assert_eq!(analysis.tool_kind, ToolKind::BrowserScript); assert_eq!( analysis.bootstrap.target_url.as_deref(), Some("http://20.76.57.61:18080/gsllys/tqLinelossStatis/tqQualifyRateMonitor") ); assert_eq!( analysis.bootstrap.expected_domain.as_deref(), Some("20.76.57.61") ); assert_eq!( analysis.collection_entry_script.as_deref(), Some("js/report.js") ); } #[test] fn generator_writes_registration_ready_package_with_scene_toml() { let output_root = temp_workspace("sgclaw-scene-generator"); generate_scene_package(GenerateSceneRequest { source_dir: PathBuf::from("tests/fixtures/generated_scene/report_collection"), scene_id: "sample-report-scene".to_string(), scene_name: "示例报表场景".to_string(), scene_kind: None, output_root: output_root.clone(), lessons_path: PathBuf::from("docs/superpowers/references/tq-lineloss-lessons-learned.toml"), }) .unwrap(); let skill_root = output_root.join("skills/sample-report-scene"); assert!(skill_root.join("SKILL.toml").exists()); assert!(skill_root.join("SKILL.md").exists()); assert!(skill_root.join("scene.toml").exists()); assert!(skill_root .join("scripts/collect_sample_report_scene.js") .exists()); assert!(skill_root .join("scripts/collect_sample_report_scene.test.js") .exists()); assert!(skill_root.join("references/generation-lessons.md").exists()); assert!(skill_root.join("references/org-dictionary.json").exists()); let generated_script = fs::read_to_string(skill_root.join("scripts/collect_sample_report_scene.js")).unwrap(); assert!(generated_script.contains("return buildBrowserEntrypointResult(args);")); let generated_manifest = fs::read_to_string(skill_root.join("scene.toml")).unwrap(); assert!(generated_manifest.contains("resolver = \"dictionary_entity\"")); assert!(generated_manifest.contains("dictionary_ref = \"references/org-dictionary.json\"")); assert!(generated_manifest.contains("required = true")); let registry = load_scene_registry(&output_root.join("skills")).unwrap(); let entry = registry .iter() .find(|entry| entry.manifest.scene.id == "sample-report-scene") .expect("generated package should be registration-ready"); assert_eq!(entry.manifest.scene.kind, "browser_script"); assert_eq!(entry.manifest.scene.category, "report_collection"); assert_eq!(entry.manifest.scene.tool, "collect_sample_report_scene"); assert_eq!(entry.manifest.bootstrap.expected_domain, "20.76.57.61"); } #[test] fn analyzer_defaults_to_report_collection_when_no_scene_kind_meta() { // non_report fixture has no scene-kind meta tag - should default to ReportCollection let analysis = analyze_scene_source(Path::new("tests/fixtures/generated_scene/non_report")).unwrap(); // With the new delegation, it defaults to ReportCollection instead of rejecting assert_eq!(analysis.scene_kind, SceneKind::ReportCollection); assert_eq!(analysis.tool_kind, ToolKind::BrowserScript); } fn temp_workspace(prefix: &str) -> PathBuf { let nanos = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_nanos(); let path = std::env::temp_dir().join(format!("{prefix}-{nanos}")); fs::create_dir_all(&path).unwrap(); path } #[test] fn analyzer_accepts_missing_meta_with_scene_kind_hint() { // non_report fixture has no scene-kind meta tag let analysis = analyze_scene_source_with_hint( Path::new("tests/fixtures/generated_scene/non_report"), Some(SceneKind::ReportCollection), ) .unwrap(); // should succeed, using hint parameter as type assert_eq!(analysis.scene_kind, SceneKind::ReportCollection); } #[test] fn analyzer_uses_hint_when_meta_missing() { let analysis = analyze_scene_source_with_hint( Path::new("tests/fixtures/generated_scene/non_report"), Some(SceneKind::Monitoring), ) .unwrap(); assert_eq!(analysis.scene_kind, SceneKind::Monitoring); } #[test] fn analyzer_uses_meta_when_present_and_no_hint() { // report_collection fixture has correct meta tag let analysis = analyze_scene_source_with_hint( Path::new("tests/fixtures/generated_scene/report_collection"), None, ) .unwrap(); assert_eq!(analysis.scene_kind, SceneKind::ReportCollection); } #[test] fn analyzer_hint_overrides_meta() { // user choice takes priority over meta tag let analysis = analyze_scene_source_with_hint( Path::new("tests/fixtures/generated_scene/report_collection"), Some(SceneKind::Monitoring), ) .unwrap(); assert_eq!(analysis.scene_kind, SceneKind::Monitoring); } #[test] fn generator_emits_monitoring_template() { let output_root = temp_workspace("sgclaw-monitoring-generator"); generate_scene_package(GenerateSceneRequest { source_dir: PathBuf::from("tests/fixtures/generated_scene/monitoring"), scene_id: "sample-monitor-scene".to_string(), scene_name: "示例监测场景".to_string(), scene_kind: Some(SceneKind::Monitoring), output_root: output_root.clone(), lessons_path: PathBuf::from("docs/superpowers/references/tq-lineloss-lessons-learned.toml"), }) .unwrap(); let skill_root = output_root.join("skills/sample-monitor-scene"); assert!(skill_root.join("SKILL.toml").exists()); assert!(skill_root.join("scene.toml").exists()); let generated_manifest = fs::read_to_string(skill_root.join("scene.toml")).unwrap(); assert!(generated_manifest.contains("category = \"monitoring\"")); // 监测类不应该有 org/period resolver assert!(!generated_manifest.contains("resolver = \"dictionary_entity\"")); } #[test] fn analyzer_extracts_domain_from_external_script() { // external_script fixture has no expected_domain meta tag, // but has an external script URL that should be auto-extracted let analysis = analyze_scene_source(Path::new( "tests/fixtures/generated_scene/external_script", )) .unwrap(); assert_eq!(analysis.scene_kind, SceneKind::ReportCollection); // Should auto-extract "25.215.213.128:18080" from script src assert_eq!( analysis.bootstrap.expected_domain.as_deref(), Some("25.215.213.128:18080") ); }