190 lines
7.9 KiB
PHP
190 lines
7.9 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']);
|
|
|
|
expect($artifactContract['requiredFiles'])->toEqualCanonicalizing(['summary.md', 'budget.json', 'report.json', 'junit.xml'])
|
|
->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',
|
|
'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'])
|
|
->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',
|
|
);
|
|
});
|
|
|
|
|
|
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');
|
|
});
|