From 8d2c00c107b2e62d0798bb2325224c178889e662 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Wed, 17 Jun 2026 03:46:30 +0800 Subject: [PATCH] feat(plugin-config): update default plugin `Enabled` behavior to false and expand test coverage - Changed default plugin `Enabled` state from `true` to `false` across configurations, runtime logic, and YAML defaults. - Added helper function `enabledPluginConfigs` for generating plugin configs with `Enabled` set explicitly. - Expanded unit tests in `pluginhost`, `config`, and `management` to validate behavior changes for disabled plugins, default settings, and skipped load scenarios. --- .../api/handlers/management/plugin_store.go | 2 +- internal/api/handlers/management/plugins.go | 8 +--- .../api/handlers/management/plugins_test.go | 2 +- internal/config/config.go | 4 +- internal/config/plugin_config_test.go | 6 +-- internal/pluginhost/config.go | 6 +-- internal/pluginhost/config_test.go | 16 +++++++ internal/pluginhost/host_test.go | 44 +++++++++++++++++++ 8 files changed, 72 insertions(+), 16 deletions(-) diff --git a/internal/api/handlers/management/plugin_store.go b/internal/api/handlers/management/plugin_store.go index 5ea1d874..3872a3ff 100644 --- a/internal/api/handlers/management/plugin_store.go +++ b/internal/api/handlers/management/plugin_store.go @@ -548,7 +548,7 @@ func pluginLocalStatuses(pluginsEnabled bool, pluginsDir string, configs map[str status.Registered = true status.InstalledVersion = strings.TrimSpace(info.Metadata.Version) if _, configured := configs[info.ID]; !configured && !status.Enabled { - status.Enabled = true + status.Enabled = false } statuses[info.ID] = status } diff --git a/internal/api/handlers/management/plugins.go b/internal/api/handlers/management/plugins.go index 631e61fb..72a1a7d9 100644 --- a/internal/api/handlers/management/plugins.go +++ b/internal/api/handlers/management/plugins.go @@ -90,7 +90,7 @@ func (h *Handler) ListPlugins(c *gin.Context) { entries[file.ID] = pluginListEntry{ ID: htmlsanitize.String(file.ID), Path: htmlsanitize.String(file.Path), - Enabled: true, + Enabled: false, ConfigFields: []pluginConfigFieldInfo{}, Menus: []pluginMenuInfo{}, } @@ -118,10 +118,6 @@ func (h *Handler) ListPlugins(c *gin.Context) { entry.ConfigFields = pluginConfigFields(info.Metadata.ConfigFields) entry.Menus = pluginMenus(info.Menus) entry.Metadata = pluginMetadata(info.Metadata) - _, configured := configs[info.ID] - if !configured && !entry.Enabled { - entry.Enabled = true - } entries[info.ID] = entry } } @@ -397,7 +393,7 @@ func normalizedPluginsDir(dir string) string { func pluginInstanceEnabled(item config.PluginInstanceConfig) bool { if item.Enabled == nil { - return true + return false } return *item.Enabled } diff --git a/internal/api/handlers/management/plugins_test.go b/internal/api/handlers/management/plugins_test.go index a07d54d8..4a790c15 100644 --- a/internal/api/handlers/management/plugins_test.go +++ b/internal/api/handlers/management/plugins_test.go @@ -158,7 +158,7 @@ func TestListPluginsIncludesScannedAndConfiguredPlugins(t *testing.T) { t.Fatalf("unregistered plugin entry has runtime fields: %#v", item) } } - if got, ok := entries["scanned"]; !ok || got.Configured || !got.Enabled || got.EffectiveEnabled || got.Path == "" { + if got, ok := entries["scanned"]; !ok || got.Configured || got.Enabled || got.EffectiveEnabled || got.Path == "" { t.Fatalf("scanned entry = %#v, exists=%v", got, ok) } if got, ok := entries["configured-only"]; !ok || !got.Configured || got.Enabled || got.EffectiveEnabled || got.Path != "" { diff --git a/internal/config/config.go b/internal/config/config.go index 4f6fb155..9f8ba44e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -172,7 +172,7 @@ type PluginsConfig struct { // PluginInstanceConfig stores host-owned plugin settings and the original plugin YAML subtree. type PluginInstanceConfig struct { - // Enabled toggles this plugin instance. Nil is normalized to true during YAML parsing. + // Enabled toggles this plugin instance. Nil is normalized to false during YAML parsing. Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"` // Priority controls plugin startup and routing order. Priority int `yaml:"priority,omitempty" json:"priority,omitempty"` @@ -187,7 +187,7 @@ func (c *PluginInstanceConfig) UnmarshalYAML(value *yaml.Node) error { } c.Priority = 0 - defaultEnabled := true + defaultEnabled := false c.Enabled = &defaultEnabled if value == nil || value.Kind == 0 { diff --git a/internal/config/plugin_config_test.go b/internal/config/plugin_config_test.go index ddf1c7a6..6a883e41 100644 --- a/internal/config/plugin_config_test.go +++ b/internal/config/plugin_config_test.go @@ -66,10 +66,10 @@ plugins: t.Fatal("Plugins.Configs[\"sample\"] missing") } if plugin.Enabled == nil { - t.Fatal("Plugin.Enabled = nil, want true pointer") + t.Fatal("Plugin.Enabled = nil, want false pointer") } - if !*plugin.Enabled { - t.Fatal("Plugin.Enabled = false, want true") + if *plugin.Enabled { + t.Fatal("Plugin.Enabled = true, want false") } if plugin.Priority != 0 { t.Fatalf("Plugin.Priority = %d, want 0", plugin.Priority) diff --git a/internal/pluginhost/config.go b/internal/pluginhost/config.go index 9fe1a05e..be339637 100644 --- a/internal/pluginhost/config.go +++ b/internal/pluginhost/config.go @@ -10,7 +10,7 @@ import ( "gopkg.in/yaml.v3" ) -var defaultRuntimeConfigYAML = []byte("enabled: true\npriority: 0\n") +var defaultRuntimeConfigYAML = []byte("enabled: false\npriority: 0\n") type runtimeConfig struct { Enabled bool @@ -48,7 +48,7 @@ func runtimeConfigFromConfig(cfg *config.Config) runtimeConfig { for _, id := range ids { item := cfg.Plugins.Configs[id] - enabled := true + enabled := false if item.Enabled != nil { enabled = *item.Enabled } @@ -66,7 +66,7 @@ func runtimeConfigFromConfig(cfg *config.Config) runtimeConfig { func defaultRuntimeItemConfig(id string) runtimeItemConfig { return runtimeItemConfig{ ID: id, - Enabled: true, + Enabled: false, Priority: 0, ConfigYAML: append([]byte(nil), defaultRuntimeConfigYAML...), } diff --git a/internal/pluginhost/config_test.go b/internal/pluginhost/config_test.go index ddd96df2..adabfe1f 100644 --- a/internal/pluginhost/config_test.go +++ b/internal/pluginhost/config_test.go @@ -33,3 +33,19 @@ func TestRuntimeConfigYAMLAddsHostDefaultsToRawPluginConfig(t *testing.T) { } } } + +func TestRuntimeConfigYAMLDefaultsEnabledFalse(t *testing.T) { + item := config.PluginInstanceConfig{ + Priority: 3, + } + + got := string(runtimeConfigYAML(item, false)) + for _, want := range []string{ + "enabled: false", + "priority: 3", + } { + if !strings.Contains(got, want) { + t.Fatalf("runtimeConfigYAML() missing %q in:\n%s", want, got) + } + } +} diff --git a/internal/pluginhost/host_test.go b/internal/pluginhost/host_test.go index 888ac1f7..bb6bed16 100644 --- a/internal/pluginhost/host_test.go +++ b/internal/pluginhost/host_test.go @@ -16,6 +16,15 @@ import ( "github.com/tidwall/gjson" ) +func enabledPluginConfigs(ids ...string) map[string]config.PluginInstanceConfig { + enabled := true + configs := make(map[string]config.PluginInstanceConfig, len(ids)) + for _, id := range ids { + configs[id] = config.PluginInstanceConfig{Enabled: &enabled} + } + return configs +} + func TestHostApplyConfig_DisabledGlobalSkipsSnapshot(t *testing.T) { loader := newTestSymbolLoader() h := NewForTest(loader) @@ -67,6 +76,30 @@ func TestHostApplyConfig_DisabledPluginSkipsCapability(t *testing.T) { } } +func TestHostApplyConfig_DefaultDisabledPluginSkipsLoad(t *testing.T) { + loader := newTestSymbolLoader() + plugin := &testPlugin{ + registerResult: validTestPlugin("alpha"), + reconfigureResult: validTestPlugin("alpha"), + } + loader.lookups["alpha"] = newTestSymbolLookup(plugin) + h := NewForTest(loader) + + h.ApplyConfig(context.Background(), &config.Config{ + Plugins: config.PluginsConfig{ + Enabled: true, + Dir: makePluginDir(t, "alpha"), + }, + }) + + if plugin.registerCalls != 0 || loader.openCalls != 0 { + t.Fatalf("calls = register %d open %d, want 0", plugin.registerCalls, loader.openCalls) + } + if len(h.Snapshot().records) != 0 { + t.Fatalf("Snapshot records = %d, want 0", len(h.Snapshot().records)) + } +} + func TestPluginLoadedTracksLoadedPluginAfterDisabled(t *testing.T) { disabled := false loader := newTestSymbolLoader() @@ -83,6 +116,7 @@ func TestPluginLoadedTracksLoadedPluginAfterDisabled(t *testing.T) { Plugins: config.PluginsConfig{ Enabled: true, Dir: pluginsDir, + Configs: enabledPluginConfigs("alpha"), }, }) @@ -136,6 +170,7 @@ func TestHostUnloadPluginTargetsOnlyRequestedPlugin(t *testing.T) { Plugins: config.PluginsConfig{ Enabled: true, Dir: makePluginDir(t, "alpha", "bravo"), + Configs: enabledPluginConfigs("alpha", "bravo"), }, } @@ -191,6 +226,7 @@ func TestHostApplyConfigRegistersPluginThinkingApplier(t *testing.T) { Plugins: config.PluginsConfig{ Enabled: true, Dir: makePluginDir(t, "alpha"), + Configs: enabledPluginConfigs("alpha"), }, } t.Cleanup(func() { @@ -240,6 +276,7 @@ func TestHostApplyConfigRegistersInterceptorOnlyPlugin(t *testing.T) { Plugins: config.PluginsConfig{ Enabled: true, Dir: makePluginDir(t, "alpha"), + Configs: enabledPluginConfigs("alpha"), }, }) @@ -282,6 +319,7 @@ func TestHostApplyConfigDispatchesInterceptorRPCMethods(t *testing.T) { Plugins: config.PluginsConfig{ Enabled: true, Dir: makePluginDir(t, "alpha"), + Configs: enabledPluginConfigs("alpha"), }, }) @@ -488,6 +526,7 @@ func TestHostApplyConfig_ReconfigureCalledOnReload(t *testing.T) { Plugins: config.PluginsConfig{ Enabled: true, Dir: makePluginDir(t, "alpha"), + Configs: enabledPluginConfigs("alpha"), }, } @@ -529,6 +568,7 @@ func TestRegisteredPluginsIncludesMetadataAndOAuthCapability(t *testing.T) { Plugins: config.PluginsConfig{ Enabled: true, Dir: makePluginDir(t, "alpha"), + Configs: enabledPluginConfigs("alpha"), }, }) @@ -589,6 +629,7 @@ func TestHostApplyConfig_PanicFusesPluginForProcessLifetime(t *testing.T) { Plugins: config.PluginsConfig{ Enabled: true, Dir: makePluginDir(t, "alpha"), + Configs: enabledPluginConfigs("alpha"), }, } @@ -674,6 +715,7 @@ func TestHostApplyConfigSerializesLifecycleCalls(t *testing.T) { Plugins: config.PluginsConfig{ Enabled: true, Dir: makePluginDir(t, "alpha"), + Configs: enabledPluginConfigs("alpha"), }, } @@ -896,6 +938,7 @@ func newBlockingOpenHost(t *testing.T) (*Host, *config.Config, <-chan struct{}, Plugins: config.PluginsConfig{ Enabled: true, Dir: makePluginDir(t, "alpha"), + Configs: enabledPluginConfigs("alpha"), }, } return h, cfg, openStarted, releaseOpen @@ -928,6 +971,7 @@ func newBlockingRegisterHost(t *testing.T) (*Host, *config.Config, <-chan struct Plugins: config.PluginsConfig{ Enabled: true, Dir: makePluginDir(t, "alpha"), + Configs: enabledPluginConfigs("alpha"), }, } return h, cfg, registerStarted, releaseRegister