2222use Composer \Package \Package ;
2323use Composer \Package \RootPackageInterface ;
2424use Composer \Plugin \PluginInterface ;
25+ use Composer \Repository \RepositoryInterface ;
2526use Composer \Repository \RepositoryManager ;
2627use Composer \Repository \WritableRepositoryInterface ;
2728use Composer \Script \Event ;
@@ -213,6 +214,96 @@ public function testFetchRecipesOrder()
213214 ], array_keys ($ recipes ));
214215 }
215216
217+ public function testFetchRecipesWithConflicts ()
218+ {
219+ $ originalRecipes = [
220+ 'locks ' => [
221+ 'doctrine/annotations ' => [
222+ 'version ' => '1.13 ' ,
223+ 'recipe ' => [
224+ 'version ' => '1.0 ' ,
225+ ],
226+ ],
227+ 'doctrine/doctrine-bundle ' => [
228+ 'version ' => '2.5 ' ,
229+ 'recipe ' => [
230+ 'version ' => '2.4 ' ,
231+ ],
232+ ],
233+ ],
234+ 'manifests ' => [
235+ 'doctrine/annotations ' => [
236+ 'version ' => '1.0 ' ,
237+ 'manifest ' => [],
238+ ],
239+ 'doctrine/doctrine-bundle ' => [
240+ 'version ' => '2.4 ' ,
241+ 'manifest ' => [
242+ 'conflict ' => [
243+ 'symfony/framework-bundle ' => '<5.3 ' ,
244+ ],
245+ ],
246+ ],
247+ ],
248+ ];
249+ $ oldRecipes = [
250+ 'locks ' => [
251+ 'doctrine/doctrine-bundle ' => [
252+ // 2.5 is still being installed, but 2.3 recipe used
253+ 'version ' => '2.5 ' ,
254+ 'recipe ' => [
255+ 'version ' => '2.3 ' ,
256+ ],
257+ ],
258+ ],
259+ 'manifests ' => [
260+ 'doctrine/doctrine-bundle ' => [
261+ 'version ' => '2.3 ' ,
262+ 'manifest ' => [],
263+ ],
264+ ],
265+ ];
266+
267+ $ io = new BufferIO ('' , OutputInterface::VERBOSITY_VERBOSE );
268+ $ rootPackage = $ this ->mockRootPackage (['symfony ' => ['allow-contrib ' => true ]]);
269+
270+ $ downloader = $ this ->getMockBuilder (Downloader::class)->disableOriginalConstructor ()->getMock ();
271+ $ downloader ->expects ($ this ->exactly (2 ))
272+ ->method ('getRecipes ' )
273+ ->willReturnOnConsecutiveCalls ($ originalRecipes , $ oldRecipes );
274+ $ downloader ->expects ($ this ->any ())->method ('isEnabled ' )->willReturn (true );
275+ $ downloader ->expects ($ this ->once ())->method ('removeRecipeFromIndex ' )->with ('doctrine/doctrine-bundle ' , '2.4 ' );
276+
277+ $ locker = $ this ->getMockBuilder (Locker::class)->disableOriginalConstructor ()->getMock ();
278+ $ lockedRepository = $ this ->getMockBuilder (RepositoryInterface::class)->disableOriginalConstructor ()->getMock ();
279+ // make the conflicted package show up
280+ $ locker ->expects ($ this ->any ())
281+ ->method ('getLockedRepository ' )
282+ ->willReturn ($ lockedRepository );
283+ $ lockedRepository ->expects ($ this ->once ())
284+ ->method ('findPackage ' )
285+ ->with ('symfony/framework-bundle ' , '<5.3 ' )
286+ ->willReturn (new Package ('symfony/framework-bundle ' , '5.2.0 ' , '5.2.0 ' ));
287+ $ composer = $ this ->mockComposer ($ locker , $ rootPackage );
288+ $ configurator = $ this ->mockConfigurator ();
289+ $ lock = $ this ->mockLock ();
290+ $ flex = $ this ->mockFlexCustom ($ io , $ composer , $ configurator , $ downloader , $ lock );
291+
292+ $ operations = [];
293+ foreach ($ originalRecipes ['manifests ' ] as $ name => $ recipeData ) {
294+ $ package = new Package ($ name , $ recipeData ['version ' ], $ recipeData ['version ' ]);
295+
296+ $ operations [] = new InstallOperation ($ package );
297+ }
298+ $ recipes = $ flex ->fetchRecipes ($ operations , true );
299+
300+ $ this ->assertSame ([
301+ 'doctrine/annotations ' ,
302+ 'doctrine/doctrine-bundle ' ,
303+ ], array_keys ($ recipes ));
304+ $ this ->assertSame ('2.3 ' , $ recipes ['doctrine/doctrine-bundle ' ]->getVersion ());
305+ }
306+
216307 public static function getTestPackages (): array
217308 {
218309 return [
@@ -358,14 +449,8 @@ private function mockManager(): RepositoryManager
358449 return $ manager ;
359450 }
360451
361- private function mockFlex (BufferIO $ io , RootPackageInterface $ package , Recipe $ recipe = null , array $ recipes = [], array $ lockerData = [] ): Flex
452+ private function mockFlexCustom (BufferIO $ io , Composer $ composer , Configurator $ configurator , Downloader $ downloader , Lock $ lock ): Flex
362453 {
363- $ composer = $ this ->mockComposer ($ this ->mockLocker ($ lockerData ), $ package );
364-
365- $ configurator = $ this ->mockConfigurator ($ recipe );
366- $ downloader = $ this ->mockDownloader ($ recipes );
367- $ lock = $ this ->mockLock ();
368-
369454 return \Closure::bind (function () use ($ composer , $ io , $ configurator , $ downloader , $ lock ) {
370455 $ flex = new Flex ();
371456 $ flex ->composer = $ composer ;
@@ -381,4 +466,15 @@ private function mockFlex(BufferIO $io, RootPackageInterface $package, Recipe $r
381466 return $ flex ;
382467 }, null , Flex::class)->__invoke ();
383468 }
469+
470+ private function mockFlex (BufferIO $ io , RootPackageInterface $ package , Recipe $ recipe = null , array $ recipes = [], array $ lockerData = []): Flex
471+ {
472+ $ composer = $ this ->mockComposer ($ this ->mockLocker ($ lockerData ), $ package );
473+
474+ $ configurator = $ this ->mockConfigurator ($ recipe );
475+ $ downloader = $ this ->mockDownloader ($ recipes );
476+ $ lock = $ this ->mockLock ();
477+
478+ return $ this ->mockFlexCustom ($ io , $ composer , $ configurator , $ downloader , $ lock );
479+ }
384480}
0 commit comments