CoDEVX / app /api /live-integration.test.ts
CodexMacTiger
feat: live package-scoped chat and thinking logs
837e3ac
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import http from "node:http";
import type { AddressInfo } from "node:net";
import seedWorkPackages from "@/agentic_pm_demo_codex_plans/data/work-packages.seed.json";
import { parseCommand } from "@/lib/command-parser";
import { resolveChatRequestContext } from "@/lib/chat-request-context";
import type { WorkPackage } from "@/lib/work-package-types";
let server: http.Server;
let baseUrl = "";
const workPackages = seedWorkPackages as WorkPackage[];
function buildFakeLlmResponse(joinedMessages: string) {
if (joinedMessages.includes("Connection test.")) {
return "Connected to the configured model.";
}
if (joinedMessages.includes('"mode": "ask"')) {
return "Verification method explains how the requirement will be checked, such as by test, inspection, or analysis.";
}
if (joinedMessages.includes('"mode": "plan"')) {
return "Please break this package into review tasks.";
}
if (joinedMessages.includes('"mode": "change"')) {
return "Please update the objective to include cybersecurity acceptance criteria.";
}
return JSON.stringify({
choices: [
{
message: {
content: JSON.stringify({
assistantMessage: "Fallback JSON response.",
boardAction: { type: "none", workPackageId: null },
}),
},
},
],
});
}
beforeAll(async () => {
server = http.createServer((req, res) => {
let body = "";
req.on("data", (chunk) => {
body += chunk;
});
req.on("end", () => {
let payload: { messages?: Array<{ content?: string }> } = {};
try {
payload = JSON.parse(body || "{}");
} catch {
payload = {};
}
const joinedMessages = (payload.messages ?? [])
.map((message) => String(message.content ?? ""))
.join("\n\n");
res.writeHead(200, { "content-type": "text/plain" });
res.end(buildFakeLlmResponse(joinedMessages));
});
});
await new Promise<void>((resolve) => {
server.listen(0, "127.0.0.1", () => {
const address = server.address() as AddressInfo;
baseUrl = `http://127.0.0.1:${address.port}/v1`;
resolve();
});
});
});
afterAll(async () => {
await new Promise<void>((resolve, reject) => {
server.close((error) => {
if (error) {
reject(error);
return;
}
resolve();
});
});
});
describe("live model integration routes", () => {
it("verifies model settings through the connection route", async () => {
const { POST } = await import("./test-connection/route");
const response = await POST(
new Request("http://localhost/api/test-connection", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
llmConfig: {
apiKey: "live-key",
baseUrl,
model: "fake-model",
},
}),
}),
);
const payload = (await response.json()) as {
ok?: boolean;
status?: string;
message?: string;
};
expect(payload.ok).toBe(true);
expect(payload.status).toBe("connected");
expect(payload.message).toBe("Connected to the configured model.");
});
it("returns a human ask response without board changes when the live model replies in plain text", async () => {
const { POST } = await import("./chat/route");
const response = await POST(
new Request("http://localhost/api/chat", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
messages: [
{ role: "user", content: "@SRS ask What does verification method mean?" },
],
workPackages: seedWorkPackages,
selectedWorkPackageId: "wp-srs",
parsedCommand: {
referencedPackageName: "System Requirements Specification",
mode: "ask",
instruction: "What does verification method mean?",
},
llmConfig: {
apiKey: "live-key",
baseUrl,
model: "fake-model",
},
}),
}),
);
const payload = (await response.json()) as {
assistantMessage: string;
boardAction?: { type?: string; workPackageId?: string | null };
};
expect(payload.assistantMessage).toContain("Verification method explains");
expect(payload.assistantMessage).not.toContain("Network or parsing error");
expect(payload.assistantMessage.trim().startsWith("{")).toBe(false);
expect(payload.boardAction?.type).toBe("none");
expect(payload.boardAction?.workPackageId).toBeNull();
});
it("falls back to a package-scoped plan update when the live model replies in plain text", async () => {
const { POST } = await import("./chat/route");
const response = await POST(
new Request("http://localhost/api/chat", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
messages: [
{ role: "user", content: "@SRS plan Break this into review tasks." },
],
workPackages: seedWorkPackages,
selectedWorkPackageId: "wp-srs",
parsedCommand: {
referencedPackageName: "System Requirements Specification",
mode: "plan",
instruction: "Break this into review tasks.",
},
llmConfig: {
apiKey: "live-key",
baseUrl,
model: "fake-model",
},
}),
}),
);
const payload = (await response.json()) as {
assistantMessage: string;
boardAction?: {
type?: string;
workPackageId?: string | null;
fields?: { tasks?: unknown[]; status?: string };
};
};
expect(payload.assistantMessage).toContain("Planned next steps for SRS");
expect(payload.boardAction?.type).toBe("update");
expect(payload.boardAction?.workPackageId).toBe("wp-srs");
expect(payload.boardAction?.fields?.tasks?.length).toBeGreaterThan(0);
expect(payload.boardAction?.fields?.status).toBe("in_progress");
});
it("falls back to a package-scoped change update when the live model replies in plain text", async () => {
const { POST } = await import("./chat/route");
const response = await POST(
new Request("http://localhost/api/chat", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
messages: [
{
role: "user",
content: "@SRS change Add cybersecurity acceptance criteria.",
},
],
workPackages: seedWorkPackages,
selectedWorkPackageId: "wp-srs",
parsedCommand: {
referencedPackageName: "System Requirements Specification",
mode: "change",
instruction: "Add cybersecurity acceptance criteria.",
},
llmConfig: {
apiKey: "live-key",
baseUrl,
model: "fake-model",
},
}),
}),
);
const payload = (await response.json()) as {
assistantMessage: string;
boardAction?: {
type?: string;
workPackageId?: string | null;
fields?: { objective?: string; status?: string };
};
};
expect(payload.assistantMessage).toContain("Updated SRS");
expect(payload.boardAction?.type).toBe("update");
expect(payload.boardAction?.workPackageId).toBe("wp-srs");
expect(payload.boardAction?.fields?.objective).toContain(
"Add cybersecurity acceptance criteria",
);
expect(payload.boardAction?.fields?.status).toBe("in_progress");
});
it("handles slash plan commands through the same selected-package routing chain used by the client", async () => {
const parsed = parseCommand("/plan Break this into review tasks.", workPackages);
const selectedWorkPackage = workPackages.find((workPackage) => workPackage.id === "wp-srs");
const resolved = resolveChatRequestContext({
parsed: parsed.parsed,
detailOpen: false,
selectedWorkPackage,
});
expect(resolved.parsedCommand.mode).toBe("plan");
expect(resolved.parsedCommand.referencedPackageName).toBe(
selectedWorkPackage?.title,
);
const { POST } = await import("./chat/route");
const response = await POST(
new Request("http://localhost/api/chat", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
messages: [
{ role: "user", content: "/plan Break this into review tasks." },
],
workPackages: seedWorkPackages,
selectedWorkPackageId: "wp-srs",
parsedCommand: resolved.parsedCommand,
llmConfig: {
apiKey: "live-key",
baseUrl,
model: "fake-model",
},
}),
}),
);
const payload = (await response.json()) as {
assistantMessage: string;
boardAction?: {
type?: string;
workPackageId?: string | null;
fields?: { tasks?: unknown[]; status?: string };
};
};
expect(payload.assistantMessage).toContain("Planned next steps for SRS");
expect(payload.boardAction?.type).toBe("update");
expect(payload.boardAction?.workPackageId).toBe("wp-srs");
expect(payload.boardAction?.fields?.tasks?.length).toBeGreaterThan(0);
expect(payload.boardAction?.fields?.status).toBe("in_progress");
});
});