Passed
Push — main ( c1e8d7...13f864 )
by Sebastian
03:58
created

InstallerTest::testDontWriteToDevNull()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 10
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 16
rs 9.9332
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
    /**
36
     * Tests Installer::setHook
37
     *
38
     * @throws \CaptainHook\App\Exception\InvalidHookName
39
     */
40
    public function testSetInvalidHook(): void
41
    {
42
        $this->expectException(InvalidHookName::class);
43
44
        $io       = $this->createIOMock();
45
        $config   = $this->createConfigMock();
46
        $repo     = $this->createRepositoryMock();
47
        $template = $this->createTemplateMock();
48
49
        $runner = new Installer($io, $config, $repo, $template);
50
        $runner->setHook('itDoNotExist');
51
    }
52
53
    /**
54
     * Tests Installer::setHook
55
     *
56
     * @throws \CaptainHook\App\Exception\InvalidHookName
57
     */
58
    public function testSetMultipleInvalidHooks(): void
59
    {
60
        $this->expectException(InvalidHookName::class);
61
62
        $io       = $this->createIOMock();
63
        $config   = $this->createConfigMock();
64
        $repo     = $this->createRepositoryMock();
65
        $template = $this->createTemplateMock();
66
67
        $runner = new Installer($io, $config, $repo, $template);
68
        $runner->setHook('itDoNotExist1,itDoNotExist2,itDontExist3');
69
    }
70
71
    /**
72
     * Tests Installer::setHook
73
     *
74
     * @throws \CaptainHook\App\Exception\InvalidHookName
75
     */
76
    public function testSetHookAndEnabledOnly(): void
77
    {
78
        $this->expectException(Exception::class);
79
80
        $io       = $this->createIOMock();
81
        $config   = $this->createConfigMock();
82
        $repo     = $this->createRepositoryMock();
83
        $template = $this->createTemplateMock();
84
85
        $runner = new Installer($io, $config, $repo, $template);
86
        $runner->setOnlyEnabled(true);
87
        $runner->setHook('pre-push');
88
    }
89
90
    /**
91
     * Tests Installer::setHook
92
     *
93
     * @throws \CaptainHook\App\Exception\InvalidHookName
94
     */
95
    public function testMoveAfterSkippingFail(): void
96
    {
97
        $this->expectException(RuntimeException::class);
98
99
        $io       = $this->createIOMock();
100
        $config   = $this->createConfigMock();
101
        $repo     = $this->createRepositoryMock();
102
        $template = $this->createTemplateMock();
103
104
        $runner = new Installer($io, $config, $repo, $template);
105
        $runner->setHook('pre-commit');
106
        $runner->setSkipExisting(true);
107
        $runner->setMoveExistingTo('/tmp/');
108
    }
109
110
    /**
111
     * Tests Installer::setHook
112
     *
113
     * @throws \CaptainHook\App\Exception\InvalidHookName
114
     */
115
    public function testSkipAfterMovingFail(): void
116
    {
117
        $this->expectException(RuntimeException::class);
118
119
        $io       = $this->createIOMock();
120
        $config   = $this->createConfigMock();
121
        $repo     = $this->createRepositoryMock();
122
        $template = $this->createTemplateMock();
123
124
        $runner = new Installer($io, $config, $repo, $template);
125
        $runner->setHook('pre-commit');
126
        $runner->setMoveExistingTo('/tmp/');
127
        $runner->setSkipExisting(true);
128
    }
129
130
    /**
131
     * Tests Installer::run
132
     */
133
    public function testHookInstallationDeclined(): void
134
    {
135
        $fakeRepo = new DummyRepo();
136
137
        $io       = $this->createIOMock();
138
        $config   = $this->createConfigMock();
139
        $repo     = $this->createRepositoryMock($fakeRepo->getRoot());
140
        $template = $this->createTemplateMock();
141
142
        $io->expects($this->atLeast(5))->method('ask')->willReturn('n');
143
144
        $runner = new Installer($io, $config, $repo, $template);
145
        $runner->setHook('');
146
        $runner->run();
147
148
        $this->assertFileDoesNotExist($fakeRepo->getHookDir() . '/pre-commit');
149
        $this->assertFileDoesNotExist($fakeRepo->getHookDir() . '/pre-push');
150
    }
151
152
    /**
153
     * Tests Installer::run
154
     */
155
    public function testWriteHook(): void
156
    {
157
        $fakeRepo = new DummyRepo();
158
159
        $io       = $this->createIOMock();
160
        $config   = $this->createConfigMock();
161
        $repo     = $this->createRepositoryMock($fakeRepo->getRoot());
162
        $template = $this->createTemplateMock();
163
164
        $template->expects($this->once())
165
                 ->method('getCode')
166
                 ->with('pre-commit')
167
                 ->willReturn('');
168
169
        $runner = new Installer($io, $config, $repo, $template);
170
        $runner->setHook('pre-commit');
171
        $runner->run();
172
173
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-commit');
174
    }
175
176
    public function testDontWriteToDevNull(): void
177
    {
178
        $fakeRepo = new DummyRepo();
179
180
        $io       = $this->createIOMock();
181
        $config   = $this->createConfigMock();
182
        $template = $this->createTemplateMock();
183
        $repo     = $this->createRepositoryMock($fakeRepo->getRoot(), '/dev/null');
184
185
        $template->expects($this->never())->method('getCode');
186
187
        $runner = new Installer($io, $config, $repo, $template);
188
        $runner->setHook('pre-commit');
189
        $runner->run();
190
191
        $this->assertFileDoesNotExist($fakeRepo->getHookDir() . '/pre-commit');
192
    }
193
194
    /**
195
     * Tests Installer::checkForBrokenSymlinks
196
     */
197
    public function testBrokenSymlinkDetectionOnExistingFile(): void
198
    {
199
        $io       = $this->createIOMock();
200
        $config   = $this->createConfigMock();
201
        $repo     = $this->createRepositoryMock();
202
        $template = $this->createTemplateMock();
203
204
        $file = $this->getMockBuilder(File::class)
205
                     ->disableOriginalConstructor()
206
                     ->getMock();
207
208
        $runner = new FakeInstaller($io, $config, $repo, $template);
209
        $file->method('isLink')->willReturn(true);
210
        $file->method('linkTarget')->willReturn(__FILE__);
211
212
        $runner->checkSymlink($file);
213
        $this->assertTrue(true);
214
    }
215
216
    /**
217
     * Tests Installer::checkForBrokenSymlinks
218
     */
219
    public function testBrokenSymlinkDetectionOnBrokenAbsoluteSymlink(): void
220
    {
221
        $this->expectException(Exception::class);
222
223
        $io       = $this->createIOMock();
224
        $config   = $this->createConfigMock();
225
        $repo     = $this->createRepositoryMock();
226
        $template = $this->createTemplateMock();
227
228
        $file = $this->getMockBuilder(File::class)
229
                     ->disableOriginalConstructor()
230
                     ->getMock();
231
232
        $runner = new FakeInstaller($io, $config, $repo, $template);
233
        $file->method('isLink')->willReturn(true);
234
        $file->method('linkTarget')->willReturn('/foo/bar/baz');
235
236
        $runner->checkSymlink($file);
237
    }
238
239
    /**
240
     * Tests Installer::checkForBrokenSymlinks
241
     */
242
    public function testBrokenSymlinkDetectionOnBrokenRelativeSymlink(): void
243
    {
244
        $this->expectException(Exception::class);
245
246
        $io       = $this->createIOMock();
247
        $config   = $this->createConfigMock();
248
        $repo     = $this->createRepositoryMock();
249
        $template = $this->createTemplateMock();
250
251
        $file = $this->getMockBuilder(File::class)
252
            ->disableOriginalConstructor()
253
            ->getMock();
254
255
        $runner = new FakeInstaller($io, $config, $repo, $template);
256
        $file->method('isLink')->willReturn(true);
257
        $file->method('linkTarget')->willReturn('../../foo/bar/baz');
258
259
        $runner->checkSymlink($file);
260
    }
261
262
    /**
263
     * Tests Installer::run
264
     */
265
    public function testWriteMultipleHooks(): void
266
    {
267
        $fakeRepo = new DummyRepo();
268
269
        $io       = $this->createIOMock();
270
        $config   = $this->createConfigMock();
271
        $repo     = $this->createRepositoryMock($fakeRepo->getRoot());
272
        $template = $this->createTemplateMock();
273
274
        $runner = new Installer($io, $config, $repo, $template);
275
        $runner->setHook('pre-commit,pre-push,post-checkout');
276
        $runner->run();
277
278
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-commit');
279
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-push');
280
        $this->assertFileExists($fakeRepo->getHookDir() . '/post-checkout');
281
    }
282
283
    /**
284
     * Tests Installer::run
285
     */
286
    public function testMoveExistingHook(): void
287
    {
288
        $fakeRepo = new DummyRepo(
289
            // git repo
290
            [
291
                'config' => '# fake git config',
292
                'hooks'  => [
293
                    'pre-commit' => '# fake pre-commit file',
294
                    'pre-push'   => '# fake pre-push file',
295
                ]
296
            ],
297
            // files
298
            [
299
                'foo' => []
300
            ]
301
        );
302
303
        $io       = $this->createIOMock();
304
        $config   = $this->createConfigMock(true, $fakeRepo->getRoot() . '/captainhook.json');
305
        $template = $this->createTemplateMock();
306
        $repo     = new Repository($fakeRepo->getRoot());
307
308
        $template->expects($this->once())
309
                 ->method('getCode')
310
                 ->with('pre-commit')
311
                 ->willReturn('');
312
313
        $runner = new Installer($io, $config, $repo, $template);
314
        $runner->setHook('pre-commit')
315
               ->setMoveExistingTo('foo/bar/')
316
               ->run();
317
318
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-commit');
319
        $this->assertFileExists($fakeRepo->getRoot() . '/foo/bar/pre-commit');
320
    }
321
322
    /**
323
     * Tests Installer::run
324
     */
325
    public function testMoveNotExistingHook(): void
326
    {
327
        $fakeRepo = new DummyRepo(
328
            // git repo
329
            [
330
                'config' => '# fake git config',
331
                'hooks'  => [
332
                    'pre-push' => '# fake pre-push file',
333
                ]
334
            ],
335
            // files
336
            [
337
                'foo' => []
338
            ]
339
        );
340
341
        $io       = $this->createIOMock();
342
        $config   = $this->createConfigMock(true, $fakeRepo->getRoot() . '/captainhook.json');
343
        $template = $this->createTemplateMock();
344
        $repo     = new Repository($fakeRepo->getRoot());
345
346
        $template->expects($this->once())
347
                 ->method('getCode')
348
                 ->with('pre-commit')
349
                 ->willReturn('');
350
351
        $runner = new Installer($io, $config, $repo, $template);
352
        $runner->setHook('pre-commit')
353
               ->setMoveExistingTo('foo/bar/')
354
               ->run();
355
356
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-commit');
357
    }
358
359
    /**
360
     * Tests Installer::run
361
     */
362
    public function testMoveExistingHookTargetIsFile(): void
363
    {
364
        $this->expectException(RuntimeException::class);
365
366
        $fakeRepo = new DummyRepo(
367
            // git repo
368
            [
369
                'config' => '# fake git config',
370
                'hooks'  => [
371
                    'pre-commit' => '# fake pre-commit file',
372
                    'pre-push'   => '# fake pre-push file',
373
                ]
374
            ],
375
            // files
376
            [
377
                'foo' => '# some random file'
378
            ]
379
        );
380
381
        $io       = $this->createIOMock();
382
        $config   = $this->createConfigMock(true, $fakeRepo->getRoot() . '/captainhook.json');
383
        $template = $this->createTemplateMock();
384
        $repo     = new Repository($fakeRepo->getRoot());
385
386
        $runner = new Installer($io, $config, $repo, $template);
387
        $runner->setHook('pre-commit')
388
               ->setMoveExistingTo('foo')
389
               ->run();
390
    }
391
392
    /**
393
     * Tests Installer::writeHookFile
394
     */
395
    public function testSkipExisting(): void
396
    {
397
        $io       = $this->createIOMock();
398
        $config   = $this->createConfigMock();
399
        $repo     = $this->createRepositoryMock();
400
        $template = $this->createTemplateMock();
401
402
        $io->expects($this->atLeast(1))->method('write');
403
        $repo->expects($this->once())->method('hookExists')->willReturn(true);
404
405
        $runner = new Installer($io, $config, $repo, $template);
406
        $runner->setSkipExisting(true);
407
        $runner->setHook('pre-commit');
408
        $runner->run();
409
    }
410
411
    /**
412
     * Tests Installer::writeHookFile
413
     */
414
    public function testOnlyEnabledAndHooksToHandle(): void
415
    {
416
        $this->expectException(Exception::class);
417
418
        $io       = $this->createIOMock();
419
        $config   = $this->createConfigMock();
420
        $repo     = $this->createRepositoryMock();
421
        $template = $this->createTemplateMock();
422
423
        $runner = new Installer($io, $config, $repo, $template);
424
        $runner->setHook('pre-commit');
425
        $runner->setOnlyEnabled(true);
426
    }
427
428
    /**
429
     * Tests Installer::writeHookFile
430
     */
431
    public function testDeclineOverwrite(): void
432
    {
433
        $io       = $this->createIOMock();
434
        $config   = $this->createConfigMock();
435
        $repo     = $this->createRepositoryMock();
436
        $template = $this->createTemplateMock();
437
438
        $io->expects($this->once())->method('ask')->willReturn('n');
439
        $repo->expects($this->once())->method('hookExists')->willReturn(true);
440
441
        $runner = new Installer($io, $config, $repo, $template);
442
        $runner->setHook('pre-commit');
443
        $runner->run();
444
    }
445
446
    public function testMoveExistingHookWhenMoveExistingIsAnAbsolutePath(): void
447
    {
448
        $virtualFs = vfsStream::setup('root');
449
450
        $fakeRepo = new DummyRepo(
451
        // git repo
452
            [
453
                'config' => '# fake git config',
454
                'hooks'  => [
455
                    'pre-commit' => '# fake pre-commit file',
456
                    'pre-push'   => '# fake pre-push file',
457
                ]
458
            ],
459
            // files
460
            [
461
                'foo' => []
462
            ]
463
        );
464
465
        $io       = $this->createIOMock();
466
        $config   = $this->createConfigMock(true, $fakeRepo->getRoot() . '/captainhook.json');
467
        $template = $this->createTemplateMock();
468
        $repo     = new Repository($fakeRepo->getRoot());
469
470
        $template->expects($this->once())
471
            ->method('getCode')
472
            ->with('pre-commit')
473
            ->willReturn('');
474
475
        $runner = new Installer($io, $config, $repo, $template);
476
        $runner->setHook('pre-commit')
477
            ->setMoveExistingTo($virtualFs->url() . '/foo/bar')
478
            ->run();
479
480
        $this->assertFileExists($fakeRepo->getHookDir() . '/pre-commit');
481
        $this->assertFileExists($fakeRepo->getRoot() . '/foo/bar/pre-commit');
482
    }
483
}
484