TenantAtlas/apps/platform/tests/Feature/Governance/GovernanceInboxPageTest.php
ahmido 1c38a08919 feat: productize governance inbox decision-first workbench (#388)
## Summary
- productize the governance inbox decision-first workbench surface and related section-building logic
- update the panel theme asset and governance inbox Blade view for the new page treatment
- add Governance Inbox feature coverage and a Spec 327 browser smoke test
- add Spec 327 planning artifacts under `specs/327-governance-inbox-decision-first-workbench-productization`

## Testing
- not run as part of this handoff

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #388
2026-05-18 16:20:17 +00:00

393 lines
15 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Pages\Governance\GovernanceInbox;
use App\Models\AlertDelivery;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentTriageReview;
use App\Models\OperationRun;
use App\Support\BackupHealth\TenantBackupHealthResolver;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewFingerprint;
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
use App\Support\Workspaces\WorkspaceContext;
it('documents the Spec 327 governance inbox repo truth map', function (): void {
$path = repo_path('specs/327-governance-inbox-decision-first-workbench-productization/repo-truth-map.md');
expect($path)->toBeFile();
$contents = (string) file_get_contents($path);
expect($contents)
->toContain('Findings')
->toContain('Finding Exceptions / Accepted Risks')
->toContain('OperationRun links')
->toContain('Workspace / Environment filter state')
->toContain('Diagnostics');
});
it('renders the Spec 327 decision-first workbench for the highest-priority finding', function (): void {
$tenant = ManagedEnvironment::factory()->create([
'status' => 'active',
'name' => 'Spec327 Environment Alpha',
]);
[$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'owner');
Finding::factory()
->for($tenant)
->assignedTo((int) $user->getKey())
->ownedBy((int) $user->getKey())
->overdueByHours()
->create([
'workspace_id' => (int) $tenant->workspace_id,
'subject_external_id' => 'spec327-priority-finding',
'severity' => Finding::SEVERITY_HIGH,
'status' => Finding::STATUS_IN_PROGRESS,
'evidence_jsonb' => [
'summary' => [
'kind' => 'policy_snapshot',
'raw_payload' => 'raw payload should stay hidden',
'debug_metadata' => 'debug metadata should stay hidden',
],
],
]);
$this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
->get(GovernanceInbox::getUrl(panel: 'admin'))
->assertOk()
->assertSee('Governance Inbox')
->assertSee('Prioritized governance decisions, owners, evidence, and follow-up actions across entitled environments.')
->assertSee('What decision clears the highest-priority item?')
->assertSee('Decision workbench')
->assertSee('Reason')
->assertSee('Impact')
->assertSee('Owner')
->assertSee('Due')
->assertSee('Evidence')
->assertSee('Accepted risk')
->assertSee('Review finding')
->assertSee('Evidence captured on finding')
->assertSee('No accepted risk')
->assertSee('Decision summary')
->assertSee('Owner / due')
->assertSee('Evidence path')
->assertSee('Primary next action')
->assertSee('Queue context')
->assertSee('Assigned findings')
->assertDontSee('No governance decisions need attention')
->assertDontSee('tenant filter')
->assertDontSee('current tenant')
->assertDontSee('entitled tenant')
->assertDontSee('all tenants')
->assertDontSee('raw payload should stay hidden')
->assertDontSee('debug metadata should stay hidden');
});
it('renders a compact empty decision state without primary zero metric cards', function (): void {
$tenant = ManagedEnvironment::factory()->create([
'status' => 'active',
'name' => 'Spec327 Empty Environment',
]);
[$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'owner');
$response = $this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
->get(GovernanceInbox::getUrl(panel: 'admin'));
$response
->assertOk()
->assertSee('What decision clears the highest-priority item?')
->assertSee('No governance decisions need attention')
->assertSee('The current workspace scope has no repo-backed governance decisions requiring action.')
->assertSee('Decision summary')
->assertSee('Owner / due')
->assertSee('Evidence path')
->assertSee('Accepted risk')
->assertSee('Primary next action')
->assertSee('Diagnostics')
->assertSee('Collapsed')
->assertDontSee('Visible decisions')
->assertDontSee('Priority family')
->assertDontSee('raw payload')
->assertDontSee('stack trace')
->assertDontSee('debug metadata')
->assertDontSee('provider secret');
});
it('renders honest missing owner due evidence and accepted-risk states', function (): void {
$tenant = ManagedEnvironment::factory()->create([
'status' => 'active',
'name' => 'Spec327 Environment Missing State',
]);
[$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'owner');
Finding::factory()
->for($tenant)
->create([
'workspace_id' => (int) $tenant->workspace_id,
'subject_external_id' => 'spec327-missing-state-finding',
'owner_user_id' => null,
'assignee_user_id' => null,
'due_at' => null,
'evidence_jsonb' => [],
]);
$this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
->get(GovernanceInbox::getUrl(panel: 'admin'))
->assertOk()
->assertSee('Owner missing')
->assertSee('Due date unavailable')
->assertSee('Evidence missing')
->assertSee('No accepted risk')
->assertSee('Triage finding');
});
it('renders accepted-risk and exception state without exposing raw diagnostics by default', function (): void {
$tenant = ManagedEnvironment::factory()->create([
'status' => 'active',
'name' => 'Spec327 Environment Exception',
]);
[$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'owner');
$finding = Finding::factory()
->for($tenant)
->riskAccepted()
->create([
'workspace_id' => (int) $tenant->workspace_id,
'subject_external_id' => 'spec327-exception-finding',
]);
FindingException::query()->create([
'workspace_id' => (int) $tenant->workspace_id,
'managed_environment_id' => (int) $tenant->getKey(),
'finding_id' => (int) $finding->getKey(),
'requested_by_user_id' => (int) $user->getKey(),
'owner_user_id' => null,
'status' => FindingException::STATUS_PENDING,
'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT,
'request_reason' => 'Exception needs governance evidence',
'requested_at' => now()->subDay(),
'review_due_at' => now()->addDay(),
'evidence_summary' => ['reference_count' => 0],
]);
$this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
->get(GovernanceInbox::getUrl(panel: 'admin').'?family=finding_exceptions')
->assertOk()
->assertSee('Review accepted-risk decision')
->assertSee('Exception needs governance evidence')
->assertSee('Pending exception')
->assertSee('Evidence missing')
->assertSee('Review accepted risk')
->assertSee('Diagnostics')
->assertSee('Collapsed')
->assertDontSee('raw payload')
->assertDontSee('stack trace')
->assertDontSee('provider secret')
->assertDontSee('internal exception')
->assertDontSee('debug metadata');
});
it('renders visible governance attention sections on the governance inbox page', function (): void {
$alphaTenant = ManagedEnvironment::factory()->create([
'status' => 'active',
'name' => 'Alpha ManagedEnvironment',
'external_id' => 'alpha-tenant',
]);
[$user, $alphaTenant] = createUserWithTenant($alphaTenant, role: 'owner', workspaceRole: 'owner');
$bravoTenant = ManagedEnvironment::factory()->create([
'status' => 'active',
'workspace_id' => (int) $alphaTenant->workspace_id,
'name' => 'Bravo ManagedEnvironment',
'external_id' => 'bravo-tenant',
]);
$user->tenants()->syncWithoutDetaching([
(int) $bravoTenant->getKey() => ['role' => 'owner'],
]);
Finding::factory()
->for($alphaTenant)
->assignedTo((int) $user->getKey())
->ownedBy((int) $user->getKey())
->overdueByHours()
->create();
Finding::factory()
->for($bravoTenant)
->reopened()
->create();
$exceptionFinding = Finding::factory()
->for($alphaTenant)
->riskAccepted()
->create([
'workspace_id' => (int) $alphaTenant->workspace_id,
'subject_external_id' => 'exception-governance-home',
]);
FindingException::query()->create([
'workspace_id' => (int) $alphaTenant->workspace_id,
'managed_environment_id' => (int) $alphaTenant->getKey(),
'finding_id' => (int) $exceptionFinding->getKey(),
'requested_by_user_id' => (int) $user->getKey(),
'owner_user_id' => (int) $user->getKey(),
'status' => FindingException::STATUS_PENDING,
'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT,
'request_reason' => 'Governance home exception review',
'requested_at' => now()->subDay(),
'review_due_at' => now()->addDay(),
'evidence_summary' => ['reference_count' => 0],
]);
OperationRun::factory()
->forTenant($alphaTenant)
->create([
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Failed->value,
'completed_at' => now()->subMinute(),
]);
AlertDelivery::factory()->create([
'managed_environment_id' => null,
'workspace_id' => (int) $alphaTenant->workspace_id,
'status' => AlertDelivery::STATUS_FAILED,
'payload' => [
'title' => 'Delivery failed',
'body' => 'A notification destination failed.',
],
]);
$backupHealthResolver = app(TenantBackupHealthResolver::class);
$fingerprints = app(ManagedEnvironmentTriageReviewFingerprint::class);
$alphaBackupFingerprint = $fingerprints->forBackupHealth($backupHealthResolver->assess($alphaTenant));
expect($alphaBackupFingerprint)->not->toBeNull();
ManagedEnvironmentTriageReview::factory()
->for($alphaTenant)
->followUpNeeded()
->create([
'workspace_id' => (int) $alphaTenant->workspace_id,
'reviewed_by_user_id' => (int) $user->getKey(),
'concern_family' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
'review_fingerprint' => $alphaBackupFingerprint['fingerprint'],
'review_snapshot' => $alphaBackupFingerprint['snapshot'],
]);
$this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id])
->get(GovernanceInbox::getUrl(panel: 'admin'))
->assertOk()
->assertSee('Assigned findings')
->assertSee('Findings intake')
->assertSee('Finding exceptions')
->assertSee('Operations follow-up')
->assertSee('Alert delivery failures')
->assertSee('Review follow-up')
->assertSee('Open my findings')
->assertSee('Open finding exceptions')
->assertSee('Open terminal follow-up')
->assertSee('Open alert deliveries')
->assertSee('Open customer review workspace');
});
it('renders honest empty states for tenant and family filtering on the governance inbox page', function (): void {
$alphaTenant = ManagedEnvironment::factory()->create([
'status' => 'active',
'name' => 'Alpha ManagedEnvironment',
'external_id' => 'alpha-tenant',
]);
[$user, $alphaTenant] = createUserWithTenant($alphaTenant, role: 'owner', workspaceRole: 'owner');
$bravoTenant = ManagedEnvironment::factory()->create([
'status' => 'active',
'workspace_id' => (int) $alphaTenant->workspace_id,
'name' => 'Bravo ManagedEnvironment',
'external_id' => 'bravo-tenant',
]);
$user->tenants()->syncWithoutDetaching([
(int) $bravoTenant->getKey() => ['role' => 'owner'],
]);
Finding::factory()
->for($bravoTenant)
->assignedTo((int) $user->getKey())
->create();
AlertDelivery::factory()->create([
'managed_environment_id' => null,
'workspace_id' => (int) $alphaTenant->workspace_id,
'status' => AlertDelivery::STATUS_FAILED,
]);
$this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id])
->get(GovernanceInbox::getUrl(panel: 'admin').'?environment_id='.(string) $alphaTenant->getKey())
->assertOk()
->assertSee('This environment filter is hiding other visible attention')
->assertSee('Clear environment filter');
$this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id])
->get(GovernanceInbox::getUrl(panel: 'admin').'?environment_id='.(string) $alphaTenant->getKey().'&family=alert_delivery_failures')
->assertOk()
->assertSee('Alert delivery failures')
->assertSee('No failed alert deliveries match this environment filter right now.')
->assertDontSee('Open my findings');
});
it('omits the finding exceptions lane when the workspace capability is not visible', function (): void {
$tenant = ManagedEnvironment::factory()->create([
'status' => 'active',
'name' => 'Alpha ManagedEnvironment',
]);
[$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'readonly');
Finding::factory()
->for($tenant)
->assignedTo((int) $user->getKey())
->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$exceptionFinding = Finding::factory()
->for($tenant)
->riskAccepted()
->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
FindingException::query()->create([
'workspace_id' => (int) $tenant->workspace_id,
'managed_environment_id' => (int) $tenant->getKey(),
'finding_id' => (int) $exceptionFinding->getKey(),
'requested_by_user_id' => (int) $user->getKey(),
'owner_user_id' => (int) $user->getKey(),
'status' => FindingException::STATUS_PENDING,
'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT,
'request_reason' => 'Hidden exception lane',
'requested_at' => now()->subDay(),
'review_due_at' => now()->addDay(),
'evidence_summary' => ['reference_count' => 0],
]);
$this->actingAs($user)
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
->get(GovernanceInbox::getUrl(panel: 'admin'))
->assertOk()
->assertSee('Assigned findings')
->assertDontSee('Finding exceptions')
->assertDontSee('Hidden exception lane');
});