Skip to content

Commit 1da3e0c

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

13 files changed

Lines changed: 190 additions & 195 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/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Tests in this directory are end-to-end tests for Vortex, written using the Bats
2+
testing framework.
3+
4+
These are being ported to PHPUnit tests.
5+
See https://github.com/drevops/vortex/issues/1560
6+
7+
BATS will be used only for unit testing of scripts.

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

.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)