packages/sql-fixture/tests/Unit/Provider/PlatformFactoryTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Provider;
6
7use Container\MySqlRelease;
8use Container\PostgreSqlRelease;
9use PDO;
10use PHPUnit\Framework\Attributes\CoversClass;
11use PHPUnit\Framework\Attributes\Test;
12use PHPUnit\Framework\Attributes\UsesClass;
13use PHPUnit\Framework\TestCase;
14use SqlFixture\Platform\MySql\MySqlSchemaFetcher;
15use SqlFixture\Platform\MySql\MySqlSchemaParser;
16use SqlFixture\Platform\MySql\MySqlTypeMapper;
17use SqlFixture\Platform\PostgreSql\PostgreSqlSchemaFetcher;
18use SqlFixture\Platform\PostgreSql\PostgreSqlSchemaParser;
19use SqlFixture\Platform\PostgreSql\PostgreSqlTypeMapper;
20use SqlFixture\Platform\Sqlite\SqliteSchemaFetcher;
21use SqlFixture\Platform\Sqlite\SqliteSchemaParser;
22use SqlFixture\Platform\Sqlite\SqliteTypeMapper;
23use SqlFixture\Provider\PlatformFactory;
24
25#[CoversClass(PlatformFactory::class)]
26#[UsesClass(SqliteSchemaFetcher::class)]
27#[UsesClass(PostgreSqlSchemaFetcher::class)]
28#[UsesClass(MySqlSchemaFetcher::class)]
29#[UsesClass(MySqlSchemaParser::class)]
30#[UsesClass(MySqlTypeMapper::class)]
31#[UsesClass(PostgreSqlSchemaParser::class)]
32#[UsesClass(PostgreSqlTypeMapper::class)]
33#[UsesClass(SqliteSchemaParser::class)]
34#[UsesClass(SqliteTypeMapper::class)]
35#[UsesClass(\SqlFixture\Schema\ColumnDefinition::class)]
36#[UsesClass(\SqlFixture\Schema\SchemaFetcherInterface::class)]
37#[UsesClass(\SqlFixture\Schema\SchemaParseException::class)]
38#[UsesClass(\SqlFixture\Schema\SchemaParserInterface::class)]
39#[UsesClass(\SqlFixture\Schema\TableSchema::class)]
40#[UsesClass(\SqlFixture\TypeMapper\TypeMapperInterface::class)]
41#[UsesClass(\SqlFixture\Platform\MySql\Schema\ColumnParser::class)]
42#[UsesClass(\SqlFixture\Platform\MySql\Schema\CreateTableQuery::class)]
43#[UsesClass(\SqlFixture\Platform\MySql\Schema\DefaultExpression::class)]
44#[UsesClass(\SqlFixture\Platform\MySql\Schema\DefinitionIntegrity::class)]
45#[UsesClass(\SqlFixture\Platform\MySql\Schema\IdentifierQuoter::class)]
46#[UsesClass(\SqlFixture\Platform\MySql\Schema\TableDefinition::class)]
47#[UsesClass(\SqlFixture\Platform\MySql\Schema\TypeParameters::class)]
48#[UsesClass(\SqlFixture\Platform\MySql\Value\ColumnGenerator::class)]
49#[UsesClass(\SqlFixture\Platform\MySql\Value\DecimalGenerator::class)]
50#[UsesClass(\SqlFixture\Platform\MySql\Value\GeometryGenerator::class)]
51#[UsesClass(\SqlFixture\Platform\MySql\Value\IntegerGenerator::class)]
52#[UsesClass(\SqlFixture\Platform\MySql\Value\NumericGenerator::class)]
53#[UsesClass(\SqlFixture\Platform\MySql\Value\SpatialGenerator::class)]
54#[UsesClass(\SqlFixture\Platform\MySql\Value\StringGenerator::class)]
55#[UsesClass(\SqlFixture\Platform\MySql\Value\TextGenerator::class)]
56#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogColumn::class)]
57#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogDdl::class)]
58#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogQuery::class)]
59#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogSchema::class)]
60#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\ColumnParser::class)]
61#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\DefaultExpression::class)]
62#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\DefinitionList::class)]
63#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\TableSyntax::class)]
64#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\TypeDeclaration::class)]
65#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\ColumnGenerator::class)]
66#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\DecimalGenerator::class)]
67#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\NumericGenerator::class)]
68#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\StringGenerator::class)]
69#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\StructuredGenerator::class)]
70#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\TemporalGenerator::class)]
71#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\ColumnParser::class)]
72#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\CreateTableQuery::class)]
73#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\DefaultExpression::class)]
74#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\DefinitionList::class)]
75#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\PragmaColumn::class)]
76#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\PragmaSchema::class)]
77#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\TableSyntax::class)]
78#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\TypeDeclaration::class)]
79#[UsesClass(\SqlFixture\Platform\Sqlite\Value\ColumnGenerator::class)]
80#[UsesClass(\SqlFixture\Platform\Sqlite\Value\TypeAffinity::class)]
81#[UsesClass(\SqlFixture\Schema\DefinitionSegments::class)]
82#[UsesClass(\SqlFixture\Schema\TypeShape::class)]
83#[UsesClass(\SqlFixture\TypeMapper\ParagraphGenerator::class)]
84#[UsesClass(\SqlFixture\Platform\MySql\Schema\TableDefinitionInput::class)]
85#[UsesClass(\SqlFixture\Schema\Exception\InvalidSqlException::class)]
86#[UsesClass(\SqlFixture\Schema\Exception\ExpectedCreateTableException::class)]
87#[UsesClass(\SqlFixture\Schema\Exception\MissingColumnDefinitionsException::class)]
88#[UsesClass(\SqlFixture\TypeMapper\IntegerRange::class)]
89#[UsesClass(\SqlFixture\TypeMapper\IntegerWidth::class)]
90#[UsesClass(\SqlFixture\TypeMapper\DecimalRange::class)]
91#[UsesClass(\SqlFixture\Version\ServerVersion::class)]
92#[UsesClass(\SqlFixture\Version\Releases::class)]
93#[UsesClass(\SqlFixture\Version\ReleaseNumber::class)]
94#[UsesClass(\SqlFixture\Version\UnsupportedVersionException::class)]
95#[\PHPUnit\Framework\Attributes\Medium]
96final class PlatformFactoryTest extends TestCase
97{
98 #[Test]
99 public function testCreateSchemaParserForMysql(): void
100 {
101 $parser = PlatformFactory::createSchemaParser(PlatformFactory::DRIVER_MYSQL);
102
103 self::assertInstanceOf(MySqlSchemaParser::class, $parser);
104 }
105
106 #[Test]
107 public function testCreateSchemaParserForSqlite(): void
108 {
109 $parser = PlatformFactory::createSchemaParser(PlatformFactory::DRIVER_SQLITE);
110
111 self::assertInstanceOf(SqliteSchemaParser::class, $parser);
112 }
113
114 #[Test]
115 public function testCreateSchemaParserForPgsql(): void
116 {
117 $parser = PlatformFactory::createSchemaParser(PlatformFactory::DRIVER_PGSQL);
118
119 self::assertInstanceOf(PostgreSqlSchemaParser::class, $parser);
120 }
121
122 #[Test]
123 public function testCreateTypeMapperForMysql(): void
124 {
125 $mapper = PlatformFactory::createTypeMapper(PlatformFactory::DRIVER_MYSQL);
126
127 self::assertInstanceOf(MySqlTypeMapper::class, $mapper);
128 }
129
130 #[Test]
131 public function testCreateTypeMapperForSqlite(): void
132 {
133 $mapper = PlatformFactory::createTypeMapper(PlatformFactory::DRIVER_SQLITE);
134
135 self::assertInstanceOf(SqliteTypeMapper::class, $mapper);
136 }
137
138 #[Test]
139 public function testCreateTypeMapperForPgsql(): void
140 {
141 $mapper = PlatformFactory::createTypeMapper(PlatformFactory::DRIVER_PGSQL);
142
143 self::assertInstanceOf(PostgreSqlTypeMapper::class, $mapper);
144 }
145
146 #[Test]
147 public function testCreateSchemaFetcherForMysql(): void
148 {
149 $fetcher = PlatformFactory::createSchemaFetcher(PlatformFactory::DRIVER_MYSQL);
150
151 self::assertInstanceOf(MySqlSchemaFetcher::class, $fetcher);
152 }
153
154 #[Test]
155 public function testCreateSchemaFetcherForSqlite(): void
156 {
157 $fetcher = PlatformFactory::createSchemaFetcher(PlatformFactory::DRIVER_SQLITE);
158
159 self::assertInstanceOf(SqliteSchemaFetcher::class, $fetcher);
160 }
161
162 #[Test]
163 public function testCreateSchemaFetcherForPgsql(): void
164 {
165 $fetcher = PlatformFactory::createSchemaFetcher(PlatformFactory::DRIVER_PGSQL);
166
167 self::assertInstanceOf(PostgreSqlSchemaFetcher::class, $fetcher);
168 }
169
170 #[Test]
171 public function testDetectDriverForSqlite(): void
172 {
173 $pdo = new PDO('sqlite::memory:');
174
175 $driver = PlatformFactory::detectDriver($pdo);
176
177 self::assertSame(PlatformFactory::DRIVER_SQLITE, $driver);
178 }
179
180 #[Test]
181 public function testGetSupportedDrivers(): void
182 {
183 $drivers = PlatformFactory::getSupportedDrivers();
184
185 self::assertContains(PlatformFactory::DRIVER_MYSQL, $drivers);
186 self::assertContains(PlatformFactory::DRIVER_SQLITE, $drivers);
187 self::assertContains(PlatformFactory::DRIVER_PGSQL, $drivers);
188 }
189
190 #[\PHPUnit\Framework\Attributes\DataProvider('providerDatabaseDrivers')]
191 public function testDetectDriverReadsTheNativeConnection(string $prefix, string $expected): void
192 {
193 $pdo = new PDO(
194 (string) getenv($prefix . '_DSN'),
195 (string) getenv($prefix . '_USER'),
196 (string) getenv($prefix . '_PASSWORD'),
197 [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false],
198 );
199 self::assertSame($expected, PlatformFactory::detectDriver($pdo));
200 }
201
202 /**
203 * @return iterable<string, array{string, string}>
204 */
205 public static function providerDatabaseDrivers(): iterable
206 {
207 yield 'mysql' => ['SQL_FIXTURE_MYSQL', 'mysql'];
208 yield 'postgres' => ['SQL_FIXTURE_PGSQL', 'pgsql'];
209 }
210
211 #[Test]
212 public function testResolveVersionNamesTheDefaultOfTheDriver(): void
213 {
214 self::assertSame('mysql-8.4.7', PlatformFactory::resolveVersion(PlatformFactory::DRIVER_MYSQL)->tag);
215 self::assertSame('pg-17.2', PlatformFactory::resolveVersion(PlatformFactory::DRIVER_PGSQL)->tag);
216 self::assertSame('sqlite-3.47.2', PlatformFactory::resolveVersion(PlatformFactory::DRIVER_SQLITE)->tag);
217 self::assertSame('5.6.51', PlatformFactory::resolveVersion(PlatformFactory::DRIVER_MYSQL, 'mysql-5.6.51')->number);
218 }
219
220 #[Test]
221 public function testResolveVersionRejectsAnUnsupportedTag(): void
222 {
223 $this->expectException(\SqlFixture\Version\UnsupportedVersionException::class);
224 PlatformFactory::resolveVersion(PlatformFactory::DRIVER_MYSQL, 'mysql-5.5.62');
225 }
226
227 #[Test]
228 public function testCreateSchemaParserRejectsATagOfAnotherDriver(): void
229 {
230 $this->expectException(\SqlFixture\Version\UnsupportedVersionException::class);
231 $this->expectExceptionMessage('Unsupported sqlite version: mysql-8.4.7');
232 PlatformFactory::createSchemaParser(PlatformFactory::DRIVER_SQLITE, 'mysql-8.4.7');
233 }
234
235 #[Test]
236 public function testCreateSchemaFetcherRejectsAnUnsupportedTag(): void
237 {
238 $this->expectException(\SqlFixture\Version\UnsupportedVersionException::class);
239 PlatformFactory::createSchemaFetcher(PlatformFactory::DRIVER_PGSQL, 'pg-9.6.0');
240 }
241
242 #[Test]
243 public function testDetectVersionMatchesTheLinkedSqlite(): void
244 {
245 $pdo = new PDO('sqlite::memory:');
246
247 $version = PlatformFactory::detectVersion($pdo);
248
249 self::assertSame(PlatformFactory::DRIVER_SQLITE, $version->dialect);
250 self::assertSame('sqlite-3.47.2', $version->tag);
251 }
252
253 #[Test]
254 public function testDetectVersionResolvesAnExplicitTagAgainstTheDetectedDriver(): void
255 {
256 $pdo = new PDO('sqlite::memory:');
257
258 self::assertSame('sqlite-3.47.2', PlatformFactory::detectVersion($pdo, 'sqlite-3.47.2')->tag);
259
260 $this->expectException(\SqlFixture\Version\UnsupportedVersionException::class);
261 PlatformFactory::detectVersion($pdo, 'mysql-8.4.7');
262 }
263
264 #[\PHPUnit\Framework\Attributes\DataProvider('providerDatabaseVersions')]
265 public function testDetectVersionReadsTheNativeServer(string $prefix, string $driver, string $expected): void
266 {
267 $pdo = new PDO(
268 (string) getenv($prefix . '_DSN'),
269 (string) getenv($prefix . '_USER'),
270 (string) getenv($prefix . '_PASSWORD'),
271 [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false],
272 );
273 $version = PlatformFactory::detectVersion($pdo);
274 self::assertSame($driver, $version->dialect);
275 self::assertSame($expected, $version->tag);
276 }
277
278 /**
279 * @return iterable<string, array{string, string, string}>
280 */
281 public static function providerDatabaseVersions(): iterable
282 {
283 $mysql = getenv('SQL_FIXTURE_MYSQL_VERSION');
284 $postgres = getenv('SQL_FIXTURE_PGSQL_VERSION');
285 yield 'mysql' => ['SQL_FIXTURE_MYSQL', 'mysql', 'mysql-' . ($mysql === false || $mysql === '' ? MySqlRelease::DEFAULT : $mysql)];
286 yield 'postgres' => ['SQL_FIXTURE_PGSQL', 'pgsql', 'pg-' . ($postgres === false || $postgres === '' ? PostgreSqlRelease::DEFAULT : $postgres)];
287 }
288
289 #[Test]
290 public function testGetSupportedVersionsListsTheTagsOfADriver(): void
291 {
292 self::assertSame(['pg-16.6', 'pg-17.2'], PlatformFactory::getSupportedVersions(PlatformFactory::DRIVER_PGSQL));
293 self::assertContains('mysql-5.6.51', PlatformFactory::getSupportedVersions(PlatformFactory::DRIVER_MYSQL));
294 self::assertContains('mysql-9.1.0', PlatformFactory::getSupportedVersions(PlatformFactory::DRIVER_MYSQL));
295 }
296}
297