@@ -347,6 +347,80 @@ def test_all_operators_filtered_skips_build(self, mock_find):
347347 self .assertEqual (bundle_nvrs , ["existing-bundle-1" ])
348348 self .assertEqual (operator_nvrs , [])
349349
350+ @patch .object (PrepareReleaseLPPipeline , '_find_existing_bundles' , new_callable = AsyncMock )
351+ @patch ('pyartcd.pipelines.prepare_release_lp.exectools.cmd_gather_async' , new_callable = AsyncMock )
352+ def test_stale_bundle_triggers_rebuild (self , mock_cmd , mock_find ):
353+ """Bundles referencing old operand images must be rebuilt when the assembly pins a new version."""
354+ mock_find .return_value = {
355+ "metadata" : [
356+ "loki-rhel9-operator-metadata-container-6.0.15-1.assembly.stream.el9-1" ,
357+ ],
358+ "olm_builds_not_found" : [],
359+ "olm_operator_nvrs" : [
360+ "loki-rhel9-operator-container-6.0.15-1.assembly.stream.el9" ,
361+ ],
362+ "olm_builds_detail" : {
363+ "loki-rhel9-operator-metadata-container-6.0.15-1.assembly.stream.el9-1" : {
364+ "operator_nvr" : "loki-rhel9-operator-container-6.0.15-1.assembly.stream.el9" ,
365+ "operand_nvrs" : [
366+ "logging-loki-rhel9-container-6.0.15-OLD.assembly.stream.el9" ,
367+ ],
368+ },
369+ },
370+ }
371+ mock_cmd .return_value = (
372+ 0 ,
373+ json .dumps ({"nvrs" : ["loki-rhel9-operator-metadata-container-6.0.15-2" ], "errors" : []}),
374+ "" ,
375+ )
376+
377+ pipeline = self ._make_pipeline ()
378+ operand_nvrs = [
379+ "loki-rhel9-operator-container-6.0.15-1.assembly.stream.el9" ,
380+ "logging-loki-rhel9-container-6.0.15-NEW.assembly.stream.el9" ,
381+ ]
382+ bundle_nvrs , operator_nvrs = asyncio .run (pipeline ._trigger_bundle_build (operand_nvrs ))
383+
384+ # The stale bundle should have been removed and a rebuild triggered
385+ mock_cmd .assert_called_once ()
386+ cmd = mock_cmd .call_args [0 ][0 ]
387+ self .assertIn ("loki-rhel9-operator-container-6.0.15-1.assembly.stream.el9" , cmd )
388+ self .assertIn ("loki-rhel9-operator-metadata-container-6.0.15-2" , bundle_nvrs )
389+ # The old stale bundle should not be in the result
390+ self .assertNotIn ("loki-rhel9-operator-metadata-container-6.0.15-1.assembly.stream.el9-1" , bundle_nvrs )
391+
392+ @patch .object (PrepareReleaseLPPipeline , '_find_existing_bundles' , new_callable = AsyncMock )
393+ def test_matching_operands_reuses_bundle (self , mock_find ):
394+ """Bundles whose operands match the assembly pins should be reused."""
395+ mock_find .return_value = {
396+ "metadata" : [
397+ "loki-rhel9-operator-metadata-container-6.0.15-1.assembly.stream.el9-1" ,
398+ ],
399+ "olm_builds_not_found" : [],
400+ "olm_operator_nvrs" : [
401+ "loki-rhel9-operator-container-6.0.15-1.assembly.stream.el9" ,
402+ ],
403+ "olm_builds_detail" : {
404+ "loki-rhel9-operator-metadata-container-6.0.15-1.assembly.stream.el9-1" : {
405+ "operator_nvr" : "loki-rhel9-operator-container-6.0.15-1.assembly.stream.el9" ,
406+ "operand_nvrs" : [
407+ "logging-loki-rhel9-container-6.0.15-SAME.assembly.stream.el9" ,
408+ ],
409+ },
410+ },
411+ }
412+
413+ pipeline = self ._make_pipeline ()
414+ operand_nvrs = [
415+ "loki-rhel9-operator-container-6.0.15-1.assembly.stream.el9" ,
416+ "logging-loki-rhel9-container-6.0.15-SAME.assembly.stream.el9" ,
417+ ]
418+ bundle_nvrs , operator_nvrs = asyncio .run (pipeline ._trigger_bundle_build (operand_nvrs ))
419+
420+ # The bundle should be reused since operands match
421+ self .assertEqual (bundle_nvrs , ["loki-rhel9-operator-metadata-container-6.0.15-1.assembly.stream.el9-1" ])
422+ self .assertEqual (operator_nvrs , ["loki-rhel9-operator-container-6.0.15-1.assembly.stream.el9" ])
423+
350424
351425class TestPrepareReleaseLPRun (unittest .TestCase ):
352426 """Integration-level tests for the run() method with mocked externals."""
0 commit comments