3638529468
Agregar componentes impuros para manejo de skills en shell/skills/: Loader (filesystem I/O): - LoadMeta(): carga metadata de todas las skills - LoadSkill(): carga skill completa con instrucciones - ReadResource(): lee recursos con path traversal protection - Parsing de SKILL.md con frontmatter YAML Executor (script execution): - Ejecucion segura de scripts con allowlist de interpreters - Timeout obligatorio por script - Inferencia de interpreter desde extension - Proteccion contra scripts maliciosos Incluye tests completos con tmpdir para loader y executor. Arquitectura: impure shell, todo I/O aislado en shell/.
132 lines
3.3 KiB
Go
132 lines
3.3 KiB
Go
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")
|
|
}
|
|
})
|
|
}
|