packages/sql-faker/tests/Unit/PostgreSql/Generation/Rewrite/Query/SelectOptionsRuleTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\PostgreSql\Generation\Rewrite\Query;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFaker\Generation\Token\ProductionOccurrence;
12use SqlFaker\Generation\Token\TerminalOccurrence;
13use SqlFaker\Generation\Token\TerminalSequence;
14use SqlFaker\PostgreSql\Generation\Rewrite\Query\SelectOptionsRule;
15
16#[CoversClass(SelectOptionsRule::class)]
17#[UsesClass(ProductionOccurrence::class)]
18#[UsesClass(TerminalOccurrence::class)]
19#[UsesClass(TerminalSequence::class)]
20final class SelectOptionsRuleTest extends TestCase
21{
22    #[DataProvider('providerOptions')]
23    public function testRewriteScopesOptionsToTheSameSelectNode(string $innerRule, string $outerRule, string $boundary, bool $removed): void
24    {
25        $body = new TerminalOccurrence('SELECT', 10, [0, 1, 2, 3], ['select_no_parens', $boundary, 'select_no_parens', 'simple_select']);
26        $inner = new TerminalOccurrence('INNER', 11, [0, 1, 2, 4], ['select_no_parens', $boundary, 'select_no_parens', $innerRule]);
27        $outer = new TerminalOccurrence('OUTER', 12, [0, 5], ['select_no_parens', $outerRule]);
28        $terminals = [$body, $inner, $outer];
29        $input = new TerminalSequence($terminals, $terminals, [], [
30            new ProductionOccurrence(0, null, 'select_no_parens', 0),
31            new ProductionOccurrence(1, 0, $boundary, 0),
32            new ProductionOccurrence(2, 1, 'select_no_parens', 0),
33            new ProductionOccurrence(3, 2, 'simple_select', 0),
34            new ProductionOccurrence(4, 2, $innerRule, 0),
35            new ProductionOccurrence(5, 0, $outerRule, 0),
36        ]);
37        $rule = new SelectOptionsRule();
38        $result = $rule->rewrite($input);
39        self::assertSame($removed ? [$body, $inner] : $terminals, $result->terminals);
40        self::assertSame($input->original, $result->original);
41        self::assertCount(count($result->terminals), array_unique(array_map(static fn ($terminal): int => $terminal->id, $result->terminals)));
42        self::assertSame($input->productions, $result->productions);
43        self::assertSame($result, $rule->rewrite($result));
44    }
45
46    /**
47     * @return iterable<array{string, string, string, bool}>
48     */
49    public static function providerOptions(): iterable
50    {
51        foreach ([['sort_clause', 'opt_sort_clause'], ['opt_sort_clause', 'sort_clause'], ['select_limit', 'opt_select_limit'], ['opt_select_limit', 'select_limit'], ['with_clause', 'with_clause']] as [$inner, $outer]) {
52            foreach (['select_clause', 'select_with_parens'] as $wrapper) {
53                yield [$inner, $outer, $wrapper, true];
54            }
55            foreach (['simple_select', 'from_clause', 'a_expr', 'with_clause'] as $boundary) {
56                yield [$inner, $outer, $boundary, false];
57            }
58        }
59        yield ['sort_clause', 'select_limit', 'select_with_parens', false];
60        yield ['with_clause', 'sort_clause', 'select_with_parens', false];
61    }
62
63    public function testRewriteIgnoresEmptyAndUnscopedClauses(): void
64    {
65        $input = new TerminalSequence([], [], [], [
66            new ProductionOccurrence(0, null, 'select_no_parens', 0),
67            new ProductionOccurrence(1, 0, 'opt_sort_clause', 0),
68            new ProductionOccurrence(2, null, 'sort_clause', 0),
69        ]);
70        self::assertSame($input, (new SelectOptionsRule())->rewrite($input));
71    }
72
73    public function testQueryFollowsOnlySelectWrappersAndResolvesOptionalClauses(): void
74    {
75        $nodes = [
76            new ProductionOccurrence(0, null, 'SelectStmt', 0),
77            new ProductionOccurrence(1, 0, 'select_no_parens', 0),
78            new ProductionOccurrence(2, 1, 'select_clause', 0),
79            new ProductionOccurrence(3, 2, 'select_with_parens', 0),
80            new ProductionOccurrence(4, 3, 'select_no_parens', 0),
81            new ProductionOccurrence(5, 4, 'opt_sort_clause', 0),
82            new ProductionOccurrence(6, 5, 'sort_clause', 0),
83            new ProductionOccurrence(7, 4, 'opt_select_limit', 0),
84            new ProductionOccurrence(8, 7, 'select_limit', 0),
85        ];
86        $rule = new SelectOptionsRule();
87        self::assertSame(1, $rule->query($nodes, $nodes[6]));
88        self::assertSame(1, $rule->query($nodes, $nodes[8]));
89        self::assertNull($rule->query($nodes, $nodes[0]));
90        self::assertNull($rule->query($nodes, $nodes[1]));
91        self::assertNull($rule->query([], $nodes[6]));
92    }
93}
94