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