378 lines
13 KiB
PHP
378 lines
13 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 setupDeclarationTestUser(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 #2: Worker sees only assigned declarations in index
|
||
|
|
test('worker sees only assigned declarations in index', function () {
|
||
|
|
[$worker, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
|
||
|
|
$assignedDeclaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
'assigned_to' => $worker->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$unassignedDeclaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
'assigned_to' => null,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$otherUser = User::factory()->create();
|
||
|
|
$otherAssignedDeclaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
'assigned_to' => $otherUser->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->get(route('declarations.index'));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->component('declarations/Index')
|
||
|
|
->has('declarations.data', 1)
|
||
|
|
->where('declarations.data.0.id', $assignedDeclaration->id)
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
// AC #3: Owner sees all declarations
|
||
|
|
test('owner sees all declarations in index', function () {
|
||
|
|
[$owner, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
|
||
|
|
Declaration::factory()->count(5)->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($owner)->get(route('declarations.index'));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->component('declarations/Index')
|
||
|
|
->has('declarations.data', 5)
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
// AC #3: Manager sees all declarations
|
||
|
|
test('manager sees all declarations in index', function () {
|
||
|
|
[$manager, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Manager);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
|
||
|
|
Declaration::factory()->count(3)->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($manager)->get(route('declarations.index'));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->component('declarations/Index')
|
||
|
|
->has('declarations.data', 3)
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
// AC #4: Worker gets 404 accessing unassigned declaration via direct URL
|
||
|
|
test('worker gets 404 accessing unassigned declaration via direct URL', function () {
|
||
|
|
[$worker, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
|
||
|
|
$declaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
'assigned_to' => null,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->get(route('declarations.show', $declaration));
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Worker can access assigned declaration show
|
||
|
|
test('worker can access assigned declaration show', function () {
|
||
|
|
[$worker, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
|
||
|
|
$declaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
'assigned_to' => $worker->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->get(route('declarations.show', $declaration));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->component('declarations/Show')
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
// AC #6: Worker gets 404 on create
|
||
|
|
test('worker gets 404 on declaration create', function () {
|
||
|
|
[$worker, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->get(route('declarations.create'));
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Worker gets 404 on store
|
||
|
|
test('worker gets 404 on declaration store', function () {
|
||
|
|
[$worker, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->post(route('declarations.store'), [
|
||
|
|
'title' => 'Test',
|
||
|
|
'type' => 'vat_monthly',
|
||
|
|
'client_id' => $client->id,
|
||
|
|
'period_year' => 2026,
|
||
|
|
'period_month' => 1,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Worker gets 404 on edit
|
||
|
|
test('worker gets 404 on declaration edit', function () {
|
||
|
|
[$worker, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
$declaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->get(route('declarations.edit', $declaration));
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Worker gets 404 on update
|
||
|
|
test('worker gets 404 on declaration update', function () {
|
||
|
|
[$worker, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
$declaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->put(route('declarations.update', $declaration), [
|
||
|
|
'title' => 'Updated',
|
||
|
|
'type' => 'vat_monthly',
|
||
|
|
'client_id' => $client->id,
|
||
|
|
'period_year' => 2026,
|
||
|
|
'period_month' => 1,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Worker gets 404 on destroy
|
||
|
|
test('worker gets 404 on declaration destroy', function () {
|
||
|
|
[$worker, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
$declaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->delete(route('declarations.destroy', $declaration));
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Manager can access all CRUD operations
|
||
|
|
test('manager can access all declaration CRUD operations', function () {
|
||
|
|
[$manager, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Manager);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
$declaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
// Index
|
||
|
|
$this->actingAs($manager)->get(route('declarations.index'))->assertOk();
|
||
|
|
|
||
|
|
// Show
|
||
|
|
$this->actingAs($manager)->get(route('declarations.show', $declaration))->assertOk();
|
||
|
|
|
||
|
|
// Create page
|
||
|
|
$this->actingAs($manager)->get(route('declarations.create'))->assertOk();
|
||
|
|
|
||
|
|
// Edit page
|
||
|
|
$this->actingAs($manager)->get(route('declarations.edit', $declaration))->assertOk();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Owner can access all CRUD operations
|
||
|
|
test('owner can access all declaration CRUD operations', function () {
|
||
|
|
[$owner, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
$declaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
// Index
|
||
|
|
$this->actingAs($owner)->get(route('declarations.index'))->assertOk();
|
||
|
|
|
||
|
|
// Show
|
||
|
|
$this->actingAs($owner)->get(route('declarations.show', $declaration))->assertOk();
|
||
|
|
|
||
|
|
// Create page
|
||
|
|
$this->actingAs($owner)->get(route('declarations.create'))->assertOk();
|
||
|
|
|
||
|
|
// Edit page
|
||
|
|
$this->actingAs($owner)->get(route('declarations.edit', $declaration))->assertOk();
|
||
|
|
});
|
||
|
|
|
||
|
|
// AC #10: canCreate/canEdit/canDelete props false for Workers
|
||
|
|
test('worker gets canCreate canEdit canDelete as false in declarations index', function () {
|
||
|
|
[$worker, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->get(route('declarations.index'));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->where('canCreate', false)
|
||
|
|
->where('canEdit', false)
|
||
|
|
->where('canDelete', false)
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
// AC #10: canCreate/canEdit/canDelete props true for Owners
|
||
|
|
test('owner gets canCreate canEdit canDelete as true in declarations index', function () {
|
||
|
|
[$owner, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
$response = $this->actingAs($owner)->get(route('declarations.index'));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->where('canCreate', true)
|
||
|
|
->where('canEdit', true)
|
||
|
|
->where('canDelete', true)
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
// AC #9: auth.workspaceRole is shared correctly
|
||
|
|
test('auth.workspaceRole is shared correctly for each role', function () {
|
||
|
|
// Test Owner
|
||
|
|
[$owner, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
$response = $this->actingAs($owner)->get(route('declarations.index'));
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->where('auth.workspaceRole', 'owner')
|
||
|
|
);
|
||
|
|
|
||
|
|
// Test Manager
|
||
|
|
[$manager, $workspace2] = setupDeclarationTestUser(WorkspaceUserRole::Manager);
|
||
|
|
|
||
|
|
$response = $this->actingAs($manager)->get(route('declarations.index'));
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->where('auth.workspaceRole', 'manager')
|
||
|
|
);
|
||
|
|
|
||
|
|
// Test Worker
|
||
|
|
[$worker, $workspace3] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->get(route('declarations.index'));
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->where('auth.workspaceRole', 'worker')
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
// AC #11: Cross-workspace isolation
|
||
|
|
test('worker in workspace A cannot see declarations in workspace B', function () {
|
||
|
|
[$worker, $workspaceA] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$workspaceB = Workspace::factory()->create();
|
||
|
|
$clientB = Client::factory()->create(['workspace_id' => $workspaceB->id]);
|
||
|
|
|
||
|
|
$declarationInB = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspaceB->id,
|
||
|
|
'client_id' => $clientB->id,
|
||
|
|
'assigned_to' => $worker->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
// Worker should not see workspace B declarations in their index
|
||
|
|
$response = $this->actingAs($worker)->get(route('declarations.index'));
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->has('declarations.data', 0)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Worker should get 404 trying to access workspace B declaration directly
|
||
|
|
$response = $this->actingAs($worker)->get(route('declarations.show', $declarationInB));
|
||
|
|
$response->assertNotFound();
|
||
|
|
});
|
||
|
|
|
||
|
|
// canMention is false for workers in declaration show
|
||
|
|
test('worker gets canMention as false in declaration show', function () {
|
||
|
|
[$worker, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Worker);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
$declaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
'assigned_to' => $worker->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($worker)->get(route('declarations.show', $declaration));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->where('canMention', false)
|
||
|
|
->where('canEdit', false)
|
||
|
|
->where('canDelete', false)
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
// canMention is true for owners/managers in declaration show
|
||
|
|
test('owner gets canMention as true in declaration show', function () {
|
||
|
|
[$owner, $workspace] = setupDeclarationTestUser(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||
|
|
$declaration = Declaration::factory()->create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'client_id' => $client->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($owner)->get(route('declarations.show', $declaration));
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$response->assertInertia(fn (Assert $page) => $page
|
||
|
|
->where('canMention', true)
|
||
|
|
->where('canEdit', true)
|
||
|
|
->where('canDelete', true)
|
||
|
|
);
|
||
|
|
});
|