TenantAtlas/apps/platform/tests/Feature/Findings/FindingWorkflowViewActionsTest.php
Ahmed Darrazi d427b8f54d
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m1s
feat: governance inbox final operator workflow (spec 346)
Implemented the final operator workflow for the Governance Inbox. This includes refactoring the inbox page, updating finding resources, adding UI enforcement policies, updating related blade views, and adding comprehensive tests for operator workflow and scope contracts.
2026-06-02 16:46:24 +02:00

134 lines
5.0 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Resources\FindingResource\Pages\ViewFinding;
use App\Filament\Resources\FindingResource;
use App\Models\Finding;
use App\Models\User;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Filament\Forms\Components\Select;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
uses(RefreshDatabase::class);
it('shows workflow header actions on the view page for authorized members', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$this->actingAs($user);
Filament::setTenant($tenant, true);
$newFinding = Finding::factory()->for($tenant)->create(['status' => Finding::STATUS_NEW]);
$triagedFinding = Finding::factory()->for($tenant)->create(['status' => Finding::STATUS_TRIAGED]);
$resolvedFinding = Finding::factory()->for($tenant)->create([
'status' => Finding::STATUS_RESOLVED,
'resolved_at' => now(),
'resolved_reason' => Finding::RESOLVE_REASON_REMEDIATED,
]);
Livewire::test(ViewFinding::class, ['record' => $newFinding->getKey()])
->assertActionVisible('triage')
->assertActionVisible('assign')
->assertActionVisible('resolve')
->assertActionVisible('close')
->assertActionVisible('request_exception');
Livewire::test(ViewFinding::class, ['record' => $triagedFinding->getKey()])
->assertActionVisible('start_progress');
Livewire::test(ViewFinding::class, ['record' => $resolvedFinding->getKey()])
->assertActionVisible('reopen')
->mountAction('reopen')
->assertActionMounted('reopen')
->assertFormFieldExists('reopen_reason', function (Select $field): bool {
return $field->getLabel() === 'Reopen reason'
&& array_keys($field->getOptions()) === Finding::reopenReasonKeys();
});
});
it('executes workflow actions from view header and supports assignment to tenant members only', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$this->actingAs($user);
Filament::setTenant($tenant, true);
$assignee = User::factory()->create();
createUserWithTenant(tenant: $tenant, user: $assignee, role: 'operator');
$outsider = User::factory()->create();
$finding = Finding::factory()->for($tenant)->create(['status' => Finding::STATUS_NEW]);
$component = Livewire::test(ViewFinding::class, ['record' => $finding->getKey()]);
$component
->callAction('triage')
->assertHasNoActionErrors()
->callAction('assign', [
'assignee_user_id' => (int) $assignee->getKey(),
'owner_user_id' => (int) $user->getKey(),
])
->assertHasNoActionErrors()
->callAction('resolve', [
'resolved_reason' => Finding::RESOLVE_REASON_REMEDIATED,
])
->assertHasNoActionErrors();
$finding->refresh();
expect($finding->status)->toBe(Finding::STATUS_RESOLVED)
->and((int) $finding->assignee_user_id)->toBe((int) $assignee->getKey())
->and((int) $finding->owner_user_id)->toBe((int) $user->getKey());
Livewire::test(ViewFinding::class, ['record' => $finding->getKey()])
->mountAction('reopen')
->assertActionMounted('reopen')
->assertFormFieldExists('reopen_reason', function (Select $field): bool {
return array_keys($field->getOptions()) === Finding::reopenReasonKeys();
});
Livewire::test(ViewFinding::class, ['record' => $finding->getKey()])
->callAction('reopen', [
'reopen_reason' => Finding::REOPEN_REASON_MANUAL_REASSESSMENT,
])
->assertHasNoActionErrors()
->callAction('assign', [
'assignee_user_id' => (int) $outsider->getKey(),
'owner_user_id' => (int) $user->getKey(),
]);
$finding->refresh();
expect((int) $finding->assignee_user_id)->toBe((int) $assignee->getKey());
});
it('executes the triage action from an environment-bound admin finding detail without filament tenancy', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$finding = Finding::factory()->for($tenant)->create(['status' => Finding::STATUS_NEW]);
$referer = FindingResource::getUrl('view', ['record' => $finding], panel: 'admin', tenant: $tenant);
$this->actingAs($user);
Filament::setCurrentPanel('admin');
Filament::setTenant($tenant, true);
Filament::bootCurrentPanel();
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
$component = Livewire::withHeaders([
'referer' => $referer,
'x-livewire' => 'true',
])
->actingAs($user)
->test(ViewFinding::class, ['record' => $finding->getKey()]);
Filament::setTenant(null, true);
$component
->call('mountAction', 'triage', [], ['recordKey' => (string) $finding->getKey()])
->assertHasNoActionErrors()
->assertNotified('Finding triaged');
$finding->refresh();
expect($finding->status)->toBe(Finding::STATUS_TRIAGED)
->and($finding->triaged_at)->not->toBeNull();
});