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) {
|
func TestInstallCommandArgs(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
|
||||||
registry string
|
registry string
|
||||||
expectError bool
|
args []string
|
||||||
errorMsg string
|
wantErr bool
|
||||||
|
errContains string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no registry, one arg",
|
name: "no registry with one arg passes",
|
||||||
args: []string{"sipeed/picoclaw-skills/weather"},
|
|
||||||
registry: "",
|
registry: "",
|
||||||
expectError: false,
|
args: []string{"sioeed/picoclaw-skills/weather"},
|
||||||
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no registry, no args",
|
name: "no registry with no args fails",
|
||||||
|
registry: "",
|
||||||
args: []string{},
|
args: []string{},
|
||||||
registry: "",
|
wantErr: true,
|
||||||
expectError: true,
|
errContains: "exactly 1 argument",
|
||||||
errorMsg: "exactly 1 argument is required: <github>",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no registry, too many args",
|
name: "with registry one arg passes",
|
||||||
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",
|
registry: "clawhub",
|
||||||
expectError: false,
|
args: []string{"github"},
|
||||||
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with registry, no args",
|
name: "with registry zero args fails",
|
||||||
|
registry: "clawhub",
|
||||||
args: []string{},
|
args: []string{},
|
||||||
registry: "clawhub",
|
wantErr: true,
|
||||||
expectError: true,
|
errContains: "exactly 1 argument",
|
||||||
errorMsg: "when --registry is set, exactly 1 argument is required: <slug>",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with registry, too many args",
|
name: "with registry two args fails",
|
||||||
args: []string{"arg1", "arg2"},
|
|
||||||
registry: "clawhub",
|
registry: "clawhub",
|
||||||
expectError: true,
|
args: []string{"arg1", "arg2"},
|
||||||
errorMsg: "when --registry is set, exactly 1 argument is required: <slug>",
|
wantErr: true,
|
||||||
|
errContains: "exactly 1 argument",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
cmd := newInstallCommand(nil)
|
cmd := newInstallCommand(nil)
|
||||||
|
// Set the registry flag
|
||||||
|
err := cmd.Flags().Set("registry", tt.registry)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
if tt.registry != "" {
|
err = cmd.Args(cmd, tt.args)
|
||||||
require.NoError(t, cmd.Flags().Set("registry", tt.registry))
|
|
||||||
}
|
|
||||||
|
|
||||||
err := cmd.Args(cmd, tt.args)
|
if tt.wantErr {
|
||||||
if tt.expectError {
|
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.Equal(t, tt.errorMsg, err.Error())
|
if tt.errContains != "" {
|
||||||
|
assert.Contains(t, err.Error(), tt.errContains)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
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