From d00580f32d4cfab63dd4c17c8d0e8a3a9f796659 Mon Sep 17 00:00:00 2001 From: Vlad Chiovianu Date: Fri, 24 Nov 2023 18:07:30 +0200 Subject: [PATCH 1/3] wip on extra feature; requires tests --- src/Contracts/RenamesMigrations.php | 10 +++ src/Services/CustomUpgrade.php | 43 +++++++++++ src/Services/Finder.php | 20 +++-- src/Services/Migrations.php | 39 ++++++++++ src/Services/Package.php | 2 + src/Services/Reflection.php | 4 +- src/Services/Structure.php | 49 +------------ tests/units/MigrationsUpgradeTest.php | 102 ++++++++++++++++++++++++++ 8 files changed, 217 insertions(+), 52 deletions(-) create mode 100644 src/Contracts/RenamesMigrations.php create mode 100644 src/Services/CustomUpgrade.php create mode 100644 src/Services/Migrations.php create mode 100644 tests/units/MigrationsUpgradeTest.php diff --git a/src/Contracts/RenamesMigrations.php b/src/Contracts/RenamesMigrations.php new file mode 100644 index 0000000..907db52 --- /dev/null +++ b/src/Contracts/RenamesMigrations.php @@ -0,0 +1,10 @@ +upgrade; + } + + public function priority(): int + { + return $this->upgrade instanceof Prioritization + ? $this->upgrade->priority() + : Prioritization::Default; + } + + public function migratePostDataMigration(): void + { + if ($this->upgrade instanceof MigratesPostDataMigration) { + $this->upgrade->migratePostDataMigration(); + } + } + + public function applicable(): bool + { + return ! $this->upgrade instanceof Applicable + || $this->upgrade->applicable(); + } +} diff --git a/src/Services/Finder.php b/src/Services/Finder.php index 8daf7c4..fc21d1d 100644 --- a/src/Services/Finder.php +++ b/src/Services/Finder.php @@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\File; use LaravelEnso\Upgrade\Contracts\MigratesStructure; +use LaravelEnso\Upgrade\Contracts\RenamesMigrations; class Finder { @@ -30,10 +31,19 @@ private function upgradePackages(): Collection private function upgradeClasses(Package $package): Collection { - return $package->upgradeClasses() - ->map(fn ($class) => new $class) - ->map(fn ($upgrade) => $upgrade instanceof MigratesStructure - ? new Structure($upgrade) - : new $upgrade); + return $package->upgradeClasses()->map(fn ($class) => $this->upgrade($class)); + } + + private function upgrade(string $class) + { + $upgrade = new $class(); + + if ($upgrade instanceof MigratesStructure) { + return new Structure($upgrade); + } elseif ($upgrade instanceof RenamesMigrations) { + return new Migrations($upgrade); + } + + return $upgrade; } } diff --git a/src/Services/Migrations.php b/src/Services/Migrations.php new file mode 100644 index 0000000..16a1ca5 --- /dev/null +++ b/src/Services/Migrations.php @@ -0,0 +1,39 @@ +upgrade->to()) + ->every(fn ($to) => DB::table('migrations')->whereMigration($to) + ->exists()); + } + + public function migrateData(): void + { + $to = Collection::wrap($this->upgrade->to())->sortKey(); + $from = Collection::wrap($this->upgrade->from())->sortKeys(); + + $invalidMapping = $to->count() !== $from->count() + || $to->keys()->diff($from->keys())->isNotEmpty(); + + if ($invalidMapping) { + $message = 'Invalid number of elements or distinct keys in "from" and "to" arrays'; + throw new EnsoException($message); + } + + $to->combine($from) + ->filter(fn ($from) => DB::table('migrations') + ->whereMigration($from) + ->exists()) + ->each(fn ($from, $to) => DB::table('migrations') + ->whereMigration($from) + ->update(['migration' => $to])); + } +} diff --git a/src/Services/Package.php b/src/Services/Package.php index 80ddddc..83165cd 100644 --- a/src/Services/Package.php +++ b/src/Services/Package.php @@ -7,6 +7,7 @@ use Illuminate\Support\Str; use LaravelEnso\Helpers\Services\JsonReader; use LaravelEnso\Upgrade\Contracts\MigratesStructure; +use LaravelEnso\Upgrade\Contracts\RenamesMigrations; use LaravelEnso\Upgrade\Contracts\Upgrade; use ReflectionClass; use Symfony\Component\Finder\SplFileInfo; @@ -87,6 +88,7 @@ private function isUpgrade($class): bool $reflection = new ReflectionClass($class); return $reflection->implementsInterface(MigratesStructure::class) + || $reflection->implementsInterface(RenamesMigrations::class) || $reflection->implementsInterface(Upgrade::class); } } diff --git a/src/Services/Reflection.php b/src/Services/Reflection.php index fa1dd14..6954f0b 100644 --- a/src/Services/Reflection.php +++ b/src/Services/Reflection.php @@ -12,8 +12,8 @@ class Reflection { public static function reflection(Upgrade $upgrade): ReflectionClass { - return $upgrade instanceof Structure - ? $upgrade->reflection() + return $upgrade instanceof Structure || $upgrade instanceof Migrations + ? new ReflectionClass($upgrade->class()) : new ReflectionClass($upgrade); } diff --git a/src/Services/Structure.php b/src/Services/Structure.php index 6a4c69e..67b712e 100644 --- a/src/Services/Structure.php +++ b/src/Services/Structure.php @@ -8,26 +8,11 @@ use Illuminate\Support\Facades\Config; use LaravelEnso\Permissions\Models\Permission; use LaravelEnso\Roles\Models\Role; -use LaravelEnso\Upgrade\Contracts\Applicable; -use LaravelEnso\Upgrade\Contracts\MigratesData; -use LaravelEnso\Upgrade\Contracts\MigratesPostDataMigration; -use LaravelEnso\Upgrade\Contracts\MigratesStructure; -use LaravelEnso\Upgrade\Contracts\Prioritization; -use LaravelEnso\Upgrade\Contracts\Upgrade; -use ReflectionClass; -class Structure implements Upgrade, MigratesData, Prioritization, MigratesPostDataMigration, Applicable +class Structure extends CustomUpgrade { - private MigratesStructure $upgrade; private Collection $existing; private Collection $roles; - private string $defaultRole; - - public function __construct(MigratesStructure $upgrade) - { - $this->upgrade = $upgrade; - $this->defaultRole = Config::get('enso.config.defaultRole'); - } public function isMigrated(): bool { @@ -50,36 +35,11 @@ public function migrateData(): void if (App::isLocal()) { $this->roles() - ->reject(fn ($role) => $role->name === $this->defaultRole) + ->reject(fn ($role) => $role->name === Config::get('enso.config.defaultRole')) ->each->writeConfig(); } } - public function reflection() - { - return new ReflectionClass($this->upgrade); - } - - public function priority(): int - { - return $this->upgrade instanceof Prioritization - ? $this->upgrade->priority() - : Prioritization::Default; - } - - public function migratePostDataMigration(): void - { - if ($this->upgrade instanceof MigratesPostDataMigration) { - $this->upgrade->migratePostDataMigration(); - } - } - - public function applicable(): bool - { - return ! $this->upgrade instanceof Applicable - || $this->upgrade->applicable(); - } - private function storeWithRoles(array $permission): void { $permission = Permission::create($permission); @@ -90,9 +50,8 @@ private function storeWithRoles(array $permission): void private function syncRoles(Permission $permission): Collection { - return $this->roles()->when(! $permission->is_default, fn ($roles) => $roles - ->filter(fn ($role) => in_array($role->name, $this->upgrade->roles()) - || $role->name === $this->defaultRole)); + return $this->roles()->when(! $permission->is_default, fn ($roles) => $roles->filter(fn ($role) => in_array($role->name, $this->upgrade->roles()) + || $role->name === Config::get('enso.config.defaultRole'))); } private function roles(): Collection diff --git a/tests/units/MigrationsUpgradeTest.php b/tests/units/MigrationsUpgradeTest.php new file mode 100644 index 0000000..caed37d --- /dev/null +++ b/tests/units/MigrationsUpgradeTest.php @@ -0,0 +1,102 @@ +upgrade = new TestStructureMigration(); + + $this->defaultRole = $this->role(Config::get('enso.config.defaultRole')); + + $this->secondaryRole = $this->role('secondaryRole'); + } + + /** @test */ + public function can_migrate() + { + $this->upgrade->permissions = [ + ['name' => 'test', 'description' => 'test', 'is_default' => true], + ]; + + $this->migrateStructure(); + + $this->assertTrue(Permission::whereName('test')->exists()); + } + + /** @test */ + public function can_migrate_default_permission() + { + $this->upgrade->permissions = [ + ['name' => 'test', 'description' => 'test', 'is_default' => true], + ]; + + $this->migrateStructure(); + + $this->assertEquals('test', $this->defaultRole->permissions->first()->name); + $this->assertEquals('test', $this->secondaryRole->permissions->first()->name); + } + + /** @test */ + public function can_migrate_non_default_permission() + { + $this->upgrade->permissions = [ + ['name' => 'test', 'description' => 'test', 'is_default' => false], + ]; + + $this->migrateStructure(); + + $this->assertEquals('test', $this->defaultRole->permissions->first()->name); + $this->assertEmpty($this->secondaryRole->permissions); + } + + /** @test */ + public function skips_existing_permissions() + { + $this->upgrade->permissions = [ + ['name' => 'test', 'description' => 'test', 'is_default' => true], + ]; + + $this->migrateStructure(); + $this->migrateStructure(); + + $this->assertEquals(1, Permission::whereName('test')->count()); + } + + protected function role($name) + { + return Role::factory()->create([ + 'name' => $name, + ]); + } + + private function migrateStructure() + { + (new Database(new Structure($this->upgrade)))->handle(); + } +} + +class TestStructureMigration implements MigratesStructure +{ + use StructureMigration; + + public $permissions = []; +} From bcc7ad6b28d1db1a8539e38f6443cfbdfd9c0714 Mon Sep 17 00:00:00 2001 From: Vlad Chiovianu Date: Fri, 24 Nov 2023 18:28:29 +0200 Subject: [PATCH 2/3] wip --- src/Services/Migrations.php | 2 +- tests/units/MigrationsUpgradeTest.php | 61 ++++++++++++--------------- 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/src/Services/Migrations.php b/src/Services/Migrations.php index 16a1ca5..151fb15 100644 --- a/src/Services/Migrations.php +++ b/src/Services/Migrations.php @@ -17,7 +17,7 @@ public function isMigrated(): bool public function migrateData(): void { - $to = Collection::wrap($this->upgrade->to())->sortKey(); + $to = Collection::wrap($this->upgrade->to())->sortKeys(); $from = Collection::wrap($this->upgrade->from())->sortKeys(); $invalidMapping = $to->count() !== $from->count() diff --git a/tests/units/MigrationsUpgradeTest.php b/tests/units/MigrationsUpgradeTest.php index caed37d..620c295 100644 --- a/tests/units/MigrationsUpgradeTest.php +++ b/tests/units/MigrationsUpgradeTest.php @@ -1,45 +1,37 @@ upgrade = new TestStructureMigration(); + // protected function setUp(): void + // { + // parent::setUp(); - $this->defaultRole = $this->role(Config::get('enso.config.defaultRole')); - - $this->secondaryRole = $this->role('secondaryRole'); - } + // $this->upgrade = new TestRenamesMigrations(); + // } /** @test */ - public function can_migrate() + public function will_throw_exception_on_argument_count_mismatch() { - $this->upgrade->permissions = [ - ['name' => 'test', 'description' => 'test', 'is_default' => true], - ]; + $this->expectException(EnsoException::class); - $this->migrateStructure(); + $mock = $this->createMock(TestRenamesMigrations::class); - $this->assertTrue(Permission::whereName('test')->exists()); + $mock->method('to')->willReturn(['foo']); + $mock->method('from')->willReturn(['foo', 'bar']); + + $this->migrateStructure($mock); } /** @test */ @@ -81,22 +73,21 @@ public function skips_existing_permissions() $this->assertEquals(1, Permission::whereName('test')->count()); } - protected function role($name) - { - return Role::factory()->create([ - 'name' => $name, - ]); - } - - private function migrateStructure() + private function migrateStructure($mock) { - (new Database(new Structure($this->upgrade)))->handle(); + (new Database(new Migrations($mock)))->handle(); } } -class TestStructureMigration implements MigratesStructure +class TestRenamesMigrations implements RenamesMigrations { - use StructureMigration; + public function from(): array + { + return []; + } - public $permissions = []; + public function to(): array + { + return []; + } } From ec8814c7c1d6c3005ab6a210ee5ca9dae074030c Mon Sep 17 00:00:00 2001 From: Vlad Chiovianu Date: Thu, 11 Jan 2024 23:08:30 +0200 Subject: [PATCH 3/3] finishes --- src/Services/Migrations.php | 9 ++-- tests/units/MigrationsUpgradeTest.php | 73 ++++++++++++--------------- tests/units/StructureUpgradeTest.php | 22 ++++---- 3 files changed, 47 insertions(+), 57 deletions(-) diff --git a/src/Services/Migrations.php b/src/Services/Migrations.php index 151fb15..b7ff44d 100644 --- a/src/Services/Migrations.php +++ b/src/Services/Migrations.php @@ -10,15 +10,14 @@ class Migrations extends CustomUpgrade { public function isMigrated(): bool { - return Collection::wrap($this->upgrade->to()) - ->every(fn ($to) => DB::table('migrations')->whereMigration($to) - ->exists()); + return Collection::wrap($this->class()->to()) + ->every(fn ($to) => DB::table('migrations')->whereMigration($to)->exists()); } public function migrateData(): void { - $to = Collection::wrap($this->upgrade->to())->sortKeys(); - $from = Collection::wrap($this->upgrade->from())->sortKeys(); + $to = Collection::wrap($this->class()->to())->sortKeys(); + $from = Collection::wrap($this->class()->from())->sortKeys(); $invalidMapping = $to->count() !== $from->count() || $to->keys()->diff($from->keys())->isNotEmpty(); diff --git a/tests/units/MigrationsUpgradeTest.php b/tests/units/MigrationsUpgradeTest.php index 620c295..d347db3 100644 --- a/tests/units/MigrationsUpgradeTest.php +++ b/tests/units/MigrationsUpgradeTest.php @@ -2,7 +2,6 @@ use Illuminate\Foundation\Testing\RefreshDatabase; use LaravelEnso\Helpers\Exceptions\EnsoException; -use LaravelEnso\Permissions\Models\Permission; use LaravelEnso\Upgrade\Contracts\RenamesMigrations; use LaravelEnso\Upgrade\Services\Database; use LaravelEnso\Upgrade\Services\Migrations; @@ -12,70 +11,64 @@ class MigrationsUpgradeTest extends TestCase { use RefreshDatabase; - // protected RenamesMigrations $upgrade; - - // protected function setUp(): void - // { - // parent::setUp(); + protected function setUp(): void + { + parent::setUp(); - // $this->upgrade = new TestRenamesMigrations(); - // } + $this->mock = $this->createMock(TestRenamesMigrations::class); + } /** @test */ - public function will_throw_exception_on_argument_count_mismatch() + public function can_rename_migrations() { - $this->expectException(EnsoException::class); + $this->createMigration('bar'); - $mock = $this->createMock(TestRenamesMigrations::class); + $this->mock->method('from')->willReturn(['bar']); + $this->mock->method('to')->willReturn(['foo']); - $mock->method('to')->willReturn(['foo']); - $mock->method('from')->willReturn(['foo', 'bar']); + $this->migrateStructure(); - $this->migrateStructure($mock); + $this->assertTrue(DB::table('migrations')->whereMigration('foo')->exists()); } /** @test */ - public function can_migrate_default_permission() + public function will_throw_exception_on_argument_count_mismatch() { - $this->upgrade->permissions = [ - ['name' => 'test', 'description' => 'test', 'is_default' => true], - ]; + $this->expectException(EnsoException::class); - $this->migrateStructure(); + $this->mock->method('to')->willReturn(['foo']); + $this->mock->method('from')->willReturn(['foo', 'bar']); - $this->assertEquals('test', $this->defaultRole->permissions->first()->name); - $this->assertEquals('test', $this->secondaryRole->permissions->first()->name); + $this->migrateStructure(); } /** @test */ - public function can_migrate_non_default_permission() + public function will_not_migrate_data_if_all_to_migrations_exist() { - $this->upgrade->permissions = [ - ['name' => 'test', 'description' => 'test', 'is_default' => false], - ]; + $this->createMigration('foo'); + $this->createMigration('qux'); - $this->migrateStructure(); + $this->mock->method('from')->willReturn(['bar', 'baz']); + $this->mock->method('to')->willReturn(['foo', 'qux']); - $this->assertEquals('test', $this->defaultRole->permissions->first()->name); - $this->assertEmpty($this->secondaryRole->permissions); + $service = Mockery::mock(Migrations::class, [$this->mock]); + $service->expects()->class()->andReturn($this->mock)->twice(); + $service->expects()->isMigrated()->andReturn(true); + + (new Database($service))->handle(); } - /** @test */ - public function skips_existing_permissions() + private function migrateStructure() { - $this->upgrade->permissions = [ - ['name' => 'test', 'description' => 'test', 'is_default' => true], - ]; - - $this->migrateStructure(); - $this->migrateStructure(); - - $this->assertEquals(1, Permission::whereName('test')->count()); + (new Database(new Migrations($this->mock)))->handle(); } - private function migrateStructure($mock) + private function createMigration(string $name): void { - (new Database(new Migrations($mock)))->handle(); + DB::table('migrations')->insert([ + 'migration' => $name, + 'batch' => 1, + ]); } } diff --git a/tests/units/StructureUpgradeTest.php b/tests/units/StructureUpgradeTest.php index 3339d2a..c966599 100644 --- a/tests/units/StructureUpgradeTest.php +++ b/tests/units/StructureUpgradeTest.php @@ -22,7 +22,7 @@ protected function setUp(): void { parent::setUp(); - $this->upgrade = new TestStructureMigration(); + $this->mock = $this->createMock(TestStructureMigration::class); $this->defaultRole = $this->role(Config::get('enso.config.defaultRole')); @@ -32,9 +32,9 @@ protected function setUp(): void /** @test */ public function can_migrate() { - $this->upgrade->permissions = [ + $this->mock->method('permissions')->willReturn([ ['name' => 'test', 'description' => 'test', 'is_default' => true], - ]; + ]); $this->migrateStructure(); @@ -44,9 +44,9 @@ public function can_migrate() /** @test */ public function can_migrate_default_permission() { - $this->upgrade->permissions = [ + $this->mock->method('permissions')->willReturn([ ['name' => 'test', 'description' => 'test', 'is_default' => true], - ]; + ]); $this->migrateStructure(); @@ -57,9 +57,9 @@ public function can_migrate_default_permission() /** @test */ public function can_migrate_non_default_permission() { - $this->upgrade->permissions = [ + $this->mock->method('permissions')->willReturn([ ['name' => 'test', 'description' => 'test', 'is_default' => false], - ]; + ]); $this->migrateStructure(); @@ -70,9 +70,9 @@ public function can_migrate_non_default_permission() /** @test */ public function skips_existing_permissions() { - $this->upgrade->permissions = [ + $this->mock->method('permissions')->willReturn([ ['name' => 'test', 'description' => 'test', 'is_default' => true], - ]; + ]); $this->migrateStructure(); $this->migrateStructure(); @@ -89,13 +89,11 @@ protected function role($name) private function migrateStructure() { - (new Database(new Structure($this->upgrade)))->handle(); + (new Database(new Structure($this->mock)))->handle(); } } class TestStructureMigration implements MigratesStructure { use StructureMigration; - - public $permissions = []; }