Files
gori-agent/test/config.test.ts
T

75 lines
3.8 KiB
TypeScript

import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import test from "node:test";
import { parseConfig } from "../src/config.js";
function raw(overrides: Record<string, unknown> = {}) {
return {
configVersion: 3,
bot: {
id: "test-bot", workspace: "/tmp", persona: "test",
agent: { id: "kimi", command: "kimi", args: ["acp"], env: {} },
skills: [], permissions: { mode: "deny", allowedTools: [], allowedCommandPatterns: [] }
},
gateway: { platform: { type: "webhook", secret: "secret" } },
runtime: {},
...overrides
};
}
test("parses Config v3 defaults for one Bot, agent, and platform", () => {
const config = parseConfig(raw());
assert.equal(config.configVersion, 3);
assert.equal(config.bot.id, "test-bot");
assert.equal(config.bot.agent.id, "kimi");
assert.equal(config.gateway.platform.type, "webhook");
assert.equal(config.runtime.acp.promptTimeoutMs, 14_400_000);
assert.equal(config.runtime.acp.maxAssistantSessions, 4);
assert.equal(config.bot.permissions.mode, "deny");
assert.equal(config.bot.assistantPersona, "");
});
test("parses optional bot.assistantPersona", () => {
const config = parseConfig(raw({ bot: { ...(raw().bot as object), assistantPersona: "像运维老同事一样讲话" } }));
assert.equal(config.bot.assistantPersona, "像运维老同事一样讲话");
});
test("explicitly rejects non-v3 configuration and unknown fields", () => {
assert.throws(() => parseConfig({ configVersion: 2 }), /requires Config v3/);
assert.throws(() => parseConfig({ defaultAgent: "kimi" }), /requires Config v3/);
assert.throws(() => parseConfig(raw({ bot: { ...(raw().bot as object), legacyRole: "ops" } })), /Unrecognized key/);
assert.throws(() => parseConfig(raw({ gateway: { platform: { type: "webhook", secret: "secret", enabled: true } } })), /Unrecognized key/);
});
test("validates bot ID, absolute workspace, skills, and command regex", () => {
assert.throws(() => parseConfig(raw({ bot: { ...(raw().bot as object), id: "../bad" } })), /bot\.id/);
assert.throws(() => parseConfig(raw({ bot: { ...(raw().bot as object), workspace: "relative" } })), /workspace must be absolute/);
assert.throws(() => parseConfig(raw({ bot: { ...(raw().bot as object), skills: [{ id: "x", file: "relative" }] } })), /file must be absolute/);
assert.throws(() => parseConfig(raw({ bot: { ...(raw().bot as object), permissions: { mode: "allowlist", allowedTools: ["bash"], allowedCommandPatterns: ["["] } } })), /invalid command pattern/);
});
test("validates gateway server host and port", () => {
for (const host of ["localhost", "0.0.0.0", "::1", "gateway.example.com"]) {
assert.equal(parseConfig(raw({ gateway: { server: { host, port: 8787 }, platform: { type: "webhook", secret: "secret" } } })).gateway.server.host, host);
}
for (const host of ["http://localhost", "localhost:8787", "[::1]", "bad host", "host/path"]) {
assert.throws(() => parseConfig(raw({ gateway: { server: { host, port: 8787 }, platform: { type: "webhook", secret: "secret" } } })), /server host/);
}
for (const port of [0, 65_536, 1.5]) {
assert.throws(() => parseConfig(raw({ gateway: { server: { host: "localhost", port }, platform: { type: "webhook", secret: "secret" } } })), /gateway/);
}
});
test("config.example.json is a parseable, secret-free documentation template", () => {
const text = fs.readFileSync(path.resolve("config.example.json"), "utf8");
const config = parseConfig(JSON.parse(text) as unknown);
assert.equal(config.configVersion, 3);
assert.equal(config.bot.id, "BOT_ID");
assert.equal(config.bot.permissions.mode, "deny");
assert.equal(config.runtime.acp.promptTimeoutMs, 14_400_000);
assert.equal(config.gateway.platform.type, "qq");
assert.match(text, /QQ_CLIENT_SECRET/);
assert.doesNotMatch(text, /configVersion"\s*:\s*[12]/);
});