Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m0s
Implemented the output contract and readiness semantics for review packs. Also added spec 348. Includes changes to ChooseEnvironment, CustomerReviewWorkspace, GenerateReviewPackJob and related blade views. Added comprehensive tests.
693 lines
32 KiB
PHP
693 lines
32 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\ReviewPack;
|
|
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('keeps the Spec 326 repo truth map available for customer review workspace productization', function (): void {
|
|
$path = repo_path('specs/326-customer-review-workspace-v1-productization/repo-truth-map.md');
|
|
|
|
expect($path)->toBeFile();
|
|
|
|
$contents = file_get_contents($path);
|
|
|
|
expect($contents)->toContain('Tenant Reviews / Environment Reviews')
|
|
->and($contents)->toContain('Evidence Snapshots')
|
|
->and($contents)->toContain('Review Packs / exports')
|
|
->and($contents)->toContain('Accepted Risks / Risk Exceptions')
|
|
->and($contents)->toContain('OperationRuns')
|
|
->and($contents)->toContain('Audit log');
|
|
});
|
|
|
|
it('lists only the latest published review per entitled environment 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::environmentScopedUrl('view', ['record' => $latestPublishedReview->fresh()], $tenantA), false)
|
|
->assertSee('Customer-safe review packages')
|
|
->assertSee('Review released governance packages, evidence readiness, accepted risks, and handoff status across entitled environments.')
|
|
->assertSee('Service delivery summary only. Does not replace formal audit opinion, certification, or legal attestation.')
|
|
->assertSee('Review package index')
|
|
->assertSee('Released reviews and customer-safe package entries available in this workspace.')
|
|
->assertDontSee('for each entitled tenant')
|
|
->assertDontSee('Each row is an index entry')
|
|
->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::environmentScopedUrl('view', ['record' => $betaPublishedReview->fresh()], $tenantB), false)
|
|
->assertDontSee(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $olderPublishedReview->fresh()], $tenantA), false)
|
|
->assertDontSee(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $newerInternalReview->fresh()], $tenantA), false)
|
|
->assertDontSee('Publish review')
|
|
->assertDontSee('Refresh review')
|
|
->assertDontSee('Create next review')
|
|
->assertDontSee('Regenerate')
|
|
->assertDontSee('Expire snapshot')
|
|
->assertDontSee(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $deniedPublishedReview->fresh()], $tenantDenied), false);
|
|
});
|
|
|
|
it('renders the Spec 326 decision-first review workspace without default raw diagnostics', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create(['name' => 'Decision Ready ManagedEnvironment']);
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');
|
|
$snapshot = seedEnvironmentReviewEvidence($tenant);
|
|
|
|
$review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
|
|
$summary = is_array($review->summary) ? $review->summary : [];
|
|
$summary['debug_payload'] = 'raw payload should stay hidden';
|
|
$summary['stack_trace'] = 'stack trace should stay hidden';
|
|
$summary['internal_exception'] = 'internal exception should stay hidden';
|
|
$summary['provider_secret'] = 'provider secret should stay hidden';
|
|
$summary['debug_metadata'] = 'debug metadata should stay hidden';
|
|
$summary['source_fingerprint'] = 'source fingerprint should stay hidden';
|
|
$summary['control_interpretation']['version_key'] = ComplianceEvidenceMappingV1::VERSION_KEY;
|
|
$summary['control_interpretation']['controls'] = [
|
|
[
|
|
'control_key' => 'customer-handoff-readiness',
|
|
'title' => 'Customer handoff readiness',
|
|
'readiness_bucket' => 'evidence_on_record',
|
|
'readiness_label' => 'Evidence on record',
|
|
'primary_reason' => 'Evidence path is complete.',
|
|
'recommended_next_action' => 'Share the current review pack.',
|
|
],
|
|
];
|
|
$summary['governance_package']['decision_summary']['entries'][] = [
|
|
'title' => 'Customer acceptance checkpoint',
|
|
'summary' => 'Confirm stakeholder awareness before handoff.',
|
|
'next_action' => 'Record the customer decision trail before the next review.',
|
|
];
|
|
|
|
$review->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'summary' => $summary,
|
|
'published_at' => now(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$pack = ReviewPack::factory()->ready()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'environment_review_id' => (int) $review->getKey(),
|
|
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
|
'initiated_by_user_id' => (int) $user->getKey(),
|
|
]);
|
|
|
|
$review->forceFill(['current_export_review_pack_id' => (int) $pack->getKey()])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertSee('Customer Review Workspace')
|
|
->assertSee('What is the current review pack output state?')
|
|
->assertSee('Review consumption flow')
|
|
->assertSee('Evidence')
|
|
->assertSee('Findings needing attention')
|
|
->assertSee('Evidence path')
|
|
->assertSeeHtml('data-testid="customer-review-evidence-aside"')
|
|
->assertSee('Review pack state')
|
|
->assertSee('Review pack')
|
|
->assertSee('Decision trail')
|
|
->assertSee('Accepted risks')
|
|
->assertSee('Expiring soon')
|
|
->assertSee('Expired')
|
|
->assertSee('Pending approval')
|
|
->assertSee('Needs review')
|
|
->assertSee('Operation proof')
|
|
->assertSee('Customer-safe follow-ups')
|
|
->assertSee('Review package index')
|
|
->assertSee('Disclosure rule')
|
|
->assertSee('Decision')
|
|
->assertSee('Visible')
|
|
->assertSee('Diagnostics')
|
|
->assertSee('Collapsed')
|
|
->assertSee('Raw/support')
|
|
->assertSee('Hidden')
|
|
->assertSee('Support details stay on authorized diagnostic surfaces')
|
|
->assertSee('Customer acceptance checkpoint')
|
|
->assertSee('Open review')
|
|
->assertSee('Download review pack with limitations')
|
|
->assertDontSee('raw payload should stay hidden')
|
|
->assertDontSee('stack trace should stay hidden')
|
|
->assertDontSee('internal exception should stay hidden')
|
|
->assertDontSee('provider secret should stay hidden')
|
|
->assertDontSee('debug metadata should stay hidden')
|
|
->assertDontSee('source fingerprint should stay hidden')
|
|
->assertDontSee('Publish review')
|
|
->assertDontSee('Refresh review')
|
|
->assertDontSee('Regenerate')
|
|
->assertDontSee('Expire snapshot');
|
|
});
|
|
|
|
it('does not show ready to share when accepted-risk follow-up is required', function (): void {
|
|
$environment = ManagedEnvironment::factory()->create(['name' => 'Accepted Risk Follow-up ManagedEnvironment']);
|
|
[$user, $environment] = createUserWithTenant(tenant: $environment, role: 'readonly');
|
|
$snapshot = seedEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0);
|
|
|
|
$review = composeEnvironmentReviewForTest($environment, $user, $snapshot);
|
|
$summary = is_array($review->summary) ? $review->summary : [];
|
|
$summary['control_interpretation']['version_key'] = ComplianceEvidenceMappingV1::VERSION_KEY;
|
|
$summary['control_interpretation']['controls'] = [
|
|
[
|
|
'control_key' => 'customer-handoff-readiness',
|
|
'title' => 'Customer handoff readiness',
|
|
'readiness_bucket' => 'evidence_on_record',
|
|
'readiness_label' => 'Evidence on record',
|
|
'primary_reason' => 'Evidence path is complete.',
|
|
'recommended_next_action' => 'Share the current review pack.',
|
|
],
|
|
];
|
|
$summary['governance_package']['decision_summary'] = [
|
|
'status' => 'none',
|
|
'total_count' => 0,
|
|
'summary' => 'No decisions require customer awareness.',
|
|
'next_action' => 'No customer action is needed.',
|
|
'entries' => [],
|
|
];
|
|
$summary['governance_package']['accepted_risks'] = [
|
|
[
|
|
'title' => 'Accepted risk renewal',
|
|
'governance_state' => 'expiring_exception',
|
|
'customer_summary' => 'Review the accepted-risk follow-up before customer handoff.',
|
|
],
|
|
];
|
|
|
|
$review->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'summary' => $summary,
|
|
'published_at' => now(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
$pack = ReviewPack::factory()->ready()->create([
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
'environment_review_id' => (int) $review->getKey(),
|
|
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
|
'initiated_by_user_id' => (int) $user->getKey(),
|
|
'options' => [
|
|
'include_pii' => false,
|
|
'include_operations' => true,
|
|
],
|
|
]);
|
|
|
|
$review->forceFill(['current_export_review_pack_id' => (int) $pack->getKey()])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $environment->workspace_id);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertSee('What is the current review pack output state?')
|
|
->assertSee('Published with limitations')
|
|
->assertSee('Accepted-risk follow-up is recorded for this review')
|
|
->assertSee('The pack can be shared only with the accepted-risk context included in the customer handoff.')
|
|
->assertSee('Needs review')
|
|
->assertSee('Follow-up required')
|
|
->assertSee('Accepted-risk follow-up is required.')
|
|
->assertSee('Open review')
|
|
->assertSeeInOrder(['Published with limitations', 'Open review'])
|
|
->assertSee('Download review pack with limitations')
|
|
->assertDontSee('Ready to share');
|
|
});
|
|
|
|
it('customer_review_workspace_does_not_use_tenant_as_platform_context_copy', function (): void {
|
|
$environment = ManagedEnvironment::factory()->create(['name' => 'Vocabulary ManagedEnvironment']);
|
|
[$user, $environment] = createUserWithTenant(tenant: $environment, role: 'readonly');
|
|
$snapshot = seedEnvironmentReviewEvidence($environment);
|
|
|
|
$review = composeEnvironmentReviewForTest($environment, $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) $environment->workspace_id);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertSee('Customer-safe review packages')
|
|
->assertSee('entitled environments')
|
|
->assertDontSee('entitled tenant')
|
|
->assertDontSee('entitled tenants')
|
|
->assertDontSee('current tenant')
|
|
->assertDontSee('tenant filter')
|
|
->assertDontSee('for each entitled tenant');
|
|
});
|
|
|
|
it('shows explicit unavailable proof states instead of false share readiness', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create(['name' => 'Needs Evidence ManagedEnvironment']);
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');
|
|
$snapshot = seedPartialEnvironmentReviewEvidence($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();
|
|
|
|
$pack = ReviewPack::factory()->ready()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'environment_review_id' => (int) $review->getKey(),
|
|
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
|
'initiated_by_user_id' => (int) $user->getKey(),
|
|
'options' => [
|
|
'include_pii' => false,
|
|
'include_operations' => true,
|
|
],
|
|
]);
|
|
|
|
$review->forceFill(['current_export_review_pack_id' => (int) $pack->getKey()])->save();
|
|
|
|
$this->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(CustomerReviewWorkspace::class)
|
|
->assertSee('What is the current review pack output state?')
|
|
->assertSee('Published with limitations')
|
|
->assertSee('The review package is published, but the evidence basis is incomplete.')
|
|
->assertSee('No operation proof linked')
|
|
->assertSee('Export ready')
|
|
->assertDontSee('Ready to share')
|
|
->assertSee('Download review pack with limitations');
|
|
});
|
|
|
|
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'),
|
|
'Decision trail',
|
|
'No governance decisions require customer awareness in this released review.',
|
|
])
|
|
->assertSee(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $betaReview->fresh()], $tenantB), false);
|
|
});
|
|
|
|
it('excludes entitled environments 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::environmentScopedUrl('view', ['record' => $internalOnlyReview->fresh()], $tenantWithoutPublished), false)
|
|
->assertSee(EnvironmentReviewResource::environmentScopedUrl('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 environment 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::environmentScopedUrl('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('Needs review')
|
|
->assertSee('Open review')
|
|
->assertDontSee('Ready for release')
|
|
->assertSee('Risk Owner')
|
|
->assertSee('Vendor patch window accepted by the customer.')
|
|
->assertSee(now()->addDays(14)->toDateString())
|
|
->assertDontSee('1 evidence signal(s) reference this control.')
|
|
->assertDontSee('1 accepted-risk finding(s) qualify this view.')
|
|
->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 trail')
|
|
->assertSee('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 environment 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_ENVIRONMENT_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()]);
|
|
});
|