Skip to content

Commit 1d50336

Browse files
Bapaweinnocenzi
andauthored
fix(database): ensure natural ordering for migrations (#1541)
Co-authored-by: Enzo Innocenzi <[email protected]>
1 parent 9d0fc5f commit 1d50336

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

packages/database/src/Migrations/RunnableMigrations.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ final class RunnableMigrations implements IteratorAggregate
1919
public function __construct(
2020
private array $migrations = [],
2121
) {
22-
usort($this->migrations, static fn (MigratesUp $a, MigratesUp $b) => $a->name <=> $b->name);
22+
usort($this->migrations, static fn (MigratesUp $a, MigratesUp $b) => strnatcmp($a->name, $b->name));
2323
}
2424

2525
public function getIterator(): Traversable
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Database\Tests\Migrations;
6+
7+
use PHPUnit\Framework\Attributes\DataProvider;
8+
use PHPUnit\Framework\Attributes\Test;
9+
use PHPUnit\Framework\TestCase;
10+
use Tempest\Database\MigratesUp;
11+
use Tempest\Database\Migrations\RunnableMigrations;
12+
use Tempest\Database\QueryStatement;
13+
use Tempest\Database\QueryStatements\RawStatement;
14+
15+
final class RunnableMigrationsTest extends TestCase
16+
{
17+
#[DataProvider('provide_migrations')]
18+
#[Test]
19+
public function migration_ordering(array $migrationNames, array $expectedOrder): void
20+
{
21+
$migrations = array_map(fn (string $name) => $this->createDatabaseMigration($name), $migrationNames);
22+
23+
$this->assertSame(
24+
expected: $expectedOrder,
25+
actual: array_map(
26+
callback: static fn (MigratesUp $migration) => $migration->name,
27+
array: iterator_to_array(new RunnableMigrations($migrations)),
28+
),
29+
);
30+
}
31+
32+
public static function provide_migrations(): array
33+
{
34+
return [
35+
[
36+
'migrationNames' => [
37+
'migration1_1',
38+
'migration1_10',
39+
'2025-10-12_create_idbn_table',
40+
'migration1_2',
41+
'2025-01-12_create_author_table',
42+
'2025-08-10_create_user_table',
43+
'2025-08-01_create_book_table',
44+
'2025-08-12_create_chapter_table',
45+
],
46+
'expectedOrder' => [
47+
'2025-01-12_create_author_table',
48+
'2025-08-01_create_book_table',
49+
'2025-08-10_create_user_table',
50+
'2025-08-12_create_chapter_table',
51+
'2025-10-12_create_idbn_table',
52+
'migration1_1',
53+
'migration1_2',
54+
'migration1_10',
55+
],
56+
],
57+
];
58+
}
59+
60+
private function createDatabaseMigration(string $name): MigratesUp
61+
{
62+
return new class($name) implements MigratesUp {
63+
public function __construct(
64+
public string $name,
65+
) {}
66+
67+
public function up(): QueryStatement
68+
{
69+
return new RawStatement('SELECT 1');
70+
}
71+
};
72+
}
73+
}

0 commit comments

Comments
 (0)