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