packages/sql-fixture/tests/Unit/FileFixtureProviderTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit;
6
7use Faker\Factory;
8use PHPUnit\Framework\Attributes\CoversClass;
9use PHPUnit\Framework\Attributes\Test;
10use PHPUnit\Framework\Attributes\UsesClass;
11use PHPUnit\Framework\TestCase;
12use RuntimeException;
13use SqlFixture\FileFixtureProvider;
14use SqlFixture\FixtureGenerator;
15use SqlFixture\Hydrator\ReflectionHydrator;
16use SqlFixture\Platform\MySql\MySqlSchemaParser;
17use SqlFixture\Platform\MySql\MySqlTypeMapper;
18use SqlFixture\Platform\PlatformFactory;
19use SqlFixture\Schema\ColumnDefinition;
20use SqlFixture\Schema\SchemaParseException;
21use SqlFixture\Schema\TableSchema;
22
23#[CoversClass(FileFixtureProvider::class)]
24#[UsesClass(FixtureGenerator::class)]
25#[UsesClass(PlatformFactory::class)]
26#[UsesClass(MySqlSchemaParser::class)]
27#[UsesClass(ColumnDefinition::class)]
28#[UsesClass(TableSchema::class)]
29#[UsesClass(SchemaParseException::class)]
30#[UsesClass(MySqlTypeMapper::class)]
31#[UsesClass(ReflectionHydrator::class)]
32#[CoversClass(\SqlFixture\Provider\DdlDirectory::class)]
33#[CoversClass(\SqlFixture\Provider\DdlFile::class)]
34#[UsesClass(\SqlFixture\Hydrator\HydrationException::class)]
35#[UsesClass(\SqlFixture\Hydrator\HydratorInterface::class)]
36#[UsesClass(\SqlFixture\Platform\MySql\MySqlSchemaFetcher::class)]
37#[UsesClass(\SqlFixture\Platform\PostgreSql\PostgreSqlSchemaFetcher::class)]
38#[UsesClass(\SqlFixture\Platform\PostgreSql\PostgreSqlSchemaParser::class)]
39#[UsesClass(\SqlFixture\Platform\PostgreSql\PostgreSqlTypeMapper::class)]
40#[UsesClass(\SqlFixture\Platform\Sqlite\SqliteSchemaFetcher::class)]
41#[UsesClass(\SqlFixture\Platform\Sqlite\SqliteSchemaParser::class)]
42#[UsesClass(\SqlFixture\Platform\Sqlite\SqliteTypeMapper::class)]
43#[UsesClass(\SqlFixture\Schema\SchemaFetcherInterface::class)]
44#[UsesClass(\SqlFixture\Schema\SchemaParserInterface::class)]
45#[UsesClass(\SqlFixture\TypeMapper\TypeMapperInterface::class)]
46#[UsesClass(\SqlFixture\Fixture\RowGeneration::class)]
47#[UsesClass(\SqlFixture\Fixture\Validation\OverrideValidator::class)]
48#[UsesClass(\SqlFixture\Hydrator\Reflection\ConstructorHydration::class)]
49#[UsesClass(\SqlFixture\Hydrator\Reflection\PropertyHydration::class)]
50#[UsesClass(\SqlFixture\Hydrator\Reflection\PropertyNames::class)]
51#[UsesClass(\SqlFixture\Hydrator\Reflection\ValueConversion::class)]
52#[UsesClass(\SqlFixture\InvalidOverrideException::class)]
53#[UsesClass(\SqlFixture\Platform\MySql\Schema\ColumnParser::class)]
54#[UsesClass(\SqlFixture\Platform\MySql\Schema\CreateTableQuery::class)]
55#[UsesClass(\SqlFixture\Platform\MySql\Schema\DefaultExpression::class)]
56#[UsesClass(\SqlFixture\Platform\MySql\Schema\DefinitionIntegrity::class)]
57#[UsesClass(\SqlFixture\Platform\MySql\Schema\IdentifierQuoter::class)]
58#[UsesClass(\SqlFixture\Platform\MySql\Schema\TableDefinition::class)]
59#[UsesClass(\SqlFixture\Platform\MySql\Schema\TypeParameters::class)]
60#[UsesClass(\SqlFixture\Platform\MySql\Value\ColumnGenerator::class)]
61#[UsesClass(\SqlFixture\Platform\MySql\Value\DecimalGenerator::class)]
62#[UsesClass(\SqlFixture\Platform\MySql\Value\GeometryGenerator::class)]
63#[UsesClass(\SqlFixture\Platform\MySql\Value\IntegerGenerator::class)]
64#[UsesClass(\SqlFixture\Platform\MySql\Value\NumericGenerator::class)]
65#[UsesClass(\SqlFixture\Platform\MySql\Value\SpatialGenerator::class)]
66#[UsesClass(\SqlFixture\Platform\MySql\Value\StringGenerator::class)]
67#[UsesClass(\SqlFixture\Platform\MySql\Value\TextGenerator::class)]
68#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogColumn::class)]
69#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogDdl::class)]
70#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogQuery::class)]
71#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogSchema::class)]
72#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\ColumnParser::class)]
73#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\DefaultExpression::class)]
74#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\DefinitionList::class)]
75#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\TableSyntax::class)]
76#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\TypeDeclaration::class)]
77#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\ColumnGenerator::class)]
78#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\DecimalGenerator::class)]
79#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\NumericGenerator::class)]
80#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\StringGenerator::class)]
81#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\StructuredGenerator::class)]
82#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\TemporalGenerator::class)]
83#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\ColumnParser::class)]
84#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\CreateTableQuery::class)]
85#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\DefaultExpression::class)]
86#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\DefinitionList::class)]
87#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\PragmaColumn::class)]
88#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\PragmaSchema::class)]
89#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\TableSyntax::class)]
90#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\TypeDeclaration::class)]
91#[UsesClass(\SqlFixture\Platform\Sqlite\Value\ColumnGenerator::class)]
92#[UsesClass(\SqlFixture\Platform\Sqlite\Value\TypeAffinity::class)]
93#[UsesClass(\SqlFixture\Schema\DefinitionSegments::class)]
94#[UsesClass(\SqlFixture\Schema\TypeShape::class)]
95#[UsesClass(\SqlFixture\TypeMapper\ParagraphGenerator::class)]
96#[UsesClass(\SqlFixture\Platform\MySql\Schema\TableDefinitionInput::class)]
97#[UsesClass(\SqlFixture\Hydrator\Exception\ClassNotFoundException::class)]
98#[UsesClass(\SqlFixture\Hydrator\Exception\MissingConstructorArgumentException::class)]
99#[UsesClass(\SqlFixture\Fixture\Exception\UnknownOverrideColumnException::class)]
100#[UsesClass(\SqlFixture\Fixture\Exception\NullOverrideException::class)]
101#[UsesClass(\SqlFixture\Fixture\Exception\GeneratedColumnOverrideException::class)]
102#[UsesClass(\SqlFixture\Schema\Exception\InvalidSqlException::class)]
103#[UsesClass(\SqlFixture\Schema\Exception\ExpectedCreateTableException::class)]
104#[UsesClass(\SqlFixture\Schema\Exception\MissingColumnDefinitionsException::class)]
105#[UsesClass(\SqlFixture\TypeMapper\IntegerRange::class)]
106#[UsesClass(\SqlFixture\TypeMapper\IntegerWidth::class)]
107#[UsesClass(\SqlFixture\TypeMapper\DecimalRange::class)]
108#[UsesClass(\SqlFixture\Hydrator\Reflection\ConversionTarget::class)]
109final class FileFixtureProviderTest extends TestCase
110{
111 #[Test]
112 public function testLoadsSchemasFromDirectory(): void
113 {
114 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
115 mkdir($tempDir, 0755, true);
116 try {
117 file_put_contents(
118 $tempDir . '/users.sql',
119 'CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255))'
120 );
121
122 $faker = Factory::create();
123 $faker->seed(12345);
124 $provider = new FileFixtureProvider($faker, $tempDir);
125
126 self::assertTrue($provider->hasTable('users'));
127 } finally {
128 unlink($tempDir . '/users.sql');
129 rmdir($tempDir);
130 }
131 }
132
133 #[Test]
134 public function testFixtureGeneratesDataForLoadedTable(): void
135 {
136 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
137 mkdir($tempDir, 0755, true);
138 try {
139 file_put_contents(
140 $tempDir . '/users.sql',
141 'CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255))'
142 );
143
144 $faker = Factory::create();
145 $faker->seed(12345);
146 $provider = new FileFixtureProvider($faker, $tempDir);
147 $data = $provider->fixture('users');
148
149 self::assertArrayHasKey('id', $data);
150 self::assertArrayHasKey('name', $data);
151 } finally {
152 unlink($tempDir . '/users.sql');
153 rmdir($tempDir);
154 }
155 }
156
157 #[Test]
158 public function testFixtureWithOverrides(): void
159 {
160 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
161 mkdir($tempDir, 0755, true);
162 try {
163 file_put_contents(
164 $tempDir . '/users.sql',
165 'CREATE TABLE users (id INT, name VARCHAR(255))'
166 );
167
168 $faker = Factory::create();
169 $faker->seed(12345);
170 $provider = new FileFixtureProvider($faker, $tempDir);
171 $data = $provider->fixture('users', ['name' => 'Test']);
172
173 self::assertSame('Test', $data['name']);
174 } finally {
175 unlink($tempDir . '/users.sql');
176 rmdir($tempDir);
177 }
178 }
179
180 #[Test]
181 public function testFixtureWithHydration(): void
182 {
183 $target = new class (0, '') {
184 public function __construct(
185 public readonly int $id,
186 public readonly string $name,
187 ) {
188 }
189 };
190 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
191 mkdir($tempDir, 0755, true);
192 try {
193 file_put_contents(
194 $tempDir . '/users.sql',
195 'CREATE TABLE users (id INT, name VARCHAR(255))'
196 );
197
198 $faker = Factory::create();
199 $faker->seed(12345);
200 $provider = new FileFixtureProvider($faker, $tempDir);
201 $user = $provider->fixture('users', ['id' => 1, 'name' => 'Test'], $target::class);
202
203 self::assertSame(1, $user->id);
204 self::assertSame('Test', $user->name);
205 } finally {
206 unlink($tempDir . '/users.sql');
207 rmdir($tempDir);
208 }
209 }
210
211 #[Test]
212 public function testThrowsExceptionForNonExistentTable(): void
213 {
214 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
215 mkdir($tempDir, 0755, true);
216 try {
217 $faker = Factory::create();
218 $faker->seed(12345);
219 $provider = new FileFixtureProvider($faker, $tempDir);
220
221 $this->expectException(RuntimeException::class);
222 $this->expectExceptionMessage('Schema not found');
223 $provider->fixture('nonexistent');
224 } finally {
225 rmdir($tempDir);
226 }
227 }
228
229 #[Test]
230 public function testThrowsExceptionForNonExistentDirectory(): void
231 {
232 $this->expectException(RuntimeException::class);
233 $this->expectExceptionMessage('not a directory');
234 $faker = Factory::create();
235 $faker->seed(12345);
236 new FileFixtureProvider($faker, '/nonexistent/path');
237 }
238
239 #[Test]
240 public function testHasTableReturnsFalseForNonExistent(): void
241 {
242 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
243 mkdir($tempDir, 0755, true);
244 try {
245 $faker = Factory::create();
246 $faker->seed(12345);
247 $provider = new FileFixtureProvider($faker, $tempDir);
248 self::assertFalse($provider->hasTable('nonexistent'));
249 } finally {
250 rmdir($tempDir);
251 }
252 }
253
254 #[Test]
255 public function testGetTableNames(): void
256 {
257 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
258 mkdir($tempDir, 0755, true);
259 try {
260 file_put_contents(
261 $tempDir . '/users.sql',
262 'CREATE TABLE users (id INT)'
263 );
264 file_put_contents(
265 $tempDir . '/posts.sql',
266 'CREATE TABLE posts (id INT)'
267 );
268
269 $faker = Factory::create();
270 $faker->seed(12345);
271 $provider = new FileFixtureProvider($faker, $tempDir);
272 $names = $provider->getTableNames();
273
274 self::assertContains('users', $names);
275 self::assertContains('posts', $names);
276 } finally {
277 unlink($tempDir . '/users.sql');
278 unlink($tempDir . '/posts.sql');
279 rmdir($tempDir);
280 }
281 }
282
283 #[Test]
284 public function testRegisterSchema(): void
285 {
286 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
287 mkdir($tempDir, 0755, true);
288 try {
289 $faker = Factory::create();
290 $faker->seed(12345);
291 $provider = new FileFixtureProvider($faker, $tempDir);
292 $provider->registerSchema('CREATE TABLE dynamic (id INT, value TEXT)');
293
294 self::assertTrue($provider->hasTable('dynamic'));
295 $data = $provider->fixture('dynamic');
296 self::assertArrayHasKey('id', $data);
297 } finally {
298 rmdir($tempDir);
299 }
300 }
301
302 #[Test]
303 public function testGetFixtureGenerator(): void
304 {
305 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
306 mkdir($tempDir, 0755, true);
307 try {
308 $faker = Factory::create();
309 $faker->seed(12345);
310 $provider = new FileFixtureProvider($faker, $tempDir);
311 self::assertSame(['id' => 7], $provider->getFixtureGenerator()->generate(new TableSchema('sample', ['id' => new ColumnDefinition('id', 'INT')]), ['id' => 7]));
312 } finally {
313 rmdir($tempDir);
314 }
315 }
316
317 #[Test]
318 public function testSkipsInvalidSqlFiles(): void
319 {
320 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
321 mkdir($tempDir, 0755, true);
322 try {
323 file_put_contents($tempDir . '/invalid.sql', 'NOT VALID SQL');
324 file_put_contents(
325 $tempDir . '/valid.sql',
326 'CREATE TABLE valid (id INT)'
327 );
328
329 $faker = Factory::create();
330 $faker->seed(12345);
331 $provider = new FileFixtureProvider($faker, $tempDir);
332
333 self::assertTrue($provider->hasTable('valid'));
334 self::assertFalse($provider->hasTable('invalid'));
335 } finally {
336 unlink($tempDir . '/invalid.sql');
337 unlink($tempDir . '/valid.sql');
338 rmdir($tempDir);
339 }
340 }
341
342 #[Test]
343 public function testHandlesCommentsInSqlFiles(): void
344 {
345 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
346 mkdir($tempDir, 0755, true);
347 try {
348 $sql = <<<'SQL'
349 -- This is a comment
350 /* Multi-line
351 comment */
352 CREATE TABLE with_comments (id INT PRIMARY KEY)
353 SQL;
354
355 file_put_contents($tempDir . '/with_comments.sql', $sql);
356
357 $faker = Factory::create();
358 $faker->seed(12345);
359 $provider = new FileFixtureProvider($faker, $tempDir);
360 self::assertTrue($provider->hasTable('with_comments'));
361 } finally {
362 unlink($tempDir . '/with_comments.sql');
363 rmdir($tempDir);
364 }
365 }
366
367 #[Test]
368 public function testHandlesEmptySqlFile(): void
369 {
370 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
371 mkdir($tempDir, 0755, true);
372 try {
373 file_put_contents($tempDir . '/empty.sql', '');
374 file_put_contents(
375 $tempDir . '/valid.sql',
376 'CREATE TABLE valid (id INT)'
377 );
378
379 $faker = Factory::create();
380 $faker->seed(12345);
381 $provider = new FileFixtureProvider($faker, $tempDir);
382 self::assertTrue($provider->hasTable('valid'));
383 } finally {
384 unlink($tempDir . '/empty.sql');
385 unlink($tempDir . '/valid.sql');
386 rmdir($tempDir);
387 }
388 }
389
390 #[Test]
391 public function testHandlesCommentOnlySqlFile(): void
392 {
393 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
394 mkdir($tempDir, 0755, true);
395 try {
396 file_put_contents($tempDir . '/comments_only.sql', "-- just comments\n/* block */");
397 file_put_contents(
398 $tempDir . '/valid.sql',
399 'CREATE TABLE valid (id INT)'
400 );
401
402 $faker = Factory::create();
403 $faker->seed(12345);
404 $provider = new FileFixtureProvider($faker, $tempDir);
405 self::assertFalse($provider->hasTable('comments_only'));
406 } finally {
407 unlink($tempDir . '/comments_only.sql');
408 unlink($tempDir . '/valid.sql');
409 rmdir($tempDir);
410 }
411 }
412
413 #[Test]
414 public function testTableNameIsCaseInsensitive(): void
415 {
416 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
417 mkdir($tempDir, 0755, true);
418 try {
419 file_put_contents(
420 $tempDir . '/MyTable.sql',
421 'CREATE TABLE MyTable (id INT)'
422 );
423
424 $faker = Factory::create();
425 $faker->seed(12345);
426 $provider = new FileFixtureProvider($faker, $tempDir);
427
428 self::assertTrue($provider->hasTable('mytable'));
429 self::assertTrue($provider->hasTable('MYTABLE'));
430 self::assertTrue($provider->hasTable('MyTable'));
431 } finally {
432 unlink($tempDir . '/MyTable.sql');
433 rmdir($tempDir);
434 }
435 }
436
437 #[Test]
438 public function testFixtureWithMixedCaseTableName(): void
439 {
440 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
441 mkdir($tempDir, 0755, true);
442 try {
443 file_put_contents(
444 $tempDir . '/Users.sql',
445 'CREATE TABLE Users (id INT, name VARCHAR(255))'
446 );
447
448 $faker = Factory::create();
449 $faker->seed(12345);
450 $provider = new FileFixtureProvider($faker, $tempDir);
451 $data = $provider->fixture('USERS');
452
453 self::assertArrayHasKey('id', $data);
454 self::assertArrayHasKey('name', $data);
455 } finally {
456 unlink($tempDir . '/Users.sql');
457 rmdir($tempDir);
458 }
459 }
460
461 #[Test]
462 public function testRegisterSchemaWithMixedCaseFixture(): void
463 {
464 $tempDir = sys_get_temp_dir() . '/sql-fixture-test-' . uniqid();
465 mkdir($tempDir, 0755, true);
466 try {
467 $faker = Factory::create();
468 $faker->seed(12345);
469 $provider = new FileFixtureProvider($faker, $tempDir);
470 $provider->registerSchema('CREATE TABLE MyItems (id INT, value TEXT)');
471
472 $data = $provider->fixture('MYITEMS');
473
474 self::assertArrayHasKey('id', $data);
475 self::assertArrayHasKey('value', $data);
476 } finally {
477 rmdir($tempDir);
478 }
479 }
480}
481