- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
180 lines
6.2 KiB
PHP
180 lines
6.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Framework\Database\Migration;
|
|
|
|
use App\Framework\Core\ValueObjects\ClassName;
|
|
use App\Framework\Database\Migration\Migration;
|
|
use App\Framework\Database\Migration\MigrationCollection;
|
|
use App\Framework\Database\Migration\MigrationLoader;
|
|
use App\Framework\Database\Migration\MigrationVersion;
|
|
use App\Framework\DI\Container;
|
|
use App\Framework\Discovery\Results\AttributeRegistry;
|
|
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
|
use App\Framework\Discovery\Results\InterfaceRegistry;
|
|
use App\Framework\Discovery\Results\RouteRegistry;
|
|
use App\Framework\Discovery\Results\TemplateRegistry;
|
|
use App\Framework\Discovery\ValueObjects\InterfaceMapping;
|
|
use App\Framework\Filesystem\ValueObjects\FilePath;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class MigrationLoaderTest extends TestCase
|
|
{
|
|
private Container $container;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->container = $this->createMock(Container::class);
|
|
}
|
|
|
|
#[Test]
|
|
public function loadMigrations_returns_empty_collection_when_no_migrations_found(): void
|
|
{
|
|
// Setup interface registry with no migrations
|
|
$interfaceRegistry = new InterfaceRegistry();
|
|
|
|
$discoveryRegistry = new DiscoveryRegistry(
|
|
new AttributeRegistry(),
|
|
$interfaceRegistry,
|
|
new RouteRegistry(),
|
|
new TemplateRegistry()
|
|
);
|
|
|
|
$loader = new MigrationLoader($discoveryRegistry, $this->container);
|
|
|
|
$result = $loader->loadMigrations();
|
|
|
|
$this->assertInstanceOf(MigrationCollection::class, $result);
|
|
$this->assertTrue($result->isEmpty());
|
|
$this->assertSame(0, $result->count());
|
|
}
|
|
|
|
#[Test]
|
|
public function loadMigrations_returns_sorted_collection_of_migrations(): void
|
|
{
|
|
// Create mock migration instances (in unsorted order)
|
|
$migration1 = $this->createMockMigration('2024_01_03_120000', 'Third migration');
|
|
$migration2 = $this->createMockMigration('2024_01_01_120000', 'First migration');
|
|
$migration3 = $this->createMockMigration('2024_01_02_120000', 'Second migration');
|
|
|
|
// Create interface mappings
|
|
$mapping1 = new InterfaceMapping(
|
|
ClassName::create(Migration::class),
|
|
ClassName::create('App\\Migration1'),
|
|
FilePath::create('/path/to/migration1.php')
|
|
);
|
|
$mapping2 = new InterfaceMapping(
|
|
ClassName::create(Migration::class),
|
|
ClassName::create('App\\Migration2'),
|
|
FilePath::create('/path/to/migration2.php')
|
|
);
|
|
$mapping3 = new InterfaceMapping(
|
|
ClassName::create(Migration::class),
|
|
ClassName::create('App\\Migration3'),
|
|
FilePath::create('/path/to/migration3.php')
|
|
);
|
|
|
|
// Setup interface registry
|
|
$interfaceRegistry = new InterfaceRegistry();
|
|
$interfaceRegistry->add($mapping1);
|
|
$interfaceRegistry->add($mapping2);
|
|
$interfaceRegistry->add($mapping3);
|
|
|
|
$discoveryRegistry = new DiscoveryRegistry(
|
|
new AttributeRegistry(),
|
|
$interfaceRegistry,
|
|
new RouteRegistry(),
|
|
new TemplateRegistry()
|
|
);
|
|
|
|
$loader = new MigrationLoader($discoveryRegistry, $this->container);
|
|
|
|
// Setup container to return migration instances
|
|
$this->container->method('get')
|
|
->willReturnMap([
|
|
['App\\Migration1', $migration1],
|
|
['App\\Migration2', $migration2],
|
|
['App\\Migration3', $migration3],
|
|
]);
|
|
|
|
$result = $loader->loadMigrations();
|
|
|
|
$this->assertInstanceOf(MigrationCollection::class, $result);
|
|
$this->assertSame(3, $result->count());
|
|
|
|
// Verify migrations are sorted by version
|
|
$migrations = $result->toArray();
|
|
$this->assertSame($migration2, $migrations[0]); // 2024_01_01_120000
|
|
$this->assertSame($migration3, $migrations[1]); // 2024_01_02_120000
|
|
$this->assertSame($migration1, $migrations[2]); // 2024_01_03_120000
|
|
}
|
|
|
|
#[Test]
|
|
public function loadMigrations_uses_discovery_registry_to_find_migration_implementations(): void
|
|
{
|
|
$interfaceRegistry = new InterfaceRegistry();
|
|
|
|
$discoveryRegistry = new DiscoveryRegistry(
|
|
new AttributeRegistry(),
|
|
$interfaceRegistry,
|
|
new RouteRegistry(),
|
|
new TemplateRegistry()
|
|
);
|
|
|
|
$loader = new MigrationLoader($discoveryRegistry, $this->container);
|
|
|
|
$result = $loader->loadMigrations();
|
|
|
|
// Verify that the discovery registry was used by checking we get an empty collection
|
|
$this->assertInstanceOf(MigrationCollection::class, $result);
|
|
$this->assertTrue($result->isEmpty());
|
|
}
|
|
|
|
#[Test]
|
|
public function loadMigrations_uses_container_to_instantiate_migrations(): void
|
|
{
|
|
$migration = $this->createMockMigration('2024_01_01_120000', 'Test migration');
|
|
|
|
$mapping = new InterfaceMapping(
|
|
ClassName::create(Migration::class),
|
|
ClassName::create('App\\TestMigration'),
|
|
FilePath::create('/path/to/test_migration.php')
|
|
);
|
|
|
|
$interfaceRegistry = new InterfaceRegistry();
|
|
$interfaceRegistry->add($mapping);
|
|
|
|
$discoveryRegistry = new DiscoveryRegistry(
|
|
new AttributeRegistry(),
|
|
$interfaceRegistry,
|
|
new RouteRegistry(),
|
|
new TemplateRegistry()
|
|
);
|
|
|
|
$loader = new MigrationLoader($discoveryRegistry, $this->container);
|
|
|
|
$this->container->expects($this->once())
|
|
->method('get')
|
|
->with('App\\TestMigration')
|
|
->willReturn($migration);
|
|
|
|
$result = $loader->loadMigrations();
|
|
|
|
$this->assertSame(1, $result->count());
|
|
$this->assertSame($migration, $result->toArray()[0]);
|
|
}
|
|
|
|
private function createMockMigration(string $version, string $description): Migration
|
|
{
|
|
$migration = $this->createMock(Migration::class);
|
|
$migration->method('getVersion')
|
|
->willReturn(MigrationVersion::fromTimestamp($version));
|
|
$migration->method('getDescription')
|
|
->willReturn($description);
|
|
|
|
return $migration;
|
|
}
|
|
}
|