packages/sql-faker/tests/Unit/MySql/Generation/Lexeme/PrecisionLexemeGeneratorTest.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\Lexeme;
12use SqlFaker\Generation\Lexeme\LexemeInput;
13use SqlFaker\Generation\Lexeme\OutputPart;
14use SqlFaker\Generation\Lexeme\ResolvedOutput;
15use SqlFaker\Generation\Token\TerminalOccurrence;
16use SqlFaker\Generation\Token\TerminalSequence;
17use SqlFaker\MySql\Generation\Lexeme\PrecisionLexemeGenerator;
18
19#[CoversClass(PrecisionLexemeGenerator::class)]
20#[UsesClass(Lexeme::class)]
21#[UsesClass(LexemeInput::class)]
22#[UsesClass(OutputPart::class)]
23#[UsesClass(ResolvedOutput::class)]
24#[UsesClass(TerminalOccurrence::class)]
25#[UsesClass(TerminalSequence::class)]
26#[UsesClass(\SqlFaker\Generation\Lexeme\LexemeCandidates::class)]
27#[UsesClass(\SqlFaker\Generation\Lexeme\LexemeSequence::class)]
28#[UsesClass(\SqlFaker\Generation\Lexeme\SpacingConstraint::class)]
29#[UsesClass(\SqlFaker\Generation\Candidate\IntegerLexemeGenerator::class)]
30#[UsesClass(\SqlFaker\Generation\Value\IntegerDomain::class)]
31final class PrecisionLexemeGeneratorTest extends TestCase
32{
33    #[DataProvider('providerWidths')]
34    public function testGenerateEnforcesPrecisionAndScaleTogether(string $name, int $scale, string $requested, bool $valid): void
35    {
36        $origin = new TerminalOccurrence($name, 10, [1], ['precision']);
37        $scaleOrigin = new TerminalOccurrence('NUMERIC_SCALE_NUMBER', 11, [1], ['precision']);
38        $right = new ResolvedOutput([new OutputPart(new Lexeme((string) $scale, 'number', $scaleOrigin, 'source'), '', 'candidate')]);
39        $input = new LexemeInput(new TerminalSequence([$origin]), 0, $right, $requested);
40        $result = (new PrecisionLexemeGenerator())->generate($input);
41        self::assertNotNull($result);
42        $candidates = [...$result->sequences()];
43        self::assertSame($valid ? [$requested] : [], array_map(static fn ($candidate): string => $candidate->lexemes[0]->text, $candidates));
44        self::assertSame($valid ? [$origin] : [], array_map(static fn ($candidate) => $candidate->lexemes[0]->origin, $candidates));
45    }
46
47    /**
48     * @return list<array{string, int, string, bool}>
49     */
50    public static function providerWidths(): array
51    {
52        return [['DECIMAL_DIGITS_NUMBER', 0, '0', true], ['DECIMAL_DIGITS_NUMBER', 1, '0', false], ['DECIMAL_DIGITS_NUMBER', 2, '1', false], ['DECIMAL_DIGITS_NUMBER', 30, '030', true], ['DECIMAL_DIGITS_NUMBER', 30, '65', true], ['DECIMAL_DIGITS_NUMBER', 30, '66', false], ['FLOAT_DIGITS_NUMBER', 0, '0', true], ['FLOAT_DIGITS_NUMBER', 2, '1', false], ['FLOAT_DIGITS_NUMBER', 30, '255', true], ['FLOAT_DIGITS_NUMBER', 0, '256', false], ['FLOAT_DIGITS_NUMBER', 0, '1.0', false]];
53    }
54
55    public function testGenerateUsesOnlyTheScaleInTheSameOccurrence(): void
56    {
57        $origin = new TerminalOccurrence('DECIMAL_DIGITS_NUMBER', 10, [1], ['precision']);
58        $foreign = new TerminalOccurrence('NUMERIC_SCALE_NUMBER', 11, [2], ['precision']);
59        $scale = new TerminalOccurrence('NUMERIC_SCALE_NUMBER', 12, [1], ['precision']);
60        $right = new ResolvedOutput([new OutputPart(new Lexeme('30', 'number', $foreign, 'source'), '', 'a'), new OutputPart(new Lexeme('2', 'number', $scale, 'source'), '', 'b')]);
61        $result = (new PrecisionLexemeGenerator())->generate(new LexemeInput(new TerminalSequence([$origin]), 0, $right));
62        self::assertNotNull($result);
63        self::assertSame(['2', '65'], array_map(static fn ($candidate): string => $candidate->lexemes[0]->text, [...$result->sequences()]));
64        $missing = (new PrecisionLexemeGenerator())->generate(new LexemeInput(new TerminalSequence([$origin]), 0, new ResolvedOutput()));
65        self::assertNotNull($missing);
66        self::assertSame([], [...$missing->sequences()]);
67        self::assertNull((new PrecisionLexemeGenerator())->generate(new LexemeInput(TerminalSequence::fromNames(['NUM']), 0, $right)));
68    }
69}
70