package skills import ( "os" "path/filepath" "testing" ) func TestLoader(t *testing.T) { // Create temporary skills directory structure tmpDir := t.TempDir() // Create a test skill skillDir := filepath.Join(tmpDir, "devops", "test-skill") if err := os.MkdirAll(skillDir, 0755); err != nil { t.Fatal(err) } // Write SKILL.md skillMD := `--- name: test-skill description: A test skill for unit testing --- # Test Skill This is the instructions body. It has multiple lines. ` skillMDPath := filepath.Join(skillDir, "SKILL.md") if err := os.WriteFile(skillMDPath, []byte(skillMD), 0644); err != nil { t.Fatal(err) } // Create scripts/ directory with a test script scriptsDir := filepath.Join(skillDir, "scripts") if err := os.MkdirAll(scriptsDir, 0755); err != nil { t.Fatal(err) } scriptPath := filepath.Join(scriptsDir, "test.sh") if err := os.WriteFile(scriptPath, []byte("#!/bin/bash\necho test"), 0755); err != nil { t.Fatal(err) } // Create references/ directory with a test reference refsDir := filepath.Join(skillDir, "references") if err := os.MkdirAll(refsDir, 0755); err != nil { t.Fatal(err) } refPath := filepath.Join(refsDir, "api.md") if err := os.WriteFile(refPath, []byte("# API Reference"), 0644); err != nil { t.Fatal(err) } loader := NewLoader(tmpDir) // Test LoadMeta t.Run("LoadMeta", func(t *testing.T) { metas, err := loader.LoadMeta() if err != nil { t.Fatalf("LoadMeta failed: %v", err) } if len(metas) != 1 { t.Fatalf("expected 1 skill, got %d", len(metas)) } meta := metas[0] if meta.Name != "test-skill" { t.Errorf("expected name 'test-skill', got %q", meta.Name) } if meta.Category != "devops" { t.Errorf("expected category 'devops', got %q", meta.Category) } if meta.Description != "A test skill for unit testing" { t.Errorf("expected description 'A test skill for unit testing', got %q", meta.Description) } }) // Test LoadSkill t.Run("LoadSkill", func(t *testing.T) { skill, err := loader.LoadSkill("test-skill") if err != nil { t.Fatalf("LoadSkill failed: %v", err) } if skill.Meta.Name != "test-skill" { t.Errorf("expected name 'test-skill', got %q", skill.Meta.Name) } if skill.Instructions == "" { t.Error("instructions should not be empty") } if len(skill.Scripts) != 1 || skill.Scripts[0] != "test.sh" { t.Errorf("expected Scripts=['test.sh'], got %v", skill.Scripts) } if len(skill.References) != 1 || skill.References[0] != "api.md" { t.Errorf("expected References=['api.md'], got %v", skill.References) } }) // Test LoadSkill nonexistent t.Run("LoadSkill_nonexistent", func(t *testing.T) { _, err := loader.LoadSkill("nonexistent") if err == nil { t.Error("expected error for nonexistent skill") } }) // Test ReadResource t.Run("ReadResource", func(t *testing.T) { content, err := loader.ReadResource("test-skill", "scripts/test.sh") if err != nil { t.Fatalf("ReadResource failed: %v", err) } if content != "#!/bin/bash\necho test" { t.Errorf("unexpected content: %q", content) } }) // Test ReadResource path traversal protection t.Run("ReadResource_path_traversal", func(t *testing.T) { _, err := loader.ReadResource("test-skill", "../../../etc/passwd") if err == nil { t.Error("expected error for path traversal attempt") } }) }