packages/sql-fixture/tests/Integration/Sqlite/SqliteIntegrationTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Integration\Sqlite;
6
7use Faker\Factory;
8use PDO;
9use PHPUnit\Framework\Attributes\CoversClass;
10use PHPUnit\Framework\Attributes\Test;
11use PHPUnit\Framework\Attributes\UsesClass;
12use PHPUnit\Framework\TestCase;
13use SqlFixture\DatabaseFixtureProvider;
14use SqlFixture\FixtureGenerator;
15use SqlFixture\FixtureProvider;
16use SqlFixture\Hydrator\ReflectionHydrator;
17use SqlFixture\Platform\PlatformFactory;
18use SqlFixture\Platform\Sqlite\SqliteSchemaFetcher;
19use SqlFixture\Platform\Sqlite\SqliteSchemaParser;
20use SqlFixture\Platform\Sqlite\SqliteTypeMapper;
21use SqlFixture\Schema\ColumnDefinition;
22use SqlFixture\Schema\TableSchema;
23
24#[CoversClass(DatabaseFixtureProvider::class)]
25#[UsesClass(FixtureProvider::class)]
26#[UsesClass(FixtureGenerator::class)]
27#[UsesClass(PlatformFactory::class)]
28#[UsesClass(SqliteSchemaFetcher::class)]
29#[UsesClass(SqliteSchemaParser::class)]
30#[UsesClass(SqliteTypeMapper::class)]
31#[UsesClass(ColumnDefinition::class)]
32#[UsesClass(TableSchema::class)]
33#[UsesClass(ReflectionHydrator::class)]
34#[CoversClass(\SqlFixture\Provider\DatabaseSchemaCache::class)]
35#[UsesClass(\SqlFixture\Fixture\FixtureSet::class)]
36#[UsesClass(\SqlFixture\Fixture\GenerationRun::class)]
37#[UsesClass(\SqlFixture\Fixture\OverrideRows::class)]
38#[UsesClass(\SqlFixture\Fixture\PlanGenerator::class)]
39#[UsesClass(\SqlFixture\Fixture\PlanSchemaException::class)]
40#[UsesClass(\SqlFixture\Fixture\PlanSchemaValidator::class)]
41#[UsesClass(\SqlFixture\Fixture\RowSpec::class)]
42#[UsesClass(\SqlFixture\Fixture\TableOverrides::class)]
43#[UsesClass(\SqlFixture\Hydrator\HydrationException::class)]
44#[UsesClass(\SqlFixture\Hydrator\HydratorInterface::class)]
45#[UsesClass(\SqlFixture\Plan\ColumnRef::class)]
46#[UsesClass(\SqlFixture\Plan\FixturePlan::class)]
47#[UsesClass(\SqlFixture\Plan\PlanParser::class)]
48#[UsesClass(\SqlFixture\Plan\PlanPrinter::class)]
49#[UsesClass(\SqlFixture\Plan\PlanSyntaxException::class)]
50#[UsesClass(\SqlFixture\Plan\Relation::class)]
51#[UsesClass(\SqlFixture\Plan\RelationKind::class)]
52#[UsesClass(\SqlFixture\Plan\RelationSide::class)]
53#[UsesClass(\SqlFixture\Platform\MySql\MySqlSchemaFetcher::class)]
54#[UsesClass(\SqlFixture\Platform\MySql\MySqlSchemaParser::class)]
55#[UsesClass(\SqlFixture\Platform\MySql\MySqlTypeMapper::class)]
56#[UsesClass(\SqlFixture\Platform\PostgreSql\PostgreSqlSchemaFetcher::class)]
57#[UsesClass(\SqlFixture\Platform\PostgreSql\PostgreSqlSchemaParser::class)]
58#[UsesClass(\SqlFixture\Platform\PostgreSql\PostgreSqlTypeMapper::class)]
59#[UsesClass(\SqlFixture\Schema\SchemaFetcherInterface::class)]
60#[UsesClass(\SqlFixture\Schema\SchemaNotFoundException::class)]
61#[UsesClass(\SqlFixture\Schema\SchemaParseException::class)]
62#[UsesClass(\SqlFixture\Schema\SchemaParserInterface::class)]
63#[UsesClass(\SqlFixture\Schema\SchemaResolverInterface::class)]
64#[UsesClass(\SqlFixture\Schema\StaticSchemaResolver::class)]
65#[UsesClass(\SqlFixture\Schema\TableIdentifier::class)]
66#[UsesClass(\SqlFixture\TypeMapper\TypeMapperInterface::class)]
67#[UsesClass(\SqlFixture\Fixture\Generation\ConnectedTables::class)]
68#[UsesClass(\SqlFixture\Fixture\Generation\OverrideSpecs::class)]
69#[UsesClass(\SqlFixture\Fixture\Generation\RelationCounts::class)]
70#[UsesClass(\SqlFixture\Fixture\Generation\RelationProjection::class)]
71#[UsesClass(\SqlFixture\Fixture\Generation\RowMaterializer::class)]
72#[UsesClass(\SqlFixture\Fixture\RowGeneration::class)]
73#[UsesClass(\SqlFixture\Fixture\Validation\EndpointValidator::class)]
74#[UsesClass(\SqlFixture\Fixture\Validation\OverrideValidator::class)]
75#[UsesClass(\SqlFixture\Hydrator\Reflection\ConstructorHydration::class)]
76#[UsesClass(\SqlFixture\Hydrator\Reflection\PropertyHydration::class)]
77#[UsesClass(\SqlFixture\Hydrator\Reflection\PropertyNames::class)]
78#[UsesClass(\SqlFixture\Hydrator\Reflection\ValueConversion::class)]
79#[UsesClass(\SqlFixture\InvalidOverrideException::class)]
80#[UsesClass(\SqlFixture\Plan\Parsing\PlanStatements::class)]
81#[UsesClass(\SqlFixture\Plan\Parsing\RelationCursor::class)]
82#[UsesClass(\SqlFixture\Plan\Parsing\RelationReader::class)]
83#[UsesClass(\SqlFixture\Plan\PlanStructureException::class)]
84#[UsesClass(\SqlFixture\Plan\Printing\PlanTables::class)]
85#[UsesClass(\SqlFixture\Plan\Printing\RelationGroups::class)]
86#[UsesClass(\SqlFixture\Plan\Printing\StatementPrinter::class)]
87#[UsesClass(\SqlFixture\Plan\Validation\PlanValidation::class)]
88#[UsesClass(\SqlFixture\Plan\Validation\TableName::class)]
89#[UsesClass(\SqlFixture\Platform\MySql\Schema\ColumnParser::class)]
90#[UsesClass(\SqlFixture\Platform\MySql\Schema\CreateTableQuery::class)]
91#[UsesClass(\SqlFixture\Platform\MySql\Schema\DefaultExpression::class)]
92#[UsesClass(\SqlFixture\Platform\MySql\Schema\DefinitionIntegrity::class)]
93#[UsesClass(\SqlFixture\Platform\MySql\Schema\IdentifierQuoter::class)]
94#[UsesClass(\SqlFixture\Platform\MySql\Schema\TableDefinition::class)]
95#[UsesClass(\SqlFixture\Platform\MySql\Schema\TypeParameters::class)]
96#[UsesClass(\SqlFixture\Platform\MySql\Value\ColumnGenerator::class)]
97#[UsesClass(\SqlFixture\Platform\MySql\Value\DecimalGenerator::class)]
98#[UsesClass(\SqlFixture\Platform\MySql\Value\GeometryGenerator::class)]
99#[UsesClass(\SqlFixture\Platform\MySql\Value\IntegerGenerator::class)]
100#[UsesClass(\SqlFixture\Platform\MySql\Value\NumericGenerator::class)]
101#[UsesClass(\SqlFixture\Platform\MySql\Value\SpatialGenerator::class)]
102#[UsesClass(\SqlFixture\Platform\MySql\Value\StringGenerator::class)]
103#[UsesClass(\SqlFixture\Platform\MySql\Value\TextGenerator::class)]
104#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogColumn::class)]
105#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogDdl::class)]
106#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogQuery::class)]
107#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogSchema::class)]
108#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\ColumnParser::class)]
109#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\DefaultExpression::class)]
110#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\DefinitionList::class)]
111#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\TableSyntax::class)]
112#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\TypeDeclaration::class)]
113#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\ColumnGenerator::class)]
114#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\DecimalGenerator::class)]
115#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\NumericGenerator::class)]
116#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\StringGenerator::class)]
117#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\StructuredGenerator::class)]
118#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\TemporalGenerator::class)]
119#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\ColumnParser::class)]
120#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\CreateTableQuery::class)]
121#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\DefaultExpression::class)]
122#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\DefinitionList::class)]
123#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\PragmaColumn::class)]
124#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\PragmaSchema::class)]
125#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\TableSyntax::class)]
126#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\TypeDeclaration::class)]
127#[UsesClass(\SqlFixture\Platform\Sqlite\Value\ColumnGenerator::class)]
128#[UsesClass(\SqlFixture\Platform\Sqlite\Value\TypeAffinity::class)]
129#[UsesClass(\SqlFixture\Provider\SqlSchemaProvider::class)]
130#[UsesClass(\SqlFixture\Schema\DefinitionSegments::class)]
131#[UsesClass(\SqlFixture\Schema\TypeShape::class)]
132#[UsesClass(\SqlFixture\TypeMapper\ParagraphGenerator::class)]
133#[UsesClass(\SqlFixture\Platform\MySql\Schema\TableDefinitionInput::class)]
134#[UsesClass(\SqlFixture\Hydrator\Exception\ClassNotFoundException::class)]
135#[UsesClass(\SqlFixture\Hydrator\Exception\MissingConstructorArgumentException::class)]
136#[UsesClass(\SqlFixture\Fixture\Exception\UnknownOverrideColumnException::class)]
137#[UsesClass(\SqlFixture\Fixture\Exception\NullOverrideException::class)]
138#[UsesClass(\SqlFixture\Fixture\Exception\GeneratedColumnOverrideException::class)]
139#[UsesClass(\SqlFixture\Fixture\Exception\GeneratedColumnReferenceException::class)]
140#[UsesClass(\SqlFixture\Fixture\Exception\MissingRelationValueException::class)]
141#[UsesClass(\SqlFixture\Fixture\Exception\UnknownPlanColumnException::class)]
142#[UsesClass(\SqlFixture\Plan\Exception\EmptyPlanException::class)]
143#[UsesClass(\SqlFixture\Plan\Exception\EmptyTableNameException::class)]
144#[UsesClass(\SqlFixture\Plan\Exception\MissingEndpointColumnsException::class)]
145#[UsesClass(\SqlFixture\Plan\Exception\InvalidTableNameException::class)]
146#[UsesClass(\SqlFixture\Plan\Exception\UnbalancedBracketsException::class)]
147#[UsesClass(\SqlFixture\Plan\Exception\UnexpectedPlanTokenException::class)]
148#[UsesClass(\SqlFixture\Plan\Exception\UnsupportedManyToManyException::class)]
149#[UsesClass(\SqlFixture\Plan\Exception\CompositeArityMismatchException::class)]
150#[UsesClass(\SqlFixture\Plan\Exception\DuplicateColumnBindingException::class)]
151#[UsesClass(\SqlFixture\Plan\Exception\CyclicDependencyException::class)]
152#[UsesClass(\SqlFixture\Plan\Exception\UnboundedSelfReferenceException::class)]
153#[UsesClass(\SqlFixture\Schema\Exception\InvalidSqlException::class)]
154#[UsesClass(\SqlFixture\Schema\Exception\ExpectedCreateTableException::class)]
155#[UsesClass(\SqlFixture\Schema\Exception\MissingColumnDefinitionsException::class)]
156#[UsesClass(\SqlFixture\TypeMapper\IntegerRange::class)]
157#[UsesClass(\SqlFixture\TypeMapper\IntegerWidth::class)]
158#[UsesClass(\SqlFixture\TypeMapper\DecimalRange::class)]
159#[UsesClass(\SqlFixture\Hydrator\Reflection\ConversionTarget::class)]
160final class SqliteIntegrationTest extends TestCase
161{
162 #[Test]
163 public function fixtureProviderWithSqliteDialect(): void
164 {
165 $faker = Factory::create();
166 $faker->seed(12345);
167 $provider = new FixtureProvider(
168 $faker,
169 dialect: PlatformFactory::DRIVER_SQLITE
170 );
171
172 $fixture = $provider->fixture(
173 'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT)'
174 );
175 self::assertArrayHasKey('name', $fixture);
176 self::assertArrayHasKey('email', $fixture);
177 self::assertIsString($fixture['name']);
178 }
179
180 #[Test]
181 public function databaseFixtureProviderWithSqlite(): void
182 {
183 $pdo = new PDO('sqlite::memory:', options: [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
184 $pdo->exec(<<<'SQL'
185 CREATE TABLE users (
186 id INTEGER PRIMARY KEY AUTOINCREMENT,
187 name TEXT NOT NULL,
188 email TEXT,
189 age INTEGER,
190 balance REAL,
191 created_at TEXT DEFAULT CURRENT_TIMESTAMP
192 )
193 SQL);
194
195 $faker = Factory::create();
196 $faker->seed(12345);
197 $provider = new DatabaseFixtureProvider($faker, $pdo);
198
199 self::assertSame(PlatformFactory::DRIVER_SQLITE, $provider->getDriver());
200
201 $fixture = $provider->fixture('users');
202 self::assertArrayNotHasKey('id', $fixture);
203 self::assertArrayHasKey('name', $fixture);
204 self::assertIsString($fixture['name']);
205 }
206
207 #[Test]
208 public function insertAndSelectFixture(): void
209 {
210 $pdo = new PDO('sqlite::memory:', options: [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
211 $pdo->exec(<<<'SQL'
212 CREATE TABLE products (
213 id INTEGER PRIMARY KEY AUTOINCREMENT,
214 name TEXT NOT NULL,
215 price REAL NOT NULL,
216 quantity INTEGER DEFAULT 0
217 )
218 SQL);
219
220 $faker = Factory::create();
221 $faker->seed(12345);
222 $provider = new DatabaseFixtureProvider($faker, $pdo);
223
224 $fixture = $provider->fixture('products');
225
226 $stmt = $pdo->prepare(
227 'INSERT INTO products (name, price, quantity) VALUES (:name, :price, :quantity)'
228 );
229 $stmt->execute($fixture);
230
231 $id = (int) $pdo->lastInsertId();
232 $stmt = $pdo->prepare('SELECT * FROM products WHERE id = ?');
233 $stmt->execute([$id]);
234 $row = $stmt->fetch(PDO::FETCH_ASSOC);
235
236 self::assertNotFalse($row);
237 self::assertIsArray($row);
238 self::assertSame($fixture['name'], $row['name']);
239 self::assertIsFloat($fixture['price']);
240 self::assertIsNumeric($row['price']);
241 self::assertEqualsWithDelta($fixture['price'], (float) $row['price'], 0.001);
242 }
243
244 #[Test]
245 public function fixtureWithOverrides(): void
246 {
247 $pdo = new PDO('sqlite::memory:', options: [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
248 $pdo->exec(<<<'SQL'
249 CREATE TABLE users (
250 id INTEGER PRIMARY KEY,
251 name TEXT NOT NULL,
252 email TEXT NOT NULL
253 )
254 SQL);
255
256 $faker = Factory::create();
257 $provider = new DatabaseFixtureProvider($faker, $pdo);
258
259 $fixture = $provider->fixture('users', [
260 'name' => 'John Doe',
261 'email' => 'john@example.com',
262 ]);
263
264 self::assertSame('John Doe', $fixture['name']);
265 self::assertSame('john@example.com', $fixture['email']);
266 }
267
268 #[Test]
269 public function fixtureWithHydration(): void
270 {
271 $target = new class (0, '', '') {
272 public function __construct(
273 public int $id,
274 public string $name,
275 public string $email,
276 ) {
277 }
278 };
279 $pdo = new PDO('sqlite::memory:', options: [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
280 $pdo->exec(<<<'SQL'
281 CREATE TABLE users (
282 id INTEGER PRIMARY KEY,
283 name TEXT NOT NULL,
284 email TEXT NOT NULL
285 )
286 SQL);
287
288 $faker = Factory::create();
289 $provider = new DatabaseFixtureProvider($faker, $pdo);
290
291 $user = $provider->fixture(
292 'users',
293 ['id' => 1],
294 $target::class
295 );
296
297 self::assertSame(1, $user->id);
298 self::assertNotEmpty($user->name);
299 self::assertNotEmpty($user->email);
300 }
301
302 #[Test]
303 public function fixtureProviderDialectOverride(): void
304 {
305 $faker = Factory::create();
306 $faker->seed(12345);
307
308 $provider = new FixtureProvider($faker);
309
310 $fixture = $provider->fixture(
311 'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)',
312 [],
313 null,
314 PlatformFactory::DRIVER_SQLITE
315 );
316 self::assertArrayHasKey('name', $fixture);
317 }
318
319 #[Test]
320 public function allSqliteTypesFixture(): void
321 {
322 $pdo = new PDO('sqlite::memory:', options: [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
323 $pdo->exec(<<<'SQL'
324 CREATE TABLE all_types (
325 col_integer INTEGER NOT NULL,
326 col_int INT NOT NULL,
327 col_tinyint TINYINT NOT NULL,
328 col_smallint SMALLINT NOT NULL,
329 col_mediumint MEDIUMINT NOT NULL,
330 col_bigint BIGINT NOT NULL,
331 col_text TEXT NOT NULL,
332 col_varchar VARCHAR(100) NOT NULL,
333 col_char CHAR(10) NOT NULL,
334 col_real REAL NOT NULL,
335 col_float FLOAT NOT NULL,
336 col_double DOUBLE NOT NULL,
337 col_decimal DECIMAL(10, 2) NOT NULL,
338 col_blob BLOB NOT NULL,
339 col_boolean BOOLEAN NOT NULL,
340 col_date DATE NOT NULL,
341 col_time TIME NOT NULL,
342 col_datetime DATETIME NOT NULL
343 )
344 SQL);
345
346 $faker = Factory::create();
347 $faker->seed(12345);
348 $provider = new DatabaseFixtureProvider($faker, $pdo);
349
350 $fixture = $provider->fixture('all_types');
351
352 self::assertIsInt($fixture['col_integer']);
353 self::assertIsInt($fixture['col_int']);
354 self::assertIsInt($fixture['col_tinyint']);
355 self::assertIsInt($fixture['col_smallint']);
356 self::assertIsInt($fixture['col_mediumint']);
357 self::assertIsInt($fixture['col_bigint']);
358 self::assertIsString($fixture['col_text']);
359 self::assertIsString($fixture['col_varchar']);
360 self::assertIsString($fixture['col_char']);
361 self::assertIsFloat($fixture['col_real']);
362 self::assertIsFloat($fixture['col_float']);
363 self::assertIsFloat($fixture['col_double']);
364 self::assertIsFloat($fixture['col_decimal']);
365 self::assertIsString($fixture['col_blob']);
366 self::assertContains($fixture['col_boolean'], [0, 1]);
367 $colDate = $fixture['col_date'];
368 self::assertIsString($colDate);
369 self::assertMatchesRegularExpression('/^\d{4}-\d{2}-\d{2}$/', $colDate);
370 $colTime = $fixture['col_time'];
371 self::assertIsString($colTime);
372 self::assertMatchesRegularExpression('/^\d{2}:\d{2}:\d{2}$/', $colTime);
373 $colDatetime = $fixture['col_datetime'];
374 self::assertIsString($colDatetime);
375 self::assertMatchesRegularExpression('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $colDatetime);
376 }
377}
378