packages/ztd-query-mysql/tests/Unit/Schema/MySqlSchemaReflectorTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Schema;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Connection\ConnectionInterface;
11use ZtdQuery\Connection\StatementInterface;
12use ZtdQuery\Platform\MySql\Schema\MySqlSchemaReflector;
13
14#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Relation\ReferenceReader::class)]
15#[CoversClass(MySqlSchemaReflector::class)]
16#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Relation\MySqlSelectRelationParser::class)]
17#[UsesClass(\ZtdQuery\Platform\MySql\Schema\View\MySqlViewDefinitionParser::class)]
18#[UsesClass(\ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::class)]
19final class MySqlSchemaReflectorTest extends TestCase
20{
21 public function testReflectViewsReturnsEmptyWhenQueryFails(): void
22 {
23 $connection = self::createStub(ConnectionInterface::class);
24 $connection->method('query')->willReturn(false);
25
26 self::assertSame([], (new MySqlSchemaReflector($connection))->reflectViews());
27 }
28
29 public function testReflectViewsSkipsMalformedDefinitions(): void
30 {
31 $views = self::createStub(StatementInterface::class);
32 $views->method('fetchAll')->willReturn([
33 ['name' => null], ['name' => ''], ['name' => 'query_failed'],
34 ['name' => 'missing_row'], ['name' => 'non_string'], ['name' => 'invalid'],
35 ['name' => 'active`users'], ['name' => 'all_users'],
36 ]);
37 $missing = self::createStub(StatementInterface::class);
38 $missing->method('fetchAll')->willReturn([]);
39 $nonString = self::createStub(StatementInterface::class);
40 $nonString->method('fetchAll')->willReturn([['Create View' => null]]);
41 $invalid = self::createStub(StatementInterface::class);
42 $invalid->method('fetchAll')->willReturn([['Create View' => 'CREATE VIEW invalid']]);
43 $active = self::createStub(StatementInterface::class);
44 $active->method('fetchAll')->willReturn([['Create View' => 'CREATE VIEW `active``users` AS SELECT * FROM app.users']]);
45 $all = self::createStub(StatementInterface::class);
46 $all->method('fetchAll')->willReturn([['Create View' => 'CREATE VIEW all_users AS SELECT * FROM app.users']]);
47 $responses = [
48 "SHOW FULL TABLES WHERE Table_type = 'VIEW'" => $views,
49 'SHOW CREATE VIEW `query_failed`' => false,
50 'SHOW CREATE VIEW `missing_row`' => $missing,
51 'SHOW CREATE VIEW `non_string`' => $nonString,
52 'SHOW CREATE VIEW `invalid`' => $invalid,
53 'SHOW CREATE VIEW `active``users`' => $active,
54 'SHOW CREATE VIEW `all_users`' => $all,
55 ];
56 $queries = [];
57 $connection = self::createMock(ConnectionInterface::class);
58 $connection->expects(self::exactly(7))->method('query')->willReturnCallback(
59 static function (string $sql) use ($responses, &$queries): StatementInterface|false {
60 $queries[] = $sql;
61 return $responses[$sql];
62 },
63 );
64
65 $definitions = (new MySqlSchemaReflector($connection))->reflectViews();
66
67 self::assertSame(['active`users', 'all_users'], array_keys($definitions));
68 self::assertSame(['users'], $definitions['active`users']->dependencies);
69 self::assertSame([
70 "SHOW FULL TABLES WHERE Table_type = 'VIEW'",
71 'SHOW CREATE VIEW `query_failed`', 'SHOW CREATE VIEW `missing_row`',
72 'SHOW CREATE VIEW `non_string`', 'SHOW CREATE VIEW `invalid`',
73 'SHOW CREATE VIEW `active``users`', 'SHOW CREATE VIEW `all_users`',
74 ], $queries);
75 }
76
77 public function testGetCreateStatementReturnsNullWhenQueryFails(): void
78 {
79 $connection = self::createStub(ConnectionInterface::class);
80 $connection->method('query')->willReturn(false);
81
82 $reflector = new MySqlSchemaReflector($connection);
83 self::assertNull($reflector->getCreateStatement('users'));
84 }
85
86 public function testGetCreateStatementReturnsNullWhenNoRows(): void
87 {
88 $statement = self::createStub(StatementInterface::class);
89 $statement->method('fetchAll')->willReturn([]);
90
91 $connection = self::createStub(ConnectionInterface::class);
92 $connection->method('query')->willReturn($statement);
93
94 $reflector = new MySqlSchemaReflector($connection);
95 self::assertNull($reflector->getCreateStatement('users'));
96 }
97
98 public function testGetCreateStatementReturnsSql(): void
99 {
100 $createSql = 'CREATE TABLE users (id INT PRIMARY KEY)';
101 $statement = self::createStub(StatementInterface::class);
102 $statement->method('fetchAll')->willReturn([['Create Table' => $createSql]]);
103
104 $connection = self::createStub(ConnectionInterface::class);
105 $connection->method('query')->willReturn($statement);
106
107 $reflector = new MySqlSchemaReflector($connection);
108 self::assertSame($createSql, $reflector->getCreateStatement('users'));
109 }
110
111 public function testReflectAllReturnsEmptyWhenQueryFails(): void
112 {
113 $connection = self::createStub(ConnectionInterface::class);
114 $connection->method('query')->willReturn(false);
115
116 $reflector = new MySqlSchemaReflector($connection);
117 self::assertSame([], $reflector->reflectAll());
118 }
119}
120