Skip to content
Snippets Groups Projects
Unverified Commit d58c8704 authored by Thomas Flori's avatar Thomas Flori
Browse files

test filtering and sorting of migrations

parent fc174f95
No related branches found
No related tags found
No related merge requests found
Showing
with 330 additions and 20 deletions
......@@ -2,6 +2,8 @@
namespace Breyta;
use Breyta\Migration\CreateMigrationTable;
class Migrations
{
/** @var \PDO */
......@@ -13,6 +15,9 @@ class Migrations
/** @var array */
protected $migrations;
/** @var array */
protected $classes;
public function __construct(\PDO $db, string $path)
{
if (!file_exists($path) || !is_dir($path)) {
......@@ -20,7 +25,7 @@ class Migrations
}
$this->db = $db;
$this->path = $path;
$this->path = rtrim($path, '/');
}
public function getStatus(): \stdClass
......@@ -42,20 +47,120 @@ class Migrations
protected function findMigrations(): array
{
$this->classes['@breyta/CreateMigrationTable.php'] = CreateMigrationTable::class;
$migrations = [(object)[
'file' => '@breyta/CreateMigrationTable.php',
'status' => 'new',
]];
$migrations = array_merge($migrations, array_map(function ($path) {
return (object)[
'file' => basename($path),
'status' => 'new',
/** @var \SplFileInfo $fileInfo */
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->path)) as $fileInfo) {
if (is_dir($fileInfo->getPathname()) ||
$fileInfo->getFilename()[0] === '.' ||
substr($fileInfo->getFilename(), -4) !== '.php'
) {
continue;
}
$className = $this->getClassFromFile($fileInfo->getPathname());
if (!$className) {
continue;
}
require_once $fileInfo->getPathname();
if (!is_subclass_of($className, AbstractMigration::class)) {
continue;
}
$file = substr($fileInfo->getPathname(), strlen($this->path) + 1);
$this->classes[$file] = $className;
$migrations[] = (object)[
'file' => $file,
'status' => 'new'
];
}, array_filter(scandir($this->path), function ($path) {
return $path !== '..' && $path !== '.';
})));
}
usort($migrations, function ($left, $right) {
// sort criteria 1: is from breyta
$leftIsFromBreyta = substr($left->file, 0, 8) === '@breyta/';
$rightIsFromBreyta = substr($right->file, 0, 8) === '@breyta/';
if ($leftIsFromBreyta !== $rightIsFromBreyta) {
return $rightIsFromBreyta - $leftIsFromBreyta;
}
$leftBaseName = basename($left->file);
$rightBaseName = basename($right->file);
// sort criteria 2: has creation date
$leftHasCreationDate = (int)preg_match(
'/^(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z)_/',
$leftBaseName,
$leftCreationDate
);
$rightHasCreationDate = (int)preg_match(
'/^(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z)_/',
$rightBaseName,
$rightCreationDate
);
if ($leftHasCreationDate !== $rightHasCreationDate) {
return $leftHasCreationDate - $rightHasCreationDate;
}
// sort criteria 3: by creation date
if (@$leftCreationDate[1] !== @$rightCreationDate[1]) {
list($leftDate, $leftTime) = explode('T', $leftCreationDate[1]);
list($rightDate, $rightTime) = explode('T', $rightCreationDate[1]);
list($leftTime, $rightTime) = str_replace('-', ':', [$leftTime, $rightTime]);
return strtotime($leftDate . 'T' . $leftTime) - strtotime($rightDate . 'T' . $rightTime);
}
// sort criteria 4: alphabetically
return strcmp($leftBaseName, $rightBaseName);
});
return $migrations;
}
protected function getClassFromFile(string $path): ?string
{
$fp = fopen($path, 'r');
$buffer = '';
$i = 0;
$class = $namespace = null;
while (!$class) {
if (feof($fp)) {
return null;
}
$buffer .= fread($fp, 512);
$tokens = token_get_all($buffer);
if (strpos($buffer, '{') === false) {
continue;
}
for (; $i < count($tokens); $i++) {
if ($tokens[$i][0] === T_NAMESPACE) {
for ($j = $i + 1; $j < count($tokens); $j++) {
if ($tokens[$j][0] === T_STRING) {
$namespace .= '\\' . $tokens[$j][1];
} else {
if ($tokens[$j] === '{' || $tokens[$j] === ';') {
break;
}
}
}
}
if ($tokens[$i][0] === T_CLASS) {
for ($j = $i + 1; $j < count($tokens); $j++) {
if ($tokens[$j] === '{') {
$class = $tokens[$i+2][1];
}
}
}
}
}
return $class ? $namespace . '\\' . $class : null;
}
}
<?php
namespace Breyta\Test\Example;
use Breyta\AbstractMigration;
class FooBaz extends AbstractMigration
{
/**
* Bring the migration up
*/
public function up(): void
{
// TODO: Implement up() method.
}
/**
* Bring the migration down
*/
public function down(): void
{
// TODO: Implement down() method.
}
}
<?php
<?php
namespace Breyta\Test\Example\Grouped;
use Breyta\AbstractMigration;
class FooBar extends AbstractMigration
{
/**
* Bring the migration up
*/
public function up(): void
{
// TODO: Implement up() method.
}
/**
* Bring the migration down
*/
public function down(): void
{
// TODO: Implement down() method.
}
}
<?php
namespace Breyta\Test\Example\Grouped;
use Breyta\AbstractMigration;
class Bar extends AbstractMigration
{
/**
* Bring the migration up
*/
public function up(): void
{
// TODO: Implement up() method.
}
/**
* Bring the migration down
*/
public function down(): void
{
// TODO: Implement down() method.
}
}
<?php
namespace Breyta\Test\Example\Grouped;
use Breyta\AbstractMigration;
class Foo extends AbstractMigration
{
/**
* Bring the migration up
*/
public function up(): void
{
// TODO: Implement up() method.
}
/**
* Bring the migration down
*/
public function down(): void
{
// TODO: Implement down() method.
}
}
<?php
namespace Breyta\Test\Example\Grouped;
use Breyta\AbstractMigration;
class Anomaly extends AbstractMigration
{
/**
* Bring the migration up
*/
public function up(): void
{
}
/**
* Bring the migration down
*/
public function down(): void
{
}
}
<?php
namespace Breyta\Test\Example\Grouped;
use Breyta\AbstractMigration;
class FamilyTable extends AbstractMigration
{
/**
* Bring the migration up
*/
public function up(): void
{
}
/**
* Bring the migration down
*/
public function down(): void
{
}
}
<?php
namespace Breyta\Test\Example;
class NoMigration
{
}
......@@ -41,18 +41,73 @@ class LocatingMigrationsTest extends TestCase
], $status->migrations, '', false, false);
}
// /** @test */
// public function findsMigrationsInSubFolders()
// {
// $migrations = new Migrations($this->pdo, __DIR__ . '/../Example');
//
// $status = $migrations->getStatus();
//
// self::assertContains((object)[
// 'file' => 'Grouped/2018-11-22T22-59-59_FooBar.php',
// 'status' => 'new',
// ], $status->migrations, '', false, false);
// }
/** @test */
public function findsMigrationsInSubFolders()
{
$migrations = new Migrations($this->pdo, __DIR__ . '/../Example');
$status = $migrations->getStatus();
self::assertContains((object)[
'file' => 'Grouped/2018-11-22T22-59-59Z_FooBar.php',
'status' => 'new',
], $status->migrations, '', false, false);
}
/** @test */
public function filtersFilesWithoutClasses()
{
$migrations = new Migrations($this->pdo, __DIR__ . '/../Example');
$status = $migrations->getStatus();
self::assertNotContains((object)[
'file' => 'EmptyFile.php',
'status' => 'new',
], $status->migrations, '', false, false);
}
/** @test */
public function filtersClassesThatNotExtendAbstractMigration()
{
$migrations = new Migrations($this->pdo, __DIR__ . '/../Example');
$status = $migrations->getStatus();
self::assertNotContains((object)[
'file' => 'NoMigration.php',
'status' => 'new',
], $status->migrations, '', false, false);
}
/** @test */
public function ordersMigrations()
{
$expectedOrder = [
'@breyta/CreateMigrationTable.php', // always first
'Grouped/Anomaly.php', // migrations without timestamps first in alphabetical order
'CreateAnimalsTable.php', // sub folders do not change the order
'Grouped/FamilyTable.php',
'Grouped/2018-11-22T22-59-59Z_FooBar.php', // then by timestamp
'2018-11-22T23-15-31Z_FooBaz.php',
'Grouped/2018-11-22T23-59-50Z_Bar.php', // in alphabetical order when equal
'Grouped/2018-11-22T23-59-50Z_Foo.php',
];
$migrations = new Migrations($this->pdo, __DIR__ . '/../Example');
$status = $migrations->getStatus();
// pluck file names
$migrationFiles = array_map(function ($migration) {
return $migration->file;
}, $status->migrations);
// filter other migrations that might get added to this folder
$migrationFiles = array_filter($migrationFiles, function ($file) use ($expectedOrder) {
return in_array($file, $expectedOrder);
});
self::assertSame($expectedOrder, $migrationFiles);
}
/** @test */
public function throwsWhenTheFolderDoesNotExist()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment