@@ -2,6 +2,7 @@ package compare_test
22
33import (
44 "encoding/json"
5+ "fmt"
56 "slices"
67 "testing"
78
@@ -13,6 +14,7 @@ import (
1314 "github.com/operator-framework/operator-registry/alpha/property"
1415
1516 "github.com/operator-framework/operator-controller/internal/operator-controller/catalogmetadata/compare"
17+ "github.com/operator-framework/operator-controller/internal/operator-controller/features"
1618)
1719
1820func TestNewVersionRange (t * testing.T ) {
@@ -138,13 +140,13 @@ func TestByVersionAndRelease(t *testing.T) {
138140 t .Run ("all bundles valid" , func (t * testing.T ) {
139141 toSort := []declcfg.Bundle {b3_1 , b2 , b3_2 , b1 }
140142 slices .SortStableFunc (toSort , compare .ByVersionAndRelease )
141- assert .Equal (t , []declcfg.Bundle {b1 , b3_2 , b3_1 , b2 }, toSort )
143+ assert .Equal (t , []declcfg.Bundle {b1 , b3_2 , b3_1 , b2 }, toSort , "should sort descending: 1.0.0 > 1.0.0-alpha+2 > 1.0.0-alpha+1 > 0.0.1" )
142144 })
143145
144146 t .Run ("some bundles are missing version" , func (t * testing.T ) {
145147 toSort := []declcfg.Bundle {b3_1 , b4noVersion , b2 , b3_2 , b5empty , b1 }
146148 slices .SortStableFunc (toSort , compare .ByVersionAndRelease )
147- assert .Equal (t , []declcfg.Bundle {b1 , b3_2 , b3_1 , b2 , b4noVersion , b5empty }, toSort )
149+ assert .Equal (t , []declcfg.Bundle {b1 , b3_2 , b3_1 , b2 , b4noVersion , b5empty }, toSort , "bundles with invalid/missing versions should sort last" )
148150 })
149151}
150152
@@ -161,10 +163,67 @@ func TestByDeprecationFunc(t *testing.T) {
161163 c := declcfg.Bundle {Name : "c" }
162164 d := declcfg.Bundle {Name : "d" }
163165
164- assert .Equal (t , 0 , byDeprecation (a , b ))
165- assert .Equal (t , 0 , byDeprecation (b , a ))
166- assert .Equal (t , 1 , byDeprecation (a , c ))
167- assert .Equal (t , - 1 , byDeprecation (c , a ))
168- assert .Equal (t , 0 , byDeprecation (c , d ))
169- assert .Equal (t , 0 , byDeprecation (d , c ))
166+ assert .Equal (t , 0 , byDeprecation (a , b ), "both deprecated bundles are equal" )
167+ assert .Equal (t , 0 , byDeprecation (b , a ), "both deprecated bundles are equal" )
168+ assert .Equal (t , 1 , byDeprecation (a , c ), "deprecated bundle 'a' should sort after non-deprecated 'c'" )
169+ assert .Equal (t , - 1 , byDeprecation (c , a ), "non-deprecated bundle 'c' should sort before deprecated 'a'" )
170+ assert .Equal (t , 0 , byDeprecation (c , d ), "both non-deprecated bundles are equal" )
171+ assert .Equal (t , 0 , byDeprecation (d , c ), "both non-deprecated bundles are equal" )
172+ }
173+
174+ // TestByVersionAndRelease_WithCompositeVersionComparison tests the feature-gated hybrid comparison
175+ // for registry+v1 bundles. When the feature gate is enabled, Bundle.Compare() is called but returns
176+ // 0 for registry+v1 bundles (no explicit release field), so we fall back to build metadata parsing.
177+ // This maintains backward compatibility while preparing for future bundle formats with explicit release.
178+ func TestByVersionAndRelease_WithCompositeVersionComparison (t * testing.T ) {
179+ // Registry+v1 bundles: same version, different build metadata
180+ b1 := declcfg.Bundle {
181+ Name : "package1.v1.0.0+1" ,
182+ Properties : []property.Property {
183+ {Type : property .TypePackage , Value : json .RawMessage (`{"packageName": "package1", "version": "1.0.0+1"}` )},
184+ },
185+ }
186+ b2 := declcfg.Bundle {
187+ Name : "package1.v1.0.0+2" ,
188+ Properties : []property.Property {
189+ {Type : property .TypePackage , Value : json .RawMessage (`{"packageName": "package1", "version": "1.0.0+2"}` )},
190+ },
191+ }
192+
193+ prevEnabled := features .OperatorControllerFeatureGate .Enabled (features .CompositeVersionComparison )
194+ t .Cleanup (func () {
195+ require .NoError (t , features .OperatorControllerFeatureGate .Set (fmt .Sprintf ("%s=%t" , features .CompositeVersionComparison , prevEnabled )))
196+ })
197+
198+ t .Run ("feature gate disabled - uses build metadata only" , func (t * testing.T ) {
199+ require .NoError (t , features .OperatorControllerFeatureGate .Set (fmt .Sprintf ("%s=false" , features .CompositeVersionComparison )))
200+ result := compare .ByVersionAndRelease (b1 , b2 )
201+ assert .Positive (t , result , "should sort by build metadata: 1.0.0+2 > 1.0.0+1" )
202+ })
203+
204+ t .Run ("feature gate enabled - registry+v1 bundles still work via build metadata fallback" , func (t * testing.T ) {
205+ require .NoError (t , features .OperatorControllerFeatureGate .Set (fmt .Sprintf ("%s=true" , features .CompositeVersionComparison )))
206+ result := compare .ByVersionAndRelease (b1 , b2 )
207+ assert .Positive (t , result , "Bundle.Compare() returns 0, fallback to build metadata: 1.0.0+2 > 1.0.0+1" )
208+ })
209+
210+ t .Run ("bundles with explicit pkg.Release field" , func (t * testing.T ) {
211+ // Bundle with explicit release=1
212+ explicitR1 := declcfg.Bundle {
213+ Name : "pkg.v2.0.0-r1" ,
214+ Properties : []property.Property {
215+ {Type : property .TypePackage , Value : json .RawMessage (`{"packageName": "pkg", "version": "2.0.0", "release": "1"}` )},
216+ },
217+ }
218+ // Bundle with explicit release=2
219+ explicitR2 := declcfg.Bundle {
220+ Name : "pkg.v2.0.0-r2" ,
221+ Properties : []property.Property {
222+ {Type : property .TypePackage , Value : json .RawMessage (`{"packageName": "pkg", "version": "2.0.0", "release": "2"}` )},
223+ },
224+ }
225+
226+ result := compare .ByVersionAndRelease (explicitR1 , explicitR2 )
227+ assert .Positive (t , result , "2.0.0+release.2 > 2.0.0+release.1 (explicit release field)" )
228+ })
170229}
0 commit comments