db = TestDatabaseFactory::createInMemory(); } public function testRunCreatesMigrationsTable(): void { Migrator::run($this->db); $stmt = $this->db->query("SELECT name FROM sqlite_master WHERE type='table' AND name='migrations'"); self::assertNotFalse($stmt->fetchColumn(), 'La table migrations doit exister après run().'); } public function testRunIsIdempotent(): void { Migrator::run($this->db); Migrator::run($this->db); $this->addToAssertionCount(1); } public function testAlreadyAppliedMigrationIsSkipped(): void { Migrator::run($this->db); $before = $this->countMigrations(); $stmt = $this->db->prepare('INSERT INTO migrations (version, run_at) VALUES (:v, :r)'); $stmt->execute([':v' => '999_future_migration', ':r' => date('Y-m-d H:i:s')]); Migrator::run($this->db); $after = $this->countMigrations(); self::assertSame($before + 1, $after); } public function testRunRecordsModuleMigrationsInTable(): void { Migrator::run($this->db); self::assertGreaterThan(0, $this->countMigrations(), 'Au moins une migration du socle doit être enregistrée.'); } public function testRunCreatesCoreModuleTables(): void { Migrator::run($this->db); foreach (['users', 'password_resets', 'rate_limits', 'settings', 'audit_log', 'notification_dispatches', 'categories', 'media'] as $table) { $stmt = $this->db->query(sprintf("SELECT name FROM sqlite_master WHERE type='table' AND name='%s'", $table)); self::assertNotFalse($stmt->fetchColumn(), sprintf('La table %s doit exister après run().', $table)); } } private function countMigrations(): int { return (int) $this->db->query('SELECT COUNT(*) FROM migrations')->fetchColumn(); } }