TenantAtlas/apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php
ahmido 866875559f
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m4s
feat(specs/259): compliance evidence mapping (#312)
Implements platform feature branch `259-compliance-evidence-mapping`.

Target branch: `platform-dev`.

Follow-up integration path after merge:

`platform-dev` -> `dev`.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #312
2026-04-30 21:27:49 +00:00

304 lines
14 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Resources\TenantReviewResource;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\Tenant;
use App\Models\User;
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
use App\Support\TenantReviewStatus;
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 = Tenant::factory()->create(['name' => 'Alpha Tenant']);
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'readonly');
$tenantB = Tenant::factory()->create([
'workspace_id' => (int) $tenantA->workspace_id,
'name' => 'Beta Tenant',
]);
createUserWithTenant(tenant: $tenantB, user: $user, role: 'readonly');
$tenantDenied = Tenant::factory()->create([
'workspace_id' => (int) $tenantA->workspace_id,
'name' => 'Denied Tenant',
]);
$otherOwner = User::factory()->create();
createUserWithTenant(tenant: $tenantDenied, user: $otherOwner, role: 'owner');
$tenantASnapshot = seedTenantReviewEvidence($tenantA);
$tenantBSnapshot = seedTenantReviewEvidence($tenantB);
$tenantDeniedSnapshot = seedTenantReviewEvidence($tenantDenied);
$olderPublishedReview = composeTenantReviewForTest($tenantA, $user, $tenantASnapshot);
$olderPublishedReview->forceFill([
'status' => TenantReviewStatus::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([
'tenant_id' => (int) $tenantA->getKey(),
'workspace_id' => (int) $tenantA->workspace_id,
'evidence_snapshot_id' => (int) $tenantASnapshot->getKey(),
'status' => TenantReviewStatus::Ready->value,
'generated_at' => now()->subDay(),
'published_at' => null,
'published_by_user_id' => null,
])->save();
$latestPublishedReview = $olderPublishedReview->replicate();
$latestPublishedReview->forceFill([
'tenant_id' => (int) $tenantA->getKey(),
'workspace_id' => (int) $tenantA->workspace_id,
'evidence_snapshot_id' => (int) $tenantASnapshot->getKey(),
'status' => TenantReviewStatus::Published->value,
'generated_at' => now(),
'published_at' => now(),
'published_by_user_id' => (int) $user->getKey(),
])->save();
$betaPublishedReview = composeTenantReviewForTest($tenantB, $user, $tenantBSnapshot);
$betaPublishedReview->forceFill([
'status' => TenantReviewStatus::Published->value,
'generated_at' => now()->subHours(2),
'published_at' => now()->subHours(2),
'published_by_user_id' => (int) $user->getKey(),
])->save();
$deniedPublishedReview = composeTenantReviewForTest($tenantDenied, $otherOwner, $tenantDeniedSnapshot);
$deniedPublishedReview->forceFill([
'status' => TenantReviewStatus::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(TenantReviewResource::tenantScopedUrl('view', ['record' => $latestPublishedReview->fresh()], $tenantA), false)
->assertSee('Compliance evidence mapping v1')
->assertSee('This is not a certification, legal attestation, or compliance guarantee.')
->assertSee('Endpoint hardening and compliance')
->assertSee(ComplianceEvidenceMappingV1::VERSION_KEY)
->assertSee(TenantReviewResource::tenantScopedUrl('view', ['record' => $betaPublishedReview->fresh()], $tenantB), false)
->assertDontSee(TenantReviewResource::tenantScopedUrl('view', ['record' => $olderPublishedReview->fresh()], $tenantA), false)
->assertDontSee(TenantReviewResource::tenantScopedUrl('view', ['record' => $newerInternalReview->fresh()], $tenantA), false)
->assertDontSee('Publish review')
->assertDontSee('Refresh review')
->assertDontSee('Create next review')
->assertDontSee('Regenerate')
->assertDontSee('Expire snapshot')
->assertDontSee(TenantReviewResource::tenantScopedUrl('view', ['record' => $deniedPublishedReview->fresh()], $tenantDenied), false);
});
it('excludes entitled tenants without a published review from customer workspace rows', function (): void {
$tenantPublished = Tenant::factory()->create(['name' => 'Published Tenant']);
[$user, $tenantPublished] = createUserWithTenant(tenant: $tenantPublished, role: 'readonly');
$tenantWithoutPublished = Tenant::factory()->create([
'workspace_id' => (int) $tenantPublished->workspace_id,
'name' => 'No Published Tenant',
]);
createUserWithTenant(tenant: $tenantWithoutPublished, user: $user, role: 'readonly');
$publishedSnapshot = seedTenantReviewEvidence($tenantPublished);
$noPublishedSnapshot = seedTenantReviewEvidence($tenantWithoutPublished);
$publishedReview = composeTenantReviewForTest($tenantPublished, $user, $publishedSnapshot);
$publishedReview->forceFill([
'status' => TenantReviewStatus::Published->value,
'published_at' => now()->subHour(),
'published_by_user_id' => (int) $user->getKey(),
])->save();
$internalOnlyReview = composeTenantReviewForTest($tenantWithoutPublished, $user, $noPublishedSnapshot);
$internalOnlyReview->forceFill([
'status' => TenantReviewStatus::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(TenantReviewResource::tenantScopedUrl('view', ['record' => $internalOnlyReview->fresh()], $tenantWithoutPublished), false)
->assertSee(TenantReviewResource::tenantScopedUrl('view', ['record' => $publishedReview->fresh()], $tenantPublished), false);
});
it('uses a page-level empty state when no entitled tenant has a released review', function (): void {
$tenant = Tenant::factory()->create(['name' => 'Internal Only Tenant']);
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');
$snapshot = seedTenantReviewEvidence($tenant);
$internalOnlyReview = composeTenantReviewForTest($tenant, $user, $snapshot);
$internalOnlyReview->forceFill([
'status' => TenantReviewStatus::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 a tenant review before it appears in the customer-safe workspace.')
->assertDontSee(TenantReviewResource::tenantScopedUrl('view', ['record' => $internalOnlyReview->fresh()], $tenant), false);
});
it('summarizes accepted-risk control interpretation and evidence proof availability in customer-safe workspace rows', function (): void {
$tenant = Tenant::factory()->create(['name' => 'Governed Tenant']);
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');
$owner = User::factory()->create(['name' => 'Risk Owner']);
$finding = Finding::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'status' => Finding::STATUS_RISK_ACCEPTED,
]);
FindingException::query()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_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 = seedTenantReviewEvidence($tenant, findingCount: 0, driftCount: 0);
$review = composeTenantReviewForTest($tenant, $user, $snapshot);
$review->forceFill([
'status' => TenantReviewStatus::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('Review recommended')
->assertSee('1 evidence signal(s) reference this control.')
->assertSee('1 accepted-risk finding(s) qualify this view.')
->assertSee('Review the accepted-risk owner and next review date before customer delivery.')
->assertSee('Accepted risk influences this view');
});
it('defaults the customer review workspace to the remembered tenant when tenant context is available', function (): void {
$tenantA = Tenant::factory()->create(['name' => 'Alpha Tenant']);
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'readonly');
$tenantB = Tenant::factory()->create([
'workspace_id' => (int) $tenantA->workspace_id,
'name' => 'Beta Tenant',
]);
createUserWithTenant(tenant: $tenantB, user: $user, role: 'readonly');
$snapshotA = seedTenantReviewEvidence($tenantA);
$snapshotB = seedTenantReviewEvidence($tenantB);
$reviewA = composeTenantReviewForTest($tenantA, $user, $snapshotA);
$reviewA->forceFill([
'status' => TenantReviewStatus::Published->value,
'published_at' => now()->subDay(),
'published_by_user_id' => (int) $user->getKey(),
])->save();
$reviewB = composeTenantReviewForTest($tenantB, $user, $snapshotB);
$reviewB->forceFill([
'status' => TenantReviewStatus::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.tenant_id.value', (string) $tenantB->getKey())
->filterTable('tenant_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 = Tenant::factory()->create(['name' => 'Alpha Tenant']);
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'readonly');
$tenantB = Tenant::factory()->create([
'workspace_id' => (int) $tenantA->workspace_id,
'name' => 'Beta Tenant',
]);
createUserWithTenant(tenant: $tenantB, user: $user, role: 'readonly');
$snapshotA = seedTenantReviewEvidence($tenantA);
$snapshotB = seedTenantReviewEvidence($tenantB);
$reviewA = composeTenantReviewForTest($tenantA, $user, $snapshotA);
$reviewA->forceFill([
'status' => TenantReviewStatus::Published->value,
'published_at' => now(),
'published_by_user_id' => (int) $user->getKey(),
])->save();
$reviewB = composeTenantReviewForTest($tenantB, $user, $snapshotB);
$reviewB->forceFill([
'status' => TenantReviewStatus::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(['tenant' => (string) $tenantA->external_id])
->test(CustomerReviewWorkspace::class)
->assertSet('tableFilters.tenant_id.value', (string) $tenantA->getKey())
->filterTable('tenant_id', (string) $tenantA->getKey())
->assertCanSeeTableRecords([$tenantA->fresh()])
->assertCanNotSeeTableRecords([$tenantB->fresh()]);
});