packages/sql-faker/tests/Unit/PostgreSql/Generation/Rewrite/ConstraintAttributesRuleTest.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\Token\ProductionOccurrence;
12use SqlFaker\Generation\Token\TerminalOccurrence;
13use SqlFaker\Generation\Token\TerminalSequence;
14use SqlFaker\PostgreSql\Generation\Rewrite\ConstraintAttributesRule;
15
16#[CoversClass(ConstraintAttributesRule::class)]
17#[UsesClass(ProductionOccurrence::class)]
18#[UsesClass(TerminalOccurrence::class)]
19#[UsesClass(TerminalSequence::class)]
20final class ConstraintAttributesRuleTest extends TestCase
21{
22    /**
23     * @param list<string> $expected
24     */
25    #[DataProvider('providerAttributes')]
26    public function testRewritePreservesCompatibleOptionsAndDropsOnlyConflicts(TerminalSequence $input, array $expected): void
27    {
28        $rule = new ConstraintAttributesRule();
29        $result = $rule->rewrite($input);
30        self::assertSame($expected, $result->names());
31        self::assertSame($input->original, $result->original);
32        self::assertCount(count($result->terminals), array_unique(array_map(static fn ($terminal): int => $terminal->id, $result->terminals)));
33        self::assertSame($result->terminals, $rule->rewrite($result)->terminals);
34    }
35
36    /**
37     * @return iterable<string, array{TerminalSequence, list<string>}>
38     */
39    public static function providerAttributes(): iterable
40    {
41        $cases = [
42            'empty' => [[], []],
43            'compatible and redundant' => [['DEFERRABLE', 'DEFERRABLE', 'INITIALLY DEFERRED', 'NOT VALID', 'NO INHERIT'], ['DEFERRABLE', 'DEFERRABLE', 'INITIALLY', 'DEFERRED', 'NOT', 'VALID', 'NO', 'INHERIT']],
44            'deferrability conflict' => [['NOT DEFERRABLE', 'DEFERRABLE'], ['NOT', 'DEFERRABLE']],
45            'reverse deferrability conflict' => [['DEFERRABLE', 'NOT DEFERRABLE'], ['DEFERRABLE']],
46            'initial state conflict' => [['INITIALLY IMMEDIATE', 'INITIALLY DEFERRED'], ['INITIALLY', 'IMMEDIATE']],
47            'reverse initial state conflict' => [['INITIALLY DEFERRED', 'INITIALLY IMMEDIATE'], ['INITIALLY', 'DEFERRED']],
48            'deferred requires deferrable' => [['NOT DEFERRABLE', 'INITIALLY DEFERRED'], ['NOT', 'DEFERRABLE']],
49            'reverse deferred requires deferrable' => [['INITIALLY DEFERRED', 'NOT DEFERRABLE'], ['INITIALLY', 'DEFERRED']],
50        ];
51        foreach ($cases as $name => [$options, $expected]) {
52            $terminals = [];
53            $productions = [new ProductionOccurrence(0, null, 'ConstraintAttributeSpec', 0)];
54            foreach ($options as $index => $option) {
55                $id = 10 + $index;
56                $productions[] = new ProductionOccurrence($id, 0, 'ConstraintAttributeElem', 0);
57                foreach (explode(' ', $option) as $word) {
58                    $terminals[] = new TerminalOccurrence($word, 100 + count($terminals), [0, $id], ['ConstraintAttributeSpec', 'ConstraintAttributeElem']);
59                }
60            }
61            yield $name => [new TerminalSequence($terminals, $terminals, [], $productions), $expected];
62        }
63    }
64
65    public function testRewriteKeepsIndependentConstraintScopesSeparate(): void
66    {
67        $a = new TerminalOccurrence('NOT', 10, [0, 1], ['ConstraintAttributeSpec', 'ConstraintAttributeElem']);
68        $b = new TerminalOccurrence('DEFERRABLE', 11, [0, 1], ['ConstraintAttributeSpec', 'ConstraintAttributeElem']);
69        $c = new TerminalOccurrence('DEFERRABLE', 12, [2, 3], ['ConstraintAttributeSpec', 'ConstraintAttributeElem']);
70        $outside = new TerminalOccurrence('DEFERRABLE', 13, [4], ['ConstraintAttributeElem']);
71        $input = new TerminalSequence([$a, $b, $c, $outside], [], [], [new ProductionOccurrence(1, 0, 'ConstraintAttributeElem', 0), new ProductionOccurrence(3, 2, 'ConstraintAttributeElem', 1), new ProductionOccurrence(4, null, 'ConstraintAttributeElem', 1), new ProductionOccurrence(5, null, 'ConstraintAttributeElem', 0)]);
72        self::assertSame($input, (new ConstraintAttributesRule())->rewrite($input));
73    }
74}
75