Automatisch erstellter PR: Synchronisiere `platform-dev` nach `dev`. Enthält alle Änderungen, die aktuell in `platform-dev` vorhanden sind. Bitte Review und Merge gegen `dev`. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #306
213 lines
7.8 KiB
PHP
213 lines
7.8 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\OperationRun;
|
|
use App\Models\Tenant;
|
|
use App\Models\TenantTriageReview;
|
|
use App\Support\BackupHealth\TenantBackupHealthResolver;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
|
|
use App\Support\PortfolioTriage\TenantTriageReviewFingerprint;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
|
|
it('renders visible governance attention sections on the governance inbox page', function (): void {
|
|
$alphaTenant = Tenant::factory()->create([
|
|
'status' => 'active',
|
|
'name' => 'Alpha Tenant',
|
|
'external_id' => 'alpha-tenant',
|
|
]);
|
|
[$user, $alphaTenant] = createUserWithTenant($alphaTenant, role: 'owner', workspaceRole: 'owner');
|
|
|
|
$bravoTenant = Tenant::factory()->create([
|
|
'status' => 'active',
|
|
'workspace_id' => (int) $alphaTenant->workspace_id,
|
|
'name' => 'Bravo Tenant',
|
|
'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,
|
|
'tenant_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([
|
|
'tenant_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(TenantTriageReviewFingerprint::class);
|
|
$alphaBackupFingerprint = $fingerprints->forBackupHealth($backupHealthResolver->assess($alphaTenant));
|
|
|
|
expect($alphaBackupFingerprint)->not->toBeNull();
|
|
|
|
TenantTriageReview::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 = Tenant::factory()->create([
|
|
'status' => 'active',
|
|
'name' => 'Alpha Tenant',
|
|
'external_id' => 'alpha-tenant',
|
|
]);
|
|
[$user, $alphaTenant] = createUserWithTenant($alphaTenant, role: 'owner', workspaceRole: 'owner');
|
|
|
|
$bravoTenant = Tenant::factory()->create([
|
|
'status' => 'active',
|
|
'workspace_id' => (int) $alphaTenant->workspace_id,
|
|
'name' => 'Bravo Tenant',
|
|
'external_id' => 'bravo-tenant',
|
|
]);
|
|
|
|
$user->tenants()->syncWithoutDetaching([
|
|
(int) $bravoTenant->getKey() => ['role' => 'owner'],
|
|
]);
|
|
|
|
Finding::factory()
|
|
->for($bravoTenant)
|
|
->assignedTo((int) $user->getKey())
|
|
->create();
|
|
|
|
AlertDelivery::factory()->create([
|
|
'tenant_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').'?tenant_id='.(string) $alphaTenant->getKey())
|
|
->assertOk()
|
|
->assertSee('This tenant filter is hiding other visible attention')
|
|
->assertSee('Clear tenant filter');
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id])
|
|
->get(GovernanceInbox::getUrl(panel: 'admin').'?tenant_id='.(string) $alphaTenant->getKey().'&family=alert_delivery_failures')
|
|
->assertOk()
|
|
->assertSee('Alert delivery failures')
|
|
->assertSee('No failed alert deliveries match this tenant filter right now.')
|
|
->assertDontSee('Open my findings');
|
|
});
|
|
|
|
it('omits the finding exceptions lane when the workspace capability is not visible', function (): void {
|
|
$tenant = Tenant::factory()->create([
|
|
'status' => 'active',
|
|
'name' => 'Alpha Tenant',
|
|
]);
|
|
[$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,
|
|
'tenant_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');
|
|
});
|