@@ -344,6 +344,108 @@ func TestClusterExtensionUpgradeShowsInstalledBundleDeprecation(t *testing.T) {
344344 require .NoError (t , cl .DeleteAllOf (ctx , & ocv1.ClusterExtension {}))
345345}
346346
347+ // TestClusterExtensionUpgradeFromDeprecatedBundleClearsDeprecation verifies that after
348+ // a successful upgrade from a deprecated bundle to a non-deprecated bundle, the deprecation
349+ // conditions are updated in the SAME reconciliation cycle (no stale conditions).
350+ //
351+ // Scenario:
352+ // - Bundle v1.0.1 is installed and deprecated in the catalog
353+ // - Bundle v1.0.3 is resolved (not deprecated) and the applier succeeds (rolloutSucceeded=true)
354+ // - After the apply, BundleDeprecated should be False (reflecting the newly installed v1.0.3)
355+ // - Deprecated rollup should also be False
356+ //
357+ // This is the regression test for the bug where deprecation conditions remained stale
358+ // (showing the old deprecated bundle) after a successful upgrade until the next reconciliation.
359+ func TestClusterExtensionUpgradeFromDeprecatedBundleClearsDeprecation (t * testing.T ) {
360+ ctx := context .Background ()
361+ pkgName := fmt .Sprintf ("upgrade-clear-%s" , rand .String (6 ))
362+ installedBundleName := fmt .Sprintf ("%s.v1.0.1" , pkgName )
363+ resolvedBundleName := fmt .Sprintf ("%s.v1.0.3" , pkgName )
364+ deprecationMessage := fmt .Sprintf ("%s is deprecated. Uninstall and install v1.0.3 for support." , installedBundleName )
365+
366+ cl , reconciler := newClientAndReconciler (t , func (d * deps ) {
367+ d .Resolver = resolve .Func (func (ctx context.Context , ext * ocv1.ClusterExtension , installedBundle * ocv1.BundleMetadata ) (* declcfg.Bundle , * bundle.VersionRelease , * declcfg.Deprecation , error ) {
368+ v := bundle.VersionRelease {
369+ Version : bsemver .MustParse ("1.0.3" ),
370+ }
371+ return & declcfg.Bundle {
372+ Name : resolvedBundleName ,
373+ Package : pkgName ,
374+ Image : fmt .Sprintf ("quay.io/example/%s@sha256:resolved103" , pkgName ),
375+ }, & v , & declcfg.Deprecation {
376+ Entries : []declcfg.DeprecationEntry {{
377+ Reference : declcfg.PackageScopedReference {
378+ Schema : declcfg .SchemaBundle ,
379+ Name : installedBundleName ,
380+ },
381+ Message : deprecationMessage ,
382+ }},
383+ }, nil
384+ })
385+ d .RevisionStatesGetter = & MockRevisionStatesGetter {
386+ RevisionStates : & controllers.RevisionStates {
387+ Installed : & controllers.RevisionMetadata {
388+ Package : pkgName ,
389+ BundleMetadata : ocv1.BundleMetadata {
390+ Name : installedBundleName ,
391+ Version : "1.0.1" ,
392+ },
393+ Image : fmt .Sprintf ("quay.io/example/%s@sha256:installed101" , pkgName ),
394+ },
395+ },
396+ }
397+ d .ImagePuller = & imageutil.MockPuller {ImageFS : fstest.MapFS {}}
398+ d .Applier = & MockApplier {installCompleted : true }
399+ })
400+
401+ extKey := types.NamespacedName {Name : fmt .Sprintf ("cluster-extension-test-%s" , rand .String (8 ))}
402+ clusterExtension := & ocv1.ClusterExtension {
403+ ObjectMeta : metav1.ObjectMeta {Name : extKey .Name },
404+ Spec : ocv1.ClusterExtensionSpec {
405+ Source : ocv1.SourceConfig {
406+ SourceType : "Catalog" ,
407+ Catalog : & ocv1.CatalogFilter {PackageName : pkgName },
408+ },
409+ Namespace : "default" ,
410+ ServiceAccount : ocv1.ServiceAccountReference {Name : "default" },
411+ },
412+ }
413+ require .NoError (t , cl .Create (ctx , clusterExtension ))
414+
415+ res , err := reconciler .Reconcile (ctx , ctrl.Request {NamespacedName : extKey })
416+ require .Equal (t , ctrl.Result {}, res )
417+ require .NoError (t , err )
418+
419+ require .NoError (t , cl .Get (ctx , extKey , clusterExtension ))
420+
421+ // After a successful upgrade to v1.0.3, deprecation should reflect the NEW bundle
422+ bundleCond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeBundleDeprecated )
423+ require .NotNil (t , bundleCond )
424+ require .Equal (t , metav1 .ConditionFalse , bundleCond .Status , "newly installed bundle v1.0.3 is NOT deprecated" )
425+ require .Equal (t , ocv1 .ReasonNotDeprecated , bundleCond .Reason )
426+
427+ deprecatedCond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeDeprecated )
428+ require .NotNil (t , deprecatedCond )
429+ require .Equal (t , metav1 .ConditionFalse , deprecatedCond .Status , "no deprecation exists after upgrade" )
430+ require .Equal (t , ocv1 .ReasonNotDeprecated , deprecatedCond .Reason )
431+
432+ pkgCond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypePackageDeprecated )
433+ require .NotNil (t , pkgCond )
434+ require .Equal (t , metav1 .ConditionFalse , pkgCond .Status )
435+
436+ channelCond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeChannelDeprecated )
437+ require .NotNil (t , channelCond )
438+ require .Equal (t , metav1 .ConditionFalse , channelCond .Status )
439+
440+ // Verify the installed bundle IS v1.0.3
441+ require .NotNil (t , clusterExtension .Status .Install )
442+ require .Equal (t , resolvedBundleName , clusterExtension .Status .Install .Bundle .Name )
443+ require .Equal (t , "1.0.3" , clusterExtension .Status .Install .Bundle .Version )
444+
445+ verifyInvariants (ctx , t , reconciler .Client , clusterExtension )
446+ require .NoError (t , cl .DeleteAllOf (ctx , & ocv1.ClusterExtension {}))
447+ }
448+
347449// TestClusterExtensionResolutionFailsWithoutCatalogDeprecationData verifies deprecation status handling when catalog data is unavailable.
348450//
349451// Scenario:
0 commit comments