TenantAtlas/apps/platform/tests/Feature/Guards/TestLaneRecalibrationEvidenceContractTest.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

128 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
use Tests\Support\TestLaneBudget;
use Tests\Support\TestLaneManifest;
use Tests\Support\TestLaneReport;
use Tests\Support\TestLaneTrendFixtures;
function reportWithSeededHistory(string $laneId, string $suffix, array $seededHistory, float $currentSeconds): array
{
$artifactDirectory = TestLaneTrendFixtures::artifactDirectory('trend-recalibration/'.$suffix);
$durationsByFile = [
'tests/Feature/Guards/TestLaneRecalibrationEvidenceContractTest.php' => 11.0,
];
$workflowId = $laneId === 'confidence' ? 'main-confidence' : 'pr-fast-feedback';
$triggerClass = $laneId === 'confidence' ? 'mainline-push' : 'pull-request';
$comparisonProfile = in_array($laneId, ['fast-feedback', 'confidence'], true)
? 'shared-test-fixture-slimming'
: null;
$baseReport = TestLaneTrendFixtures::buildReport(
laneId: $laneId,
wallClockSeconds: $seededHistory[0],
durationsByFile: $durationsByFile,
artifactDirectory: $artifactDirectory,
ciContext: [
'workflowId' => $workflowId,
'triggerClass' => $triggerClass,
'entryPointResolved' => true,
'workflowLaneMatched' => true,
],
comparisonProfile: $comparisonProfile,
);
$artifact = $baseReport['trendHistoryArtifact'];
$templateRecord = $artifact['history'][0];
$artifact['history'] = array_values(array_map(
static function (float $seconds, int $index) use ($templateRecord): array {
$record = $templateRecord;
$record['runRef'] = sprintf('%s-recalibration-%d', $templateRecord['laneId'], $index + 1);
$record['generatedAt'] = sprintf('2026-04-%02dT08:30:00+00:00', $index + 1);
$record['wallClockSeconds'] = round($seconds, 6);
return $record;
},
$seededHistory,
array_keys($seededHistory),
));
TestLaneTrendFixtures::writeTrendHistory($laneId, $artifact, $artifactDirectory);
return TestLaneTrendFixtures::buildReport(
laneId: $laneId,
wallClockSeconds: $currentSeconds,
durationsByFile: $durationsByFile,
artifactDirectory: $artifactDirectory,
ciContext: [
'workflowId' => $workflowId,
'triggerClass' => $triggerClass,
'entryPointResolved' => true,
'workflowLaneMatched' => true,
],
comparisonProfile: $comparisonProfile,
);
}
it('emits candidate, approved, and rejected recalibration records with explicit summary disclosure', function (): void {
$candidateReport = reportWithSeededHistory('confidence', 'candidate', [420.0, 380.0, 340.0, 300.0, 260.0], 460.0);
$approvedReport = reportWithSeededHistory('fast-feedback', 'approved', [176.0, 176.3, 176.1, 176.4, 176.2], 176.3);
$rejectedReport = reportWithSeededHistory('fast-feedback', 'rejected', [176.0, 191.0, 177.0, 192.0, 178.0], 193.0);
$approvedDecision = TestLaneBudget::buildRecalibrationDecisionRecord(
laneId: 'fast-feedback',
targetType: 'baseline',
assessment: ['recalibrationRecommendation' => 'review-baseline'],
historyRecords: $approvedReport['trendHistoryArtifact']['history'],
decisionStatus: 'approved',
rationaleCode: 'post-improvement-reset',
recordedIn: 'specs/211-runtime-trend-recalibration/spec.md',
proposedValueSeconds: 182.0,
notes: 'Approved baseline reset after the suite stabilized following a deliberate improvement pass.',
);
$candidateDecision = $candidateReport['trendRecalibrationDecisions'][0] ?? null;
$rejectedDecision = $rejectedReport['trendRecalibrationDecisions'][0] ?? null;
$approvedReport['trendRecalibrationDecisions'][] = $approvedDecision;
$approvedReport['trendHistoryArtifact']['recalibrationDecisions'][] = $approvedDecision;
TestLaneReport::writeArtifacts(
laneId: 'confidence',
report: $candidateReport,
artifactDirectory: $candidateReport['artifactDirectory'],
);
TestLaneReport::writeArtifacts(
laneId: 'fast-feedback',
report: $approvedReport,
artifactDirectory: $approvedReport['artifactDirectory'],
);
TestLaneReport::writeArtifacts(
laneId: 'fast-feedback',
report: $rejectedReport,
artifactDirectory: $rejectedReport['artifactDirectory'],
);
$candidateSummary = (string) file_get_contents(TestLaneManifest::absolutePath(
TestLaneReport::artifactPaths('confidence', $candidateReport['artifactDirectory'])['summary'],
));
$approvedSummary = (string) file_get_contents(TestLaneManifest::absolutePath(
TestLaneReport::artifactPaths('fast-feedback', $approvedReport['artifactDirectory'])['summary'],
));
$rejectedSummary = (string) file_get_contents(TestLaneManifest::absolutePath(
TestLaneReport::artifactPaths('fast-feedback', $rejectedReport['artifactDirectory'])['summary'],
));
expect($candidateDecision)->toBeArray()
->and($candidateDecision['decisionStatus'])->toBe('candidate')
->and($candidateDecision['targetType'])->toBe('budget')
->and($approvedDecision['decisionStatus'])->toBe('approved')
->and($approvedDecision['targetType'])->toBe('baseline')
->and($rejectedDecision)->toBeArray()
->and($rejectedDecision['decisionStatus'])->toBe('rejected')
->and($rejectedDecision['rationaleCode'])->toBe('noise-rejected')
->and($candidateSummary)->toContain('Recalibration: budget candidate')
->and($approvedSummary)->toContain('Recalibration: baseline approved')
->and($rejectedSummary)->toContain('Recalibration: budget rejected', 'noise-rejected');
});