Spaces:
Running
Running
| import { afterEach, describe, expect, it, vi } from "vitest"; | |
| const generateLlmText = vi.fn(); | |
| vi.mock("@/lib/llm-client", () => ({ | |
| generateLlmText, | |
| })); | |
| afterEach(() => { | |
| generateLlmText.mockReset(); | |
| }); | |
| describe("/api/chat route", () => { | |
| it("returns resolved context and thinking summary for mock package asks", async () => { | |
| const { POST } = await import("./route"); | |
| const seed = (await import("@/agentic_pm_demo_codex_plans/data/work-packages.seed.json")) | |
| .default as Array<Record<string, unknown>>; | |
| 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: seed, | |
| selectedWorkPackageId: "wp-srs", | |
| parsedCommand: { | |
| referencedPackageName: "System Requirements Specification", | |
| mode: "ask", | |
| instruction: "What does verification method mean?", | |
| }, | |
| }), | |
| }), | |
| ); | |
| const payload = (await response.json()) as { | |
| boardAction?: { type?: string }; | |
| resolvedContext?: { | |
| scope?: string; | |
| workPackageId?: string | null; | |
| mode?: string; | |
| provider?: string; | |
| boardMutationPolicy?: string; | |
| }; | |
| thinkingSummary?: string[]; | |
| }; | |
| expect(payload.boardAction?.type).toBe("none"); | |
| expect(payload.resolvedContext).toMatchObject({ | |
| scope: "package", | |
| workPackageId: "wp-srs", | |
| mode: "ask", | |
| provider: "mock", | |
| boardMutationPolicy: "none", | |
| }); | |
| expect(payload.thinkingSummary?.join("\n")).toContain("Resolved scope to System Requirements Specification."); | |
| expect(payload.thinkingSummary?.join("\n")).toContain("Using mock mode."); | |
| }); | |
| it("falls back to package-scoped mock ask guidance when the live model call throws", async () => { | |
| generateLlmText.mockRejectedValueOnce(new Error("Invalid JSON")); | |
| const { POST } = await import("./route"); | |
| const seed = (await import("@/agentic_pm_demo_codex_plans/data/work-packages.seed.json")) | |
| .default as Array<Record<string, unknown>>; | |
| 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: seed, | |
| selectedWorkPackageId: "wp-srs", | |
| parsedCommand: { | |
| referencedPackageName: "System Requirements Specification", | |
| mode: "ask", | |
| instruction: "What does verification method mean?", | |
| }, | |
| llmConfig: { | |
| apiKey: "live-key", | |
| baseUrl: "https://api.example.com/v1", | |
| model: "gpt-test", | |
| }, | |
| }), | |
| }), | |
| ); | |
| const payload = (await response.json()) as { | |
| assistantMessage: string; | |
| boardAction?: { type?: string }; | |
| resolvedContext?: { | |
| scope?: string; | |
| workPackageId?: string | null; | |
| mode?: string; | |
| provider?: string; | |
| boardMutationPolicy?: string; | |
| }; | |
| thinkingSummary?: string[]; | |
| }; | |
| expect(payload.assistantMessage).toContain("For SRS, the objective here is:"); | |
| expect(payload.assistantMessage).toContain("verification"); | |
| expect(payload.assistantMessage).not.toContain("Network or parsing error calling LLM"); | |
| expect(payload.boardAction?.type).toBe("none"); | |
| expect(payload.resolvedContext).toMatchObject({ | |
| scope: "package", | |
| workPackageId: "wp-srs", | |
| mode: "ask", | |
| provider: "live", | |
| boardMutationPolicy: "none", | |
| }); | |
| expect(payload.thinkingSummary?.join("\n")).toContain( | |
| "Provider call failed; preserving the package-scoped fallback.", | |
| ); | |
| }); | |
| it("falls back to a scoped mock plan update when the live model call throws", async () => { | |
| generateLlmText.mockRejectedValueOnce(new Error("Invalid JSON")); | |
| const { POST } = await import("./route"); | |
| const seed = (await import("@/agentic_pm_demo_codex_plans/data/work-packages.seed.json")) | |
| .default as Array<Record<string, unknown>>; | |
| 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 steps." }, | |
| ], | |
| workPackages: seed, | |
| selectedWorkPackageId: "wp-srs", | |
| parsedCommand: { | |
| referencedPackageName: "System Requirements Specification", | |
| mode: "plan", | |
| instruction: "Break this into review steps.", | |
| }, | |
| llmConfig: { | |
| apiKey: "live-key", | |
| baseUrl: "https://api.example.com/v1", | |
| model: "gpt-test", | |
| }, | |
| }), | |
| }), | |
| ); | |
| const payload = (await response.json()) as { | |
| assistantMessage: string; | |
| boardAction?: { | |
| type?: string; | |
| workPackageId?: string | null; | |
| fields?: { tasks?: unknown[]; status?: string }; | |
| }; | |
| resolvedContext?: { | |
| scope?: string; | |
| workPackageId?: string | null; | |
| mode?: string; | |
| provider?: string; | |
| boardMutationPolicy?: string; | |
| }; | |
| thinkingSummary?: 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"); | |
| expect(payload.resolvedContext).toMatchObject({ | |
| scope: "package", | |
| workPackageId: "wp-srs", | |
| mode: "plan", | |
| provider: "live", | |
| boardMutationPolicy: "selected_package_only", | |
| }); | |
| expect(payload.thinkingSummary?.join("\n")).toContain( | |
| "Provider call failed; preserving the package-scoped fallback.", | |
| ); | |
| }); | |
| it("neutralizes live package updates that target a different work package", async () => { | |
| generateLlmText.mockResolvedValueOnce({ | |
| text: JSON.stringify({ | |
| assistantMessage: "Changed the wrong package.", | |
| boardAction: { | |
| type: "update", | |
| workPackageId: "wp-final-concept", | |
| fields: { | |
| objective: "This should not land on Final Concept.", | |
| }, | |
| }, | |
| }), | |
| endpoint: "https://api.example.com/v1/chat/completions", | |
| requestPreview: "request", | |
| responsePreview: "response", | |
| }); | |
| const { POST } = await import("./route"); | |
| const seed = (await import("@/agentic_pm_demo_codex_plans/data/work-packages.seed.json")) | |
| .default as Array<Record<string, unknown>>; | |
| 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: seed, | |
| selectedWorkPackageId: "wp-srs", | |
| parsedCommand: { | |
| referencedPackageName: "System Requirements Specification", | |
| mode: "change", | |
| instruction: "Add cybersecurity acceptance criteria.", | |
| }, | |
| llmConfig: { | |
| apiKey: "live-key", | |
| baseUrl: "https://api.example.com/v1", | |
| model: "gpt-test", | |
| }, | |
| }), | |
| }), | |
| ); | |
| const payload = (await response.json()) as { | |
| boardAction?: { type?: string; workPackageId?: string | null }; | |
| thinkingSummary?: string[]; | |
| }; | |
| expect(payload.boardAction).toMatchObject({ | |
| type: "none", | |
| workPackageId: null, | |
| }); | |
| expect(payload.thinkingSummary?.join("\n")).toContain( | |
| "Blocked a board update that targeted a different work package.", | |
| ); | |
| }); | |
| }); | |