packages/sql-faker/tests/Unit/MySql/Generation/Rewrite/Alter/OrderByRuleTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\MySql\Generation\Rewrite\Alter;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlFaker\Generation\Token\ProductionOccurrence;
11use SqlFaker\Generation\Token\TerminalOccurrence;
12use SqlFaker\Generation\Token\TerminalSequence;
13use SqlFaker\MySql\Generation\Rewrite\Alter\OrderByRule;
14
15#[CoversClass(OrderByRule::class)]
16#[UsesClass(ProductionOccurrence::class)]
17#[UsesClass(TerminalOccurrence::class)]
18#[UsesClass(TerminalSequence::class)]
19final class OrderByRuleTest extends TestCase
20{
21    public function testRewriteMovesTheCompleteOrderListAfterFollowingActions(): void
22    {
23        $order = new TerminalOccurrence('ORDER_SYM', 10, [0, 1, 2], ['alter_list', 'alter_list', 'alter_list_item']);
24        $by = new TerminalOccurrence('BY', 11, [0, 1, 2], ['alter_list', 'alter_list', 'alter_list_item']);
25        $a = new TerminalOccurrence('A', 12, [0, 1, 2, 3], ['alter_list', 'alter_list', 'alter_list_item', 'alter_order_list']);
26        $innerComma = new TerminalOccurrence(',', 13, [0, 1, 2, 3], ['alter_list', 'alter_list', 'alter_list_item', 'alter_order_list']);
27        $b = new TerminalOccurrence('B', 14, [0, 1, 2, 3], ['alter_list', 'alter_list', 'alter_list_item', 'alter_order_list']);
28        $comma = new TerminalOccurrence(',', 15, [0], ['alter_list']);
29        $enable = new TerminalOccurrence('ENABLE_SYM', 16, [0, 4], ['alter_list', 'alter_list_item']);
30        $keys = new TerminalOccurrence('KEYS', 17, [0, 4], ['alter_list', 'alter_list_item']);
31        $outside = new TerminalOccurrence('OTHER', 18, [9], ['other_statement']);
32        $terminals = [$order, $by, $a, $innerComma, $b, $comma, $enable, $keys, $outside];
33        $input = new TerminalSequence($terminals, $terminals, [], [
34            new ProductionOccurrence(0, null, 'alter_list', 1), new ProductionOccurrence(1, 0, 'alter_list', 0),
35            new ProductionOccurrence(2, 1, 'alter_list_item', 0), new ProductionOccurrence(3, 2, 'alter_order_list', 1),
36            new ProductionOccurrence(4, 0, 'alter_list_item', 0), new ProductionOccurrence(9, null, 'other_statement', 0),
37        ]);
38        $rule = new OrderByRule();
39        $result = $rule->rewrite($input);
40        self::assertSame([$enable, $keys, $comma, $order, $by, $a, $innerComma, $b, $outside], $result->terminals);
41        self::assertSame($input->original, $result->original);
42        self::assertCount(count($result->terminals), array_unique(array_map(static fn ($terminal): int => $terminal->id, $result->terminals)));
43        self::assertSame($input->productions, $result->productions);
44        self::assertSame(['sql_yacc.yy:alter_list:order-by-last'], $result->rewrites);
45        self::assertSame($result, $rule->rewrite($result));
46    }
47
48    public function testReorderPreservesAnOrderByThatAlreadyEndsItsOwnStatement(): void
49    {
50        $order = new TerminalOccurrence('ORDER_SYM', 2, [0, 1], ['alter_list', 'alter_list_item']);
51        $outside = new TerminalOccurrence('ENABLE_SYM', 3, [4, 5], ['alter_list', 'alter_list_item']);
52        $input = new TerminalSequence([$order, $outside], [], [], [
53            new ProductionOccurrence(0, null, 'alter_list', 0), new ProductionOccurrence(1, 0, 'alter_list_item', 0),
54            new ProductionOccurrence(4, null, 'alter_list', 0), new ProductionOccurrence(5, 4, 'alter_list_item', 0),
55        ]);
56        self::assertSame($input, (new OrderByRule())->rewrite($input));
57        self::assertSame($input, (new OrderByRule())->reorder($input, 99));
58    }
59
60    public function testRewritePreservesUnrelatedOrderByTokens(): void
61    {
62        $input = TerminalSequence::fromNames(['ORDER_SYM', 'BY', 'IDENT', ',', 'ENABLE_SYM', 'KEYS']);
63        self::assertSame($input, (new OrderByRule())->rewrite($input));
64    }
65    public function testRewriteCombinesRepeatedOrderByActionsIntoOneColumnList(): void
66    {
67        $tokens = [
68            new TerminalOccurrence('ORDER_SYM', 10, [0, 1], ['alter_list', 'alter_list_item']),
69            new TerminalOccurrence('BY', 11, [0, 1], ['alter_list', 'alter_list_item']),
70            new TerminalOccurrence('A', 12, [0, 1], ['alter_list', 'alter_list_item']),
71            new TerminalOccurrence(',', 13, [0], ['alter_list']),
72            new TerminalOccurrence('ORDER_SYM', 14, [0, 2], ['alter_list', 'alter_list_item']),
73            new TerminalOccurrence('BY', 15, [0, 2], ['alter_list', 'alter_list_item']),
74            new TerminalOccurrence('B', 16, [0, 2], ['alter_list', 'alter_list_item']),
75        ];
76        $input = new TerminalSequence($tokens, $tokens, [], [new ProductionOccurrence(0, null, 'alter_list', 1), new ProductionOccurrence(1, 0, 'alter_list_item', 0), new ProductionOccurrence(2, 0, 'alter_list_item', 0)]);
77        $rule = new OrderByRule();
78        $result = $rule->rewrite($input);
79        self::assertSame(['ORDER_SYM', 'BY', 'A', ',', 'B'], $result->names());
80        self::assertSame([10, 11, 12, 13, 16], array_map(static fn ($terminal): int => $terminal->id, $result->terminals));
81        self::assertSame($input->original, $result->original);
82        self::assertSame($input->productions, $result->productions);
83        self::assertSame($result, $rule->rewrite($result));
84    }
85
86}
87