Files
agents_and_robots/shell/skills/loader_test.go
T
egutierrez 3638529468 feat: loader y executor de skills en shell
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/.
2026-03-08 22:13:12 +00:00

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")
}
})
}