feat: Token 支持多项目关联且全部为 MCP 令牌;配置操作全量审计,支持按项目/键查询
build-and-deploy / build-and-deploy (push) Successful in 40s

This commit is contained in:
jif2.zhang
2026-09-21 09:40:33 +08:00
parent be2e4d9cdf
commit 6bc738d359
13 changed files with 833 additions and 249 deletions
+56 -39
View File
@@ -1,6 +1,6 @@
/**
* Token 服务单元测试
* 覆盖:mcpEnabled 标记、MCP token 列表过滤、历史 MCP 配置迁移
* 覆盖:多项目关联、名称唯一、全部 Token 均为 MCP 令牌、历史 MCP 配置迁移
* 运行:npm test
*/
const test = require("node:test");
@@ -20,61 +20,78 @@ test.before(async () => {
await init();
});
test("createToken 保存 mcpEnabled 标记", async () => {
const mcpToken = await tokenService.createToken({
name: "mcp-key",
projectId: "all",
permission: "readwrite",
mcpEnabled: true,
expiresInDays: 30,
test("创建 Token:支持多个关联项目与全部项目", async () => {
const multi = await tokenService.createToken({
name: "multi-proj",
projectIds: ["proj-a", "proj-b"],
expiresInDays: 0,
});
assert.strictEqual(mcpToken.mcpEnabled, true);
const plainToken = await tokenService.createToken({
name: "plain",
mcpEnabled: false,
});
assert.strictEqual(plainToken.mcpEnabled, false);
assert.deepStrictEqual(multi.projectIds, ["proj-a", "proj-b"]);
assert.strictEqual(tokenService.coversProject(multi, "proj-a"), true);
assert.strictEqual(tokenService.coversProject(multi, "proj-b"), true);
assert.strictEqual(tokenService.coversProject(multi, "proj-c"), false);
const all = await tokenService.createToken({ name: "all-proj", projectIds: ["all"] });
assert.deepStrictEqual(all.projectIds, ["all"]);
assert.strictEqual(tokenService.coversProject(all, "任意项目"), true);
const legacyProjectId = await tokenService.createToken({ name: "legacy-field", projectId: "proj-x" });
assert.deepStrictEqual(legacyProjectId.projectIds, ["proj-x"]);
});
test("getMcpTokenValues 仅返回生效的 MCP token 明文", async () => {
const values = await tokenService.getMcpTokenValues();
assert.ok(values.length >= 1);
// 明文值以 reg_ 开头
assert.ok(values.every((v) => typeof v === "string" && v.startsWith("reg_")));
test("Token 名称唯一:重复名称创建与更新均报错", async () => {
await tokenService.createToken({ name: "dup-name", projectIds: ["all"] });
await assert.rejects(
() => tokenService.createToken({ name: "dup-name", projectIds: ["all"] }),
/名称已存在/
);
// 更新为已存在的名称也报错
const other = await tokenService.createToken({ name: "dup-other", projectIds: ["all"] });
await assert.rejects(
() => tokenService.updateToken(other.id, { name: "dup-name" }),
/名称已存在/
);
// 更新自身名称不改动、且可改成唯一名
await tokenService.updateToken(other.id, { name: "dup-other" });
await tokenService.updateToken(other.id, { name: "dup-renamed" });
assert.strictEqual((await tokenService.findById(other.id)).name, "dup-renamed");
});
test("updateToken 可切换 mcpEnabled", async () => {
const plain = await tokenService.createToken({ name: "toggle", mcpEnabled: false });
const before = await tokenService.getMcpTokenValues();
assert.ok(!before.includes(plain.value));
await tokenService.updateToken(plain.id, { mcpEnabled: true });
const after = await tokenService.getMcpTokenValues();
assert.ok(after.includes(plain.value));
test("所有 Token 均为 MCP 令牌:全部生效 Token 明文可获取,通过 resolveActiveToken 鉴权", async () => {
const t = await tokenService.createToken({ name: "mcp-all", projectIds: ["all"] });
const values = await tokenService.getActiveTokenValues();
assert.ok(values.includes(t.value), "新 Token 应直接生效于 MCP");
const resolved = await tokenService.resolveActiveToken(t.value);
assert.strictEqual(resolved.name, "mcp-all");
assert.strictEqual(await tokenService.resolveActiveToken("bad-token"), null);
});
test("吊销或过期后 MCP token 不再生效", async () => {
const t = await tokenService.createToken({ name: "will-revoke", mcpEnabled: true, expiresInDays: 30 });
const ok = await tokenService.getMcpTokenValues();
assert.ok(ok.includes(t.value));
test("吊销或过期后 Token 不再生效", async () => {
const t = await tokenService.createToken({ name: "will-revoke", projectIds: ["all"], expiresInDays: 30 });
assert.ok((await tokenService.getActiveTokenValues()).includes(t.value));
await tokenService.revokeToken(t.id);
const revoked = await tokenService.getMcpTokenValues();
assert.ok(!revoked.includes(t.value));
assert.ok(!(await tokenService.getActiveTokenValues()).includes(t.value));
assert.strictEqual(await tokenService.resolveActiveToken(t.value), null);
});
test("migrateLegacyMcpToken 把历史 mcp_kv 配置迁移为 Token 记录", async () => {
// 清理当前 token 表,模拟空 MCP 标记状态
const { loadApiTokens, saveApiTokens, saveMcpConfig, loadMcpConfig } = require("../src/persistence/duckdbAdapter");
await saveApiTokens([]);
const existing = await loadApiTokens();
await saveMcpConfig({ enabled: true, token: "legacy-mcp-token-abc" });
await tokenService.migrateLegacyMcpToken();
const tokens = await loadApiTokens();
const migrated = tokens.find((t) => t.mcpEnabled);
assert.ok(migrated, "应生成一条 mcpEnabled 的 Token");
assert.strictEqual(migrated.value, "legacy-mcp-token-abc");
const values = await tokenService.getMcpTokenValues();
assert.ok(values.includes("legacy-mcp-token-abc"));
const migrated = tokens.find((t) => t.value === "legacy-mcp-token-abc");
assert.ok(migrated, "应生成一条包含旧 token 的记录");
assert.strictEqual(migrated.projectIds[0], "all");
assert.ok((await tokenService.getActiveTokenValues()).includes("legacy-mcp-token-abc"));
// 旧配置已清空
const mcp = await loadMcpConfig();
assert.strictEqual(mcp.token, "");
// 清掉迁移产生的记录,避免影响后续测试
await saveApiTokens(existing);
});