TenantAtlas/apps/platform/tests/Feature/EnvironmentReview/Spec385EnvironmentReviewBaselineReadinessTest.php
Ahmed Darrazi d71493f82a
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 4m44s
feat(evidence): implement baseline review readiness integration
Added BaselineReadinessGate, resolution propagation, and disclosure semantics logic per Spec 385. Integrated baseline unreadiness into Customer Review Workspace and Review Packs.
2026-06-18 00:52:53 +02:00

126 lines
5.8 KiB
PHP

<?php
declare(strict_types=1);
use App\Support\Baselines\CompareSemantics\CompareResultReason;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use App\Support\OperationRunOutcome;
it('blocks environment review publication when baseline subject identity is unresolved', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
[$profile, $baselineSnapshot] = seedActiveBaselineForTenant($tenant);
seedBaselineCompareRun(
tenant: $tenant,
profile: $profile,
snapshot: $baselineSnapshot,
compareContext: spec385EnvironmentCompareContext([CompareResultReason::UnresolvedAmbiguousIdentity]),
outcome: OperationRunOutcome::PartiallySucceeded->value,
);
$snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 0, driftCount: 0);
$review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
$baselineSection = $review->sections->firstWhere('section_key', 'baseline_drift_posture');
expect($baselineSection->completeness_state)->toBe(EnvironmentReviewCompletenessState::Partial->value)
->and($baselineSection->summary_payload['baseline_readiness']['readiness_state'])->toBe('baseline_identity_unresolved')
->and($baselineSection->summary_payload['publication_blockers'])->not->toBeEmpty()
->and($review->status)->toBe(EnvironmentReviewStatus::Draft->value)
->and($review->publishBlockers())->toContain('Baseline drift posture: Baseline subject identity must be resolved before customer-ready publication.');
});
it('keeps trusted baseline drift complete with findings and publication-ready', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
[$profile, $baselineSnapshot] = seedActiveBaselineForTenant($tenant);
seedBaselineCompareRun(
tenant: $tenant,
profile: $profile,
snapshot: $baselineSnapshot,
compareContext: spec385EnvironmentCompareContext([CompareResultReason::VerifiedDriftDetected]),
);
$snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 0, driftCount: 1);
$review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
expect($review->status)->toBe(EnvironmentReviewStatus::Ready->value)
->and($review->publishBlockers())->toBeEmpty()
->and($review->summary['baseline_readiness']['customer_safe_claim'])->toBe('customer_ready_with_findings')
->and($review->summary['baseline_readiness']['publication_blockers'])->toBe([]);
});
it('carries accepted baseline limitations without blocking publication', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
[$profile, $baselineSnapshot] = seedActiveBaselineForTenant($tenant);
seedBaselineCompareRun(
tenant: $tenant,
profile: $profile,
snapshot: $baselineSnapshot,
compareContext: spec385EnvironmentCompareContext([CompareResultReason::AcceptedLimitation]),
outcome: OperationRunOutcome::PartiallySucceeded->value,
);
$snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 1, driftCount: 0);
$review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
expect($review->publishBlockers())->toBeEmpty()
->and($review->status)->toBe(EnvironmentReviewStatus::Ready->value)
->and($review->summary['baseline_readiness']['state'])->toBe(EnvironmentReviewCompletenessState::Partial->value)
->and($review->summary['baseline_readiness']['limitation_codes'])->toBe(['baseline_accepted_limitations'])
->and($review->summary['baseline_readiness']['publication_blockers'])->toBe([]);
});
it('turns missing baseline local evidence into refresh guidance and a review blocker', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
[$profile, $baselineSnapshot] = seedActiveBaselineForTenant($tenant);
seedBaselineCompareRun(
tenant: $tenant,
profile: $profile,
snapshot: $baselineSnapshot,
compareContext: spec385EnvironmentCompareContext([CompareResultReason::MissingLocalEvidence]),
outcome: OperationRunOutcome::PartiallySucceeded->value,
);
$snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 1, driftCount: 0);
$review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
$baselineSection = $review->sections->firstWhere('section_key', 'baseline_drift_posture');
expect($review->status)->toBe(EnvironmentReviewStatus::Draft->value)
->and($review->summary['baseline_readiness']['readiness_state'])->toBe('baseline_local_evidence_missing')
->and($review->summary['baseline_readiness']['next_action'])->toBe('open_evidence_basis')
->and($review->publishBlockers())->toContain('Baseline drift posture: Baseline local evidence is missing and must be refreshed before publication.')
->and($baselineSection->render_payload['next_actions'])->toContain('Refresh baseline compare evidence before relying on the review output.');
});
/**
* @param list<CompareResultReason> $reasons
* @return array<string, mixed>
*/
function spec385EnvironmentCompareContext(array $reasons): array
{
$byReason = [];
$byReadinessImpact = [];
foreach ($reasons as $reason) {
$byReason[$reason->value] = ($byReason[$reason->value] ?? 0) + 1;
$impact = $reason->readinessImpact()->value;
$byReadinessImpact[$impact] = ($byReadinessImpact[$impact] ?? 0) + 1;
}
return [
'result_semantics' => [
'version' => 'compare_semantics.v1',
'run_outcome' => 'completed',
'operation_outcome' => OperationRunOutcome::Succeeded->value,
'counts' => [
'by_reason' => $byReason,
'by_readiness_impact' => $byReadinessImpact,
],
],
];
}