tests/fix-optin.test.js
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: GPL-3.0-or-later
/**
* The opt-in gate for `--fix --yes`, tested without touching the host.
*
* `fix-dryrun.test.js` proves the default is a dry run; it deliberately never
* runs the `--yes` path because that path executes real system commands. This
* file closes that gap safely:
*
* - a user plugin emits findings whose codes exist in the safe-fix catalog,
* so the plan is deterministic and needs no real system state;
* - PATH is prefixed with stub `systemctl` / `podman` / `docker` scripts that
* only append their arguments to a log, so nothing real is executed;
* - the assertions are about *what was invoked*, not about system state.
*
* Guarantees checked:
* 1. `--fix` without `--yes` invokes no fix command at all.
* 2. `--fix --yes` invokes the `[apply]` commands.
* 3. `--fix --yes` never invokes `[manual]` commands.
*/
import { test } from "node:test";
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
const ROOT = join(import.meta.dirname, "..");
const PLUGIN = `export default {
id: "zz-fixprobe",
title: "fix probe",
async run() {
return [
{
severity: "medium",
code: "services/failed",
title: "1 service failed to start",
detail: "probe",
evidence: "zz-fixprobe.service user",
fix: "probe",
confidence: "high",
},
{
severity: "medium",
code: "containerdisk/high",
title: "container storage is large",
detail: "probe",
evidence: "probe",
fix: "probe",
confidence: "high",
},
];
},
};
`;
/** A stub that records its arguments instead of doing anything. */
function stub(dir, name, logName) {
const file = join(dir, name);
writeFileSync(
file,
`#!/bin/sh\nprintf '%s\\n' "$*" >> "${join(dir, logName)}"\nexit 0\n`,
"utf8",
);
chmodSync(file, 0o755);
}
function setup() {
const dir = mkdtempSync(join(tmpdir(), "ld-optin-"));
const plugins = join(dir, "plugins");
const bin = join(dir, "bin");
mkdirSync(plugins);
mkdirSync(bin);
writeFileSync(join(plugins, "zz-fixprobe.js"), PLUGIN, "utf8");
stub(bin, "systemctl", "systemctl.log");
stub(bin, "podman", "podman.log");
stub(bin, "docker", "docker.log");
return { dir, plugins, bin, logs: join(dir) };
}
function runFix({ plugins, bin, dir }, extraArgs) {
return spawnSync(process.execPath, ["bin/doctor.js", "--fix", ...extraArgs], {
cwd: ROOT,
encoding: "utf8",
timeout: 120000,
env: {
...process.env,
PATH: `${bin}:${process.env.PATH}`,
LINUX_DOCTOR_PLUGINS: plugins,
LINUX_DOCTOR_NO_HISTORY: "1",
LINUX_DOCTOR_CONFIG: join(dir, "config.json"),
LINUX_DOCTOR_CACHE: join(dir, "cache"),
},
});
}
function readLog(dir, name) {
const file = join(dir, name);
return existsSync(file) ? readFileSync(file, "utf8") : "";
}
test("--fix without --yes executes no fix command", () => {
const ctx = setup();
const r = runFix(ctx, ["--check", "zz-fixprobe"]);
assert.equal(r.error, undefined, `spawn failed: ${r.error?.message}`);
assert.equal(r.status, 0, `expected exit 0, got ${r.status}: ${r.stderr}`);
assert.match(r.stdout, /dry run — nothing was executed/);
assert.match(r.stdout, /\[apply\].*systemctl --user reset-failed/, "the plan should list the apply command");
assert.equal(readLog(ctx.bin, "systemctl.log"), "", "systemctl must not run on a dry run");
assert.equal(readLog(ctx.bin, "podman.log"), "", "podman must not run on a dry run");
assert.equal(readLog(ctx.bin, "docker.log"), "", "docker must not run on a dry run");
});
test("--fix --yes executes the [apply] commands", () => {
const ctx = setup();
const r = runFix(ctx, ["--yes", "--check", "zz-fixprobe"]);
assert.equal(r.error, undefined, `spawn failed: ${r.error?.message}`);
assert.equal(r.status, 0, `expected exit 0, got ${r.status}: ${r.stderr}`);
const calls = readLog(ctx.bin, "systemctl.log");
assert.match(calls, /--user reset-failed zz-fixprobe\.service/);
assert.match(calls, /--user restart zz-fixprobe\.service/);
});
test("--fix --yes never executes [manual] commands", () => {
const ctx = setup();
const r = runFix(ctx, ["--yes", "--check", "zz-fixprobe"]);
assert.equal(r.status, 0, `expected exit 0, got ${r.status}: ${r.stderr}`);
// containerdisk/high is tier "manual": it is printed, never run.
assert.match(r.stdout, /\[manual\]/, "the manual entry should still be printed");
assert.equal(readLog(ctx.bin, "podman.log"), "", "manual podman command must not run");
assert.equal(readLog(ctx.bin, "docker.log"), "", "manual docker command must not run");
});