- Story 1.1: Permission enum, config, AuthorizesPermissions & HasWorkspaceScope traits, member→worker migration - Story 1.2: Team page with member list, invitation system with queued email - Story 1.3: Role assignment (Manager/Worker) and member removal with activity logging - Story 1.4: Owner-only permission toggle matrix for Managers (manage team, view logs, configure portal) - Story 1.5: Role-based access enforcement — Workers see only assigned declarations/clients, sidebar scoping - Story 1.6: Workspace switcher dropdown for multi-workspace users with session-based switching - 83 new/modified files, 182 tests passing with zero regressions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
246 lines
8.0 KiB
PHP
246 lines
8.0 KiB
PHP
<?php
|
|
|
|
use App\Enums\WorkspaceUserRole;
|
|
use App\Models\Client;
|
|
use App\Models\Declaration;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
|
|
|
function setupClientTestUser(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];
|
|
}
|
|
|
|
// AC #1: Workers see only clients with assigned declarations
|
|
test('worker sees only clients with assigned declarations in index', function () {
|
|
[$worker, $workspace] = setupClientTestUser(WorkspaceUserRole::Worker);
|
|
|
|
$clientWithAssignment = Client::factory()->create(['workspace_id' => $workspace->id]);
|
|
$clientWithoutAssignment = Client::factory()->create(['workspace_id' => $workspace->id]);
|
|
|
|
Declaration::factory()->create([
|
|
'workspace_id' => $workspace->id,
|
|
'client_id' => $clientWithAssignment->id,
|
|
'assigned_to' => $worker->id,
|
|
]);
|
|
|
|
Declaration::factory()->create([
|
|
'workspace_id' => $workspace->id,
|
|
'client_id' => $clientWithoutAssignment->id,
|
|
'assigned_to' => null,
|
|
]);
|
|
|
|
$response = $this->actingAs($worker)->get(route('clients.index'));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->component('clients/Index')
|
|
->has('clients.data', 1)
|
|
->where('clients.data.0.id', $clientWithAssignment->id)
|
|
);
|
|
});
|
|
|
|
// AC #3: Owners see all workspace clients
|
|
test('owner sees all workspace clients in index', function () {
|
|
[$owner, $workspace] = setupClientTestUser(WorkspaceUserRole::Owner);
|
|
|
|
Client::factory()->count(3)->create(['workspace_id' => $workspace->id]);
|
|
|
|
$response = $this->actingAs($owner)->get(route('clients.index'));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->component('clients/Index')
|
|
->has('clients.data', 3)
|
|
);
|
|
});
|
|
|
|
// AC #3: Managers see all workspace clients
|
|
test('manager sees all workspace clients in index', function () {
|
|
[$manager, $workspace] = setupClientTestUser(WorkspaceUserRole::Manager);
|
|
|
|
Client::factory()->count(3)->create(['workspace_id' => $workspace->id]);
|
|
|
|
$response = $this->actingAs($manager)->get(route('clients.index'));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->component('clients/Index')
|
|
->has('clients.data', 3)
|
|
);
|
|
});
|
|
|
|
// AC #5: Worker gets 404 accessing client with no assigned declarations
|
|
test('worker gets 404 accessing client with no assigned declarations', function () {
|
|
[$worker, $workspace] = setupClientTestUser(WorkspaceUserRole::Worker);
|
|
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
|
|
|
// No declarations assigned to this worker for this client
|
|
Declaration::factory()->create([
|
|
'workspace_id' => $workspace->id,
|
|
'client_id' => $client->id,
|
|
'assigned_to' => null,
|
|
]);
|
|
|
|
$response = $this->actingAs($worker)->get(route('clients.show', $client));
|
|
|
|
$response->assertNotFound();
|
|
});
|
|
|
|
// Worker can access client show when they have assigned declarations
|
|
test('worker can access client show when they have assigned declarations', function () {
|
|
[$worker, $workspace] = setupClientTestUser(WorkspaceUserRole::Worker);
|
|
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
|
|
|
Declaration::factory()->create([
|
|
'workspace_id' => $workspace->id,
|
|
'client_id' => $client->id,
|
|
'assigned_to' => $worker->id,
|
|
]);
|
|
|
|
$response = $this->actingAs($worker)->get(route('clients.show', $client));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->component('clients/Show')
|
|
);
|
|
});
|
|
|
|
// AC #6: Worker gets 404 on create
|
|
test('worker gets 404 on client create', function () {
|
|
[$worker, $workspace] = setupClientTestUser(WorkspaceUserRole::Worker);
|
|
|
|
$response = $this->actingAs($worker)->get(route('clients.create'));
|
|
|
|
$response->assertNotFound();
|
|
});
|
|
|
|
// AC #6: Worker gets 404 on store
|
|
test('worker gets 404 on client store', function () {
|
|
[$worker, $workspace] = setupClientTestUser(WorkspaceUserRole::Worker);
|
|
|
|
$response = $this->actingAs($worker)->post(route('clients.store'), [
|
|
'company_name' => 'Test',
|
|
'legal_form' => 'sarl',
|
|
'contacts' => [['full_name' => 'Test', 'is_principal' => true]],
|
|
]);
|
|
|
|
$response->assertNotFound();
|
|
});
|
|
|
|
// AC #6: Worker gets 404 on edit
|
|
test('worker gets 404 on client edit', function () {
|
|
[$worker, $workspace] = setupClientTestUser(WorkspaceUserRole::Worker);
|
|
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
|
|
|
$response = $this->actingAs($worker)->get(route('clients.edit', $client));
|
|
|
|
$response->assertNotFound();
|
|
});
|
|
|
|
// AC #6: Worker gets 404 on update
|
|
test('worker gets 404 on client update', function () {
|
|
[$worker, $workspace] = setupClientTestUser(WorkspaceUserRole::Worker);
|
|
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
|
|
|
$response = $this->actingAs($worker)->put(route('clients.update', $client), [
|
|
'company_name' => 'Updated',
|
|
'legal_form' => 'sarl',
|
|
'contacts' => [
|
|
['full_name' => 'Test Contact', 'is_principal' => true],
|
|
],
|
|
]);
|
|
|
|
$response->assertNotFound();
|
|
});
|
|
|
|
// AC #6: Worker gets 404 on destroy
|
|
test('worker gets 404 on client destroy', function () {
|
|
[$worker, $workspace] = setupClientTestUser(WorkspaceUserRole::Worker);
|
|
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
|
|
|
$response = $this->actingAs($worker)->delete(route('clients.destroy', $client));
|
|
|
|
$response->assertNotFound();
|
|
});
|
|
|
|
// AC #3: Manager can access all CRUD operations
|
|
test('manager can access all client CRUD operations', function () {
|
|
[$manager, $workspace] = setupClientTestUser(WorkspaceUserRole::Manager);
|
|
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
|
|
|
// Index
|
|
$this->actingAs($manager)->get(route('clients.index'))->assertOk();
|
|
|
|
// Show
|
|
$this->actingAs($manager)->get(route('clients.show', $client))->assertOk();
|
|
|
|
// Create page
|
|
$this->actingAs($manager)->get(route('clients.create'))->assertOk();
|
|
|
|
// Edit page
|
|
$this->actingAs($manager)->get(route('clients.edit', $client))->assertOk();
|
|
});
|
|
|
|
// AC #3: Owner can access all CRUD operations
|
|
test('owner can access all client CRUD operations', function () {
|
|
[$owner, $workspace] = setupClientTestUser(WorkspaceUserRole::Owner);
|
|
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
|
|
|
// Index
|
|
$this->actingAs($owner)->get(route('clients.index'))->assertOk();
|
|
|
|
// Show
|
|
$this->actingAs($owner)->get(route('clients.show', $client))->assertOk();
|
|
|
|
// Create page
|
|
$this->actingAs($owner)->get(route('clients.create'))->assertOk();
|
|
|
|
// Edit page
|
|
$this->actingAs($owner)->get(route('clients.edit', $client))->assertOk();
|
|
});
|
|
|
|
// AC #10: canCreate/canEdit/canDelete props are false for Workers
|
|
test('worker gets canCreate canEdit canDelete as false in index', function () {
|
|
[$worker, $workspace] = setupClientTestUser(WorkspaceUserRole::Worker);
|
|
|
|
$response = $this->actingAs($worker)->get(route('clients.index'));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->where('canCreate', false)
|
|
->where('canEdit', false)
|
|
->where('canDelete', false)
|
|
);
|
|
});
|
|
|
|
// AC #10: canCreate/canEdit/canDelete props are true for Owners
|
|
test('owner gets canCreate canEdit canDelete as true in index', function () {
|
|
[$owner, $workspace] = setupClientTestUser(WorkspaceUserRole::Owner);
|
|
|
|
$response = $this->actingAs($owner)->get(route('clients.index'));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->where('canCreate', true)
|
|
->where('canEdit', true)
|
|
->where('canDelete', true)
|
|
);
|
|
});
|