Update deps and README + some cleanup

This commit is contained in:
Sergei Shitikov
2024-09-06 20:02:48 +02:00
parent f05ea0020d
commit a10484e7f5
12 changed files with 348 additions and 166 deletions
+55 -36
View File
@@ -19,10 +19,13 @@ composer req --dev testcontainers/testcontainers
use Testcontainers\Container\GenericContainer; use Testcontainers\Container\GenericContainer;
$container = new GenericContainer::make('nginx:alpine'); $container = new GenericContainer('nginx:alpine');
// set an environment variable // set an environment variable
$container->withEnvironment('name', 'var'); $container->withEnvironment([
'key1' => 'val1',
'key2' => 'val2'
]);
// enable health check for an container // enable health check for an container
$container->withHealthCheckCommand('curl --fail localhost'); $container->withHealthCheckCommand('curl --fail localhost');
@@ -35,10 +38,18 @@ Normally you have to wait until the Container is ready. so for this you can defi
```php ```php
use Testcontainers\Container\GenericContainer;
use Testcontainers\Wait\WaitForExec;
use Testcontainers\Wait\WaitForLog;
use Testcontainers\Wait\WaitForHttp;
use Testcontainers\Wait\WaitForHealthCheck;
$container = new GenericContainer('nginx:alpine');
// Run mysqladmin ping until the command returns exit code 0 // Run mysqladmin ping until the command returns exit code 0
$container->withWait(new WaitForExec(['mysqladmin', 'ping', '-h', '127.0.0.1'])); $container->withWait(new WaitForExec(['mysqladmin', 'ping', '-h', '127.0.0.1']));
$container->withWait(new WaitForExec(['mysqladmin', 'ping', '-h', '127.0.0.1']), function(Process $process) { $container->withWait(new WaitForExec(['mysqladmin', 'ping', '-h', '127.0.0.1']), function($exitCode, $contents) {
// throw exception if process result is bad // throw exception if process result is bad
}); });
@@ -60,14 +71,17 @@ $container->withWait(new WaitForHealthCheck());
use Testcontainers\Modules\MySQLContainer; use Testcontainers\Modules\MySQLContainer;
$container = MySQLContainer::make('8.0'); $container = (new MySQLContainer('8.0'))
$container->withMySQLDatabase('foo'); ->withMySQLDatabase('foo')
$container->withMySQLUser('bar', 'baz'); ->withMySQLUser('bar', 'baz')
->start();
$container->run();
$pdo = new \PDO( $pdo = new \PDO(
sprintf('mysql:host=%s;port=3306', $container->getAddress()), sprintf(
'mysql:host=%s;port=%d',
$container->getHost(),
$container->getFirstMappedPort()
),
'bar', 'bar',
'baz', 'baz',
); );
@@ -82,14 +96,17 @@ $pdo = new \PDO(
use Testcontainers\Modules\MariaDBContainer; use Testcontainers\Modules\MariaDBContainer;
$container = MariaDBContainer::make('8.0'); $container = $container = (new MariaDBContainer())
$container->withMariaDBDatabase('foo'); ->withMariaDBDatabase('foo')
$container->withMariaDBUser('bar', 'baz'); ->withMariaDBUser('bar', 'baz')
->start();
$container->run();
$pdo = new \PDO( $pdo = new \PDO(
sprintf('mysql:host=%s;port=3306', $container->getAddress()), sprintf(
'mysql:host=%s;port=%d',
$container->getHost(),
$container->getFirstMappedPort()
),
'bar', 'bar',
'baz', 'baz',
); );
@@ -104,16 +121,19 @@ $pdo = new \PDO(
use Testcontainers\Modules\PostgresContainer; use Testcontainers\Modules\PostgresContainer;
$container = PostgresContainer::make('15.0', 'password'); $container = (new PostgresContainer())
$container->withPostgresDatabase('database'); ->withPostgresUser('bar')
$container->withPostgresUser('username'); ->withPostgresDatabase('foo')
->start();
$container->run();
$pdo = new \PDO( $pdo = new \PDO(
sprintf('pgsql:host=%s;port=5432;dbname=database', $container->getAddress()), sprintf(
'username', 'pgsql:host=%s;port=%d;dbname=foo',
'password', self::$container->getHost(),
self::$container->getFirstMappedPort()
),
'bar',
'test',
); );
// Do something with pdo // Do something with pdo
@@ -125,12 +145,11 @@ $pdo = new \PDO(
use Testcontainers\Modules\RedisContainer; use Testcontainers\Modules\RedisContainer;
$container = RedisContainer::make('6.0'); $container = (new RedisContainer())
->start();
$container->run();
$redis = new \Redis(); $redis = new \Redis();
$redis->connect($container->getAddress()); $redis->connect($container->getHost(), $container->getFirstMappedPort());
// Do something with redis // Do something with redis
``` ```
@@ -141,10 +160,9 @@ $redis->connect($container->getAddress());
use Testcontainers\Modules\OpenSearchContainer; use Testcontainers\Modules\OpenSearchContainer;
$container = OpenSearchContainer::make('2'); $container = (new OpenSearchContainer())
$container->disableSecurityPlugin(); ->withDisabledSecurityPlugin()
->start();
$container->run();
// Do something with opensearch // Do something with opensearch
``` ```
@@ -175,11 +193,12 @@ class TestConnectionFactory extends ConnectionFactory
public function __construct(array $typesConfig, ?DsnParser $dsnParser = null) public function __construct(array $typesConfig, ?DsnParser $dsnParser = null)
{ {
if (!$this::$testDsn) { if (!$this::$testDsn) {
$psql = PostgresContainer::make('14.0', 'password'); $psql = (new PostgresContainer())
$psql->withPostgresDatabase('database'); ->withPostgresUser('user')
$psql->withPostgresUser('user'); ->withPostgresPassword('password')
$psql->run(); ->withPostgresDatabase('database')
$this::$testDsn = sprintf('postgresql://user:password@%s:5432/database?serverVersion=14&charset=utf8', $psql->getAddress()); ->start();
$this::$testDsn = sprintf('postgresql://user:password@%s:%d/database?serverVersion=14&charset=utf8', $psql->getAddress(), $psql->getFirstMappedPort());
} }
parent::__construct($typesConfig, $dsnParser); parent::__construct($typesConfig, $dsnParser);
} }
+1 -1
View File
@@ -14,12 +14,12 @@
} }
], ],
"require": { "require": {
"ext-curl": "*",
"php": ">= 8.1", "php": ">= 8.1",
"beluga-php/docker-php": "^1.45", "beluga-php/docker-php": "^1.45",
"symfony/http-client": "^7.1" "symfony/http-client": "^7.1"
}, },
"require-dev": { "require-dev": {
"ext-curl": "*",
"ext-pdo": "*", "ext-pdo": "*",
"phpunit/phpunit": "^9.5", "phpunit/phpunit": "^9.5",
"brianium/paratest": "^6.6", "brianium/paratest": "^6.6",
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Testcontainers\Container;
* @deprecated Use GenericContainer instead. * @deprecated Use GenericContainer instead.
* TODO: Remove in next major release. * TODO: Remove in next major release.
*/ */
final class Container extends GenericContainer class Container extends GenericContainer
{ {
protected ?StartedTestContainer $startedContainer = null; protected ?StartedTestContainer $startedContainer = null;
+33 -1
View File
@@ -4,12 +4,44 @@ declare(strict_types=1);
namespace Testcontainers\Container; namespace Testcontainers\Container;
use Testcontainers\Wait\WaitForLog;
/** /**
* Left for namespace backward compatibility * Left for namespace backward compatibility
* @deprecated Use \Testcontainers\Modules\MariaDBContainer instead. * @deprecated Use \Testcontainers\Modules\MariaDBContainer instead.
* TODO: Remove in next major release. * TODO: Remove in next major release.
*/ */
class MariaDBContainer extends \Testcontainers\Modules\MariaDBContainer class MariaDBContainer extends Container
{ {
public function __construct(string $version = 'latest', string $mysqlRootPassword = 'root')
{
parent::__construct('mariadb:' . $version);
$this->withExposedPorts(3306);
$this->withWait(new WaitForLog('ready for connections'));
$this->withEnvironment('MARIADB_ROOT_PASSWORD', $mysqlRootPassword);
}
/**
* @deprecated Use constructor instead
* Left for backward compatibility
*/
public static function make(string $version = 'latest', string $mysqlRootPassword = 'root'): self
{
return new self($version, $mysqlRootPassword);
}
public function withMariaDBUser(string $username, string $password): self
{
$this->withEnvironment('MARIADB_USER', $username);
$this->withEnvironment('MARIADB_PASSWORD', $password);
return $this;
}
public function withMariaDBDatabase(string $database): self
{
$this->withEnvironment('MARIADB_DATABASE', $database);
return $this;
}
} }
+33 -1
View File
@@ -4,12 +4,44 @@ declare(strict_types=1);
namespace Testcontainers\Container; namespace Testcontainers\Container;
use Testcontainers\Wait\WaitForLog;
/** /**
* Left for namespace backward compatibility * Left for namespace backward compatibility
* @deprecated Use \Testcontainers\Modules\MySQLContainer instead. * @deprecated Use \Testcontainers\Modules\MySQLContainer instead.
* TODO: Remove in next major release. * TODO: Remove in next major release.
*/ */
class MySQLContainer extends \Testcontainers\Modules\MySQLContainer class MySQLContainer extends Container
{ {
public function __construct(string $version = 'latest', string $mysqlRootPassword = 'root')
{
parent::__construct('mysql:' . $version);
$this->withExposedPorts(3306);
$this->withEnvironment('MYSQL_ROOT_PASSWORD', $mysqlRootPassword);
$this->withWait(new WaitForLog('ready for connections'));
}
/**
* @deprecated Use constructor instead
* Left for backward compatibility
*/
public static function make(string $version = 'latest', string $mysqlRootPassword = 'root'): self
{
return new self($version, $mysqlRootPassword);
}
public function withMySQLUser(string $username, string $password): self
{
$this->withEnvironment('MYSQL_USER', $username);
$this->withEnvironment('MYSQL_PASSWORD', $password);
return $this;
}
public function withMySQLDatabase(string $database): self
{
$this->withEnvironment('MYSQL_DATABASE', $database);
return $this;
}
} }
+39 -2
View File
@@ -4,12 +4,49 @@ declare(strict_types=1);
namespace Testcontainers\Container; namespace Testcontainers\Container;
use Testcontainers\Wait\WaitForLog;
/** /**
* Left for namespace backward compatibility * Left for namespace backward compatibility
* @deprecated Use \Testcontainers\Modules\OpenSearchContainer instead. * @deprecated Use \Testcontainers\Modules\OpenSearchContainer instead.
* TODO: Remove in next major release. * TODO: Remove in next major release.
*/ */
class OpenSearchContainer extends \Testcontainers\Modules\OpenSearchContainer class OpenSearchContainer extends Container
{ {
public function __construct(string $version = 'latest')
{
parent::__construct('opensearchproject/opensearch:' . $version);
$this->withExposedPorts(9200);
$this->withEnvironment('discovery.type', 'single-node');
$this->withEnvironment('OPENSEARCH_INITIAL_ADMIN_PASSWORD', 'c3o_ZPHo!');
$this->withWait(new WaitForLog(
'/\]\s+started\?\[/',
true,
30000
));
}
} /**
* @deprecated Use constructor instead
* Left for backward compatibility
*/
public static function make(string $version = 'latest'): self
{
return new self($version);
}
public function withDisabledSecurityPlugin(): self
{
$this->withEnvironment('plugins.security.disabled', 'true');
return $this;
}
/**
* @deprecated Use withDisabledSecurityPlugin instead
*/
public function disableSecurityPlugin(): self
{
return $this->withDisabledSecurityPlugin();
}
}
+42 -2
View File
@@ -4,12 +4,52 @@ declare(strict_types=1);
namespace Testcontainers\Container; namespace Testcontainers\Container;
use Testcontainers\Wait\WaitForExec;
/** /**
* Left for namespace backward compatibility * Left for namespace backward compatibility
* @deprecated Use \Testcontainers\Modules\PostgresContainer instead. * @deprecated Use \Testcontainers\Modules\PostgresContainer instead.
* TODO: Remove in next major release. * TODO: Remove in next major release.
*/ */
class PostgresContainer extends \Testcontainers\Modules\PostgresContainer class PostgresContainer extends Container
{ {
public function __construct(
string $version = 'latest',
public readonly string $username = 'test',
public readonly string $password = 'test',
public readonly string $database = 'test'
) {
parent::__construct('postgres:' . $version);
$this->withExposedPorts(5432);
$this->withEnvironment('POSTGRES_USER', $this->username);
$this->withEnvironment('POSTGRES_PASSWORD', $this->password);
$this->withEnvironment('POSTGRES_DB', $this->database);
$this->withWait(new WaitForExec(["pg_isready", "-h", "127.0.0.1", "-U", $this->username]));
}
} /**
* @deprecated Use constructor instead
* Left for backward compatibility
*/
public static function make(string $version = 'latest', string $dbPassword = 'root'): self
{
return new self(
version: $version,
password: $dbPassword
);
}
public function withPostgresUser(string $username): self
{
$this->withEnvironment('POSTGRES_USER', $username);
return $this;
}
public function withPostgresDatabase(string $database): self
{
$this->withEnvironment('POSTGRES_DB', $database);
return $this;
}
}
+18 -2
View File
@@ -4,12 +4,28 @@ declare(strict_types=1);
namespace Testcontainers\Container; namespace Testcontainers\Container;
use Testcontainers\Wait\WaitForLog;
/** /**
* Left for namespace backward compatibility * Left for namespace backward compatibility
* @deprecated Use \Testcontainers\Modules\RedisContainer instead. * @deprecated Use \Testcontainers\Modules\RedisContainer instead.
* TODO: Remove in next major release. * TODO: Remove in next major release.
*/ */
class RedisContainer extends \Testcontainers\Modules\RedisContainer class RedisContainer extends Container
{ {
public function __construct(string $version = 'latest')
{
parent::__construct('redis:' . $version);
$this->withExposedPorts(6379);
$this->withWait(new WaitForLog('Ready to accept connections'));
}
} /**
* @deprecated Use constructor instead
* Left for backward compatibility
*/
public static function make(string $version = 'latest'): self
{
return new self($version);
}
}
+7 -12
View File
@@ -23,18 +23,6 @@ class PostgresContainer extends GenericContainer
$this->withWait(new WaitForExec(["pg_isready", "-h", "127.0.0.1", "-U", $this->username])); $this->withWait(new WaitForExec(["pg_isready", "-h", "127.0.0.1", "-U", $this->username]));
} }
/**
* @deprecated Use constructor instead
* Left for backward compatibility
*/
public static function make(string $version = 'latest', string $dbPassword = 'root'): self
{
return new self(
version: $version,
password: $dbPassword
);
}
public function withPostgresUser(string $username): self public function withPostgresUser(string $username): self
{ {
$this->withEnvironment('POSTGRES_USER', $username); $this->withEnvironment('POSTGRES_USER', $username);
@@ -42,6 +30,13 @@ class PostgresContainer extends GenericContainer
return $this; return $this;
} }
public function withPostgresPassword(string $password): self
{
$this->withEnvironment('POSTGRES_PASSWORD', $password);
return $this;
}
public function withPostgresDatabase(string $database): self public function withPostgresDatabase(string $database): self
{ {
$this->withEnvironment('POSTGRES_DB', $database); $this->withEnvironment('POSTGRES_DB', $database);
-1
View File
@@ -30,7 +30,6 @@ class WaitForHealthCheck extends BaseWaitStrategy
/** @var \Psr\Http\Message\ResponseInterface | null $containerInspect */ /** @var \Psr\Http\Message\ResponseInterface | null $containerInspect */
$containerInspect = $container->getClient()->containerInspect($container->getId(), [], Docker::FETCH_RESPONSE); $containerInspect = $container->getClient()->containerInspect($container->getId(), [], Docker::FETCH_RESPONSE);
//$containerStatus = $containerInspect?->getArrayCopy() ?? null; //$containerStatus = $containerInspect?->getArrayCopy() ?? null;
var_dump($containerInspect->getBody()->getContents());
$containerStatus = ''; $containerStatus = '';
if ($containerStatus === 'healthy') { if ($containerStatus === 'healthy') {
return; return;
+11 -5
View File
@@ -6,17 +6,23 @@ namespace Testcontainers\Tests\Integration\OldTests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Predis\Client; use Predis\Client;
use Testcontainers\Modules\MariaDBContainer; use Testcontainers\Container\MariaDBContainer;
use Testcontainers\Modules\MySQLContainer; use Testcontainers\Container\MySQLContainer;
use Testcontainers\Modules\OpenSearchContainer; use Testcontainers\Container\OpenSearchContainer;
use Testcontainers\Modules\PostgresContainer; use Testcontainers\Container\PostgresContainer;
use Testcontainers\Modules\RedisContainer; use Testcontainers\Container\RedisContainer;
/** /**
* Old test classes kept to check backward compatibility * Old test classes kept to check backward compatibility
*/ */
class ContainerTest extends TestCase class ContainerTest extends TestCase
{ {
//TODO: remove after check
protected function setUp(): void
{
$this->markTestIncomplete();
}
public function testMySQL(): void public function testMySQL(): void
{ {
$container = MySQLContainer::make(); $container = MySQLContainer::make();
+108 -102
View File
@@ -7,7 +7,7 @@ namespace Testcontainers\Tests\Integration\OldTests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Predis\Client; use Predis\Client;
use Predis\Connection\ConnectionException; use Predis\Connection\ConnectionException;
use Testcontainers\Container\GenericContainer; use Testcontainers\Container\Container;
use Testcontainers\Exception\ContainerNotReadyException; use Testcontainers\Exception\ContainerNotReadyException;
use Testcontainers\Wait\WaitForExec; use Testcontainers\Wait\WaitForExec;
use Testcontainers\Wait\WaitForHealthCheck; use Testcontainers\Wait\WaitForHealthCheck;
@@ -20,9 +20,15 @@ use Testcontainers\Wait\WaitForTcpPortOpen;
*/ */
class WaitStrategyTest extends TestCase class WaitStrategyTest extends TestCase
{ {
//TODO: remove after check
protected function setUp(): void
{
$this->markTestIncomplete();
}
public function testWaitForExec(): void public function testWaitForExec(): void
{ {
$container = GenericContainer::make('mysql') $container = Container::make('mysql')
->withEnvironment('MYSQL_ROOT_PASSWORD', 'root') ->withEnvironment('MYSQL_ROOT_PASSWORD', 'root')
->withWait( ->withWait(
new WaitForExec([ new WaitForExec([
@@ -48,104 +54,104 @@ class WaitStrategyTest extends TestCase
$this->assertNotEmpty($version); $this->assertNotEmpty($version);
} }
// public function testWaitForLog(): void public function testWaitForLog(): void
// { {
// $container = GenericContainer::make('redis:6.2.5') $container = Container::make('redis:6.2.5')
// ->withWait(new WaitForLog('Ready to accept connections')); ->withWait(new WaitForLog('Ready to accept connections'));
//
// $container->run(); $container->run();
//
// $redis = new Client([ $redis = new Client([
// 'scheme' => 'tcp', 'scheme' => 'tcp',
// 'host' => $container->getAddress(), 'host' => $container->getAddress(),
// 'port' => 6379, 'port' => 6379,
// ]); ]);
//
// $redis->set('foo', 'bar'); $redis->set('foo', 'bar');
//
// $this->assertEquals('bar', $redis->get('foo')); $this->assertEquals('bar', $redis->get('foo'));
//
// $container->stop(); $container->stop();
//
// $this->expectException(ConnectionException::class); $this->expectException(ConnectionException::class);
//
// $redis->get('foo'); $redis->get('foo');
//
// $container->remove(); $container->remove();
// } }
//
// public function testWaitForHTTP(): void public function testWaitForHTTP(): void
// { {
// $container = GenericContainer::make('nginx:alpine') $container = Container::make('nginx:alpine')
// ->withWait(WaitForHttp::make(80)); ->withWait(WaitForHttp::make(80));
//
// $container->run(); $container->run();
//
// $ch = curl_init(); $ch = curl_init();
// curl_setopt($ch, CURLOPT_URL, sprintf('http://%s:%d', $container->getAddress(), 80)); curl_setopt($ch, CURLOPT_URL, sprintf('http://%s:%d', $container->getAddress(), 80));
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//
// $response = (string) curl_exec($ch); $response = (string) curl_exec($ch);
//
// curl_close($ch); curl_close($ch);
//
// $this->assertNotEmpty($response); $this->assertNotEmpty($response);
// } }
//
// /** /**
// * @dataProvider provideWaitForTcpPortOpen * @dataProvider provideWaitForTcpPortOpen
// */ */
// public function testWaitForTcpPortOpen(bool $wait): void public function testWaitForTcpPortOpen(bool $wait): void
// { {
// $container = GenericContainer::make('nginx:alpine'); $container = Container::make('nginx:alpine');
//
// if ($wait) { if ($wait) {
// $container->withWait(WaitForTcpPortOpen::make(80)); $container->withWait(WaitForTcpPortOpen::make(80));
// } }
//
// $container->run(); $container->run();
//
// if ($wait) { if ($wait) {
// static::assertIsResource(fsockopen($container->getAddress(), 80), 'Failed to connect to container'); static::assertIsResource(fsockopen($container->getAddress(), 80), 'Failed to connect to container');
// return; return;
// } }
//
// $containerId = $container->getId(); $containerId = $container->getId();
//
// $this->expectExceptionObject(new ContainerNotReadyException($containerId)); $this->expectExceptionObject(new ContainerNotReadyException($containerId));
//
// (new WaitForTcpPortOpen(8080))->wait($containerId); (new WaitForTcpPortOpen(8080))->wait($containerId);
// } }
//
// /** /**
// * @return array<string, array<bool>> * @return array<string, array<bool>>
// */ */
// public function provideWaitForTcpPortOpen(): array public function provideWaitForTcpPortOpen(): array
// { {
// return [ return [
// 'Can connect to container' => [true], 'Can connect to container' => [true],
// 'Cannot connect to container' => [false], 'Cannot connect to container' => [false],
// ]; ];
// } }
//
// public function testWaitForHealthCheck(): void public function testWaitForHealthCheck(): void
// { {
// $container = GenericContainer::make('nginx') $container = Container::make('nginx')
// ->withHealthCheckCommand('curl --fail http://localhost') ->withHealthCheckCommand('curl --fail http://localhost')
// ->withWait(new WaitForHealthCheck()); ->withWait(new WaitForHealthCheck());
//
// $container->run(); $container->run();
//
// $ch = curl_init(); $ch = curl_init();
//
// curl_setopt($ch, CURLOPT_URL, sprintf('http://%s:%d', $container->getAddress(), 80)); curl_setopt($ch, CURLOPT_URL, sprintf('http://%s:%d', $container->getAddress(), 80));
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//
// $response = curl_exec($ch); $response = curl_exec($ch);
//
// $this->assertNotEmpty($response); $this->assertNotEmpty($response);
// $this->assertIsString($response); $this->assertIsString($response);
//
// $this->assertStringContainsString('Welcome to nginx!', $response); $this->assertStringContainsString('Welcome to nginx!', $response);
// } }
} }