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