value, hasReadyExport: false, includePii: true, publishBlockers: ['Operator approval note is still missing.'], requiredSectionStates: [ EnvironmentReviewCompletenessState::Complete->value => 3, EnvironmentReviewCompletenessState::Missing->value => 2, ], ); $guidance = ReviewPackOutputResolutionGuidance::fromReadiness($readiness, [ 'download' => '/review-packs/1/download', 'review' => '/environment-reviews/1', 'evidence' => '/evidence/1', 'operation' => '/operations/1', ]); expect($guidance['state'])->toBe('publication_blocked') ->and($guidance['label'])->toBe('Output not customer-ready') ->and($guidance['primary_action']['label'])->toBe('Inspect review blockers') ->and($guidance['action_help'])->toContain('opens the review detail with blockers, evidence status, and next steps') ->and($guidance['limitations'])->toHaveCount(5) ->and($guidance['limitations'][0]['key'])->toBe('publish_blockers_present') ->and(collect($guidance['secondary_actions'])->pluck('label')->all())->not->toContain('Open review') ->and(collect($guidance['limitations'])->pluck('key')->all())->toEqual([ 'publish_blockers_present', 'export_not_ready', 'evidence_basis_incomplete', 'required_sections_incomplete', 'contains_pii', ]); }); it('maps incomplete evidence to a published-with-limitations guidance item', function (): void { $readiness = spec349Readiness( evidenceState: EnvironmentReviewCompletenessState::Missing->value, hasReadyExport: true, ); $guidance = ReviewPackOutputResolutionGuidance::fromReadiness($readiness, [ 'review' => '/environment-reviews/1', 'evidence' => '/evidence/1', ]); expect($guidance['state'])->toBe('published_with_limitations') ->and($guidance['primary_reason'])->toBe('Evidence basis is incomplete.') ->and($guidance['primary_action']['label'])->toBe('Open evidence basis') ->and($guidance['limitations'])->toHaveCount(1) ->and($guidance['limitations'][0]['label'])->toBe('Evidence basis incomplete'); }); it('renders required section limitation reasons through pluralization instead of raw translation ranges', function (): void { $readiness = spec349Readiness( hasReadyExport: true, requiredSectionStates: [ EnvironmentReviewCompletenessState::Complete->value => 1, EnvironmentReviewCompletenessState::Partial->value => 4, EnvironmentReviewCompletenessState::Missing->value => 2, ], ); $guidance = ReviewPackOutputResolutionGuidance::fromReadiness($readiness, [ 'review' => '/environment-reviews/1', ]); expect($guidance['limitations'])->toHaveCount(1) ->and($guidance['limitations'][0]['key'])->toBe('required_sections_incomplete') ->and($guidance['limitations'][0]['reason'])->toBe('6 required sections are partial, missing, or stale.') ->and($guidance['limitations'][0]['reason'])->not->toContain('{1}') ->and($guidance['limitations'][0]['reason'])->not->toContain('[2,*]'); }); it('marks pii-bearing ready exports as internal-only and qualifies the download action', function (): void { $readiness = spec349Readiness( includePii: true, hasReadyExport: true, ); $guidance = ReviewPackOutputResolutionGuidance::fromReadiness($readiness, [ 'download' => '/review-packs/1/download', 'review' => '/environment-reviews/1', ]); expect($guidance['state'])->toBe('internal_only') ->and($guidance['label'])->toBe('Internal review package available') ->and($guidance['primary_action']['label'])->toBe('Review PII/redaction state') ->and(collect($guidance['secondary_actions'])->pluck('label')->all())->toContain('Download internal review pack'); }); it('marks complete non-pii exports as customer-safe ready', function (): void { $readiness = spec349Readiness( hasReadyExport: true, ); $guidance = ReviewPackOutputResolutionGuidance::fromReadiness($readiness, [ 'download' => '/review-packs/1/download', 'review' => '/environment-reviews/1', ]); expect($guidance['state'])->toBe('customer_safe_ready') ->and($guidance['label'])->toBe('Customer-safe review pack ready') ->and($guidance['primary_action']['label'])->toBe('Download customer-safe review pack') ->and($guidance['limitations'])->toBeEmpty(); }); /** * @param array $requiredSectionStates * @param list $publishBlockers * @return array */ function spec349Readiness( string $evidenceState = EnvironmentReviewCompletenessState::Complete->value, bool $hasReadyExport = true, bool $includePii = false, array $publishBlockers = [], array $requiredSectionStates = [ EnvironmentReviewCompletenessState::Complete->value => 5, ], ): array { return ReviewPackOutputReadiness::derive( reviewStatus: 'published', reviewCompletenessState: EnvironmentReviewCompletenessState::Complete->value, evidenceCompletenessState: $evidenceState, sectionStateCounts: $requiredSectionStates, requiredSectionCount: array_sum($requiredSectionStates), requiredSectionStateCounts: $requiredSectionStates, publishBlockers: $publishBlockers, hasReadyExport: $hasReadyExport, includePii: $includePii, protectedValuesHidden: true, disclosurePresent: true, ); }