392 lines
16 KiB
PHP
392 lines
16 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Resources\BaselineSnapshotResource;
|
|
use App\Filament\Resources\EvidenceSnapshotResource;
|
|
use App\Filament\Resources\RestoreRunResource;
|
|
use App\Filament\Resources\ReviewPackResource;
|
|
use App\Filament\Resources\StoredReportResource;
|
|
use App\Models\BackupSet;
|
|
use App\Models\BaselineProfile;
|
|
use App\Models\BaselineSnapshot;
|
|
use App\Models\BaselineSnapshotItem;
|
|
use App\Models\EvidenceSnapshot;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\OperationRun;
|
|
use App\Models\RestoreRun;
|
|
use App\Models\ReviewPack;
|
|
use App\Models\StoredReport;
|
|
use App\Models\User;
|
|
use App\Support\Baselines\BaselineSubjectKey;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\OperationRunType;
|
|
use App\Support\RestoreRunStatus;
|
|
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');
|
|
});
|
|
|
|
function spec397BrowserScreenshotName(string $name): string
|
|
{
|
|
return 'spec397-receipt-page-reduction-'.$name;
|
|
}
|
|
|
|
function spec397CopyBrowserScreenshot(string $name): void
|
|
{
|
|
$filename = spec397BrowserScreenshotName($name).'.png';
|
|
$source = base_path('tests/Browser/Screenshots/'.$filename);
|
|
$targetDirectory = repo_path('specs/397-receipt-page-reduction/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');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array{snapshot: BaselineSnapshot, profile: BaselineProfile}
|
|
*/
|
|
function spec397BrowserBaselineSnapshot(ManagedEnvironment $tenant): array
|
|
{
|
|
$profile = BaselineProfile::factory()->active()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'name' => 'Spec397 Browser Baseline',
|
|
]);
|
|
|
|
$policyTypeCounts = [];
|
|
|
|
$snapshot = BaselineSnapshot::factory()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'baseline_profile_id' => (int) $profile->getKey(),
|
|
'summary_jsonb' => [
|
|
'total_items' => 10,
|
|
'fidelity_counts' => ['content' => 10, 'meta' => 0],
|
|
'gaps' => ['count' => 0, 'by_reason' => []],
|
|
],
|
|
]);
|
|
|
|
foreach (range(1, 10) as $index) {
|
|
$policyType = sprintf('spec397BrowserPolicyType%02d', $index);
|
|
$policyTypeCounts[$policyType] = 1;
|
|
$subjectKey = baselineProviderResourceSubjectKeyForTest($policyType, sprintf('spec397-browser-policy-%02d', $index));
|
|
|
|
BaselineSnapshotItem::factory()->create([
|
|
'baseline_snapshot_id' => (int) $snapshot->getKey(),
|
|
'policy_type' => $policyType,
|
|
'subject_key' => $subjectKey,
|
|
'subject_external_id' => BaselineSubjectKey::workspaceSafeSubjectExternalId($policyType, $subjectKey),
|
|
'meta_jsonb' => [
|
|
'display_name' => sprintf('Spec397 Browser Policy %02d', $index),
|
|
'evidence' => [
|
|
'fidelity' => 'content',
|
|
'source' => 'policy_version',
|
|
'observed_at' => now()->toIso8601String(),
|
|
],
|
|
],
|
|
]);
|
|
}
|
|
|
|
$snapshot->forceFill([
|
|
'summary_jsonb' => array_merge($snapshot->summary_jsonb, [
|
|
'policy_type_counts' => $policyTypeCounts,
|
|
]),
|
|
])->save();
|
|
|
|
return ['snapshot' => $snapshot->refresh(), 'profile' => $profile];
|
|
}
|
|
|
|
function spec397BrowserRestoreRun(ManagedEnvironment $tenant): RestoreRun
|
|
{
|
|
$backupSet = BackupSet::factory()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'name' => 'Spec397 Browser Backup',
|
|
'status' => 'completed',
|
|
'item_count' => 10,
|
|
]);
|
|
|
|
$operationRun = OperationRun::factory()->forTenant($tenant)->create([
|
|
'type' => OperationRunType::RestoreExecute->value,
|
|
'status' => OperationRunStatus::Completed->value,
|
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
|
'completed_at' => now(),
|
|
]);
|
|
|
|
$items = [];
|
|
|
|
foreach (range(1, 10) as $index) {
|
|
$items[] = [
|
|
'status' => 'applied',
|
|
'policy_identifier' => sprintf('Spec397 Restore Policy %02d', $index),
|
|
'policy_type' => 'settingsCatalogPolicy',
|
|
'platform' => 'windows',
|
|
'assignment_summary' => [
|
|
'success' => 1,
|
|
'failed' => 0,
|
|
'skipped' => 0,
|
|
],
|
|
'settings_apply' => [
|
|
'applied' => 1,
|
|
'failed' => 0,
|
|
'manual_required' => 0,
|
|
],
|
|
'graph_error_message' => sprintf('spec397 raw restore payload %02d', $index),
|
|
];
|
|
}
|
|
|
|
return RestoreRun::factory()->for($tenant, 'tenant')->for($backupSet)->create([
|
|
'status' => 'completed',
|
|
'operation_run_id' => (int) $operationRun->getKey(),
|
|
'requested_by' => 'Spec397 Browser Operator',
|
|
'results' => [
|
|
'foundations' => [],
|
|
'items' => $items,
|
|
],
|
|
'metadata' => [
|
|
'total' => 10,
|
|
'succeeded' => 10,
|
|
'failed' => 0,
|
|
'skipped' => 0,
|
|
'partial' => 0,
|
|
'non_applied' => 0,
|
|
],
|
|
'completed_at' => now(),
|
|
]);
|
|
}
|
|
|
|
function spec397BrowserRunningRestoreRun(ManagedEnvironment $tenant): RestoreRun
|
|
{
|
|
$backupSet = BackupSet::factory()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'name' => 'Spec397 Browser Running Backup',
|
|
'status' => 'completed',
|
|
'item_count' => 2,
|
|
]);
|
|
|
|
$operationRun = OperationRun::factory()->forTenant($tenant)->create([
|
|
'type' => OperationRunType::RestoreExecute->value,
|
|
'status' => OperationRunStatus::Running->value,
|
|
'outcome' => OperationRunOutcome::Pending->value,
|
|
'started_at' => now(),
|
|
'completed_at' => null,
|
|
]);
|
|
|
|
return RestoreRun::factory()->for($tenant, 'tenant')->for($backupSet)->create([
|
|
'status' => RestoreRunStatus::Running->value,
|
|
'operation_run_id' => (int) $operationRun->getKey(),
|
|
'requested_by' => 'Spec397 Browser Operator',
|
|
'results' => [
|
|
'foundations' => [],
|
|
'items' => [],
|
|
],
|
|
'metadata' => [],
|
|
'started_at' => now(),
|
|
'completed_at' => null,
|
|
]);
|
|
}
|
|
|
|
function spec397BrowserReviewPack(ManagedEnvironment $tenant, User $user, EvidenceSnapshot $snapshot): ReviewPack
|
|
{
|
|
$review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
|
|
$review = markEnvironmentReviewCustomerSafeReady($review);
|
|
|
|
Storage::disk('exports')->put('review-packs/spec397-browser-ready.zip', 'PK-spec397-browser');
|
|
|
|
$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(),
|
|
'file_path' => 'review-packs/spec397-browser-ready.zip',
|
|
'file_disk' => 'exports',
|
|
'options' => [
|
|
'include_pii' => false,
|
|
'include_operations' => true,
|
|
],
|
|
'summary' => array_replace_recursive(is_array($review->summary) ? $review->summary : [], [
|
|
'finding_count' => 1,
|
|
'report_count' => 2,
|
|
'operation_count' => 1,
|
|
'evidence_resolution' => [
|
|
'outcome' => 'resolved',
|
|
'snapshot_id' => (int) $snapshot->getKey(),
|
|
'snapshot_fingerprint' => (string) $snapshot->fingerprint,
|
|
],
|
|
]),
|
|
]);
|
|
|
|
$review->forceFill([
|
|
'current_export_review_pack_id' => (int) $pack->getKey(),
|
|
])->save();
|
|
|
|
return $pack->refresh();
|
|
}
|
|
|
|
function spec397BrowserStoredReport(ManagedEnvironment $tenant): StoredReport
|
|
{
|
|
return StoredReport::factory()
|
|
->permissionPosture([
|
|
'posture_score' => 88,
|
|
'required_count' => 4,
|
|
'granted_count' => 3,
|
|
'permissions' => [
|
|
['key' => 'DeviceManagementConfiguration.Read.All', 'status' => 'granted'],
|
|
['key' => 'DeviceManagementApps.ReadWrite.All', 'status' => 'missing'],
|
|
],
|
|
])
|
|
->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'fingerprint' => 'spec397-browser-report-fingerprint',
|
|
]);
|
|
}
|
|
|
|
it('Spec397 smokes reduced receipt defaults across changed detail surfaces', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(
|
|
role: 'owner',
|
|
workspaceRole: 'manager',
|
|
ensureDefaultMicrosoftProviderConnection: false,
|
|
);
|
|
|
|
$evidenceSnapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 1, driftCount: 0);
|
|
['snapshot' => $baselineSnapshot] = spec397BrowserBaselineSnapshot($tenant);
|
|
$restoreRun = spec397BrowserRestoreRun($tenant);
|
|
$runningRestoreRun = spec397BrowserRunningRestoreRun($tenant);
|
|
$reviewPack = spec397BrowserReviewPack($tenant, $user, $evidenceSnapshot);
|
|
$storedReport = spec397BrowserStoredReport($tenant);
|
|
|
|
$this->actingAs($user)->withSession([
|
|
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
|
|
WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
|
|
(string) $tenant->workspace_id => (int) $tenant->getKey(),
|
|
],
|
|
]);
|
|
|
|
$page = visit(EvidenceSnapshotResource::getUrl('view', ['record' => $evidenceSnapshot], tenant: $tenant, panel: 'admin'))
|
|
->resize(1440, 1100)
|
|
->waitForText('Outcome summary')
|
|
->assertSee('Evidence basis and readiness')
|
|
->assertSee('Evidence coverage summary')
|
|
->assertSee('Internal evidence dimensions')
|
|
->assertDontSee('Artifact source')
|
|
->assertScript("document.body.textContent.indexOf('Evidence coverage summary') < document.body.textContent.indexOf('Internal evidence dimensions')", true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec397BrowserScreenshotName('01-evidence-snapshot-detail'));
|
|
spec397CopyBrowserScreenshot('01-evidence-snapshot-detail');
|
|
|
|
$page = visit(BaselineSnapshotResource::getUrl('view', ['record' => $baselineSnapshot], panel: 'admin'))
|
|
->resize(1440, 1100)
|
|
->waitForText('Outcome summary')
|
|
->assertSee('Coverage summary')
|
|
->assertSee('Captured governed subjects')
|
|
->assertSee('Showing the first 8 governed subjects for receipt review.')
|
|
->assertSee('2 additional subjects stay in the internal detail path.')
|
|
->assertDontSee('Spec397 Browser Policy 09')
|
|
->assertScript("document.body.textContent.indexOf('Coverage summary') < document.body.textContent.indexOf('Captured governed subjects')", true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec397BrowserScreenshotName('02-baseline-snapshot-detail'));
|
|
spec397CopyBrowserScreenshot('02-baseline-snapshot-detail');
|
|
|
|
$page = visit(RestoreRunResource::getUrl('view', ['record' => $restoreRun], tenant: $tenant, panel: 'admin'))
|
|
->resize(1440, 1100)
|
|
->waitForText('Needs attention')
|
|
->assertSee('Restore result summary')
|
|
->assertSee('View technical run details')
|
|
->assertSee('Confirm operation proof and capture post-run evidence before closing this as recovered.')
|
|
->assertSee('Spec397 Restore Policy 08')
|
|
->assertDontSee('Spec397 Restore Policy 09')
|
|
->assertDontSee('Operation proof available')
|
|
->assertDontSee('spec397 raw restore payload')
|
|
->assertScript('document.querySelector("[data-testid=\"restore-run-proof-panel\"] details")?.open === false', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec397BrowserScreenshotName('03-restore-run-detail'));
|
|
spec397CopyBrowserScreenshot('03-restore-run-detail');
|
|
|
|
$page
|
|
->click('[data-next-action-target="restore-run-technical-details"]')
|
|
->assertScript('document.getElementById("restore-run-technical-details")?.open === true', true)
|
|
->assertSee('Post-run evidence unavailable')
|
|
->assertSee('Open Evidence snapshots and use Create snapshot for this environment. Do not close the restore as recovered until the post-run snapshot is available.')
|
|
->assertSee('Open evidence snapshots')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
|
|
$page = visit(RestoreRunResource::getUrl('view', ['record' => $runningRestoreRun], tenant: $tenant, panel: 'admin'))
|
|
->resize(1440, 1100)
|
|
->waitForText('Running')
|
|
->assertSee('Monitor restore progress')
|
|
->assertSee('View technical run details')
|
|
->assertDontSee('View operation progress')
|
|
->assertDontSee('Open operation')
|
|
->assertDontSee('Operation proof in progress')
|
|
->assertScript('document.querySelector("[data-testid=\"restore-run-proof-panel\"] details")?.open === false', true)
|
|
->assertScript('(() => { const surface = document.querySelector("[data-testid=\"restore-run-detail-surface\"]"); return !!surface && Array.from(surface.querySelectorAll("a[href*=\"operations\"]")).every((a) => a.closest("[data-testid=\"restore-run-proof-panel\"] details")?.open === false); })()', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec397BrowserScreenshotName('03b-restore-run-running-detail'));
|
|
spec397CopyBrowserScreenshot('03b-restore-run-running-detail');
|
|
|
|
$page
|
|
->click('[data-next-action-target="restore-run-technical-details"]')
|
|
->assertScript('document.getElementById("restore-run-technical-details")?.open === true', true)
|
|
->assertSee('Operation proof in progress')
|
|
->assertSee('Open operation')
|
|
->assertSee('Wait until operation proof is completed, then capture a post-run evidence snapshot before closing this restore as recovered.')
|
|
->assertDontSee('Open evidence snapshots')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
|
|
$page = visit(ReviewPackResource::getUrl('view', ['record' => $reviewPack], tenant: $tenant, panel: 'admin'))
|
|
->resize(1440, 1100)
|
|
->waitForText('Outcome summary')
|
|
->assertSee('Output guidance')
|
|
->assertSee('Pack readiness and contents')
|
|
->assertSee('Evidence basis')
|
|
->assertSee('Evidence completeness')
|
|
->assertSee('Technical pack details')
|
|
->assertDontSee('Internal evidence details')
|
|
->assertDontSee('View internal evidence details')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec397BrowserScreenshotName('04-review-pack-detail'));
|
|
spec397CopyBrowserScreenshot('04-review-pack-detail');
|
|
|
|
$page = visit(StoredReportResource::getUrl('view', ['record' => $storedReport], tenant: $tenant, panel: 'admin'))
|
|
->resize(1440, 1100)
|
|
->waitForText('Outcome summary')
|
|
->assertSee('Report scope and readiness')
|
|
->assertSee('Permission posture summary')
|
|
->assertSee('Internal report artifact')
|
|
->assertSee('Internal payload details')
|
|
->assertDontSee('Raw payload')
|
|
->assertScript("document.body.textContent.indexOf('Permission posture summary') < document.body.textContent.indexOf('Internal report artifact')", true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec397BrowserScreenshotName('05-stored-report-detail'));
|
|
spec397CopyBrowserScreenshot('05-stored-report-detail');
|
|
});
|