packages/sql-faker/tests/Unit/MySql/Generation/Rewrite/Partition/ValueShapeTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\MySql\Generation\Rewrite\Partition;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFaker\Generation\Token\TerminalOccurrence;
12use SqlFaker\Generation\Token\TerminalSequence;
13use SqlFaker\MySql\Generation\Rewrite\Partition\ValueShape;
14
15#[CoversClass(ValueShape::class)]
16#[UsesClass(TerminalOccurrence::class)]
17#[UsesClass(TerminalSequence::class)]
18final class ValueShapeTest extends TestCase
19{
20    #[DataProvider('providerShapes')]
21    public function testResizePreservesExpressionsWhileMatchingTheRequiredWidth(string $input, bool $list, int $width, string $expected): void
22    {
23        $tokens = TerminalSequence::fromNames(explode(' ', $input))->terminals;
24        $result = (new ValueShape())->resize($tokens, $list, $width);
25        self::assertSame(explode(' ', $expected), array_column($result, 'name'));
26        $retained = array_values(array_filter($result, static fn (TerminalOccurrence $token): bool => $token->id !== PHP_INT_MIN));
27        self::assertSame(array_map(static fn (TerminalOccurrence $token): TerminalOccurrence => $tokens[$token->id], $retained), $retained);
28    }
29
30    /**
31     * @return list<array{string, bool, int, string}>
32     */
33    public static function providerShapes(): array
34    {
35        return [
36            ['MAX_VALUE_SYM', false, 1, 'MAX_VALUE_SYM'],
37            ['MAX_VALUE_SYM', false, 3, '( MAX_VALUE_SYM , MAX_VALUE_SYM , MAX_VALUE_SYM )'],
38            ['( NUM , NULL_SYM )', false, 1, '( NUM )'],
39            ['( NUM )', false, 2, '( NUM , NUM )'],
40            ['( NUM , NULL_SYM )', true, 1, '( NUM , NULL_SYM )'],
41            ['( ( NUM ) , ( NULL_SYM , NUM ) )', true, 1, '( NUM , NULL_SYM , NUM )'],
42            ['( ( NUM ) , ( NULL_SYM , NUM , NUM ) )', true, 2, '( ( NUM , NUM ) , ( NULL_SYM , NUM ) )'],
43            ['( NUM , NULL_SYM )', true, 2, '( ( NUM , NUM ) , ( NULL_SYM , NUM ) )'],
44            ['( + ( NUM + NUM ) , NUM )', true, 2, '( ( + ( NUM + NUM ) , NUM ) , ( NUM , NUM ) )'],
45        ];
46    }
47
48    public function testSplitAndJoinHandleEmptyListsAndNestedCommas(): void
49    {
50        $shape = new ValueShape();
51        self::assertSame([], $shape->split([]));
52        self::assertSame([], $shape->join([], false));
53        $tokens = TerminalSequence::fromNames(['NUM', ',', '(', 'NUM', ',', 'NUM', ')'])->terminals;
54        self::assertSame([[$tokens[0]], array_slice($tokens, 2)], $shape->split($tokens));
55    }
56
57    public function testRowsDistinguishesScalarListsAndTuples(): void
58    {
59        $tokens = TerminalSequence::fromNames(['(', 'NUM', ',', 'NULL_SYM', ')'])->terminals;
60        self::assertSame([[[$tokens[1]], [$tokens[3]]]], (new ValueShape())->rows($tokens, false));
61        self::assertSame([[[$tokens[1]]], [[$tokens[3]]]], (new ValueShape())->rows($tokens, true));
62    }
63
64    public function testJoinUsesNewPunctuationAndRetainsValueIdentity(): void
65    {
66        $value = new TerminalOccurrence('NUM', 7);
67        $joined = (new ValueShape())->join([[$value], [$value]], true);
68        self::assertSame(['(', 'NUM', ',', 'NUM', ')'], array_column($joined, 'name'));
69        self::assertSame($value, $joined[1]);
70        self::assertSame($value, $joined[3]);
71    }
72
73    public function testTokenReservesAnIdentityOutsideTheDerivation(): void
74    {
75        $token = (new ValueShape())->token(',');
76        self::assertSame(',', $token->name);
77        self::assertSame(PHP_INT_MIN, $token->id);
78    }
79}
80