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