packages/ztd-query-sqlite/tests/Unit/Sql/Relation/RelationSourceParserTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Sql\Relation;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Platform\Sqlite\Sql\Relation\RelationSourceParser;
11use ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile;
12use ZtdQuery\Sql\SqlToken;
13use ZtdQuery\Sql\SqlTokenStream;
14
15#[CoversClass(RelationSourceParser::class)]
16#[UsesClass(SqliteLexerProfile::class)]
17final class RelationSourceParserTest extends TestCase
18{
19 public function testReferencesFromClauseFindsJoinsButNotSubqueryBodies(): void
20 {
21 $parser = new RelationSourceParser();
22 self::assertSame(['users', 'orders'], array_column($parser->referencesFromClause('main.users u JOIN orders o ON o.user_id = u.id'), 'name'));
23 self::assertSame(['users', 'orders'], array_column($parser->referencesFromClause('(users JOIN orders ON users.id = orders.user_id)'), 'name'));
24 self::assertSame([], $parser->referencesFromClause('(SELECT * FROM hidden)'));
25 self::assertSame([], $parser->referencesFromClause('json_each(items)'));
26 self::assertSame([], $parser->referencesFromClause('()'));
27 }
28
29 public function testNestedReferencesTranslatesOffsets(): void
30 {
31 $parser = new RelationSourceParser();
32 self::assertSame([['name' => 'users', 'start' => 3, 'unqualifiedStart' => 3, 'end' => 8]], $parser->nestedReferences('xx(users)', 3, 8));
33 self::assertSame([], $parser->nestedReferences('(SELECT 1)', 1, 9));
34 }
35
36 public function testClosingTokenReturnsMatchingTopLevelEnd(): void
37 {
38 $parser = new RelationSourceParser();
39 $tokens = SqlTokenStream::tokenize('(users)', SqliteLexerProfile::create())->significantTokens();
40 self::assertSame($tokens[2], $parser->closingToken($tokens, 0));
41 self::assertNull($parser->closingToken([], 0));
42 }
43
44 public function testReferenceAtRetainsQualifiedOffsets(): void
45 {
46 $parser = new RelationSourceParser();
47 $sql = 'main.users';
48 self::assertSame(['name' => 'users', 'start' => 0, 'unqualifiedStart' => 5, 'end' => 10], $parser->referenceAt($sql, SqlTokenStream::tokenize($sql, SqliteLexerProfile::create())->significantTokens(), 0));
49 self::assertNull($parser->referenceAt('SELECT', SqlTokenStream::tokenize('SELECT', SqliteLexerProfile::create())->significantTokens(), 0));
50 }
51
52 public function testIdentifierComponentAtAcceptsWordsAndQuotedNames(): void
53 {
54 $parser = new RelationSourceParser();
55 self::assertSame(['full name', 1, 0, 0, 11], $parser->identifierComponentAt(SqlTokenStream::tokenize('"full name"', SqliteLexerProfile::create())->significantTokens(), 0));
56 self::assertNull($parser->identifierComponentAt([], 0));
57 self::assertNull($parser->identifierComponentAt(SqlTokenStream::tokenize('123', SqliteLexerProfile::create())->significantTokens(), 0));
58 }
59
60 public function testFindFromEndStopsAtClauseBoundary(): void
61 {
62 $parser = new RelationSourceParser();
63 $sql = 'SELECT * FROM users ORDER BY id';
64 $tokens = SqlTokenStream::tokenize($sql, SqliteLexerProfile::create())->significantTokens();
65 self::assertSame(20, $parser->findFromEnd($sql, $tokens, $tokens[2]));
66 self::assertSame(19, $parser->findFromEnd('SELECT * FROM users', array_slice($tokens, 0, 4), $tokens[2]));
67 }
68
69 public function testMatchesKeywordSequenceChecksEveryPart(): void
70 {
71 $parser = new RelationSourceParser();
72 $tokens = SqlTokenStream::tokenize('ORDER BY id', SqliteLexerProfile::create())->significantTokens();
73 self::assertTrue($parser->matchesKeywordSequence($tokens, 0, ['ORDER', 'BY']));
74 self::assertFalse($parser->matchesKeywordSequence($tokens, 0, ['ORDER', 'FROM']));
75 self::assertFalse($parser->matchesKeywordSequence([], 0, ['ORDER']));
76 }
77
78 public function testTokensOmitsCommentsAndWhitespace(): void
79 {
80 self::assertSame(['SELECT', '1'], array_map(static fn (SqlToken $token): string => $token->text, (new RelationSourceParser())->tokens(' SELECT /* x */ 1 ')));
81 }
82
83}
84