TenantAtlas/apps/platform/tests/Feature/Filament/GovernanceArtifacts/GovernanceArtifactDeepLinkContractTest.php
ahmido f50d57370f feat: cut over workspace-first admin environment surfaces (#341)
## Summary
- cut over the admin runtime to the workspace-first environment and operations routes from spec 280
- retarget governance artifact resources, related navigation, and operation drillthroughs to the surviving admin panel contract from spec 282
- add focused feature and browser coverage plus spec close-out updates for the shipped 280/282 slice

## Validation
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/WorkspaceFoundation tests/Feature/Workspaces tests/Feature/ManagedEnvironment tests/Feature/RequiredPermissions tests/Feature/Operations tests/Feature/MonitoringOperationsTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec280WorkspaceTenancyEnvironmentRoutingSmokeTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/GovernanceArtifacts/GovernanceArtifactAdminPanelRegistrationTest.php tests/Feature/Filament/GovernanceArtifacts/GovernanceArtifactEnvironmentContextTest.php tests/Feature/Filament/GovernanceArtifacts/GovernanceArtifactDeepLinkContractTest.php tests/Feature/Filament/GovernanceArtifacts/GovernanceArtifactLegacyTenantPanelGuardTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec282GovernanceArtifactRetargetingSmokeTest.php`

## Notes
- provider registration remains in `apps/platform/bootstrap/providers.php`
- Filament stays on v5 with Livewire v4 semantics
- touched searchable governance surfaces remain truthful or disabled in the same slice

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #341
2026-05-07 23:50:36 +00:00

286 lines
12 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Resources\BackupSetResource;
use App\Filament\Resources\EvidenceSnapshotResource;
use App\Filament\Resources\FindingExceptionResource;
use App\Filament\Resources\FindingResource;
use App\Filament\Resources\RestoreRunResource;
use App\Filament\Resources\ReviewPackResource;
use App\Filament\Resources\TenantReviewResource;
use App\Models\AuditLog;
use App\Models\BackupSet;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\FindingExceptionDecision;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\RestoreRun;
use App\Models\ReviewPack;
use App\Models\User;
use App\Support\Navigation\RelatedNavigationResolver;
use App\Support\OperationRunLinks;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter;
use App\Support\Workspaces\WorkspaceContext;
function setGovernanceArtifactAdminContext(ManagedEnvironment $tenant): void
{
setAdminPanelContext();
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
session()->put(WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY, [
(string) $tenant->workspace_id => (int) $tenant->getKey(),
]);
}
function governanceArtifactAuditRecord(ManagedEnvironment $tenant, string $resourceType, int|string $resourceId): AuditLog
{
return AuditLog::query()->create([
'workspace_id' => (int) $tenant->workspace_id,
'managed_environment_id' => (int) $tenant->getKey(),
'actor_email' => 'governance@example.com',
'actor_name' => 'Governance Operator',
'action' => 'governance.updated',
'status' => 'success',
'resource_type' => $resourceType,
'resource_id' => (string) $resourceId,
'summary' => 'Governance resource updated',
'metadata' => [],
'recorded_at' => now(),
]);
}
it('keeps evidence snapshot truth drillthroughs on workspace-first operation routes', function (): void {
$tenant = ManagedEnvironment::factory()->create();
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', setUiContext: false);
$snapshot = seedTenantReviewEvidence($tenant);
$run = OperationRun::factory()
->forTenant($tenant)
->create([
'type' => OperationRunType::EvidenceSnapshotGenerate->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
]);
$snapshot->forceFill([
'operation_run_id' => (int) $run->getKey(),
])->save();
$this->actingAs($user);
setGovernanceArtifactAdminContext($tenant);
$envelope = app(ArtifactTruthPresenter::class)->forEvidenceSnapshotFresh($snapshot->fresh());
expect($envelope->nextActionUrl)->toBe(OperationRunLinks::tenantlessView($run))
->and(parse_url((string) $envelope->nextActionUrl, PHP_URL_PATH))
->toBe('/admin/workspaces/'.$tenant->workspace->getRouteKey().'/operations/'.$run->getRouteKey())
->not->toContain('/admin/t/');
});
it('builds workspace-first related record links for governance operations', function (): void {
$tenant = ManagedEnvironment::factory()->create();
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', setUiContext: false);
$backupSet = BackupSet::factory()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
]);
$backupSetRun = OperationRun::factory()
->forTenant($tenant)
->create([
'type' => OperationRunType::BackupSetUpdate->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
'context' => [
'backup_set_id' => (int) $backupSet->getKey(),
],
]);
$restoreRun = RestoreRun::factory()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
]);
$restoreOperation = OperationRun::factory()
->forTenant($tenant)
->create([
'type' => OperationRunType::RestoreExecute->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
'context' => [
'restore_run_id' => (int) $restoreRun->getKey(),
],
]);
$snapshot = seedTenantReviewEvidence($tenant);
$snapshotRun = OperationRun::factory()
->forTenant($tenant)
->create([
'type' => OperationRunType::EvidenceSnapshotGenerate->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
]);
$snapshot->forceFill([
'operation_run_id' => (int) $snapshotRun->getKey(),
])->save();
$review = composeTenantReviewForTest($tenant, $user, $snapshot);
$reviewRun = OperationRun::factory()
->forTenant($tenant)
->create([
'type' => OperationRunType::TenantReviewCompose->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
]);
$review->forceFill([
'operation_run_id' => (int) $reviewRun->getKey(),
])->save();
$packRun = OperationRun::factory()
->forTenant($tenant)
->create([
'type' => OperationRunType::ReviewPackGenerate->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
]);
$pack = ReviewPack::factory()->ready()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'tenant_review_id' => (int) $review->getKey(),
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'initiated_by_user_id' => (int) $user->getKey(),
'operation_run_id' => (int) $packRun->getKey(),
'expires_at' => now()->addDay(),
]);
$this->actingAs($user);
setGovernanceArtifactAdminContext($tenant);
$backupLinks = OperationRunLinks::related($backupSetRun, $tenant);
$restoreLinks = OperationRunLinks::related($restoreOperation, $tenant);
$snapshotLinks = OperationRunLinks::related($snapshotRun, $tenant);
$reviewLinks = OperationRunLinks::related($reviewRun, $tenant);
$packLinks = OperationRunLinks::related($packRun, $tenant);
expect($backupLinks)->toMatchArray([
OperationRunLinks::collectionLabel() => OperationRunLinks::index($tenant),
'Backup Sets' => BackupSetResource::getUrl('index', tenant: $tenant),
'Backup Set' => BackupSetResource::getUrl('view', ['record' => (int) $backupSet->getKey()], tenant: $tenant),
]);
expect($restoreLinks)->toMatchArray([
OperationRunLinks::collectionLabel() => OperationRunLinks::index($tenant),
'Restore Runs' => RestoreRunResource::getUrl('index', tenant: $tenant),
'Restore Run' => RestoreRunResource::getUrl('view', ['record' => (int) $restoreRun->getKey()], tenant: $tenant),
]);
expect($snapshotLinks)->toMatchArray([
OperationRunLinks::collectionLabel() => OperationRunLinks::index($tenant),
'Evidence Snapshot' => EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant),
]);
expect($reviewLinks)->toMatchArray([
OperationRunLinks::collectionLabel() => OperationRunLinks::index($tenant),
'ManagedEnvironment Review' => TenantReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant),
]);
expect($packLinks)->toMatchArray([
OperationRunLinks::collectionLabel() => OperationRunLinks::index($tenant),
'Review Pack' => ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant),
]);
foreach ([$backupLinks, $restoreLinks, $snapshotLinks, $reviewLinks, $packLinks] as $links) {
foreach ($links as $url) {
expect(parse_url($url, PHP_URL_PATH))
->toBeString()
->not->toContain('/admin/t/');
}
}
});
it('builds workspace-first audit target links for governance artifact records', function (): void {
$tenant = ManagedEnvironment::factory()->create();
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', setUiContext: false);
$backupSet = BackupSet::factory()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
]);
$restoreRun = RestoreRun::factory()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
]);
$finding = Finding::factory()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
]);
$findingException = FindingException::query()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'finding_id' => (int) $finding->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' => 'Temporary exception request',
'requested_at' => now(),
'review_due_at' => now()->addWeek(),
'evidence_summary' => ['reference_count' => 0],
]);
$decision = $findingException->decisions()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'actor_user_id' => (int) $user->getKey(),
'decision_type' => FindingExceptionDecision::TYPE_REQUESTED,
'reason' => 'Temporary exception request',
'metadata' => [],
'decided_at' => now(),
]);
$findingException->forceFill([
'current_decision_id' => (int) $decision->getKey(),
])->save();
$this->actingAs($user);
setGovernanceArtifactAdminContext($tenant);
$resolver = app(RelatedNavigationResolver::class);
$cases = [
[
'audit' => governanceArtifactAuditRecord($tenant, 'backup_set', (int) $backupSet->getKey()),
'label' => 'Open backup set',
'url' => BackupSetResource::getUrl('view', ['record' => (int) $backupSet->getKey()], tenant: $tenant),
],
[
'audit' => governanceArtifactAuditRecord($tenant, 'restore_run', (int) $restoreRun->getKey()),
'label' => 'Open restore run',
'url' => RestoreRunResource::getUrl('view', ['record' => (int) $restoreRun->getKey()], tenant: $tenant),
],
[
'audit' => governanceArtifactAuditRecord($tenant, 'finding', (int) $finding->getKey()),
'label' => 'Open finding',
'url' => FindingResource::getUrl('view', ['record' => (int) $finding->getKey()], tenant: $tenant),
],
[
'audit' => governanceArtifactAuditRecord($tenant, 'finding_exception', (int) $findingException->getKey()),
'label' => 'Open finding exception',
'url' => FindingExceptionResource::getUrl('view', ['record' => $findingException], tenant: $tenant),
],
];
foreach ($cases as $case) {
$link = $resolver->auditTargetLink($case['audit']);
expect($link)->toMatchArray([
'label' => $case['label'],
'url' => $case['url'],
]);
expect(parse_url($link['url'], PHP_URL_PATH))
->toBeString()
->not->toContain('/admin/t/');
}
});