260 lines
9.8 KiB
PHP
260 lines
9.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\Governance\DecisionRegister;
|
|
use App\Models\EvidenceSnapshot;
|
|
use App\Models\Finding;
|
|
use App\Models\FindingException;
|
|
use App\Models\FindingExceptionDecision;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\OperationRun;
|
|
use App\Models\User;
|
|
use App\Support\Evidence\EvidenceCompletenessState;
|
|
use App\Support\Evidence\EvidenceSnapshotStatus;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('renders open and recently closed decision rows for visible tenants only', function (): void {
|
|
$visibleTenant = ManagedEnvironment::factory()->create([
|
|
'status' => 'active',
|
|
'name' => 'Visible ManagedEnvironment',
|
|
'external_id' => 'visible-tenant',
|
|
]);
|
|
[$user, $visibleTenant] = createUserWithTenant($visibleTenant, role: 'readonly', workspaceRole: 'readonly');
|
|
|
|
$hiddenTenant = ManagedEnvironment::factory()->create([
|
|
'status' => 'active',
|
|
'workspace_id' => (int) $visibleTenant->workspace_id,
|
|
'name' => 'Hidden ManagedEnvironment',
|
|
'external_id' => 'hidden-tenant',
|
|
]);
|
|
|
|
decisionRegisterPageException(
|
|
tenant: $visibleTenant,
|
|
actor: $user,
|
|
status: FindingException::STATUS_PENDING,
|
|
validityState: FindingException::VALIDITY_MISSING_SUPPORT,
|
|
decisionType: FindingExceptionDecision::TYPE_REQUESTED,
|
|
decisionReason: 'Visible approval request',
|
|
exceptionAttributes: [
|
|
'requested_at' => now()->subDays(2),
|
|
'review_due_at' => now()->addDay(),
|
|
],
|
|
decisionAttributes: [
|
|
'decided_at' => now()->subDays(2),
|
|
],
|
|
);
|
|
|
|
decisionRegisterPageException(
|
|
tenant: $visibleTenant,
|
|
actor: $user,
|
|
status: FindingException::STATUS_REJECTED,
|
|
validityState: FindingException::VALIDITY_REJECTED,
|
|
decisionType: FindingExceptionDecision::TYPE_REJECTED,
|
|
decisionReason: 'Recently rejected closure reason',
|
|
exceptionAttributes: [
|
|
'rejected_at' => now()->subDays(2),
|
|
'review_due_at' => now()->subDays(3),
|
|
],
|
|
decisionAttributes: [
|
|
'decided_at' => now()->subDays(2),
|
|
],
|
|
);
|
|
|
|
decisionRegisterPageException(
|
|
tenant: $hiddenTenant,
|
|
actor: $user,
|
|
status: FindingException::STATUS_PENDING,
|
|
validityState: FindingException::VALIDITY_MISSING_SUPPORT,
|
|
decisionType: FindingExceptionDecision::TYPE_REQUESTED,
|
|
decisionReason: 'Hidden tenant request',
|
|
);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $visibleTenant->workspace_id])
|
|
->get(DecisionRegister::getUrl(panel: 'admin'))
|
|
->assertOk()
|
|
->assertSee('Decision register')
|
|
->assertSee('Visible ManagedEnvironment')
|
|
->assertSee('Review approval')
|
|
->assertSee('Open decision')
|
|
->assertDontSee('Recently rejected closure reason')
|
|
->assertDontSee('Hidden tenant request');
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $visibleTenant->workspace_id])
|
|
->get(DecisionRegister::getUrl(panel: 'admin').'?register_state=recently_closed')
|
|
->assertOk()
|
|
->assertSee('Recently rejected closure reason')
|
|
->assertDontSee('Visible approval request');
|
|
});
|
|
|
|
it('shows truthful filtered empty states for tenant and register-state filters', 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'],
|
|
]);
|
|
|
|
decisionRegisterPageException(
|
|
tenant: $bravoTenant,
|
|
actor: $user,
|
|
status: FindingException::STATUS_PENDING,
|
|
validityState: FindingException::VALIDITY_MISSING_SUPPORT,
|
|
decisionType: FindingExceptionDecision::TYPE_REQUESTED,
|
|
decisionReason: 'Bravo tenant request',
|
|
);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id])
|
|
->get(DecisionRegister::getUrl(panel: 'admin').'?managed_environment_id='.(string) $alphaTenant->getKey())
|
|
->assertOk()
|
|
->assertSee('This environment filter is hiding other visible decision follow-through')
|
|
->assertSee('Clear environment filter');
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id])
|
|
->get(DecisionRegister::getUrl(panel: 'admin').'?register_state=recently_closed')
|
|
->assertOk()
|
|
->assertSee('No recently closed decisions match this filter right now.');
|
|
});
|
|
|
|
it('renders proof and operation affordances only when real links are available', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create([
|
|
'status' => 'active',
|
|
'name' => 'Proof ManagedEnvironment',
|
|
'external_id' => 'proof-tenant',
|
|
]);
|
|
[$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'owner');
|
|
|
|
$run = OperationRun::factory()->forTenant($tenant)->create([
|
|
'type' => 'tenant.evidence.snapshot.generate',
|
|
'status' => OperationRunStatus::Completed->value,
|
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
|
'completed_at' => now(),
|
|
]);
|
|
|
|
$snapshot = EvidenceSnapshot::query()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'operation_run_id' => (int) $run->getKey(),
|
|
'status' => EvidenceSnapshotStatus::Active->value,
|
|
'completeness_state' => EvidenceCompletenessState::Complete->value,
|
|
'summary' => ['finding_count' => 1],
|
|
'generated_at' => now(),
|
|
]);
|
|
|
|
$withProof = decisionRegisterPageException(
|
|
tenant: $tenant,
|
|
actor: $user,
|
|
status: FindingException::STATUS_PENDING,
|
|
validityState: FindingException::VALIDITY_MISSING_SUPPORT,
|
|
decisionType: FindingExceptionDecision::TYPE_REQUESTED,
|
|
decisionReason: 'Proof-backed request',
|
|
exceptionAttributes: [
|
|
'evidence_summary' => ['reference_count' => 1],
|
|
'review_due_at' => now()->addDay(),
|
|
],
|
|
);
|
|
|
|
$withProof->evidenceReferences()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'source_type' => 'evidence_snapshot',
|
|
'source_id' => (string) $snapshot->getKey(),
|
|
'label' => 'Current evidence snapshot',
|
|
'summary_payload' => [],
|
|
]);
|
|
|
|
decisionRegisterPageException(
|
|
tenant: $tenant,
|
|
actor: $user,
|
|
status: FindingException::STATUS_ACTIVE,
|
|
validityState: FindingException::VALIDITY_VALID,
|
|
decisionType: FindingExceptionDecision::TYPE_APPROVED,
|
|
decisionReason: 'No proof request',
|
|
exceptionAttributes: [
|
|
'approved_by_user_id' => (int) $user->getKey(),
|
|
'approved_at' => now()->subDay(),
|
|
'effective_from' => now()->subDay(),
|
|
'evidence_summary' => ['reference_count' => 0],
|
|
'review_due_at' => now()->addDays(2),
|
|
],
|
|
);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(DecisionRegister::getUrl(panel: 'admin'))
|
|
->assertOk()
|
|
->assertSee('1 proof item')
|
|
->assertSee('View evidence')
|
|
->assertSee('View operation')
|
|
->assertSee('No linked proof')
|
|
->assertSee('No operation linked')
|
|
->assertDontSee('/admin/t', false);
|
|
});
|
|
|
|
/**
|
|
* @param array<string, mixed> $exceptionAttributes
|
|
* @param array<string, mixed> $decisionAttributes
|
|
*/
|
|
function decisionRegisterPageException(
|
|
ManagedEnvironment $tenant,
|
|
User $actor,
|
|
string $status,
|
|
string $validityState,
|
|
string $decisionType,
|
|
string $decisionReason,
|
|
array $exceptionAttributes = [],
|
|
array $decisionAttributes = [],
|
|
): FindingException {
|
|
$finding = Finding::factory()->for($tenant)->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
]);
|
|
|
|
$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) $actor->getKey(),
|
|
'owner_user_id' => (int) $actor->getKey(),
|
|
'status' => $status,
|
|
'current_validity_state' => $validityState,
|
|
'request_reason' => 'Decision register page test',
|
|
'requested_at' => now()->subDay(),
|
|
'review_due_at' => now()->addDay(),
|
|
'evidence_summary' => ['reference_count' => 0],
|
|
], $exceptionAttributes));
|
|
|
|
$decision = $exception->decisions()->create(array_merge([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'actor_user_id' => (int) $actor->getKey(),
|
|
'decision_type' => $decisionType,
|
|
'reason' => $decisionReason,
|
|
'metadata' => [],
|
|
'decided_at' => now()->subDay(),
|
|
], $decisionAttributes));
|
|
|
|
$exception->forceFill(['current_decision_id' => (int) $decision->getKey()])->save();
|
|
|
|
return $exception->fresh(['currentDecision']);
|
|
}
|