| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- <?php
- /*
- * Copyright 2026 Alessandro Chiapparini <sprint_planer_web@chiapparini.org>
- * SPDX-License-Identifier: Apache-2.0
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * See the LICENSE file in the project root for the full license text.
- */
- declare(strict_types=1);
- namespace App\Tests\Db;
- use App\Db\Migrator;
- use PDO;
- use PHPUnit\Framework\TestCase;
- /**
- * R01-N22: pin the contract that `Migrator::pendingFiles()` lets the request
- * path detect a deploy-skipped migration without applying SQL itself, and
- * that `migrate()` is still the apply path used by `bin/migrate.php`.
- */
- final class MigratorTest extends TestCase
- {
- private string $tmpDir;
- protected function setUp(): void
- {
- $this->tmpDir = sys_get_temp_dir() . '/spw-migrator-' . bin2hex(random_bytes(4));
- if (!mkdir($this->tmpDir) && !is_dir($this->tmpDir)) {
- $this->fail('cannot mkdir tmp');
- }
- }
- protected function tearDown(): void
- {
- foreach (glob($this->tmpDir . '/*') ?: [] as $f) {
- @unlink($f);
- }
- @rmdir($this->tmpDir);
- }
- private function makePdo(): PDO
- {
- $pdo = new PDO('sqlite::memory:', null, null, [
- PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
- PDO::ATTR_EMULATE_PREPARES => false,
- ]);
- $pdo->exec('PRAGMA foreign_keys = ON');
- return $pdo;
- }
- private function writeMigration(string $name, string $sql): void
- {
- file_put_contents($this->tmpDir . DIRECTORY_SEPARATOR . $name, $sql);
- }
- public function testPendingFilesReturnsEverythingOnVirginDb(): void
- {
- $this->writeMigration('001_init.sql', 'CREATE TABLE a (x INTEGER);');
- $this->writeMigration('002_more.sql', 'CREATE TABLE b (x INTEGER);');
- $migrator = new Migrator($this->makePdo(), $this->tmpDir);
- $this->assertSame(['001_init.sql', '002_more.sql'], $migrator->pendingFiles());
- }
- public function testPendingFilesIsEmptyAfterMigrate(): void
- {
- $this->writeMigration('001_init.sql', 'CREATE TABLE a (x INTEGER);');
- $this->writeMigration('002_more.sql', 'CREATE TABLE b (x INTEGER);');
- $migrator = new Migrator($this->makePdo(), $this->tmpDir);
- $migrator->migrate();
- $this->assertSame([], $migrator->pendingFiles());
- $this->assertSame(2, $migrator->currentVersion());
- }
- public function testPendingFilesReportsOnlyNewlyAdded(): void
- {
- $this->writeMigration('001_init.sql', 'CREATE TABLE a (x INTEGER);');
- $pdo = $this->makePdo();
- (new Migrator($pdo, $this->tmpDir))->migrate();
- // Operator dropped a new file in but didn't re-run migrate.
- $this->writeMigration('002_more.sql', 'CREATE TABLE b (x INTEGER);');
- $migrator = new Migrator($pdo, $this->tmpDir);
- $this->assertSame(['002_more.sql'], $migrator->pendingFiles());
- }
- public function testPendingFilesIgnoresFilesWithoutVersionPrefix(): void
- {
- $this->writeMigration('001_init.sql', 'CREATE TABLE a (x INTEGER);');
- $this->writeMigration('README.md', '# notes');
- $this->writeMigration('foo_bar.sql', 'CREATE TABLE c (x INTEGER);');
- $migrator = new Migrator($this->makePdo(), $this->tmpDir);
- $this->assertSame(['001_init.sql'], $migrator->pendingFiles());
- }
- public function testPendingFilesCreatesSchemaVersionTable(): void
- {
- // Empty dir: even without any migrations the call must not blow up
- // and the version table must exist (idempotent ensure).
- $pdo = $this->makePdo();
- $migrator = new Migrator($pdo, $this->tmpDir);
- $this->assertSame([], $migrator->pendingFiles());
- $row = $pdo->query("SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'")->fetch();
- $this->assertIsArray($row);
- $this->assertSame('schema_version', $row['name']);
- }
- public function testRealMigrationsDirectoryHasNoPendingAfterFullApply(): void
- {
- // Smoke test against the real migrations/ folder so a future reviewer
- // can't drop a malformed file in without the suite noticing.
- $pdo = $this->makePdo();
- $migrator = new Migrator($pdo);
- $migrator->migrate();
- $this->assertSame([], $migrator->pendingFiles(), 'real migrations apply cleanly');
- }
- }
|