packages/sql-fixture/tests/Integration/MySql/MySqlReleaseRoundTripTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Integration\MySql;
6
7use Container\Endpoint;
8use Container\MySql56Container;
9use Container\MySql57Container;
10use Container\MySql80Container;
11use Container\MySql81Container;
12use Container\MySql82Container;
13use Container\MySql83Container;
14use Container\MySql84Container;
15use Container\MySql90Container;
16use Container\MySql91Container;
17use Container\MySqlContainer;
18use Faker\Factory;
19use PDO;
20use PHPUnit\Framework\Attributes\CoversNothing;
21use PHPUnit\Framework\Attributes\DataProvider;
22use PHPUnit\Framework\Attributes\Large;
23use PHPUnit\Framework\TestCase;
24use SqlFixture\Provider\DatabaseFixtureProvider;
25use SqlFixture\Version\ServerVersion;
26use Testcontainers\Testcontainers;
27
28#[CoversNothing]
29#[Large]
30final class MySqlReleaseRoundTripTest extends TestCase
31{
32    /**
33     * @return iterable<string, array{class-string<MySqlContainer>, int}>
34     */
35    public static function providerReleases(): iterable
36    {
37        $selected = getenv('SQL_FIXTURE_MYSQL_VERSION');
38        $containers = [
39            MySql56Container::class,
40            MySql57Container::class,
41            MySql80Container::class,
42            MySql81Container::class,
43            MySql82Container::class,
44            MySql83Container::class,
45            MySql84Container::class,
46            MySql90Container::class,
47            MySql91Container::class,
48        ];
49        foreach ($containers as $container) {
50            $tag = $container::getGrammarVersion();
51            if ($selected !== false && $selected !== '' && $tag !== 'mysql-' . $selected) {
52                continue;
53            }
54            foreach ([1, 2, 3] as $seed) {
55                yield $tag . ' seed ' . $seed => [$container, $seed];
56            }
57        }
58    }
59
60    /**
61     * @param class-string<MySqlContainer> $container
62     */
63    #[DataProvider('providerReleases')]
64    public function testRowGeneratedFromTheReportedSchemaIsAccepted(string $container, int $seed): void
65    {
66        $endpoint = Testcontainers::run($container)->getData(Endpoint::class);
67        $pdo = new PDO($endpoint->dsn(), $endpoint->username, $endpoint->password, [
68            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
69            PDO::ATTR_EMULATE_PREPARES => false,
70        ]);
71        $faker = Factory::create();
72        $faker->seed($seed);
73        $provider = new DatabaseFixtureProvider($faker, $pdo);
74        self::assertSame($container::getGrammarVersion(), $provider->getVersion());
75
76        $table = 'fixture_' . bin2hex(random_bytes(4));
77        $json = ServerVersion::resolve('mysql', $provider->getVersion())->isAtLeast('5.7.8') ? 'col_json JSON NOT NULL,' : '';
78        $pdo->exec(<<<SQL
79            CREATE TABLE {$table} (
80                id INT PRIMARY KEY AUTO_INCREMENT,
81                col_tinyint TINYINT NOT NULL,
82                col_tinyint_unsigned TINYINT UNSIGNED NOT NULL,
83                col_smallint SMALLINT NOT NULL,
84                col_mediumint MEDIUMINT NOT NULL,
85                col_int INT NOT NULL,
86                col_bigint BIGINT NOT NULL,
87                col_bigint_unsigned BIGINT UNSIGNED NOT NULL,
88                col_float FLOAT NOT NULL,
89                col_double DOUBLE NOT NULL,
90                col_decimal DECIMAL(10, 2) NOT NULL,
91                col_bit BIT(8) NOT NULL,
92                col_bool BOOLEAN NOT NULL,
93                col_char CHAR(10) NOT NULL,
94                col_varchar VARCHAR(255) NOT NULL,
95                col_binary BINARY(8) NOT NULL,
96                col_varbinary VARBINARY(32) NOT NULL,
97                col_tinytext TINYTEXT NOT NULL,
98                col_text TEXT NOT NULL,
99                col_mediumtext MEDIUMTEXT NOT NULL,
100                col_longtext LONGTEXT NOT NULL,
101                col_tinyblob TINYBLOB NOT NULL,
102                col_blob BLOB NOT NULL,
103                col_enum ENUM('a', 'b', 'c') NOT NULL,
104                col_set SET('x', 'y', 'z') NOT NULL,
105                col_date DATE NOT NULL,
106                col_time TIME NOT NULL,
107                col_datetime DATETIME NOT NULL,
108                col_timestamp TIMESTAMP NULL,
109                col_year YEAR NOT NULL,
110                col_nullable VARCHAR(20) NULL DEFAULT 'none',
111                {$json}
112                col_point POINT NOT NULL,
113                col_polygon POLYGON NOT NULL
114            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
115            SQL);
116        try {
117            $row = $provider->fixture($table);
118            $expressions = ['col_bit' => 'CAST(? AS UNSIGNED)', 'col_point' => 'ST_GeomFromText(?)', 'col_polygon' => 'ST_GeomFromText(?)'];
119            $statement = $pdo->prepare(sprintf(
120                'INSERT INTO %s (%s) VALUES (%s)',
121                $table,
122                implode(', ', array_keys($row)),
123                implode(', ', array_map(static fn (string $column): string => $expressions[$column] ?? '?', array_keys($row))),
124            ));
125            self::assertTrue($statement->execute(array_values(array_replace($row, array_map('intval', array_filter($row, 'is_bool'))))));
126            $count = $pdo->query(sprintf('SELECT COUNT(*) FROM %s', $table));
127            self::assertNotFalse($count);
128            self::assertSame(1, (int) $count->fetchColumn());
129        } finally {
130            $pdo->exec(sprintf('DROP TABLE IF EXISTS %s', $table));
131        }
132    }
133}
134