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

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\MySql\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\MySql\Generation\Rewrite\TransactionCompletionRule;
19
20#[CoversClass(TransactionCompletionRule::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 TransactionCompletionRuleTest extends TestCase
29{
30    /**
31     * @param list<string> $chain
32     * @param list<string> $release
33     * @param list<string> $expected
34     */
35    #[DataProvider('providerCompletions')]
36    public function testRewritePreservesValidCompletionsAndSeparatesChainFromRelease(string $root, array $chain, array $release, array $expected): void
37    {
38        $trace = new DerivationTrace($root);
39        $trace->expand(0, new Production([new NonTerminal('opt_chain'), new NonTerminal('opt_release')]), 0);
40        $trace->expand(0, new Production(array_map(static fn (string $name): Terminal => new Terminal($name), $chain)), 0);
41        $trace->expand(count($chain), new Production(array_map(static fn (string $name): Terminal => new Terminal($name), $release)), 0);
42        $input = $trace->terminals();
43        $rule = new TransactionCompletionRule();
44        $result = $rule->rewrite($input);
45        self::assertSame($expected, $result->names());
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{string, list<string>, list<string>, list<string>}>
54     */
55    public static function providerCompletions(): iterable
56    {
57        foreach (['commit', 'rollback'] as $root) {
58            yield $root . ' conflicting' => [$root, ['AND_SYM', 'CHAIN_SYM'], ['RELEASE_SYM'], ['AND_SYM', 'CHAIN_SYM', 'NO_SYM', 'RELEASE_SYM']];
59            yield $root . ' no chain' => [$root, ['AND_SYM', 'NO_SYM', 'CHAIN_SYM'], ['RELEASE_SYM'], ['AND_SYM', 'NO_SYM', 'CHAIN_SYM', 'RELEASE_SYM']];
60            yield $root . ' no release' => [$root, ['AND_SYM', 'CHAIN_SYM'], ['NO_SYM', 'RELEASE_SYM'], ['AND_SYM', 'CHAIN_SYM', 'NO_SYM', 'RELEASE_SYM']];
61            yield $root . ' default chain' => [$root, [], ['RELEASE_SYM'], ['RELEASE_SYM']];
62            yield $root . ' default release' => [$root, ['AND_SYM', 'CHAIN_SYM'], [], ['AND_SYM', 'CHAIN_SYM']];
63        }
64        yield 'unrelated scope' => ['other', ['AND_SYM', 'CHAIN_SYM'], ['RELEASE_SYM'], ['AND_SYM', 'CHAIN_SYM', 'RELEASE_SYM']];
65    }
66
67    public function testRewritePreservesRollbackToSavepoint(): void
68    {
69        $trace = new DerivationTrace('rollback');
70        $trace->expand(0, new Production([new Terminal('ROLLBACK_SYM'), new Terminal('TO_SYM'), new Terminal('IDENT')]), 1);
71        $input = $trace->terminals();
72        self::assertSame($input, (new TransactionCompletionRule())->rewrite($input));
73    }
74}
75