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