Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Database\Schema\Comparison;
|
||||
|
||||
use App\Framework\Database\ConnectionInterface;
|
||||
use App\Framework\Database\ResultInterface;
|
||||
use App\Framework\Database\Schema\Comparison\SchemaComparator;
|
||||
use App\Framework\Database\Schema\Comparison\SchemaDifference;
|
||||
use Mockery;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->sourceConnection = Mockery::mock(ConnectionInterface::class);
|
||||
$this->targetConnection = Mockery::mock(ConnectionInterface::class);
|
||||
$this->comparator = new SchemaComparator($this->sourceConnection, $this->targetConnection);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Mockery::close();
|
||||
});
|
||||
|
||||
test('compares schemas with different tables', function () {
|
||||
// Mock source tables
|
||||
$sourceTables = Mockery::mock(ResultInterface::class);
|
||||
$sourceTables->shouldReceive('fetchAll')->andReturn([
|
||||
['table_name' => 'users'],
|
||||
['table_name' => 'posts'],
|
||||
['table_name' => 'comments'],
|
||||
]);
|
||||
|
||||
// Mock target tables
|
||||
$targetTables = Mockery::mock(ResultInterface::class);
|
||||
$targetTables->shouldReceive('fetchAll')->andReturn([
|
||||
['table_name' => 'users'],
|
||||
['table_name' => 'posts'],
|
||||
['table_name' => 'categories'], // Different table
|
||||
]);
|
||||
|
||||
// Mock table structure queries
|
||||
$this->sourceConnection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('pgsql');
|
||||
|
||||
$this->targetConnection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('pgsql');
|
||||
|
||||
$this->sourceConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.tables/'), [])
|
||||
->andReturn($sourceTables);
|
||||
|
||||
$this->targetConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.tables/'), [])
|
||||
->andReturn($targetTables);
|
||||
|
||||
// Mock column queries for each table
|
||||
$this->mockTableStructure('users', true, true);
|
||||
$this->mockTableStructure('posts', true, true);
|
||||
$this->mockTableStructure('comments', true, false);
|
||||
$this->mockTableStructure('categories', false, true);
|
||||
|
||||
$difference = $this->comparator->compare();
|
||||
|
||||
expect($difference)->toBeInstanceOf(SchemaDifference::class);
|
||||
expect($difference->hasDifferences())->toBeTrue();
|
||||
expect($difference->missingTables)->toHaveCount(1);
|
||||
expect($difference->extraTables)->toHaveCount(1);
|
||||
expect(array_keys($difference->missingTables))->toBe(['comments']);
|
||||
expect(array_keys($difference->extraTables))->toBe(['categories']);
|
||||
});
|
||||
|
||||
test('compares schemas with identical tables', function () {
|
||||
// Mock source tables
|
||||
$sourceTables = Mockery::mock(ResultInterface::class);
|
||||
$sourceTables->shouldReceive('fetchAll')->andReturn([
|
||||
['table_name' => 'users'],
|
||||
['table_name' => 'posts'],
|
||||
]);
|
||||
|
||||
// Mock target tables
|
||||
$targetTables = Mockery::mock(ResultInterface::class);
|
||||
$targetTables->shouldReceive('fetchAll')->andReturn([
|
||||
['table_name' => 'users'],
|
||||
['table_name' => 'posts'],
|
||||
]);
|
||||
|
||||
// Mock table structure queries
|
||||
$this->sourceConnection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('pgsql');
|
||||
|
||||
$this->targetConnection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('pgsql');
|
||||
|
||||
$this->sourceConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.tables/'), [])
|
||||
->andReturn($sourceTables);
|
||||
|
||||
$this->targetConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.tables/'), [])
|
||||
->andReturn($targetTables);
|
||||
|
||||
// Mock identical column structure for each table
|
||||
$this->mockIdenticalTableStructure('users');
|
||||
$this->mockIdenticalTableStructure('posts');
|
||||
|
||||
$difference = $this->comparator->compare();
|
||||
|
||||
expect($difference)->toBeInstanceOf(SchemaDifference::class);
|
||||
expect($difference->hasDifferences())->toBeFalse();
|
||||
expect($difference->missingTables)->toBeEmpty();
|
||||
expect($difference->extraTables)->toBeEmpty();
|
||||
expect($difference->tableDifferences)->toBeEmpty();
|
||||
});
|
||||
|
||||
test('compares schemas with different column definitions', function () {
|
||||
// Mock source tables
|
||||
$sourceTables = Mockery::mock(ResultInterface::class);
|
||||
$sourceTables->shouldReceive('fetchAll')->andReturn([
|
||||
['table_name' => 'users'],
|
||||
]);
|
||||
|
||||
// Mock target tables
|
||||
$targetTables = Mockery::mock(ResultInterface::class);
|
||||
$targetTables->shouldReceive('fetchAll')->andReturn([
|
||||
['table_name' => 'users'],
|
||||
]);
|
||||
|
||||
// Mock table structure queries
|
||||
$this->sourceConnection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('pgsql');
|
||||
|
||||
$this->targetConnection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('pgsql');
|
||||
|
||||
$this->sourceConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.tables/'), [])
|
||||
->andReturn($sourceTables);
|
||||
|
||||
$this->targetConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.tables/'), [])
|
||||
->andReturn($targetTables);
|
||||
|
||||
// Mock source columns
|
||||
$sourceColumns = Mockery::mock(ResultInterface::class);
|
||||
$sourceColumns->shouldReceive('fetchAll')->andReturn([
|
||||
[
|
||||
'column_name' => 'id',
|
||||
'data_type' => 'integer',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => 'nextval(\'users_id_seq\'::regclass)',
|
||||
],
|
||||
[
|
||||
'column_name' => 'name',
|
||||
'data_type' => 'character varying',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => null,
|
||||
],
|
||||
[
|
||||
'column_name' => 'email',
|
||||
'data_type' => 'character varying',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => null,
|
||||
],
|
||||
]);
|
||||
|
||||
// Mock target columns with differences
|
||||
$targetColumns = Mockery::mock(ResultInterface::class);
|
||||
$targetColumns->shouldReceive('fetchAll')->andReturn([
|
||||
[
|
||||
'column_name' => 'id',
|
||||
'data_type' => 'integer',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => 'nextval(\'users_id_seq\'::regclass)',
|
||||
],
|
||||
[
|
||||
'column_name' => 'name',
|
||||
'data_type' => 'character varying',
|
||||
'is_nullable' => 'YES', // Changed to nullable
|
||||
'column_default' => null,
|
||||
],
|
||||
[
|
||||
'column_name' => 'username', // Different column
|
||||
'data_type' => 'character varying',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => null,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->sourceConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.columns.*users/'), [])
|
||||
->andReturn($sourceColumns);
|
||||
|
||||
$this->targetConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.columns.*users/'), [])
|
||||
->andReturn($targetColumns);
|
||||
|
||||
// Mock empty indexes and foreign keys
|
||||
$this->mockEmptyIndexes('users');
|
||||
$this->mockEmptyForeignKeys('users');
|
||||
|
||||
$difference = $this->comparator->compare();
|
||||
|
||||
expect($difference)->toBeInstanceOf(SchemaDifference::class);
|
||||
expect($difference->hasDifferences())->toBeTrue();
|
||||
expect($difference->tableDifferences)->toHaveCount(1);
|
||||
expect($difference->tableDifferences['users']->missingColumns)->toHaveKey('email');
|
||||
expect($difference->tableDifferences['users']->extraColumns)->toHaveKey('username');
|
||||
expect($difference->tableDifferences['users']->modifiedColumns)->toHaveKey('name');
|
||||
});
|
||||
|
||||
test('compares schemas with different indexes', function () {
|
||||
// Mock source tables
|
||||
$sourceTables = Mockery::mock(ResultInterface::class);
|
||||
$sourceTables->shouldReceive('fetchAll')->andReturn([
|
||||
['table_name' => 'users'],
|
||||
]);
|
||||
|
||||
// Mock target tables
|
||||
$targetTables = Mockery::mock(ResultInterface::class);
|
||||
$targetTables->shouldReceive('fetchAll')->andReturn([
|
||||
['table_name' => 'users'],
|
||||
]);
|
||||
|
||||
// Mock table structure queries
|
||||
$this->sourceConnection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('pgsql');
|
||||
|
||||
$this->targetConnection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('pgsql');
|
||||
|
||||
$this->sourceConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.tables/'), [])
|
||||
->andReturn($sourceTables);
|
||||
|
||||
$this->targetConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.tables/'), [])
|
||||
->andReturn($targetTables);
|
||||
|
||||
// Mock identical columns
|
||||
$this->mockIdenticalColumns('users');
|
||||
|
||||
// Mock source indexes
|
||||
$sourceIndexes = Mockery::mock(ResultInterface::class);
|
||||
$sourceIndexes->shouldReceive('fetchAll')->andReturn([
|
||||
[
|
||||
'indexname' => 'users_pkey',
|
||||
'indexdef' => 'CREATE UNIQUE INDEX users_pkey ON users USING btree (id)',
|
||||
],
|
||||
[
|
||||
'indexname' => 'users_email_idx',
|
||||
'indexdef' => 'CREATE UNIQUE INDEX users_email_idx ON users USING btree (email)',
|
||||
],
|
||||
]);
|
||||
|
||||
// Mock target indexes with differences
|
||||
$targetIndexes = Mockery::mock(ResultInterface::class);
|
||||
$targetIndexes->shouldReceive('fetchAll')->andReturn([
|
||||
[
|
||||
'indexname' => 'users_pkey',
|
||||
'indexdef' => 'CREATE UNIQUE INDEX users_pkey ON users USING btree (id)',
|
||||
],
|
||||
[
|
||||
'indexname' => 'users_name_idx', // Different index
|
||||
'indexdef' => 'CREATE INDEX users_name_idx ON users USING btree (name)',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->sourceConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/pg_indexes.*users/'), [])
|
||||
->andReturn($sourceIndexes);
|
||||
|
||||
$this->targetConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/pg_indexes.*users/'), [])
|
||||
->andReturn($targetIndexes);
|
||||
|
||||
// Mock empty foreign keys
|
||||
$this->mockEmptyForeignKeys('users');
|
||||
|
||||
$difference = $this->comparator->compare();
|
||||
|
||||
expect($difference)->toBeInstanceOf(SchemaDifference::class);
|
||||
expect($difference->hasDifferences())->toBeTrue();
|
||||
expect($difference->tableDifferences)->toHaveCount(1);
|
||||
expect($difference->tableDifferences['users']->missingIndexes)->toHaveKey('users_email_idx');
|
||||
expect($difference->tableDifferences['users']->extraIndexes)->toHaveKey('users_name_idx');
|
||||
});
|
||||
|
||||
// Helper functions for tests
|
||||
beforeEach(function () {
|
||||
// Define helper methods in the test context
|
||||
$this->mockTableStructure = function (string $tableName, bool $inSource, bool $inTarget): void {
|
||||
if ($inSource) {
|
||||
$sourceColumns = Mockery::mock(ResultInterface::class);
|
||||
$sourceColumns->shouldReceive('fetchAll')->andReturn([
|
||||
[
|
||||
'column_name' => 'id',
|
||||
'data_type' => 'integer',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => 'nextval(\'' . $tableName . '_id_seq\'::regclass)',
|
||||
],
|
||||
[
|
||||
'column_name' => 'name',
|
||||
'data_type' => 'character varying',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => null,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->sourceConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.columns.*' . $tableName . '/'), [])
|
||||
->andReturn($sourceColumns);
|
||||
|
||||
$this->mockEmptyIndexes($tableName, true, false);
|
||||
$this->mockEmptyForeignKeys($tableName, true, false);
|
||||
}
|
||||
|
||||
if ($inTarget) {
|
||||
$targetColumns = Mockery::mock(ResultInterface::class);
|
||||
$targetColumns->shouldReceive('fetchAll')->andReturn([
|
||||
[
|
||||
'column_name' => 'id',
|
||||
'data_type' => 'integer',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => 'nextval(\'' . $tableName . '_id_seq\'::regclass)',
|
||||
],
|
||||
[
|
||||
'column_name' => 'name',
|
||||
'data_type' => 'character varying',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => null,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->targetConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.columns.*' . $tableName . '/'), [])
|
||||
->andReturn($targetColumns);
|
||||
|
||||
$this->mockEmptyIndexes($tableName, false, true);
|
||||
$this->mockEmptyForeignKeys($tableName, false, true);
|
||||
}
|
||||
};
|
||||
|
||||
$this->mockIdenticalTableStructure = function (string $tableName): void {
|
||||
$this->mockIdenticalColumns($tableName);
|
||||
$this->mockEmptyIndexes($tableName);
|
||||
$this->mockEmptyForeignKeys($tableName);
|
||||
};
|
||||
|
||||
$this->mockIdenticalColumns = function (string $tableName): void {
|
||||
$columns = Mockery::mock(ResultInterface::class);
|
||||
$columns->shouldReceive('fetchAll')->andReturn([
|
||||
[
|
||||
'column_name' => 'id',
|
||||
'data_type' => 'integer',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => 'nextval(\'' . $tableName . '_id_seq\'::regclass)',
|
||||
],
|
||||
[
|
||||
'column_name' => 'name',
|
||||
'data_type' => 'character varying',
|
||||
'is_nullable' => 'NO',
|
||||
'column_default' => null,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->sourceConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.columns.*' . $tableName . '/'), [])
|
||||
->andReturn($columns);
|
||||
|
||||
$this->targetConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.columns.*' . $tableName . '/'), [])
|
||||
->andReturn($columns);
|
||||
};
|
||||
|
||||
$this->mockEmptyIndexes = function (string $tableName, bool $source = true, bool $target = true): void {
|
||||
$emptyIndexes = Mockery::mock(ResultInterface::class);
|
||||
$emptyIndexes->shouldReceive('fetchAll')->andReturn([]);
|
||||
|
||||
if ($source) {
|
||||
$this->sourceConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/pg_indexes.*' . $tableName . '/'), [])
|
||||
->andReturn($emptyIndexes);
|
||||
}
|
||||
|
||||
if ($target) {
|
||||
$this->targetConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/pg_indexes.*' . $tableName . '/'), [])
|
||||
->andReturn($emptyIndexes);
|
||||
}
|
||||
};
|
||||
|
||||
$this->mockEmptyForeignKeys = function (string $tableName, bool $source = true, bool $target = true): void {
|
||||
$emptyForeignKeys = Mockery::mock(ResultInterface::class);
|
||||
$emptyForeignKeys->shouldReceive('fetchAll')->andReturn([]);
|
||||
|
||||
if ($source) {
|
||||
$this->sourceConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.table_constraints.*' . $tableName . '/'), [])
|
||||
->andReturn($emptyForeignKeys);
|
||||
}
|
||||
|
||||
if ($target) {
|
||||
$this->targetConnection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/information_schema.table_constraints.*' . $tableName . '/'), [])
|
||||
->andReturn($emptyForeignKeys);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Database\Schema\Comparison;
|
||||
|
||||
use App\Framework\Database\Schema\Comparison\SchemaDifference;
|
||||
use App\Framework\Database\Schema\Comparison\TableDifference;
|
||||
|
||||
test('creates a schema difference with missing and extra tables', function () {
|
||||
$missingTables = [
|
||||
'users' => [
|
||||
'columns' => [
|
||||
'id' => ['type' => 'integer', 'nullable' => false],
|
||||
'name' => ['type' => 'varchar', 'nullable' => false],
|
||||
'email' => ['type' => 'varchar', 'nullable' => false],
|
||||
],
|
||||
],
|
||||
'posts' => [
|
||||
'columns' => [
|
||||
'id' => ['type' => 'integer', 'nullable' => false],
|
||||
'title' => ['type' => 'varchar', 'nullable' => false],
|
||||
'content' => ['type' => 'text', 'nullable' => true],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$extraTables = [
|
||||
'categories' => [
|
||||
'columns' => [
|
||||
'id' => ['type' => 'integer', 'nullable' => false],
|
||||
'name' => ['type' => 'varchar', 'nullable' => false],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$difference = new SchemaDifference(
|
||||
'source_schema',
|
||||
'target_schema',
|
||||
$missingTables,
|
||||
$extraTables,
|
||||
[]
|
||||
);
|
||||
|
||||
expect($difference->sourceSchema)->toBe('source_schema');
|
||||
expect($difference->targetSchema)->toBe('target_schema');
|
||||
expect($difference->missingTables)->toBe($missingTables);
|
||||
expect($difference->extraTables)->toBe($extraTables);
|
||||
expect($difference->tableDifferences)->toBeEmpty();
|
||||
expect($difference->hasDifferences())->toBeTrue();
|
||||
});
|
||||
|
||||
test('creates a schema difference with table differences', function () {
|
||||
$tableDifference = new TableDifference(
|
||||
'users',
|
||||
['email' => ['type' => 'varchar', 'nullable' => false]], // missing columns
|
||||
['username' => ['type' => 'varchar', 'nullable' => false]], // extra columns
|
||||
[
|
||||
'name' => [
|
||||
'source' => ['type' => 'varchar', 'nullable' => false],
|
||||
'target' => ['type' => 'varchar', 'nullable' => true],
|
||||
],
|
||||
], // modified columns
|
||||
[], // missing indexes
|
||||
[], // extra indexes
|
||||
[], // modified indexes
|
||||
[], // missing foreign keys
|
||||
[], // extra foreign keys
|
||||
[] // modified foreign keys
|
||||
);
|
||||
|
||||
$difference = new SchemaDifference(
|
||||
'source_schema',
|
||||
'target_schema',
|
||||
[], // missing tables
|
||||
[], // extra tables
|
||||
['users' => $tableDifference]
|
||||
);
|
||||
|
||||
expect($difference->sourceSchema)->toBe('source_schema');
|
||||
expect($difference->targetSchema)->toBe('target_schema');
|
||||
expect($difference->missingTables)->toBeEmpty();
|
||||
expect($difference->extraTables)->toBeEmpty();
|
||||
expect($difference->tableDifferences)->toHaveCount(1);
|
||||
expect($difference->tableDifferences['users'])->toBe($tableDifference);
|
||||
expect($difference->hasDifferences())->toBeTrue();
|
||||
});
|
||||
|
||||
test('creates a schema difference with no differences', function () {
|
||||
$difference = new SchemaDifference(
|
||||
'source_schema',
|
||||
'target_schema',
|
||||
[], // missing tables
|
||||
[], // extra tables
|
||||
[] // table differences
|
||||
);
|
||||
|
||||
expect($difference->sourceSchema)->toBe('source_schema');
|
||||
expect($difference->targetSchema)->toBe('target_schema');
|
||||
expect($difference->missingTables)->toBeEmpty();
|
||||
expect($difference->extraTables)->toBeEmpty();
|
||||
expect($difference->tableDifferences)->toBeEmpty();
|
||||
expect($difference->hasDifferences())->toBeFalse();
|
||||
});
|
||||
|
||||
test('gets summary of differences', function () {
|
||||
$tableDifference1 = new TableDifference(
|
||||
'users',
|
||||
['email' => ['type' => 'varchar', 'nullable' => false]], // missing columns
|
||||
['username' => ['type' => 'varchar', 'nullable' => false]], // extra columns
|
||||
[], // modified columns
|
||||
[], // missing indexes
|
||||
[], // extra indexes
|
||||
[], // modified indexes
|
||||
[], // missing foreign keys
|
||||
[], // extra foreign keys
|
||||
[] // modified foreign keys
|
||||
);
|
||||
|
||||
$tableDifference2 = new TableDifference(
|
||||
'posts',
|
||||
[], // missing columns
|
||||
[], // extra columns
|
||||
[], // modified columns
|
||||
['idx_posts_title' => ['type' => 'index', 'columns' => ['title']]], // missing indexes
|
||||
[], // extra indexes
|
||||
[], // modified indexes
|
||||
[], // missing foreign keys
|
||||
[], // extra foreign keys
|
||||
[] // modified foreign keys
|
||||
);
|
||||
|
||||
$difference = new SchemaDifference(
|
||||
'source_schema',
|
||||
'target_schema',
|
||||
['comments' => []], // missing tables
|
||||
['categories' => []], // extra tables
|
||||
[
|
||||
'users' => $tableDifference1,
|
||||
'posts' => $tableDifference2,
|
||||
]
|
||||
);
|
||||
|
||||
$summary = $difference->getSummary();
|
||||
|
||||
expect($summary)->toBeArray();
|
||||
expect($summary['missing_tables'])->toBe(1);
|
||||
expect($summary['extra_tables'])->toBe(1);
|
||||
expect($summary['modified_tables'])->toBe(2);
|
||||
expect($summary['total_differences'])->toBe(4);
|
||||
});
|
||||
|
||||
test('gets description of differences', function () {
|
||||
$tableDifference = new TableDifference(
|
||||
'users',
|
||||
['email' => ['type' => 'varchar', 'nullable' => false]], // missing columns
|
||||
['username' => ['type' => 'varchar', 'nullable' => false]], // extra columns
|
||||
[
|
||||
'name' => [
|
||||
'source' => ['type' => 'varchar', 'nullable' => false],
|
||||
'target' => ['type' => 'varchar', 'nullable' => true],
|
||||
],
|
||||
], // modified columns
|
||||
[], // missing indexes
|
||||
[], // extra indexes
|
||||
[], // modified indexes
|
||||
[], // missing foreign keys
|
||||
[], // extra foreign keys
|
||||
[] // modified foreign keys
|
||||
);
|
||||
|
||||
$difference = new SchemaDifference(
|
||||
'source_schema',
|
||||
'target_schema',
|
||||
['comments' => []], // missing tables
|
||||
['categories' => []], // extra tables
|
||||
['users' => $tableDifference]
|
||||
);
|
||||
|
||||
$description = $difference->getDescription();
|
||||
|
||||
expect($description)->toBeString();
|
||||
expect($description)->toContain('Schema Differences');
|
||||
expect($description)->toContain('Missing Tables');
|
||||
expect($description)->toContain('comments');
|
||||
expect($description)->toContain('Extra Tables');
|
||||
expect($description)->toContain('categories');
|
||||
expect($description)->toContain('Table Differences');
|
||||
expect($description)->toContain('users');
|
||||
});
|
||||
|
||||
test('generates migration code from differences', function () {
|
||||
$tableDifference = new TableDifference(
|
||||
'users',
|
||||
['email' => ['type' => 'varchar', 'nullable' => false]], // missing columns
|
||||
['username' => ['type' => 'varchar', 'nullable' => false]], // extra columns
|
||||
[
|
||||
'name' => [
|
||||
'source' => ['type' => 'varchar', 'nullable' => false],
|
||||
'target' => ['type' => 'varchar', 'nullable' => true],
|
||||
],
|
||||
], // modified columns
|
||||
[], // missing indexes
|
||||
[], // extra indexes
|
||||
[], // modified indexes
|
||||
[], // missing foreign keys
|
||||
[], // extra foreign keys
|
||||
[] // modified foreign keys
|
||||
);
|
||||
|
||||
$difference = new SchemaDifference(
|
||||
'source_schema',
|
||||
'target_schema',
|
||||
['comments' => [
|
||||
'columns' => [
|
||||
'id' => ['type' => 'integer', 'nullable' => false],
|
||||
'content' => ['type' => 'text', 'nullable' => false],
|
||||
'post_id' => ['type' => 'integer', 'nullable' => false],
|
||||
],
|
||||
]], // missing tables
|
||||
['categories' => []], // extra tables
|
||||
['users' => $tableDifference]
|
||||
);
|
||||
|
||||
$migrationCode = $difference->generateMigrationCode('UpdateSchema');
|
||||
|
||||
expect($migrationCode)->toBeString();
|
||||
expect($migrationCode)->toContain('class UpdateSchema extends AbstractMigration');
|
||||
expect($migrationCode)->toContain('public function up(ConnectionInterface $connection)');
|
||||
expect($migrationCode)->toContain('public function down(ConnectionInterface $connection)');
|
||||
expect($migrationCode)->toContain('$schema->create(\'comments\'');
|
||||
expect($migrationCode)->toContain('$schema->table(\'users\'');
|
||||
expect($migrationCode)->toContain('$schema->dropIfExists(\'categories\')');
|
||||
});
|
||||
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Database\Schema\Comparison;
|
||||
|
||||
use App\Framework\Database\Schema\Comparison\TableDifference;
|
||||
|
||||
test('creates a table difference with column differences', function () {
|
||||
$tableDifference = new TableDifference(
|
||||
'users',
|
||||
[
|
||||
'email' => ['type' => 'varchar', 'nullable' => false],
|
||||
'created_at' => ['type' => 'timestamp', 'nullable' => false],
|
||||
], // missing columns
|
||||
[
|
||||
'username' => ['type' => 'varchar', 'nullable' => false],
|
||||
'last_login' => ['type' => 'timestamp', 'nullable' => true],
|
||||
], // extra columns
|
||||
[
|
||||
'name' => [
|
||||
'source' => ['type' => 'varchar', 'nullable' => false],
|
||||
'target' => ['type' => 'varchar', 'nullable' => true],
|
||||
],
|
||||
], // modified columns
|
||||
[], // missing indexes
|
||||
[], // extra indexes
|
||||
[], // modified indexes
|
||||
[], // missing foreign keys
|
||||
[], // extra foreign keys
|
||||
[] // modified foreign keys
|
||||
);
|
||||
|
||||
expect($tableDifference->tableName)->toBe('users');
|
||||
expect($tableDifference->missingColumns)->toHaveCount(2);
|
||||
expect($tableDifference->extraColumns)->toHaveCount(2);
|
||||
expect($tableDifference->modifiedColumns)->toHaveCount(1);
|
||||
expect($tableDifference->missingIndexes)->toBeEmpty();
|
||||
expect($tableDifference->extraIndexes)->toBeEmpty();
|
||||
expect($tableDifference->modifiedIndexes)->toBeEmpty();
|
||||
expect($tableDifference->missingForeignKeys)->toBeEmpty();
|
||||
expect($tableDifference->extraForeignKeys)->toBeEmpty();
|
||||
expect($tableDifference->modifiedForeignKeys)->toBeEmpty();
|
||||
expect($tableDifference->hasDifferences())->toBeTrue();
|
||||
});
|
||||
|
||||
test('creates a table difference with index differences', function () {
|
||||
$tableDifference = new TableDifference(
|
||||
'users',
|
||||
[], // missing columns
|
||||
[], // extra columns
|
||||
[], // modified columns
|
||||
[
|
||||
'idx_users_email' => [
|
||||
'type' => 'unique',
|
||||
'columns' => ['email'],
|
||||
],
|
||||
'idx_users_name' => [
|
||||
'type' => 'index',
|
||||
'columns' => ['name'],
|
||||
],
|
||||
], // missing indexes
|
||||
[
|
||||
'idx_users_username' => [
|
||||
'type' => 'unique',
|
||||
'columns' => ['username'],
|
||||
],
|
||||
], // extra indexes
|
||||
[
|
||||
'idx_users_created_at' => [
|
||||
'source' => [
|
||||
'type' => 'index',
|
||||
'columns' => ['created_at'],
|
||||
],
|
||||
'target' => [
|
||||
'type' => 'index',
|
||||
'columns' => ['created_at', 'updated_at'],
|
||||
],
|
||||
],
|
||||
], // modified indexes
|
||||
[], // missing foreign keys
|
||||
[], // extra foreign keys
|
||||
[] // modified foreign keys
|
||||
);
|
||||
|
||||
expect($tableDifference->tableName)->toBe('users');
|
||||
expect($tableDifference->missingColumns)->toBeEmpty();
|
||||
expect($tableDifference->extraColumns)->toBeEmpty();
|
||||
expect($tableDifference->modifiedColumns)->toBeEmpty();
|
||||
expect($tableDifference->missingIndexes)->toHaveCount(2);
|
||||
expect($tableDifference->extraIndexes)->toHaveCount(1);
|
||||
expect($tableDifference->modifiedIndexes)->toHaveCount(1);
|
||||
expect($tableDifference->missingForeignKeys)->toBeEmpty();
|
||||
expect($tableDifference->extraForeignKeys)->toBeEmpty();
|
||||
expect($tableDifference->modifiedForeignKeys)->toBeEmpty();
|
||||
expect($tableDifference->hasDifferences())->toBeTrue();
|
||||
});
|
||||
|
||||
test('creates a table difference with foreign key differences', function () {
|
||||
$tableDifference = new TableDifference(
|
||||
'posts',
|
||||
[], // missing columns
|
||||
[], // extra columns
|
||||
[], // modified columns
|
||||
[], // missing indexes
|
||||
[], // extra indexes
|
||||
[], // modified indexes
|
||||
[
|
||||
'fk_posts_user_id' => [
|
||||
'columns' => ['user_id'],
|
||||
'referenced_table' => 'users',
|
||||
'referenced_columns' => ['id'],
|
||||
'update_rule' => 'CASCADE',
|
||||
'delete_rule' => 'CASCADE',
|
||||
],
|
||||
], // missing foreign keys
|
||||
[
|
||||
'fk_posts_category_id' => [
|
||||
'columns' => ['category_id'],
|
||||
'referenced_table' => 'categories',
|
||||
'referenced_columns' => ['id'],
|
||||
'update_rule' => 'CASCADE',
|
||||
'delete_rule' => 'CASCADE',
|
||||
],
|
||||
], // extra foreign keys
|
||||
[
|
||||
'fk_posts_parent_id' => [
|
||||
'source' => [
|
||||
'columns' => ['parent_id'],
|
||||
'referenced_table' => 'posts',
|
||||
'referenced_columns' => ['id'],
|
||||
'update_rule' => 'CASCADE',
|
||||
'delete_rule' => 'CASCADE',
|
||||
],
|
||||
'target' => [
|
||||
'columns' => ['parent_id'],
|
||||
'referenced_table' => 'posts',
|
||||
'referenced_columns' => ['id'],
|
||||
'update_rule' => 'CASCADE',
|
||||
'delete_rule' => 'SET NULL',
|
||||
],
|
||||
],
|
||||
] // modified foreign keys
|
||||
);
|
||||
|
||||
expect($tableDifference->tableName)->toBe('posts');
|
||||
expect($tableDifference->missingColumns)->toBeEmpty();
|
||||
expect($tableDifference->extraColumns)->toBeEmpty();
|
||||
expect($tableDifference->modifiedColumns)->toBeEmpty();
|
||||
expect($tableDifference->missingIndexes)->toBeEmpty();
|
||||
expect($tableDifference->extraIndexes)->toBeEmpty();
|
||||
expect($tableDifference->modifiedIndexes)->toBeEmpty();
|
||||
expect($tableDifference->missingForeignKeys)->toHaveCount(1);
|
||||
expect($tableDifference->extraForeignKeys)->toHaveCount(1);
|
||||
expect($tableDifference->modifiedForeignKeys)->toHaveCount(1);
|
||||
expect($tableDifference->hasDifferences())->toBeTrue();
|
||||
});
|
||||
|
||||
test('creates a table difference with no differences', function () {
|
||||
$tableDifference = new TableDifference(
|
||||
'users',
|
||||
[], // missing columns
|
||||
[], // extra columns
|
||||
[], // modified columns
|
||||
[], // missing indexes
|
||||
[], // extra indexes
|
||||
[], // modified indexes
|
||||
[], // missing foreign keys
|
||||
[], // extra foreign keys
|
||||
[] // modified foreign keys
|
||||
);
|
||||
|
||||
expect($tableDifference->tableName)->toBe('users');
|
||||
expect($tableDifference->missingColumns)->toBeEmpty();
|
||||
expect($tableDifference->extraColumns)->toBeEmpty();
|
||||
expect($tableDifference->modifiedColumns)->toBeEmpty();
|
||||
expect($tableDifference->missingIndexes)->toBeEmpty();
|
||||
expect($tableDifference->extraIndexes)->toBeEmpty();
|
||||
expect($tableDifference->modifiedIndexes)->toBeEmpty();
|
||||
expect($tableDifference->missingForeignKeys)->toBeEmpty();
|
||||
expect($tableDifference->extraForeignKeys)->toBeEmpty();
|
||||
expect($tableDifference->modifiedForeignKeys)->toBeEmpty();
|
||||
expect($tableDifference->hasDifferences())->toBeFalse();
|
||||
});
|
||||
|
||||
test('gets summary of differences', function () {
|
||||
$tableDifference = new TableDifference(
|
||||
'users',
|
||||
['email' => ['type' => 'varchar', 'nullable' => false]], // missing columns
|
||||
['username' => ['type' => 'varchar', 'nullable' => false]], // extra columns
|
||||
[
|
||||
'name' => [
|
||||
'source' => ['type' => 'varchar', 'nullable' => false],
|
||||
'target' => ['type' => 'varchar', 'nullable' => true],
|
||||
],
|
||||
], // modified columns
|
||||
['idx_users_email' => ['type' => 'unique', 'columns' => ['email']]], // missing indexes
|
||||
['idx_users_username' => ['type' => 'unique', 'columns' => ['username']]], // extra indexes
|
||||
[], // modified indexes
|
||||
['fk_users_role_id' => []], // missing foreign keys
|
||||
['fk_users_team_id' => []], // extra foreign keys
|
||||
[] // modified foreign keys
|
||||
);
|
||||
|
||||
$summary = $tableDifference->getSummary();
|
||||
|
||||
expect($summary)->toBeArray();
|
||||
expect($summary['missing_columns'])->toBe(1);
|
||||
expect($summary['extra_columns'])->toBe(1);
|
||||
expect($summary['modified_columns'])->toBe(1);
|
||||
expect($summary['missing_indexes'])->toBe(1);
|
||||
expect($summary['extra_indexes'])->toBe(1);
|
||||
expect($summary['modified_indexes'])->toBe(0);
|
||||
expect($summary['missing_foreign_keys'])->toBe(1);
|
||||
expect($summary['extra_foreign_keys'])->toBe(1);
|
||||
expect($summary['modified_foreign_keys'])->toBe(0);
|
||||
});
|
||||
|
||||
test('gets description of differences', function () {
|
||||
$tableDifference = new TableDifference(
|
||||
'users',
|
||||
['email' => ['type' => 'varchar', 'nullable' => false]], // missing columns
|
||||
['username' => ['type' => 'varchar', 'nullable' => false]], // extra columns
|
||||
[
|
||||
'name' => [
|
||||
'source' => ['type' => 'varchar', 'nullable' => false],
|
||||
'target' => ['type' => 'varchar', 'nullable' => true],
|
||||
],
|
||||
], // modified columns
|
||||
['idx_users_email' => ['type' => 'unique', 'columns' => ['email']]], // missing indexes
|
||||
['idx_users_username' => ['type' => 'unique', 'columns' => ['username']]], // extra indexes
|
||||
[], // modified indexes
|
||||
['fk_users_role_id' => [
|
||||
'columns' => ['role_id'],
|
||||
'referenced_table' => 'roles',
|
||||
'referenced_columns' => ['id'],
|
||||
]], // missing foreign keys
|
||||
['fk_users_team_id' => [
|
||||
'columns' => ['team_id'],
|
||||
'referenced_table' => 'teams',
|
||||
'referenced_columns' => ['id'],
|
||||
]], // extra foreign keys
|
||||
[] // modified foreign keys
|
||||
);
|
||||
|
||||
$description = $tableDifference->getDescription();
|
||||
|
||||
expect($description)->toBeString();
|
||||
expect($description)->toContain('Table: users');
|
||||
expect($description)->toContain('Missing Columns');
|
||||
expect($description)->toContain('email');
|
||||
expect($description)->toContain('Extra Columns');
|
||||
expect($description)->toContain('username');
|
||||
expect($description)->toContain('Modified Columns');
|
||||
expect($description)->toContain('name');
|
||||
expect($description)->toContain('Missing Indexes');
|
||||
expect($description)->toContain('idx_users_email');
|
||||
expect($description)->toContain('Extra Indexes');
|
||||
expect($description)->toContain('idx_users_username');
|
||||
expect($description)->toContain('Missing Foreign Keys');
|
||||
expect($description)->toContain('fk_users_role_id');
|
||||
expect($description)->toContain('Extra Foreign Keys');
|
||||
expect($description)->toContain('fk_users_team_id');
|
||||
});
|
||||
Reference in New Issue
Block a user