package plugins import ( "fmt" "testing" "time" "github.com/stretchr/testify/assert" ) // MockPlugin is a test plugin implementation type MockPlugin struct { name string version string description string shouldFail bool shouldWait time.Duration } func (mp *MockPlugin) Name() string { return mp.name } func (mp *MockPlugin) Version() string { return mp.version } func (mp *MockPlugin) Description() string { return mp.description } func (mp *MockPlugin) Execute(input map[string]interface{}) (map[string]interface{}, error) { if mp.shouldWait > 0 { time.Sleep(mp.shouldWait) } if mp.shouldFail { return nil, fmt.Errorf("plugin execution failed") } return map[string]interface{}{ "result": "success", "input": input, }, nil } func (mp *MockPlugin) Validate() error { if mp.name == "" { return fmt.Errorf("plugin name cannot be empty") } return nil } func TestNewPluginRegistry(t *testing.T) { registry := NewPluginRegistry() assert.NotNil(t, registry) assert.Equal(t, 0, registry.stats.TotalPlugins) } func TestRegisterPlugin(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{ name: "test-plugin", version: "1.0.0", description: "Test plugin", } err := registry.Register(plugin, "test-author", nil) assert.NoError(t, err) assert.Equal(t, 1, registry.stats.TotalPlugins) } func TestRegisterPluginNil(t *testing.T) { registry := NewPluginRegistry() err := registry.Register(nil, "test-author", nil) assert.Error(t, err) } func TestRegisterDuplicatePlugin(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{ name: "test-plugin", version: "1.0.0", } registry.Register(plugin, "author", nil) err := registry.Register(plugin, "author", nil) assert.Error(t, err) } func TestUnregisterPlugin(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) assert.Equal(t, 1, registry.stats.TotalPlugins) err := registry.Unregister("test-plugin") assert.NoError(t, err) assert.Equal(t, 0, registry.stats.TotalPlugins) } func TestExecutePlugin(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) input := map[string]interface{}{"key": "value"} output, err := registry.Execute("test-plugin", input) assert.NoError(t, err) assert.NotNil(t, output) assert.Equal(t, "success", output["result"]) } func TestExecutePluginNotFound(t *testing.T) { registry := NewPluginRegistry() _, err := registry.Execute("nonexistent", map[string]interface{}{}) assert.Error(t, err) } func TestExecutePluginDisabled(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) registry.DisablePlugin("test-plugin") _, err := registry.Execute("test-plugin", map[string]interface{}{}) assert.Error(t, err) } func TestExecutePluginFailure(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{ name: "test-plugin", version: "1.0.0", shouldFail: true, } registry.Register(plugin, "author", nil) _, err := registry.Execute("test-plugin", map[string]interface{}{}) assert.Error(t, err) assert.Equal(t, 1, registry.stats.FailedExecutions) } func TestGetPlugin(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) retrieved, found := registry.Get("test-plugin") assert.True(t, found) assert.Equal(t, "test-plugin", retrieved.Name()) } func TestGetPluginNotFound(t *testing.T) { registry := NewPluginRegistry() _, found := registry.Get("nonexistent") assert.False(t, found) } func TestGetMetadata(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{ name: "test-plugin", version: "1.0.0", description: "Test description", } registry.Register(plugin, "test-author", nil) meta, found := registry.GetMetadata("test-plugin") assert.True(t, found) assert.Equal(t, "test-plugin", meta.Name) assert.Equal(t, "1.0.0", meta.Version) assert.Equal(t, "test-author", meta.Author) assert.Equal(t, "plugin://test-plugin", meta.URL) assert.True(t, meta.Enabled) } func TestListPlugins(t *testing.T) { registry := NewPluginRegistry() for i := 0; i < 3; i++ { plugin := &MockPlugin{ name: fmt.Sprintf("plugin-%d", i), version: "1.0.0", } registry.Register(plugin, "author", nil) } plugins := registry.ListPlugins() assert.Equal(t, 3, len(plugins)) } func TestEnableDisablePlugin(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) assert.Equal(t, 1, registry.stats.EnabledPlugins) registry.DisablePlugin("test-plugin") assert.Equal(t, 0, registry.stats.EnabledPlugins) registry.EnablePlugin("test-plugin") assert.Equal(t, 1, registry.stats.EnabledPlugins) } func TestGetExecutionLog(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) registry.Execute("test-plugin", map[string]interface{}{}) registry.Execute("test-plugin", map[string]interface{}{}) log := registry.GetExecutionLog("test-plugin") assert.Equal(t, 2, len(log)) } func TestExecutionLogSuccess(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) registry.Execute("test-plugin", map[string]interface{}{}) log := registry.GetExecutionLog("test-plugin") assert.Equal(t, 1, len(log)) assert.True(t, log[0].Success) assert.Nil(t, log[0].Error) } func TestExecutionLogFailure(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{ name: "test-plugin", version: "1.0.0", shouldFail: true, } registry.Register(plugin, "author", nil) registry.Execute("test-plugin", map[string]interface{}{}) log := registry.GetExecutionLog("test-plugin") assert.Equal(t, 1, len(log)) assert.False(t, log[0].Success) assert.NotNil(t, log[0].Error) } func TestGetStats(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) registry.Execute("test-plugin", map[string]interface{}{}) registry.Execute("test-plugin", map[string]interface{}{}) stats := registry.GetStats() assert.Equal(t, 1, stats.TotalPlugins) assert.Equal(t, 2, stats.TotalExecutions) assert.Equal(t, 2, stats.SuccessfulExecutions) } func TestResolvePluginURL(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) resolved, err := registry.ResolvePluginURL("plugin://test-plugin") assert.NoError(t, err) assert.Equal(t, "test-plugin", resolved.Name()) } func TestResolvePluginURLInvalid(t *testing.T) { registry := NewPluginRegistry() _, err := registry.ResolvePluginURL("http://example.com") assert.Error(t, err) } func TestResolvePluginURLNotFound(t *testing.T) { registry := NewPluginRegistry() _, err := registry.ResolvePluginURL("plugin://nonexistent") assert.Error(t, err) } func TestClear(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) assert.Equal(t, 1, registry.stats.TotalPlugins) registry.Clear() assert.Equal(t, 0, registry.stats.TotalPlugins) } func TestIsPluginURL(t *testing.T) { assert.True(t, IsPluginURL("plugin://test")) assert.False(t, IsPluginURL("http://test")) assert.False(t, IsPluginURL("file://test")) } func TestExtractPluginName(t *testing.T) { name := ExtractPluginName("plugin://test-plugin") assert.Equal(t, "test-plugin", name) name = ExtractPluginName("http://test") assert.Equal(t, "", name) } func TestExecutionTiming(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{ name: "test-plugin", version: "1.0.0", shouldWait: 50 * time.Millisecond, } registry.Register(plugin, "author", nil) registry.Execute("test-plugin", map[string]interface{}{}) log := registry.GetExecutionLog("test-plugin") assert.Greater(t, log[0].Duration, 40*time.Millisecond) } func TestMultiplePlugins(t *testing.T) { registry := NewPluginRegistry() for i := 0; i < 5; i++ { plugin := &MockPlugin{ name: fmt.Sprintf("plugin-%d", i), version: "1.0.0", } registry.Register(plugin, "author", nil) } assert.Equal(t, 5, registry.stats.TotalPlugins) for i := 0; i < 5; i++ { registry.Execute(fmt.Sprintf("plugin-%d", i), map[string]interface{}{}) } stats := registry.GetStats() assert.Equal(t, 5, stats.TotalExecutions) } func TestPluginConfig(t *testing.T) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} config := map[string]interface{}{ "setting1": "value1", "setting2": 42, } registry.Register(plugin, "author", config) meta, _ := registry.GetMetadata("test-plugin") assert.NotNil(t, meta.Config) assert.Equal(t, "value1", meta.Config["setting1"]) assert.Equal(t, 42, meta.Config["setting2"]) } func BenchmarkRegisterPlugin(b *testing.B) { registry := NewPluginRegistry() for i := 0; i < b.N; i++ { plugin := &MockPlugin{ name: fmt.Sprintf("plugin-%d", i), version: "1.0.0", } registry.Register(plugin, "author", nil) } } func BenchmarkExecutePlugin(b *testing.B) { registry := NewPluginRegistry() plugin := &MockPlugin{name: "test-plugin", version: "1.0.0"} registry.Register(plugin, "author", nil) b.ResetTimer() for i := 0; i < b.N; i++ { registry.Execute("test-plugin", map[string]interface{}{}) } }