Lumi/scripts/verify-core-repair-patch.js
2026-07-18 16:56:40 +02:00

63 lines
3.1 KiB
JavaScript

const assert = require("assert");
const crypto = require("crypto");
const fs = require("fs");
const os = require("os");
const path = require("path");
const AdmZip = require("adm-zip");
const { verifyPatchPackage } = require("../src/services/update-manager");
const root = path.join(__dirname, "..");
const archivePath = path.join(root, "dist", "lumi-core-1.2.0-to-0.2.4-repair.zip");
assert.equal(fs.existsSync(archivePath), true, "build the repair patch first");
const zip = new AdmZip(archivePath);
const entries = zip.getEntries().filter((entry) => !entry.isDirectory);
const names = new Set(entries.map((entry) => entry.entryName.replace(/\\/g, "/")));
assert.equal(names.has("patch-manifest.json"), true);
const manifest = JSON.parse(zip.readAsText("patch-manifest.json"));
assert.equal(manifest.target, "core");
assert.equal(manifest.to_version, "0.2.4");
assert.equal(manifest.data_policy, "preserve");
assert.deepEqual(manifest.from_versions, ["0.1.9", "1.2.0", "0.2.0", "0.2.1", "0.2.2", "0.2.3", "0.2.4"]);
const forbidden = /^(?:data|plugins|node_modules|config|storage|uploads|logs|database|databases|knowledge\/(?:community|corrections))(?:\/|$)|^\.env(?:\.|$)|^\.secrets$/;
for (const entry of entries) {
assert.doesNotMatch(entry.entryName, forbidden, `repair includes preserved data path ${entry.entryName}`);
}
for (const [relativePath, expected] of Object.entries(manifest.files)) {
assert.equal(names.has(relativePath), true, `${relativePath} is listed but missing`);
const actual = crypto.createHash("sha256").update(zip.readFile(relativePath)).digest("hex");
assert.equal(actual, expected, `${relativePath} checksum`);
}
assert.equal(Object.keys(manifest.files).length + 1, entries.length, "every repair file must be checksummed");
assert.equal(JSON.parse(zip.readAsText("package.json")).version, "0.2.4");
const simulation = fs.mkdtempSync(path.join(os.tmpdir(), "lumi-repair-simulation-"));
try {
const sentinels = [
"data/app.db",
"plugins/okf/data/local.db",
"node_modules/local-only/package.json",
"knowledge/community/people.md",
"knowledge/corrections/fix.md",
".env"
];
for (const sentinel of sentinels) {
const target = path.join(simulation, sentinel);
fs.mkdirSync(path.dirname(target), { recursive: true });
fs.writeFileSync(target, `preserve:${sentinel}`);
}
zip.extractAllTo(simulation, true);
assert.equal(verifyPatchPackage(simulation).to_version, "0.2.4");
for (const sentinel of sentinels) {
assert.equal(fs.readFileSync(path.join(simulation, sentinel), "utf8"), `preserve:${sentinel}`);
}
assert.equal(JSON.parse(fs.readFileSync(path.join(simulation, "package.json"), "utf8")).version, "0.2.4");
assert.equal(fs.existsSync(path.join(simulation, "src", "services", "dependency-manager.js")), true);
assert.equal(fs.existsSync(path.join(simulation, "src", "services", "production-diagnostics.js")), true);
assert.equal(fs.existsSync(path.join(simulation, "src", "web", "views", "admin-diagnostics.ejs")), true);
} finally {
fs.rmSync(simulation, { recursive: true, force: true });
}
console.log(`Core repair patch verification passed (${entries.length} files, no preserved data payloads).`);