mirror of
https://github.com/stan220/testcontainers-php.git
synced 2026-09-12 08:29:34 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Testcontainer\Wait;
|
||||
|
||||
use Closure;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Testcontainer\Exception\ContainerNotReadyException;
|
||||
|
||||
class WaitForExec implements WaitInterface
|
||||
{
|
||||
/**
|
||||
* @param array<string> $command
|
||||
*/
|
||||
public function __construct(private array $command, private ?Closure $checkFunction = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function wait(string $id): void
|
||||
{
|
||||
$process = new Process(['docker', 'exec', $id, ...$this->command]);
|
||||
|
||||
try {
|
||||
$process->mustRun();
|
||||
} catch (\Exception $e) {
|
||||
throw new ContainerNotReadyException($id, $e);
|
||||
}
|
||||
|
||||
if ($this->checkFunction !== null) {
|
||||
$func = $this->checkFunction;
|
||||
$func($process);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Testcontainer\Wait;
|
||||
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Testcontainer\Exception\ContainerNotReadyException;
|
||||
|
||||
class WaitForHealthCheck implements WaitInterface
|
||||
{
|
||||
public function wait(string $id): void
|
||||
{
|
||||
$process = new Process(['docker', 'inspect', '--format', '{{json .State.Health.Status}}', $id]);
|
||||
$process->mustRun();
|
||||
|
||||
$status = json_decode($process->getOutput(), true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
if (!is_string($status)) {
|
||||
throw new ContainerNotReadyException($id, new RuntimeException('Invalid json output'));
|
||||
}
|
||||
|
||||
$status = trim($status, '"');
|
||||
|
||||
if ($status !== 'healthy') {
|
||||
throw new ContainerNotReadyException($id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Testcontainer\Wait;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
use Testcontainer\Exception\ContainerNotReadyException;
|
||||
|
||||
/**
|
||||
* @phpstan-import-type ContainerInspect from \Testcontainer\Container\Container
|
||||
*/
|
||||
class WaitForHttp implements WaitInterface
|
||||
{
|
||||
public const METHOD_GET = 'GET';
|
||||
public const METHOD_POST = 'POST';
|
||||
public const METHOD_PUT = 'PUT';
|
||||
public const METHOD_DELETE = 'DELETE';
|
||||
public const METHOD_HEAD = 'HEAD';
|
||||
public const METHOD_OPTIONS = 'OPTIONS';
|
||||
|
||||
|
||||
private string $method = 'GET';
|
||||
private string $path = '/';
|
||||
private int $statusCode = 200;
|
||||
|
||||
public function __construct(private int $port)
|
||||
{
|
||||
}
|
||||
|
||||
public static function make(int $port): self
|
||||
{
|
||||
return new WaitForHttp($port);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WaitForHttp::METHOD_* $method
|
||||
*/
|
||||
public function withMethod(string $method): self
|
||||
{
|
||||
$this->method = $method;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPath(string $path): self
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withStatusCode(int $statusCode): self
|
||||
{
|
||||
$this->statusCode = $statusCode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function wait(string $id): void
|
||||
{
|
||||
$process = new Process(['docker', 'inspect', $id]);
|
||||
$process->mustRun();
|
||||
|
||||
/** @var ContainerInspect $data */
|
||||
$data = json_decode($process->getOutput(), true);
|
||||
|
||||
$ip = $data[0]['NetworkSettings']['IPAddress'];
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, sprintf('http://%s:%d%s', $ip, $this->port, $this->path));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
curl_setopt($ch, CURLOPT_NOBODY, true);
|
||||
|
||||
curl_exec($ch);
|
||||
|
||||
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== $this->statusCode) {
|
||||
throw new ContainerNotReadyException($id, new \RuntimeException('HTTP status code does not match'));
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Testcontainer\Wait;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
use Testcontainer\Exception\ContainerNotReadyException;
|
||||
|
||||
class WaitForLog implements WaitInterface
|
||||
{
|
||||
public function __construct(private string $message, private bool $enableRegex = false)
|
||||
{
|
||||
}
|
||||
|
||||
public function wait(string $id): void
|
||||
{
|
||||
$process = new Process(['docker', 'logs', $id]);
|
||||
$process->mustRun();
|
||||
|
||||
$output = $process->getOutput() . PHP_EOL . $process->getErrorOutput();
|
||||
|
||||
if ($this->enableRegex) {
|
||||
if (!preg_match($this->message, $output)) {
|
||||
throw new ContainerNotReadyException($id, new \RuntimeException('Message not found in logs'));
|
||||
}
|
||||
} else {
|
||||
if (!str_contains($output, $this->message)) {
|
||||
throw new ContainerNotReadyException($id, new \RuntimeException('Message not found in logs'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Testcontainer\Wait;
|
||||
|
||||
class WaitForNothing implements WaitInterface
|
||||
{
|
||||
public function wait(string $id): void
|
||||
{
|
||||
// does nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Testcontainer\Wait;
|
||||
|
||||
interface WaitInterface
|
||||
{
|
||||
public function wait(string $id): void;
|
||||
}
|
||||
Reference in New Issue
Block a user