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

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\PostgreSql\Generation\Rewrite;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFaker\Generation\Derivation\DerivationTrace;
12use SqlFaker\Generation\Token\ProductionOccurrence;
13use SqlFaker\Generation\Token\TerminalOccurrence;
14use SqlFaker\Generation\Token\TerminalSequence;
15use SqlFaker\Grammar\Model\NonTerminal;
16use SqlFaker\Grammar\Model\Production;
17use SqlFaker\Grammar\Model\Terminal;
18use SqlFaker\PostgreSql\Generation\Rewrite\GeneratedColumnRule;
19
20#[CoversClass(GeneratedColumnRule::class)]
21#[UsesClass(DerivationTrace::class)]
22#[UsesClass(TerminalSequence::class)]
23#[UsesClass(TerminalOccurrence::class)]
24#[UsesClass(ProductionOccurrence::class)]
25#[UsesClass(NonTerminal::class)]
26#[UsesClass(Production::class)]
27#[UsesClass(Terminal::class)]
28final class GeneratedColumnRuleTest extends TestCase
29{
30    /**
31     * @param list<string> $when
32     * @param list<string> $expected
33     */
34    #[DataProvider('providerGenerationModes')]
35    public function testRewriteRequiresAlwaysForExpressionsAndPreservesIdentityModes(array $when, string $valueRule, array $expected): void
36    {
37        $trace = new DerivationTrace('ColConstraintElem');
38        $trace->expand(0, new Production([new Terminal('GENERATED'), new NonTerminal('generated_when'), new Terminal('AS'), new NonTerminal($valueRule)]), 0);
39        $trace->expand(1, new Production(array_map(static fn (string $name): Terminal => new Terminal($name), $when)), 0);
40        $trace->expand(2 + count($when), new Production([new Terminal('VALUE')]), 0);
41        $input = $trace->terminals();
42        $rule = new GeneratedColumnRule();
43        $result = $rule->rewrite($input);
44        self::assertSame($expected, $result->names());
45        self::assertSame($input->terminals[count($input->terminals) - 1], $result->terminals[count($result->terminals) - 1]);
46        self::assertSame($input->original, $result->original);
47        self::assertCount(count($result->terminals), array_unique(array_map(static fn ($terminal): int => $terminal->id, $result->terminals)));
48        self::assertSame($input->productions, $result->productions);
49        self::assertSame($result, $rule->rewrite($result));
50    }
51
52    /**
53     * @return iterable<string, array{list<string>, string, list<string>}>
54     */
55    public static function providerGenerationModes(): iterable
56    {
57        yield 'expression by default' => [['BY', 'DEFAULT'], 'a_expr', ['GENERATED', 'ALWAYS', 'AS', 'VALUE']];
58        yield 'expression always' => [['ALWAYS'], 'a_expr', ['GENERATED', 'ALWAYS', 'AS', 'VALUE']];
59        yield 'identity by default' => [['BY', 'DEFAULT'], 'OptParenthesizedSeqOptList', ['GENERATED', 'BY', 'DEFAULT', 'AS', 'VALUE']];
60        yield 'identity always' => [['ALWAYS'], 'OptParenthesizedSeqOptList', ['GENERATED', 'ALWAYS', 'AS', 'VALUE']];
61        yield 'removed timing' => [[], 'a_expr', ['GENERATED', 'AS', 'VALUE']];
62    }
63
64    public function testRewritePreservesOtherColumnConstraints(): void
65    {
66        $trace = new DerivationTrace('ColConstraintElem');
67        $trace->expand(0, new Production([new Terminal('CHECK'), new NonTerminal('a_expr')]), 0);
68        $trace->expand(1, new Production([new Terminal('VALUE')]), 0);
69        $input = $trace->terminals();
70        self::assertSame($input, (new GeneratedColumnRule())->rewrite($input));
71    }
72}
73