436 lines
20 KiB
PHP
436 lines
20 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
|
use App\Filament\Resources\EnvironmentReviewResource;
|
|
use App\Models\Finding;
|
|
use App\Models\FindingException;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\User;
|
|
use App\Support\EnvironmentReviewStatus;
|
|
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('lists only the latest published review per entitled tenant on the customer review workspace', function (): void {
|
|
$tenantA = ManagedEnvironment::factory()->create(['name' => 'Alpha ManagedEnvironment']);
|
|
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'readonly');
|
|
|
|
$tenantB = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'name' => 'Beta ManagedEnvironment',
|
|
]);
|
|
createUserWithTenant(tenant: $tenantB, user: $user, role: 'readonly');
|
|
|
|
$tenantDenied = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'name' => 'Denied ManagedEnvironment',
|
|
]);
|
|
$otherOwner = User::factory()->create();
|
|
createUserWithTenant(tenant: $tenantDenied, user: $otherOwner, role: 'owner');
|
|
|
|
$tenantASnapshot = seedEnvironmentReviewEvidence($tenantA);
|
|
$tenantBSnapshot = seedEnvironmentReviewEvidence($tenantB);
|
|
$tenantDeniedSnapshot = seedEnvironmentReviewEvidence($tenantDenied);
|
|
|
|
$olderPublishedReview = composeEnvironmentReviewForTest($tenantA, $user, $tenantASnapshot);
|
|
$olderPublishedReview->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'generated_at' => now()->subDays(3),
|
|
'published_at' => now()->subDays(3),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$newerInternalReview = $olderPublishedReview->replicate();
|
|
$newerInternalReview->forceFill([
|
|
'managed_environment_id' => (int) $tenantA->getKey(),
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'evidence_snapshot_id' => (int) $tenantASnapshot->getKey(),
|
|
'status' => EnvironmentReviewStatus::Ready->value,
|
|
'generated_at' => now()->subDay(),
|
|
'published_at' => null,
|
|
'published_by_user_id' => null,
|
|
])->save();
|
|
|
|
$latestPublishedReview = $olderPublishedReview->replicate();
|
|
$latestPublishedReview->forceFill([
|
|
'managed_environment_id' => (int) $tenantA->getKey(),
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'evidence_snapshot_id' => (int) $tenantASnapshot->getKey(),
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'generated_at' => now(),
|
|
'published_at' => now(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$betaPublishedReview = composeEnvironmentReviewForTest($tenantB, $user, $tenantBSnapshot);
|
|
$betaPublishedReview->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'generated_at' => now()->subHours(2),
|
|
'published_at' => now()->subHours(2),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$deniedPublishedReview = composeEnvironmentReviewForTest($tenantDenied, $otherOwner, $tenantDeniedSnapshot);
|
|
$deniedPublishedReview->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'generated_at' => now()->subHours(3),
|
|
'published_at' => now()->subHours(3),
|
|
'published_by_user_id' => (int) $otherOwner->getKey(),
|
|
])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertCanSeeTableRecords([$tenantA->fresh(), $tenantB->fresh()])
|
|
->assertCanNotSeeTableRecords([$tenantDenied->fresh()])
|
|
->assertSee(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $latestPublishedReview->fresh()], $tenantA), false)
|
|
->assertSee('Review the executive-ready governance package status for each entitled tenant and open the customer-safe detail when follow-up is needed.')
|
|
->assertSee('Each row is an index entry: open the review detail to inspect package status, the executive entrypoint, supporting evidence, current risks, and the next customer-safe action.')
|
|
->assertSee('This workspace summarizes current review evidence for service delivery. It does not replace a formal audit opinion, certification, or legal attestation.')
|
|
->assertSee('Governance package')
|
|
->assertSee('Status')
|
|
->assertSee('Evidence')
|
|
->assertSee('Next step')
|
|
->assertSee('Open')
|
|
->assertSee('Open review')
|
|
->assertDontSee('Assessment status')
|
|
->assertDontSee('Publishable')
|
|
->assertDontSee('No mapped controls')
|
|
->assertDontSee('Compliance evidence mapping v1')
|
|
->assertDontSee(ComplianceEvidenceMappingV1::VERSION_KEY)
|
|
->assertSee(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $betaPublishedReview->fresh()], $tenantB), false)
|
|
->assertDontSee(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $olderPublishedReview->fresh()], $tenantA), false)
|
|
->assertDontSee(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $newerInternalReview->fresh()], $tenantA), false)
|
|
->assertDontSee('Publish review')
|
|
->assertDontSee('Refresh review')
|
|
->assertDontSee('Create next review')
|
|
->assertDontSee('Regenerate')
|
|
->assertDontSee('Expire snapshot')
|
|
->assertDontSee(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $deniedPublishedReview->fresh()], $tenantDenied), false);
|
|
});
|
|
|
|
it('shows the current released review using deterministic published review ordering', function (): void {
|
|
$publishedAt = now()->subHour();
|
|
$tenantA = ManagedEnvironment::factory()->create(['name' => 'Alpha ManagedEnvironment']);
|
|
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'readonly');
|
|
|
|
$tenantB = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'name' => 'Beta ManagedEnvironment',
|
|
]);
|
|
createUserWithTenant(tenant: $tenantB, user: $user, role: 'readonly');
|
|
|
|
$snapshotA = seedEnvironmentReviewEvidence($tenantA);
|
|
$snapshotB = seedEnvironmentReviewEvidence($tenantB);
|
|
|
|
$alphaReview = composeEnvironmentReviewForTest($tenantA, $user, $snapshotA);
|
|
$alphaReview->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'generated_at' => $publishedAt,
|
|
'published_at' => $publishedAt,
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$betaReview = composeEnvironmentReviewForTest($tenantB, $user, $snapshotB);
|
|
$betaReview->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'generated_at' => $publishedAt,
|
|
'published_at' => $publishedAt,
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertSeeInOrder([
|
|
'Latest released review',
|
|
'Beta ManagedEnvironment',
|
|
$publishedAt->format('M j, Y'),
|
|
'No decisions require awareness',
|
|
])
|
|
->assertSee(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $betaReview->fresh()], $tenantB), false);
|
|
});
|
|
|
|
it('excludes entitled tenants without a published review from customer workspace rows', function (): void {
|
|
$tenantPublished = ManagedEnvironment::factory()->create(['name' => 'Published ManagedEnvironment']);
|
|
[$user, $tenantPublished] = createUserWithTenant(tenant: $tenantPublished, role: 'readonly');
|
|
|
|
$tenantWithoutPublished = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantPublished->workspace_id,
|
|
'name' => 'No Published ManagedEnvironment',
|
|
]);
|
|
createUserWithTenant(tenant: $tenantWithoutPublished, user: $user, role: 'readonly');
|
|
|
|
$publishedSnapshot = seedEnvironmentReviewEvidence($tenantPublished);
|
|
$noPublishedSnapshot = seedEnvironmentReviewEvidence($tenantWithoutPublished);
|
|
|
|
$publishedReview = composeEnvironmentReviewForTest($tenantPublished, $user, $publishedSnapshot);
|
|
$publishedReview->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now()->subHour(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$internalOnlyReview = composeEnvironmentReviewForTest($tenantWithoutPublished, $user, $noPublishedSnapshot);
|
|
$internalOnlyReview->forceFill([
|
|
'status' => EnvironmentReviewStatus::Ready->value,
|
|
'published_at' => null,
|
|
'published_by_user_id' => null,
|
|
])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantPublished->workspace_id);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertCanSeeTableRecords([$tenantPublished->fresh()])
|
|
->assertCanNotSeeTableRecords([$tenantWithoutPublished->fresh()])
|
|
->assertDontSee('No published review')
|
|
->assertDontSee('No published review available yet')
|
|
->assertDontSee(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $internalOnlyReview->fresh()], $tenantWithoutPublished), false)
|
|
->assertSee(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $publishedReview->fresh()], $tenantPublished), false);
|
|
});
|
|
|
|
it('uses a filter-aware empty state when the active environment has no released review', function (): void {
|
|
$tenantPublished = ManagedEnvironment::factory()->create(['name' => 'Published ManagedEnvironment']);
|
|
[$user, $tenantPublished] = createUserWithTenant(tenant: $tenantPublished, role: 'readonly');
|
|
|
|
$tenantWithoutPublished = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantPublished->workspace_id,
|
|
'name' => 'Filtered ManagedEnvironment',
|
|
]);
|
|
createUserWithTenant(tenant: $tenantWithoutPublished, user: $user, role: 'readonly');
|
|
|
|
$publishedReview = composeEnvironmentReviewForTest($tenantPublished, $user, seedEnvironmentReviewEvidence($tenantPublished));
|
|
$publishedReview->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$internalReview = composeEnvironmentReviewForTest($tenantWithoutPublished, $user, seedEnvironmentReviewEvidence($tenantWithoutPublished));
|
|
$internalReview->forceFill([
|
|
'status' => EnvironmentReviewStatus::Ready->value,
|
|
'published_at' => null,
|
|
'published_by_user_id' => null,
|
|
])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantPublished->workspace_id);
|
|
|
|
Livewire::withQueryParams(['environment_id' => (int) $tenantWithoutPublished->getKey()])
|
|
->actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertSet('tableFilters.managed_environment_id.value', (string) $tenantWithoutPublished->getKey())
|
|
->assertSee('Environment filter:')
|
|
->assertSee('No released customer reviews match the active environment filter.')
|
|
->assertSee('Clear the environment filter to view other released reviews in this workspace.')
|
|
->assertCanNotSeeTableRecords([$tenantPublished->fresh(), $tenantWithoutPublished->fresh()])
|
|
->assertDontSee('Publish an environment review before it appears in the customer-safe workspace.');
|
|
});
|
|
|
|
it('uses a page-level empty state when no entitled tenant has a released review', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create(['name' => 'Internal Only ManagedEnvironment']);
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');
|
|
$snapshot = seedEnvironmentReviewEvidence($tenant);
|
|
|
|
$internalOnlyReview = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
|
|
$internalOnlyReview->forceFill([
|
|
'status' => EnvironmentReviewStatus::Ready->value,
|
|
'published_at' => null,
|
|
'published_by_user_id' => null,
|
|
])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertCanNotSeeTableRecords([$tenant->fresh()])
|
|
->assertSee('No released customer reviews match this view')
|
|
->assertSee('Publish an environment review before it appears in the customer-safe workspace.')
|
|
->assertDontSee(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $internalOnlyReview->fresh()], $tenant), false);
|
|
});
|
|
|
|
it('summarizes accepted risks from the released review without exposing internal accountability details', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create(['name' => 'Governed ManagedEnvironment']);
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');
|
|
$owner = User::factory()->create(['name' => 'Risk Owner']);
|
|
$finding = Finding::factory()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'status' => Finding::STATUS_RISK_ACCEPTED,
|
|
]);
|
|
|
|
FindingException::query()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'finding_id' => (int) $finding->getKey(),
|
|
'status' => FindingException::STATUS_ACTIVE,
|
|
'current_validity_state' => FindingException::VALIDITY_VALID,
|
|
'requested_by_user_id' => (int) $user->getKey(),
|
|
'request_reason' => 'Vendor patch window accepted by the customer.',
|
|
'owner_user_id' => (int) $owner->getKey(),
|
|
'approved_by_user_id' => (int) $owner->getKey(),
|
|
'requested_at' => now()->subDays(2),
|
|
'approved_at' => now()->subDay(),
|
|
'effective_from' => now()->subDay(),
|
|
'review_due_at' => now()->addDays(14),
|
|
]);
|
|
|
|
$snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 0, driftCount: 0);
|
|
|
|
$review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
|
|
$review->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertCanSeeTableRecords([$tenant->fresh()])
|
|
->assertSee('Accepted risks')
|
|
->assertSee('Accepted risk')
|
|
->assertSee('Included in the released review evidence basis.')
|
|
->assertSee('Review required')
|
|
->assertSee('Open review')
|
|
->assertDontSee('Ready for release')
|
|
->assertDontSee('Risk Owner')
|
|
->assertDontSee('Vendor patch window accepted by the customer.')
|
|
->assertDontSee('1 evidence signal(s) reference this control.')
|
|
->assertDontSee('1 accepted-risk finding(s) qualify this view.')
|
|
->assertDontSee('Review the accepted-risk owner and next review date before customer delivery.')
|
|
->assertDontSee('Accepted risk influences this view');
|
|
});
|
|
|
|
it('renders legacy released reviews without a spec-308 decision summary as customer-safe unavailable evidence', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create(['name' => 'Legacy ManagedEnvironment']);
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');
|
|
|
|
$review = composeEnvironmentReviewForTest($tenant, $user, seedEnvironmentReviewEvidence($tenant));
|
|
$review->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
'summary' => [
|
|
'finding_count' => 1,
|
|
'debug_payload' => 'raw evidence JSON',
|
|
'source_fingerprint' => 'legacy-fingerprint-abc123',
|
|
'operation_run_url' => '/admin/t/legacy/operation-runs/999',
|
|
],
|
|
])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertSee('Decision evidence unavailable')
|
|
->assertSee('Customer-safe decision evidence is unavailable for this released review.')
|
|
->assertDontSee('raw evidence JSON')
|
|
->assertDontSee('legacy-fingerprint-abc123')
|
|
->assertDontSee('operation-runs')
|
|
->assertDontSee('/admin/t', false);
|
|
});
|
|
|
|
it('keeps the customer review workspace unfiltered when remembered tenant context is available', function (): void {
|
|
$tenantA = ManagedEnvironment::factory()->create(['name' => 'Alpha ManagedEnvironment']);
|
|
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'readonly');
|
|
|
|
$tenantB = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'name' => 'Beta ManagedEnvironment',
|
|
]);
|
|
createUserWithTenant(tenant: $tenantB, user: $user, role: 'readonly');
|
|
|
|
$snapshotA = seedEnvironmentReviewEvidence($tenantA);
|
|
$snapshotB = seedEnvironmentReviewEvidence($tenantB);
|
|
|
|
$reviewA = composeEnvironmentReviewForTest($tenantA, $user, $snapshotA);
|
|
$reviewA->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now()->subDay(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$reviewB = composeEnvironmentReviewForTest($tenantB, $user, $snapshotB);
|
|
$reviewB->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
|
|
session()->put(WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY, [
|
|
(string) $tenantA->workspace_id => (int) $tenantB->getKey(),
|
|
]);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertSet('tableFilters.managed_environment_id.value', null)
|
|
->assertCanSeeTableRecords([$tenantA->fresh(), $tenantB->fresh()])
|
|
->filterTable('managed_environment_id', (string) $tenantB->getKey())
|
|
->assertCanSeeTableRecords([$tenantB->fresh()])
|
|
->assertCanNotSeeTableRecords([$tenantA->fresh()]);
|
|
});
|
|
|
|
it('prefilters the customer review workspace from an explicit tenant query parameter and accepts external tenant identifiers', function (): void {
|
|
$tenantA = ManagedEnvironment::factory()->create(['name' => 'Alpha ManagedEnvironment']);
|
|
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'readonly');
|
|
|
|
$tenantB = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'name' => 'Beta ManagedEnvironment',
|
|
]);
|
|
createUserWithTenant(tenant: $tenantB, user: $user, role: 'readonly');
|
|
|
|
$snapshotA = seedEnvironmentReviewEvidence($tenantA);
|
|
$snapshotB = seedEnvironmentReviewEvidence($tenantB);
|
|
|
|
$reviewA = composeEnvironmentReviewForTest($tenantA, $user, $snapshotA);
|
|
$reviewA->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$reviewB = composeEnvironmentReviewForTest($tenantB, $user, $snapshotB);
|
|
$reviewB->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now()->subDay(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
|
|
|
|
Livewire::withQueryParams(['environment_id' => (int) $tenantA->getKey()])
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertSet('tableFilters.managed_environment_id.value', (string) $tenantA->getKey())
|
|
->filterTable('managed_environment_id', (string) $tenantA->getKey())
|
|
->assertCanSeeTableRecords([$tenantA->fresh()])
|
|
->assertCanNotSeeTableRecords([$tenantB->fresh()]);
|
|
});
|