InstallerTest   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 393
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 227
dl 0
loc 393
rs 10
c 7
b 0
f 0
wmc 19

19 Methods

Rating   Name   Duplication   Size   Complexity  
A testSetInvalidHook() 0 11 1
A testMoveExistingHook() 0 34 1
A testSetHookAndEnabledOnly() 0 12 1
A testMoveNotExistingHook() 0 32 1
A testBrokenSymlinkDetectionOnBrokenAbsoluteSymlink() 0 18 1
A testSetMultipleInvalidHooks() 0 11 1
A testMoveExistingHookWhenMoveExistingIsAnAbsolutePath() 0 36 1
A testSkipExisting() 0 14 1
A testSkipAfterMovingFail() 0 13 1
A testWriteMultipleHooks() 0 16 1
A testBrokenSymlinkDetectionOnBrokenRelativeSymlink() 0 18 1
A testHookInstallationDeclined() 0 17 1
A testBrokenSymlinkDetectionOnExistingFile() 0 17 1
A testMoveAfterSkippingFail() 0 13 1
A testOnlyEnabledAndHooksToHandle() 0 12 1
A testDeclineOverwrite() 0 13 1
A testDontWriteToDevNull() 0 16 1
A testMoveExistingHookTargetIsFile() 0 28 1
A testWriteHook() 0 19 1
1
<?php
2
3
/**
4
 * This file is part of CaptainHook
5
 *
6
 * (c) Sebastian Feldmann <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace CaptainHook\App\Runner;
13
14
use CaptainHook\App\Config\Mockery as ConfigMockery;
15
use CaptainHook\App\Console\IO\DefaultIO;
16
use CaptainHook\App\Console\IO\Mockery as IOMockery;
17
use CaptainHook\App\Exception\InvalidHookName;
18
use CaptainHook\App\Git\DummyRepo;
19
use CaptainHook\App\Hook\Mockery as HookMockery;
20
use CaptainHook\App\Mockery as CHMockery;
21
use CaptainHook\App\Storage\File;
22
use Exception;
23
use org\bovigo\vfs\vfsStream;
24
use PHPUnit\Framework\TestCase;
25
use RuntimeException;
26
use SebastianFeldmann\Git\Repository;
27
28
class InstallerTest extends TestCase
29
{
30
    use ConfigMockery;
31
    use IOMockery;
32
    use CHMockery;
33
    use HookMockery;
34
35
    public function testSetInvalidHook(): void
36
    {
37
        $this->expectException(InvalidHookName::class);
38
39
        $io       = $this->createIOMock();
40
        $config   = $this->createConfigMock();
41
        $repo     = $this->createRepositoryMock();
42
        $template = $this->createTemplateMock();
43
44
        $runner = new Installer($io, $config, $repo, $template);
45
        $runner->setHook('itDoNotExist');
46
    }
47
48
    public function testSetMultipleInvalidHooks(): void
49
    {
50
        $this->expectException(InvalidHookName::class);
51
52
        $io       = $this->createIOMock();
53
        $config   = $this->createConfigMock();
54
        $repo     = $this->createRepositoryMock();
55
        $template = $this->createTemplateMock();
56
57
        $runner = new Installer($io, $config, $repo, $template);
58
        $runner->setHook('itDoNotExist1,itDoNotExist2,itDontExist3');
59
    }
60
61
    public function testSetHookAndEnabledOnly(): void
62
    {
63
        $this->expectException(Exception::class);
64
65
        $io       = $this->createIOMock();
66
        $config   = $this->createConfigMock();
67
        $repo     = $this->createRepositoryMock();
68
        $template = $this->createTemplateMock();
69
70
        $runner = new Installer($io, $config, $repo, $template);
71
        $runner->setOnlyEnabled(true);
72
        $runner->setHook('pre-push');
73
    }
74
75
    public function testMoveAfterSkippingFail(): void
76
    {
77
        $this->expectException(RuntimeException::class);
78
79
        $io       = $this->createIOMock();
80
        $config   = $this->createConfigMock();
81
        $repo     = $this->createRepositoryMock();
82
        $template = $this->createTemplateMock();
83
84
        $runner = new Installer($io, $config, $repo, $template);
85
        $runner->setHook('pre-commit');
86
        $runner->setSkipExisting(true);
87
        $runner->setMoveExistingTo('/tmp/');
88
    }
89
90
    public function testSkipAfterMovingFail(): void
91
    {
92
        $this->expectException(RuntimeException::class);
93
94
        $io       = $this->createIOMock();
95
        $config   = $this->createConfigMock();
96
        $repo     = $this->createRepositoryMock();
97
        $template = $this->createTemplateMock();
98
99
        $runner = new Installer($io, $config, $repo, $template);
100
        $runner->setHook('pre-commit');
101
        $runner->setMoveExistingTo('/tmp/');
102
        $runner->setSkipExisting(true);
103
    }
104
105
    public function testHookInstallationDeclined(): void
106
    {
107
        $fakeRepo = new DummyRepo();
108
109
        $io       = $this->createIOMock();
110
        $config   = $this->createConfigMock();
111
        $repo     = $this->createRepositoryMock($fakeRepo->getRoot());
112
        $template = $this->createTemplateMock();
113
114
        $io->expects($this->atLeast(5))->method('ask')->willReturn('n');
115
116
        $runner = new Installer($io, $config, $repo, $template);
117
        $runner->setHook('');
118
        $runner->run();
119
120
        $this->assertFileDoesNotExist($fakeRepo->getHookDir() . '/pre-commit');
121
        $this->assertFileDoesNotExist($fakeRepo->getHookDir() . '/pre-push');
122
    }
123
124
    public function testWriteHook(): void
125
    {
126
        $fakeRepo = new DummyRepo();
127
128
        $io       = $this->createIOMock();
129
        $config   = $this->createConfigMock();
130
        $repo     = $this->createRepositoryMock($fakeRepo->getRoot());
131
        $template = $this->createTemplateMock();
132
133
        $template->expects($this->once())
134
                 ->method('getCode')
135
                 ->with('pre-commit')
136
                 ->willReturn('');
137
138
        $runner = new Installer($io, $config, $repo, $template);
139
        $runner->setHook('pre-commit');
140
        $runner->run();
141
142
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-commit');
143
    }
144
145
    public function testDontWriteToDevNull(): void
146
    {
147
        $fakeRepo = new DummyRepo();
148
149
        $io       = $this->createIOMock();
150
        $config   = $this->createConfigMock();
151
        $template = $this->createTemplateMock();
152
        $repo     = $this->createRepositoryMock($fakeRepo->getRoot(), '/dev/null');
153
154
        $template->expects($this->never())->method('getCode');
155
156
        $runner = new Installer($io, $config, $repo, $template);
157
        $runner->setHook('pre-commit');
158
        $runner->run();
159
160
        $this->assertFileDoesNotExist($fakeRepo->getHookDir() . '/pre-commit');
161
    }
162
163
    public function testBrokenSymlinkDetectionOnExistingFile(): void
164
    {
165
        $io       = $this->createIOMock();
166
        $config   = $this->createConfigMock();
167
        $repo     = $this->createRepositoryMock();
168
        $template = $this->createTemplateMock();
169
170
        $file = $this->getMockBuilder(File::class)
171
                     ->disableOriginalConstructor()
172
                     ->getMock();
173
174
        $runner = new FakeInstaller($io, $config, $repo, $template);
175
        $file->method('isLink')->willReturn(true);
176
        $file->method('linkTarget')->willReturn(__FILE__);
177
178
        $runner->checkSymlink($file);
179
        $this->assertTrue(true);
180
    }
181
182
    public function testBrokenSymlinkDetectionOnBrokenAbsoluteSymlink(): void
183
    {
184
        $this->expectException(Exception::class);
185
186
        $io       = $this->createIOMock();
187
        $config   = $this->createConfigMock();
188
        $repo     = $this->createRepositoryMock();
189
        $template = $this->createTemplateMock();
190
191
        $file = $this->getMockBuilder(File::class)
192
                     ->disableOriginalConstructor()
193
                     ->getMock();
194
195
        $runner = new FakeInstaller($io, $config, $repo, $template);
196
        $file->method('isLink')->willReturn(true);
197
        $file->method('linkTarget')->willReturn('/foo/bar/baz');
198
199
        $runner->checkSymlink($file);
200
    }
201
202
    public function testBrokenSymlinkDetectionOnBrokenRelativeSymlink(): void
203
    {
204
        $this->expectException(Exception::class);
205
206
        $io       = $this->createIOMock();
207
        $config   = $this->createConfigMock();
208
        $repo     = $this->createRepositoryMock();
209
        $template = $this->createTemplateMock();
210
211
        $file = $this->getMockBuilder(File::class)
212
            ->disableOriginalConstructor()
213
            ->getMock();
214
215
        $runner = new FakeInstaller($io, $config, $repo, $template);
216
        $file->method('isLink')->willReturn(true);
217
        $file->method('linkTarget')->willReturn('../../foo/bar/baz');
218
219
        $runner->checkSymlink($file);
220
    }
221
222
    public function testWriteMultipleHooks(): void
223
    {
224
        $fakeRepo = new DummyRepo();
225
226
        $io       = $this->createIOMock();
227
        $config   = $this->createConfigMock();
228
        $repo     = $this->createRepositoryMock($fakeRepo->getRoot());
229
        $template = $this->createTemplateMock();
230
231
        $runner = new Installer($io, $config, $repo, $template);
232
        $runner->setHook('pre-commit,pre-push,post-checkout');
233
        $runner->run();
234
235
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-commit');
236
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-push');
237
        $this->assertFileExists($fakeRepo->getHookDir() . '/post-checkout');
238
    }
239
240
    public function testMoveExistingHook(): void
241
    {
242
        $fakeRepo = new DummyRepo(
243
            // git repo
244
            [
245
                'config' => '# fake git config',
246
                'hooks'  => [
247
                    'pre-commit' => '# fake pre-commit file',
248
                    'pre-push'   => '# fake pre-push file',
249
                ]
250
            ],
251
            // files
252
            [
253
                'foo' => []
254
            ]
255
        );
256
257
        $io       = $this->createIOMock();
258
        $config   = $this->createConfigMock(true, $fakeRepo->getRoot() . '/captainhook.json');
259
        $template = $this->createTemplateMock();
260
        $repo     = new Repository($fakeRepo->getRoot());
261
262
        $template->expects($this->once())
263
                 ->method('getCode')
264
                 ->with('pre-commit')
265
                 ->willReturn('');
266
267
        $runner = new Installer($io, $config, $repo, $template);
268
        $runner->setHook('pre-commit')
269
               ->setMoveExistingTo('foo/bar/')
270
               ->run();
271
272
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-commit');
273
        $this->assertFileExists($fakeRepo->getRoot() . '/foo/bar/pre-commit');
274
    }
275
276
    public function testMoveNotExistingHook(): void
277
    {
278
        $fakeRepo = new DummyRepo(
279
            // git repo
280
            [
281
                'config' => '# fake git config',
282
                'hooks'  => [
283
                    'pre-push' => '# fake pre-push file',
284
                ]
285
            ],
286
            // files
287
            [
288
                'foo' => []
289
            ]
290
        );
291
292
        $io       = $this->createIOMock();
293
        $config   = $this->createConfigMock(true, $fakeRepo->getRoot() . '/captainhook.json');
294
        $template = $this->createTemplateMock();
295
        $repo     = new Repository($fakeRepo->getRoot());
296
297
        $template->expects($this->once())
298
                 ->method('getCode')
299
                 ->with('pre-commit')
300
                 ->willReturn('');
301
302
        $runner = new Installer($io, $config, $repo, $template);
303
        $runner->setHook('pre-commit')
304
               ->setMoveExistingTo('foo/bar/')
305
               ->run();
306
307
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-commit');
308
    }
309
310
    public function testMoveExistingHookTargetIsFile(): void
311
    {
312
        $this->expectException(RuntimeException::class);
313
314
        $fakeRepo = new DummyRepo(
315
            // git repo
316
            [
317
                'config' => '# fake git config',
318
                'hooks'  => [
319
                    'pre-commit' => '# fake pre-commit file',
320
                    'pre-push'   => '# fake pre-push file',
321
                ]
322
            ],
323
            // files
324
            [
325
                'foo' => '# some random file'
326
            ]
327
        );
328
329
        $io       = $this->createIOMock();
330
        $config   = $this->createConfigMock(true, $fakeRepo->getRoot() . '/captainhook.json');
331
        $template = $this->createTemplateMock();
332
        $repo     = new Repository($fakeRepo->getRoot());
333
334
        $runner = new Installer($io, $config, $repo, $template);
335
        $runner->setHook('pre-commit')
336
               ->setMoveExistingTo('foo')
337
               ->run();
338
    }
339
340
    public function testSkipExisting(): void
341
    {
342
        $io       = $this->createIOMock();
343
        $config   = $this->createConfigMock();
344
        $repo     = $this->createRepositoryMock();
345
        $template = $this->createTemplateMock();
346
347
        $io->expects($this->atLeast(1))->method('write');
348
        $repo->expects($this->once())->method('hookExists')->willReturn(true);
349
350
        $runner = new Installer($io, $config, $repo, $template);
351
        $runner->setSkipExisting(true);
352
        $runner->setHook('pre-commit');
353
        $runner->run();
354
    }
355
356
    public function testOnlyEnabledAndHooksToHandle(): void
357
    {
358
        $this->expectException(Exception::class);
359
360
        $io       = $this->createIOMock();
361
        $config   = $this->createConfigMock();
362
        $repo     = $this->createRepositoryMock();
363
        $template = $this->createTemplateMock();
364
365
        $runner = new Installer($io, $config, $repo, $template);
366
        $runner->setHook('pre-commit');
367
        $runner->setOnlyEnabled(true);
368
    }
369
370
    public function testDeclineOverwrite(): void
371
    {
372
        $io       = $this->createIOMock();
373
        $config   = $this->createConfigMock();
374
        $repo     = $this->createRepositoryMock();
375
        $template = $this->createTemplateMock();
376
377
        $io->expects($this->once())->method('ask')->willReturn('n');
378
        $repo->expects($this->once())->method('hookExists')->willReturn(true);
379
380
        $runner = new Installer($io, $config, $repo, $template);
381
        $runner->setHook('pre-commit');
382
        $runner->run();
383
    }
384
385
    public function testMoveExistingHookWhenMoveExistingIsAnAbsolutePath(): void
386
    {
387
        $virtualFs = vfsStream::setup('root');
388
389
        $fakeRepo = new DummyRepo(
390
        // git repo
391
            [
392
                'config' => '# fake git config',
393
                'hooks'  => [
394
                    'pre-commit' => '# fake pre-commit file',
395
                    'pre-push'   => '# fake pre-push file',
396
                ]
397
            ],
398
            // files
399
            [
400
                'foo' => []
401
            ]
402
        );
403
404
        $io       = $this->createIOMock();
405
        $config   = $this->createConfigMock(true, $fakeRepo->getRoot() . '/captainhook.json');
406
        $template = $this->createTemplateMock();
407
        $repo     = new Repository($fakeRepo->getRoot());
408
409
        $template->expects($this->once())
410
            ->method('getCode')
411
            ->with('pre-commit')
412
            ->willReturn('');
413
414
        $runner = new Installer($io, $config, $repo, $template);
415
        $runner->setHook('pre-commit')
416
            ->setMoveExistingTo($virtualFs->url() . '/foo/bar')
417
            ->run();
418
419
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-commit');
420
        $this->assertFileExists($fakeRepo->getRoot() . '/foo/bar/pre-commit');
421
    }
422
}
423