package plugin import ( "testing" "git.nakama.town/fmartingr/butterrobot/internal/model" ) // Mock plugin for testing type testPlugin struct { BasePlugin } func (p *testPlugin) OnMessage(msg *model.Message, config map[string]interface{}, cache model.CacheInterface) []*model.MessageAction { return []*model.MessageAction{ { Type: model.ActionSendMessage, Message: &model.Message{ Text: "test response", Chat: msg.Chat, Channel: msg.Channel, }, }, } } func TestGetAvailablePluginIDs(t *testing.T) { // Clear registry before test ClearRegistry() // Register test plugins testPlugin1 := &testPlugin{ BasePlugin: BasePlugin{ ID: "test.plugin1", Name: "Test Plugin 1", }, } testPlugin2 := &testPlugin{ BasePlugin: BasePlugin{ ID: "test.plugin2", Name: "Test Plugin 2", }, } Register(testPlugin1) Register(testPlugin2) // Test GetAvailablePluginIDs pluginIDs := GetAvailablePluginIDs() if len(pluginIDs) != 2 { t.Errorf("Expected 2 plugin IDs, got %d", len(pluginIDs)) } // Check that both plugin IDs are present found1, found2 := false, false for _, id := range pluginIDs { if id == "test.plugin1" { found1 = true } if id == "test.plugin2" { found2 = true } } if !found1 { t.Errorf("Expected to find test.plugin1 in plugin IDs") } if !found2 { t.Errorf("Expected to find test.plugin2 in plugin IDs") } } func TestEnableAllPluginsProcessingLogic(t *testing.T) { // Clear registry before test ClearRegistry() // Register test plugins testPlugin1 := &testPlugin{ BasePlugin: BasePlugin{ ID: "ping", Name: "Ping Plugin", }, } testPlugin2 := &testPlugin{ BasePlugin: BasePlugin{ ID: "echo", Name: "Echo Plugin", }, } testPlugin3 := &testPlugin{ BasePlugin: BasePlugin{ ID: "help", Name: "Help Plugin", }, } Register(testPlugin1) Register(testPlugin2) Register(testPlugin3) t.Run("EnableAllPlugins false - only explicitly enabled plugins", func(t *testing.T) { // Create a channel with EnableAllPlugins = false and only some plugins enabled channel := &model.Channel{ ID: 1, Platform: "telegram", PlatformChannelID: "123456", Enabled: true, EnableAllPlugins: false, Plugins: map[string]*model.ChannelPlugin{ "ping": { ID: 1, ChannelID: 1, PluginID: "ping", Enabled: true, Config: map[string]interface{}{"key": "value"}, }, "echo": { ID: 2, ChannelID: 1, PluginID: "echo", Enabled: false, // Disabled Config: map[string]interface{}{}, }, // help plugin not configured }, } // Simulate the plugin processing logic from handleMessage var pluginsToProcess []string if channel.EnableAllPlugins { pluginsToProcess = GetAvailablePluginIDs() } else { for pluginID := range channel.Plugins { if channel.HasEnabledPlugin(pluginID) { pluginsToProcess = append(pluginsToProcess, pluginID) } } } // Should only have "ping" since echo is disabled and help is not configured if len(pluginsToProcess) != 1 { t.Errorf("Expected 1 plugin to process, got %d: %v", len(pluginsToProcess), pluginsToProcess) } if len(pluginsToProcess) > 0 && pluginsToProcess[0] != "ping" { t.Errorf("Expected ping plugin to be processed, got %s", pluginsToProcess[0]) } }) t.Run("EnableAllPlugins true - all registered plugins", func(t *testing.T) { // Create a channel with EnableAllPlugins = true channel := &model.Channel{ ID: 1, Platform: "telegram", PlatformChannelID: "123456", Enabled: true, EnableAllPlugins: true, Plugins: map[string]*model.ChannelPlugin{ "ping": { ID: 1, ChannelID: 1, PluginID: "ping", Enabled: true, Config: map[string]interface{}{"key": "value"}, }, "echo": { ID: 2, ChannelID: 1, PluginID: "echo", Enabled: false, // Disabled, but should still be processed Config: map[string]interface{}{}, }, // help plugin not configured, but should still be processed }, } // Simulate the plugin processing logic from handleMessage var pluginsToProcess []string if channel.EnableAllPlugins { pluginsToProcess = GetAvailablePluginIDs() } else { for pluginID := range channel.Plugins { if channel.HasEnabledPlugin(pluginID) { pluginsToProcess = append(pluginsToProcess, pluginID) } } } // Should have all 3 registered plugins if len(pluginsToProcess) != 3 { t.Errorf("Expected 3 plugins to process, got %d: %v", len(pluginsToProcess), pluginsToProcess) } // Check that all plugins are included expectedPlugins := map[string]bool{"ping": false, "echo": false, "help": false} for _, pluginID := range pluginsToProcess { if _, exists := expectedPlugins[pluginID]; exists { expectedPlugins[pluginID] = true } else { t.Errorf("Unexpected plugin in processing list: %s", pluginID) } } for pluginID, found := range expectedPlugins { if !found { t.Errorf("Expected plugin %s to be in processing list", pluginID) } } }) t.Run("Plugin configuration handling", func(t *testing.T) { // Test the configuration logic from handleMessage channel := &model.Channel{ ID: 1, Platform: "telegram", PlatformChannelID: "123456", Enabled: true, EnableAllPlugins: true, Plugins: map[string]*model.ChannelPlugin{ "ping": { ID: 1, ChannelID: 1, PluginID: "ping", Enabled: true, Config: map[string]interface{}{"configured": "value"}, }, }, } testCases := []struct { pluginID string expectedConfig map[string]interface{} }{ { pluginID: "ping", expectedConfig: map[string]interface{}{"configured": "value"}, }, { pluginID: "echo", // Not explicitly configured expectedConfig: map[string]interface{}{}, }, } for _, tc := range testCases { // Simulate the config retrieval logic from handleMessage var config map[string]interface{} if channelPlugin, exists := channel.Plugins[tc.pluginID]; exists { config = channelPlugin.Config } else { config = make(map[string]interface{}) } if len(config) != len(tc.expectedConfig) { t.Errorf("Plugin %s: expected config length %d, got %d", tc.pluginID, len(tc.expectedConfig), len(config)) } for key, expectedValue := range tc.expectedConfig { if actualValue, exists := config[key]; !exists || actualValue != expectedValue { t.Errorf("Plugin %s: expected config[%s] = %v, got %v", tc.pluginID, key, expectedValue, actualValue) } } } }) } func TestPluginRegistry(t *testing.T) { // Clear registry before test ClearRegistry() testPlugin := &testPlugin{ BasePlugin: BasePlugin{ ID: "test.registry", Name: "Test Registry Plugin", }, } t.Run("Register and Get plugin", func(t *testing.T) { Register(testPlugin) retrieved, err := Get("test.registry") if err != nil { t.Errorf("Failed to get registered plugin: %v", err) } if retrieved.GetID() != "test.registry" { t.Errorf("Expected plugin ID 'test.registry', got '%s'", retrieved.GetID()) } }) t.Run("Get nonexistent plugin", func(t *testing.T) { _, err := Get("nonexistent.plugin") if err == nil { t.Errorf("Expected error when getting nonexistent plugin, got nil") } if err != model.ErrPluginNotFound { t.Errorf("Expected ErrPluginNotFound, got %v", err) } }) t.Run("GetAvailablePlugins", func(t *testing.T) { plugins := GetAvailablePlugins() if len(plugins) != 1 { t.Errorf("Expected 1 plugin in registry, got %d", len(plugins)) } if plugin, exists := plugins["test.registry"]; !exists { t.Errorf("Expected to find test.registry in available plugins") } else if plugin.GetID() != "test.registry" { t.Errorf("Expected plugin ID 'test.registry', got '%s'", plugin.GetID()) } }) t.Run("ClearRegistry", func(t *testing.T) { ClearRegistry() plugins := GetAvailablePlugins() if len(plugins) != 0 { t.Errorf("Expected 0 plugins after clearing registry, got %d", len(plugins)) } _, err := Get("test.registry") if err == nil { t.Errorf("Expected error when getting plugin after clearing registry, got nil") } }) }