Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 4m44s
Added BaselineReadinessGate, resolution propagation, and disclosure semantics logic per Spec 385. Integrated baseline unreadiness into Customer Review Workspace and Review Packs.
126 lines
5.8 KiB
PHP
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,
|
|
],
|
|
],
|
|
];
|
|
}
|