59 lines
3.4 KiB
JavaScript
59 lines
3.4 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const sourcePath = path.join(repoRoot, "docs/web/operations-diagnostics.json");
|
|
const diagnostics = JSON.parse(fs.readFileSync(sourcePath, "utf8"));
|
|
const contract = JSON.parse(fs.readFileSync(path.join(repoRoot, "docs/web/deployment-contract.json"), "utf8"));
|
|
const guide = fs.readFileSync(path.join(repoRoot, "docs/web/DEPLOYMENT.md"), "utf8");
|
|
const requiredDomains = ["mime", "range", "isolation", "hash", "quota", "worker", "gpu"];
|
|
|
|
assert.equal(diagnostics.schemaVersion, 1);
|
|
assert.equal(diagnostics.product, contract.product);
|
|
assert.deepEqual(diagnostics.entries.map((entry) => entry.domain).sort(), [...requiredDomains].sort());
|
|
assert.equal(new Set(diagnostics.entries.map((entry) => entry.id)).size, requiredDomains.length);
|
|
const allSignals = diagnostics.entries.flatMap((entry) => entry.signals);
|
|
assert.equal(new Set(allSignals).size, allSignals.length, "diagnostic signals must be unambiguous");
|
|
|
|
for (const entry of diagnostics.entries) {
|
|
assert.match(entry.id, /^OPS-[A-Z]+$/);
|
|
for (const name of ["signals", "checks", "expected", "recovery"]) {
|
|
assert.ok(Array.isArray(entry[name]) && entry[name].length > 0, `${entry.id} omits ${name}`);
|
|
assert.ok(entry[name].every((value) => typeof value === "string" && value.length > 0), `${entry.id} has an empty ${name}`);
|
|
}
|
|
assert.ok(typeof entry.dataSafety === "string" && entry.dataSafety.length > 0, `${entry.id} omits dataSafety`);
|
|
assert.ok(guide.toLowerCase().includes(`| ${entry.domain}`), `${entry.id} is absent from DEPLOYMENT.md`);
|
|
}
|
|
|
|
const byDomain = Object.fromEntries(diagnostics.entries.map((entry) => [entry.domain, entry]));
|
|
for (const mime of [contract.mimeTypes[".wasm"], contract.mimeTypes[".js"], contract.mimeTypes[".json"]]) {
|
|
assert.ok(byDomain.mime.expected.some((value) => value.includes(mime)), `MIME diagnostics omit ${mime}`);
|
|
}
|
|
for (const status of [contract.rangeRequests.satisfiedStatus, contract.rangeRequests.unsatisfiedStatus, 200]) {
|
|
assert.ok(byDomain.range.expected.some((value) => value.includes(String(status))), `range diagnostics omit ${status}`);
|
|
}
|
|
for (const [name, value] of Object.entries(contract.responseHeaders.allResponses)) {
|
|
assert.ok(byDomain.isolation.expected.some((item) => item.includes(name) && item.includes(value)), `isolation diagnostics omit ${name}`);
|
|
}
|
|
assert.ok(byDomain.hash.expected.some((value) => value.includes("no fallback") && value.includes("no project open")));
|
|
assert.ok(byDomain.quota.dataSafety.includes("Never delete"));
|
|
assert.ok(byDomain.worker.dataSafety.includes("late Worker output"));
|
|
assert.ok(byDomain.gpu.dataSafety.includes("must not rewrite"));
|
|
|
|
const report = {
|
|
schemaVersion: 1,
|
|
task: "M6-17D",
|
|
status: "READY",
|
|
sourceSha256: crypto.createHash("sha256").update(fs.readFileSync(sourcePath)).digest("hex"),
|
|
domains: requiredDomains,
|
|
entries: diagnostics.entries.length,
|
|
signals: allSignals.length,
|
|
};
|
|
const reportRoot = path.join(repoRoot, "release/operations-reports");
|
|
fs.mkdirSync(reportRoot, { recursive: true });
|
|
fs.writeFileSync(path.join(reportRoot, "diagnostics.json"), `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`operations-diagnostics-ok domains=${requiredDomains.join(",")} signals=${allSignals.length} source=${report.sourceSha256}\n`);
|