From 46058b92d6848f364a39050d79b8d66dd82bdd62 Mon Sep 17 00:00:00 2001 From: AdzerKI Date: Fri, 23 Oct 2026 21:33:54 +0300 Subject: [PATCH] fix: let the Docker daemon allocate host ports by default Fixes #80 --- src/Container/GenericContainer.php | 8 +-- tests/Unit/Container/GenericContainerTest.php | 67 +++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 tests/Unit/Container/GenericContainerTest.php diff --git a/src/Container/GenericContainer.php b/src/Container/GenericContainer.php index 34c6408..9d4a251 100644 --- a/src/Container/GenericContainer.php +++ b/src/Container/GenericContainer.php @@ -19,7 +19,6 @@ use RuntimeException; use Testcontainers\ContainerClient\DockerContainerClient; use Testcontainers\Utils\PortGenerator\PortGenerator; -use Testcontainers\Utils\PortGenerator\RandomUniquePortGenerator; use Testcontainers\Utils\PortNormalizer; use Testcontainers\Utils\TarBuilder; use Testcontainers\Wait\WaitForContainer; @@ -60,7 +59,7 @@ class GenericContainer implements TestContainer protected WaitStrategy $waitStrategy; - protected PortGenerator $portGenerator; + protected ?PortGenerator $portGenerator = null; protected bool $isPrivileged = false; @@ -107,7 +106,6 @@ public function __construct(string $image) $this->image = $image; $this->dockerClient = DockerContainerClient::getDockerClient(); $this->waitStrategy = new WaitForContainer(); - $this->portGenerator = new RandomUniquePortGenerator(); } public function getId(): string @@ -505,7 +503,9 @@ protected function createPortBindings(): array foreach ($this->exposedPorts as $port) { $portBinding = new PortBinding(); - $portBinding->setHostPort((string)$this->portGenerator->generatePort()); + if ($this->portGenerator !== null) { + $portBinding->setHostPort((string)$this->portGenerator->generatePort()); + } $portBinding->setHostIp('0.0.0.0'); $portBindings[$port] = [$portBinding]; } diff --git a/tests/Unit/Container/GenericContainerTest.php b/tests/Unit/Container/GenericContainerTest.php new file mode 100644 index 0000000..269e3e8 --- /dev/null +++ b/tests/Unit/Container/GenericContainerTest.php @@ -0,0 +1,67 @@ +getProperty('dockerClient')->setValue(null, null); + parent::tearDown(); + } + + /** + * @return array + */ + public static function portGenerators(): array + { + return [ + 'docker allocates by default' => [null, null], + 'port generator' => [new FixedPortGenerator([8080]), '8080'], + ]; + } + + /** + * @dataProvider portGenerators + */ + public function testHostPortOfExposedPort(?PortGenerator $portGenerator, ?string $hostPort): void + { + $created = null; + $docker = $this->createMock(DockerClient::class); + $docker->method('containerCreate')->willReturnCallback( + static function (ContainersCreatePostBody $body) use (&$created): never { + $created = $body; + throw new RuntimeException('created'); + } + ); + DockerContainerClient::setDockerClient($docker); + $container = (new GenericContainer('nginx'))->withExposedPorts(80); + if ($portGenerator !== null) { + $container->withPortGenerator($portGenerator); + } + + try { + $container->start(); + } catch (RuntimeException) { + } + + self::assertInstanceOf(ContainersCreatePostBody::class, $created); + $binding = $created->getHostConfig()?->getPortBindings()['80/tcp'][0] ?? null; + self::assertNotNull($binding); + self::assertSame($hostPort, $binding->isInitialized('hostPort') ? $binding->getHostPort() : null); + } +}