2022-06-23 12:37:04 +00:00
< ? php declare ( strict_types = 1 );
/*
* This file is part of Composer .
*
* ( c ) Nils Adermann < naderman @ naderman . de >
* Jordi Boggiano < j . boggiano @ seld . be >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Composer\Test\Command ;
2024-08-22 08:27:58 +00:00
use Composer\Json\JsonFile ;
2022-06-23 12:37:04 +00:00
use Composer\Test\TestCase ;
use InvalidArgumentException ;
class RequireCommandTest extends TestCase
{
public function testRequireThrowsIfNoneMatches () : void
{
$this -> expectException ( InvalidArgumentException :: class );
$this -> expectExceptionMessage (
'Package required/pkg has requirements incompatible with your PHP version, PHP extensions and Composer version:' . PHP_EOL .
' - required/pkg 1.0.0 requires ext-foobar ^1 but it is not present.'
);
$this -> initTempComposer ([
'repositories' => [
'packages' => [
'type' => 'package' ,
'package' => [
[ 'name' => 'required/pkg' , 'version' => '1.0.0' , 'require' => [ 'ext-foobar' => '^1' ]],
],
],
],
]);
$appTester = $this -> getApplicationTester ();
$appTester -> run ([ 'command' => 'require' , '--dry-run' => true , '--no-audit' => true , 'packages' => [ 'required/pkg' ]]);
}
2023-01-19 20:42:09 +00:00
public function testRequireWarnsIfResolvedToFeatureBranch () : void
{
$this -> initTempComposer ([
'repositories' => [
'packages' => [
'type' => 'package' ,
'package' => [
[ 'name' => 'required/pkg' , 'version' => '2.0.0' , 'require' => [ 'common/dep' => '^1' ]],
[ 'name' => 'required/pkg' , 'version' => 'dev-foo-bar' , 'require' => [ 'common/dep' => '^2' ]],
[ 'name' => 'common/dep' , 'version' => '2.0.0' ],
],
],
],
'require' => [
'common/dep' => '^2.0' ,
],
'minimum-stability' => 'dev' ,
'prefer-stable' => true ,
]);
$appTester = $this -> getApplicationTester ();
$appTester -> setInputs ([ 'n' ]);
$appTester -> run ([ 'command' => 'require' , '--dry-run' => true , '--no-audit' => true , 'packages' => [ 'required/pkg' ]], [ 'interactive' => true ]);
self :: assertSame (
' ./ composer . json has been updated
Running composer update required / pkg
Loading composer repositories with package information
Updating dependencies
Lock file operations : 2 installs , 0 updates , 0 removals
- Locking common / dep ( 2.0 . 0 )
- Locking required / pkg ( dev - foo - bar )
Installing dependencies from lock file ( including require - dev )
Package operations : 2 installs , 0 updates , 0 removals
- Installing common / dep ( 2.0 . 0 )
- Installing required / pkg ( dev - foo - bar )
Using version dev - foo - bar for required / pkg
< warning > Version dev - foo - bar looks like it may be a feature branch which is unlikely to keep working in the long run and may be in an unstable state </ warning >
Are you sure you want to use this constraint ( Y ) or would you rather abort ( n ) the whole operation [ Y , n ] ? '.'
Installation failed , reverting ./ composer . json to its original content .
' , $appTester -> getDisplay ( true ));
}
2022-06-23 12:37:04 +00:00
/**
* @ dataProvider provideRequire
* @ param array < mixed > $composerJson
* @ param array < mixed > $command
*/
public function testRequire ( array $composerJson , array $command , string $expected ) : void
{
$this -> initTempComposer ( $composerJson );
$appTester = $this -> getApplicationTester ();
$appTester -> run ( array_merge ([ 'command' => 'require' , '--dry-run' => true , '--no-audit' => true ], $command ));
if ( str_contains ( $expected , '%d' )) {
$pattern = '{^' . str_replace ( '%d' , '[0-9.]+' , preg_quote ( trim ( $expected ))) . '$}' ;
2024-05-29 21:12:06 +00:00
self :: assertMatchesRegularExpression ( $pattern , trim ( $appTester -> getDisplay ( true )));
2022-06-23 12:37:04 +00:00
} else {
2024-05-29 21:12:06 +00:00
self :: assertSame ( trim ( $expected ), trim ( $appTester -> getDisplay ( true )));
2022-06-23 12:37:04 +00:00
}
}
2022-11-24 13:39:08 +00:00
public static function provideRequire () : \Generator
2022-06-23 12:37:04 +00:00
{
yield 'warn once for missing ext but a lower package matches' => [
[
'repositories' => [
'packages' => [
'type' => 'package' ,
'package' => [
[ 'name' => 'required/pkg' , 'version' => '1.2.0' , 'require' => [ 'ext-foobar' => '^1' ]],
[ 'name' => 'required/pkg' , 'version' => '1.1.0' , 'require' => [ 'ext-foobar' => '^1' ]],
[ 'name' => 'required/pkg' , 'version' => '1.0.0' ],
],
],
],
],
[ 'packages' => [ 'required/pkg' ]],
<<< OUTPUT
< warning > Cannot use required / pkg ' s latest version 1.2 . 0 as it requires ext - foobar ^ 1 which is missing from your platform .
./ composer . json has been updated
Running composer update required / pkg
Loading composer repositories with package information
Updating dependencies
Lock file operations : 1 install , 0 updates , 0 removals
- Locking required / pkg ( 1.0 . 0 )
Installing dependencies from lock file ( including require - dev )
Package operations : 1 install , 0 updates , 0 removals
- Installing required / pkg ( 1.0 . 0 )
2022-11-01 14:48:52 +00:00
Using version ^ 1.0 for required / pkg
2022-06-23 12:37:04 +00:00
OUTPUT
];
yield 'warn multiple times when verbose' => [
[
'repositories' => [
'packages' => [
'type' => 'package' ,
'package' => [
[ 'name' => 'required/pkg' , 'version' => '1.2.0' , 'require' => [ 'ext-foobar' => '^1' ]],
[ 'name' => 'required/pkg' , 'version' => '1.1.0' , 'require' => [ 'ext-foobar' => '^1' ]],
[ 'name' => 'required/pkg' , 'version' => '1.0.0' ],
],
],
],
],
[ 'packages' => [ 'required/pkg' ], '--no-install' => true , '-v' => true ],
<<< OUTPUT
< warning > Cannot use required / pkg ' s latest version 1.2 . 0 as it requires ext - foobar ^ 1 which is missing from your platform .
< warning > Cannot use required / pkg 1.1 . 0 as it requires ext - foobar ^ 1 which is missing from your platform .
./ composer . json has been updated
Running composer update required / pkg
Loading composer repositories with package information
Updating dependencies
Dependency resolution completed in % d seconds
Analyzed % d packages to resolve dependencies
Analyzed % d rules to resolve dependencies
Lock file operations : 1 install , 0 updates , 0 removals
Installs : required / pkg : 1.0 . 0
- Locking required / pkg ( 1.0 . 0 )
2022-11-01 14:48:52 +00:00
Using version ^ 1.0 for required / pkg
2022-06-23 12:37:04 +00:00
OUTPUT
];
yield 'warn for not satisfied req which is satisfied by lower version' => [
[
'repositories' => [
'packages' => [
'type' => 'package' ,
'package' => [
[ 'name' => 'required/pkg' , 'version' => '1.1.0' , 'require' => [ 'php' => '^20' ]],
[ 'name' => 'required/pkg' , 'version' => '1.0.0' , 'require' => [ 'php' => '>=7' ]],
],
],
],
],
[ 'packages' => [ 'required/pkg' ], '--no-install' => true ],
<<< OUTPUT
< warning > Cannot use required / pkg ' s latest version 1.1 . 0 as it requires php ^ 20 which is not satisfied by your platform .
./ composer . json has been updated
Running composer update required / pkg
Loading composer repositories with package information
Updating dependencies
Lock file operations : 1 install , 0 updates , 0 removals
- Locking required / pkg ( 1.0 . 0 )
2022-11-01 14:48:52 +00:00
Using version ^ 1.0 for required / pkg
OUTPUT
];
yield 'version selection happens early even if not completely accurate if no update is requested' => [
[
'repositories' => [
'packages' => [
'type' => 'package' ,
'package' => [
[ 'name' => 'required/pkg' , 'version' => '1.1.0' , 'require' => [ 'php' => '^20' ]],
[ 'name' => 'required/pkg' , 'version' => '1.0.0' , 'require' => [ 'php' => '>=7' ]],
],
],
],
],
[ 'packages' => [ 'required/pkg' ], '--no-update' => true ],
<<< OUTPUT
< warning > Cannot use required / pkg ' s latest version 1.1 . 0 as it requires php ^ 20 which is not satisfied by your platform .
Using version ^ 1.0 for required / pkg
./ composer . json has been updated
OUTPUT
];
yield 'pick best matching version when not provided' => [
[
'repositories' => [
'packages' => [
'type' => 'package' ,
'package' => [
[ 'name' => 'existing/dep' , 'version' => '1.1.0' , 'require' => [ 'required/pkg' => '^1' ]],
[ 'name' => 'required/pkg' , 'version' => '2.0.0' ],
[ 'name' => 'required/pkg' , 'version' => '1.1.0' ],
[ 'name' => 'required/pkg' , 'version' => '1.0.0' ],
],
],
],
'require' => [
'existing/dep' => '^1'
],
],
[ 'packages' => [ 'required/pkg' ], '--no-install' => true ],
<<< OUTPUT
./ composer . json has been updated
Running composer update required / pkg
Loading composer repositories with package information
Updating dependencies
Lock file operations : 2 installs , 0 updates , 0 removals
- Locking existing / dep ( 1.1 . 0 )
- Locking required / pkg ( 1.1 . 0 )
Using version ^ 1.1 for required / pkg
2023-01-19 20:35:50 +00:00
OUTPUT
];
yield 'use exact constraint with --fixed' => [
[
'type' => 'project' ,
'repositories' => [
'packages' => [
'type' => 'package' ,
'package' => [
[ 'name' => 'required/pkg' , 'version' => '1.1.0' ],
],
],
],
],
[ 'packages' => [ 'required/pkg' ], '--no-install' => true , '--fixed' => true ],
<<< OUTPUT
./ composer . json has been updated
Running composer update required / pkg
Loading composer repositories with package information
Updating dependencies
Lock file operations : 1 install , 0 updates , 0 removals
- Locking required / pkg ( 1.1 . 0 )
Using version 1.1 . 0 for required / pkg
2022-06-23 12:37:04 +00:00
OUTPUT
];
}
2024-08-22 08:27:58 +00:00
/**
* @ dataProvider provideInconsistentRequireKeys
* @ param bool $isDev
* @ param bool $isInteractive
* @ param string $expectedWarning
*/
public function testInconsistentRequireKeys ( bool $isDev , bool $isInteractive , string $expectedWarning ) : void
{
$currentKey = $isDev ? " require " : " require-dev " ;
$otherKey = $isDev ? " require-dev " : " require " ;
$dir = $this -> initTempComposer ([
'repositories' => [
'packages' => [
'type' => 'package' ,
'package' => [
[ 'name' => 'required/pkg' , 'version' => '1.0.0' ],
],
],
],
$currentKey => [
" required/pkg " => " ^1.0 " ,
],
]);
$package = self :: getPackage ( 'required/pkg' );
if ( $isDev ) {
$this -> createComposerLock ([], [ $package ]);
$this -> createInstalledJson ([], [ $package ]);
} else {
$this -> createComposerLock ([ $package ], []);
$this -> createInstalledJson ([ $package ], []);
}
$appTester = $this -> getApplicationTester ();
$command = [
'command' => 'require' ,
'--no-audit' => true ,
'--dev' => $isDev ,
'--no-install' => true ,
'packages' => [ 'required/pkg' ]
];
if ( $isInteractive )
$appTester -> setInputs ([ 'yes' ]);
else
$command [ '--no-interaction' ] = true ;
$appTester -> run ( $command );
self :: assertStringContainsString (
$expectedWarning ,
$appTester -> getDisplay ( true )
);
$composer_content = ( new JsonFile ( $dir . '/composer.json' )) -> read ();
self :: assertArrayHasKey ( $otherKey , $composer_content );
self :: assertArrayNotHasKey ( $currentKey , $composer_content );
}
public function provideInconsistentRequireKeys () : \Generator
{
yield [
true ,
false ,
'<warning>required/pkg is currently present in the require key and you ran the command with the --dev flag, which will move it to the require-dev key.</warning>'
];
yield [
false ,
false ,
'<warning>required/pkg is currently present in the require-dev key and you ran the command without the --dev flag, which will move it to the require key.</warning>'
];
yield [
true ,
true ,
'<warning>required/pkg is currently present in the require key and you ran the command with the --dev flag, which will move it to the require-dev key.</warning>'
];
yield [
false ,
true ,
'<warning>required/pkg is currently present in the require-dev key and you ran the command without the --dev flag, which will move it to the require key.</warning>'
];
}
2022-06-23 12:37:04 +00:00
}