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/'); } });