diff --git a/internal/getproviders/types.go b/internal/getproviders/types.go index 0f8bc7d7b..03d13c2b9 100644 --- a/internal/getproviders/types.go +++ b/internal/getproviders/types.go @@ -395,8 +395,16 @@ func VersionConstraintsString(spec VersionConstraints) string { // lock files. Therefore the canonical forms produced here are a compatibility // constraint for the dependency lock file parser. + // Keep track of selection specifications which have been seen, so that we + // don't repeat the same constraint multiple times. + rendered := make(map[constraints.SelectionSpec]struct{}) + var b strings.Builder for i, sel := range spec { + // If we've already rendered this selection spec, skip it. + if _, exists := rendered[sel]; exists { + continue + } if i > 0 { b.WriteString(", ") } @@ -451,6 +459,9 @@ func VersionConstraintsString(spec VersionConstraints) string { if sel.Boundary.Metadata != "" { b.WriteString("+" + boundary.Metadata) } + + // Mark this selection spec as rendered. + rendered[sel] = struct{}{} } return b.String() } diff --git a/internal/getproviders/types_test.go b/internal/getproviders/types_test.go new file mode 100644 index 000000000..5004e8d2c --- /dev/null +++ b/internal/getproviders/types_test.go @@ -0,0 +1,66 @@ +package getproviders + +import ( + "testing" +) + +func TestVersionConstraintsString(t *testing.T) { + testCases := map[string]struct { + spec VersionConstraints + want string + }{ + "exact": { + MustParseVersionConstraints("1.2.3"), + "1.2.3", + }, + "prerelease": { + MustParseVersionConstraints("1.2.3-beta"), + "1.2.3-beta", + }, + "metadata": { + MustParseVersionConstraints("1.2.3+foo.bar"), + "1.2.3+foo.bar", + }, + "prerelease and metadata": { + MustParseVersionConstraints("1.2.3-beta+foo.bar"), + "1.2.3-beta+foo.bar", + }, + "major only": { + MustParseVersionConstraints(">= 3"), + ">= 3.0.0", + }, + "major only with pessimistic operator": { + MustParseVersionConstraints("~> 3"), + "~> 3.0", + }, + "pessimistic minor": { + MustParseVersionConstraints("~> 3.0"), + "~> 3.0", + }, + "pessimistic patch": { + MustParseVersionConstraints("~> 3.0.0"), + "~> 3.0.0", + }, + "other operators": { + MustParseVersionConstraints("> 1.0.0, < 1.0.0, >= 1.0.0, <= 1.0.0, != 1.0.0"), + "> 1.0.0, < 1.0.0, >= 1.0.0, <= 1.0.0, != 1.0.0", + }, + "multiple": { + MustParseVersionConstraints(">= 3.0, < 4.0"), + ">= 3.0.0, < 4.0.0", + }, + "duplicates removed": { + MustParseVersionConstraints(">= 1.2.3, 1.2.3, ~> 1.2, 1.2.3"), + ">= 1.2.3, 1.2.3, ~> 1.2", + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got := VersionConstraintsString(tc.spec) + + if got != tc.want { + t.Errorf("wrong\n got: %q\nwant: %q", got, tc.want) + } + }) + } +}