create(); createUserWithTenant(tenant: $tenant, user: $approver, role: 'owner', workspaceRole: 'manager'); $finding = Finding::factory() ->for($tenant) ->riskAccepted() ->create(array_merge([ 'workspace_id' => (int) $tenant->workspace_id, ], $findingAttributes)); $exception = FindingException::query()->create(array_merge([ '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' => (int) $user->getKey(), 'approved_by_user_id' => (int) $approver->getKey(), 'status' => FindingException::STATUS_ACTIVE, 'current_validity_state' => FindingException::VALIDITY_VALID, 'request_reason' => 'Spec354 detail request', 'approval_reason' => 'Spec354 detail approval', 'requested_at' => now()->subDays(6), 'approved_at' => now()->subDays(5), 'effective_from' => now()->subDays(5), 'review_due_at' => now()->addDays(10), 'expires_at' => now()->addDays(30), 'evidence_summary' => ['reference_count' => 0], ], $exceptionAttributes)); if ($decisionType !== null) { $decision = $exception->decisions()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'actor_user_id' => (int) $approver->getKey(), 'decision_type' => $decisionType, 'reason' => 'Spec354 detail decision', 'metadata' => [], 'decided_at' => now()->subDays(5), ]); $exception->forceFill(['current_decision_id' => (int) $decision->getKey()])->save(); } test()->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id); return [$user, $tenant, $finding->fresh(), $exception->fresh(['finding', 'tenant', 'owner', 'currentDecision', 'decisions.actor', 'evidenceReferences'])]; } it('renders dominant guidance before decision history and keeps incomplete governance visible first', function (): void { [, , , $exception] = spec354DetailFixture(exceptionAttributes: [ 'owner_user_id' => null, 'request_reason' => '', 'review_due_at' => null, ]); $component = Livewire::test(ViewFindingException::class, ['record' => $exception->getKey()]) ->assertOk() ->assertSee(__('localization.accepted_risk_guidance.title_incomplete_governance')) ->assertSee(__('localization.accepted_risk_guidance.detail_missing_fields_label')) ->assertActionVisible('renew_exception') ->assertActionVisible('revoke_exception'); $html = $component->html(); expect(strpos($html, __('localization.accepted_risk_guidance.title_incomplete_governance'))) ->toBeLessThan(strpos($html, 'Decision history')); }); it('keeps readonly detail semantics aligned while hiding manage actions', function (): void { [$user, $tenant, , $exception] = spec354DetailFixture(role: 'readonly', exceptionAttributes: [ 'status' => FindingException::STATUS_EXPIRING, 'current_validity_state' => FindingException::VALIDITY_EXPIRING, 'review_due_at' => now()->addDay(), 'expires_at' => now()->addDays(2), ]); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, ]); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::test(ViewFindingException::class, ['record' => $exception->getKey()]) ->assertOk() ->assertSee(__('localization.accepted_risk_guidance.title_expiring')) ->assertActionHidden('renew_exception') ->assertActionHidden('revoke_exception') ->assertDontSee(__('localization.accepted_risk_guidance.action_open_queue')); }); it('keeps existing related context scoped and renders without outbound http', function (): void { bindFailHardGraphClient(); [, $tenant, , $exception] = spec354DetailFixture(); assertNoOutboundHttp(function () use ($tenant, $exception): void { $response = $this->get(FindingExceptionResource::getUrl('view', ['record' => $exception], tenant: $tenant)); $response->assertSuccessful() ->assertSee(__('localization.accepted_risk_guidance.title_ready')) ->assertSee(__('localization.accepted_risk_guidance.action_open_finding')) ->assertSee(__('localization.accepted_risk_guidance.action_open_queue')); }); }); it('routes detail guidance and related-context queue links through the explicit queue filter contract', function (): void { [, $tenant, , $exception] = spec354DetailFixture(); $originalQuery = request()->query(); $context = CanonicalNavigationContext::forDecisionRegister( canonicalRouteName: DecisionRegister::getRouteName(), tenantId: (int) $tenant->getKey(), backLinkUrl: DecisionRegister::getUrl(panel: 'admin', parameters: [ 'managed_environment_id' => (string) $tenant->getKey(), ]), ); request()->query->replace($context->toQuery()); $guidance = FindingExceptionResource::acceptedRiskGuidance($exception); $relatedContext = FindingExceptionResource::relatedContextEntries($exception); $guidanceQueueAction = collect($guidance['secondary_actions'])->firstWhere('key', 'accepted_risk.ready.open_queue'); $relatedQueueEntry = collect($relatedContext)->firstWhere('key', 'approval_queue'); expect($guidanceQueueAction)->toBeArray() ->and($relatedQueueEntry)->toBeArray() ->and((string) $guidanceQueueAction['url'])->toContain('environment_id='.(string) $tenant->getKey()) ->and((string) $guidanceQueueAction['url'])->toContain('exception='.(string) $exception->getKey()) ->and((string) $guidanceQueueAction['url'])->toContain('nav%5Bsource_surface%5D=governance.decision_register') ->and((string) $guidanceQueueAction['url'])->not->toContain('tenant=') ->and((string) $relatedQueueEntry['targetUrl'])->toContain('environment_id='.(string) $tenant->getKey()) ->and((string) $relatedQueueEntry['targetUrl'])->toContain('exception='.(string) $exception->getKey()) ->and((string) $relatedQueueEntry['targetUrl'])->not->toContain('tenant='); parse_str((string) parse_url((string) $guidanceQueueAction['url'], PHP_URL_QUERY), $guidanceQuery); parse_str((string) parse_url((string) $relatedQueueEntry['targetUrl'], PHP_URL_QUERY), $relatedQuery); setAdminPanelContext(); session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id); Livewire::withQueryParams($guidanceQuery) ->test(FindingExceptionsQueue::class) ->assertActionVisible('view_tenant_register') ->assertSet('selectedFindingExceptionId', (int) $exception->getKey()); Livewire::withQueryParams($relatedQuery) ->test(FindingExceptionsQueue::class) ->assertActionVisible('view_tenant_register') ->assertSet('selectedFindingExceptionId', (int) $exception->getKey()); request()->query->replace($originalQuery); }); it('promotes pending detail guidance to a real approval-queue affordance', function (): void { [, $tenant, , $exception] = spec354DetailFixture( exceptionAttributes: [ 'status' => FindingException::STATUS_PENDING, 'current_validity_state' => FindingException::VALIDITY_VALID, ], decisionType: FindingExceptionDecision::TYPE_RENEWAL_REQUESTED, ); $guidance = FindingExceptionResource::acceptedRiskGuidance($exception); expect($guidance['title'])->toBe(__('localization.accepted_risk_guidance.title_pending_renewal')) ->and($guidance['primary_action']['url'])->toBeString() ->and((string) $guidance['primary_action']['url'])->toContain('environment_id='.(string) $tenant->getKey()) ->and((string) $guidance['primary_action']['url'])->toContain('exception='.(string) $exception->getKey()) ->and($guidance['primary_action']['type'])->toBe('navigation'); });