* Jordi Boggiano * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer\Test\Command; use Composer\Command\InitCommand; use Composer\Util\Filesystem; use Symfony\Component\Console\Tester\ApplicationTester; use Composer\Command\RequireCommand; use Symfony\Component\Console\Input\ArrayInput; use Composer\Console\Application; use Composer\Test\TestCase; use Symfony\Component\Process\Process; class RequireCommandTest extends TestCase { /** * @var string */ private $start_directory; private $working_directory; private $local_repository; public function setUp(): void { $this->local_repository = $this->getUniqueTmpDirectory(); $repo_manifest = [ 'name' => 'testvendor/testpackage', 'version' => '1.0.0', 'type' => 'library', 'autoload' => [ 'psr-4' => [ 'TestVendor\\Testpackage\\' => 'src/', ] ] ]; file_put_contents( $this->local_repository.'/composer.json', json_encode( $repo_manifest, JSON_UNESCAPED_SLASHES, ) ); $this->working_directory = $this->getUniqueTmpDirectory(); $manifest = [ 'name' => 'test/something', 'require' => (object) null, 'repositories' => [ [ 'type' => 'path', 'url' => $this->local_repository, ], [ 'packagist.org' => false ] ] ]; file_put_contents( $this->working_directory.'/composer.json', json_encode($manifest, JSON_UNESCAPED_SLASHES) ); $this->start_directory = getcwd(); } protected function tearDown(): void { parent::tearDown(); // Reset the working directory. Some commands leave the working // directory contaminated when they exit with an error. chdir($this->start_directory); $fs = new Filesystem(); $fs->removeDirectory($this->working_directory); $fs->removeDirectory($this->local_repository); } /** * testcase: UC01-001 */ public function testInstallPackage(): void { // Arrange $application = new Application(); $application->setAutoExit(false); $application->add(new RequireCommand()); $local_repository_class_code = 'local_repository . '/src'; $local_repository_class_file = $local_repository_class_file_dir . '/TestClass.php'; mkdir($local_repository_class_file_dir, 0777, true); file_put_contents($local_repository_class_file, $local_repository_class_code); $check_autoload_code = 'include "./vendor/autoload.php";' . 'use TestVendor\\Testpackage\\TestClass;' . '(new TestClass())->printTestClass();'; $check_autoload_process = new Process(['php', '-r', $check_autoload_code]); $check_autoload_process->setWorkingDirectory($this->working_directory); // Act $tester = new ApplicationTester($application); $tester->run([ 'command' => 'require', // Confirmation 1: provide dependency name of format vendor/package 'packages' => [ 'testvendor/testpackage' ], '-d' => $this->working_directory, ]); $check_autoload_process->run(); // Assert $output = $tester->getDisplay(); // Confirmation 4: exit code 0 $tester->assertCommandIsSuccessful(); $this->assertStringContainsString('Installing testvendor/testpackage', $output); // Confirmation 2: dependency is present in vendor directory $this->assertTrue(file_exists($this->working_directory . '/vendor/testvendor/testpackage')); // Confirmation 3: dependency classes can be autoloaded $this->assertEquals(0, $check_autoload_process->getExitCode()); $this->assertEquals('TestClass', $check_autoload_process->getOutput()); } /** * testcase: UC01-002 */ public function testInstallPackageSaveToComposerJson(): void { // Arrange $application = new Application(); $application->setAutoExit(false); $application->add(new RequireCommand()); // Act $tester = new ApplicationTester($application); // Confirmation 1: Install dependency $tester->run([ 'command' => 'require', 'packages' => [ 'testvendor/testpackage 1.0.0' ], '-d' => $this->working_directory, ]); // Assert $manifest = json_decode( file_get_contents($this->working_directory . '/composer.json'), true ); // Confirmation 2: dependency has been added to composer.json $this->assertArrayHasKey('testvendor/testpackage', $manifest['require']); $this->assertEquals('1.0.0', $manifest['require']['testvendor/testpackage']); } /** * testcase: UC01-003 */ public function testInstallPackagesSaveToComposerLock(): void { // Arrange $application = new Application(); $application->setAutoExit(false); $application->add(new RequireCommand()); // Act $tester = new ApplicationTester($application); $tester->run([ 'command' => 'require', 'packages' => [ 'testvendor/testpackage 1.0.0' ], '-d' => $this->working_directory, ]); // Assert $lockfile = json_decode( file_get_contents($this->working_directory . '/composer.lock'), true ); $packages = $lockfile['packages']; $installed_package = null; foreach($packages as $package) { if ($package['name'] === 'testvendor/testpackage') { $installed_package = $package; } } $this->assertNotNull($installed_package); $this->assertEquals('1.0.0', $installed_package['version']); } /** * testcase: UC01-004 */ public function testInstallMisspelledPackage(): void { // Arrange $application = new Application(); $application->setAutoExit(false); $application->add(new RequireCommand()); // Act $tester = new ApplicationTester($application); $tester->run([ 'command' => 'require', 'packages' => [ 'testvendor/testpcakage 1.0.0' ], '-d' => $this->working_directory, ]); // Assert $this->assertNotEquals(0, $tester->getStatusCode()); $output = $tester->getDisplay(); $this->assertStringContainsString( 'Your requirements could not be resolved to an installable set of packages.', $output ); $this->assertStringContainsString( 'requires testvendor/testpcakage, it could not be found in any version', $output ); $this->assertStringContainsString( '- A typo in the package name', $output ); } /** * testcase: UC01-005 */ public function testInstallNoPackage(): void { // Arrange $application = new Application(); $application->setAutoExit(false); $application->add(new RequireCommand()); // Act $tester = new ApplicationTester($application); $tester->run([ 'command' => 'require', 'packages' => [ '-' ], '-d' => $this->working_directory, ]); // Assert $this->assertNotEquals(0, $tester->getStatusCode()); $output = $tester->getDisplay(); $this->assertStringContainsString( 'Could not find package -.', $output ); // Assert $manifest = json_decode( file_get_contents($this->working_directory . '/composer.json'), true ); // Confirmation 2: dependency has not been added to composer.json $this->assertEquals(0, count($manifest['require'])); } /** * testcase: UC01-006 */ public function testInstallLongPackageName(): void { // Arrange $application = new Application(); $application->setAutoExit(false); $application->add(new RequireCommand()); $package_name = $this->generateRandomString(8000); // Act $tester = new ApplicationTester($application); $tester->run([ 'command' => 'require', 'packages' => [ $package_name ], '-d' => $this->working_directory, ]); // Assert $this->assertNotEquals(0, $tester->getStatusCode()); $output = $tester->getDisplay(); $this->assertStringContainsString( 'Could not find a matching version of package ', $output ); } private function generateRandomString(int $length): string { $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $charactersLength = strlen($characters); $randomString = ''; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[rand(0, $charactersLength - 1)]; } return $randomString; } }