packages/sql-fixture/tests/Unit/Provider/DatabaseFixtureProviderTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Provider;
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\Platform\Sqlite\SqliteSchemaFetcher;
14use SqlFixture\Platform\Sqlite\SqliteSchemaParser;
15use SqlFixture\Platform\Sqlite\SqliteTypeMapper;
16use SqlFixture\Provider\DatabaseFixtureProvider;
17use SqlFixture\Provider\FixtureGenerator;
18use SqlFixture\Provider\PlatformFactory;
19use SqlFixture\Schema\ColumnDefinition;
20use SqlFixture\Schema\TableSchema;
21
22#[CoversClass(DatabaseFixtureProvider::class)]
23#[UsesClass(FixtureGenerator::class)]
24#[UsesClass(PlatformFactory::class)]
25#[UsesClass(SqliteSchemaFetcher::class)]
26#[UsesClass(SqliteSchemaParser::class)]
27#[UsesClass(SqliteTypeMapper::class)]
28#[UsesClass(ColumnDefinition::class)]
29#[UsesClass(TableSchema::class)]
30#[CoversClass(\SqlFixture\Provider\DatabaseSchemaCache::class)]
31#[UsesClass(\SqlFixture\Hydrator\HydrationException::class)]
32#[UsesClass(\SqlFixture\Hydrator\HydratorInterface::class)]
33#[UsesClass(\SqlFixture\Hydrator\ReflectionHydrator::class)]
34#[UsesClass(\SqlFixture\Platform\MySql\MySqlSchemaFetcher::class)]
35#[UsesClass(\SqlFixture\Platform\MySql\MySqlSchemaParser::class)]
36#[UsesClass(\SqlFixture\Platform\MySql\MySqlTypeMapper::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\Schema\SchemaFetcherInterface::class)]
41#[UsesClass(\SqlFixture\Schema\SchemaParseException::class)]
42#[UsesClass(\SqlFixture\Schema\SchemaParserInterface::class)]
43#[UsesClass(\SqlFixture\TypeMapper\TypeMapperInterface::class)]
44#[UsesClass(\SqlFixture\Fixture\RowGeneration::class)]
45#[UsesClass(\SqlFixture\Fixture\Validation\OverrideValidator::class)]
46#[UsesClass(\SqlFixture\Hydrator\Reflection\ConstructorHydration::class)]
47#[UsesClass(\SqlFixture\Hydrator\Reflection\PropertyHydration::class)]
48#[UsesClass(\SqlFixture\Hydrator\Reflection\PropertyNames::class)]
49#[UsesClass(\SqlFixture\Hydrator\Reflection\ValueConversion::class)]
50#[UsesClass(\SqlFixture\Fixture\Exception\InvalidOverrideException::class)]
51#[UsesClass(\SqlFixture\Platform\MySql\Schema\ColumnParser::class)]
52#[UsesClass(\SqlFixture\Platform\MySql\Schema\CreateTableQuery::class)]
53#[UsesClass(\SqlFixture\Platform\MySql\Schema\DefaultExpression::class)]
54#[UsesClass(\SqlFixture\Platform\MySql\Schema\DefinitionIntegrity::class)]
55#[UsesClass(\SqlFixture\Platform\MySql\Schema\IdentifierQuoter::class)]
56#[UsesClass(\SqlFixture\Platform\MySql\Schema\TableDefinition::class)]
57#[UsesClass(\SqlFixture\Platform\MySql\Schema\TypeParameters::class)]
58#[UsesClass(\SqlFixture\Platform\MySql\Value\ColumnGenerator::class)]
59#[UsesClass(\SqlFixture\Platform\MySql\Value\DecimalGenerator::class)]
60#[UsesClass(\SqlFixture\Platform\MySql\Value\GeometryGenerator::class)]
61#[UsesClass(\SqlFixture\Platform\MySql\Value\IntegerGenerator::class)]
62#[UsesClass(\SqlFixture\Platform\MySql\Value\NumericGenerator::class)]
63#[UsesClass(\SqlFixture\Platform\MySql\Value\SpatialGenerator::class)]
64#[UsesClass(\SqlFixture\Platform\MySql\Value\StringGenerator::class)]
65#[UsesClass(\SqlFixture\Platform\MySql\Value\TextGenerator::class)]
66#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogColumn::class)]
67#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogDdl::class)]
68#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogQuery::class)]
69#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\CatalogSchema::class)]
70#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\ColumnParser::class)]
71#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\DefaultExpression::class)]
72#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\DefinitionList::class)]
73#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\TableSyntax::class)]
74#[UsesClass(\SqlFixture\Platform\PostgreSql\Schema\TypeDeclaration::class)]
75#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\ColumnGenerator::class)]
76#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\DecimalGenerator::class)]
77#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\NumericGenerator::class)]
78#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\StringGenerator::class)]
79#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\StructuredGenerator::class)]
80#[UsesClass(\SqlFixture\Platform\PostgreSql\Value\TemporalGenerator::class)]
81#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\ColumnParser::class)]
82#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\CreateTableQuery::class)]
83#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\DefaultExpression::class)]
84#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\DefinitionList::class)]
85#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\PragmaColumn::class)]
86#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\PragmaSchema::class)]
87#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\TableSyntax::class)]
88#[UsesClass(\SqlFixture\Platform\Sqlite\Schema\TypeDeclaration::class)]
89#[UsesClass(\SqlFixture\Platform\Sqlite\Value\ColumnGenerator::class)]
90#[UsesClass(\SqlFixture\Platform\Sqlite\Value\TypeAffinity::class)]
91#[UsesClass(\SqlFixture\Schema\DefinitionSegments::class)]
92#[UsesClass(\SqlFixture\Schema\TypeShape::class)]
93#[UsesClass(\SqlFixture\TypeMapper\ParagraphGenerator::class)]
94#[UsesClass(\SqlFixture\Platform\MySql\Schema\TableDefinitionInput::class)]
95#[UsesClass(\SqlFixture\Hydrator\Exception\ClassNotFoundException::class)]
96#[UsesClass(\SqlFixture\Hydrator\Exception\MissingConstructorArgumentException::class)]
97#[UsesClass(\SqlFixture\Fixture\Exception\UnknownOverrideColumnException::class)]
98#[UsesClass(\SqlFixture\Fixture\Exception\NullOverrideException::class)]
99#[UsesClass(\SqlFixture\Fixture\Exception\GeneratedColumnOverrideException::class)]
100#[UsesClass(\SqlFixture\Schema\Exception\InvalidSqlException::class)]
101#[UsesClass(\SqlFixture\Schema\Exception\ExpectedCreateTableException::class)]
102#[UsesClass(\SqlFixture\Schema\Exception\MissingColumnDefinitionsException::class)]
103#[UsesClass(\SqlFixture\TypeMapper\IntegerRange::class)]
104#[UsesClass(\SqlFixture\TypeMapper\IntegerWidth::class)]
105#[UsesClass(\SqlFixture\TypeMapper\DecimalRange::class)]
106#[UsesClass(\SqlFixture\Hydrator\Reflection\ConversionTarget::class)]
107#[UsesClass(\SqlFixture\Fixture\RowGenerator::class)]
108#[UsesClass(\SqlFixture\Version\ServerVersion::class)]
109#[UsesClass(\SqlFixture\Version\Releases::class)]
110#[UsesClass(\SqlFixture\Version\ReleaseNumber::class)]
111#[UsesClass(\SqlFixture\Version\UnsupportedVersionException::class)]
112final class DatabaseFixtureProviderTest extends TestCase
113{
114    #[Test]
115    public function testFixtureGeneratesArrayFromSqliteTable(): void
116    {
117        $pdo = new PDO('sqlite::memory:');
118        $pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT)');
119
120        $faker = Factory::create();
121        $provider = new DatabaseFixtureProvider($faker, $pdo);
122
123        $result = $provider->fixture('users');
124        self::assertArrayHasKey('name', $result);
125    }
126
127    #[Test]
128    public function testClearCacheAllowsRefetch(): void
129    {
130        $pdo = new PDO('sqlite::memory:');
131        $pdo->exec('CREATE TABLE items (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT)');
132
133        $faker = Factory::create();
134        $provider = new DatabaseFixtureProvider($faker, $pdo);
135
136        $provider->fixture('items');
137        $provider->clearCache();
138        $result = $provider->fixture('items');
139        self::assertArrayHasKey('title', $result);
140    }
141
142    #[Test]
143    public function testGetDriverReturnsSqlite(): void
144    {
145        $pdo = new PDO('sqlite::memory:');
146        $faker = Factory::create();
147        $provider = new DatabaseFixtureProvider($faker, $pdo);
148
149        self::assertSame('sqlite', $provider->getDriver());
150    }
151
152    #[Test]
153    public function testGetFixtureGeneratorReturnsInstance(): void
154    {
155        $pdo = new PDO('sqlite::memory:');
156        $faker = Factory::create();
157        $provider = new DatabaseFixtureProvider($faker, $pdo);
158
159        self::assertSame(['id' => 7], $provider->getFixtureGenerator()->generate(new TableSchema('sample', ['id' => new ColumnDefinition('id', 'INT')]), ['id' => 7]));
160    }
161
162    #[Test]
163    public function testFixtureWithCustomTypeMapper(): void
164    {
165        $pdo = new PDO('sqlite::memory:');
166        $pdo->exec('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT NOT NULL)');
167
168        $faker = Factory::create();
169        $typeMapper = new SqliteTypeMapper();
170        $provider = new DatabaseFixtureProvider($faker, $pdo, typeMapper: $typeMapper);
171
172        $data = $provider->fixture('test');
173        self::assertArrayHasKey('name', $data);
174    }
175
176    #[Test]
177    public function testFixtureWithCustomSchemaFetcher(): void
178    {
179        $pdo = new PDO('sqlite::memory:');
180        $pdo->exec('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT NOT NULL)');
181
182        $faker = Factory::create();
183        $fetcher = new SqliteSchemaFetcher();
184        $provider = new DatabaseFixtureProvider($faker, $pdo, schemaFetcher: $fetcher);
185
186        $data = $provider->fixture('test');
187        self::assertArrayHasKey('name', $data);
188    }
189
190    #[Test]
191    public function testSchemaCacheWorksOnSecondCall(): void
192    {
193        $pdo = new PDO('sqlite::memory:');
194        $pdo->exec('CREATE TABLE items (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT NOT NULL)');
195
196        $faker = Factory::create();
197        $provider = new DatabaseFixtureProvider($faker, $pdo);
198
199        $data1 = $provider->fixture('items');
200        $data2 = $provider->fixture('items');
201
202        self::assertArrayHasKey('val', $data1);
203        self::assertArrayHasKey('val', $data2);
204    }
205
206    #[Test]
207    public function testFixtureNormalizesQuotedTableNames(): void
208    {
209        $pdo = new PDO('sqlite::memory:');
210        $pdo->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL)');
211
212        $faker = Factory::create();
213        $provider = new DatabaseFixtureProvider($faker, $pdo);
214
215        $data = $provider->fixture('products');
216        self::assertArrayHasKey('name', $data);
217    }
218
219    #[Test]
220    public function testFixtureWithOverrides(): void
221    {
222        $pdo = new PDO('sqlite::memory:');
223        $pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)');
224
225        $faker = Factory::create();
226        $provider = new DatabaseFixtureProvider($faker, $pdo);
227
228        $data = $provider->fixture('users', ['name' => 'Override']);
229        self::assertSame('Override', $data['name']);
230    }
231
232    #[Test]
233    public function testGetVersionMatchesTheLinkedSqlite(): void
234    {
235        $provider = new DatabaseFixtureProvider(Factory::create(), new PDO('sqlite::memory:'));
236
237        self::assertSame('sqlite-3.47.2', $provider->getVersion());
238    }
239
240    #[Test]
241    public function testGetVersionKeepsAnExplicitTag(): void
242    {
243        $provider = new DatabaseFixtureProvider(Factory::create(), new PDO('sqlite::memory:'), version: 'sqlite-3.47.2');
244
245        self::assertSame('sqlite-3.47.2', $provider->getVersion());
246
247        $this->expectException(\SqlFixture\Version\UnsupportedVersionException::class);
248        new DatabaseFixtureProvider(Factory::create(), new PDO('sqlite::memory:'), version: 'mysql-8.4.7');
249    }
250}
251