213 lines
6.1 KiB
Go
213 lines
6.1 KiB
Go
package plugin
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// createTempPlugin creates a minimal valid plugin directory for testing.
|
|
func createTempPlugin(t *testing.T, dir, id, name string) string {
|
|
t.Helper()
|
|
pluginDir := filepath.Join(dir, id)
|
|
if err := os.MkdirAll(pluginDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
manifest := `{
|
|
"schemaVersion": 1,
|
|
"id": "` + id + `",
|
|
"name": "` + name + `",
|
|
"version": "1.0.0",
|
|
"apiVersion": "1.0",
|
|
"provides": ["` + id + `.cap1"],
|
|
"permissions": ["vault.read"]
|
|
}`
|
|
if err := os.WriteFile(filepath.Join(pluginDir, "plugin.json"), []byte(manifest), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return pluginDir
|
|
}
|
|
|
|
// TestDiscoverPlugins_EmptyDir tests discovery on a directory that does not exist.
|
|
// It should return empty results, not hang or error out.
|
|
func TestDiscoverPlugins_EmptyDir(t *testing.T) {
|
|
plugins, errs := DiscoverPlugins([]string{"/tmp/nonexistent-plugin-dir-12345"})
|
|
if len(plugins) != 0 {
|
|
t.Errorf("expected 0 plugins, got %d", len(plugins))
|
|
}
|
|
if len(errs) != 0 {
|
|
t.Errorf("expected 0 errors, got %d: %v", len(errs), errs)
|
|
}
|
|
}
|
|
|
|
// TestDiscoverPlugins_MissingDir tests discovery where the directory does not exist.
|
|
// It should not be treated as an error — just skip.
|
|
func TestDiscoverPlugins_MissingDir(t *testing.T) {
|
|
plugins, errs := DiscoverPlugins([]string{"/tmp/missing-dir-99999"})
|
|
if len(plugins) != 0 {
|
|
t.Errorf("expected 0 plugins, got %d", len(plugins))
|
|
}
|
|
if len(errs) != 0 {
|
|
t.Errorf("expected 0 errors for missing dir, got %d", len(errs))
|
|
}
|
|
}
|
|
|
|
// TestDiscoverPlugins_ValidPlugin tests discovery of a single valid plugin.
|
|
func TestDiscoverPlugins_ValidPlugin(t *testing.T) {
|
|
dir := t.TempDir()
|
|
createTempPlugin(t, dir, "test.plugin", "Test Plugin")
|
|
|
|
plugins, errs := DiscoverPlugins([]string{dir})
|
|
if len(plugins) != 1 {
|
|
t.Fatalf("expected 1 plugin, got %d", len(plugins))
|
|
}
|
|
if plugins[0].Manifest.ID != "test.plugin" {
|
|
t.Errorf("expected id 'test.plugin', got %q", plugins[0].Manifest.ID)
|
|
}
|
|
if !plugins[0].Enabled {
|
|
t.Error("expected plugin to be enabled by default")
|
|
}
|
|
if len(errs) > 0 {
|
|
t.Errorf("expected 0 errors, got %d: %v", len(errs), errs)
|
|
}
|
|
}
|
|
|
|
// TestDiscoverPlugins_NoManifest ensures subdirs without plugin.json are skipped.
|
|
func TestDiscoverPlugins_NoManifest(t *testing.T) {
|
|
dir := t.TempDir()
|
|
noManifestDir := filepath.Join(dir, "no-manifest")
|
|
if err := os.MkdirAll(noManifestDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
plugins, errs := DiscoverPlugins([]string{dir})
|
|
if len(plugins) != 0 {
|
|
t.Errorf("expected 0 plugins (no manifest), got %d", len(plugins))
|
|
}
|
|
if len(errs) != 0 {
|
|
t.Errorf("expected 0 errors, got %d", len(errs))
|
|
}
|
|
}
|
|
|
|
// TestDiscoverPlugins_BrokenJSON ensures a corrupted manifest is reported as error
|
|
// but does not crash or hang discovery.
|
|
func TestDiscoverPlugins_BrokenJSON(t *testing.T) {
|
|
dir := t.TempDir()
|
|
pluginDir := filepath.Join(dir, "broken")
|
|
if err := os.MkdirAll(pluginDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(pluginDir, "plugin.json"), []byte("{invalid json}"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
plugins, errs := DiscoverPlugins([]string{dir})
|
|
if len(plugins) != 0 {
|
|
t.Errorf("expected 0 plugins (broken manifest), got %d", len(plugins))
|
|
}
|
|
if len(errs) == 0 {
|
|
t.Error("expected at least 1 error for broken manifest, got 0")
|
|
}
|
|
}
|
|
|
|
// TestDiscoverPlugins_DuplicateID ensures duplicate plugin IDs are detected.
|
|
func TestDiscoverPlugins_DuplicateID(t *testing.T) {
|
|
dir := t.TempDir()
|
|
createTempPlugin(t, dir, "dup-one", "First")
|
|
|
|
// Create a second plugin with the same ID (duplicate)
|
|
pluginDir2 := filepath.Join(dir, "dup-two")
|
|
if err := os.MkdirAll(pluginDir2, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
manifest := `{
|
|
"schemaVersion": 1,
|
|
"id": "dup-one",
|
|
"name": "Second",
|
|
"version": "1.0.0",
|
|
"apiVersion": "1.0",
|
|
"provides": ["dup-one.cap1"],
|
|
"permissions": ["vault.read"]
|
|
}`
|
|
if err := os.WriteFile(filepath.Join(pluginDir2, "plugin.json"), []byte(manifest), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
plugins, errs := DiscoverPlugins([]string{dir})
|
|
if len(plugins) != 1 {
|
|
t.Errorf("expected 1 plugin (deduped), got %d", len(plugins))
|
|
}
|
|
hasDupError := false
|
|
for _, e := range errs {
|
|
if strings.Contains(e.Error(), "duplicate") {
|
|
hasDupError = true
|
|
}
|
|
}
|
|
if !hasDupError {
|
|
t.Error("expected duplicate plugin ID error")
|
|
}
|
|
}
|
|
|
|
// TestDiscoverPlugins_MultipleDirs ensures discovery scans multiple directories.
|
|
func TestDiscoverPlugins_MultipleDirs(t *testing.T) {
|
|
dir1 := t.TempDir()
|
|
dir2 := t.TempDir()
|
|
createTempPlugin(t, dir1, "alpha.plugin", "Alpha")
|
|
createTempPlugin(t, dir2, "beta.plugin", "Beta")
|
|
|
|
plugins, errs := DiscoverPlugins([]string{dir1, dir2})
|
|
if len(plugins) != 2 {
|
|
t.Fatalf("expected 2 plugins, got %d", len(plugins))
|
|
}
|
|
if len(errs) > 0 {
|
|
t.Errorf("expected 0 errors, got %d: %v", len(errs), errs)
|
|
}
|
|
}
|
|
|
|
// TestDiscoverPlugins_AllowsNonexistentDirs ensures that a mix of valid and
|
|
// nonexistent directories doesn't cause issues.
|
|
func TestDiscoverPlugins_AllowsNonexistentDirs(t *testing.T) {
|
|
dir := t.TempDir()
|
|
createTempPlugin(t, dir, "survivor.plugin", "Survivor")
|
|
|
|
plugins, errs := DiscoverPlugins([]string{
|
|
"/tmp/missing-aaa-88888",
|
|
dir,
|
|
"/tmp/missing-bbb-99999",
|
|
})
|
|
if len(plugins) != 1 {
|
|
t.Fatalf("expected 1 plugin, got %d", len(plugins))
|
|
}
|
|
if plugins[0].Manifest.ID != "survivor.plugin" {
|
|
t.Errorf("expected 'survivor.plugin', got %q", plugins[0].Manifest.ID)
|
|
}
|
|
if len(errs) > 0 {
|
|
t.Errorf("expected 0 errors, got %d: %v", len(errs), errs)
|
|
}
|
|
}
|
|
|
|
// TestFormatDiscoverySummary on empty list.
|
|
func TestFormatDiscoverySummary_Empty(t *testing.T) {
|
|
s := FormatDiscoverySummary(nil)
|
|
if s != "no plugins found" {
|
|
t.Errorf("expected 'no plugins found', got %q", s)
|
|
}
|
|
}
|
|
|
|
// TestFormatDiscoverySummary on populated list.
|
|
func TestFormatDiscoverySummary_Plugins(t *testing.T) {
|
|
plugins := []Plugin{
|
|
{
|
|
Manifest: Manifest{ID: "test.one", Version: "1.0.0"},
|
|
},
|
|
{
|
|
Manifest: Manifest{ID: "test.two", Version: "2.0.0"},
|
|
},
|
|
}
|
|
s := FormatDiscoverySummary(plugins)
|
|
if s != "2 plugin(s): test.one@1.0.0, test.two@2.0.0" {
|
|
t.Errorf("unexpected summary: %q", s)
|
|
}
|
|
}
|