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\ComposerCli;
9use org\bovigo\vfs\vfsStream;
10
11/*
12 * @group unit
13 */
14
15class Tiki_Package_ComposerCliTest extends TikiTestCase
16{
17
18	const COMPOSER_JSON_DIST = '{
19	"name": "tiki/tiki-custom",
20	"description": "Tiki Wiki CMS Groupware",
21	"license": "LGPL-2.1",
22	"homepage": "https://tiki.org",
23	"minimum-stability": "stable",
24	"require": {
25	},
26	"config": {
27		"process-timeout": 5000,
28		"bin-dir": "bin",
29		"component-dir": "vendor/components"
30	}
31}';
32
33	const SAMPLE_COMPOSER = '{
34    "name": "tiki/tiki-custom",
35    "description": "Tiki Wiki CMS Groupware",
36    "license": "LGPL-2.1",
37    "homepage": "https://tiki.org",
38    "minimum-stability": "stable",
39    "config": {
40        "process-timeout": 5000,
41        "bin-dir": "bin",
42        "component-dir": "vendor/components"
43    },
44    "require": {
45        "psr/log": "^1.0"
46    }
47}';
48
49	const SAMPLE_COMPOSER_BIG = '{
50  "name": "tiki/tiki-custom",
51  "description": "Tiki Wiki CMS Groupware - composer.json",
52  "license": "LGPL-2.1",
53  "homepage": "https://tiki.org",
54  "minimum-stability": "stable",
55  "require": {
56    "foo/bar": "^1.0.0",
57    "test/unit": "^1.0.0"
58  },
59  "config": {
60    "process-timeout": 5000,
61    "bin-dir": "bin",
62    "component-dir": "vendor/components"
63  }
64}';
65
66	protected $root;
67	protected $composerCli;
68
69	function setUp()
70	{
71		parent::setUp();
72
73		$this->root = vfsStream::setup(__CLASS__);
74		$this->composerCli = new ComposerCli(vfsStream::url(__CLASS__));
75	}
76
77	/**
78	 * Test getComposerConfigOrDefault
79	 *
80	 * @param $composer
81	 * @param $composerDist
82	 * @param $expected
83	 *
84	 * @dataProvider getComposerConfigOrDefaultProvider
85	 */
86	public function testGetComposerConfigOrDefault($composer, $composerDist, $expected)
87	{
88		$structure = [];
89		if (! is_null($composer)) {
90			$structure['composer.json'] = $composer;
91		}
92		if (! is_null($composerDist)) {
93			$structure['composer.json.dist'] = $composerDist;
94		}
95
96		$stream = uniqid(true);
97		vfsStream::setup($stream, null, $structure);
98
99		$composerCli = new ComposerCli(vfsStream::url($stream));
100
101		$this->assertEquals(json_decode($expected, true), $composerCli->getComposerConfigOrDefault());
102	}
103
104	public function getComposerConfigOrDefaultProvider()
105	{
106		return [
107			[self::SAMPLE_COMPOSER, self::COMPOSER_JSON_DIST, self::SAMPLE_COMPOSER],
108			[null, self::COMPOSER_JSON_DIST, self::COMPOSER_JSON_DIST],
109			[null, null, ComposerCli::FALLBACK_COMPOSER_JSON],
110		];
111	}
112
113
114	/**
115	 * Test getListOfPackagesFromConfig
116	 *
117	 * @param $composerJson
118	 * @param $showResult
119	 * @param $expected
120	 *
121	 * @dataProvider getListOfPackagesFromConfigProvider
122	 */
123	function testGetListOfPackagesFromConfig($composerJson, $showResult, $expected)
124	{
125		vfsStream::create(
126			[
127				'composer.json' => $composerJson,
128			],
129			$this->root
130		);
131
132		$composerCli = $this->getMockBuilder('Tiki\Package\ComposerCli')
133			->setMethods(['checkConfigExists', 'canExecuteComposer', 'execShow'])
134			->setConstructorArgs([vfsStream::url(__CLASS__)])
135			->getMock();
136
137		$composerCli->method('checkConfigExists')
138			->willReturn(true);
139
140		$composerCli->method('canExecuteComposer')
141			->willReturn(true);
142
143		$composerCli->method('execShow')
144			->willReturn($showResult);
145
146		$this->assertEquals($expected, $composerCli->getListOfPackagesFromConfig());
147	}
148
149	public function getListOfPackagesFromConfigProvider()
150	{
151		return [
152			[ // no package installed, no key
153				self::SAMPLE_COMPOSER_BIG,
154				[],
155				[
156					[
157						'name' => 'foo/bar',
158						'status' => 'missing',
159						'required' => '^1.0.0',
160						'installed' => '',
161					],
162					[
163						'name' => 'test/unit',
164						'status' => 'missing',
165						'required' => '^1.0.0',
166						'installed' => '',
167					],
168				],
169			],
170			[ // no package installed, with key
171				self::SAMPLE_COMPOSER_BIG,
172				['installed' => []],
173				[
174					[
175						'name' => 'foo/bar',
176						'status' => 'missing',
177						'required' => '^1.0.0',
178						'installed' => '',
179					],
180					[
181						'name' => 'test/unit',
182						'status' => 'missing',
183						'required' => '^1.0.0',
184						'installed' => '',
185					],
186				],
187			],
188			[ // one package installed
189				self::SAMPLE_COMPOSER_BIG,
190				['installed' => [['name' => 'foo/bar', 'version' => '1.2.3']]],
191				[
192					[
193						'name' => 'foo/bar',
194						'status' => 'installed',
195						'required' => '^1.0.0',
196						'installed' => '1.2.3',
197					],
198					[
199						'name' => 'test/unit',
200						'status' => 'missing',
201						'required' => '^1.0.0',
202						'installed' => '',
203					],
204				],
205			],
206			[ // all packages installed
207				self::SAMPLE_COMPOSER_BIG,
208				['installed' => [['name' => 'foo/bar', 'version' => '1.2.3'], ['name' => 'test/unit', 'version' => '1.4.5']]],
209				[
210					[
211						'name' => 'foo/bar',
212						'status' => 'installed',
213						'required' => '^1.0.0',
214						'installed' => '1.2.3',
215					],
216					[
217						'name' => 'test/unit',
218						'status' => 'installed',
219						'required' => '^1.0.0',
220						'installed' => '1.4.5',
221					],
222				],
223			],
224			[ // packages installed that are not in composer.json
225				self::SAMPLE_COMPOSER_BIG,
226				['installed' => [['name' => 'foo/bar2', 'version' => '1.2.3']]],
227				[
228					[
229						'name' => 'foo/bar',
230						'status' => 'missing',
231						'required' => '^1.0.0',
232						'installed' => '',
233					],
234					[
235						'name' => 'test/unit',
236						'status' => 'missing',
237						'required' => '^1.0.0',
238						'installed' => '',
239					],
240				],
241			],
242			[ // all packages installed, case mismatch
243				self::SAMPLE_COMPOSER_BIG,
244				[
245					'installed' => [
246						['name' => 'FOO/BAR', 'version' => '1.2.3'],
247						['name' => 'TesT/UniT', 'version' => '1.4.5'],
248					],
249				],
250				[
251					[
252						'name' => 'foo/bar',
253						'status' => 'installed',
254						'required' => '^1.0.0',
255						'installed' => '1.2.3',
256					],
257					[
258						'name' => 'test/unit',
259						'status' => 'installed',
260						'required' => '^1.0.0',
261						'installed' => '1.4.5',
262					],
263				],
264			],
265		];
266	}
267
268	/**
269	 * Test addComposerPackageToJson
270	 * @dataProvider addComposerPackageToJsonProvider
271	 */
272	function testAddComposerPackageToJson($composerJson, $package, $version, $scripts, $expected)
273	{
274
275		$result = $this->composerCli->addComposerPackageToJson(
276			$composerJson,
277			$package,
278			$version,
279			$scripts
280		);
281
282		$this->assertEquals($expected, $result);
283	}
284
285	public function addComposerPackageToJsonProvider()
286	{
287
288		$returnDataProvider = [];
289
290		$composerJson = json_decode(self::SAMPLE_COMPOSER, true);
291
292		// simple test
293		$expected = $composerJson;
294		$expected['require']['package'] = 'version';
295		$returnDataProvider[] = [
296			$composerJson,
297			'package',
298			'version',
299			null,
300			$expected,
301		];
302
303		// empty JSON
304		$expected = ['require' => ['package' => 'version']];
305		$returnDataProvider[] = [
306			'',
307			'package',
308			'version',
309			null,
310			$expected,
311		];
312
313		// require existing package
314		$returnDataProvider[] = [
315			$composerJson,
316			'psr/log',
317			'^1.0',
318			null,
319			$composerJson,
320		];
321
322		// Add all scripts
323		$expected = $composerJson;
324		$expected['require']['foo/bar'] = '^1.0';
325		$expected['scripts'] = [
326			'pre-install-cmd' => ['FooBarInstaller\Installer::install'],
327			'pre-update-cmd' => ['FooBarInstaller\Installer::install'],
328			'post-install-cmd' => ['FooBarInstaller\Installer::install'],
329			'post-update-cmd' => ['FooBarInstaller\Installer::install'],
330		];
331		$returnDataProvider[] = [
332			$composerJson,
333			'foo/bar',
334			'^1.0',
335			[
336				'pre-install-cmd' => ['FooBarInstaller\Installer::install'],
337				'pre-update-cmd' => ['FooBarInstaller\Installer::install'],
338				'post-install-cmd' => ['FooBarInstaller\Installer::install'],
339				'post-update-cmd' => ['FooBarInstaller\Installer::install'],
340			],
341			$expected,
342		];
343
344		// Append Script
345		$source = $composerJson;
346		$source['scripts'] = [
347			'pre-install-cmd' => ['SomeInstaller\Installer::install'],
348		];
349		$expected = $composerJson;
350		$expected['require']['foo/bar'] = '^1.0';
351		$expected['scripts'] = [
352			'pre-install-cmd' => ['SomeInstaller\Installer::install', 'FooBarInstaller\Installer::install'],
353		];
354		$returnDataProvider[] = [
355			$source,
356			'foo/bar',
357			'^1.0',
358			[
359				'pre-install-cmd' => ['FooBarInstaller\Installer::install'],
360			],
361			$expected,
362		];
363
364		return $returnDataProvider;
365	}
366}
367