diff --git a/src/app/Console/Commands/OwnerSwapCommand.php b/src/app/Console/Commands/OwnerSwapCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/OwnerSwapCommand.php @@ -0,0 +1,93 @@ +argument('current-user') == $this->argument('target-user')) { + $this->error('Users cannot be the same.'); + return 1; + } + + $user = $this->getUser($this->argument('current-user')); + + if (!$user) { + $this->error('User not found.'); + return 1; + } + + $target = $this->getUser($this->argument('target-user')); + + if (!$target) { + $this->error('User not found.'); + return 1; + } + + $wallet = $user->wallets->first(); + $target_wallet = $target->wallets->first(); + + if ($wallet->id != $target->wallet()->id) { + $this->error('The target user does not belong to the same account.'); + return 1; + } + + Queue::fake(); + + DB::beginTransaction(); + + // Switch wallet for existing entitlements + $wallet->entitlements()->withTrashed()->update(['wallet_id' => $target_wallet->id]); + + // Update target user created_at + $dt = \now()->subMonthsWithoutOverflow(1); + if ($target->created_at >= $dt) { + $target->created_at = $dt; + $target->save(); + } + + // Migrate wallet properties + $target_wallet->balance = $wallet->balance; + $target_wallet->currency = $wallet->currency; + $target_wallet->save(); + + $wallet->balance = 0; + $wallet->save(); + + // Migrate wallet settings + $settings = $wallet->settings()->get(); + + \App\WalletSetting::where('wallet_id', $wallet->id)->delete(); + \App\WalletSetting::where('wallet_id', $target_wallet->id)->delete(); + + foreach ($settings as $setting) { + $target_wallet->setSetting($setting->key, $setting->value); + } + + DB::commit(); + } +} diff --git a/src/tests/Feature/Console/OwnerSwapTest.php b/src/tests/Feature/Console/OwnerSwapTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/OwnerSwapTest.php @@ -0,0 +1,99 @@ +deleteTestUser('user1@owner-swap.com'); + $this->deleteTestUser('user2@owner-swap.com'); + $this->deleteTestDomain('owner-swap.com'); + } + + /** + * {@inheritDoc} + */ + public function tearDown(): void + { + $this->deleteTestUser('user1@owner-swap.com'); + $this->deleteTestUser('user2@owner-swap.com'); + $this->deleteTestDomain('owner-swap.com'); + + parent::tearDown(); + } + + /** + * Test the command + */ + public function testHandle(): void + { + Queue::fake(); + + // Create some sample account + $owner = $this->getTestUser('user1@owner-swap.com'); + $user = $this->getTestUser('user2@owner-swap.com'); + $domain = $this->getTestDomain('owner-swap.com', [ + 'status' => \App\Domain::STATUS_NEW, + 'type' => \App\Domain::TYPE_HOSTED, + ]); + $package_kolab = \App\Package::withEnvTenantContext()->where('title', 'kolab')->first(); + $package_domain = \App\Package::withEnvTenantContext()->where('title', 'domain-hosting')->first(); + $owner->assignPackage($package_kolab); + $owner->assignPackage($package_kolab, $user); + $domain->assignPackage($package_domain, $owner); + $wallet = $owner->wallets()->first(); + $wallet->currency = 'USD'; + $wallet->balance = 100; + $wallet->save(); + $wallet->setSetting('test', 'test'); + $target_wallet = $user->wallets()->first(); + + $entitlements = $wallet->entitlements()->orderBy('id')->pluck('id')->all(); + $this->assertCount(15, $entitlements); + $this->assertSame(0, $target_wallet->entitlements()->count()); + + // Non-existing target user + $code = \Artisan::call("owner:swap user1@owner-swap.com unknown@unknown.org"); + $output = trim(\Artisan::output()); + $this->assertSame(1, $code); + $this->assertSame("User not found.", $output); + + // The same user + $code = \Artisan::call("owner:swap user1@owner-swap.com user1@owner-swap.com"); + $output = trim(\Artisan::output()); + $this->assertSame(1, $code); + $this->assertSame("Users cannot be the same.", $output); + + // Success + $code = \Artisan::call("owner:swap user1@owner-swap.com user2@owner-swap.com"); + $output = trim(\Artisan::output()); + $this->assertSame(0, $code); + $this->assertSame("", $output); + + $user->refresh(); + $target_wallet->refresh(); + $target_entitlements = $target_wallet->entitlements()->orderBy('id')->pluck('id')->all(); + + $this->assertSame($target_entitlements, $entitlements); + $this->assertSame(0, $wallet->entitlements()->count()); + $this->assertSame($wallet->balance, $target_wallet->balance); + $this->assertSame($wallet->currency, $target_wallet->currency); + $this->assertTrue($user->created_at <= \now()->subMonthsWithoutOverflow(1)); + $this->assertSame('test', $target_wallet->getSetting('test')); + + $wallet->refresh(); + $this->assertSame(null, $wallet->getSetting('test')); + $this->assertSame(0, $wallet->balance); + + // TODO: Test case when the target user does not belong to the same account + } +}