scripts/local-vm.test.mjs
Rama main · réplica de lectura del repositorio original.
import assert from "node:assert/strict";
import { mkdtempSync, writeFileSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { spawnSync } from "node:child_process";
import test from "node:test";
function invoke(command, initial, owned = true, network = true) {
const dir = mkdtempSync(join(tmpdir(), "devpush-vm-cli-"));
try {
const state = join(dir, "state"),
calls = join(dir, "calls");
writeFileSync(state, initial);
writeFileSync(calls, "");
// Exercise the macOS-only setup on Linux CI without weakening its host guard.
writeFileSync(join(dir, "uname"), "#!/bin/sh\necho Darwin\n", { mode: 0o755 });
writeFileSync(
join(dir, "limactl"),
`#!${process.execPath}
const fs = require('node:fs'); const a = process.argv.slice(2);
fs.appendFileSync(process.env.CALLS, JSON.stringify(a)+'\\n');
if(a[0]==='list') {
const s=fs.readFileSync(process.env.STATE,'utf8');
if(s) console.log(a.includes('--json') ? JSON.stringify({name:'devpush-test',vmType:'vz',config:{networks:process.env.NETWORK==='1'?[{vzNAT:true}]:[]}}) : 'devpush-test '+s);
}
if(a[0]==='create') fs.writeFileSync(process.env.STATE,'Stopped');
if(a[0]==='start') fs.writeFileSync(process.env.STATE,'Running');
if(a[0]==='shell' && a.includes('/etc/devpush-test-vm') && process.env.OWNED==='0') process.exit(1);
`,
{ mode: 0o755 },
);
const result = spawnSync(
"bash",
[resolve("scripts/local-vm.sh"), command],
{
encoding: "utf8",
env: {
...process.env,
PATH: `${dir}:${process.env.PATH}`,
STATE: state,
CALLS: calls,
OWNED: owned ? "1" : "0",
NETWORK: network ? "1" : "0",
},
},
);
return {
status: result.status,
stderr: result.stderr,
calls: readFileSync(calls, "utf8").trim().split("\n").map(JSON.parse),
};
} finally {
rmSync(dir, { recursive: true, force: true });
}
}
test("setup creates a missing VM; reuses an already running VM", () => {
const first = invoke("setup", "");
assert.equal(first.status, 0, first.stderr);
assert.deepEqual(
first.calls
.filter((a) => ["create", "start"].includes(a[0]))
.map((a) => a[0]),
["create", "start"],
);
const repeat = invoke("setup", "Running");
assert.equal(repeat.status, 0);
assert.equal(
repeat.calls.some((a) => ["create", "start", "delete"].includes(a[0])),
false,
);
});
test("destroy verifies ownership before stopping or deleting the dedicated VM", () => {
const refused = invoke("destroy", "Running", false);
assert.notEqual(refused.status, 0);
assert.equal(
refused.calls.some((a) => ["stop", "delete"].includes(a[0])),
false,
);
const allowed = invoke("destroy", "Running");
assert.equal(allowed.status, 0);
assert.deepEqual(
allowed.calls
.filter((a) => ["stop", "delete"].includes(a[0]))
.map((a) => a[0]),
["stop", "delete"],
);
assert.deepEqual(allowed.calls.at(-1), [
"delete",
"--tty=false",
"devpush-test",
]);
});
test("stopping preserves the disk; destroying an absent VM creates nothing", () => {
const stopped = invoke("stop", "Running");
assert.equal(stopped.status, 0);
assert.deepEqual(stopped.calls.at(-1), ["stop", "devpush-test"]);
const absent = invoke("destroy", "");
assert.equal(absent.status, 0);
assert.equal(
absent.calls.some((a) => ["create", "start", "delete"].includes(a[0])),
false,
);
});
test("setup upgrades owned VM networking; never edits an unowned VM", () => {
const upgrade = invoke("setup", "Running", true, false);
assert.equal(upgrade.status, 0, upgrade.stderr);
assert.deepEqual(
upgrade.calls.filter((a) => ["stop", "edit", "start"].includes(a[0])),
[
["stop", "devpush-test"],
["edit", "--tty=false", "--network=vzNAT", "devpush-test"],
["start", "--tty=false", "devpush-test"],
],
);
const refused = invoke("setup", "Running", false, false);
assert.notEqual(refused.status, 0);
assert.equal(
refused.calls.some((a) => ["stop", "edit", "delete"].includes(a[0])),
false,
);
});
test("VM enrollment downloads the installer before invoking it with the routing address", async () => {
const { createServer } = await import("node:http");
const { spawn } = await import("node:child_process");
const dir = mkdtempSync(join(tmpdir(), "devpush-vm-install-"));
const body = "#!/bin/sh\n# downloaded installer fixture\n";
const server = createServer((_request, response) => response.end(body));
await new Promise((done) => server.listen(0, "127.0.0.1", done));
try {
writeFileSync(
join(dir, "sudo"),
`#!/bin/sh\ncat "$2" > "$RECEIVED"\nprintf '%s' "$3 $4" > "$ARGUMENTS"\nprintf '%s' "$2" > "$TEMP_SCRIPT"\n`,
{ mode: 0o755 },
);
const source = readFileSync(resolve("scripts/local-vm.sh"), "utf8");
const guestScript = source.match(/<<'INSTALL'\n([\s\S]*?)\nINSTALL/)?.[1];
assert.ok(guestScript);
const child = spawn(
"bash",
[
"-s",
"--",
`http://127.0.0.1:${server.address().port}/installer`,
"192.168.64.5",
],
{
env: {
...process.env,
PATH: `${dir}:${process.env.PATH}`,
RECEIVED: join(dir, "received"),
ARGUMENTS: join(dir, "arguments"),
TEMP_SCRIPT: join(dir, "temp-script"),
},
stdio: ["pipe", "ignore", "pipe"],
},
);
let errors = "";
child.stderr.on("data", (chunk) => {
errors += chunk;
});
child.stdin.end(guestScript);
const status = await new Promise((done) => child.on("exit", done));
assert.equal(status, 0, errors);
assert.equal(readFileSync(join(dir, "received"), "utf8"), body);
assert.equal(
readFileSync(join(dir, "arguments"), "utf8"),
"--address 192.168.64.5",
);
const { existsSync } = await import("node:fs");
assert.equal(
existsSync(readFileSync(join(dir, "temp-script"), "utf8")),
false,
);
} finally {
await new Promise((done) => server.close(done));
rmSync(dir, { recursive: true, force: true });
}
});