packages/deriver/tests/Unit/Configuration/LoaderTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Configuration;
6
7use Deriver\Configuration\Loader;
8use Deriver\Exception\ConfigurationException;
9use org\bovigo\vfs\vfsStream;
10use Override;
11use phpmock\Mock;
12use phpmock\MockBuilder;
13use PHPUnit\Framework\Attributes\CoversNothing;
14use PHPUnit\Framework\Attributes\PreserveGlobalState;
15use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
16use PHPUnit\Framework\TestCase;
17
18#[CoversNothing]
19#[RunTestsInSeparateProcesses]
20#[PreserveGlobalState(false)]
21final class LoaderTest extends TestCase
22{
23    /**
24     * @throws \phpmock\MockEnabledException
25     */
26    #[Override]
27    protected function setUp(): void
28    {
29        parent::setUp();
30        vfsStream::setup('project', null, ['config' => ['deliver.yaml' => "paths: [src, /external]\nphpVersion: '8.4'\n"]]);
31        (new MockBuilder())->setNamespace('Deriver\Configuration')->setName('is_file')->setFunction(static fn (string $path): bool => is_file('vfs:/' . $path))->build()->enable();
32        (new MockBuilder())->setNamespace('Deriver\Configuration')->setName('is_readable')->setFunction(static fn (string $path): bool => is_readable('vfs:/' . $path))->build()->enable();
33        (new MockBuilder())->setNamespace('Deriver\Configuration')->setName('file_get_contents')->setFunction(static fn (string $path): string|false => file_get_contents('vfs:/' . $path))->build()->enable();
34    }
35
36    public function testLoadAnchorsRelativeRootsToTheConfigurationDirectory(): void
37    {
38        $configuration = (new Loader())->load('/project/config/deliver.yaml');
39        self::assertSame(['/project/config/src', '/external'], $configuration->paths);
40        self::assertSame('8.4', $configuration->phpVersion);
41    }
42
43    public function testLoadWithoutAPathUsesDefaults(): void
44    {
45        self::assertSame(['.'], (new Loader())->load()->paths);
46    }
47
48    public function testLoadAnchorsDefaultRootForAnEmptyFile(): void
49    {
50        file_put_contents('vfs://project/config/deliver.yaml', '');
51        self::assertSame(['/project/config/.'], (new Loader())->load('/project/config/deliver.yaml')->paths);
52    }
53
54    public function testLoadRejectsMissingFiles(): void
55    {
56        $this->expectException(ConfigurationException::class);
57        (new Loader())->load('/project/missing.yaml');
58    }
59
60    public function testLoadRejectsMalformedYamlWithItsCause(): void
61    {
62        file_put_contents('vfs://project/config/deliver.yaml', "paths: [\n");
63        $this->expectException(ConfigurationException::class);
64        $this->expectExceptionMessage('Cannot load configuration');
65        (new Loader())->load('/project/config/deliver.yaml');
66    }
67
68    public function testLoadRejectsScalarDocuments(): void
69    {
70        file_put_contents('vfs://project/config/deliver.yaml', 'not-a-mapping');
71        $this->expectException(ConfigurationException::class);
72        (new Loader())->load('/project/config/deliver.yaml');
73    }
74
75    public function testLoadRejectsObjectTags(): void
76    {
77        file_put_contents('vfs://project/config/deliver.yaml', "environment: {APP_ENV: !php/object 'O:8:\"stdClass\":0:{}'}");
78        $this->expectException(ConfigurationException::class);
79        (new Loader())->load('/project/config/deliver.yaml');
80    }
81
82    #[Override]
83    protected function tearDown(): void
84    {
85        Mock::disableAll();
86        parent::tearDown();
87    }
88}
89