//! Tests for skill loader — SKILL.md and TOML parsing use zclaw_skills::*; // === parse_skill_md === #[test] fn parse_skill_md_basic_frontmatter() { let content = r#"--- name: "Code Reviewer" description: "Reviews code" version: "1.0.0" mode: prompt-only tags: coding, review --- # Code Reviewer Reviews code for quality. "#; let manifest = parse_skill_md(content).unwrap(); assert_eq!(manifest.name, "Code Reviewer"); assert_eq!(manifest.description, "Reviews code"); assert_eq!(manifest.version, "1.0.0"); assert_eq!(manifest.mode, zclaw_skills::SkillMode::PromptOnly); assert_eq!(manifest.tags, vec!["coding", "review"]); } #[test] fn parse_skill_md_with_triggers_list() { let content = r#"--- name: "Translator" description: "Translates text" version: "1.0.0" mode: prompt-only triggers: - "翻译" - "translate" - "中译英" --- # Translator "#; let manifest = parse_skill_md(content).unwrap(); assert_eq!(manifest.triggers, vec!["翻译", "translate", "中译英"]); } #[test] fn parse_skill_md_with_tools_list() { let content = r#"--- name: "Builder" description: "Builds projects" version: "1.0.0" mode: shell tools: - "bash" - "cargo" --- # Builder "#; let manifest = parse_skill_md(content).unwrap(); assert_eq!(manifest.tools, vec!["bash", "cargo"]); assert_eq!(manifest.mode, zclaw_skills::SkillMode::Shell); } #[test] fn parse_skill_md_with_category() { let content = r#"--- name: "Math Solver" description: "Solves math problems" version: "1.0.0" mode: prompt-only category: "math" --- # Math Solver "#; let manifest = parse_skill_md(content).unwrap(); assert_eq!(manifest.category.unwrap(), "math"); } #[test] fn parse_skill_md_auto_classify_coding() { let content = r#"--- name: "Code Helper" description: "Helps with programming and debugging" version: "1.0.0" mode: prompt-only --- # Code Helper "#; let manifest = parse_skill_md(content).unwrap(); // Should auto-classify as "coding" based on description assert_eq!(manifest.category.unwrap(), "coding"); } #[test] fn parse_skill_md_auto_classify_translation() { let content = r#"--- name: "Translator" description: "Helps with translation between languages" version: "1.0.0" mode: prompt-only --- # Translator "#; let manifest = parse_skill_md(content).unwrap(); // Should auto-classify based on "translat" keyword assert!(manifest.category.is_some(), "Should auto-classify translation skill"); } #[test] fn parse_skill_md_no_frontmatter_extracts_name() { let content = "# My Skill\n\nThis is a cool skill."; let manifest = parse_skill_md(content).unwrap(); assert_eq!(manifest.name, "My Skill"); } #[test] fn parse_skill_md_fallback_name() { let content = "Just some text without structure."; let manifest = parse_skill_md(content).unwrap(); assert_eq!(manifest.name, "unnamed-skill"); } #[test] fn parse_skill_md_id_generation() { let content = "---\nname: \"Hello World\"\n---\n"; let manifest = parse_skill_md(content).unwrap(); assert_eq!(manifest.id.as_str(), "hello-world"); } #[test] fn parse_skill_md_all_modes() { for (mode_str, expected) in &[ ("prompt-only", zclaw_skills::SkillMode::PromptOnly), ("python", zclaw_skills::SkillMode::Python), ("shell", zclaw_skills::SkillMode::Shell), ("wasm", zclaw_skills::SkillMode::Wasm), ("native", zclaw_skills::SkillMode::Native), ] { let content = format!("---\nname: \"Test\"\nmode: {}\n---\n", mode_str); let manifest = parse_skill_md(&content).unwrap(); assert_eq!(&manifest.mode, expected, "Failed for mode: {}", mode_str); } } #[test] fn parse_skill_md_capabilities_csv() { let content = "---\nname: \"Multi\"\ncapabilities: llm, web, file\n---\n"; let manifest = parse_skill_md(content).unwrap(); assert_eq!(manifest.capabilities, vec!["llm", "web", "file"]); } // === parse_skill_toml === #[test] fn parse_skill_toml_basic() { let content = r#" name = "Calculator" description = "Performs calculations" version = "2.0.0" mode = "prompt_only" "#; let manifest = parse_skill_toml(content).unwrap(); assert_eq!(manifest.name, "Calculator"); assert_eq!(manifest.description, "Performs calculations"); assert_eq!(manifest.version, "2.0.0"); } #[test] fn parse_skill_toml_with_id() { let content = r#" id = "my-calc" name = "Calculator" description = "Calc" "#; let manifest = parse_skill_toml(content).unwrap(); assert_eq!(manifest.id.as_str(), "my-calc"); } #[test] fn parse_skill_toml_generates_id_from_name() { let content = "name = \"Hello World\"\ndescription = \"x\""; let manifest = parse_skill_toml(content).unwrap(); assert_eq!(manifest.id.as_str(), "hello-world"); } #[test] fn parse_skill_toml_requires_name() { let content = r#"description = "no name""#; let result = parse_skill_toml(content); assert!(result.is_err()); } #[test] fn parse_skill_toml_arrays() { let content = r#" name = "X" description = "x" tags = ["a", "b", "c"] capabilities = ["llm"] triggers = ["go", "run"] "#; let manifest = parse_skill_toml(content).unwrap(); assert_eq!(manifest.tags, vec!["a", "b", "c"]); assert_eq!(manifest.capabilities, vec!["llm"]); assert_eq!(manifest.triggers, vec!["go", "run"]); } #[test] fn parse_skill_toml_category() { let content = r#" name = "X" description = "x" category = "data" "#; let manifest = parse_skill_toml(content).unwrap(); assert_eq!(manifest.category.unwrap(), "data"); } #[test] fn parse_skill_toml_tools() { let content = r#" name = "X" description = "x" tools = ["bash", "cargo"] "#; let manifest = parse_skill_toml(content).unwrap(); assert_eq!(manifest.tools, vec!["bash", "cargo"]); } #[test] fn parse_skill_toml_ignores_comments_and_sections() { let content = r#" # This is a comment [section] name = "X" description = "x" "#; let manifest = parse_skill_toml(content).unwrap(); assert_eq!(manifest.name, "X"); } // === discover_skills === #[test] fn discover_skills_nonexistent_dir() { let result = discover_skills(std::path::Path::new("/nonexistent/path")).unwrap(); assert!(result.is_empty()); }