Files
AgentHub/server/__tests__/autostart.test.js
T
nntrivi2001 03ecc9e29a feat: start-at-login is a Settings switch, off by default (v2.5.0)
- Settings → About → "Start AgentHub automatically" (AutostartSettings.tsx,
  GET/PUT /api/settings/autostart, server/lib/autostart.js). The switch is
  the systemd --user unit's / LaunchAgent's enabled state: on writes a unit
  for the running server when none exists and enables it, off disables it;
  neither starts or stops the server. A foreign unit is never touched
  (409 EFOREIGN)
- The plugin bootstrap no longer installs a unit on every install: it
  installs or starts one only while the user has it enabled, and a rewrite
  keeps the enabled state (writeUserService({ enable })). An install that
  already had the unit enabled stays on. A stale-but-enabled unit beside a
  live detached server is repointed, no longer started onto the bound port
- unitIsEnabled / setUnitEnabled in systemd-service.js and launchd-service.js

Also:
- fix(workspace): status bar no longer covers the chat box below md (#61)
2026-10-06 18:53:10 +07:00

104 lines
3.8 KiB
JavaScript

/**
* @file Tests server/lib/autostart.js (Settings → About's "Start AgentHub
* automatically"): off by default, on writes + enables a unit for this server
* without starting it, off disables without stopping, and a foreign unit is
* never touched. Also the bootstrap's half: `writeUserService({enable:false})`
* leaves the enabled state alone. systemctl is faked; HOME is a temp dir.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { describe, it, beforeEach, after } = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const TMP_HOME = fs.mkdtempSync(path.join(os.tmpdir(), "agenthub-autostart-"));
const prevHome = process.env.HOME;
process.env.HOME = TMP_HOME;
const svc = require("../../scripts/systemd-service");
const autostart = require("../lib/autostart");
svc.enableLinger = () => true; // the real one runs loginctl, outside the seam
let calls = [];
let enabled = false;
beforeEach(() => {
calls = [];
enabled = false;
fs.rmSync(svc.unitPath("agenthub"), { force: true });
svc.__setExecImpl((args) => {
if (args[0] === "--version") return { status: 0 };
calls.push(args.join(" "));
if (args[0] === "is-enabled")
return { status: 0, stdout: enabled ? "enabled\n" : "disabled\n" };
if (args[0] === "enable") enabled = true;
if (args[0] === "disable") enabled = false;
return { status: 0 };
});
});
after(() => {
svc.__reset();
if (prevHome === undefined) delete process.env.HOME;
else process.env.HOME = prevHome;
fs.rmSync(TMP_HOME, { recursive: true, force: true });
});
const linuxOnly = { skip: process.platform !== "linux" ? "systemd is Linux-only" : false };
const startsOrStops = (c) => /^(start|stop|restart)\b|--now/.test(c);
describe("autostart", linuxOnly, () => {
it("is off with no unit, and turning it off writes nothing", () => {
assert.equal(autostart.status().enabled, false);
autostart.setEnabled(false);
assert.equal(fs.existsSync(svc.unitPath("agenthub")), false);
});
it("on writes a unit for this server and enables it, starting nothing", () => {
const res = autostart.setEnabled(true);
assert.equal(res.enabled, true);
const body = fs.readFileSync(svc.unitPath("agenthub"), "utf8");
assert.match(
body,
new RegExp(`^WorkingDirectory=${path.resolve(__dirname, "..", "..")}$`, "m")
);
assert.ok(calls.includes("enable agenthub.service"));
assert.ok(!calls.some(startsOrStops), JSON.stringify(calls));
});
it("off disables our unit and keeps it, stopping nothing", () => {
autostart.setEnabled(true);
calls = [];
const res = autostart.setEnabled(false);
assert.equal(res.enabled, false);
assert.ok(calls.includes("disable agenthub.service"));
assert.ok(!calls.some(startsOrStops), JSON.stringify(calls));
assert.ok(fs.existsSync(svc.unitPath("agenthub")));
});
it("refuses a unit it did not write", () => {
fs.mkdirSync(path.dirname(svc.unitPath("agenthub")), { recursive: true });
fs.writeFileSync(svc.unitPath("agenthub"), "[Service]\nExecStart=/opt/mine\n", "utf8");
assert.equal(autostart.status().foreign, true);
assert.throws(() => autostart.setEnabled(true), { code: "EFOREIGN" });
assert.equal(
fs.readFileSync(svc.unitPath("agenthub"), "utf8"),
"[Service]\nExecStart=/opt/mine\n"
);
});
it("writeUserService({enable:false}) repoints without enabling — the bootstrap's off path", () => {
svc.writeUserService({
name: "agenthub",
description: "x",
workingDir: "/plugin/root",
execStart: "/usr/bin/node /plugin/root/server/index.js",
enable: false,
});
assert.ok(!calls.some((c) => c.startsWith("enable")), JSON.stringify(calls));
assert.equal(svc.unitIsEnabled("agenthub"), false);
});
});