1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8use Tiki\Package\ComposerManager; 9use org\bovigo\vfs\vfsStream; 10use org\bovigo\vfs\vfsStreamDirectory; 11 12class Tiki_Package_ComposerManagerTest extends TikiTestCase 13{ 14 15 /** @var vfsStreamDirectory */ 16 protected $root; 17 /** @var string */ 18 protected $rootPath; 19 20 /** @var ComposerManager */ 21 protected $composerManager; 22 23 function setUp() 24 { 25 parent::setUp(); 26 27 $this->root = vfsStream::setup(__CLASS__); 28 $this->rootPath = vfsStream::url(__CLASS__); 29 30 $this->composerManager = new ComposerManager( 31 $this->rootPath, 32 null, 33 null, 34 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml' 35 ); 36 } 37 38 function testGetComposer() 39 { 40 $this->assertInstanceOf('Tiki\Package\ComposerCli', $this->composerManager->getComposer()); 41 } 42 43 function testComposerPath() 44 { 45 $this->assertEquals($this->composerManager->composerPath(), $this->rootPath . '/temp/composer.phar'); 46 } 47 48 function testBrokenYaml() 49 { 50 51 $composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli') 52 ->setMethods(['getListOfPackagesFromConfig']) 53 ->setConstructorArgs([$this->rootPath]) 54 ->getMock(); 55 56 $composerCli->method('getListOfPackagesFromConfig') 57 ->willReturn([]); 58 59 $composerManager = new ComposerManager( 60 $this->rootPath, 61 null, 62 $composerCli, 63 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackagesBroken.yml' 64 ); 65 66 $response = $composerManager->getAvailable(); 67 68 $this->assertCount(0, $response); 69 } 70 71 function testIfNoPackageIsInstalledAllAreAvailable() 72 { 73 74 $composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli') 75 ->setMethods(['getListOfPackagesFromConfig']) 76 ->setConstructorArgs([$this->rootPath]) 77 ->getMock(); 78 79 $composerCli->method('getListOfPackagesFromConfig') 80 ->willReturn([]); 81 82 $composerManager = new ComposerManager( 83 $this->rootPath, 84 null, 85 $composerCli, 86 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml' 87 ); 88 89 $response = $composerManager->getAvailable(); 90 $nameOfPackages = array_column($response, 'name'); 91 92 $this->assertContains('jerome-breton/casperjs-installer', $nameOfPackages); 93 $this->assertContains('enygma/expose', $nameOfPackages); 94 } 95 96 function testPackageNotAvailableIfInstalled() 97 { 98 99 $composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli') 100 ->setMethods(['getListOfPackagesFromConfig']) 101 ->setConstructorArgs([$this->rootPath]) 102 ->getMock(); 103 104 $composerCli->method('getListOfPackagesFromConfig') 105 ->willReturn( 106 [ 107 [ 108 'name' => 'jerome-breton/casperjs-installer', 109 'status' => 'installed', 110 'required' => '^1.0.0', 111 'installed' => '1.2.3', 112 ], 113 ] 114 ); 115 116 $composerManager = new ComposerManager( 117 $this->rootPath, 118 null, 119 $composerCli, 120 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml' 121 ); 122 123 $response = $composerManager->getAvailable(); 124 $nameOfPackages = array_column($response, 'name'); 125 126 $this->assertNotContains('jerome-breton/casperjs-installer', $nameOfPackages); 127 $this->assertContains('enygma/expose', $nameOfPackages); 128 } 129 130 function testAllPackagesAvailableIfNotFiltered() 131 { 132 133 $composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli') 134 ->setMethods(['getListOfPackagesFromConfig']) 135 ->setConstructorArgs([$this->rootPath]) 136 ->getMock(); 137 138 $composerCli->method('getListOfPackagesFromConfig') 139 ->willReturn( 140 [ 141 [ 142 'name' => 'jerome-breton/casperjs-installer', 143 'status' => 'installed', 144 'required' => '^1.0.0', 145 'installed' => '1.2.3', 146 ], 147 ] 148 ); 149 150 $composerManager = new ComposerManager( 151 $this->rootPath, 152 null, 153 $composerCli, 154 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml' 155 ); 156 157 $response = $composerManager->getAvailable(false); 158 $nameOfPackages = array_column($response, 'name'); 159 160 $this->assertContains('jerome-breton/casperjs-installer', $nameOfPackages); 161 $this->assertContains('enygma/expose', $nameOfPackages); 162 } 163 164 function testInstallNotExistingPackage() 165 { 166 $composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli') 167 ->setMethods(['installPackage']) 168 ->setConstructorArgs([$this->rootPath]) 169 ->getMock(); 170 171 $composerManager = new ComposerManager( 172 $this->rootPath, 173 null, 174 $composerCli, 175 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml' 176 ); 177 178 $this->assertNull($composerManager->installPackage('FooBar')); 179 } 180 181 function testInstallPackage() 182 { 183 $composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli') 184 ->setMethods(['canExecuteComposer', 'installMissingPackages']) 185 ->setConstructorArgs([$this->rootPath]) 186 ->getMock(); 187 188 $composerCli 189 ->expects($this->once()) 190 ->method('canExecuteComposer') 191 ->willReturn(true); 192 193 $composerCli 194 ->expects($this->once()) 195 ->method('installMissingPackages') 196 ->willReturn('__PACKAGE__INSTALLED__'); 197 198 $composerManager = new ComposerManager( 199 $this->rootPath, 200 null, 201 $composerCli, 202 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml' 203 ); 204 205 $this->assertRegexp('/__PACKAGE__INSTALLED__/', $composerManager->installPackage('CasperJS')); 206 207 $this->assertJsonFileEqualsJsonFile( 208 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'CasperJsComposer.json', 209 $this->rootPath . '/composer.json' 210 ); 211 } 212 213 function testGetInstalled() 214 { 215 $composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli') 216 ->setMethods(['getListOfPackagesFromConfig']) 217 ->setConstructorArgs([$this->rootPath]) 218 ->getMock(); 219 220 $composerCli 221 ->expects($this->once()) 222 ->method('getListOfPackagesFromConfig') 223 ->willReturn( 224 [ 225 [ 226 'name' => 'jerome-breton/casperjs-installer', 227 'status' => 'installed', 228 'required' => '^1.0.0', 229 'installed' => '1.2.3', 230 ], 231 [ 232 'name' => 'Foo/Bar', 233 'status' => 'installed', 234 'required' => '^2.0.0', 235 'installed' => '2.2.3', 236 ], 237 ] 238 ); 239 240 $composerManager = new ComposerManager( 241 $this->rootPath, 242 null, 243 $composerCli, 244 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml' 245 ); 246 247 $response = $composerManager->getInstalled(); 248 249 $this->assertCount(2, $response); 250 251 $this->assertEquals('CasperJS', $response[0]['key']); 252 $this->assertEquals('jerome-breton/casperjs-installer', $response[0]['name']); 253 254 $this->assertEquals('', $response[1]['key']); 255 $this->assertEquals('Foo/Bar', $response[1]['name']); 256 } 257 258 function testGetInstalledCaseMismatch() 259 { 260 $composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli') 261 ->setMethods(['getListOfPackagesFromConfig']) 262 ->setConstructorArgs([$this->rootPath]) 263 ->getMock(); 264 265 $composerCli 266 ->expects($this->once()) 267 ->method('getListOfPackagesFromConfig') 268 ->willReturn( 269 [ 270 [ 271 'name' => 'JEROME-BRETON/casperjs-installer', 272 'status' => 'installed', 273 'required' => '^1.0.0', 274 'installed' => '1.2.3', 275 ], 276 ] 277 ); 278 279 $composerManager = new ComposerManager( 280 $this->rootPath, 281 null, 282 $composerCli, 283 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml' 284 ); 285 286 $response = $composerManager->getInstalled(); 287 288 $this->assertCount(1, $response); 289 290 $this->assertEquals('CasperJS', $response[0]['key']); 291 $this->assertEquals('JEROME-BRETON/casperjs-installer', $response[0]['name']); 292 } 293 294 function testRemoveUnknownPackageFails() 295 { 296 $composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli') 297 ->setMethods(['removePackage']) 298 ->setConstructorArgs([$this->rootPath]) 299 ->getMock(); 300 301 $composerCli 302 ->expects($this->never()) 303 ->method('removePackage'); 304 305 $composerManager = new ComposerManager( 306 $this->rootPath, 307 null, 308 $composerCli, 309 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml' 310 ); 311 312 $this->assertNull($composerManager->removePackage('FooBar')); 313 } 314 315 function testRemovePackage() 316 { 317 $composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli') 318 ->setMethods(['removePackage']) 319 ->setConstructorArgs([$this->rootPath]) 320 ->getMock(); 321 322 $composerCli 323 ->expects($this->once()) 324 ->method('removePackage') 325 ->willReturn('__PACKAGE__REMOVED__'); 326 327 $composerManager = new ComposerManager( 328 $this->rootPath, 329 null, 330 $composerCli, 331 __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml' 332 ); 333 334 $this->assertEquals('__PACKAGE__REMOVED__', $composerManager->removePackage('CasperJS')); 335 } 336 337 /** 338 * @dataProvider providerForTestCheckThatCanInstallPackages 339 */ 340 function testCheckThatCanInstallPackages($files, $expected) 341 { 342 foreach ($files as $file) { 343 list( $path, $isDir, $mode) = $file; 344 if ($path === '/') { 345 $this->root->chmod($mode); 346 } 347 if ($isDir) { 348 vfsStream::newDirectory($path, $mode)->at($this->root); 349 } else { 350 vfsStream::newFile($path, $mode)->at($this->root); 351 } 352 } 353 354 $result = $this->composerManager->checkThatCanInstallPackages(); 355 356 $this->assertEquals($expected, $result); 357 } 358 359 function providerForTestCheckThatCanInstallPackages() 360 { 361 return [ 362 [ // root dir writable, no files 363 [ 364 ['/', true, 0700], 365 ], 366 [] 367 ], 368 [ // root dir and all files / dir writable 369 [ 370 ['/', true, 0700], 371 ['vendor', true, 0700], 372 ['composer.json', false, 0600], 373 ['composer.lock', false, 0600], 374 ], 375 [] 376 ], 377 [ // root dir not writable, all files and directories writable and in place 378 [ 379 ['/', true, 0111], 380 ['vendor', true, 0700], 381 ['composer.json', false, 0600], 382 ['composer.lock', false, 0600], 383 ], 384 [] 385 ], 386 [ // all in place, but nothing is writable 387 [ 388 ['/', true, 0111], 389 ['vendor', true, 0500], 390 ['composer.json', false, 0400], 391 ['composer.lock', false, 0400], 392 ], 393 [ 394 'Tiki can not write to file "vfs://Tiki_Package_ComposerManagerTest/composer.json"', 395 'Tiki can not write to file "vfs://Tiki_Package_ComposerManagerTest/composer.lock"', 396 'Tiki can not write to directory "vfs://Tiki_Package_ComposerManagerTest/vendor"', 397 ] 398 ], 399 [ // root dir not writable, no files 400 [ 401 ['/', true, 0000], 402 ], 403 [ 404 'Tiki root directory is not writable, so file "vfs://Tiki_Package_ComposerManagerTest/composer.json" can not be created', 405 'Tiki root directory is not writable, so file "vfs://Tiki_Package_ComposerManagerTest/composer.lock" can not be created', 406 'Tiki root directory is not writable, so directory "vfs://Tiki_Package_ComposerManagerTest/vendor" can not be created', 407 ] 408 ], 409 [ // Mixed Environment 410 [ 411 ['/', true, 0111], 412 ['vendor', true, 0700], 413 ['composer.json', false, 0400], 414 ['composer.lock', false, 0400], 415 ], 416 [ 417 'Tiki can not write to file "vfs://Tiki_Package_ComposerManagerTest/composer.json"', 418 'Tiki can not write to file "vfs://Tiki_Package_ComposerManagerTest/composer.lock"', 419 ] 420 ], 421 [ // Mixed Environment 422 [ 423 ['/', true, 0111], 424 ['vendor', true, 0500], 425 ['composer.json', false, 0600], 426 ['composer.lock', false, 0400], 427 ], 428 [ 429 'Tiki can not write to file "vfs://Tiki_Package_ComposerManagerTest/composer.lock"', 430 'Tiki can not write to directory "vfs://Tiki_Package_ComposerManagerTest/vendor"', 431 ] 432 ], 433 [ // Mixed Environment 434 [ 435 ['/', true, 0111], 436 ['vendor', true, 0500], 437 ['composer.json', false, 0400], 438 ['composer.lock', false, 0600], 439 ], 440 [ 441 'Tiki can not write to file "vfs://Tiki_Package_ComposerManagerTest/composer.json"', 442 'Tiki can not write to directory "vfs://Tiki_Package_ComposerManagerTest/vendor"', 443 ] 444 ], 445 [ // root dir not writable, Mixed Environment 446 [ 447 ['/', true, 0000], 448 ['vendor', true, 0700], 449 ], 450 [ 451 'Tiki root directory is not writable, so file "vfs://Tiki_Package_ComposerManagerTest/composer.json" can not be created', 452 'Tiki root directory is not writable, so file "vfs://Tiki_Package_ComposerManagerTest/composer.lock" can not be created', 453 ] 454 ], 455 [ // root dir not writable, Mixed Environment 456 [ 457 ['/', true, 0000], 458 ['composer.json', false, 0600], 459 ], 460 [ 461 'Tiki root directory is not writable, so file "vfs://Tiki_Package_ComposerManagerTest/composer.lock" can not be created', 462 'Tiki root directory is not writable, so directory "vfs://Tiki_Package_ComposerManagerTest/vendor" can not be created', 463 ] 464 ], 465 [ // root dir not writable, Mixed Environment 466 [ 467 ['/', true, 0000], 468 ['composer.lock', false, 0600], 469 ], 470 [ 471 'Tiki root directory is not writable, so file "vfs://Tiki_Package_ComposerManagerTest/composer.json" can not be created', 472 'Tiki root directory is not writable, so directory "vfs://Tiki_Package_ComposerManagerTest/vendor" can not be created', 473 ] 474 ], 475 ]; 476 } 477 478 /** 479 * @covers Tiki\Package\ComposerManager::getPackageInfo() 480 */ 481 public function testGetPackagesInfo() 482 { 483 $configFile = __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'ComposerPackages.yml'; 484 485 $expected = [ 486 'licence' => 'MIT', 487 'licenceUrl' => 'https://github.com/jerome-breton/casperjs-installer/blob/master/LICENSE', 488 'name' => 'jerome-breton/casperjs-installer', 489 'requiredBy' => [ 490 'wikiplugin_casperjs' 491 ], 492 'requiredVersion' => 'dev-master', 493 'scripts' => [ 494 'post-install-cmd' => [ 495 "CasperJsInstaller\Installer::install" 496 ], 497 'post-update-cmd' => [ 498 'CasperJsInstaller\Installer::install', 499 ] 500 ] 501 ]; 502 503 $this->assertEquals($expected, ComposerManager::getPackageInfo('jerome-breton/casperjs-installer', $configFile)); 504 505 $expected = [$expected]; 506 $expected[] = [ 507 'licence' => 'MIT', 508 'licenceUrl' => 'https://github.com/enygma/expose/blob/master/LICENSE', 509 'name' => 'enygma/expose', 510 'requiredBy' => [ 511 'ids_enabled' 512 ], 513 'requiredVersion' => '^3.0', 514 ]; 515 516 $this->assertEquals($expected, ComposerManager::getPackageInfo(['jerome-breton/casperjs-installer', 'enygma/expose'], $configFile)); 517 } 518} 519