packages/sql-fixture/tests/Unit/Hydrator/Reflection/ValueConversionTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Hydrator\Reflection;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFixture\Hydrator\Reflection\ConversionTarget;
12use SqlFixture\Hydrator\Reflection\ValueConversion;
13
14#[CoversClass(ValueConversion::class)]
15#[UsesClass(ConversionTarget::class)]
16final class ValueConversionTest extends TestCase
17{
18    /**
19     * @param list<int|string>|int|float|bool|string|null $value
20     * @param list<int|string>|int|float|bool|string|null $expected
21     */
22    #[DataProvider('providerConversions')]
23    public function testCastValuePreservesOrConvertsTheDatabaseValue(
24        array|int|float|bool|string|null $value,
25        ?ConversionTarget $type,
26        array|int|float|bool|string|null $expected,
27    ): void {
28        self::assertSame($expected, (new ValueConversion())->castValue($value, $type));
29    }
30
31    /**
32     * @return iterable<string, array{list<int|string>|int|float|bool|string|null, ?ConversionTarget, list<int|string>|int|float|bool|string|null}>
33     */
34    public static function providerConversions(): iterable
35    {
36        yield 'numeric string to integer' => ['42', ConversionTarget::Integer, 42];
37        yield 'numeric float to integer' => [42.9, ConversionTarget::Integer, 42];
38        yield 'nonnumeric integer input is preserved' => ['pending', ConversionTarget::Integer, 'pending'];
39        yield 'numeric string to float' => ['12.5', ConversionTarget::Float, 12.5];
40        yield 'integer to float' => [12, ConversionTarget::Float, 12.0];
41        yield 'nonnumeric float input is preserved' => ['pending', ConversionTarget::Float, 'pending'];
42        yield 'integer to string' => [42, ConversionTarget::String, '42'];
43        yield 'boolean to string' => [false, ConversionTarget::String, ''];
44        yield 'nonscalar string input is preserved' => [[1, 2], ConversionTarget::String, [1, 2]];
45        yield 'zero to boolean' => [0, ConversionTarget::Boolean, false];
46        yield 'one to boolean' => [1, ConversionTarget::Boolean, true];
47        yield 'null stays null' => [null, ConversionTarget::Boolean, null];
48        yield 'JSON array' => ['[1,2]', ConversionTarget::Array, [1, 2]];
49        yield 'JSON null becomes an element' => ['null', ConversionTarget::Array, ['null']];
50        yield 'plain text becomes an element' => ['plain', ConversionTarget::Array, ['plain']];
51        yield 'scalar becomes an array' => [42, ConversionTarget::Array, [42]];
52        yield 'existing array is preserved' => [[1, 2], ConversionTarget::Array, [1, 2]];
53        yield 'unconverted type preserves numeric strings' => ['42', null, '42'];
54    }
55}
56