packages/ztd-query-mysql/tests/Unit/Sql/Cte/HeaderParserTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Sql\Cte;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\TestCase;
9use ZtdQuery\Platform\MySql\Sql\Cte\HeaderParser;
10
11#[\PHPUnit\Framework\Attributes\UsesClass(\ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::class)]
12#[CoversClass(HeaderParser::class)]
13final class HeaderParserTest extends TestCase
14{
15    public function testParseHeaderExtractsNamesAndStatementOffset(): void
16    {
17        $reader = new HeaderParser(\ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::create());
18        self::assertSame(['names' => ['a'], 'statementOffset' => 21], $reader->parseHeader('WITH a AS (SELECT 1) SELECT * FROM a'));
19        self::assertSame(['names' => [], 'statementOffset' => null], $reader->parseHeader('SELECT 1'));
20        self::assertSame(['names' => [], 'statementOffset' => null], $reader->parseHeader('WITH a AS ('));
21    }
22
23    public function testFindAsIndexSkipsColumnListsAndStopsAtWords(): void
24    {
25        $reader = new HeaderParser(\ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::create());
26        self::assertSame(2, $reader->findAsIndex(\ZtdQuery\Sql\SqlTokenStream::tokenize('() AS', \ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::create())->significantTokens(), 0));
27        self::assertNull($reader->findAsIndex(\ZtdQuery\Sql\SqlTokenStream::tokenize('other AS', \ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::create())->significantTokens(), 0));
28        self::assertNull($reader->findAsIndex([], 0));
29    }
30
31    public function testIdentifierNameUnquotesNamesAndRejectsSymbols(): void
32    {
33        $reader = new HeaderParser(\ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::create());
34        $tokens = \ZtdQuery\Sql\SqlTokenStream::tokenize('plain `a``b` ,', \ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::create())->significantTokens();
35        self::assertSame('plain', $reader->identifierName($tokens[0]));
36        self::assertSame('a`b', $reader->identifierName($tokens[1]));
37        self::assertNull($reader->identifierName($tokens[2]));
38    }
39
40    public function testIsSymbolRequiresTheRequestedDelimiter(): void
41    {
42        $reader = new HeaderParser(\ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::create());
43        $tokens = \ZtdQuery\Sql\SqlTokenStream::tokenize('( )', \ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::create())->significantTokens();
44        self::assertTrue($reader->isSymbol($tokens[0], '('));
45        self::assertFalse($reader->isSymbol($tokens[0], ')'));
46        self::assertFalse($reader->isSymbol(null, '('));
47    }
48
49    public function testTopLevelTokensExcludeNestedBodies(): void
50    {
51        $reader = new HeaderParser(\ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::create());
52        $tokens = $reader->topLevelTokens('WITH a AS (SELECT 1) SELECT 2');
53        self::assertSame(['WITH', 'a', 'AS', '(', ')', 'SELECT', '2'], array_map(static fn (\ZtdQuery\Sql\SqlToken $token): string => $token->text, $tokens));
54    }
55
56    public function testContentsPreservesRecursiveHeaders(): void
57    {
58        $reader = new HeaderParser(\ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::create());
59        self::assertSame(['leading' => '', 'recursive' => true, 'body' => 'a AS (SELECT 1)'], $reader->contents('WITH RECURSIVE a AS (SELECT 1) SELECT * FROM a', 31));
60    }
61
62}
63