Skip to content

Commit 754afd7

Browse files
pl-githubtemp
authored andcommitted
feat: Support sqlite file based schema tests
1 parent d457616 commit 754afd7

10 files changed

+497
-3
lines changed

src/Schema/ApplySchema.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brainbits\FunctionalTestHelpers\Schema;
6+
7+
use Doctrine\DBAL\Connection;
8+
9+
interface ApplySchema
10+
{
11+
public function __invoke(SchemaBuilder $schemaBuilder, Connection $connection): void;
12+
}

src/Schema/CreateApplySchema.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brainbits\FunctionalTestHelpers\Schema;
6+
7+
use Doctrine\DBAL\Connection;
8+
9+
use function str_contains;
10+
use function str_ends_with;
11+
12+
final class CreateApplySchema
13+
{
14+
public function __invoke(Connection $connection): ApplySchema
15+
{
16+
$params = $connection->getParams();
17+
18+
if ($this->isSqLiteMemory($params)) {
19+
return new SqliteMemoryBasedApplySchema();
20+
}
21+
22+
if ($this->isSqLiteFile($params)) {
23+
return new SqliteFileBasedApplySchema();
24+
}
25+
26+
throw NoApplySchemaStrategyFound::forConnectionParameters($params);
27+
}
28+
29+
/** @param mixed[] $params */
30+
private function isSqLiteMemory(array $params): bool
31+
{
32+
$url = (string) ($params['url'] ?? '');
33+
$driver = (string) ($params['driver'] ?? '');
34+
$memory = (bool) ($params['memory'] ?? false);
35+
36+
return str_contains($url, 'sqlite:') && str_contains($url, ':memory:')
37+
|| $memory === true && str_ends_with($driver, 'sqlite');
38+
}
39+
40+
/** @param mixed[] $params */
41+
private function isSqLiteFile(array $params): bool
42+
{
43+
$url = (string) ($params['url'] ?? '');
44+
$driver = (string) ($params['driver'] ?? '');
45+
$path = (string) ($params['path'] ?? '');
46+
47+
return $path !== '' && (str_contains($url, 'sqlite:') || str_ends_with($driver, 'sqlite'));
48+
}
49+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brainbits\FunctionalTestHelpers\Schema;
6+
7+
use RuntimeException;
8+
9+
use function Safe\json_encode;
10+
use function sprintf;
11+
12+
final class NoApplySchemaStrategyFound extends RuntimeException
13+
{
14+
/** @param mixed[] $connectionParameters */
15+
public static function forConnectionParameters(array $connectionParameters): self
16+
{
17+
throw new self(
18+
sprintf(
19+
'No apply schema strategy found for connection parameters: %s',
20+
json_encode($connectionParameters),
21+
),
22+
);
23+
}
24+
}

src/Schema/SchemaTrait.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ final protected function fixtureFromNewConnection( // @phpstan-ignore-line
5050
/** @internal */
5151
private function applySchema(SchemaBuilder $schemaBuilder, Connection $connection): void
5252
{
53-
foreach ($schemaBuilder->getSchema()->toSql($connection->getDatabasePlatform()) as $sql) {
54-
$connection->executeStatement($sql);
55-
}
53+
$applySchema = (new CreateApplySchema())($connection);
54+
55+
$applySchema($schemaBuilder, $connection);
5656
}
5757

5858
/** @internal */
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brainbits\FunctionalTestHelpers\Schema;
6+
7+
use ArrayObject;
8+
use Doctrine\DBAL\Connection;
9+
10+
use function assert;
11+
use function count;
12+
use function dirname;
13+
use function file_exists;
14+
use function is_string;
15+
use function Safe\copy;
16+
use function Safe\mkdir;
17+
use function Safe\unlink;
18+
19+
final class SqliteFileBasedApplySchema implements ApplySchema
20+
{
21+
public static ArrayObject $executedStatements;
22+
23+
public function __construct(bool $resetExecutedStatements = false)
24+
{
25+
if ($resetExecutedStatements) {
26+
self::$executedStatements = new ArrayObject();
27+
}
28+
29+
self::$executedStatements ??= new ArrayObject();
30+
}
31+
32+
public function __invoke(SchemaBuilder $schemaBuilder, Connection $connection): void
33+
{
34+
$params = $connection->getParams();
35+
36+
$pathToDatabase = $params['path'] ?? null;
37+
assert(is_string($pathToDatabase));
38+
39+
$pathToCache = $pathToDatabase . '.cache';
40+
$pathToDatabaseDirectory = dirname($pathToDatabase);
41+
42+
$databaseExists = file_exists($pathToDatabase);
43+
$cacheExists = file_exists($pathToCache);
44+
$databaseDirectoryExists = file_exists($pathToDatabaseDirectory);
45+
46+
if (!$databaseDirectoryExists) {
47+
mkdir($pathToDatabaseDirectory);
48+
}
49+
50+
if (!$databaseExists) {
51+
self::$executedStatements = new ArrayObject();
52+
}
53+
54+
if (count(self::$executedStatements) === 0) {
55+
if ($databaseExists) {
56+
unlink($pathToDatabase);
57+
$databaseExists = false;
58+
}
59+
60+
if ($cacheExists) {
61+
unlink($pathToCache);
62+
$cacheExists = false;
63+
}
64+
}
65+
66+
if ($cacheExists) {
67+
copy($pathToCache, $pathToDatabase);
68+
}
69+
70+
$databaseChanged = $this->applyMissingSchema($schemaBuilder, $connection);
71+
72+
// phpcs:ignore SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed
73+
if ($databaseChanged) {
74+
copy($pathToDatabase, $pathToCache);
75+
}
76+
}
77+
78+
private function applyMissingSchema(SchemaBuilder $schemaBuilder, Connection $connection): bool
79+
{
80+
$databaseChanged = false;
81+
82+
foreach ($schemaBuilder->getSchema()->toSql($connection->getDatabasePlatform()) as $sql) {
83+
if (self::$executedStatements->offsetExists($sql)) {
84+
continue;
85+
}
86+
87+
self::$executedStatements[$sql] = $connection->executeStatement($sql);
88+
$databaseChanged = true;
89+
}
90+
91+
return $databaseChanged;
92+
}
93+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brainbits\FunctionalTestHelpers\Schema;
6+
7+
use Doctrine\DBAL\Connection;
8+
9+
final class SqliteMemoryBasedApplySchema implements ApplySchema
10+
{
11+
public function __invoke(SchemaBuilder $schemaBuilder, Connection $connection): void
12+
{
13+
foreach ($schemaBuilder->getSchema()->toSql($connection->getDatabasePlatform()) as $sql) {
14+
$connection->executeStatement($sql);
15+
}
16+
}
17+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brainbits\FunctionalTestHelpers\Tests\Schema;
6+
7+
use Brainbits\FunctionalTestHelpers\Schema\CreateApplySchema;
8+
use Brainbits\FunctionalTestHelpers\Schema\NoApplySchemaStrategyFound;
9+
use Brainbits\FunctionalTestHelpers\Schema\SqliteFileBasedApplySchema;
10+
use Brainbits\FunctionalTestHelpers\Schema\SqliteMemoryBasedApplySchema;
11+
use Doctrine\DBAL\Connection;
12+
use PHPUnit\Framework\TestCase;
13+
14+
/** @covers \Brainbits\FunctionalTestHelpers\Schema\SqliteFileBasedApplySchema */
15+
final class CreateApplySchemaTest extends TestCase
16+
{
17+
public function testItUsesSqliteMemoryStrategyIfConfiguredByUrl(): void
18+
{
19+
$connection = $this->createMock(Connection::class);
20+
$connection->expects($this->once())
21+
->method('getParams')
22+
->willReturn(['url' => 'sqlite://:memory:']);
23+
24+
$applySchema = (new CreateApplySchema())($connection);
25+
26+
$this->assertInstanceOf(SqliteMemoryBasedApplySchema::class, $applySchema);
27+
}
28+
29+
public function testItUsesSqliteMemoryStrategyIfConfiguredByParams(): void
30+
{
31+
$connection = $this->createMock(Connection::class);
32+
$connection->expects($this->once())
33+
->method('getParams')
34+
->willReturn(['driver' => 'pdo_sqlite', 'memory' => true]);
35+
36+
$applySchema = (new CreateApplySchema())($connection);
37+
38+
$this->assertInstanceOf(SqliteMemoryBasedApplySchema::class, $applySchema);
39+
}
40+
41+
public function testItUsesSqliteFileStrategyIfConfiguredByUrl(): void
42+
{
43+
$connection = $this->createMock(Connection::class);
44+
$connection->expects($this->once())
45+
->method('getParams')
46+
->willReturn(['url' => 'sqlite:///tmp/my.db', 'path' => '/tmp/my.db']);
47+
48+
$applySchema = (new CreateApplySchema())($connection);
49+
50+
$this->assertInstanceOf(SqliteFileBasedApplySchema::class, $applySchema);
51+
}
52+
53+
public function testItUsesSqliteStrategyIfConfiguredByParams(): void
54+
{
55+
$connection = $this->createMock(Connection::class);
56+
$connection->expects($this->once())
57+
->method('getParams')
58+
->willReturn(['driver' => 'pdo_sqlite', 'path' => '/tmp/my.db']);
59+
60+
$applySchema = (new CreateApplySchema())($connection);
61+
62+
$this->assertInstanceOf(SqliteFileBasedApplySchema::class, $applySchema);
63+
}
64+
65+
public function testItThrowsException(): void
66+
{
67+
$this->expectException(NoApplySchemaStrategyFound::class);
68+
$this->expectExceptionMessage('No apply schema strategy found for connection parameters: {"key":"value"}');
69+
70+
$connection = $this->createMock(Connection::class);
71+
$connection->expects($this->once())
72+
->method('getParams')
73+
->willReturn(['key' => 'value']);
74+
75+
$applySchema = (new CreateApplySchema())($connection);
76+
77+
$this->assertInstanceOf(SqliteFileBasedApplySchema::class, $applySchema);
78+
}
79+
}

tests/Schema/SchemaTraitTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ public function testApplySchema(): void
2323
$schemaBuilder->foo();
2424

2525
$connection = $this->createMock(Connection::class);
26+
$connection->expects($this->once())
27+
->method('getParams')
28+
->willReturn(['driver' => 'pdo_sqlite', 'memory' => true]);
2629
$connection->expects($this->once())
2730
->method('getDatabasePlatform')
2831
->willReturn(new SqlitePlatform());
@@ -72,6 +75,9 @@ public function testFixtureFromConnectionWithTableNameQuote(): void
7275
$dataBuilder = $this->createDataBuilder($schemaBuilder);
7376

7477
$connection = $this->createMock(Connection::class);
78+
$connection->expects($this->once())
79+
->method('getParams')
80+
->willReturn(['driver' => 'pdo_sqlite', 'memory' => true]);
7581
$connection->expects($this->once())
7682
->method('getDatabasePlatform')
7783
->willReturn(new SqlitePlatform());
@@ -102,6 +108,9 @@ public function testFixtureFromConnectionWithoutTableNameQuote(): void
102108
$dataBuilder = $this->createDataBuilder($schemaBuilder);
103109

104110
$connection = $this->createMock(Connection::class);
111+
$connection->expects($this->once())
112+
->method('getParams')
113+
->willReturn(['driver' => 'pdo_sqlite', 'memory' => true]);
105114
$connection->expects($this->once())
106115
->method('getDatabasePlatform')
107116
->willReturn(new SqlitePlatform());

0 commit comments

Comments
 (0)