Skip to content

Commit aa1114c

Browse files
committed
Moved Docker Compose config test to PHPUnit.
1 parent 4c0013c commit aa1114c

11 files changed

Lines changed: 185 additions & 191 deletions

.vortex/.ahoy.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ commands:
66
install:
77
name: Install test dependencies.
88
cmd: |
9+
[ -d ./tests/vendor ] && rm -rf ./tests/vendor
10+
composer--working-dir tests install
911
[ -d ./tests/node_modules ] && rm -rf ./tests/node_modules
1012
yarn --cwd=tests install --frozen-lockfile
11-
[ -d ./docs/node_modules ] && rm -rf ./docs/node_modules
12-
yarn --cwd=docs install --frozen-lockfile
1313
[ -d ./installer/vendor ] && rm -rf ./installer/vendor
1414
composer --working-dir installer install
15+
[ -d ./docs/node_modules ] && rm -rf ./docs/node_modules
16+
yarn --cwd=docs install --frozen-lockfile
1517
1618
docs:
1719
name: Start documentation server.
@@ -89,12 +91,11 @@ commands:
8991
# If there are changes to the fixtures - this command will re-run twice reporting error the first time.
9092
update-fixtures:
9193
cmd: |
92-
export UPDATE_FIXTURES=1
93-
tests/node_modules/.bin/bats tests/bats/e2e/docker-compose.bats || tests/node_modules/.bin/bats tests/e2e/bats/docker-compose.bats
9494
export XDEBUG_MODE=off
9595
export COMPOSER_PROCESS_TIMEOUT=600
96-
composer --working-dir=installer test -- --filter=testInstall@baseline || exit 1
97-
composer --working-dir=installer test -- --filter=testInstall || composer --working-dir=installer test -- --filter=testInstall
96+
composer --working-dir=tests test-fixtures || composer --working-dir=tests test-fixtures
97+
composer --working-dir=installer test-baseline || exit 1
98+
composer --working-dir=installer test-fixtures || composer --working-dir=installer test-fixtures
9899
99100
update-fixtures-install:
100101
cmd: |

.vortex/tests/bats/e2e/docker-compose.bats

Lines changed: 0 additions & 173 deletions
This file was deleted.

.vortex/tests/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"phpcbf"
5353
],
5454
"reset": "rm -Rf vendor",
55-
"test": "phpunit"
55+
"test": "phpunit",
56+
"test-fixtures": "UPDATE_FIXTURES=1 phpunit --no-coverage --filter=testDockerComposeConfig"
5657
}
5758
}
File renamed without changes.

.vortex/tests/bats/fixtures/docker-compose.env_local.json renamed to .vortex/tests/phpunit/Fixtures/docker-compose.env_local.json

File renamed without changes.

.vortex/tests/bats/fixtures/docker-compose.env_mod.json renamed to .vortex/tests/phpunit/Fixtures/docker-compose.env_mod.json

File renamed without changes.

.vortex/tests/bats/fixtures/docker-compose.noenv.json renamed to .vortex/tests/phpunit/Fixtures/docker-compose.noenv.json

File renamed without changes.
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DrevOps\Vortex\Tests\Functional;
6+
7+
use AlexSkrypnyk\File\File;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use PHPUnit\Framework\Attributes\Group;
10+
11+
/**
12+
* Tests Docker Compose configuration format and default variables.
13+
*/
14+
class DockerComposeTest extends FunctionalTestCase {
15+
16+
protected function setUp(): void {
17+
parent::setUp();
18+
19+
static::$sutInstallerEnv = [];
20+
}
21+
22+
#[Group('p0')]
23+
#[DataProvider('dataProviderDockerComposeConfig')]
24+
public function testDockerComposeConfig(string $expected_file, ?callable $before = NULL): void {
25+
$this->forceVolumesUnmounted();
26+
File::copy(static::$root . '/docker-compose.yml', 'docker-compose.yml');
27+
$this->adjustCodebaseForUnmountedVolumes();
28+
29+
if (is_callable($before)) {
30+
$before($this);
31+
}
32+
33+
$this->logSubstep('Validate configuration');
34+
$this->cmd('docker compose -f docker-compose.yml config', txt: 'Docker Compose configuration should be valid');
35+
36+
$this->logSubstep('Generate actual docker-compose.yml configuration as json');
37+
$this->cmd(
38+
cmd: 'docker compose -f docker-compose.yml config --format json',
39+
txt: 'Docker Compose configuration generation should succeed',
40+
env: [
41+
'PACKAGE_TOKEN' => FALSE,
42+
'CI' => 'true',
43+
],
44+
);
45+
File::dump('docker-compose.actual.json', $this->processGet()->getOutput());
46+
$this->processDockerComposeJson('docker-compose.actual.json');
47+
48+
$this->logSubstep('Prepare expected fixture');
49+
File::copy(static::$fixtures . DIRECTORY_SEPARATOR . $expected_file, $expected_file);
50+
File::replaceContentInFile($expected_file, static::$sut, 'FIXTURE_CUR_DIR');
51+
52+
if (getenv('UPDATE_FIXTURES')) {
53+
$this->logSubstep('Updating fixture file ' . $expected_file);
54+
File::copy('docker-compose.actual.json', static::$fixtures . DIRECTORY_SEPARATOR . $expected_file);
55+
}
56+
57+
$this->logSubstep('Compare with fixture');
58+
$this->assertFileEquals($expected_file, 'docker-compose.actual.json', 'Docker Compose configuration should match expected fixture');
59+
}
60+
61+
public static function dataProviderDockerComposeConfig(): array {
62+
return [
63+
['docker-compose.noenv.json'],
64+
[
65+
'docker-compose.env.json',
66+
function (): void {
67+
File::copy(static::$root . '/.env', '.env');
68+
},
69+
],
70+
[
71+
'docker-compose.env_mod.json',
72+
function (FunctionalTestCase $test): void {
73+
File::copy(static::$root . '/.env', '.env');
74+
75+
// Add modified environment variables.
76+
$test->fileAddVar('.env', 'COMPOSE_PROJECT_NAME', 'the_matrix');
77+
$test->fileAddVar('.env', 'WEBROOT', 'docroot');
78+
$test->fileAddVar('.env', 'VORTEX_DB_IMAGE', 'myorg/my_db_image');
79+
$test->fileAddVar('.env', 'XDEBUG_ENABLE', '1');
80+
$test->fileAddVar('.env', 'DRUPAL_SHIELD_USER', 'jane');
81+
$test->fileAddVar('.env', 'DRUPAL_SHIELD_PASS', 'passw');
82+
$test->fileAddVar('.env', 'DRUPAL_REDIS_ENABLED', '1');
83+
$test->fileAddVar('.env', 'LAGOON_ENVIRONMENT_TYPE', 'development');
84+
},
85+
],
86+
[
87+
'docker-compose.env_local.json',
88+
function (): void {
89+
File::copy(static::$root . '/.env', '.env');
90+
File::copy(static::$root . '/.env.local.example', '.env.local');
91+
},
92+
],
93+
];
94+
}
95+
96+
/**
97+
* Process docker-compose JSON output for comparison.
98+
*
99+
* This method normalizes the Docker Compose configuration JSON:
100+
* - Sorts all values recursively by key in alphabetical order
101+
* - Removes YAML anchors starting with 'x-'
102+
* - Normalizes version numbers to 'VERSION' placeholder
103+
* - Replaces HOME directory references with 'HOME' placeholder.
104+
*/
105+
protected function processDockerComposeJson(string $from, ?string $to = NULL): void {
106+
$to = $to ?: $from;
107+
108+
$data = json_decode(File::read($from), TRUE);
109+
110+
if (!is_array($data)) {
111+
throw new \RuntimeException('Invalid JSON in ' . $from);
112+
}
113+
114+
$this->ksortRecursive($data);
115+
116+
// Remove YAML anchors starting with 'x-'.
117+
$data = array_filter($data, function ($key): bool {
118+
return strpos($key, 'x-') !== 0;
119+
}, ARRAY_FILTER_USE_KEY);
120+
121+
array_walk_recursive($data, function (&$value): void {
122+
if ($value !== NULL && is_string($value) && preg_match('/:\d+\.\d+(\.\d+)?/', $value)) {
123+
$value = preg_replace('/:\d+\.\d+(?:\.\d+)?/', ':VERSION', $value);
124+
}
125+
});
126+
127+
array_walk_recursive($data, function (&$value): void {
128+
if (empty($_SERVER['HOME']) || !is_string($_SERVER['HOME'])) {
129+
throw new \RuntimeException('HOME environment variable is not set.');
130+
}
131+
if ($value !== NULL && is_string($value) && str_contains($value, $_SERVER['HOME'])) {
132+
$value = str_replace($_SERVER['HOME'], 'HOME', $value);
133+
}
134+
});
135+
136+
$processed_content = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
137+
138+
if ($processed_content === FALSE) {
139+
throw new \RuntimeException('Failed to encode processed JSON.');
140+
}
141+
142+
$processed_content = File::replaceContent($processed_content, static::$sut, 'FIXTURE_CUR_DIR');
143+
144+
File::dump($to, $processed_content . "\n");
145+
}
146+
147+
/**
148+
* Recursively sort arrays by key.
149+
*/
150+
protected function ksortRecursive(array &$array): void {
151+
foreach ($array as &$value) {
152+
if (is_array($value)) {
153+
$this->ksortRecursive($value);
154+
}
155+
}
156+
ksort($array);
157+
}
158+
159+
}

.vortex/tests/phpunit/Functional/FunctionalTestCase.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ protected function tearDown(): void {
7979
parent::tearDown();
8080
}
8181

82+
/**
83+
* {@inheritdoc}
84+
*/
85+
public static function locationsFixturesDir(): string {
86+
return '.vortex/tests/phpunit/Fixtures';
87+
}
88+
8289
public function fixtureExportCodebase(string $src, string $dst): void {
8390
$current_dir = File::cwd();
8491
if (!File::exists($dst)) {

0 commit comments

Comments
 (0)