import test from "node:test"; import assert from "node:assert/strict"; import os from "node:os"; import path from "node:path"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { NextRequest } from "next/server"; let runtimeRoot = ""; let AUTH_SESSION_COOKIE = ""; let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"]; let readState: (typeof import("../src/lib/boss-data"))["readState"]; let orchestrationBackendRoute: typeof import("../src/app/api/v1/projects/[projectId]/orchestration-backend/route"); async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-orchestration-backend-route-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [data, auth, routeModule] = await Promise.all([ import("../src/lib/boss-data.ts"), import("../src/lib/boss-auth.ts"), import("../src/app/api/v1/projects/[projectId]/orchestration-backend/route.ts"), ]); createAuthSession = data.createAuthSession; readState = data.readState; AUTH_SESSION_COOKIE = auth.AUTH_SESSION_COOKIE; orchestrationBackendRoute = routeModule; } async function createAuthedHeaders() { await setup(); const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); return { cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, "content-type": "application/json", }; } function snapshotOmxEnv() { return { BOSS_OMX_ENABLED: process.env.BOSS_OMX_ENABLED, BOSS_OMX_COMMAND: process.env.BOSS_OMX_COMMAND, BOSS_OMX_ARGS: process.env.BOSS_OMX_ARGS, BOSS_OMX_WORKDIR: process.env.BOSS_OMX_WORKDIR, }; } function restoreOmxEnv(snapshot: ReturnType) { for (const [key, value] of Object.entries(snapshot)) { if (value === undefined) { delete process.env[key]; } else { process.env[key] = value; } } } test.after(async () => { if (runtimeRoot) { await rm(runtimeRoot, { recursive: true, force: true }); } }); test("GET /api/v1/projects/[projectId]/orchestration-backend returns current choice and OMX availability", async () => { await setup(); const tempDir = await mkdtemp(path.join(os.tmpdir(), "boss-orchestration-backend-omx-")); const scriptPath = path.join(tempDir, "omx-team-smoke.mjs"); await writeFile(scriptPath, "console.log('ok');\n", "utf8"); const previousEnv = snapshotOmxEnv(); process.env.BOSS_OMX_ENABLED = "true"; process.env.BOSS_OMX_COMMAND = process.execPath; process.env.BOSS_OMX_ARGS = scriptPath; process.env.BOSS_OMX_WORKDIR = tempDir; try { const response = await orchestrationBackendRoute.GET( new NextRequest("http://127.0.0.1:3000/api/v1/projects/audit-collab/orchestration-backend", { method: "GET", headers: await createAuthedHeaders(), }), { params: Promise.resolve({ projectId: "audit-collab" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; projectId: string; currentBackendId: string; requestedBackendId: string | null; availableChoices: Array<{ backendId: string; selectable: boolean; current: boolean }>; omxAvailability: { selectable: boolean; reason: string }; }; assert.equal(payload.ok, true); assert.equal(payload.projectId, "audit-collab"); assert.equal(payload.currentBackendId, "boss-native-orchestrator"); assert.equal(payload.requestedBackendId, null); assert.deepEqual( payload.availableChoices.map((choice) => choice.backendId), ["boss-native-orchestrator", "omx-team"], ); assert.equal(payload.availableChoices[0]?.current, true); assert.equal(payload.availableChoices[1]?.selectable, true); assert.equal(payload.omxAvailability.selectable, true); assert.equal(payload.omxAvailability.reason, "ready"); } finally { restoreOmxEnv(previousEnv); await rm(tempDir, { recursive: true, force: true }); } }); test("PATCH /api/v1/projects/[projectId]/orchestration-backend persists OMX selection when selectable", async () => { await setup(); const tempDir = await mkdtemp(path.join(os.tmpdir(), "boss-orchestration-backend-omx-")); const scriptPath = path.join(tempDir, "omx-team-smoke.mjs"); await writeFile(scriptPath, "console.log('ok');\n", "utf8"); const previousEnv = snapshotOmxEnv(); process.env.BOSS_OMX_ENABLED = "true"; process.env.BOSS_OMX_COMMAND = process.execPath; process.env.BOSS_OMX_ARGS = scriptPath; process.env.BOSS_OMX_WORKDIR = tempDir; try { const response = await orchestrationBackendRoute.PATCH( new NextRequest("http://127.0.0.1:3000/api/v1/projects/audit-collab/orchestration-backend", { method: "PATCH", headers: await createAuthedHeaders(), body: JSON.stringify({ orchestrationBackendOverride: "omx-team" }), }), { params: Promise.resolve({ projectId: "audit-collab" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; currentBackendId: string; requestedBackendId: string; omxAvailability: { selectable: boolean }; }; assert.equal(payload.ok, true); assert.equal(payload.currentBackendId, "omx-team"); assert.equal(payload.requestedBackendId, "omx-team"); assert.equal(payload.omxAvailability.selectable, true); const state = await readState(); const project = state.projects.find((item) => item.id === "audit-collab"); assert.equal(project?.orchestrationBackendOverride, "omx-team"); } finally { restoreOmxEnv(previousEnv); await rm(tempDir, { recursive: true, force: true }); } }); test("PATCH /api/v1/projects/[projectId]/orchestration-backend falls back to native when OMX is unavailable", async () => { await setup(); const previousEnv = snapshotOmxEnv(); delete process.env.BOSS_OMX_ENABLED; delete process.env.BOSS_OMX_COMMAND; delete process.env.BOSS_OMX_ARGS; delete process.env.BOSS_OMX_WORKDIR; try { const response = await orchestrationBackendRoute.PATCH( new NextRequest("http://127.0.0.1:3000/api/v1/projects/audit-collab/orchestration-backend", { method: "PATCH", headers: await createAuthedHeaders(), body: JSON.stringify({ orchestrationBackendOverride: "omx-team" }), }), { params: Promise.resolve({ projectId: "audit-collab" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; currentBackendId: string; requestedBackendId: string; omxAvailability: { selectable: boolean; reasonLabel: string }; }; assert.equal(payload.ok, true); assert.equal(payload.requestedBackendId, "omx-team"); assert.equal(payload.currentBackendId, "boss-native-orchestrator"); assert.equal(payload.omxAvailability.selectable, false); assert.equal(payload.omxAvailability.reasonLabel, "OMX Team Runtime 当前未启用。"); const state = await readState(); const project = state.projects.find((item) => item.id === "audit-collab"); assert.equal(project?.orchestrationBackendOverride, "omx-team"); } finally { restoreOmxEnv(previousEnv); } });