packages/sql-faker/tests/Unit/Sqlite/Generation/GrammarAdaptationTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Sqlite\Generation;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlFaker\Grammar\Model\Grammar;
11use SqlFaker\Grammar\Model\NonTerminal;
12use SqlFaker\Grammar\Model\Production;
13use SqlFaker\Grammar\Model\ProductionRule;
14use SqlFaker\Grammar\Model\Terminal;
15use SqlFaker\Sqlite\Generation\GrammarAdaptation;
16
17#[CoversClass(GrammarAdaptation::class)]
18#[UsesClass(Grammar::class)]
19#[UsesClass(NonTerminal::class)]
20#[UsesClass(Production::class)]
21#[UsesClass(ProductionRule::class)]
22#[UsesClass(Terminal::class)]
23final class GrammarAdaptationTest extends TestCase
24{
25    public function testAdaptedGivesEachStatementKindARuleOfItsOwn(): void
26    {
27        $grammar = new Grammar('cmd', [
28            'cmd' => new ProductionRule('cmd', [
29                new Production([new Terminal('DELETE'), new Terminal('FROM'), new NonTerminal('nm')]),
30                new Production([new Terminal('UPDATE'), new NonTerminal('nm')]),
31            ]),
32        ]);
33
34        self::assertSame(
35            ['cmd', 'delete', 'update'],
36            array_keys((new GrammarAdaptation())->adapted($grammar)->ruleMap),
37        );
38    }
39
40    public function testAdaptedLeavesAGrammarWithoutACmdRuleAlone(): void
41    {
42        $grammar = new Grammar('expr', ['expr' => new ProductionRule('expr', [new Production([new Terminal('NULL')])])]);
43
44        self::assertSame(['expr'], array_keys((new GrammarAdaptation())->adapted($grammar)->ruleMap));
45    }
46
47    public function testWithStatementRulesPreservesEveryImportedAlternative(): void
48    {
49        $cmd = new ProductionRule('cmd', [
50            new Production([new Terminal('DELETE'), new NonTerminal('orderby_opt')]),
51        ]);
52
53        self::assertSame($cmd->alternatives, (new GrammarAdaptation())->withStatementRules([], $cmd)['delete']->alternatives);
54    }
55
56    public function testStatementAlternativesSortsAnAlternativeByTheKeywordItLeadsWith(): void
57    {
58        $delete = new Production([new Terminal('DELETE'), new NonTerminal('nm')]);
59        $update = new Production([new Terminal('UPDATE'), new NonTerminal('nm')]);
60        $groups = (new GrammarAdaptation())->statementAlternatives(new ProductionRule('cmd', [$delete, $update]));
61
62        self::assertSame([$delete], $groups['delete']);
63        self::assertSame([$update], $groups['update']);
64    }
65
66    public function testStatementAlternativesSortsAnAlternativeThatBeginsWithAnOptionalWithClause(): void
67    {
68        $delete = new Production([new NonTerminal('with'), new Terminal('DELETE')]);
69        $insert = new Production([new NonTerminal('with'), new NonTerminal('insert_cmd')]);
70        $groups = (new GrammarAdaptation())->statementAlternatives(new ProductionRule('cmd', [$delete, $insert]));
71
72        self::assertSame([$delete], $groups['delete']);
73        self::assertSame([$insert], $groups['insert']);
74    }
75
76    public function testStatementAlternativesKeepsAlterAndDropOnlyWhenTheyActOnATable(): void
77    {
78        $alterTable = new Production([new Terminal('ALTER'), new Terminal('TABLE')]);
79        $dropTable = new Production([new Terminal('DROP'), new Terminal('TABLE')]);
80        $dropIndex = new Production([new Terminal('DROP'), new Terminal('INDEX')]);
81        $groups = (new GrammarAdaptation())->statementAlternatives(
82            new ProductionRule('cmd', [$alterTable, $dropTable, $dropIndex]),
83        );
84
85        self::assertSame([$alterTable], $groups['alter_table']);
86        self::assertSame([$dropTable], $groups['drop_table']);
87    }
88}
89