3b16fdd52a
- mécanisme presetVars (variables fixes injectées au rendu, surchargées par le formulaire) - 6 profils : base_tools / network_tools / dev_git (listes de paquets, low), docker_official (dépôt officiel Debian, confirmation), sharing (Samba/NFS/mDNS, confirmation), vm_guest_tools (qemu/vmware) - 4 templates custom (install-package-groups, docker-official-debian, sharing, vm-guest-tools) émettant PKG_INSTALLED/SERVICE_ENABLED/ERR → réutilise buildPostInstallResult - l'UI post-install générique les expose automatiquement (manifeste → formulaire → run) tsc 0 · 104 tests · build OK · boot OK (8 profils servis). Clôt le volet moteur tâche 2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
131 lines
4.2 KiB
TypeScript
131 lines
4.2 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import {
|
|
PROFILES,
|
|
validateProfileValues,
|
|
maskSecretValues,
|
|
buildPostInstallResult,
|
|
previewProfile,
|
|
type ProfileManifest,
|
|
} from "./postInstall.js";
|
|
|
|
describe("validateProfileValues", () => {
|
|
const identity = PROFILES.identity_network!;
|
|
|
|
it("échoue si un champ requis manque", () => {
|
|
const r = validateProfileValues(identity, { newHostname: "srv1" });
|
|
expect(r.ok).toBe(false);
|
|
expect(r.errors.some((e) => e.field === "interfaceName")).toBe(true);
|
|
});
|
|
|
|
it("échoue sur une IP/CIDR invalide", () => {
|
|
const r = validateProfileValues(identity, {
|
|
newHostname: "srv1",
|
|
domain: "home",
|
|
interfaceName: "eth0",
|
|
staticAddress: "999.1.1.1/24",
|
|
gateway: "10.0.0.1",
|
|
dnsNameservers: "10.0.0.1",
|
|
reconnectHost: "10.0.0.50",
|
|
});
|
|
expect(r.ok).toBe(false);
|
|
expect(r.errors.some((e) => e.field === "staticAddress")).toBe(true);
|
|
});
|
|
|
|
it("passe avec des valeurs valides", () => {
|
|
const r = validateProfileValues(identity, {
|
|
newHostname: "srv1",
|
|
domain: "home",
|
|
interfaceName: "eth0",
|
|
staticAddress: "10.0.0.50/22",
|
|
gateway: "10.0.0.1",
|
|
dnsNameservers: "10.0.0.1 10.0.0.10",
|
|
reconnectHost: "10.0.0.50",
|
|
});
|
|
expect(r.ok).toBe(true);
|
|
expect(r.errors).toHaveLength(0);
|
|
});
|
|
});
|
|
|
|
describe("maskSecretValues", () => {
|
|
const manifest: ProfileManifest = {
|
|
id: "x",
|
|
label: "x",
|
|
description: "",
|
|
risk: "low",
|
|
requiresConfirmation: false,
|
|
template: "custom/bootstrap-root.sh.tpl",
|
|
fields: [
|
|
{ name: "user", type: "string", required: true },
|
|
{ name: "token", type: "secret", required: true },
|
|
],
|
|
};
|
|
|
|
it("masque les champs secret et conserve les autres", () => {
|
|
const masked = maskSecretValues(manifest, { user: "gilles", token: "s3cr3t-ABC" });
|
|
expect(masked.user).toBe("gilles");
|
|
expect(masked.token).toBe("********");
|
|
expect(JSON.stringify(masked)).not.toContain("s3cr3t");
|
|
});
|
|
});
|
|
|
|
describe("profils SJ-9 (presetVars + sections)", () => {
|
|
it("base_tools injecte la liste de paquets fixe", () => {
|
|
expect(PROFILES.base_tools).toBeTruthy();
|
|
const script = previewProfile("base_tools", {});
|
|
expect(script).toContain("nano");
|
|
expect(script).toContain("htop");
|
|
});
|
|
|
|
it("sharing ne rend que les paquets cochés", () => {
|
|
const script = previewProfile("sharing", { installSamba: true, installNfs: false, installMdns: true });
|
|
expect(script).toContain("samba");
|
|
expect(script).toContain("avahi-daemon");
|
|
expect(script).not.toContain("nfs-kernel-server");
|
|
});
|
|
|
|
it("docker_official exige une confirmation", () => {
|
|
expect(PROFILES.docker_official!.requiresConfirmation).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("buildPostInstallResult", () => {
|
|
const raw = [
|
|
"===SU:CUSTOM_IDENTITY===",
|
|
"FILE_MODIFIED=/etc/hosts",
|
|
"FILE_MODIFIED=/etc/network/interfaces",
|
|
"OLD_ENDPOINT=10.0.0.99",
|
|
"HOSTNAME_SET=srv1",
|
|
"ERR=interface_not_found",
|
|
"NEW_ENDPOINT=10.0.0.50",
|
|
"RECONNECT_REQUIRED=1",
|
|
"REBOOT_REQUESTED=1",
|
|
"===SU:EXIT=0===",
|
|
].join("\n");
|
|
|
|
it("extrait fichiers modifiés, reboot, changement réseau et erreurs", () => {
|
|
const r = buildPostInstallResult(raw, ["identity_network"], { newHostname: "srv1" });
|
|
expect(r.profilesRun).toEqual(["identity_network"]);
|
|
expect(r.filesModified).toContain("/etc/hosts");
|
|
expect(r.filesModified).toContain("/etc/network/interfaces");
|
|
expect(r.rebootsRequested).toBe(true);
|
|
expect(r.networkChange).toEqual({ oldEndpoint: "10.0.0.99", newEndpoint: "10.0.0.50", reconnectHost: "10.0.0.50" });
|
|
expect(r.errors?.some((e) => e.kind === "interface_not_found")).toBe(true);
|
|
expect(r.variablesUsed.newHostname).toBe("srv1");
|
|
});
|
|
|
|
it("parse les paquets installés du bootstrap", () => {
|
|
const boot = [
|
|
"===SU:CUSTOM_BOOTSTRAP===",
|
|
"PKG_INSTALLED=sudo",
|
|
"PKG_INSTALLED=curl",
|
|
"GROUP_ADDED=sudo:gilles",
|
|
"SUDO_OK=1",
|
|
"===SU:EXIT=0===",
|
|
].join("\n");
|
|
const r = buildPostInstallResult(boot, ["bootstrap_root"], { operatorUser: "gilles" });
|
|
expect(r.packagesInstalled).toEqual(["sudo", "curl"]);
|
|
expect(r.rebootsRequested).toBe(false);
|
|
expect(r.errors ?? []).toHaveLength(0);
|
|
});
|
|
});
|