packages/sql-faker/tests/Unit/MySql/Generation/Lexeme/ReplicationTablePatternLexemeGeneratorTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\MySql\Generation\Lexeme;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFaker\Generation\Lexeme\LexemeInput;
12use SqlFaker\Generation\Lexeme\ResolvedOutput;
13use SqlFaker\Generation\Token\TerminalSequence;
14use SqlFaker\MySql\Generation\Lexeme\ReplicationTablePatternLexemeGenerator;
15
16#[CoversClass(ReplicationTablePatternLexemeGenerator::class)]
17#[UsesClass(\SqlFaker\Generation\Lexeme\Lexeme::class)]
18#[UsesClass(\SqlFaker\Generation\Lexeme\LexemeCandidates::class)]
19#[UsesClass(\SqlFaker\Generation\Lexeme\LexemeSequence::class)]
20#[UsesClass(LexemeInput::class)]
21#[UsesClass(ResolvedOutput::class)]
22#[UsesClass(\SqlFaker\Generation\Lexeme\SpacingConstraint::class)]
23#[UsesClass(\SqlFaker\Generation\Token\TerminalOccurrence::class)]
24#[UsesClass(TerminalSequence::class)]
25#[UsesClass(\SqlFaker\MySql\Generation\Value\QuotedDomain::class)]
26final class ReplicationTablePatternLexemeGeneratorTest extends TestCase
27{
28 public function testGenerateKeepsDefaultPatternsAndOrdinaryStringsSeparate(): void
29 {
30 $generator = new ReplicationTablePatternLexemeGenerator();
31 $result = $generator->generate(new LexemeInput(TerminalSequence::fromNames(['REPLICATION_TABLE_PATTERN']), 0, new ResolvedOutput()));
32 self::assertNotNull($result);
33 self::assertSame(["'db.table'", "'db.%'", "'%.table'"], array_map(static fn ($candidate): string => $candidate->lexemes[0]->text, [...$result->sequences()]));
34 self::assertNull($generator->generate(new LexemeInput(TerminalSequence::fromNames(['TEXT_STRING']), 0, new ResolvedOutput())));
35 }
36
37 #[DataProvider('providerPatterns')]
38 public function testGenerateValidatesDecodedPatternsWithoutChangingTheirSpelling(string $spelling, bool $valid): void
39 {
40 $generator = new ReplicationTablePatternLexemeGenerator();
41 $input = new LexemeInput(TerminalSequence::fromNames(['REPLICATION_TABLE_PATTERN']), 0, new ResolvedOutput(), $spelling);
42 $result = $generator->generate($input);
43 self::assertNotNull($result);
44 $candidates = [...$result->sequences()];
45 self::assertSame($valid ? [$spelling] : [], array_map(static fn ($candidate): string => $candidate->lexemes[0]->text, $candidates));
46 }
47
48 #[DataProvider('providerPatterns')]
49 public function testAcceptsValidatesTheDecodedParserValue(string $spelling, bool $valid): void
50 {
51 self::assertSame($valid, (new ReplicationTablePatternLexemeGenerator())->accepts($spelling));
52 }
53
54 public function testGeneratePreservesExplicitCandidateProvenance(): void
55 {
56 $input = new LexemeInput(TerminalSequence::fromNames(['REPLICATION_TABLE_PATTERN']), 0, new ResolvedOutput(), "'db.%'");
57 $result = (new ReplicationTablePatternLexemeGenerator())->generate($input);
58 self::assertNotNull($result);
59 $candidates = [...$result->sequences()];
60 self::assertCount(1, $candidates);
61 self::assertSame($input->terminal(), $candidates[0]->lexemes[0]->origin);
62 self::assertSame('sql/sql_yacc.yy:filter_wild_db_table_string', $candidates[0]->lexemes[0]->definition);
63 }
64
65 /**
66 * @return iterable<array{string, bool}>
67 */
68 public static function providerPatterns(): iterable
69 {
70 yield ["'db.table'", true];
71 yield ["'.'", true];
72 yield ["'%.%'", true];
73 yield ["'db.it''s'", true];
74 yield ["'db.\\_%'", true];
75 yield ["'db\\.table'", true];
76 yield ["'db.\\\\n'", true];
77 yield ["'db.\\0\\n'", true];
78 yield ["'db.\\n'", false];
79 yield ["'db.\n'", false];
80 yield ["'db.\\\n'", false];
81 yield ["'db\\0.table'", false];
82 yield ["'db.table\0'", false];
83 yield ["'text'", false];
84 yield ["''", false];
85 yield ['db.table', false];
86 yield ["'db.table", false];
87 yield ["'db.table' trailing", false];
88 yield ["'db.t'able'", false];
89 }
90}
91