value, evidenceCompletenessState: EnvironmentReviewCompletenessState::Partial->value, sectionStateCounts: [ EnvironmentReviewCompletenessState::Complete->value => 3, EnvironmentReviewCompletenessState::Missing->value => 2, ], requiredSectionCount: 5, requiredSectionStateCounts: [ EnvironmentReviewCompletenessState::Complete->value => 3, EnvironmentReviewCompletenessState::Missing->value => 2, ], publishBlockers: ['Operator approval note is still missing.'], hasReadyExport: false, includePii: false, protectedValuesHidden: true, disclosurePresent: true, ); $guidance = ReviewPackOutputResolutionGuidance::fromReadiness($readiness, [ 'download' => '/admin/review-packs/8/download', 'review' => '/admin/reviews/6', 'evidence' => '/admin/evidence/8', ]); $review = new EnvironmentReview; $review->forceFill([ 'id' => 6, 'workspace_id' => 1, 'managed_environment_id' => 41, 'status' => 'published', ]); $review->setRelation('evidenceSnapshot', tap(new EvidenceSnapshot, function (EvidenceSnapshot $snapshot): void { $snapshot->forceFill(['id' => 8]); })); $review->setRelation('currentExportReviewPack', tap(new ReviewPack, function (ReviewPack $pack): void { $pack->forceFill(['id' => 8]); })); $case = ReviewPackOutputResolutionAdapter::fromGuidance($review, $guidance, 'customer_review_workspace'); expect($case['key'])->toBe('review_output.publication_blocked') ->and($case['severity'])->toBe('critical') ->and($case['status'])->toBe('blocked') ->and($case['title'])->toBe('Output not customer-ready') ->and($case['primary_action']['key'])->toBe('resolve_review_blockers') ->and($case['primary_action']['type'])->toBe(ResolutionAction::TYPE_NAVIGATION) ->and($case['source_refs'])->toEqual([ ['type' => 'environment_review', 'id' => 6], ['type' => 'review_pack', 'id' => 8], ]) ->and($case['evidence_refs'])->toEqual([ ['type' => 'evidence_snapshot', 'id' => 8], ]); }); it('maps ready customer-safe exports to a download-first shared resolution case', function (): void { $readiness = ReviewPackOutputReadiness::derive( reviewStatus: 'published', reviewCompletenessState: EnvironmentReviewCompletenessState::Complete->value, evidenceCompletenessState: EnvironmentReviewCompletenessState::Complete->value, sectionStateCounts: [ EnvironmentReviewCompletenessState::Complete->value => 5, ], requiredSectionCount: 5, requiredSectionStateCounts: [ EnvironmentReviewCompletenessState::Complete->value => 5, ], publishBlockers: [], hasReadyExport: true, includePii: false, protectedValuesHidden: true, disclosurePresent: true, ); $guidance = ReviewPackOutputResolutionGuidance::fromReadiness($readiness, [ 'download' => '/admin/review-packs/8/download', 'review' => '/admin/reviews/6', ]); $review = new EnvironmentReview; $review->forceFill([ 'id' => 6, 'workspace_id' => 1, 'managed_environment_id' => 41, 'status' => 'published', ]); $case = ReviewPackOutputResolutionAdapter::fromGuidance($review, $guidance, 'customer_review_workspace'); expect($case['key'])->toBe('review_output.customer_safe_ready') ->and($case['severity'])->toBe('success') ->and($case['status'])->toBe('ready') ->and($case['primary_action']['key'])->toBe('download_customer_safe_review_pack') ->and($case['primary_action']['type'])->toBe(ResolutionAction::TYPE_DOWNLOAD); });