@@ -434,10 +434,10 @@ public function testPullNode(): void
434434 public function testPullDatabasesWithCodebaseUuid (): void
435435 {
436436 $ codebaseUuid = '11111111-041c-44c7-a486-7972ed2cafc8 ' ;
437- self ::SetEnvVars (['AH_CODEBASE_UUID ' => $ codebaseUuid ]);
437+ self ::SetEnvVars (['AH_CODEBASE_UUID ' => $ codebaseUuid ]);
438438
439439 // Mock the codebase returned from /codebases/{uuid}.
440- $ codebase = $ this ->getMockCodeBaseResponse ();
440+ $ codebase = $ this ->getMockCodeBaseResponse ();
441441 $ this ->clientProphecy ->request ('get ' , '/codebases/ ' . $ codebaseUuid )
442442 ->willReturn ($ codebase );
443443
@@ -562,10 +562,10 @@ public function testPullDatabasesKillsLogicalAndMutation(): void
562562 public function testPullDatabasesWithCodebaseUuidOnDemand (): void
563563 {
564564 $ codebaseUuid = '11111111-041c-44c7-a486-7972ed2cafc8 ' ;
565- self ::SetEnvVars (['AH_CODEBASE_UUID ' => $ codebaseUuid ]);
565+ self ::SetEnvVars (['AH_CODEBASE_UUID ' => $ codebaseUuid ]);
566566
567567 // Mock the codebase returned from /codebases/{uuid}.
568- $ codebase = $ this ->getMockCodeBaseResponse ();
568+ $ codebase = $ this ->getMockCodeBaseResponse ();
569569 $ this ->clientProphecy ->request ('get ' , '/codebases/ ' . $ codebaseUuid )
570570 ->willReturn ($ codebase );
571571
@@ -600,7 +600,6 @@ public function testPullDatabasesWithCodebaseUuidOnDemand(): void
600600 $ this ->clientProphecy ->request ('post ' , '/site-instances/8979a8ac-80dc-4df8-b2f0-6be36554a370.3e8ecbec-ea7c-4260-8414-ef2938c859bc/database/backups ' )
601601 ->willReturn ($ createSiteInstanceDatabaseBackup )
602602 ->shouldBeCalled ();
603- ;
604603 $ siteInstanceDatabaseBackups = $ this ->getMockSiteInstanceDatabaseBackupsResponse ();
605604 $ this ->clientProphecy ->request ('get ' , '/site-instances/8979a8ac-80dc-4df8-b2f0-6be36554a370.3e8ecbec-ea7c-4260-8414-ef2938c859bc/database/backups ' )
606605 ->willReturn ($ siteInstanceDatabaseBackups ->_embedded ->items )
@@ -647,6 +646,87 @@ public function testPullDatabasesWithCodebaseUuidOnDemand(): void
647646 self ::unsetEnvVars (['AH_CODEBASE_UUID ' ]);
648647 }
649648
649+ /**
650+ * Tests that a codebase backup response without a download link
651+ * throws AcquiaCliException before attempting any HTTP download.
652+ */
653+ public function testPullDatabasesCodebaseBackupMissingDownloadUrl (): void
654+ {
655+ $ codebaseUuid = '11111111-041c-44c7-a486-7972ed2cafc8 ' ;
656+ self ::SetEnvVars (['AH_CODEBASE_UUID ' => $ codebaseUuid ]);
657+
658+ $ codebase = $ this ->getMockCodeBaseResponse ();
659+ $ this ->clientProphecy ->request ('get ' , '/codebases/ ' . $ codebaseUuid )
660+ ->willReturn ($ codebase );
661+
662+ $ codebaseEnv = $ this ->getMockCodeBaseEnvironment ();
663+ $ this ->clientProphecy ->request ('get ' , '/codebases/ ' . $ codebaseUuid . '/environments ' )
664+ ->willReturn ([$ codebaseEnv ])
665+ ->shouldBeCalled ();
666+
667+ $ codeabaseSites = $ this ->getMockCodeBaseSites ();
668+ $ this ->clientProphecy ->request ('get ' , '/codebases/ ' . $ codebaseUuid . '/sites ' )
669+ ->willReturn ($ codeabaseSites );
670+ $ siteInstance = $ this ->getMockSiteInstanceResponse ();
671+
672+ $ this ->clientProphecy ->request ('get ' , '/site-instances/8979a8ac-80dc-4df8-b2f0-6be36554a370.3e8ecbec-ea7c-4260-8414-ef2938c859bc ' )
673+ ->willReturn ($ siteInstance )
674+ ->shouldBeCalled ();
675+ $ siteId = '8979a8ac-80dc-4df8-b2f0-6be36554a370 ' ;
676+ $ site = $ this ->getMockSite ();
677+ $ this ->clientProphecy ->request ('get ' , '/sites/ ' . $ siteId )
678+ ->willReturn ($ site )
679+ ->shouldBeCalled ();
680+ $ siteInstanceDatabase = $ this ->getMockSiteInstanceDatabaseResponse ();
681+ $ this ->clientProphecy ->request ('get ' , '/site-instances/8979a8ac-80dc-4df8-b2f0-6be36554a370.3e8ecbec-ea7c-4260-8414-ef2938c859bc/database ' )
682+ ->willReturn ($ siteInstanceDatabase )
683+ ->shouldBeCalled ();
684+ $ siteInstanceDatabaseConnection = $ this ->getMockSiteInstanceDatabaseConnectionResponse ();
685+ $ this ->clientProphecy ->request ('get ' , '/site-instances/8979a8ac-80dc-4df8-b2f0-6be36554a370.3e8ecbec-ea7c-4260-8414-ef2938c859bc/database/connection ' )
686+ ->willReturn ($ siteInstanceDatabaseConnection )
687+ ->shouldBeCalled ();
688+ $ createSiteInstanceDatabaseBackup = $ this ->getMockSiteInstanceDatabaseBackupsResponse ('post ' , '201 ' );
689+ $ this ->clientProphecy ->request ('post ' , '/site-instances/8979a8ac-80dc-4df8-b2f0-6be36554a370.3e8ecbec-ea7c-4260-8414-ef2938c859bc/database/backups ' )
690+ ->willReturn ($ createSiteInstanceDatabaseBackup )
691+ ->shouldBeCalled ();
692+
693+ // Return a backup response WITHOUT the download link.
694+ $ backupWithoutDownload = (object )[
695+ 'created_at ' => '2025-04-01T13:01:06.603Z ' ,
696+ 'database_id ' => 'b0c9dff7-56b6-4c0d-bad0-0e6593f66cd3 ' ,
697+ 'id ' => 'e0c9dff7-56b6-4c0d-bad0-0e6593f66cd3 ' ,
698+ '_links ' => (object )[
699+ 'self ' => (object )[
700+ 'href ' => 'https://environment-service-php.acquia.com/api/site-instances/3e8ecbec-ea7c-4260-8414-ef2938c859bc.d3f7270e-c45f-4801-9308-5e8afe84a323/database/backups/a0c9dff7-56b6-4c0d-bad0-0e6593f66cd3 ' ,
701+ ],
702+ ],
703+ ];
704+ $ this ->clientProphecy ->request ('get ' , '/site-instances/8979a8ac-80dc-4df8-b2f0-6be36554a370.3e8ecbec-ea7c-4260-8414-ef2938c859bc/database/backups ' )
705+ ->willReturn ([$ backupWithoutDownload ])
706+ ->shouldBeCalled ();
707+
708+ // Mock the client options that are set before the download URL check.
709+ $ this ->clientProphecy ->addOption ('sink ' , Argument::type ('string ' ))->shouldBeCalled ();
710+ $ this ->clientProphecy ->addOption ('curl.options ' , Argument::type ('array ' ))->shouldBeCalled ();
711+ $ this ->clientProphecy ->addOption ('progress ' , Argument::that (static fn ($ v ) => is_callable ($ v )))->shouldBeCalled ();
712+ $ this ->clientProphecy ->addOption ('on_stats ' , Argument::that (static fn ($ v ) => is_callable ($ v )))->shouldBeCalled ();
713+
714+ $ localMachineHelper = $ this ->mockLocalMachineHelper ();
715+ $ this ->mockExecuteMySqlConnect ($ localMachineHelper , true );
716+
717+ // No HTTP download should be attempted.
718+ $ this ->httpClientProphecy ->request ('GET ' , Argument::any (), Argument::any ())->shouldNotBeCalled ();
719+
720+ $ inputs = self ::inputChooseEnvironment ();
721+
722+ $ this ->expectException (AcquiaCliException::class);
723+ $ this ->expectExceptionMessage ('Cloud API failed to provide a valid backup download URL ' );
724+ $ this ->executeCommand ([
725+ '--no-scripts ' => true ,
726+ '--on-demand ' => true ,
727+ ], $ inputs );
728+ }
729+
650730 /**
651731 * Test catch block in getSiteInstanceDatabaseConnection method.
652732 * Covers the logger->debug() line when an exception is caught.
0 commit comments