TenantAtlas/apps/platform/tests/Feature/Guards/TestLaneRecalibrationPolicyTest.php
Ahmed Darrazi 97262787c9
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 4m15s
feat: implement runtime trend recalibration reporting
2026-04-18 09:34:23 +02:00

112 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
use Tests\Support\TestLaneBudget;
use Tests\Support\TestLaneTrendFixtures;
function recalibrationEvidenceHistory(string $laneId, int $count): array
{
$report = TestLaneTrendFixtures::buildReport(
laneId: $laneId,
wallClockSeconds: $laneId === 'confidence' ? 438.0 : 188.0,
durationsByFile: [
'tests/Feature/Guards/TestLaneRecalibrationPolicyTest.php' => 12.0,
],
ciContext: [
'workflowId' => $laneId === 'confidence' ? 'main-confidence' : 'pr-fast-feedback',
'triggerClass' => $laneId === 'confidence' ? 'mainline-push' : 'pull-request',
'entryPointResolved' => true,
'workflowLaneMatched' => true,
],
comparisonProfile: in_array($laneId, ['fast-feedback', 'confidence'], true) ? 'shared-test-fixture-slimming' : null,
);
$templateRecord = $report['trendHistoryArtifact']['history'][0];
return array_values(array_map(
static function (int $index) use ($templateRecord): array {
$record = $templateRecord;
$record['runRef'] = sprintf('%s-evidence-%d', $templateRecord['laneId'], $index + 1);
$record['generatedAt'] = sprintf('2026-04-%02dT10:00:00+00:00', $index + 1);
return $record;
},
range(0, $count - 1),
));
}
it('keeps baseline and budget recalibration rules separate and enforces the stronger budget evidence window', function (): void {
$assessment = [
'recalibrationRecommendation' => 'review-baseline',
];
$baselineHistory = recalibrationEvidenceHistory('fast-feedback', 3);
$budgetHistory = recalibrationEvidenceHistory('confidence', 5);
$baselineDecision = TestLaneBudget::buildRecalibrationDecisionRecord(
laneId: 'fast-feedback',
targetType: 'baseline',
assessment: $assessment,
historyRecords: $baselineHistory,
decisionStatus: 'approved',
rationaleCode: 'lane-scope-change',
recordedIn: 'specs/211-runtime-trend-recalibration/spec.md',
proposedValueSeconds: 184.0,
);
$budgetDecision = TestLaneBudget::buildRecalibrationDecisionRecord(
laneId: 'confidence',
targetType: 'budget',
assessment: ['recalibrationRecommendation' => 'review-budget'],
historyRecords: $budgetHistory,
decisionStatus: 'approved',
rationaleCode: 'sustained-erosion',
recordedIn: 'specs/211-runtime-trend-recalibration/spec.md',
proposedValueSeconds: 470.0,
);
expect($baselineDecision['targetType'])->toBe('baseline')
->and($baselineDecision['decisionStatus'])->toBe('approved')
->and($budgetDecision['targetType'])->toBe('budget')
->and($budgetDecision['decisionStatus'])->toBe('approved')
->and($budgetDecision['evidenceRunRefs'])->toHaveCount(5);
expect(static fn () => TestLaneBudget::buildRecalibrationDecisionRecord(
laneId: 'confidence',
targetType: 'budget',
assessment: ['recalibrationRecommendation' => 'review-budget'],
historyRecords: recalibrationEvidenceHistory('confidence', 4),
decisionStatus: 'approved',
rationaleCode: 'sustained-erosion',
recordedIn: 'specs/211-runtime-trend-recalibration/spec.md',
proposedValueSeconds: 470.0,
))->toThrow(InvalidArgumentException::class);
});
it('requires approved versus rejected rationale handling that matches the policy', function (): void {
$history = recalibrationEvidenceHistory('fast-feedback', 3);
$rejectedDecision = TestLaneBudget::buildRecalibrationDecisionRecord(
laneId: 'fast-feedback',
targetType: 'budget',
assessment: ['recalibrationRecommendation' => 'investigate'],
historyRecords: [$history[0]],
decisionStatus: 'rejected',
rationaleCode: 'noise-rejected',
recordedIn: 'specs/211-runtime-trend-recalibration/spec.md',
);
expect($rejectedDecision['decisionStatus'])->toBe('rejected')
->and($rejectedDecision['rationaleCode'])->toBe('noise-rejected');
expect(static fn () => TestLaneBudget::buildRecalibrationDecisionRecord(
laneId: 'fast-feedback',
targetType: 'baseline',
assessment: ['recalibrationRecommendation' => 'review-baseline'],
historyRecords: $history,
decisionStatus: 'approved',
rationaleCode: 'sustained-erosion',
recordedIn: 'specs/211-runtime-trend-recalibration/spec.md',
proposedValueSeconds: 184.0,
))->toThrow(InvalidArgumentException::class);
});