diff --git a/internal/plugin/metadata.go b/internal/plugin/metadata.go index 111c0599f..15a954037 100644 --- a/internal/plugin/metadata.go +++ b/internal/plugin/metadata.go @@ -54,7 +54,7 @@ func (m Metadata) Validate() error { var errs []error if !validPluginName.MatchString(m.Name) { - errs = append(errs, fmt.Errorf("invalid name")) + errs = append(errs, fmt.Errorf("invalid name %q: must contain only a-z, A-Z, 0-9, _ and -", m.Name)) } if m.APIVersion == "" { diff --git a/internal/plugin/metadata_legacy.go b/internal/plugin/metadata_legacy.go index 3cd1a50cd..1b80e6196 100644 --- a/internal/plugin/metadata_legacy.go +++ b/internal/plugin/metadata_legacy.go @@ -69,7 +69,7 @@ type MetadataLegacy struct { func (m *MetadataLegacy) Validate() error { if !validPluginName.MatchString(m.Name) { - return fmt.Errorf("invalid plugin name %q: must contain only a-z, A-Z, 0-9, _ and -", m.Name) + return fmt.Errorf("invalid name %q: must contain only a-z, A-Z, 0-9, _ and -", m.Name) } m.Usage = sanitizeString(m.Usage) diff --git a/internal/plugin/metadata_legacy_test.go b/internal/plugin/metadata_legacy_test.go index 1ee23739c..79d57211c 100644 --- a/internal/plugin/metadata_legacy_test.go +++ b/internal/plugin/metadata_legacy_test.go @@ -25,23 +25,8 @@ func TestMetadataLegacyValidate_PluginName(t *testing.T) { pluginName string shouldPass bool }{ - // Valid names - {"lowercase", "myplugin", true}, - {"uppercase", "MYPLUGIN", true}, - {"mixed case", "MyPlugin", true}, - {"with digits", "plugin123", true}, - {"with hyphen", "my-plugin", true}, - {"with underscore", "my_plugin", true}, - {"mixed chars", "my-awesome_plugin_123", true}, - - // Invalid names - {"empty", "", false}, - {"space", "my plugin", false}, - {"colon", "Name:", false}, - {"period", "my.plugin", false}, - {"slash", "my/plugin", false}, - {"dollar", "$plugin", false}, - {"unicode", "plügîn", false}, + {"valid name", "my-plugin", true}, + {"invalid with space", "my plugin", false}, } for _, tt := range tests { diff --git a/internal/plugin/metadata_test.go b/internal/plugin/metadata_test.go index 28bc4cf51..0c699b00f 100644 --- a/internal/plugin/metadata_test.go +++ b/internal/plugin/metadata_test.go @@ -66,8 +66,8 @@ func TestValidatePluginData(t *testing.T) { } else if !item.pass && err == nil { t.Errorf("expected case %d to fail", i) } - if !item.pass && err.Error() != item.errString { - t.Errorf("index [%d]: expected the following error: %s, but got: %s", i, item.errString, err.Error()) + if !item.pass && !strings.Contains(err.Error(), item.errString) { + t.Errorf("index [%d]: expected error to contain: %s, but got: %s", i, item.errString, err.Error()) } } } diff --git a/internal/plugin/plugin_test.go b/internal/plugin/plugin_test.go index b6c2245ff..d911942b9 100644 --- a/internal/plugin/plugin_test.go +++ b/internal/plugin/plugin_test.go @@ -21,6 +21,44 @@ import ( "helm.sh/helm/v4/internal/plugin/schema" ) +func TestValidPluginName(t *testing.T) { + tests := []struct { + name string + pluginName string + shouldPass bool + }{ + // Valid names + {"lowercase", "myplugin", true}, + {"uppercase", "MYPLUGIN", true}, + {"mixed case", "MyPlugin", true}, + {"with digits", "plugin123", true}, + {"with hyphen", "my-plugin", true}, + {"with underscore", "my_plugin", true}, + {"mixed chars", "my-awesome_plugin_123", true}, + + // Invalid names + {"empty", "", false}, + {"space", "my plugin", false}, + {"colon", "plugin:", false}, + {"period", "my.plugin", false}, + {"slash", "my/plugin", false}, + {"dollar", "$plugin", false}, + {"unicode", "plügîn", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + matches := validPluginName.MatchString(tt.pluginName) + if tt.shouldPass && !matches { + t.Errorf("expected %q to match validPluginName regex", tt.pluginName) + } + if !tt.shouldPass && matches { + t.Errorf("expected %q to not match validPluginName regex", tt.pluginName) + } + }) + } +} + func mockSubprocessCLIPlugin(t *testing.T, pluginName string) *SubprocessPluginRuntime { t.Helper()