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

197 lines
8.2 KiB
PHP

<?php
declare(strict_types=1);
use Tests\Support\TestLaneManifest;
use Tests\Support\TestLaneReport;
function heavyGovernanceSyntheticHotspots(): array
{
return [
['file' => 'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php', 'seconds' => 22.4],
['file' => 'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php', 'seconds' => 21.1],
['file' => 'tests/Feature/Filament/BaselineActionAuthorizationTest.php', 'seconds' => 19.5],
['file' => 'tests/Feature/Findings/FindingBulkActionsTest.php', 'seconds' => 18.2],
['file' => 'tests/Feature/Findings/FindingWorkflowRowActionsTest.php', 'seconds' => 14.8],
['file' => 'tests/Feature/Findings/FindingWorkflowViewActionsTest.php', 'seconds' => 13.6],
['file' => 'tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php', 'seconds' => 12.7],
['file' => 'tests/Feature/Guards/ActionSurfaceContractTest.php', 'seconds' => 11.9],
['file' => 'tests/Feature/Guards/OperationLifecycleOpsUxGuardTest.php', 'seconds' => 10.4],
['file' => 'tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php', 'seconds' => 4.2],
];
}
it('keeps lane artifact paths app-root relative under storage/logs/test-lanes', function (): void {
$artifacts = TestLaneReport::artifactPaths('fast-feedback');
$artifactContract = TestLaneManifest::artifactPublicationContract('fast-feedback');
expect($artifacts)->toHaveKeys(['junit', 'summary', 'budget', 'report', 'profile', 'trendHistory']);
expect($artifactContract['requiredFiles'])->toEqualCanonicalizing(['summary.md', 'budget.json', 'report.json', 'junit.xml', 'trend-history.json'])
->and($artifactContract['stagedNamePattern'])->toBe('{laneId}.{artifactFile}');
foreach (array_values($artifacts) as $relativePath) {
expect($relativePath)->toStartWith('storage/logs/test-lanes/');
}
});
it('keeps only the skeleton file checked into the lane artifact directory', function (): void {
$gitignore = base_path('storage/logs/test-lanes/.gitignore');
expect(file_exists($gitignore))->toBeTrue()
->and((string) file_get_contents($gitignore))->toContain('*')
->and((string) file_get_contents($gitignore))->toContain('!.gitignore');
});
it('publishes heavy attribution, contract, and coverage payloads under the canonical artifact root', function (): void {
$durationsByFile = collect(heavyGovernanceSyntheticHotspots())
->mapWithKeys(static fn (array $entry): array => [$entry['file'] => $entry['seconds']])
->all();
$slowestEntries = collect(heavyGovernanceSyntheticHotspots())
->map(static fn (array $entry): array => [
'label' => $entry['file'].'::synthetic',
'subject' => $entry['file'].'::synthetic',
'filePath' => $entry['file'],
'durationSeconds' => $entry['seconds'],
'wallClockSeconds' => $entry['seconds'],
'laneId' => 'heavy-governance',
])
->values()
->all();
$report = TestLaneReport::buildReport(
laneId: 'heavy-governance',
wallClockSeconds: 118.4,
slowestEntries: $slowestEntries,
durationsByFile: $durationsByFile,
);
expect($report['artifactDirectory'])->toBe('storage/logs/test-lanes')
->and($report['slowestEntries'])->toHaveCount(10)
->and($report)->toHaveKeys([
'artifactPublicationContract',
'knownWorkflowProfiles',
'failureClasses',
'trendHistoryArtifact',
'trendCurrentAssessment',
'trendHotspotSnapshot',
'trendRecalibrationDecisions',
'trendWarnings',
'budgetContract',
'hotspotInventory',
'decompositionRecords',
'slimmingDecisions',
'authorGuidance',
'inventoryCoverage',
'budgetSnapshots',
'budgetOutcome',
'remainingOpenFamilies',
'stabilizedFamilies',
])
->and(collect($report['classificationAttribution'])->pluck('classificationId')->all())
->toContain('ui-workflow', 'surface-guard', 'discovery-heavy')
->and(collect($report['familyAttribution'])->pluck('familyId')->all())
->toContain(
'baseline-profile-start-surfaces',
'findings-workflow-surfaces',
'finding-bulk-actions-workflow',
'workspace-settings-slice-management',
'action-surface-contract',
'ops-ux-governance',
)
->and(collect($report['budgetEvaluations'])->pluck('targetType')->unique()->values()->all())
->toEqualCanonicalizing(['lane', 'classification', 'family'])
->and($report['familyBudgetEvaluations'])->not->toBeEmpty()
->and($report['inventoryCoverage']['meetsInclusionRule'])->toBeTrue()
->and($report['inventoryCoverage']['inventoryFamilyCount'])->toBe(6)
->and($report['budgetSnapshots'])->toHaveCount(2)
->and($report['budgetOutcome'])->toHaveKeys([
'decisionStatus',
'finalThresholdSeconds',
'remainingOpenFamilies',
'followUpDebt',
])
->and($report['knownWorkflowProfiles'])->toContain('heavy-governance-manual', 'heavy-governance-scheduled');
});
it('stages deterministic CI artifact bundles from the canonical lane outputs', function (): void {
$artifactDirectory = 'storage/logs/test-lanes/contract-stage-test';
$stagingDirectory = base_path('storage/logs/test-lanes/contract-stage-test/staged');
$report = TestLaneReport::buildReport(
laneId: 'fast-feedback',
wallClockSeconds: 182.4,
slowestEntries: [],
durationsByFile: [],
artifactDirectory: $artifactDirectory,
ciContext: [
'workflowId' => 'pr-fast-feedback',
'triggerClass' => 'pull-request',
'entryPointResolved' => true,
'workflowLaneMatched' => true,
],
);
TestLaneReport::writeArtifacts(
laneId: 'fast-feedback',
report: $report,
profileOutput: null,
artifactDirectory: $artifactDirectory,
exitCode: 0,
);
$artifactPaths = TestLaneReport::artifactPaths('fast-feedback', $artifactDirectory);
file_put_contents(
TestLaneManifest::absolutePath($artifactPaths['junit']),
<<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="fast-feedback" tests="1" assertions="1" errors="0" failures="0" skipped="0" time="0.1">
<testcase name="synthetic" file="tests/Feature/Guards/TestLaneArtifactsContractTest.php" time="0.1" />
</testsuite>
</testsuites>
XML,
);
$stagingResult = TestLaneReport::stageArtifacts(
laneId: 'fast-feedback',
stagingDirectory: $stagingDirectory,
artifactDirectory: $artifactDirectory,
);
expect($stagingResult['complete'])->toBeTrue()
->and(collect($stagingResult['stagedArtifacts'])->pluck('artifactType')->all())
->toEqualCanonicalizing(['summary.md', 'budget.json', 'report.json', 'junit.xml', 'trend-history.json'])
->and(collect($stagingResult['stagedArtifacts'])->pluck('relativePath')->all())
->toContain(
$stagingDirectory.'/fast-feedback.summary.md',
$stagingDirectory.'/fast-feedback.budget.json',
$stagingDirectory.'/fast-feedback.report.json',
$stagingDirectory.'/fast-feedback.junit.xml',
$stagingDirectory.'/fast-feedback.trend-history.json',
);
});
it('publishes the shared fixture slimming comparison only for the governed standard lanes', function (): void {
$fastFeedback = TestLaneReport::buildReport(
laneId: 'fast-feedback',
wallClockSeconds: 176.73623,
slowestEntries: [],
durationsByFile: [],
comparisonProfile: 'shared-test-fixture-slimming',
);
$heavyGovernance = TestLaneReport::buildReport(
laneId: 'heavy-governance',
wallClockSeconds: 83.66,
slowestEntries: [],
durationsByFile: [],
comparisonProfile: 'shared-test-fixture-slimming',
);
expect($fastFeedback)->toHaveKey('sharedFixtureSlimmingComparison')
->and($heavyGovernance)->not->toHaveKey('sharedFixtureSlimmingComparison');
});