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

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\MySql\Generation\Rewrite\Column;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFaker\Generation\Token\ProductionOccurrence;
12use SqlFaker\Generation\Token\TerminalOccurrence;
13use SqlFaker\Generation\Token\TerminalSequence;
14use SqlFaker\MySql\Generation\Rewrite\Column\FieldLengthRule;
15
16#[CoversClass(FieldLengthRule::class)]
17#[UsesClass(ProductionOccurrence::class)]
18#[UsesClass(TerminalOccurrence::class)]
19#[UsesClass(TerminalSequence::class)]
20final class FieldLengthRuleTest extends TestCase
21{
22    #[DataProvider('providerPrecisions')]
23    public function testPrecisionRetainsBothNumericOccurrencesWithDependentDomains(string $type, string $domain): void
24    {
25        $terminals = [new TerminalOccurrence($type, 10, [0], ['type']), ...array_map(static fn (string $name, int $index): TerminalOccurrence => new TerminalOccurrence($name, 11 + $index, [0, 1], ['type', 'precision']), ['(', 'NUM', ',', 'NUM', ')'], [0, 1, 2, 3, 4])];
26        $input = new TerminalSequence($terminals, productions: [new ProductionOccurrence(0, null, 'type', 0), new ProductionOccurrence(1, 0, 'precision', 0)]);
27        $result = (new FieldLengthRule())->precision($input);
28        self::assertSame([$type, '(', $domain, ',', 'NUMERIC_SCALE_NUMBER', ')'], $result->names());
29        self::assertSame(array_column($input->terminals, 'id'), array_column($result->terminals, 'id'));
30        self::assertSame($input->original, $result->original);
31        self::assertSame($input->productions, $result->productions);
32        self::assertSame($result, (new FieldLengthRule())->rewrite($result));
33    }
34
35    /**
36     * @return list<array{string, string}>
37     */
38    public static function providerPrecisions(): array
39    {
40        return [['DECIMAL_SYM', 'DECIMAL_DIGITS_NUMBER'], ['NUMERIC_SYM', 'DECIMAL_DIGITS_NUMBER'], ['FIXED_SYM', 'DECIMAL_DIGITS_NUMBER'], ['FLOAT_SYM', 'FLOAT_DIGITS_NUMBER'], ['DOUBLE_SYM', 'FLOAT_DIGITS_NUMBER']];
41    }
42
43    #[DataProvider('providerTypes')]
44    public function testWidthRestrictsOnlyTheWidthOwnedByItsType(string $name, string $domain, string $context, ?string $wrapper): void
45    {
46        $type = new TerminalOccurrence($name, 10, [0], [$context]);
47        $rules = [$context, 'field_length'];
48        $open = new TerminalOccurrence('(', 11, [0, 1], $rules);
49        $number = new TerminalOccurrence('ULONGLONG_NUM', 12, [0, 1], $rules);
50        $close = new TerminalOccurrence(')', 13, [0, 1], $rules);
51        $ordinary = new TerminalOccurrence('LONG_NUM', 14);
52        $input = new TerminalSequence([$type, $open, $number, $close, $ordinary], [$type, $open, $number, $close, $ordinary], [], [new ProductionOccurrence(0, null, $context, 0), new ProductionOccurrence(1, 0, 'field_length', 1), ...($wrapper === null ? [] : [new ProductionOccurrence(2, 0, $wrapper, 0)])]);
53        $rule = new FieldLengthRule();
54        $result = $rule->width($input);
55        self::assertSame([$name, '(', $domain, ')', 'LONG_NUM'], $result->names());
56        self::assertSame($number->id, $result->terminals[2]->id);
57        self::assertSame($input->original, $result->original);
58        self::assertSame($input->productions, $result->productions);
59        self::assertSame($result, $rule->rewrite($result));
60    }
61
62    /**
63     * @return iterable<array{string, string, string, string|null}>
64     */
65    public static function providerTypes(): iterable
66    {
67        foreach (['INT_SYM', 'TINYINT_SYM', 'SMALLINT_SYM', 'MEDIUMINT_SYM', 'BIGINT_SYM', 'CHAR_SYM', 'NCHAR_SYM', 'NATIONAL_SYM', 'BINARY_SYM'] as $name) {
68            yield [$name, 'DISPLAY_WIDTH_NUMBER', 'type', null];
69        }
70        foreach (['DECIMAL_SYM', 'NUMERIC_SYM', 'FIXED_SYM'] as $name) {
71            yield [$name, 'DECIMAL_PRECISION_NUMBER', 'type', null];
72        }
73        foreach (['VARCHAR_SYM', 'NVARCHAR_SYM', 'VARBINARY_SYM'] as $name) {
74            yield [$name, 'VARCHAR_LENGTH_NUMBER', 'type', null];
75        }
76        yield ['BIT_SYM', 'BIT_WIDTH_NUMBER', 'type', null];
77        yield ['FLOAT_SYM', 'FLOAT_PRECISION_NUMBER', 'type', null];
78        yield ['BLOB_SYM', 'FIELD_LENGTH_NUMBER', 'type', null];
79        yield ['YEAR_SYM', 'ULONGLONG_NUM', 'type', null];
80        yield ['INT_SYM', 'ULONGLONG_NUM', 'ordinary', null];
81        yield ['NATIONAL_SYM', 'VARCHAR_LENGTH_NUMBER', 'type', 'nvarchar'];
82        yield ['CHAR_SYM', 'VARCHAR_LENGTH_NUMBER', 'type', 'varchar'];
83    }
84
85    public function testRewritePreservesEmptyFieldLengths(): void
86    {
87        $input = new TerminalSequence([], [], [], [new ProductionOccurrence(0, null, 'field_length', 0)]);
88        self::assertSame($input, (new FieldLengthRule())->rewrite($input));
89    }
90}
91