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.0-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.0"); assert.equal(manifest.data_policy, "preserve"); assert.deepEqual(manifest.from_versions, ["0.1.9", "1.2.0", "0.2.0"]); 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.0"); 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.0"); 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.0"); assert.equal(fs.existsSync(path.join(simulation, "src", "services", "dependency-manager.js")), true); } finally { fs.rmSync(simulation, { recursive: true, force: true }); } console.log(`Core repair patch verification passed (${entries.length} files, no preserved data payloads).`);