diff --git a/app/Http/Controllers/App/Settings/ProfileController.php b/app/Http/Controllers/App/Settings/ProfileController.php index 0d7273be..880927ab 100644 --- a/app/Http/Controllers/App/Settings/ProfileController.php +++ b/app/Http/Controllers/App/Settings/ProfileController.php @@ -94,15 +94,7 @@ public function destroy(ProfileDeleteRequest $request): RedirectResponse $user->update(['current_workspace_id' => null]); $account = $user->account; - - // Cancel account subscription if exists - if ($account && $account->subscribed(Account::SUBSCRIPTION_NAME)) { - $account->subscription(Account::SUBSCRIPTION_NAME)->cancelNow(); - } - - if ($account) { - $account->subscriptions()->delete(); - } + $isOwner = $user->isAccountOwner(); $ownedWorkspaces = Workspace::where('user_id', $user->id)->get(); @@ -126,7 +118,12 @@ public function destroy(ProfileDeleteRequest $request): RedirectResponse $user->workspaces()->detach(); - if ($account) { + if ($account && $isOwner) { + if ($account->subscribed(Account::SUBSCRIPTION_NAME)) { + $account->subscription(Account::SUBSCRIPTION_NAME)->cancelNow(); + } + + $account->subscriptions()->delete(); $account->delete(); } }); diff --git a/tests/Feature/Settings/ProfileUpdateTest.php b/tests/Feature/Settings/ProfileUpdateTest.php index 1955360a..60786517 100644 --- a/tests/Feature/Settings/ProfileUpdateTest.php +++ b/tests/Feature/Settings/ProfileUpdateTest.php @@ -3,6 +3,7 @@ declare(strict_types=1); use App\Enums\UserWorkspace\Role; +use App\Models\Account; use App\Models\User; use App\Models\Workspace; use Illuminate\Http\UploadedFile; @@ -250,3 +251,65 @@ $response->assertRedirect(route('login')); }); + +test('member deleting profile does NOT destroy the shared account', function (bool $selfHosted) { + config()->set('trypost.self_hosted', $selfHosted); + + $owner = User::factory()->create(); + $member = User::factory()->create(['account_id' => $owner->account_id]); + + $workspace = Workspace::factory()->create([ + 'account_id' => $owner->account_id, + 'user_id' => $owner->id, + ]); + $owner->workspaces()->attach($workspace->id, ['role' => Role::Member->value]); + $member->workspaces()->attach($workspace->id, ['role' => Role::Member->value]); + + $this->actingAs($member)->delete(route('app.profile.destroy'), [ + 'password' => 'password', + ]); + + expect($member->fresh())->toBeNull(); + expect(Account::find($owner->account_id))->not->toBeNull(); + expect(Workspace::find($workspace->id))->not->toBeNull(); + expect($owner->fresh())->not->toBeNull(); +})->with([true, false]); + +test('member deleting profile detaches them from workspaces', function (bool $selfHosted) { + config()->set('trypost.self_hosted', $selfHosted); + + $owner = User::factory()->create(); + $member = User::factory()->create(['account_id' => $owner->account_id]); + + $workspace = Workspace::factory()->create([ + 'account_id' => $owner->account_id, + 'user_id' => $owner->id, + ]); + $member->workspaces()->attach($workspace->id, ['role' => Role::Member->value]); + + $this->actingAs($member)->delete(route('app.profile.destroy'), [ + 'password' => 'password', + ]); + + expect($workspace->fresh()->members()->where('users.id', $member->id)->exists())->toBeFalse(); +})->with([true, false]); + +test('owner deleting profile destroys the account and cascades', function (bool $selfHosted) { + config()->set('trypost.self_hosted', $selfHosted); + + $owner = User::factory()->create(); + $accountId = $owner->account_id; + + $workspace = Workspace::factory()->create([ + 'account_id' => $accountId, + 'user_id' => $owner->id, + ]); + $owner->workspaces()->attach($workspace->id, ['role' => Role::Member->value]); + + $this->actingAs($owner)->delete(route('app.profile.destroy'), [ + 'password' => 'password', + ]); + + expect(Account::find($accountId))->toBeNull(); + expect(Workspace::find($workspace->id))->toBeNull(); +})->with([true, false]);