138 lines
5.0 KiB
PHP
138 lines
5.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Tests\Support\TestLaneTrendFixtures;
|
|
|
|
function validateTrendSchema(mixed $value, array $schema, array $defs, string $path = '$'): array
|
|
{
|
|
if (isset($schema['$ref']) && is_string($schema['$ref'])) {
|
|
$ref = $schema['$ref'];
|
|
$definitionKey = str_replace('#/$defs/', '', $ref);
|
|
|
|
return validateTrendSchema($value, $defs[$definitionKey] ?? [], $defs, $path);
|
|
}
|
|
|
|
$errors = [];
|
|
$types = $schema['type'] ?? null;
|
|
|
|
if (isset($schema['const']) && $value !== $schema['const']) {
|
|
$errors[] = sprintf('%s must equal %s.', $path, var_export($schema['const'], true));
|
|
}
|
|
|
|
if (isset($schema['enum']) && ! in_array($value, $schema['enum'], true)) {
|
|
$errors[] = sprintf('%s must be one of [%s].', $path, implode(', ', array_map(static fn (mixed $item): string => var_export($item, true), $schema['enum'])));
|
|
}
|
|
|
|
if (is_string($types)) {
|
|
$types = [$types];
|
|
}
|
|
|
|
if (is_array($types)) {
|
|
$typeValid = false;
|
|
|
|
foreach ($types as $type) {
|
|
$typeValid = match ($type) {
|
|
'array' => is_array($value) && array_is_list($value),
|
|
'object' => is_array($value) && ! array_is_list($value),
|
|
'string' => is_string($value),
|
|
'integer' => is_int($value),
|
|
'number' => is_int($value) || is_float($value),
|
|
'boolean' => is_bool($value),
|
|
'null' => $value === null,
|
|
default => false,
|
|
};
|
|
|
|
if ($typeValid) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! $typeValid) {
|
|
$errors[] = sprintf('%s has the wrong type.', $path);
|
|
|
|
return $errors;
|
|
}
|
|
}
|
|
|
|
if (is_array($value) && array_is_list($value)) {
|
|
if (isset($schema['minItems']) && count($value) < (int) $schema['minItems']) {
|
|
$errors[] = sprintf('%s must contain at least %d item(s).', $path, (int) $schema['minItems']);
|
|
}
|
|
|
|
if (isset($schema['items']) && is_array($schema['items'])) {
|
|
foreach ($value as $index => $item) {
|
|
$errors = array_merge($errors, validateTrendSchema($item, $schema['items'], $defs, sprintf('%s[%d]', $path, $index)));
|
|
}
|
|
}
|
|
|
|
return $errors;
|
|
}
|
|
|
|
if (is_array($value) && ! array_is_list($value)) {
|
|
foreach ($schema['required'] ?? [] as $requiredKey) {
|
|
if (! array_key_exists((string) $requiredKey, $value)) {
|
|
$errors[] = sprintf('%s is missing required key [%s].', $path, $requiredKey);
|
|
}
|
|
}
|
|
|
|
if (($schema['additionalProperties'] ?? true) === false) {
|
|
$allowedKeys = array_keys($schema['properties'] ?? []);
|
|
|
|
foreach (array_keys($value) as $key) {
|
|
if (! in_array($key, $allowedKeys, true)) {
|
|
$errors[] = sprintf('%s contains unsupported key [%s].', $path, $key);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($schema['properties'] ?? [] as $key => $propertySchema) {
|
|
if (! array_key_exists($key, $value)) {
|
|
continue;
|
|
}
|
|
|
|
$errors = array_merge($errors, validateTrendSchema($value[$key], $propertySchema, $defs, $path.'.'.$key));
|
|
}
|
|
|
|
return $errors;
|
|
}
|
|
|
|
if ((is_int($value) || is_float($value)) && isset($schema['minimum']) && $value < $schema['minimum']) {
|
|
$errors[] = sprintf('%s must be >= %s.', $path, $schema['minimum']);
|
|
}
|
|
|
|
return $errors;
|
|
}
|
|
|
|
it('keeps the generated trend-history artifact synchronized with the checked-in JSON schema contract', function (): void {
|
|
$schemaPath = repo_path('specs/211-runtime-trend-recalibration/contracts/test-runtime-trend-history.schema.json');
|
|
$artifactDirectory = TestLaneTrendFixtures::artifactDirectory('trend-schema-contract');
|
|
$report = TestLaneTrendFixtures::buildReport(
|
|
laneId: 'fast-feedback',
|
|
wallClockSeconds: 184.6,
|
|
durationsByFile: [
|
|
'tests/Feature/Guards/TestLaneTrendContractSchemaTest.php' => 16.4,
|
|
'tests/Feature/Guards/TestLaneArtifactsContractTest.php' => 11.2,
|
|
],
|
|
artifactDirectory: $artifactDirectory,
|
|
ciContext: [
|
|
'workflowId' => 'pr-fast-feedback',
|
|
'triggerClass' => 'pull-request',
|
|
'entryPointResolved' => true,
|
|
'workflowLaneMatched' => true,
|
|
],
|
|
comparisonProfile: 'shared-test-fixture-slimming',
|
|
);
|
|
|
|
/** @var array<string, mixed> $schema */
|
|
$schema = json_decode((string) file_get_contents($schemaPath), true, 512, JSON_THROW_ON_ERROR);
|
|
$artifact = $report['trendHistoryArtifact'];
|
|
$errors = validateTrendSchema($artifact, $schema, $schema['$defs'] ?? []);
|
|
|
|
expect($artifact['schemaVersion'])->toBe('1.0.0')
|
|
->and($artifact['laneId'])->toBe('fast-feedback')
|
|
->and($artifact['workflowProfile'])->toBe('pr-fast-feedback')
|
|
->and($artifact['history'][0]['artifactRefs']['trendHistory'])->toBe($artifactDirectory.'/fast-feedback-latest.trend-history.json')
|
|
->and($errors)->toBe([]);
|
|
});
|