215 lines
6.6 KiB
PHP
215 lines
6.6 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
use App\Enums\Permission;
|
||
|
|
use App\Enums\WorkspaceUserRole;
|
||
|
|
use App\Mail\TeamInvitationMail;
|
||
|
|
use App\Models\TeamInvitation;
|
||
|
|
use App\Models\User;
|
||
|
|
use App\Models\Workspace;
|
||
|
|
use Illuminate\Support\Facades\Mail;
|
||
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
||
|
|
|
||
|
|
function setupTeamUser(string $role, array $permissions = []): array
|
||
|
|
{
|
||
|
|
$user = User::factory()->create();
|
||
|
|
$workspace = Workspace::factory()->create();
|
||
|
|
$workspace->users()->attach($user->id, [
|
||
|
|
'role' => $role,
|
||
|
|
'permissions' => $permissions,
|
||
|
|
]);
|
||
|
|
session(['current_workspace_id' => $workspace->id]);
|
||
|
|
|
||
|
|
return [$user, $workspace];
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Team Index ──────────────────────────────────────────────
|
||
|
|
|
||
|
|
test('owner can view team index page', function () {
|
||
|
|
[$user, $workspace] = setupTeamUser(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
$response = $this->actingAs($user)->get(route('team.index'));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->component('team/Index')
|
||
|
|
->has('members')
|
||
|
|
->has('pendingInvitations')
|
||
|
|
->where('canManageTeam', true)
|
||
|
|
->has('inviteUrl')
|
||
|
|
->has('roles')
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('manager with can_manage_team can view team index page', function () {
|
||
|
|
[$user] = setupTeamUser(WorkspaceUserRole::Manager, [
|
||
|
|
Permission::CanManageTeam => true,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($user)->get(route('team.index'));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->component('team/Index')
|
||
|
|
->where('canManageTeam', true)
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('manager without can_manage_team can view team index but canManageTeam is false', function () {
|
||
|
|
[$user] = setupTeamUser(WorkspaceUserRole::Manager, [
|
||
|
|
Permission::CanManageTeam => false,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($user)->get(route('team.index'));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->component('team/Index')
|
||
|
|
->where('canManageTeam', false)
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('worker receives 404 on team index', function () {
|
||
|
|
[$user] = setupTeamUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$response = $this->actingAs($user)->get(route('team.index'));
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
});
|
||
|
|
|
||
|
|
// ── Invite Member ───────────────────────────────────────────
|
||
|
|
|
||
|
|
test('owner can invite a new member', function () {
|
||
|
|
Mail::fake();
|
||
|
|
[$user, $workspace] = setupTeamUser(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
$response = $this->actingAs($user)->post(route('team.invite'), [
|
||
|
|
'email' => 'newmember@example.com',
|
||
|
|
'role' => 'worker',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertRedirect();
|
||
|
|
$response->assertSessionHas('success', 'Invitation envoyée');
|
||
|
|
|
||
|
|
expect(TeamInvitation::where('workspace_id', $workspace->id)
|
||
|
|
->where('email', 'newmember@example.com')
|
||
|
|
->exists()
|
||
|
|
)->toBeTrue();
|
||
|
|
|
||
|
|
Mail::assertQueued(TeamInvitationMail::class, function ($mail) {
|
||
|
|
return $mail->hasTo('newmember@example.com');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test('manager with can_manage_team can invite a new member', function () {
|
||
|
|
Mail::fake();
|
||
|
|
[$user, $workspace] = setupTeamUser(WorkspaceUserRole::Manager, [
|
||
|
|
Permission::CanManageTeam => true,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($user)->post(route('team.invite'), [
|
||
|
|
'email' => 'newmanager@example.com',
|
||
|
|
'role' => 'manager',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertRedirect();
|
||
|
|
$response->assertSessionHas('success', 'Invitation envoyée');
|
||
|
|
|
||
|
|
expect(TeamInvitation::where('workspace_id', $workspace->id)
|
||
|
|
->where('email', 'newmanager@example.com')
|
||
|
|
->where('role', 'manager')
|
||
|
|
->exists()
|
||
|
|
)->toBeTrue();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('manager without permission gets 404 on invite', function () {
|
||
|
|
[$user] = setupTeamUser(WorkspaceUserRole::Manager, [
|
||
|
|
Permission::CanManageTeam => false,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($user)->post(route('team.invite'), [
|
||
|
|
'email' => 'blocked@example.com',
|
||
|
|
'role' => 'worker',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('worker gets 404 on invite', function () {
|
||
|
|
[$user] = setupTeamUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$response = $this->actingAs($user)->post(route('team.invite'), [
|
||
|
|
'email' => 'blocked@example.com',
|
||
|
|
'role' => 'worker',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('cannot invite email already in workspace', function () {
|
||
|
|
[$user, $workspace] = setupTeamUser(WorkspaceUserRole::Owner);
|
||
|
|
$existingMember = User::factory()->create(['email' => 'existing@example.com']);
|
||
|
|
$workspace->users()->attach($existingMember->id, ['role' => 'worker']);
|
||
|
|
|
||
|
|
$response = $this->actingAs($user)->post(route('team.invite'), [
|
||
|
|
'email' => 'existing@example.com',
|
||
|
|
'role' => 'worker',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertSessionHasErrors('email');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('invitation creates team invitation record with correct data', function () {
|
||
|
|
Mail::fake();
|
||
|
|
[$user, $workspace] = setupTeamUser(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
$this->actingAs($user)->post(route('team.invite'), [
|
||
|
|
'email' => 'record@example.com',
|
||
|
|
'role' => 'manager',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$invitation = TeamInvitation::where('email', 'record@example.com')->first();
|
||
|
|
|
||
|
|
expect($invitation)->not->toBeNull()
|
||
|
|
->and($invitation->workspace_id)->toBe($workspace->id)
|
||
|
|
->and($invitation->role)->toBe('manager')
|
||
|
|
->and($invitation->invited_by)->toBe($user->id)
|
||
|
|
->and($invitation->token)->not->toBeEmpty()
|
||
|
|
->and($invitation->expires_at)->not->toBeNull()
|
||
|
|
->and($invitation->accepted_at)->toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('invitation sends email', function () {
|
||
|
|
Mail::fake();
|
||
|
|
[$user] = setupTeamUser(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
$this->actingAs($user)->post(route('team.invite'), [
|
||
|
|
'email' => 'mailto@example.com',
|
||
|
|
'role' => 'worker',
|
||
|
|
]);
|
||
|
|
|
||
|
|
Mail::assertQueued(TeamInvitationMail::class, function ($mail) {
|
||
|
|
return $mail->hasTo('mailto@example.com');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test('cannot send duplicate active invitation to same email', function () {
|
||
|
|
Mail::fake();
|
||
|
|
[$user, $workspace] = setupTeamUser(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
// Create an existing active invitation
|
||
|
|
TeamInvitation::create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'email' => 'duplicate@example.com',
|
||
|
|
'role' => 'worker',
|
||
|
|
'invited_by' => $user->id,
|
||
|
|
'expires_at' => now()->addDays(7),
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($user)->post(route('team.invite'), [
|
||
|
|
'email' => 'duplicate@example.com',
|
||
|
|
'role' => 'manager',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertSessionHasErrors('email');
|
||
|
|
});
|