packages/sql-faker/tests/Unit/Sqlite/Generation/Lexeme/WindowNameLexemeGeneratorTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Sqlite\Generation\Lexeme;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFaker\Generation\Candidate\MatchingLexemeGenerator;
12use SqlFaker\Generation\Candidate\RegisteredLexemeGenerator;
13use SqlFaker\Generation\Exception\LexicalException;
14use SqlFaker\Generation\Lexeme\Lexeme;
15use SqlFaker\Generation\Lexeme\LexemeCandidates;
16use SqlFaker\Generation\Lexeme\LexemeInput;
17use SqlFaker\Generation\Lexeme\LexemeSequence;
18use SqlFaker\Generation\Lexeme\ResolvedOutput;
19use SqlFaker\Generation\Token\TerminalOccurrence;
20use SqlFaker\Generation\Token\TerminalSequence;
21use SqlFaker\Sqlite\Generation\Lexeme\WindowNameLexemeGenerator;
22
23#[CoversClass(WindowNameLexemeGenerator::class)]
24#[UsesClass(Lexeme::class)]
25#[UsesClass(LexemeCandidates::class)]
26#[UsesClass(LexemeInput::class)]
27#[UsesClass(LexemeSequence::class)]
28#[UsesClass(MatchingLexemeGenerator::class)]
29#[UsesClass(RegisteredLexemeGenerator::class)]
30#[UsesClass(ResolvedOutput::class)]
31#[UsesClass(TerminalOccurrence::class)]
32#[UsesClass(TerminalSequence::class)]
33#[UsesClass(LexicalException::class)]
34#[UsesClass(\SqlFaker\Generation\Lexeme\SpacingConstraint::class)]
35#[UsesClass(\SqlFaker\Generation\Token\ProductionOccurrence::class)]
36final class WindowNameLexemeGeneratorTest extends TestCase
37{
38 /**
39 * @param non-empty-list<string> $names
40 */
41 #[DataProvider('providerNames')]
42 public function testGenerateQuotesIndexedOnlyWhereWindowLookaheadRequiresAnIdentifier(array $names, string $expected, string $source): void
43 {
44 $input = new LexemeInput(TerminalSequence::fromNames($names), count($names) - 1, new ResolvedOutput());
45 $generator = new WindowNameLexemeGenerator(new RegisteredLexemeGenerator(['INDEXED' => ['INDEXED'], 'JOIN_KW' => ['INNER']], 'registrations'));
46 $candidates = $generator->generate($input);
47 self::assertNotNull($candidates);
48 $candidate = [...$candidates->sequences()][0];
49 self::assertSame($expected, $candidate->lexemes[0]->text);
50 self::assertSame($input->terminal(), $candidate->lexemes[0]->origin);
51 self::assertSame([$source], [...$candidate->sources()]);
52 }
53
54 /**
55 * @return iterable<string, array{non-empty-list<string>, string, string}>
56 */
57 public static function providerNames(): iterable
58 {
59 yield 'over' => [['OVER', 'INDEXED'], '"INDEXED"', 'registrations:INDEXED'];
60 yield 'window' => [['WINDOW', 'INDEXED'], '"INDEXED"', 'registrations:INDEXED'];
61 yield 'other name' => [['OVER', 'JOIN_KW'], 'INNER', 'registrations:INNER'];
62 yield 'ordinary indexed' => [['SELECT', 'INDEXED'], 'INDEXED', 'registrations:INDEXED'];
63 yield 'standalone' => [['INDEXED'], 'INDEXED', 'registrations:INDEXED'];
64 }
65
66 public function testGenerateRetainsNonApplicability(): void
67 {
68 $generator = new WindowNameLexemeGenerator(new MatchingLexemeGenerator('OTHER', new RegisteredLexemeGenerator([], 'registrations')));
69 self::assertNull($generator->generate(new LexemeInput(TerminalSequence::fromNames(['OVER', 'INDEXED']), 1, new ResolvedOutput())));
70 }
71
72 public function testGeneratePropagatesMissingRegistrationData(): void
73 {
74 $generator = new WindowNameLexemeGenerator(new RegisteredLexemeGenerator([], 'registrations'));
75 $this->expectException(LexicalException::class);
76 $generator->generate(new LexemeInput(TerminalSequence::fromNames(['OVER', 'INDEXED']), 1, new ResolvedOutput()));
77 }
78}
79