From 51869d77413a2857a06776e79577828c14269089 Mon Sep 17 00:00:00 2001 From: Yanick Witschi Date: Wed, 30 Sep 2026 18:32:59 +0200 Subject: [PATCH 1/3] Warm up Docker services before E2E tests --- e2e-testing/README.md | 60 ++++++++++++++-- .../src/Database/DatabaseReadinessProbe.php | 8 ++- e2e-testing/src/Database/DockerClient.php | 9 ++- .../src/Database/DockerDatabaseServer.php | 53 ++++++++++----- .../src/Database/DockerDatabaseService.php | 41 +++++++++++ .../src/Docker/DockerServiceInterface.php | 20 ++++++ .../Docker/DockerServiceProviderInterface.php | 21 ++++++ .../AbstractManagedEditionTestCase.php | 26 +++++++ .../ManagedEdition/ManagedEditionConfig.php | 17 +++++ .../src/PhpUnit/DockerWarmUpExtension.php | 50 ++++++++++++++ e2e-testing/src/PhpUnit/DockerWarmUpPlan.php | 58 ++++++++++++++++ .../src/PhpUnit/DockerWarmUpSubscriber.php | 40 +++++++++++ .../AbstractManagedEditionTestCaseTest.php | 29 ++++++++ .../tests/DockerDatabaseServerTest.php | 67 ++++++++++++++++++ e2e-testing/tests/DockerWarmUpPlanTest.php | 66 ++++++++++++++++++ .../tests/DockerWarmUpSubscriberTest.php | 68 +++++++++++++++++++ .../AbstractManagedEditionTestCase.php | 17 +++-- .../Fixture/DockerWarmUpProviderTestCase.php | 47 +++++++++++++ .../tests/ManagedEditionConfigTest.php | 17 +++++ phpunit.xml.dist | 9 ++- 20 files changed, 691 insertions(+), 32 deletions(-) create mode 100644 e2e-testing/src/Database/DockerDatabaseService.php create mode 100644 e2e-testing/src/Docker/DockerServiceInterface.php create mode 100644 e2e-testing/src/Docker/DockerServiceProviderInterface.php create mode 100644 e2e-testing/src/ManagedEdition/AbstractManagedEditionTestCase.php create mode 100644 e2e-testing/src/PhpUnit/DockerWarmUpExtension.php create mode 100644 e2e-testing/src/PhpUnit/DockerWarmUpPlan.php create mode 100644 e2e-testing/src/PhpUnit/DockerWarmUpSubscriber.php create mode 100644 e2e-testing/tests/AbstractManagedEditionTestCaseTest.php create mode 100644 e2e-testing/tests/DockerDatabaseServerTest.php create mode 100644 e2e-testing/tests/DockerWarmUpPlanTest.php create mode 100644 e2e-testing/tests/DockerWarmUpSubscriberTest.php create mode 100644 e2e-testing/tests/Fixture/DockerWarmUpProviderTestCase.php diff --git a/e2e-testing/README.md b/e2e-testing/README.md index 80a5cf7..32ce45a 100644 --- a/e2e-testing/README.md +++ b/e2e-testing/README.md @@ -21,7 +21,53 @@ $mariaDb = $config->withDatabase(DockerDatabaseConfig::mariaDb('mariadb:10.11')) $mysql = $config->withDatabase(DockerDatabaseConfig::mysql('mysql:8.0')); ``` -Different types and image versions use independent reusable containers and storage directories. This makes those configurations suitable for a PHPUnit data provider or separate CI jobs. A CI matrix can configure the same tests without changing PHP code: +Different types and image versions use independent reusable containers and storage directories. This makes those configurations suitable for a PHPUnit data provider or separate CI jobs. + +Database warm-up is optional. Without the extension, each test starts its database variant on demand and waits until it is ready. When one PHPUnit run uses several database images, enable the PHPUnit extension in the E2E configuration to start them earlier: + +```xml + + + + + +``` + +The comma-separated `testsuites` parameter selects the PHPUnit suites whose Docker services should be warmed. `ManagedEditionConfig::dockerServices()` exposes the services implied by the configuration, and `AbstractManagedEditionTestCase` implements the provider by returning that collection. The complete bundle example below uses this base class. + +If a test already has another base class, it can continue using `ManagedEditionTestTrait` and advertise services directly by implementing `DockerServiceProviderInterface`: + +```php +use Contao\E2eTesting\Docker\DockerServiceProviderInterface; +use Contao\E2eTesting\ManagedEdition\ManagedEditionConfig; +use Contao\E2eTesting\ManagedEdition\ManagedEditionTestTrait; +use Contao\InstallationRecipe\Composer\ComposerConfig; +use Contao\InstallationRecipe\Recipe\InstallationRecipe; +use PHPUnit\Framework\TestCase; + +final class ManagedEditionSmokeTest extends TestCase implements DockerServiceProviderInterface +{ + use ManagedEditionTestTrait; + + protected static function createManagedEditionConfig(): ManagedEditionConfig + { + $bundleRoot = dirname(__DIR__, 2); + $composer = ComposerConfig::managedEdition('^5.7') + ->withPathPackage('acme/example-bundle', $bundleRoot, '1.0.x-dev'); + + return ManagedEditionConfig::create(InstallationRecipe::create($composer), $bundleRoot); + } + + public static function dockerServices(): iterable + { + return static::createManagedEditionConfig()->dockerServices(); + } +} +``` + +When a selected suite starts, the extension collects and deduplicates its services before warming them without waiting for readiness. Tests still wait for a service when they first use it. A subclass can advertise additional services by overriding `dockerServices()`, yielding from `parent::dockerServices()`, and then yielding its own services. Other suites, such as `unit`, do not start Docker. The provider mechanism is independent of `ManagedEditionTestTrait`, and future service types such as Redis can implement `DockerServiceInterface` without changing the PHPUnit extension. + +A CI matrix can configure the same tests without changing PHP code: ```shell CONTAO_E2E_DATABASE_TYPE=mysql CONTAO_E2E_DATABASE_IMAGE=mysql:8.0 composer e2e-tests @@ -70,16 +116,13 @@ The following example lives in a Contao bundle repository, not in this library. declare(strict_types=1); +use Contao\E2eTesting\ManagedEdition\AbstractManagedEditionTestCase; use Contao\E2eTesting\ManagedEdition\ManagedEditionConfig; -use Contao\E2eTesting\ManagedEdition\ManagedEditionTestTrait; use Contao\InstallationRecipe\Composer\ComposerConfig; use Contao\InstallationRecipe\Recipe\InstallationRecipe; -use PHPUnit\Framework\TestCase; -final class ManagedEditionSmokeTest extends TestCase +final class ManagedEditionSmokeTest extends AbstractManagedEditionTestCase { - use ManagedEditionTestTrait; - protected static function createManagedEditionConfig(): ManagedEditionConfig { $bundleRoot = dirname(__DIR__, 2); @@ -109,6 +152,11 @@ Add `tests/E2e` to your existing PHPUnit test suite, or use this minimal `phpuni tests/E2e + + + + + ``` diff --git a/e2e-testing/src/Database/DatabaseReadinessProbe.php b/e2e-testing/src/Database/DatabaseReadinessProbe.php index 2eb03d4..bd537e5 100644 --- a/e2e-testing/src/Database/DatabaseReadinessProbe.php +++ b/e2e-testing/src/Database/DatabaseReadinessProbe.php @@ -18,9 +18,13 @@ final readonly class DatabaseReadinessProbe { + public function __construct(private float $timeout = 60) + { + } + public function wait(DatabaseServerConfig $config): void { - $deadline = microtime(true) + 60; + $deadline = microtime(true) + $this->timeout; $lastException = null; do { @@ -40,6 +44,6 @@ public function wait(DatabaseServerConfig $config): void } } while (microtime(true) < $deadline); - throw new DockerUnavailableException('The Docker MariaDB server did not become ready within 60 seconds: '.$lastException->getMessage(), 0, $lastException); + throw new DockerUnavailableException(\sprintf('The Docker MariaDB server did not become ready within %g seconds: %s', $this->timeout, $lastException->getMessage()), 0, $lastException); } } diff --git a/e2e-testing/src/Database/DockerClient.php b/e2e-testing/src/Database/DockerClient.php index 603d066..c8bd53a 100644 --- a/e2e-testing/src/Database/DockerClient.php +++ b/e2e-testing/src/Database/DockerClient.php @@ -21,6 +21,13 @@ private const DATABASE_CONFIG_LABEL = 'org.contao.e2e.database-config'; + /** + * @param non-empty-list $command + */ + public function __construct(private array $command = ['docker']) + { + } + public function isRunning(string $container): bool { return 'true' === $this->optional(['inspect', '--format', '{{.State.Running}}', $container]); @@ -146,6 +153,6 @@ private function run(array $arguments): string */ private function process(array $arguments): Process { - return new Process(['docker', ...$arguments]); + return new Process([...$this->command, ...$arguments]); } } diff --git a/e2e-testing/src/Database/DockerDatabaseServer.php b/e2e-testing/src/Database/DockerDatabaseServer.php index b3d79e8..3cde10a 100644 --- a/e2e-testing/src/Database/DockerDatabaseServer.php +++ b/e2e-testing/src/Database/DockerDatabaseServer.php @@ -13,6 +13,7 @@ namespace Contao\E2eTesting\Database; use Contao\E2eTesting\Cache\CacheConfig; +use Contao\E2eTesting\Cache\WorkspaceInitializer; use Contao\E2eTesting\Exception\DockerUnavailableException; use Contao\E2eTesting\Exception\E2eTestException; use Symfony\Component\Filesystem\Filesystem; @@ -25,10 +26,44 @@ public function __construct( private DatabaseReadinessProbe $readinessProbe = new DatabaseReadinessProbe(), private Filesystem $filesystem = new Filesystem(), private DockerDatabaseLeaseRegistry $leaseRegistry = new DockerDatabaseLeaseRegistry(), + private WorkspaceInitializer $workspaceInitializer = new WorkspaceInitializer(), ) { } public function provide(CacheConfig $cache, DockerDatabaseConfig $database): DatabaseServerConfig + { + $config = $this->startContainer($cache, $database); + $this->readinessProbe->wait($config); + + return $config; + } + + public function warmUp(CacheConfig $cache, DockerDatabaseConfig ...$databases): void + { + $this->workspaceInitializer->initialize($cache); + + foreach ($databases as $database) { + $this->startContainer($cache, $database); + } + } + + public function stop(CacheConfig $cache, bool $force = false): void + { + $containers = $this->docker->find($this->containerPrefix($cache)); + $locks = $force ? [] : $this->acquireStopLocks($cache, $containers); + + try { + foreach ($containers as $container) { + $this->docker->stop($container); + } + } finally { + foreach ($locks as $lock) { + $lock->release(); + } + } + } + + private function startContainer(CacheConfig $cache, DockerDatabaseConfig $database): DatabaseServerConfig { $container = $this->containerName($cache, $database); $leasePath = $this->leasePath($cache, $container); @@ -52,27 +87,9 @@ public function provide(CacheConfig $cache, DockerDatabaseConfig $database): Dat fclose($lock); } - $this->readinessProbe->wait($config); - return $config; } - public function stop(CacheConfig $cache, bool $force = false): void - { - $containers = $this->docker->find($this->containerPrefix($cache)); - $locks = $force ? [] : $this->acquireStopLocks($cache, $containers); - - try { - foreach ($containers as $container) { - $this->docker->stop($container); - } - } finally { - foreach ($locks as $lock) { - $lock->release(); - } - } - } - private function start(CacheConfig $cache, DockerDatabaseConfig $database): DatabaseServerConfig { $container = $this->containerName($cache, $database); diff --git a/e2e-testing/src/Database/DockerDatabaseService.php b/e2e-testing/src/Database/DockerDatabaseService.php new file mode 100644 index 0000000..88f9a43 --- /dev/null +++ b/e2e-testing/src/Database/DockerDatabaseService.php @@ -0,0 +1,41 @@ +cache->projectDirectory, + $this->cache->rootDirectory, + 'database', + $this->config->fingerprint(), + ])); + } + + public function warmUp(): void + { + $this->server->warmUp($this->cache, $this->config); + } +} diff --git a/e2e-testing/src/Docker/DockerServiceInterface.php b/e2e-testing/src/Docker/DockerServiceInterface.php new file mode 100644 index 0000000..5e4858b --- /dev/null +++ b/e2e-testing/src/Docker/DockerServiceInterface.php @@ -0,0 +1,20 @@ + + */ + public static function dockerServices(): iterable; +} diff --git a/e2e-testing/src/ManagedEdition/AbstractManagedEditionTestCase.php b/e2e-testing/src/ManagedEdition/AbstractManagedEditionTestCase.php new file mode 100644 index 0000000..4e29f2f --- /dev/null +++ b/e2e-testing/src/ManagedEdition/AbstractManagedEditionTestCase.php @@ -0,0 +1,26 @@ +dockerServices(); + } +} diff --git a/e2e-testing/src/ManagedEdition/ManagedEditionConfig.php b/e2e-testing/src/ManagedEdition/ManagedEditionConfig.php index 1730f11..a642ea4 100644 --- a/e2e-testing/src/ManagedEdition/ManagedEditionConfig.php +++ b/e2e-testing/src/ManagedEdition/ManagedEditionConfig.php @@ -16,6 +16,8 @@ use Contao\E2eTesting\Database\DatabaseResetMode; use Contao\E2eTesting\Database\DatabaseServerConfig; use Contao\E2eTesting\Database\DockerDatabaseConfig; +use Contao\E2eTesting\Database\DockerDatabaseService; +use Contao\E2eTesting\Docker\DockerServiceInterface; use Contao\InstallationRecipe\File\FileMapping; use Contao\InstallationRecipe\Recipe\InstallationRecipe; @@ -47,6 +49,21 @@ public function withDatabase(DatabaseServerConfig|DockerDatabaseConfig $database return $this->withEnvironment($this->environment->withDatabase($database)); } + /** + * @return list + */ + public function dockerServices(): array + { + if ($this->environment->database instanceof DatabaseServerConfig) { + return []; + } + + return [new DockerDatabaseService( + $this->environment->cache, + $this->environment->database ?? DockerDatabaseConfig::fromEnvironment(), + )]; + } + public function withDcaFile(string $path): self { if (!is_file($path) || !str_ends_with($path, '.php')) { diff --git a/e2e-testing/src/PhpUnit/DockerWarmUpExtension.php b/e2e-testing/src/PhpUnit/DockerWarmUpExtension.php new file mode 100644 index 0000000..9a18801 --- /dev/null +++ b/e2e-testing/src/PhpUnit/DockerWarmUpExtension.php @@ -0,0 +1,50 @@ +registerSubscriber($this->subscriber($parameters)); + } + + private function subscriber(ParameterCollection $parameters): DockerWarmUpSubscriber + { + if (!$parameters->has('testsuites')) { + throw new E2eTestException('The Docker warm-up extension requires a comma-separated "testsuites" parameter.'); + } + + $testSuites = $this->values($parameters->get('testsuites')); + + if ([] === $testSuites) { + throw new E2eTestException('The Docker warm-up extension requires at least one test suite.'); + } + + return new DockerWarmUpSubscriber($testSuites); + } + + /** + * @return list + */ + private function values(string $value): array + { + return array_values(array_filter(array_map(trim(...), explode(',', $value)))); + } +} diff --git a/e2e-testing/src/PhpUnit/DockerWarmUpPlan.php b/e2e-testing/src/PhpUnit/DockerWarmUpPlan.php new file mode 100644 index 0000000..055c632 --- /dev/null +++ b/e2e-testing/src/PhpUnit/DockerWarmUpPlan.php @@ -0,0 +1,58 @@ + $services + */ + private function __construct(public array $services) + { + } + + public function warmUp(): void + { + foreach ($this->services as $service) { + $service->warmUp(); + } + } + + /** + * @param list $testSuites + * @param list $testClasses + */ + public static function forTestClasses(array $testSuites, string $testSuite, array $testClasses): self + { + if (!\in_array($testSuite, $testSuites, true)) { + return new self([]); + } + + $services = []; + + foreach (array_unique($testClasses) as $class) { + if (!is_subclass_of($class, DockerServiceProviderInterface::class)) { + continue; + } + + foreach ($class::dockerServices() as $service) { + $services[$service->fingerprint()] = $service; + } + } + + return new self(array_values($services)); + } +} diff --git a/e2e-testing/src/PhpUnit/DockerWarmUpSubscriber.php b/e2e-testing/src/PhpUnit/DockerWarmUpSubscriber.php new file mode 100644 index 0000000..342b10a --- /dev/null +++ b/e2e-testing/src/PhpUnit/DockerWarmUpSubscriber.php @@ -0,0 +1,40 @@ + $testSuites + */ + public function __construct(private array $testSuites) + { + } + + public function notify(Started $event): void + { + $classes = []; + + foreach ($event->testSuite()->tests() as $test) { + if ($test instanceof TestMethod) { + $classes[] = $test->className(); + } + } + + DockerWarmUpPlan::forTestClasses($this->testSuites, $event->testSuite()->name(), $classes)->warmUp(); + } +} diff --git a/e2e-testing/tests/AbstractManagedEditionTestCaseTest.php b/e2e-testing/tests/AbstractManagedEditionTestCaseTest.php new file mode 100644 index 0000000..1f00f34 --- /dev/null +++ b/e2e-testing/tests/AbstractManagedEditionTestCaseTest.php @@ -0,0 +1,29 @@ +assertCount(1, $services); + $this->assertInstanceOf(DockerDatabaseService::class, $services[0]); + $this->assertSame('mysql:8.4', $services[0]->config->image); + } +} diff --git a/e2e-testing/tests/DockerDatabaseServerTest.php b/e2e-testing/tests/DockerDatabaseServerTest.php new file mode 100644 index 0000000..21a953a --- /dev/null +++ b/e2e-testing/tests/DockerDatabaseServerTest.php @@ -0,0 +1,67 @@ +mkdir($project); + + $command = $project.'/docker.php'; + $log = $project.'/docker.log'; + $filesystem->dumpFile($command, $this->dockerCommand($log)); + $cache = CacheConfig::forProject($project); + $registry = new DockerDatabaseLeaseRegistry(); + $server = new DockerDatabaseServer( + new DockerClient([PHP_BINARY, $command]), + new DatabaseReadinessProbe(0), + leaseRegistry: $registry, + ); + + try { + $server->warmUp($cache, DockerDatabaseConfig::mariaDb()); + + $this->assertStringContainsString('"run"', (string) file_get_contents($log)); + } finally { + $registry->release($this->leasePath($cache)); + $filesystem->remove($project); + } + } + + private function dockerCommand(string $log): string + { + return 'projectDirectory), 0, 12); + + return Path::join($cache->rootDirectory, 'locks', $container.'.lock'); + } +} diff --git a/e2e-testing/tests/DockerWarmUpPlanTest.php b/e2e-testing/tests/DockerWarmUpPlanTest.php new file mode 100644 index 0000000..9241cd2 --- /dev/null +++ b/e2e-testing/tests/DockerWarmUpPlanTest.php @@ -0,0 +1,66 @@ +testClass(); + + $skippedPlan = DockerWarmUpPlan::forTestClasses(['e2e'], 'unit', [$testClass]); + $skippedPlan->warmUp(); + $this->assertSame(0, self::$warmUps); + + $plan = DockerWarmUpPlan::forTestClasses(['e2e'], 'e2e', [$testClass, $testClass, self::class]); + + $this->assertCount(1, $plan->services); + $this->assertSame('example', $plan->services[0]->fingerprint()); + + $plan->warmUp(); + $this->assertSame(1, self::$warmUps); + } + + /** + * @return class-string + */ + private function testClass(): string + { + $test = new class() implements DockerServiceProviderInterface { + public static function dockerServices(): iterable + { + yield new class() implements DockerServiceInterface { + public function fingerprint(): string + { + return 'example'; + } + + public function warmUp(): void + { + ++DockerWarmUpPlanTest::$warmUps; + } + }; + } + }; + + return $test::class; + } +} diff --git a/e2e-testing/tests/DockerWarmUpSubscriberTest.php b/e2e-testing/tests/DockerWarmUpSubscriberTest.php new file mode 100644 index 0000000..b846149 --- /dev/null +++ b/e2e-testing/tests/DockerWarmUpSubscriberTest.php @@ -0,0 +1,68 @@ +mkdir($directory); + $filesystem->dumpFile($configuration, $this->configuration($root, $directory)); + + $process = new Process( + [PHP_BINARY, $root.'/vendor/bin/phpunit', '--configuration='.$configuration], + $root, + ['CONTAO_E2E_WARM_UP_MARKER' => $marker], + ); + + try { + $process->mustRun(); + + $this->assertFileExists($marker); + } finally { + $filesystem->remove($directory); + } + } + + private function configuration(string $root, string $cache): string + { + $bootstrap = htmlspecialchars($root.'/vendor/autoload.php', ENT_XML1); + $fixture = htmlspecialchars(__DIR__.'/Fixture/DockerWarmUpProviderTestCase.php', ENT_XML1); + $cache = htmlspecialchars($cache.'/cache', ENT_XML1); + + return << + + + + $fixture + + + + + + + + + XML; + } +} diff --git a/e2e-testing/tests/Fixture/AbstractManagedEditionTestCase.php b/e2e-testing/tests/Fixture/AbstractManagedEditionTestCase.php index df0b1d0..7078e4d 100644 --- a/e2e-testing/tests/Fixture/AbstractManagedEditionTestCase.php +++ b/e2e-testing/tests/Fixture/AbstractManagedEditionTestCase.php @@ -12,10 +12,19 @@ namespace Contao\E2eTesting\Tests\Fixture; -use Contao\E2eTesting\ManagedEdition\ManagedEditionTestTrait; -use PHPUnit\Framework\TestCase; +use Contao\E2eTesting\Database\DockerDatabaseConfig; +use Contao\E2eTesting\ManagedEdition\AbstractManagedEditionTestCase as BaseManagedEditionTestCase; +use Contao\E2eTesting\ManagedEdition\ManagedEditionConfig; +use Contao\InstallationRecipe\Composer\ComposerConfig; +use Contao\InstallationRecipe\Recipe\InstallationRecipe; -abstract class AbstractManagedEditionTestCase extends TestCase +abstract class AbstractManagedEditionTestCase extends BaseManagedEditionTestCase { - use ManagedEditionTestTrait; + protected static function createManagedEditionConfig(): ManagedEditionConfig + { + return ManagedEditionConfig::create( + InstallationRecipe::create(ComposerConfig::managedEdition('^5.7')), + \dirname(__DIR__, 2), + )->withDatabase(DockerDatabaseConfig::mysql()); + } } diff --git a/e2e-testing/tests/Fixture/DockerWarmUpProviderTestCase.php b/e2e-testing/tests/Fixture/DockerWarmUpProviderTestCase.php new file mode 100644 index 0000000..d25e2c2 --- /dev/null +++ b/e2e-testing/tests/Fixture/DockerWarmUpProviderTestCase.php @@ -0,0 +1,47 @@ +assertIsString($marker); + $this->assertFileExists($marker); + } + + public static function dockerServices(): iterable + { + yield new class() implements DockerServiceInterface { + public function fingerprint(): string + { + return 'subscriber-test'; + } + + public function warmUp(): void + { + $marker = getenv('CONTAO_E2E_WARM_UP_MARKER'); + + if (!\is_string($marker) || false === file_put_contents($marker, 'warmed')) { + throw new \RuntimeException('Could not write the Docker warm-up marker.'); + } + } + }; + } +} diff --git a/e2e-testing/tests/ManagedEditionConfigTest.php b/e2e-testing/tests/ManagedEditionConfigTest.php index cfb10d8..2e6f7f1 100644 --- a/e2e-testing/tests/ManagedEditionConfigTest.php +++ b/e2e-testing/tests/ManagedEditionConfigTest.php @@ -12,7 +12,9 @@ namespace Contao\E2eTesting\Tests; +use Contao\E2eTesting\Cache\CacheConfig; use Contao\E2eTesting\Database\DockerDatabaseConfig; +use Contao\E2eTesting\Database\DockerDatabaseService; use Contao\E2eTesting\Installation\ApplicationPreparer; use Contao\E2eTesting\ManagedEdition\ManagedEditionConfig; use Contao\InstallationRecipe\Composer\ComposerConfig; @@ -43,6 +45,21 @@ public function testSelectsAnExplicitDockerDatabase(): void $config = ManagedEditionConfig::create($recipe, \dirname(__DIR__, 2))->withDatabase($database); $this->assertSame($database, $config->environment->database); + $this->assertInstanceOf(DockerDatabaseService::class, $config->dockerServices()[0]); + $this->assertSame($database, $config->dockerServices()[0]->config); + } + + public function testDockerServicesForDifferentProjectsHaveDifferentFingerprints(): void + { + $database = DockerDatabaseConfig::mariaDb(); + $temporaryDirectory = sys_get_temp_dir(); + $sharedCache = $temporaryDirectory.'/shared-e2e-cache'; + $firstCache = CacheConfig::forProject($temporaryDirectory.'/project-a')->withRootDirectory($sharedCache); + $secondCache = CacheConfig::forProject($temporaryDirectory.'/project-b')->withRootDirectory($sharedCache); + $first = new DockerDatabaseService($firstCache, $database); + $second = new DockerDatabaseService($secondCache, $database); + + $this->assertNotSame($first->fingerprint(), $second->fingerprint()); } public function testSelectsTheAppEnvironmentWithoutChangingTheOriginalConfig(): void diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d918f8d..111d0f7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -3,9 +3,16 @@ bootstrap="vendor/autoload.php" cacheDirectory=".contao-e2e/cache/phpunit"> - + installation-recipe/tests + + e2e-testing/tests + + + + + From cb32e857bc0d37d6bb8fdca8092a6224ce574f81 Mon Sep 17 00:00:00 2001 From: Yanick Witschi Date: Wed, 30 Sep 2026 18:54:19 +0200 Subject: [PATCH 2/3] Fix split --- .github/workflows/split.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/split.yml b/.github/workflows/split.yml index 45c9b95..b9d8078 100644 --- a/.github/workflows/split.yml +++ b/.github/workflows/split.yml @@ -28,12 +28,12 @@ jobs: uses: actions/cache@v5 with: path: .monorepo-split-cache - key: dev-${GITHUB_REF##*/} + key: dev-${{ github.ref_name }} - name: Install the dependencies run: composer global require contao/monorepo-tools:^0.2 - name: Split the monorepo - run: $HOME/.composer/vendor/bin/monorepo-tools split ${GITHUB_REF##*/} ${{ github.event.forced && '--force-push' || '' }} + run: $HOME/.composer/vendor/bin/monorepo-tools split "$GITHUB_REF_NAME" ${{ github.event.forced && '--force-push' || '' }} env: GITHUB_TOKEN: ${{ secrets.MONOREPO_SPLIT_TOKEN }} From 7edd0a65e2e3c1bc80d1295cd9f6793b1c11ce0e Mon Sep 17 00:00:00 2001 From: Yanick Witschi Date: Wed, 30 Sep 2026 19:20:13 +0200 Subject: [PATCH 3/3] CS --- e2e-testing/src/Database/DatabaseReadinessProbe.php | 4 ++-- e2e-testing/tests/ManagedEditionConfigTest.php | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/e2e-testing/src/Database/DatabaseReadinessProbe.php b/e2e-testing/src/Database/DatabaseReadinessProbe.php index bd537e5..3d3a152 100644 --- a/e2e-testing/src/Database/DatabaseReadinessProbe.php +++ b/e2e-testing/src/Database/DatabaseReadinessProbe.php @@ -29,11 +29,11 @@ public function wait(DatabaseServerConfig $config): void do { try { - $connection = DriverManager::getConnection(new DsnParser([ + $connection = DriverManager::getConnection((new DsnParser([ 'mysql' => 'pdo_mysql', 'pdo-mysql' => 'pdo_mysql', 'mysqli' => 'mysqli', - ])->parse($config->url)); + ]))->parse($config->url)); $connection->executeQuery('SELECT 1'); $connection->close(); diff --git a/e2e-testing/tests/ManagedEditionConfigTest.php b/e2e-testing/tests/ManagedEditionConfigTest.php index 2e6f7f1..dfbf9e3 100644 --- a/e2e-testing/tests/ManagedEditionConfigTest.php +++ b/e2e-testing/tests/ManagedEditionConfigTest.php @@ -43,10 +43,11 @@ public function testSelectsAnExplicitDockerDatabase(): void $recipe = InstallationRecipe::create(ComposerConfig::managedEdition('^5.7')); $database = DockerDatabaseConfig::mysql('mysql:8.0'); $config = ManagedEditionConfig::create($recipe, \dirname(__DIR__, 2))->withDatabase($database); + $service = $config->dockerServices()[0]; $this->assertSame($database, $config->environment->database); - $this->assertInstanceOf(DockerDatabaseService::class, $config->dockerServices()[0]); - $this->assertSame($database, $config->dockerServices()[0]->config); + $this->assertInstanceOf(DockerDatabaseService::class, $service); + $this->assertSame($database, $service->config); } public function testDockerServicesForDifferentProjectsHaveDifferentFingerprints(): void