forceFill(['name' => 'Spec372 Customer Workspace'])->save(); spec372ReviewOutputFixture($tenant, $user); $component = spec372WorkspaceComponent($user, $tenant) ->assertSee('What is the current review pack output state?') ->assertSee('Evidence path') ->assertSee('Review pack state') ->assertDontSee('Operation proof') ->assertDontSee('Spec372 Operator'); $html = $component->html(); expect(substr_count($html, 'data-testid="customer-review-primary-action"'))->toBe(1) ->and($html)->toContain('data-testid="customer-review-diagnostics"') ->and($html)->not->toContain('data-testid="customer-review-diagnostics" open'); }); it('Spec372 renders environment review output guidance before technical metadata', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); ['review' => $review] = spec372ReviewOutputFixture($tenant, $user); setAdminEnvironmentContext($tenant); $response = $this->actingAs($user) ->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant)) ->assertOk(); $content = $response->getContent(); spec372AssertOrdered($content, [ 'Outcome summary', 'Output guidance', 'Executive posture', 'Evidence basis', 'Technical details', 'Sections', ]); expect(spec372ContentBetween($content, 'Evidence basis', 'Technical details')) ->not->toContain('spec372-review-fingerprint') ->not->toContain('Review status'); }); it('Spec372 renders review pack readiness and limitations before storage and operation metadata', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); ['pack' => $pack] = spec372ReviewOutputFixture($tenant, $user); setAdminEnvironmentContext($tenant); $response = $this->actingAs($user) ->get(ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant, panel: 'admin')) ->assertOk(); $content = $response->getContent(); spec372AssertOrdered($content, [ 'Outcome summary', 'Output guidance', 'Pack readiness and contents', 'Technical pack details', ]); expect(spec372ContentBetween($content, 'Pack readiness and contents', 'Technical pack details')) ->not->toContain('SHA-256') ->not->toContain('spec372-pack-fingerprint') ->not->toContain('Spec372 Operator'); }); it('Spec372 renders stored report scope and summary before source descriptors and raw payload', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $report = spec372StoredReport($tenant); setAdminEnvironmentContext($tenant); $response = $this->actingAs($user) ->get(StoredReportResource::getUrl('view', ['record' => $report], tenant: $tenant, panel: 'admin')) ->assertOk(); $content = $response->getContent(); spec372AssertOrdered($content, [ 'Outcome summary', 'Report scope and readiness', 'Permission posture summary', 'Technical report details', 'Raw payload', ]); expect(spec372ContentBetween($content, 'Report scope and readiness', 'Technical report details')) ->not->toContain('Source family') ->not->toContain('Integrity anchor') ->not->toContain('spec372-stored-report-fingerprint'); }); it('Spec372 renders evidence snapshot proof before diagnostics and omits operation links from related context', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); ['snapshot' => $snapshot] = spec372ReviewOutputFixture($tenant, $user); spec372EvidenceSnapshotItem($snapshot); setAdminEnvironmentContext($tenant); $response = $this->actingAs($user) ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin')) ->assertOk(); $content = $response->getContent(); spec372AssertOrdered($content, [ 'Outcome summary', 'Evidence basis and readiness', 'Evidence coverage summary', 'Related review and report context', 'Technical evidence details', 'Evidence dimensions', ]); expect(collect(EvidenceSnapshotResource::relatedContextEntries($snapshot))->pluck('key')->all()) ->not->toContain('operation_run') ->and(spec372ContentBetween($content, 'Related review and report context', 'Technical evidence details')) ->not->toContain('Operation') ->not->toContain('spec372-evidence-fingerprint'); }); it('Spec372 preserves scoped record access as not found for the wrong environment', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $foreignTenant = ManagedEnvironment::factory()->active()->create(['name' => 'Spec372 Foreign Environment']); ['review' => $foreignReview] = spec372ReviewOutputFixture($foreignTenant, $user); setAdminEnvironmentContext($tenant); $this->actingAs($user) ->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $foreignReview], $tenant)) ->assertNotFound(); }); /** * @return array{review: EnvironmentReview, snapshot: EvidenceSnapshot, pack: ReviewPack, run: OperationRun} */ function spec372ReviewOutputFixture(ManagedEnvironment $tenant, User $user): array { $snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 1, driftCount: 0); $run = OperationRun::factory() ->forTenant($tenant) ->withUser($user) ->create([ 'type' => OperationRunType::ReviewPackGenerate->value, 'status' => OperationRunStatus::Completed->value, 'initiator_name' => 'Spec372 Operator', ]); $review = composeEnvironmentReviewForTest($tenant, $user, $snapshot); $review->forceFill([ 'status' => EnvironmentReviewStatus::Published->value, 'generated_at' => now()->subHours(2), 'published_at' => now()->subHour(), 'published_by_user_id' => (int) $user->getKey(), 'operation_run_id' => (int) $run->getKey(), 'fingerprint' => 'spec372-review-fingerprint', ])->save(); $review = markEnvironmentReviewCustomerSafeReady($review); Storage::disk('exports')->put('review-packs/spec372-review-pack.zip', 'PK-spec372'); $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(), 'operation_run_id' => (int) $run->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'file_path' => 'review-packs/spec372-review-pack.zip', 'file_disk' => 'exports', 'fingerprint' => 'spec372-pack-fingerprint', 'summary' => [ 'finding_count' => 1, 'report_count' => 2, 'operation_count' => 1, 'evidence_resolution' => [ 'outcome' => 'complete', 'snapshot_fingerprint' => 'spec372-evidence-fingerprint', ], ], 'options' => [ 'include_pii' => false, 'include_operations' => true, ], ]); $review->forceFill(['current_export_review_pack_id' => (int) $pack->getKey()])->save(); $snapshot->forceFill([ 'operation_run_id' => (int) $run->getKey(), 'fingerprint' => 'spec372-evidence-fingerprint', 'summary' => array_replace(is_array($snapshot->summary) ? $snapshot->summary : [], [ 'finding_count' => 1, 'report_count' => 2, 'operation_count' => 1, 'missing_dimensions' => 0, 'stale_dimensions' => 0, ]), ])->save(); return [ 'review' => $review->refresh(), 'snapshot' => $snapshot->refresh(), 'pack' => $pack->refresh(), 'run' => $run, ]; } function spec372StoredReport(ManagedEnvironment $tenant): StoredReport { return StoredReport::factory() ->permissionPosture([ 'posture_score' => 78, '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' => 'spec372-stored-report-fingerprint', ]); } function spec372EvidenceSnapshotItem(EvidenceSnapshot $snapshot): EvidenceSnapshotItem { return EvidenceSnapshotItem::query()->updateOrCreate( [ 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'dimension_key' => 'permission_posture', ], [ 'workspace_id' => (int) $snapshot->workspace_id, 'managed_environment_id' => (int) $snapshot->managed_environment_id, 'state' => EvidenceCompletenessState::Complete->value, 'required' => true, 'source_kind' => 'stored_report', 'source_record_type' => 'stored_report', 'source_record_id' => 'spec372-stored-report', 'source_fingerprint' => 'spec372-item-source-fingerprint', 'measured_at' => now(), 'freshness_at' => now(), 'summary_payload' => [ 'required_count' => 4, 'granted_count' => 3, 'posture_score' => 78, 'payload' => [ 'missing_permissions' => ['DeviceManagementApps.ReadWrite.All'], ], ], 'sort_order' => 10, ], ); } function spec372WorkspaceComponent(User $user, ManagedEnvironment $tenant): mixed { $workspaceId = (int) $tenant->workspace_id; session()->put(WorkspaceContext::SESSION_KEY, $workspaceId); setAdminPanelContext(); return Livewire::withQueryParams(['environment_id' => (int) $tenant->getKey()]) ->actingAs($user) ->test(CustomerReviewWorkspace::class); } /** * @param list $needles */ function spec372AssertOrdered(string $content, array $needles): void { $lastPosition = -1; foreach ($needles as $needle) { $position = strpos($content, $needle, $lastPosition + 1); expect($position)->not->toBeFalse(); expect((int) $position)->toBeGreaterThan($lastPosition); $lastPosition = (int) $position; } } function spec372ContentBetween(string $content, string $startMarker, string $endMarker): string { $start = strpos($content, $startMarker); $end = strpos($content, $endMarker, $start === false ? 0 : (int) $start); expect($start)->not->toBeFalse(); expect($end)->not->toBeFalse(); expect((int) $end)->toBeGreaterThan((int) $start); return substr($content, (int) $start, (int) $end - (int) $start); }