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