diff --git a/alpha/declcfg/declcfg_to_model.go b/alpha/declcfg/declcfg_to_model.go index 42b86e8a4..060bc9152 100644 --- a/alpha/declcfg/declcfg_to_model.go +++ b/alpha/declcfg/declcfg_to_model.go @@ -18,6 +18,10 @@ func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) { return nil, fmt.Errorf("config contains package with no name") } + if _, ok := mpkgs[p.Name]; ok { + return nil, fmt.Errorf("duplicate package %q", p.Name) + } + mpkg := &model.Package{ Name: p.Name, Description: p.Description, @@ -44,6 +48,10 @@ func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) { return nil, fmt.Errorf("package %q contains channel with no name", c.Package) } + if _, ok := mpkg.Channels[c.Name]; ok { + return nil, fmt.Errorf("package %q has duplicate channel %q", c.Package, c.Name) + } + mch := &model.Channel{ Package: mpkg, Name: c.Name, @@ -75,6 +83,10 @@ func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) { } } + // packageBundles tracks the set of bundle names for each package + // and is used to detect duplicate bundles. + packageBundles := map[string]sets.String{} + for _, b := range cfg.Bundles { if b.Package == "" { return nil, fmt.Errorf("package name must be set for bundle %q", b.Name) @@ -84,6 +96,16 @@ func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) { return nil, fmt.Errorf("unknown package %q for bundle %q", b.Package, b.Name) } + bundles, ok := packageBundles[b.Package] + if !ok { + bundles = sets.NewString() + } + if bundles.Has(b.Name) { + return nil, fmt.Errorf("package %q has duplicate bundle %q", b.Package, b.Name) + } + bundles.Insert(b.Name) + packageBundles[b.Package] = bundles + props, err := property.Parse(b.Properties) if err != nil { return nil, fmt.Errorf("parse properties for bundle %q: %v", b.Name, err) diff --git a/alpha/declcfg/declcfg_to_model_test.go b/alpha/declcfg/declcfg_to_model_test.go index 242aa6c5c..34c7bc020 100644 --- a/alpha/declcfg/declcfg_to_model_test.go +++ b/alpha/declcfg/declcfg_to_model_test.go @@ -203,6 +203,42 @@ func TestConvertToModel(t *testing.T) { Bundles: []Bundle{newTestBundle("foo", "0.1.0")}, }, }, + { + name: "Error/DuplicatePackage", + assertion: hasError(`duplicate package "foo"`), + cfg: DeclarativeConfig{ + Packages: []Package{ + newTestPackage("foo", "alpha", svgSmallCircle), + newTestPackage("foo", "alpha", svgSmallCircle), + }, + Channels: []Channel{newTestChannel("foo", "alpha", ChannelEntry{Name: "foo.v0.1.0"})}, + Bundles: []Bundle{newTestBundle("foo", "0.1.0")}, + }, + }, + { + name: "Error/DuplicateChannel", + assertion: hasError(`package "foo" has duplicate channel "alpha"`), + cfg: DeclarativeConfig{ + Packages: []Package{newTestPackage("foo", "alpha", svgSmallCircle)}, + Channels: []Channel{ + newTestChannel("foo", "alpha", ChannelEntry{Name: "foo.v0.1.0"}), + newTestChannel("foo", "alpha", ChannelEntry{Name: "foo.v0.1.0"}), + }, + Bundles: []Bundle{newTestBundle("foo", "0.1.0")}, + }, + }, + { + name: "Error/DuplicateBundle", + assertion: hasError(`package "foo" has duplicate bundle "foo.v0.1.0"`), + cfg: DeclarativeConfig{ + Packages: []Package{newTestPackage("foo", "alpha", svgSmallCircle)}, + Channels: []Channel{newTestChannel("foo", "alpha", ChannelEntry{Name: "foo.v0.1.0"})}, + Bundles: []Bundle{ + newTestBundle("foo", "0.1.0"), + newTestBundle("foo", "0.1.0"), + }, + }, + }, { name: "Success/ValidModel", assertion: require.NoError, @@ -242,7 +278,7 @@ func hasError(expectedError string) require.ErrorAssertionFunc { if stdt, ok := t.(*testing.T); ok { stdt.Helper() } - if actualError.Error() == expectedError { + if actualError != nil && actualError.Error() == expectedError { return } t.Errorf("expected error to be `%s`, got `%s`", expectedError, actualError)