226 lines
7.5 KiB
TypeScript
226 lines
7.5 KiB
TypeScript
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";
|
|
import { NextRequest } from "next/server";
|
|
|
|
let runtimeRoot = "";
|
|
let getRoute: (typeof import("../src/app/api/v1/integrations/telegram/route"))["GET"];
|
|
let postRoute: (typeof import("../src/app/api/v1/integrations/telegram/route"))["POST"];
|
|
let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"];
|
|
let AUTH_SESSION_COOKIE = "";
|
|
|
|
async function setup() {
|
|
if (runtimeRoot) return;
|
|
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-telegram-route-"));
|
|
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
|
|
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
|
|
|
|
const [routeModule, data, auth] = await Promise.all([
|
|
import("../src/app/api/v1/integrations/telegram/route.ts"),
|
|
import("../src/lib/boss-data.ts"),
|
|
import("../src/lib/boss-auth.ts"),
|
|
]);
|
|
getRoute = routeModule.GET;
|
|
postRoute = routeModule.POST;
|
|
createAuthSession = data.createAuthSession;
|
|
AUTH_SESSION_COOKIE = auth.AUTH_SESSION_COOKIE;
|
|
}
|
|
|
|
test.after(async () => {
|
|
if (runtimeRoot) {
|
|
await rm(runtimeRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
async function createAuthedRequest(url: string, method: "GET" | "POST", body?: unknown) {
|
|
const session = await createAuthSession({
|
|
account: "krisolo",
|
|
role: "highest_admin",
|
|
displayName: "Boss 超级管理员",
|
|
loginMethod: "password",
|
|
});
|
|
return new NextRequest(url, {
|
|
method,
|
|
headers: {
|
|
cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`,
|
|
...(body ? { "content-type": "application/json" } : {}),
|
|
},
|
|
body: body ? JSON.stringify(body) : undefined,
|
|
});
|
|
}
|
|
|
|
test("Telegram 配置接口返回脱敏配置视图", async () => {
|
|
await setup();
|
|
|
|
const saveResponse = await postRoute(
|
|
await createAuthedRequest("http://127.0.0.1:3000/api/v1/integrations/telegram", "POST", {
|
|
enabled: true,
|
|
botToken: "123:abc",
|
|
allowFrom: ["123456"],
|
|
webhookSecret: "secret-demo",
|
|
}),
|
|
);
|
|
assert.equal(saveResponse.status, 200);
|
|
|
|
const getResponse = await getRoute(
|
|
await createAuthedRequest("http://127.0.0.1:3000/api/v1/integrations/telegram", "GET"),
|
|
);
|
|
assert.equal(getResponse.status, 200);
|
|
const payload = (await getResponse.json()) as {
|
|
ok: boolean;
|
|
telegram: {
|
|
botTokenConfigured: boolean;
|
|
webhookSecretConfigured: boolean;
|
|
allowFrom: string[];
|
|
};
|
|
};
|
|
assert.equal(payload.ok, true);
|
|
assert.equal(payload.telegram.botTokenConfigured, true);
|
|
assert.equal(payload.telegram.webhookSecretConfigured, true);
|
|
assert.deepEqual(payload.telegram.allowFrom, ["123456"]);
|
|
assert.equal("botToken" in payload.telegram, false);
|
|
});
|
|
|
|
test("Telegram 配置保存到 webhook 模式时会自动调用 setWebhook", async () => {
|
|
await setup();
|
|
const originalFetch = globalThis.fetch;
|
|
const calls: Array<{ url: string; body?: unknown }> = [];
|
|
|
|
globalThis.fetch = (async (input, init) => {
|
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
calls.push({
|
|
url,
|
|
body: init?.body ? JSON.parse(String(init.body)) : undefined,
|
|
});
|
|
if (url === "https://api.telegram.org/bot123:abc/setWebhook") {
|
|
return new Response(JSON.stringify({ ok: true, result: true }), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
}
|
|
throw new Error(`unexpected fetch: ${url}`);
|
|
}) as typeof fetch;
|
|
|
|
try {
|
|
const response = await postRoute(
|
|
await createAuthedRequest("http://127.0.0.1:3000/api/v1/integrations/telegram", "POST", {
|
|
enabled: true,
|
|
mode: "webhook",
|
|
botToken: "123:abc",
|
|
webhookSecret: "boss-telegram-secret",
|
|
webhookUrl: "https://boss.hyzq.net/api/v1/integrations/telegram/webhook",
|
|
}),
|
|
);
|
|
assert.equal(response.status, 200);
|
|
const payload = (await response.json()) as {
|
|
ok: boolean;
|
|
webhookSync?: { ok: boolean; action: string };
|
|
};
|
|
assert.equal(payload.ok, true);
|
|
assert.equal(payload.webhookSync?.ok, true);
|
|
assert.equal(payload.webhookSync?.action, "set_webhook");
|
|
assert.equal(calls.length, 1);
|
|
assert.equal(calls[0]?.url, "https://api.telegram.org/bot123:abc/setWebhook");
|
|
assert.deepEqual(calls[0]?.body, {
|
|
url: "https://boss.hyzq.net/api/v1/integrations/telegram/webhook",
|
|
secret_token: "boss-telegram-secret",
|
|
drop_pending_updates: false,
|
|
});
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("Telegram 配置切回 polling 时会自动调用 deleteWebhook", async () => {
|
|
await setup();
|
|
const originalFetch = globalThis.fetch;
|
|
const calls: string[] = [];
|
|
|
|
globalThis.fetch = (async (input) => {
|
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
calls.push(url);
|
|
if (url === "https://api.telegram.org/bot123:abc/deleteWebhook") {
|
|
return new Response(JSON.stringify({ ok: true, result: true }), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
}
|
|
throw new Error(`unexpected fetch: ${url}`);
|
|
}) as typeof fetch;
|
|
|
|
try {
|
|
const response = await postRoute(
|
|
await createAuthedRequest("http://127.0.0.1:3000/api/v1/integrations/telegram", "POST", {
|
|
enabled: true,
|
|
mode: "polling",
|
|
botToken: "123:abc",
|
|
}),
|
|
);
|
|
assert.equal(response.status, 200);
|
|
const payload = (await response.json()) as {
|
|
ok: boolean;
|
|
webhookSync?: { ok: boolean; action: string };
|
|
};
|
|
assert.equal(payload.ok, true);
|
|
assert.equal(payload.webhookSync?.ok, true);
|
|
assert.equal(payload.webhookSync?.action, "delete_webhook");
|
|
assert.deepEqual(calls, ["https://api.telegram.org/bot123:abc/deleteWebhook"]);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("Telegram 配置接口可以保存群聊到项目的路由表", async () => {
|
|
await setup();
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
globalThis.fetch = (async (input) => {
|
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
if (url === "https://api.telegram.org/bot123:abc/deleteWebhook") {
|
|
return new Response(JSON.stringify({ ok: true, result: true }), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
}
|
|
throw new Error(`unexpected fetch: ${url}`);
|
|
}) as typeof fetch;
|
|
|
|
try {
|
|
const response = await postRoute(
|
|
await createAuthedRequest("http://127.0.0.1:3000/api/v1/integrations/telegram", "POST", {
|
|
enabled: true,
|
|
groupProjectRoutes: [
|
|
{
|
|
chatId: "-100200300",
|
|
projectId: "audit-collab",
|
|
label: "审计群",
|
|
},
|
|
{
|
|
chatId: "-100200300",
|
|
threadId: 12,
|
|
projectId: "master-agent",
|
|
label: "主控 Topic",
|
|
},
|
|
],
|
|
}),
|
|
);
|
|
|
|
assert.equal(response.status, 200);
|
|
const payload = (await response.json()) as {
|
|
ok: boolean;
|
|
telegram: {
|
|
groupProjectRoutes: Array<{ chatId: string; threadId?: number; projectId: string; label?: string }>;
|
|
};
|
|
};
|
|
assert.equal(payload.ok, true);
|
|
assert.deepEqual(payload.telegram.groupProjectRoutes, [
|
|
{ chatId: "-100200300", projectId: "audit-collab", label: "审计群" },
|
|
{ chatId: "-100200300", threadId: 12, projectId: "master-agent", label: "主控 Topic" },
|
|
]);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|