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 readState: (typeof import("../src/lib/boss-data"))["readState"]; let writeState: (typeof import("../src/lib/boss-data"))["writeState"]; let deleteProjectMessage: (typeof import("../src/lib/boss-data"))["deleteProjectMessage"]; let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"]; let deleteMessageRoute: (typeof import("../src/app/api/v1/projects/[projectId]/messages/route"))["DELETE"]; let subscribeBossEvents: (typeof import("../src/lib/boss-events"))["subscribeBossEvents"]; let AUTH_SESSION_COOKIE = ""; async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-project-message-delete-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [data, route, auth, events] = await Promise.all([ import("../src/lib/boss-data.ts"), import("../src/app/api/v1/projects/[projectId]/messages/route.ts"), import("../src/lib/boss-auth.ts"), import("../src/lib/boss-events.ts"), ]); readState = data.readState; writeState = data.writeState; deleteProjectMessage = data.deleteProjectMessage; createAuthSession = data.createAuthSession; deleteMessageRoute = route.DELETE; subscribeBossEvents = events.subscribeBossEvents; AUTH_SESSION_COOKIE = auth.AUTH_SESSION_COOKIE; } async function seedProject() { const state = await readState(); const baseProject = state.projects.find((project) => project.id !== "master-agent") ?? state.projects[0]; const project = structuredClone(baseProject); project.id = "message-delete-project"; project.name = "消息删除测试"; project.preview = "第二条回复"; project.lastMessageAt = "2026-04-26T10:05:00.000Z"; project.unreadCount = 2; project.messages = [ { id: "msg-1", sender: "user", senderLabel: "你", body: "第一条请求", sentAt: "2026-04-26T10:00:00.000Z", kind: "text", }, { id: "msg-process", sender: "device", senderLabel: "线程", body: "我先检查代码。", sentAt: "2026-04-26T10:03:00.000Z", kind: "thread_process", }, { id: "msg-2", sender: "master", senderLabel: "主 Agent", body: "第二条回复", sentAt: "2026-04-26T10:05:00.000Z", kind: "text", }, ]; state.projects = state.projects.filter((item) => item.id !== project.id); state.projects.unshift(project); await writeState(state); } test.beforeEach(async () => { await setup(); await seedProject(); }); test.after(async () => { if (runtimeRoot) { await rm(runtimeRoot, { recursive: true, force: true }); } }); test("deleteProjectMessage removes a message and refreshes project preview events", async () => { const events: Array<{ event: string; payload: { projectId?: string } }> = []; const unsubscribe = subscribeBossEvents((event, payload) => { events.push({ event, payload }); }); const result = await deleteProjectMessage({ projectId: "message-delete-project", messageId: "msg-2", }); unsubscribe(); assert.equal(result.deletedMessage.id, "msg-2"); assert.equal(result.projectId, "message-delete-project"); const project = (await readState()).projects.find((item) => item.id === "message-delete-project"); assert.ok(project); assert.deepEqual(project!.messages.map((message) => message.id), ["msg-1", "msg-process"]); assert.equal(project!.preview, "第一条请求"); assert.equal(project!.lastMessageAt, "2026-04-26T10:03:00.000Z"); assert.ok( events.some( (item) => item.event === "project.messages.updated" && item.payload.projectId === "message-delete-project", ), ); assert.ok( events.some( (item) => item.event === "conversation.updated" && item.payload.projectId === "message-delete-project", ), ); }); test("DELETE /api/v1/projects/[projectId]/messages deletes the requested message", async () => { const session = await createAuthSession({ account: "krisolo", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const request = new NextRequest( "http://127.0.0.1:3000/api/v1/projects/message-delete-project/messages?messageId=msg-2", { method: "DELETE", headers: { cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }, }, ); const response = await deleteMessageRoute(request, { params: Promise.resolve({ projectId: "message-delete-project" }), }); const payload = (await response.json()) as { ok: boolean; deletedMessage?: { id: string }; }; assert.equal(response.status, 200); assert.equal(payload.ok, true); assert.equal(payload.deletedMessage?.id, "msg-2"); });