feat: socle BDD (tâche 1.9 Phase 1-2) + moteur APT (tâche 2 SJ-0→3) + WIP capabilities/auth/Rust
Checkpoint multi-chantiers (arbre vert : tsc 0 erreur, 70 tests, build OK). - tâche 1.9 Phase 1 : schéma socle (machine_state/events/reports/raw_artifacts/ hardware/metrics + colonnes étendues) + wiring refresh/execute. Migration 0002. - tâche 1.9 Phase 2 : machine_credentials + machine_host_keys (non destructif, dual-read + backfill). Migration 0003. Fix séquence journal de migration. - tâche 2 : SJ-0 (types étendus rétro-compatibles, réducteur Docker, resolveTemplate), SJ-1 (update-analyze enrichi), SJ-2 (apply + diff dpkg + timeout inactivité SSH), SJ-3 (reboot vérifié boot_id). - WIP parallèle inclus : /api/capabilities, auth/apiTokens/apiClients, system metrics, scaffold app_rust, ajustements frontend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,17 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { parseAptSimulate, parseRebootRequired } from "./aptParse.js";
|
||||
import { parseAptSimulate, parseRebootRequired, parseAptRemovals, parseHeld, parseRebootDetail, buildAptSnapshotDetail, parseDpkgList, buildAptExecutionResult } from "./aptParse.js";
|
||||
|
||||
const raw = readFileSync(fileURLToPath(new URL("./__fixtures__/apt-simulate.txt", import.meta.url)), "utf8");
|
||||
|
||||
const ua = readFileSync(fileURLToPath(new URL("./__fixtures__/apt-update-analyze.txt", import.meta.url)), "utf8");
|
||||
function section(rawInput: string, start: string, end: string): string {
|
||||
const s = rawInput.indexOf(start); if (s === -1) return "";
|
||||
const from = s + start.length; const e = rawInput.indexOf(end, from);
|
||||
return rawInput.slice(from, e === -1 ? undefined : e).trim();
|
||||
}
|
||||
|
||||
describe("parseAptSimulate", () => {
|
||||
it("extrait les paquets upgradables avec versions et origine", () => {
|
||||
const pkgs = parseAptSimulate(raw);
|
||||
@@ -28,3 +35,74 @@ describe("parseRebootRequired", () => {
|
||||
expect(parseRebootRequired("rien")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseAptRemovals", () => {
|
||||
it("extrait les suppressions Remv", () => {
|
||||
expect(parseAptRemovals("Remv oldpkg [3.2-1]\nInst x [1] (2 Y [amd64])"))
|
||||
.toEqual([{ name: "oldpkg", currentVersion: "3.2-1" }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseHeld", () => {
|
||||
it("liste les paquets retenus non vides", () => {
|
||||
expect(parseHeld("frozenpkg\n\n other ")).toEqual(["frozenpkg", "other"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseRebootDetail", () => {
|
||||
it("lit le flag et les paquets reboot", () => {
|
||||
expect(parseRebootDetail("REBOOT_REQUIRED=1\nPKG=linux-image-amd64\nPKG=foo"))
|
||||
.toEqual({ rebootRequired: true, pkgs: ["linux-image-amd64", "foo"] });
|
||||
expect(parseRebootDetail("REBOOT_REQUIRED=0")).toEqual({ rebootRequired: false, pkgs: [] });
|
||||
});
|
||||
});
|
||||
|
||||
const BEFORE = "libc6\t2.31-13\tamd64\noldpkg\t3.2-1\tamd64\nstable\t1.0\tamd64";
|
||||
const AFTER = "libc6\t2.31-14\tamd64\nnewpkg\t1.0.0\tall\nstable\t1.0\tamd64";
|
||||
|
||||
describe("parseDpkgList", () => {
|
||||
it("indexe par package:arch", () => {
|
||||
const m = parseDpkgList("libc6\t2.31-13\tamd64");
|
||||
expect(m["libc6:amd64"]).toEqual({ version: "2.31-13", arch: "amd64" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildAptExecutionResult", () => {
|
||||
it("calcule le diff réel before/after", () => {
|
||||
const r = buildAptExecutionResult(BEFORE, AFTER, "REBOOT_REQUIRED=1");
|
||||
expect(r.applied.find((c) => c.name === "libc6")).toMatchObject({ operation: "upgraded", fromVersion: "2.31-13", toVersion: "2.31-14" });
|
||||
expect(r.installed.map((c) => c.name)).toEqual(["newpkg"]);
|
||||
expect(r.removed.map((c) => c.name)).toEqual(["oldpkg"]);
|
||||
expect(r.applied.some((c) => c.name === "stable")).toBe(false); // unchanged exclu
|
||||
expect(r.rebootRequiredAfterRun).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildAptSnapshotDetail", () => {
|
||||
it("construit le détail enrichi depuis les sections", () => {
|
||||
const detail = buildAptSnapshotDetail({
|
||||
upgradeSim: section(ua, "===SU:APT_SIM_UPGRADE===", "===SU:APT_SIM_DISTUPGRADE==="),
|
||||
distUpgradeSim: section(ua, "===SU:APT_SIM_DISTUPGRADE===", "===SU:APT_HELD==="),
|
||||
heldRaw: section(ua, "===SU:APT_HELD===", "===SU:REBOOT==="),
|
||||
rebootRaw: section(ua, "===SU:REBOOT===", "===SU:EXIT"),
|
||||
updateFailed: false,
|
||||
});
|
||||
expect(detail.enabled).toBe(true);
|
||||
expect(detail.count).toBe(2); // 2 Inst en dist-upgrade
|
||||
expect(detail.upgradeCount).toBe(1); // 1 Inst en upgrade
|
||||
expect(detail.distUpgradeCount).toBe(2);
|
||||
expect(detail.rebootRequired).toBe(true);
|
||||
expect(detail.rebootPkgs).toEqual(["linux-image-amd64"]);
|
||||
expect(detail.held).toEqual(["frozenpkg"]);
|
||||
expect(detail.removed?.map((r) => r.name)).toEqual(["oldpkg"]);
|
||||
expect(detail.installed?.map((p) => p.name)).toEqual(["newdep"]);
|
||||
expect(detail.status).toBe("warning"); // car removed + held non vides
|
||||
});
|
||||
|
||||
it("status=updates_available sans removed/held, error si update échoue", () => {
|
||||
const ok = buildAptSnapshotDetail({ upgradeSim: "Inst a [1] (2 Y [amd64])", distUpgradeSim: "Inst a [1] (2 Y [amd64])", heldRaw: "", rebootRaw: "REBOOT_REQUIRED=0", updateFailed: false });
|
||||
expect(ok.status).toBe("updates_available");
|
||||
const err = buildAptSnapshotDetail({ upgradeSim: "", distUpgradeSim: "", heldRaw: "", rebootRaw: "REBOOT_REQUIRED=0", updateFailed: true });
|
||||
expect(err.status).toBe("error");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user