57dc91585d
Internal SmartGift build of a Claude Code monitoring dashboard. Lanes: a durable unit of parallel agent work, one per working directory, tracked across session restarts. Managed lanes are git worktrees the dashboard provisions and can reset or remove behind a three-check destroy guard and a counted preflight; adopted lanes are directories you already own and are never destroyable. Pipelines: a lane moves through pipeline stages. A stage the agent declares with evidence renders green; a stage inferred from the tool-event stream renders dashed amber and never counts as done. Detection is forward-only within a 30-minute window, and never writes the declared stage. Workspace: one page at /run with a lane grid, the selected lane's pipeline, and a full Claude console behind a disclosure.
170 lines
6.7 KiB
TypeScript
170 lines
6.7 KiB
TypeScript
/**
|
|
* @file app-config.test.ts
|
|
* @description Unit tests for the app configuration loader, which reads environment variables and constructs a configuration object for the MCP server. The tests cover default values, parsing of different transport modes, HTTP port and host parsing with validation, boolean parsing for mutation/destructive flags, timeout and retry parsing with clamping, log level parsing with fallback, dashboard URL validation to ensure it targets a local host and uses http/https, and custom server name/version parsing. The tests use Node's built-in test runner and assert module for assertions.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { loadConfig, type TransportMode } from "../src/config/app-config.js";
|
|
|
|
function env(overrides: Record<string, string> = {}): NodeJS.ProcessEnv {
|
|
return {
|
|
MCP_DASHBOARD_BASE_URL: "http://127.0.0.1:4820",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("loadConfig", () => {
|
|
it("returns sane defaults when no env vars set", () => {
|
|
const cfg = loadConfig(env());
|
|
assert.equal(cfg.serverName, "agent-dashboard-mcp");
|
|
assert.equal(cfg.serverVersion, "1.0.0");
|
|
assert.equal(cfg.dashboardBaseUrl.toString(), "http://127.0.0.1:4820/");
|
|
assert.equal(cfg.requestTimeoutMs, 10_000);
|
|
assert.equal(cfg.retryCount, 2);
|
|
assert.equal(cfg.retryBackoffMs, 250);
|
|
assert.equal(cfg.allowMutations, false);
|
|
assert.equal(cfg.allowDestructive, false);
|
|
assert.equal(cfg.logLevel, "info");
|
|
assert.equal(cfg.transport, "stdio");
|
|
assert.equal(cfg.httpPort, 8819);
|
|
assert.equal(cfg.httpHost, "127.0.0.1");
|
|
});
|
|
|
|
// ── Transport parsing ───────────────────────────────────────
|
|
it("parses MCP_TRANSPORT=http", () => {
|
|
const cfg = loadConfig(env({ MCP_TRANSPORT: "http" }));
|
|
assert.equal(cfg.transport, "http");
|
|
});
|
|
|
|
it("parses MCP_TRANSPORT=repl", () => {
|
|
const cfg = loadConfig(env({ MCP_TRANSPORT: "repl" }));
|
|
assert.equal(cfg.transport, "repl");
|
|
});
|
|
|
|
it("parses MCP_TRANSPORT=stdio", () => {
|
|
const cfg = loadConfig(env({ MCP_TRANSPORT: "stdio" }));
|
|
assert.equal(cfg.transport, "stdio");
|
|
});
|
|
|
|
it("defaults unknown transport to stdio", () => {
|
|
const cfg = loadConfig(env({ MCP_TRANSPORT: "grpc" }));
|
|
assert.equal(cfg.transport, "stdio");
|
|
});
|
|
|
|
it("is case-insensitive for transport", () => {
|
|
const cfg = loadConfig(env({ MCP_TRANSPORT: "HTTP" }));
|
|
assert.equal(cfg.transport, "http");
|
|
});
|
|
|
|
// ── HTTP port/host ──────────────────────────────────────────
|
|
it("parses MCP_HTTP_PORT", () => {
|
|
const cfg = loadConfig(env({ MCP_HTTP_PORT: "9999" }));
|
|
assert.equal(cfg.httpPort, 9999);
|
|
});
|
|
|
|
it("clamps MCP_HTTP_PORT to valid range", () => {
|
|
const low = loadConfig(env({ MCP_HTTP_PORT: "0" }));
|
|
assert.equal(low.httpPort, 1);
|
|
const high = loadConfig(env({ MCP_HTTP_PORT: "99999" }));
|
|
assert.equal(high.httpPort, 65535);
|
|
});
|
|
|
|
it("falls back to default on invalid MCP_HTTP_PORT", () => {
|
|
const cfg = loadConfig(env({ MCP_HTTP_PORT: "banana" }));
|
|
assert.equal(cfg.httpPort, 8819);
|
|
});
|
|
|
|
it("parses MCP_HTTP_HOST", () => {
|
|
const cfg = loadConfig(env({ MCP_HTTP_HOST: "0.0.0.0" }));
|
|
assert.equal(cfg.httpHost, "0.0.0.0");
|
|
});
|
|
|
|
// ── Boolean parsing ─────────────────────────────────────────
|
|
for (const truthy of ["1", "true", "yes", "on", "TRUE", "Yes"]) {
|
|
it(`parses allowMutations='${truthy}' as true`, () => {
|
|
const cfg = loadConfig(env({ MCP_DASHBOARD_ALLOW_MUTATIONS: truthy }));
|
|
assert.equal(cfg.allowMutations, true);
|
|
});
|
|
}
|
|
|
|
for (const falsy of ["0", "false", "no", "off", "FALSE", "No"]) {
|
|
it(`parses allowMutations='${falsy}' as false`, () => {
|
|
const cfg = loadConfig(env({ MCP_DASHBOARD_ALLOW_MUTATIONS: falsy }));
|
|
assert.equal(cfg.allowMutations, false);
|
|
});
|
|
}
|
|
|
|
it("parses allowDestructive", () => {
|
|
const cfg = loadConfig(env({ MCP_DASHBOARD_ALLOW_DESTRUCTIVE: "true" }));
|
|
assert.equal(cfg.allowDestructive, true);
|
|
});
|
|
|
|
// ── Timeout / retry parsing ─────────────────────────────────
|
|
it("parses timeout with clamping", () => {
|
|
const cfg = loadConfig(env({ MCP_DASHBOARD_TIMEOUT_MS: "200" }));
|
|
assert.equal(cfg.requestTimeoutMs, 500); // min 500
|
|
});
|
|
|
|
it("parses retry count", () => {
|
|
const cfg = loadConfig(env({ MCP_DASHBOARD_RETRY_COUNT: "5" }));
|
|
assert.equal(cfg.retryCount, 5);
|
|
});
|
|
|
|
// ── Log level ───────────────────────────────────────────────
|
|
it("parses valid log level", () => {
|
|
const cfg = loadConfig(env({ MCP_LOG_LEVEL: "debug" }));
|
|
assert.equal(cfg.logLevel, "debug");
|
|
});
|
|
|
|
it("defaults invalid log level to info", () => {
|
|
const cfg = loadConfig(env({ MCP_LOG_LEVEL: "verbose" }));
|
|
assert.equal(cfg.logLevel, "info");
|
|
});
|
|
|
|
// ── Dashboard URL validation ────────────────────────────────
|
|
it("rejects non-local dashboard hosts", () => {
|
|
assert.throws(
|
|
() => loadConfig(env({ MCP_DASHBOARD_BASE_URL: "http://evil.com:4820" })),
|
|
/must target a local dashboard host/
|
|
);
|
|
});
|
|
|
|
it("rejects non-http protocols", () => {
|
|
assert.throws(
|
|
() => loadConfig(env({ MCP_DASHBOARD_BASE_URL: "ftp://127.0.0.1:4820" })),
|
|
/must use http or https/
|
|
);
|
|
});
|
|
|
|
it("rejects invalid URLs", () => {
|
|
assert.throws(
|
|
() => loadConfig(env({ MCP_DASHBOARD_BASE_URL: "not a url" })),
|
|
/Invalid MCP_DASHBOARD_BASE_URL/
|
|
);
|
|
});
|
|
|
|
it("accepts localhost", () => {
|
|
const cfg = loadConfig(env({ MCP_DASHBOARD_BASE_URL: "http://localhost:4820" }));
|
|
assert.equal(cfg.dashboardBaseUrl.hostname, "localhost");
|
|
});
|
|
|
|
it("accepts host.docker.internal", () => {
|
|
const cfg = loadConfig(env({ MCP_DASHBOARD_BASE_URL: "http://host.docker.internal:4820" }));
|
|
assert.equal(cfg.dashboardBaseUrl.hostname, "host.docker.internal");
|
|
});
|
|
|
|
// ── Custom server name/version ──────────────────────────────
|
|
it("parses custom server name and version", () => {
|
|
const cfg = loadConfig(
|
|
env({
|
|
MCP_SERVER_NAME: "my-mcp",
|
|
MCP_SERVER_VERSION: "2.0.0",
|
|
})
|
|
);
|
|
assert.equal(cfg.serverName, "my-mcp");
|
|
assert.equal(cfg.serverVersion, "2.0.0");
|
|
});
|
|
});
|