validator = new AbstractSchemaValidator(); } /** * @dataProvider provideSchemas * @param string $path Path to tables.json file */ public function testSchemaIsValid( string $path ): void { try { $this->validator->validate( $path ); // All good $this->addToAssertionCount( 1 ); } catch ( AbstractSchemaValidationError $e ) { $this->fail( "The abstract schema in $path is not valid: " . $e->getMessage() ); } } /** * @dataProvider provideSchemaChanges * @param string $path Path to the schema change JSON file */ public function testSchemaChangesAreValid( string $path ): void { try { $this->validator->validate( $path ); // All good $this->addToAssertionCount( 1 ); } catch ( AbstractSchemaValidationError $e ) { $this->fail( "The abstract schema change in $path is not valid: " . $e->getMessage() ); } } /** * @dataProvider provideSchemas */ public function testSchemasHaveAutoGeneratedFiles( string $jsonPath ) { $jsonName = basename( $jsonPath ); // Same as SchemaMaintenance::getSqlPathWithFileName() $nameReplacement = str_starts_with( $jsonName, 'tables' ) ? '-generated.sql' : '.sql'; $expectedSQLName = str_replace( '.json', $nameReplacement, $jsonName ); foreach ( static::getSchemaSQLDirs() as $platform => $dir ) { $expectedSQLPath = rtrim( $dir, '/' ) . '/' . $expectedSQLName; $realSQLPath = realpath( $expectedSQLPath ); $this->assertFileExists( $expectedSQLPath, "Schema file $realSQLPath for $platform not found. Generate it using generateSchemaSql.php." ); $actualSQL = file_get_contents( $expectedSQLPath ); $expectedSQL = ( new SchemaGenerator() )->generateSchema( $platform, $jsonPath ); $this->assertSQLSame( $expectedSQL, $actualSQL, "SQL in $realSQLPath for $platform does not appear to be autogenerated. Re-generate it " . "using generateSchemaSql.php." ); } } /** * @dataProvider provideSchemaChanges */ public function testSchemaChangesHaveAutoGeneratedFiles( string $jsonPath ) { $expectedSQLName = str_replace( '.json', '.sql', basename( $jsonPath ) ); $foundOne = false; foreach ( static::getSchemaChangesSQLDirs() as $platform => $dir ) { $expectedSQLPath = rtrim( $dir, '/' ) . '/' . $expectedSQLName; if ( !file_exists( $expectedSQLPath ) ) { // Schema changes don't necessarily have to exist for all platforms. One is enough. continue; } $foundOne = true; $actualSQL = file_get_contents( $expectedSQLPath ); $expectedSQL = ( new SchemaGenerator() )->generateSchemaChange( $platform, $jsonPath ); $realSQLPath = realpath( $expectedSQLPath ); $this->assertSQLSame( $expectedSQL, $actualSQL, "SQL in $realSQLPath for $platform does not appear to be autogenerated. Re-generate it " . "using generateSchemaChangeSql.php." ); } $realJSONPath = realpath( $jsonPath ); $this->assertTrue( $foundOne, "Did not find SQL files for any platforms for schema change $realJSONPath. Generate them using " . "generateSchemaChangeSql.php." ); } /** * Asserts that two SQL strings are identical, modulo formatting (whitespace) differences. We can't just compare * the raw output, otherwise changing the format would be massively painful due to test failures everywhere. */ private function assertSQLSame( string $expected, string $actual, string $message ): void { $formatter = new SqlFormatter( new NullHighlighter() ); $this->assertSame( $formatter->format( $expected ), $formatter->format( $actual ), $message ); } final public static function provideSchemas(): Generator { $schemaDir = rtrim( static::getSchemasDirectory(), '/' ) . '/'; foreach ( glob( $schemaDir . '*.json' ) as $schemaFile ) { yield basename( $schemaFile ) => [ $schemaFile ]; } } final public static function provideSchemaChanges(): Generator { $schemaChangesDir = rtrim( static::getSchemaChangesDirectory(), '/' ) . '/'; foreach ( glob( $schemaChangesDir . '*.json' ) as $schemaChange ) { yield basename( $schemaChange ) => [ $schemaChange ]; } } /** * Returns the path to a directory with JSON files for the schema. All JSON files in this directory are assumed * to be schema files and tested accordingly. */ abstract protected static function getSchemasDirectory(): string; /** * Returns the path to a directory with schema change JSON files. All JSON files in this directory are assumed * to be schema change files and tested accordingly. */ abstract protected static function getSchemaChangesDirectory(): string; /** * Returns a map of supported DB types to the path of a directory with the .sql files for that DB type's schema. */ abstract protected static function getSchemaSQLDirs(): array; /** * Returns a map of supported DB types to the path of a directory with the .sql schema change files for that DB * type. Uses the same directory as {@see self::getSchemaSQLDirs} by default. */ protected static function getSchemaChangesSQLDirs(): array { return static::getSchemaSQLDirs(); } }