Files
pleno-vue/scripts/test-graph/result-schema.mjs
T
Jeppe B 12a2d73b32 Replace monolithic E2E matrices with dependency-aware component CI
Generate small reusable component partitions with explicit provider dependencies, GitHub-hosted concurrency, fail-closed evidence, and exact mobile store gates. Include the literal-i18n fix from #206 so validation covers the exact post-merge tree.
2026-07-20 23:52:15 +02:00

61 lines
2.5 KiB
JavaScript

export const DEPENDENCY_CI_ARTIFACT_PREFIX = "dependency-ci-results-";
export const DEPENDENCY_CI_RESULT_FILE = "dependency-ci-results.json";
export const DEPENDENCY_CI_SCHEMA_VERSION = 2;
export const dependencyCiStatuses = ["success", "failed", "dependency-blocked", "cancelled", "missing", "unselected"];
const requiredString = (value, label) => {
if (typeof value !== "string" || !value.trim()) {
throw new Error(`${label} must be a non-empty string.`);
}
};
export function validateDependencyCiResults(payload) {
if (!payload || typeof payload !== "object") {
throw new Error("Dependency CI results must be an object.");
}
if (payload.schemaVersion !== DEPENDENCY_CI_SCHEMA_VERSION) {
throw new Error(`Unsupported dependency CI schema version: ${payload.schemaVersion}.`);
}
requiredString(payload.sourceSha, "sourceSha");
requiredString(payload.profile, "profile");
if (!Number.isInteger(payload.graphVersion) || payload.graphVersion < 1) {
throw new Error("graphVersion must be a positive integer.");
}
if (typeof payload.requiredCi !== "boolean") {
throw new Error("requiredCi must be boolean.");
}
if (payload.infrastructure !== undefined) {
if (
!payload.infrastructure ||
typeof payload.infrastructure !== "object" ||
Array.isArray(payload.infrastructure)
) {
throw new Error("infrastructure must be an object.");
}
for (const jobId of ["plan_e2e", "quality", "build"]) {
if (!dependencyCiStatuses.includes(payload.infrastructure[jobId])) {
throw new Error(`infrastructure.${jobId} is invalid.`);
}
}
}
if (!Array.isArray(payload.results)) {
throw new Error("results must be an array.");
}
for (const [index, result] of payload.results.entries()) {
requiredString(result?.nodeId, `results[${index}].nodeId`);
requiredString(result?.lane, `results[${index}].lane`);
if (!dependencyCiStatuses.includes(result?.status)) {
throw new Error(`results[${index}].status is invalid.`);
}
for (const countField of ["expectedTests", "executedTests", "skippedTests"]) {
if (!Number.isInteger(result[countField]) || result[countField] < 0) {
throw new Error(`results[${index}].${countField} must be a non-negative integer.`);
}
}
if (!Array.isArray(result.roles) || result.roles.some((role) => typeof role !== "string" || !role)) {
throw new Error(`results[${index}].roles must be an array of non-empty strings.`);
}
}
return payload;
}