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