test(skills): add IndexRegistry tests
This commit is contained in:
parent
a5b88dce5e
commit
9796051136
2 changed files with 209 additions and 41 deletions
|
|
@ -30,65 +30,60 @@ func TestNewInstallSubcommand(t *testing.T) {
|
|||
func TestInstallCommandArgs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
registry string
|
||||
expectError bool
|
||||
errorMsg string
|
||||
args []string
|
||||
wantErr bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "no registry, one arg",
|
||||
args: []string{"sipeed/picoclaw-skills/weather"},
|
||||
registry: "",
|
||||
expectError: false,
|
||||
name: "no registry with one arg passes",
|
||||
registry: "",
|
||||
args: []string{"sioeed/picoclaw-skills/weather"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no registry, no args",
|
||||
name: "no registry with no args fails",
|
||||
registry: "",
|
||||
args: []string{},
|
||||
wantErr: true,
|
||||
errContains: "exactly 1 argument",
|
||||
},
|
||||
{
|
||||
name: "with registry one arg passes",
|
||||
registry: "clawhub",
|
||||
args: []string{"github"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "with registry zero args fails",
|
||||
registry: "clawhub",
|
||||
args: []string{},
|
||||
registry: "",
|
||||
expectError: true,
|
||||
errorMsg: "exactly 1 argument is required: <github>",
|
||||
wantErr: true,
|
||||
errContains: "exactly 1 argument",
|
||||
},
|
||||
{
|
||||
name: "no registry, too many args",
|
||||
name: "with registry two args fails",
|
||||
registry: "clawhub",
|
||||
args: []string{"arg1", "arg2"},
|
||||
registry: "",
|
||||
expectError: true,
|
||||
errorMsg: "exactly 1 argument is required: <github>",
|
||||
},
|
||||
{
|
||||
name: "with registry, one arg",
|
||||
args: []string{"weather-skill"},
|
||||
registry: "clawhub",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "with registry, no args",
|
||||
args: []string{},
|
||||
registry: "clawhub",
|
||||
expectError: true,
|
||||
errorMsg: "when --registry is set, exactly 1 argument is required: <slug>",
|
||||
},
|
||||
{
|
||||
name: "with registry, too many args",
|
||||
args: []string{"arg1", "arg2"},
|
||||
registry: "clawhub",
|
||||
expectError: true,
|
||||
errorMsg: "when --registry is set, exactly 1 argument is required: <slug>",
|
||||
wantErr: true,
|
||||
errContains: "exactly 1 argument",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := newInstallCommand(nil)
|
||||
// Set the registry flag
|
||||
err := cmd.Flags().Set("registry", tt.registry)
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.registry != "" {
|
||||
require.NoError(t, cmd.Flags().Set("registry", tt.registry))
|
||||
}
|
||||
err = cmd.Args(cmd, tt.args)
|
||||
|
||||
err := cmd.Args(cmd, tt.args)
|
||||
if tt.expectError {
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, tt.errorMsg, err.Error())
|
||||
if tt.errContains != "" {
|
||||
assert.Contains(t, err.Error(), tt.errContains)
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
|||
173
pkg/skills/index_registry_test.go
Normal file
173
pkg/skills/index_registry_test.go
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
package skills
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIndexRegistryMapURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mappings map[string]string
|
||||
inputURL string
|
||||
expectedURL string
|
||||
}{
|
||||
{
|
||||
name: "maps HTTP to fork",
|
||||
mappings: map[string]string{
|
||||
"https://raw.githubusercontent.com/keithy/angelhub/main/": "https://raw.githubusercontent.com/myfork/angelhub/main/",
|
||||
},
|
||||
inputURL: "https://raw.githubusercontent.com/keithy/angelhub/main/picoclaw/skills/self-config",
|
||||
expectedURL: "https://raw.githubusercontent.com/myfork/angelhub/main/picoclaw/skills/self-config",
|
||||
},
|
||||
{
|
||||
name: "maps HTTP to local file",
|
||||
mappings: map[string]string{
|
||||
"https://raw.githubusercontent.com/keithy/angelhub/": "file:///home/me/repos/angelhub/",
|
||||
},
|
||||
inputURL: "https://raw.githubusercontent.com/keithy/angelhub/main/picoclaw/skills/self-config",
|
||||
expectedURL: "file:///home/me/repos/angelhub/main/picoclaw/skills/self-config",
|
||||
},
|
||||
{
|
||||
name: "no mapping returns original",
|
||||
mappings: map[string]string{
|
||||
"https://other.com/": "https://example.com/",
|
||||
},
|
||||
inputURL: "https://raw.githubusercontent.com/keithy/angelhub/main/skills",
|
||||
expectedURL: "https://raw.githubusercontent.com/keithy/angelhub/main/skills",
|
||||
},
|
||||
{
|
||||
name: "empty mappings returns original",
|
||||
mappings: map[string]string{},
|
||||
inputURL: "https://raw.githubusercontent.com/keithy/angelhub/main/skills",
|
||||
expectedURL: "https://raw.githubusercontent.com/keithy/angelhub/main/skills",
|
||||
},
|
||||
{
|
||||
name: "nil mappings returns original",
|
||||
mappings: nil,
|
||||
inputURL: "https://raw.githubusercontent.com/keithy/angelhub/main/skills",
|
||||
expectedURL: "https://raw.githubusercontent.com/keithy/angelhub/main/skills",
|
||||
},
|
||||
{
|
||||
name: "first matching prefix wins",
|
||||
mappings: map[string]string{
|
||||
"https://raw.githubusercontent.com/keithy/": "https://raw.githubusercontent.com/fork1/",
|
||||
},
|
||||
inputURL: "https://raw.githubusercontent.com/keithy/angelhub/main/skills",
|
||||
expectedURL: "https://raw.githubusercontent.com/fork1/angelhub/main/skills",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &IndexRegistry{
|
||||
name: "test",
|
||||
urlMappings: tt.mappings,
|
||||
}
|
||||
result := r.mapURL(tt.inputURL)
|
||||
assert.Equal(t, tt.expectedURL, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexRegistryIsURLAllowed(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
allowedPrefixes []string
|
||||
url string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "matching prefix returns true",
|
||||
allowedPrefixes: []string{"https://raw.githubusercontent.com/keithy/"},
|
||||
url: "https://raw.githubusercontent.com/keithy/angelhub/main/skills",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "non-matching prefix returns false",
|
||||
allowedPrefixes: []string{"https://raw.githubusercontent.com/keithy/"},
|
||||
url: "https://raw.githubusercontent.com/other/repo/main",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "empty allowedPrefixes allows all",
|
||||
allowedPrefixes: []string{},
|
||||
url: "https://any-site.com/file",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "nil allowedPrefixes allows all",
|
||||
allowedPrefixes: nil,
|
||||
url: "https://any-site.com/file",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "multiple prefixes first match wins",
|
||||
allowedPrefixes: []string{"https://raw.githubusercontent.com/other/", "https://raw.githubusercontent.com/keithy/"},
|
||||
url: "https://raw.githubusercontent.com/keithy/angelhub/main",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "prefix not at start returns false",
|
||||
allowedPrefixes: []string{"https://raw.githubusercontent.com/keithy/"},
|
||||
url: "https://raw.githubusercontent.com/other/keithy/foo",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &IndexRegistry{
|
||||
name: "test",
|
||||
allowedPrefixes: tt.allowedPrefixes,
|
||||
}
|
||||
result := r.isURLAllowed(tt.url)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexRegistryCopyLocalPath(t *testing.T) {
|
||||
// Create temp directory for testing
|
||||
tmpDir := t.TempDir()
|
||||
tmpSkillDir := filepath.Join(tmpDir, "test-skill")
|
||||
err := os.MkdirAll(filepath.Join(tmpSkillDir, "subdir"), 0o755)
|
||||
require.NoError(t, err)
|
||||
err = os.WriteFile(filepath.Join(tmpSkillDir, "SKILL.md"), []byte("# Test Skill"), 0o644)
|
||||
require.NoError(t, err)
|
||||
err = os.WriteFile(filepath.Join(tmpSkillDir, "subdir", "script.sh"), []byte("#!/bin/bash\necho hello"), 0o755)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("symlink_local true creates symlink", func(t *testing.T) {
|
||||
targetDir := filepath.Join(tmpDir, "installed-skill")
|
||||
r := &IndexRegistry{
|
||||
name: "test",
|
||||
symlinkLocal: true,
|
||||
}
|
||||
err := r.copyLocalPath(tmpSkillDir, targetDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify symlink was created
|
||||
info, err := os.Lstat(targetDir)
|
||||
require.NoError(t, err)
|
||||
require.True(t, info.Mode()&os.ModeSymlink != 0, "should be a symlink")
|
||||
|
||||
// Verify symlink points to correct path
|
||||
linkDest, err := os.Readlink(targetDir)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tmpSkillDir, linkDest)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIndexRegistrySearch(t *testing.T) {
|
||||
// This test would require mocking HTTP - just verify the method signature works
|
||||
r := &IndexRegistry{
|
||||
name: "test",
|
||||
indexURL: "https://example.com/index.json",
|
||||
}
|
||||
assert.Equal(t, "test", r.Name())
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue