tests / setup.test.cjs
"use strict";
/**
* Tests for scripts/setup.cjs (postinstall hook).
*
* These tests verify the setup script's behavior:
* 1. Clones the repo when qemu-mcp-server/ does not exist.
* 2. Does git pull --rebase when qemu-mcp-server/ already exists.
* 3. Runs cargo build --release and verifies the binary.
* 4. Sets PATH and HOME environment variables.
* 5. Idempotency.
*
* Run with: node --test tests/setup.test.cjs
*/
const { test } = require("node:test");
const { strict: assert } = require("node:assert");
const { execSync } = require("child_process");
const { existsSync, writeFileSync, unlinkSync, mkdirSync, renameSync } = require("fs");
const { resolve, join } = require("path");
const SETUP_SCRIPT = resolve(__dirname, "..", "scripts", "setup.cjs");
const PLUGIN_ROOT = resolve(__dirname, "..");
const QEMU_MCP_SERVER_DIR = resolve(PLUGIN_ROOT, "qemu-mcp-server");
const QEMU_MCP_SERVER_DIR_BACKUP = resolve(PLUGIN_ROOT, "qemu-mcp-server.backup");
// ──────────────────────────────────────────────────────────────────────────
// Helper functions
// ──────────────────────────────────────────────────────────────────────────
/**
* Run the setup script with a modified environment.
*/
function runSetup(envOverrides = {}) {
const env = { ...process.env, ...envOverrides };
// Use spawnSync to capture both stdout and stderr
const { spawnSync } = require("child_process");
const result = spawnSync("node", [SETUP_SCRIPT], {
cwd: PLUGIN_ROOT,
env,
encoding: "utf8",
timeout: 120000,
stdio: ["pipe", "pipe", "pipe"],
});
// Combine stdout and stderr for assertion
const stdout = typeof result.stdout === "string" ? result.stdout : (result.stdout ? result.stdout.toString() : "");
const stderr = typeof result.stderr === "string" ? result.stderr : (result.stderr ? result.stderr.toString() : "");
return stdout + stderr;
}
/**
* Temporarily remove the qemu-mcp-server directory.
*/
function removeSourceDir() {
if (existsSync(QEMU_MCP_SERVER_DIR)) {
renameSync(QEMU_MCP_SERVER_DIR, QEMU_MCP_SERVER_DIR_BACKUP);
}
}
/**
* Restore the qemu-mcp-server directory.
*/
function restoreSourceDir() {
if (existsSync(QEMU_MCP_SERVER_DIR_BACKUP)) {
renameSync(QEMU_MCP_SERVER_DIR_BACKUP, QEMU_MCP_SERVER_DIR);
}
}
// ──────────────────────────────────────────────────────────────────────────
// Tests
// ──────────────────────────────────────────────────────────────────────────
test("setup.cjs sets PATH and HOME when undefined", (t) => {
// We test this by checking that the setup script runs successfully
// and sets PATH and HOME for child processes (cargo, git).
// The setup.cjs script sets PATH and HOME if they are undefined.
// We verify this by checking that it doesn't crash with "command not found"
// for cargo or git, and that it completes successfully.
const output = runSetup({});
assert.ok(output.includes("qemu-mcp plugin is ready to use"), "should complete successfully");
assert.ok(output.includes("Build complete"), "should report build completion");
});
test("setup.cjs builds the Rust binary", (t) => {
// Verify the binary exists after setup
const binaryPath = resolve(QEMU_MCP_SERVER_DIR, "target", "release", "qemu-mcp-server");
assert.ok(existsSync(binaryPath), `binary should exist at ${binaryPath}`);
});
test("setup.cjs is idempotent (running twice works)", (t) => {
// First run
const output1 = runSetup();
assert.ok(output1.includes("qemu-mcp plugin is ready to use"), "first run should succeed");
// Second run - should do git pull --rebase, not re-clone
const output2 = runSetup();
assert.ok(output2.includes("qemu-mcp plugin is ready to use"), "second run should succeed");
});
test("setup.cjs git pull when source already exists", (t) => {
// Ensure the source dir exists
assert.ok(existsSync(QEMU_MCP_SERVER_DIR), "source dir should exist");
// Run setup - it should do git pull --rebase
const output = runSetup();
assert.ok(output.includes("Source tree updated successfully"), "should update existing source tree");
});
test("setup.cjs verifies binary after build", (t) => {
// The setup script verifies the binary exists after build.
// If it doesn't, it throws an error. We verify by checking the output.
const output = runSetup();
assert.ok(output.includes("Build complete"), "should report build completion");
});
tests / setup.test.cjs
"use strict";
/**
* Tests for scripts/setup.cjs (postinstall hook).
*
* These tests verify the setup script's behavior:
* 1. Clones the repo when qemu-mcp-server/ does not exist.
* 2. Does git pull --rebase when qemu-mcp-server/ already exists.
* 3. Runs cargo build --release and verifies the binary.
* 4. Sets PATH and HOME environment variables.
* 5. Idempotency.
*
* Run with: node --test tests/setup.test.cjs
*/
const { test } = require("node:test");
const { strict: assert } = require("node:assert");
const { execSync } = require("child_process");
const { existsSync, writeFileSync, unlinkSync, mkdirSync, renameSync } = require("fs");
const { resolve, join } = require("path");
const SETUP_SCRIPT = resolve(__dirname, "..", "scripts", "setup.cjs");
const PLUGIN_ROOT = resolve(__dirname, "..");
const QEMU_MCP_SERVER_DIR = resolve(PLUGIN_ROOT, "qemu-mcp-server");
const QEMU_MCP_SERVER_DIR_BACKUP = resolve(PLUGIN_ROOT, "qemu-mcp-server.backup");
// ──────────────────────────────────────────────────────────────────────────
// Helper functions
// ──────────────────────────────────────────────────────────────────────────
/**
* Run the setup script with a modified environment.
*/
function runSetup(envOverrides = {}) {
const env = { ...process.env, ...envOverrides };
// Use spawnSync to capture both stdout and stderr
const { spawnSync } = require("child_process");
const result = spawnSync("node", [SETUP_SCRIPT], {
cwd: PLUGIN_ROOT,
env,
encoding: "utf8",
timeout: 120000,
stdio: ["pipe", "pipe", "pipe"],
});
// Combine stdout and stderr for assertion
const stdout = typeof result.stdout === "string" ? result.stdout : (result.stdout ? result.stdout.toString() : "");
const stderr = typeof result.stderr === "string" ? result.stderr : (result.stderr ? result.stderr.toString() : "");
return stdout + stderr;
}
/**
* Temporarily remove the qemu-mcp-server directory.
*/
function removeSourceDir() {
if (existsSync(QEMU_MCP_SERVER_DIR)) {
renameSync(QEMU_MCP_SERVER_DIR, QEMU_MCP_SERVER_DIR_BACKUP);
}
}
/**
* Restore the qemu-mcp-server directory.
*/
function restoreSourceDir() {
if (existsSync(QEMU_MCP_SERVER_DIR_BACKUP)) {
renameSync(QEMU_MCP_SERVER_DIR_BACKUP, QEMU_MCP_SERVER_DIR);
}
}
// ──────────────────────────────────────────────────────────────────────────
// Tests
// ──────────────────────────────────────────────────────────────────────────
test("setup.cjs sets PATH and HOME when undefined", (t) => {
// We test this by checking that the setup script runs successfully
// and sets PATH and HOME for child processes (cargo, git).
// The setup.cjs script sets PATH and HOME if they are undefined.
// We verify this by checking that it doesn't crash with "command not found"
// for cargo or git, and that it completes successfully.
const output = runSetup({});
assert.ok(output.includes("qemu-mcp plugin is ready to use"), "should complete successfully");
assert.ok(output.includes("Build complete"), "should report build completion");
});
test("setup.cjs builds the Rust binary", (t) => {
// Verify the binary exists after setup
const binaryPath = resolve(QEMU_MCP_SERVER_DIR, "target", "release", "qemu-mcp-server");
assert.ok(existsSync(binaryPath), `binary should exist at ${binaryPath}`);
});
test("setup.cjs is idempotent (running twice works)", (t) => {
// First run
const output1 = runSetup();
assert.ok(output1.includes("qemu-mcp plugin is ready to use"), "first run should succeed");
// Second run - should do git pull --rebase, not re-clone
const output2 = runSetup();
assert.ok(output2.includes("qemu-mcp plugin is ready to use"), "second run should succeed");
});
test("setup.cjs git pull when source already exists", (t) => {
// Ensure the source dir exists
assert.ok(existsSync(QEMU_MCP_SERVER_DIR), "source dir should exist");
// Run setup - it should do git pull --rebase
const output = runSetup();
assert.ok(output.includes("Source tree updated successfully"), "should update existing source tree");
});
test("setup.cjs verifies binary after build", (t) => {
// The setup script verifies the binary exists after build.
// If it doesn't, it throws an error. We verify by checking the output.
const output = runSetup();
assert.ok(output.includes("Build complete"), "should report build completion");
});