TenantAtlas/apps/platform/tests/Feature/Filament/Spec343CustomerReviewAttestationAcceptedRiskTest.php
ahmido 0987527d0e feat: customer review acknowledgement lifecycle (343) (#415)
## Summary
- add persisted customer review acknowledgement truth with capability gating and audit emission
- extend the customer review workspace with acknowledgement state, evidence basis details, and accepted-risk lifecycle visibility
- add focused feature and browser coverage plus Spec 343 screenshot artifacts and UI audit updates

## Scope
- Livewire v4 / Filament v5 surface only; no panel provider changes
- no new global assets; no `filament:assets` deployment change for this slice
- includes a PostgreSQL migration for `environment_review_acknowledgements`

## Guardrail / Exception / Smoke Coverage
- reachable UI surface changed: existing `/admin/reviews/workspace` customer-safe page
- UI audit updated in `docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md`
- screenshot artifacts included under `specs/343-customer-review-attestation-accepted-risk-lifecycle/artifacts/screenshots/`
- spec package includes plan, tasks, repo-truth map, and state contract for the implemented slice

## Notes
- target branch requested: `platform-dev`
- branch pushed from commit `aaaad441fd13dbac54e971ab48765c502ced6b3f`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #415
2026-06-01 18:00:37 +00:00

158 lines
6.6 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Models\AuditLog;
use App\Models\EnvironmentReview;
use App\Models\EnvironmentReviewAcknowledgement;
use App\Models\EvidenceSnapshot;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Services\EnvironmentReviews\EnvironmentReviewAcknowledgementService;
use App\Support\Audit\AuditActionId;
use App\Support\EnvironmentReviewStatus;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Actions\Action;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
uses(RefreshDatabase::class);
it('renders acknowledgement required for a published review and allows authorized acknowledgement', function (): void {
$tenant = ManagedEnvironment::factory()->create(['name' => 'Spec343 Ack Required']);
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', workspaceRole: 'manager');
$review = spec343CreatePublishedReview($tenant, $user, seedEnvironmentReviewEvidence($tenant, findingCount: 0, driftCount: 0));
$this->actingAs($user);
setAdminPanelContext();
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
$component = Livewire::actingAs($user)->test(CustomerReviewWorkspace::class)
->assertSee(__('localization.review.review_acknowledgement'))
->assertSee(__('localization.review.acknowledgement_required'))
->assertSee(__('localization.review.acknowledge_review'))
->assertActionExists('acknowledgeReview', fn (Action $action): bool => $action->isConfirmationRequired());
$payload = $component->instance()->latestReviewConsumptionPayload();
expect(data_get($payload, 'acknowledgement.state'))->toBe('required');
$component
->mountAction('acknowledgeReview')
->setActionData([
'comment' => 'Reviewed with stakeholders.',
])
->callMountedAction()
->assertNotified(__('localization.review.review_acknowledged'));
$ack = EnvironmentReviewAcknowledgement::query()
->where('environment_review_id', (int) $review->getKey())
->first();
expect($ack)->toBeInstanceOf(EnvironmentReviewAcknowledgement::class)
->and((int) $ack?->managed_environment_id)->toBe((int) $tenant->getKey())
->and((int) $ack?->workspace_id)->toBe((int) $tenant->workspace_id)
->and((int) $ack?->environment_review_id)->toBe((int) $review->getKey())
->and((int) $ack?->acknowledged_by_user_id)->toBe((int) $user->getKey())
->and($ack?->comment)->toBe('Reviewed with stakeholders.')
->and((int) $ack?->evidence_snapshot_id)->toBe((int) $review->evidence_snapshot_id);
$audit = AuditLog::query()
->where('action', AuditActionId::EnvironmentReviewAcknowledged->value)
->where('resource_type', 'environment_review')
->where('resource_id', (string) $review->getKey())
->latest('id')
->first();
expect($audit)->not->toBeNull()
->and((int) $audit?->managed_environment_id)->toBe((int) $tenant->getKey())
->and((int) data_get($audit?->metadata, 'review_id'))->toBe((int) $review->getKey());
});
it('surfaces re-ack required when the evidence basis changes after acknowledgement', function (): void {
$tenant = ManagedEnvironment::factory()->create(['name' => 'Spec343 Re-ack']);
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', workspaceRole: 'manager');
$snapshotA = seedEnvironmentReviewEvidence($tenant, findingCount: 0, driftCount: 0);
$review = spec343CreatePublishedReview($tenant, $user, $snapshotA);
$this->actingAs($user);
setAdminPanelContext();
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
Livewire::actingAs($user)
->test(CustomerReviewWorkspace::class)
->mountAction('acknowledgeReview')
->callMountedAction()
->assertNotified(__('localization.review.review_acknowledged'));
$snapshotA->forceFill([
'status' => EvidenceSnapshotStatus::Superseded->value,
])->save();
$snapshotB = seedEnvironmentReviewEvidence($tenant, findingCount: 0, driftCount: 0);
$review->forceFill([
'evidence_snapshot_id' => (int) $snapshotB->getKey(),
])->save();
$component = Livewire::actingAs($user)->test(CustomerReviewWorkspace::class);
$payload = $component->instance()->latestReviewConsumptionPayload();
expect(data_get($payload, 'acknowledgement.state'))->toBe('re_ack_required');
});
it('shows a visible empty state when no accepted risks are recorded', function (): void {
$tenant = ManagedEnvironment::factory()->create(['name' => 'Spec343 Empty Accepted Risks']);
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', workspaceRole: 'manager');
spec343CreatePublishedReview($tenant, $user, seedEnvironmentReviewEvidence($tenant, findingCount: 0, driftCount: 0));
$this->actingAs($user);
setAdminPanelContext();
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
Livewire::actingAs($user)
->test(CustomerReviewWorkspace::class)
->assertSee(__('localization.review.accepted_risk_summary'))
->assertSee(__('localization.review.no_accepted_risks_recorded'));
});
it('denies acknowledgement outside the actor tenant scope as not found', function (): void {
[$actor] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
$otherTenant = ManagedEnvironment::factory()->create(['name' => 'Spec343 Other Tenant']);
[$creator, $otherTenant] = createUserWithTenant(tenant: $otherTenant, role: 'owner', workspaceRole: 'manager');
$review = spec343CreatePublishedReview(
$otherTenant,
$creator,
seedEnvironmentReviewEvidence($otherTenant, findingCount: 0, driftCount: 0),
);
expect(fn (): mixed => app(EnvironmentReviewAcknowledgementService::class)->acknowledge(
tenant: $otherTenant,
review: $review,
actor: $actor,
comment: null,
))->toThrow(NotFoundHttpException::class);
});
function spec343CreatePublishedReview(ManagedEnvironment $tenant, User $user, EvidenceSnapshot $snapshot): EnvironmentReview
{
$review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
$review->forceFill([
'status' => EnvironmentReviewStatus::Published->value,
'generated_at' => now(),
'published_at' => now(),
'published_by_user_id' => (int) $user->getKey(),
])->save();
return $review->refresh();
}