feat: complete chat routing and openai onboarding
This commit is contained in:
102
tests/master-agent-openai-fallback.test.ts
Normal file
102
tests/master-agent-openai-fallback.test.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
|
||||
let runtimeRoot = "";
|
||||
let replyToMasterAgentUserMessage: (typeof import("../src/lib/boss-master-agent"))["replyToMasterAgentUserMessage"];
|
||||
let saveAiAccount: (typeof import("../src/lib/boss-data"))["saveAiAccount"];
|
||||
let readState: (typeof import("../src/lib/boss-data"))["readState"];
|
||||
|
||||
async function setup() {
|
||||
if (runtimeRoot) return;
|
||||
|
||||
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-fallback-"));
|
||||
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
|
||||
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
|
||||
|
||||
const [masterAgent, data] = await Promise.all([
|
||||
import("../src/lib/boss-master-agent.ts"),
|
||||
import("../src/lib/boss-data.ts"),
|
||||
]);
|
||||
|
||||
replyToMasterAgentUserMessage = masterAgent.replyToMasterAgentUserMessage;
|
||||
saveAiAccount = data.saveAiAccount;
|
||||
readState = data.readState;
|
||||
}
|
||||
|
||||
test.after(async () => {
|
||||
if (runtimeRoot) {
|
||||
await rm(runtimeRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("replyToMasterAgentUserMessage falls back to a runnable OpenAI API account when the master node is offline", async () => {
|
||||
await setup();
|
||||
|
||||
await saveAiAccount({
|
||||
accountId: "master-codex-primary",
|
||||
label: "主 GPT",
|
||||
role: "primary",
|
||||
provider: "master_codex_node",
|
||||
displayName: "Mac 上的 Master Codex Node",
|
||||
nodeId: "offline-node",
|
||||
nodeLabel: "离线节点",
|
||||
model: "gpt-5.4",
|
||||
enabled: true,
|
||||
setActive: true,
|
||||
loginStatusNote: "通过绑定的 Master Codex Node 对话。",
|
||||
});
|
||||
|
||||
await saveAiAccount({
|
||||
accountId: "openai-backup",
|
||||
label: "备用 GPT",
|
||||
role: "backup",
|
||||
provider: "openai_api",
|
||||
displayName: "OpenAI API 备用账号",
|
||||
accountIdentifier: "sk-demo",
|
||||
model: "gpt-5.4",
|
||||
apiKey: "sk-live-demo-123456",
|
||||
enabled: true,
|
||||
setActive: false,
|
||||
loginStatusNote: "备用 API 账号。",
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = (async (input) => {
|
||||
if (typeof input === "string" && input === "https://api.openai.com/v1/responses") {
|
||||
return new Response(JSON.stringify({ output_text: "主Agent链路正常。" }), {
|
||||
status: 200,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-request-id": "req-master-fallback",
|
||||
},
|
||||
});
|
||||
}
|
||||
throw new Error(`unexpected fetch: ${String(input)}`);
|
||||
}) as typeof fetch;
|
||||
|
||||
try {
|
||||
const result = await replyToMasterAgentUserMessage({
|
||||
requestMessageId: "msg-master-fallback",
|
||||
requestText: "请只回复:主Agent链路正常。",
|
||||
requestedBy: "Boss 超级管理员",
|
||||
requestedByAccount: "17600003315",
|
||||
});
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(result.accountId, "openai-backup");
|
||||
assert.equal(result.requestId, "req-master-fallback");
|
||||
|
||||
const state = await readState();
|
||||
const masterProject = state.projects.find((project) => project.id === "master-agent");
|
||||
const reply = masterProject?.messages.at(-1);
|
||||
assert.ok(reply, "expected a master-agent reply to be appended");
|
||||
assert.equal(reply?.sender, "master");
|
||||
assert.equal(reply?.senderLabel, "主 Agent · 备用 GPT");
|
||||
assert.match(reply?.body ?? "", /主Agent链路正常/);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user