Implemented the output resolution guidance for the customer review workspace and internal views. Added ReviewPackOutputResolutionGuidance, updated CustomerReviewWorkspace and EnvironmentReviewResource, and added related blade views and tests. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #420
268 lines
11 KiB
PHP
268 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
|
use App\Models\EnvironmentReview;
|
|
use App\Models\EvidenceSnapshot;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\ReviewPack;
|
|
use App\Models\User;
|
|
use App\Support\EnvironmentReviewCompletenessState;
|
|
use App\Support\EnvironmentReviewStatus;
|
|
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
pest()->browser()->timeout(60_000);
|
|
|
|
beforeEach(function (): void {
|
|
Storage::fake('exports');
|
|
});
|
|
|
|
it('Spec349 smokes output resolution guidance states and collapsed disclosures', function (): void {
|
|
[$user, $readyEnvironment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
|
|
$readyEnvironment->forceFill(['name' => 'Spec349 Browser Ready'])->save();
|
|
$blockedEnvironment = spec349BrowserEnvironmentFor($user, $readyEnvironment, 'Spec349 Browser Blocked');
|
|
$internalEnvironment = spec349BrowserEnvironmentFor($user, $readyEnvironment, 'Spec349 Browser Internal');
|
|
|
|
spec349BrowserCreatePublishedReviewWithPack(
|
|
$readyEnvironment,
|
|
$user,
|
|
seedEnvironmentReviewEvidence($readyEnvironment, findingCount: 0, driftCount: 0),
|
|
[],
|
|
[
|
|
'include_pii' => false,
|
|
'include_operations' => true,
|
|
],
|
|
'review-packs/spec349-browser-ready.zip',
|
|
markReady: true,
|
|
);
|
|
|
|
spec349BrowserCreatePublishedReviewWithPack(
|
|
$blockedEnvironment,
|
|
$user,
|
|
seedPartialEnvironmentReviewEvidence($blockedEnvironment, findingCount: 0, driftCount: 0),
|
|
[
|
|
'governance_package' => [
|
|
'decision_summary' => [
|
|
'status' => 'incomplete',
|
|
'evidence_state' => EnvironmentReviewCompletenessState::Partial->value,
|
|
'decision_data_state' => 'incomplete',
|
|
'total_count' => 1,
|
|
'summary' => 'Decision evidence is incomplete for this released review.',
|
|
'next_action' => 'Review the evidence basis before relying on the decision summary.',
|
|
'entries' => [],
|
|
],
|
|
],
|
|
],
|
|
[
|
|
'include_pii' => false,
|
|
'include_operations' => true,
|
|
],
|
|
'review-packs/spec349-browser-blocked.zip',
|
|
markReady: false,
|
|
);
|
|
|
|
spec349BrowserCreatePublishedReviewWithPack(
|
|
$internalEnvironment,
|
|
$user,
|
|
seedEnvironmentReviewEvidence($internalEnvironment, findingCount: 0, driftCount: 0),
|
|
[],
|
|
[
|
|
'include_pii' => true,
|
|
'include_operations' => true,
|
|
],
|
|
'review-packs/spec349-browser-internal.zip',
|
|
markReady: true,
|
|
);
|
|
|
|
spec349AuthenticateBrowser($this, $user, $readyEnvironment);
|
|
|
|
$page = visit(CustomerReviewWorkspace::environmentFilterUrl($blockedEnvironment))
|
|
->resize(1236, 900)
|
|
->waitForText('Output not customer-ready')
|
|
->assertSee('Inspect review blockers')
|
|
->assertSee('Download review pack with limitations')
|
|
->assertSee('The primary action opens the review detail with blockers, evidence status, and next steps.')
|
|
->assertScript('Array.from(document.querySelectorAll("[data-testid=\"customer-review-decision-card\"] [data-testid=\"customer-review-secondary-action\"]")).some((element) => element.innerText.includes("Open review")) === false', true)
|
|
->assertSee('Requires review')
|
|
->assertScript('document.querySelector("[data-testid=\"customer-review-output-limitations\"]")?.open === false', true)
|
|
->assertScript('document.querySelector("[data-testid=\"customer-review-technical-details\"]")?.open === false', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
$page->screenshot(true, spec349BrowserScreenshotName('01-output-blocked'));
|
|
spec349CopyBrowserScreenshot('01-output-blocked');
|
|
|
|
$page = visit(CustomerReviewWorkspace::environmentFilterUrl($internalEnvironment))
|
|
->waitForText('Internal review package available')
|
|
->assertSee('Review PII/redaction state')
|
|
->assertSee('Download internal review pack')
|
|
->assertSee('Internal only')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
$page->screenshot(true, spec349BrowserScreenshotName('02-internal-only'));
|
|
spec349CopyBrowserScreenshot('02-internal-only');
|
|
|
|
$page = visit(CustomerReviewWorkspace::environmentFilterUrl($readyEnvironment))
|
|
->waitForText('Customer-safe review pack ready')
|
|
->assertSee('Download customer-safe review pack')
|
|
->assertDontSee('Ready to share')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
$page->screenshot(true, spec349BrowserScreenshotName('03-customer-safe-ready'));
|
|
spec349CopyBrowserScreenshot('03-customer-safe-ready');
|
|
});
|
|
|
|
function spec349BrowserScreenshotName(string $name): string
|
|
{
|
|
return 'spec349-output-resolution-guidance-'.$name;
|
|
}
|
|
|
|
function spec349CopyBrowserScreenshot(string $name): void
|
|
{
|
|
$filename = spec349BrowserScreenshotName($name).'.png';
|
|
$source = base_path('tests/Browser/Screenshots/'.$filename);
|
|
$targetDirectory = repo_path('specs/349-customer-review-workspace-output-resolution-guidance/artifacts/screenshots');
|
|
|
|
if (! is_dir($targetDirectory)) {
|
|
@mkdir($targetDirectory, 0755, true);
|
|
}
|
|
|
|
if (! is_file($source)) {
|
|
$source = \Pest\Browser\Support\Screenshot::path($filename);
|
|
}
|
|
|
|
for ($attempt = 0; $attempt < 10 && ! is_file($source); $attempt++) {
|
|
usleep(100_000);
|
|
clearstatcache(true, $source);
|
|
}
|
|
|
|
if (is_file($source) && is_dir($targetDirectory) && is_writable($targetDirectory)) {
|
|
@copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$name.'.png');
|
|
}
|
|
}
|
|
|
|
function spec349AuthenticateBrowser(mixed $test, User $user, ManagedEnvironment $environment): void
|
|
{
|
|
$workspaceId = (int) $environment->workspace_id;
|
|
|
|
$test->actingAs($user)->withSession([
|
|
WorkspaceContext::SESSION_KEY => $workspaceId,
|
|
WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
|
|
(string) $workspaceId => (int) $environment->getKey(),
|
|
],
|
|
]);
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, $workspaceId);
|
|
session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
|
|
(string) $workspaceId => (int) $environment->getKey(),
|
|
]);
|
|
|
|
setAdminPanelContext($environment);
|
|
}
|
|
|
|
function spec349BrowserEnvironmentFor(User $user, ManagedEnvironment $baseEnvironment, string $name): ManagedEnvironment
|
|
{
|
|
$environment = ManagedEnvironment::factory()->active()->create([
|
|
'workspace_id' => (int) $baseEnvironment->workspace_id,
|
|
'name' => $name,
|
|
]);
|
|
|
|
createUserWithTenant(tenant: $environment, user: $user, role: 'owner', workspaceRole: 'manager');
|
|
|
|
return $environment;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $summaryOverrides
|
|
* @param array<string, mixed> $packOptions
|
|
* @return array{0: EnvironmentReview, 1: ReviewPack}
|
|
*/
|
|
function spec349BrowserCreatePublishedReviewWithPack(
|
|
ManagedEnvironment $environment,
|
|
User $user,
|
|
EvidenceSnapshot $snapshot,
|
|
array $summaryOverrides = [],
|
|
array $packOptions = [],
|
|
string $filePath = 'review-packs/spec349-browser-review-pack.zip',
|
|
bool $markReady = true,
|
|
): array {
|
|
$review = composeEnvironmentReviewForTest($environment, $user, $snapshot);
|
|
$summary = array_replace_recursive(
|
|
is_array($review->summary) ? $review->summary : [],
|
|
[
|
|
'control_interpretation' => [
|
|
'version_key' => ComplianceEvidenceMappingV1::VERSION_KEY,
|
|
'controls' => [
|
|
[
|
|
'control_key' => 'customer-output',
|
|
'title' => 'Customer output',
|
|
'readiness_bucket' => $markReady ? 'evidence_on_record' : 'review_recommended',
|
|
'readiness_label' => $markReady ? 'Evidence on record' : 'Review recommended',
|
|
'primary_reason' => $markReady ? 'Evidence path is complete.' : 'Evidence basis needs review.',
|
|
'recommended_next_action' => $markReady ? 'Open the current customer review pack.' : 'Review the evidence basis before sharing.',
|
|
],
|
|
],
|
|
],
|
|
'governance_package' => [
|
|
'decision_summary' => [
|
|
'status' => $markReady ? 'none' : 'incomplete',
|
|
'evidence_state' => $markReady ? EnvironmentReviewCompletenessState::Complete->value : EnvironmentReviewCompletenessState::Partial->value,
|
|
'decision_data_state' => $markReady ? 'complete' : 'incomplete',
|
|
'total_count' => $markReady ? 0 : 1,
|
|
'summary' => $markReady
|
|
? 'No governance decisions require customer awareness.'
|
|
: 'Decision evidence is incomplete for this released review.',
|
|
'next_action' => $markReady
|
|
? 'Open the current customer review pack.'
|
|
: 'Review the evidence basis before relying on the decision summary.',
|
|
'entries' => [],
|
|
],
|
|
],
|
|
],
|
|
$summaryOverrides,
|
|
);
|
|
|
|
Storage::disk('exports')->put($filePath, 'PK-spec349-browser-test');
|
|
|
|
$review->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'completeness_state' => $markReady
|
|
? EnvironmentReviewCompletenessState::Complete->value
|
|
: (string) $review->completeness_state,
|
|
'summary' => $summary,
|
|
'generated_at' => now()->subMinutes(5),
|
|
'published_at' => now()->subMinutes(3),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
])->save();
|
|
|
|
if ($markReady) {
|
|
$review = markEnvironmentReviewCustomerSafeReady($review);
|
|
}
|
|
|
|
$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' => array_replace([
|
|
'include_pii' => false,
|
|
'include_operations' => true,
|
|
], $packOptions),
|
|
'file_path' => $filePath,
|
|
'file_disk' => 'exports',
|
|
'generated_at' => now()->subMinutes(4),
|
|
]);
|
|
|
|
$review->forceFill([
|
|
'current_export_review_pack_id' => (int) $pack->getKey(),
|
|
])->save();
|
|
|
|
return [$review->refresh(), $pack->refresh()];
|
|
}
|