packages/sql-faker/tests/Unit/Grammar/Resource/SqlVersionTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Grammar\Resource;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use RuntimeException;
12use SqlFaker\Grammar\Resource\SqlVersion;
13use SqlFaker\Grammar\Resource\SqlVersionRegistry;
14
15#[CoversClass(SqlVersion::class)]
16#[UsesClass(SqlVersionRegistry::class)]
17final class SqlVersionTest extends TestCase
18{
19    public function testResolveAnswersTheDefaultReleaseWithItsGrammar(): void
20    {
21        $version = SqlVersion::resolve('mysql');
22
23        self::assertSame('mysql', $version->dialect);
24        self::assertSame('mysql-8.4.7', $version->name);
25        self::assertTrue(is_file($version->astPath));
26    }
27
28    public function testAllEnumeratesEveryRegisteredRelease(): void
29    {
30        $versions = SqlVersion::all();
31
32        self::assertCount(12, $versions);
33        self::assertSame(
34            ['mysql-5.6.51', 'mysql-5.7.44', 'mysql-8.0.44'],
35            array_slice(array_map(static fn (SqlVersion $version): string => $version->name, $versions), 0, 3),
36        );
37        array_walk($versions, static function (SqlVersion $version): void {
38            self::assertTrue(is_file($version->astPath));
39        });
40    }
41
42    #[DataProvider('providerOfficialGrammarSource')]
43    public function testAllEnumeratesReleasesWhoseGrammarsRecordTheirOfficialSource(string $name, string $source): void
44    {
45        $versions = array_values(array_filter(SqlVersion::all(), static fn (SqlVersion $version): bool => $version->name === $name));
46        self::assertCount(1, $versions);
47        $contents = (string) file_get_contents($versions[0]->astPath);
48        $grammar = require $versions[0]->astPath;
49
50        self::assertStringContainsString(" * Source: {$source}\n", $contents);
51        self::assertStringContainsString(" * Version: {$name}\n", $contents);
52        self::assertIsArray($grammar);
53        self::assertMatchesRegularExpression('/^[0-9a-f]{64}$/', (string) array_key_first($grammar));
54    }
55
56    /**
57     * @return iterable<string, array{string, string}>
58     */
59    public static function providerOfficialGrammarSource(): iterable
60    {
61        foreach (['5.6.51', '5.7.44', '8.0.44', '8.1.0', '8.2.0', '8.3.0', '8.4.7', '9.0.1', '9.1.0'] as $release) {
62            yield "mysql-{$release}" => ["mysql-{$release}", "https://raw.githubusercontent.com/mysql/mysql-server/refs/tags/mysql-{$release}/sql/sql_yacc.yy"];
63        }
64        yield 'pg-16.6' => ['pg-16.6', 'https://raw.githubusercontent.com/postgres/postgres/refs/tags/REL_16_6/src/backend/parser/gram.y'];
65        yield 'pg-17.2' => ['pg-17.2', 'https://raw.githubusercontent.com/postgres/postgres/refs/tags/REL_17_2/src/backend/parser/gram.y'];
66        yield 'sqlite-3.47.2' => ['sqlite-3.47.2', 'https://raw.githubusercontent.com/sqlite/sqlite/refs/tags/version-3.47.2/src/parse.y'];
67    }
68
69    public function testNamesEnumeratesTheReleasesOfOneDialect(): void
70    {
71        self::assertSame([
72            'mysql-5.6.51',
73            'mysql-5.7.44',
74            'mysql-8.0.44',
75            'mysql-8.1.0',
76            'mysql-8.2.0',
77            'mysql-8.3.0',
78            'mysql-8.4.7',
79            'mysql-9.0.1',
80            'mysql-9.1.0',
81        ], SqlVersion::names('mysql'));
82    }
83
84    public function testNamesRejectsADialectThePackageDoesNotShip(): void
85    {
86        $this->expectException(RuntimeException::class);
87        $this->expectExceptionMessage('Unknown SQL dialect: unknown');
88
89        SqlVersion::names('unknown');
90    }
91
92    public function testResolveRejectsAReleaseThePackageDoesNotShip(): void
93    {
94        $this->expectException(RuntimeException::class);
95        $this->expectExceptionMessage('Unsupported mysql version');
96
97        SqlVersion::resolve('mysql', 'mysql-999.0.0');
98    }
99
100    public function testResolveRejectsADialectThePackageDoesNotShip(): void
101    {
102        $this->expectException(RuntimeException::class);
103        $this->expectExceptionMessage('Unknown SQL dialect');
104
105        SqlVersion::resolve('oracle');
106    }
107
108}
109