fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
@@ -2,15 +2,24 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\Config\AppConfig;
|
||||
use App\Framework\Database\ConnectionInterface;
|
||||
use App\Framework\Database\Exception\DatabaseException;
|
||||
use App\Framework\Database\Migration\Migration;
|
||||
use App\Framework\Database\Migration\MigrationCollection;
|
||||
use App\Framework\Database\Migration\MigrationDependencyGraph;
|
||||
use App\Framework\Database\Migration\MigrationRunner;
|
||||
use App\Framework\Database\Migration\Services\MigrationDatabaseManager;
|
||||
use App\Framework\Database\Migration\Services\MigrationErrorAnalyzer;
|
||||
use App\Framework\Database\Migration\Services\MigrationLogger;
|
||||
use App\Framework\Database\Migration\Services\MigrationPerformanceTracker;
|
||||
use App\Framework\Database\Migration\Services\MigrationValidator;
|
||||
use App\Framework\Database\Migration\ValueObjects\MigrationTableConfig;
|
||||
use App\Framework\Database\Platform\MySqlPlatform;
|
||||
use App\Framework\Database\ResultInterface;
|
||||
use App\Framework\Database\ValueObjects\SqlQuery;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\Id\Ulid\UlidGenerator;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
use App\Framework\Performance\OperationTracker;
|
||||
|
||||
@@ -20,15 +29,49 @@ beforeEach(function () {
|
||||
$this->clock = new SystemClock();
|
||||
$this->memoryMonitor = new MemoryMonitor();
|
||||
$this->operationTracker = new OperationTracker();
|
||||
$this->ulidGenerator = new class implements UlidGenerator {
|
||||
public function generate(): string
|
||||
{
|
||||
return '01ARZ3NDEKTSV4RRFFQ69G5FAV';
|
||||
}
|
||||
};
|
||||
$this->appConfig = new AppConfig(\App\Framework\Config\EnvironmentType::DEVELOPMENT);
|
||||
|
||||
$tableConfig = MigrationTableConfig::withCustomTable('test_migrations');
|
||||
$dependencyGraph = new MigrationDependencyGraph();
|
||||
$databaseManager = new MigrationDatabaseManager(
|
||||
$this->connection,
|
||||
$this->platform,
|
||||
$this->clock,
|
||||
$tableConfig
|
||||
);
|
||||
$performanceTracker = new MigrationPerformanceTracker(
|
||||
$this->operationTracker,
|
||||
$this->memoryMonitor,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
$migrationLogger = new MigrationLogger(null);
|
||||
$validator = new MigrationValidator(
|
||||
$this->connection,
|
||||
$this->platform,
|
||||
$this->appConfig
|
||||
);
|
||||
$errorAnalyzer = new MigrationErrorAnalyzer();
|
||||
|
||||
$this->migrationRunner = new MigrationRunner(
|
||||
$this->connection,
|
||||
$this->platform,
|
||||
$this->clock,
|
||||
null, // tableConfig
|
||||
null, // logger
|
||||
$this->operationTracker,
|
||||
$this->memoryMonitor
|
||||
$this->ulidGenerator,
|
||||
$this->appConfig,
|
||||
$dependencyGraph,
|
||||
$databaseManager,
|
||||
$performanceTracker,
|
||||
$migrationLogger,
|
||||
$validator,
|
||||
$errorAnalyzer
|
||||
);
|
||||
});
|
||||
|
||||
@@ -38,7 +81,8 @@ test('constructor creates migrations table', function () {
|
||||
|
||||
expect($queries)->toHaveCount(1)
|
||||
->and($queries[0]['type'])->toBe('execute')
|
||||
->and($queries[0]['sql'])->toContain('CREATE TABLE IF NOT EXISTS test_migrations');
|
||||
->and($queries[0]['sql'])->toContain('CREATE TABLE')
|
||||
->and($queries[0]['sql'])->toContain('test_migrations');
|
||||
});
|
||||
|
||||
test('migrate runs pending migrations', function () {
|
||||
@@ -66,16 +110,12 @@ test('migrate runs pending migrations', function () {
|
||||
|
||||
test('migrate skips already applied migrations', function () {
|
||||
$migration = new TestMigration();
|
||||
$migrationData = (object) [
|
||||
'version' => '2024_01_01_000000',
|
||||
'description' => 'Test Migration',
|
||||
'instance' => $migration,
|
||||
];
|
||||
$migrations = new MigrationCollection($migration);
|
||||
|
||||
// Set migration as already applied
|
||||
$this->connection->setAppliedMigrations([['version' => '2024_01_01_000000']]);
|
||||
|
||||
$result = $this->migrationRunner->migrate([$migrationData]);
|
||||
$result = $this->migrationRunner->migrate($migrations);
|
||||
|
||||
expect($result)->toBeEmpty();
|
||||
expect($migration->wasExecuted())->toBeFalse();
|
||||
@@ -84,17 +124,13 @@ test('migrate skips already applied migrations', function () {
|
||||
test('migrate rolls back on failure', function () {
|
||||
// Mock failing migration
|
||||
$failingMigration = new FailingTestMigration();
|
||||
$migrationData = (object) [
|
||||
'version' => '2024_01_01_000000',
|
||||
'description' => 'Failing Migration',
|
||||
'instance' => $failingMigration,
|
||||
];
|
||||
$migrations = new MigrationCollection($failingMigration);
|
||||
|
||||
// Set no applied migrations initially
|
||||
$this->connection->setAppliedMigrations([]);
|
||||
$this->connection->setShouldFail(true);
|
||||
|
||||
expect(fn () => $this->migrationRunner->migrate([$migrationData]))
|
||||
expect(fn () => $this->migrationRunner->migrate($migrations))
|
||||
->toThrow(DatabaseException::class);
|
||||
|
||||
// Verify transaction was used (inTransaction was called)
|
||||
@@ -103,18 +139,14 @@ test('migrate rolls back on failure', function () {
|
||||
|
||||
test('rollback reverts applied migration', function () {
|
||||
$migration = new TestMigration();
|
||||
$migrationData = (object) [
|
||||
'version' => '2024_01_01_000000',
|
||||
'description' => 'Test Migration',
|
||||
'instance' => $migration,
|
||||
];
|
||||
$migrations = new MigrationCollection($migration);
|
||||
|
||||
// Set migration as already applied
|
||||
$this->connection->setAppliedMigrations([['version' => '2024_01_01_000000']]);
|
||||
|
||||
$result = $this->migrationRunner->rollback([$migrationData], 1);
|
||||
$result = $this->migrationRunner->rollback($migrations, 1);
|
||||
|
||||
expect($result)->toContain('2024_01_01_000000');
|
||||
expect($result)->toHaveCount(1);
|
||||
expect($migration->wasRolledBack())->toBeTrue();
|
||||
|
||||
// Verify the migration record was deleted
|
||||
@@ -122,31 +154,31 @@ test('rollback reverts applied migration', function () {
|
||||
$deleteQueries = array_filter(
|
||||
$queries,
|
||||
fn ($q) =>
|
||||
$q['type'] === 'execute' && str_contains($q['sql'], 'DELETE FROM test_migrations')
|
||||
$q['type'] === 'execute' && str_contains($q['sql'], 'DELETE FROM')
|
||||
);
|
||||
expect($deleteQueries)->toHaveCount(1);
|
||||
});
|
||||
|
||||
test('get status returns migration status', function () {
|
||||
$migration1Data = (object) [
|
||||
'version' => '2024_01_01_000000',
|
||||
'description' => 'Applied Migration',
|
||||
'instance' => new TestMigration(),
|
||||
];
|
||||
$migration2Data = (object) [
|
||||
'version' => '2024_01_02_000000',
|
||||
'description' => 'Pending Migration',
|
||||
'instance' => new TestMigration(),
|
||||
];
|
||||
$migration1 = new TestMigration();
|
||||
$migration2 = new class implements Migration {
|
||||
public function up(ConnectionInterface $connection): void {}
|
||||
public function down(ConnectionInterface $connection): void {}
|
||||
public function getDescription(): string { return 'Pending Migration'; }
|
||||
public function getVersion(): \App\Framework\Database\Migration\MigrationVersion {
|
||||
return \App\Framework\Database\Migration\MigrationVersion::fromTimestamp('2024_01_02_000000');
|
||||
}
|
||||
};
|
||||
$migrations = new MigrationCollection($migration1, $migration2);
|
||||
|
||||
// Set only first migration as applied
|
||||
$this->connection->setAppliedMigrations([['version' => '2024_01_01_000000']]);
|
||||
|
||||
$status = $this->migrationRunner->getStatus([$migration1Data, $migration2Data]);
|
||||
$status = $this->migrationRunner->getStatus($migrations);
|
||||
|
||||
expect($status)->toHaveCount(2)
|
||||
->and($status[0]['applied'])->toBeTrue()
|
||||
->and($status[1]['applied'])->toBeFalse();
|
||||
->and($status[0]->applied)->toBeTrue()
|
||||
->and($status[1]->applied)->toBeFalse();
|
||||
});
|
||||
|
||||
// Test fixtures
|
||||
@@ -160,14 +192,14 @@ class TestMigration implements Migration
|
||||
{
|
||||
$this->executed = true;
|
||||
// Simulate migration execution
|
||||
$connection->execute('CREATE TABLE test_table (id INT)');
|
||||
$connection->execute(SqlQuery::create('CREATE TABLE test_table (id INT)'));
|
||||
}
|
||||
|
||||
public function down(ConnectionInterface $connection): void
|
||||
{
|
||||
$this->rolledBack = true;
|
||||
// Simulate migration rollback
|
||||
$connection->execute('DROP TABLE test_table');
|
||||
$connection->execute(SqlQuery::create('DROP TABLE test_table'));
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
|
||||
66
tests/Framework/Database/Seed/SeedRepositoryTest.php
Normal file
66
tests/Framework/Database/Seed/SeedRepositoryTest.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\Database\ConnectionInterface;
|
||||
use App\Framework\Database\Seed\SeedRepository;
|
||||
use App\Framework\Database\ValueObjects\SqlQuery;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
|
||||
describe('SeedRepository', function () {
|
||||
beforeEach(function () {
|
||||
$this->connection = Mockery::mock(ConnectionInterface::class);
|
||||
$this->clock = new SystemClock();
|
||||
$this->repository = new SeedRepository($this->connection, $this->clock);
|
||||
});
|
||||
|
||||
it('checks if seeder has run', function () {
|
||||
$result = Mockery::mock();
|
||||
$result->shouldReceive('fetch')
|
||||
->once()
|
||||
->andReturn(['count' => 1]);
|
||||
|
||||
$this->connection->shouldReceive('query')
|
||||
->once()
|
||||
->with(Mockery::on(function (SqlQuery $query) {
|
||||
return str_contains($query->sql, 'SELECT COUNT(*)');
|
||||
}))
|
||||
->andReturn($result);
|
||||
|
||||
expect($this->repository->hasRun('TestSeeder'))->toBeTrue();
|
||||
});
|
||||
|
||||
it('returns false when seeder has not run', function () {
|
||||
$result = Mockery::mock();
|
||||
$result->shouldReceive('fetch')
|
||||
->once()
|
||||
->andReturn(['count' => 0]);
|
||||
|
||||
$this->connection->shouldReceive('query')
|
||||
->once()
|
||||
->andReturn($result);
|
||||
|
||||
expect($this->repository->hasRun('TestSeeder'))->toBeFalse();
|
||||
});
|
||||
|
||||
it('marks seeder as run', function () {
|
||||
$this->connection->shouldReceive('execute')
|
||||
->once()
|
||||
->with(Mockery::on(function (SqlQuery $query) {
|
||||
return str_contains($query->sql, 'INSERT INTO seeds');
|
||||
}));
|
||||
|
||||
$this->repository->markAsRun('TestSeeder', 'Test description');
|
||||
});
|
||||
|
||||
it('clears all seeds', function () {
|
||||
$this->connection->shouldReceive('execute')
|
||||
->once()
|
||||
->with(Mockery::on(function (SqlQuery $query) {
|
||||
return str_contains($query->sql, 'DELETE FROM seeds');
|
||||
}));
|
||||
|
||||
$this->repository->clearAll();
|
||||
});
|
||||
});
|
||||
|
||||
100
tests/Framework/Database/Seed/SeedRunnerTest.php
Normal file
100
tests/Framework/Database/Seed/SeedRunnerTest.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\Database\Seed\SeedRepository;
|
||||
use App\Framework\Database\Seed\SeedRunner;
|
||||
use App\Framework\Database\Seed\Seeder;
|
||||
|
||||
describe('SeedRunner', function () {
|
||||
beforeEach(function () {
|
||||
$this->seedRepository = Mockery::mock(SeedRepository::class);
|
||||
$this->runner = new SeedRunner($this->seedRepository);
|
||||
});
|
||||
|
||||
it('skips seeder if already run', function () {
|
||||
$seeder = Mockery::mock(Seeder::class);
|
||||
$seeder->shouldReceive('getName')
|
||||
->once()
|
||||
->andReturn('TestSeeder');
|
||||
|
||||
$this->seedRepository->shouldReceive('hasRun')
|
||||
->once()
|
||||
->with('TestSeeder')
|
||||
->andReturn(true);
|
||||
|
||||
$this->seedRepository->shouldNotReceive('markAsRun');
|
||||
|
||||
$this->runner->run($seeder);
|
||||
});
|
||||
|
||||
it('runs seeder if not run yet', function () {
|
||||
$seeder = Mockery::mock(Seeder::class);
|
||||
$seeder->shouldReceive('getName')
|
||||
->once()
|
||||
->andReturn('TestSeeder');
|
||||
$seeder->shouldReceive('getDescription')
|
||||
->once()
|
||||
->andReturn('Test description');
|
||||
$seeder->shouldReceive('seed')
|
||||
->once();
|
||||
|
||||
$this->seedRepository->shouldReceive('hasRun')
|
||||
->once()
|
||||
->with('TestSeeder')
|
||||
->andReturn(false);
|
||||
|
||||
$this->seedRepository->shouldReceive('markAsRun')
|
||||
->once()
|
||||
->with('TestSeeder', 'Test description');
|
||||
|
||||
$this->runner->run($seeder);
|
||||
});
|
||||
|
||||
it('throws exception if seeder fails', function () {
|
||||
$seeder = Mockery::mock(Seeder::class);
|
||||
$seeder->shouldReceive('getName')
|
||||
->once()
|
||||
->andReturn('TestSeeder');
|
||||
$seeder->shouldReceive('seed')
|
||||
->once()
|
||||
->andThrow(new \RuntimeException('Seeder failed'));
|
||||
|
||||
$this->seedRepository->shouldReceive('hasRun')
|
||||
->once()
|
||||
->with('TestSeeder')
|
||||
->andReturn(false);
|
||||
|
||||
$this->seedRepository->shouldNotReceive('markAsRun');
|
||||
|
||||
expect(fn () => $this->runner->run($seeder))
|
||||
->toThrow(\RuntimeException::class, 'Seeder failed');
|
||||
});
|
||||
|
||||
it('runs multiple seeders', function () {
|
||||
$seeder1 = Mockery::mock(Seeder::class);
|
||||
$seeder1->shouldReceive('getName')->andReturn('Seeder1');
|
||||
$seeder1->shouldReceive('getDescription')->andReturn('Description 1');
|
||||
$seeder1->shouldReceive('seed');
|
||||
|
||||
$seeder2 = Mockery::mock(Seeder::class);
|
||||
$seeder2->shouldReceive('getName')->andReturn('Seeder2');
|
||||
$seeder2->shouldReceive('getDescription')->andReturn('Description 2');
|
||||
$seeder2->shouldReceive('seed');
|
||||
|
||||
$this->seedRepository->shouldReceive('hasRun')
|
||||
->with('Seeder1')
|
||||
->andReturn(false);
|
||||
$this->seedRepository->shouldReceive('hasRun')
|
||||
->with('Seeder2')
|
||||
->andReturn(false);
|
||||
|
||||
$this->seedRepository->shouldReceive('markAsRun')
|
||||
->with('Seeder1', 'Description 1');
|
||||
$this->seedRepository->shouldReceive('markAsRun')
|
||||
->with('Seeder2', 'Description 2');
|
||||
|
||||
$this->runner->runAll([$seeder1, $seeder2]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user