Rocky_Mountain_Vending/app/api/chat/route.test.ts

181 lines
4.7 KiB
TypeScript

import assert from "node:assert/strict"
import test from "node:test"
import { NextRequest } from "next/server"
import { POST } from "@/app/api/chat/route"
type CapturedPayload = {
model: string
messages: Array<{ role: string; content: string }>
}
const ORIGINAL_FETCH = globalThis.fetch
const ORIGINAL_XAI_KEY = process.env.XAI_API_KEY
function buildVisitor(intent: string) {
return {
name: "Taylor",
phone: "(801) 555-1000",
email: "taylor@example.com",
intent,
serviceTextConsent: true,
marketingTextConsent: false,
consentVersion: "sms-consent-v1-2026-03-26",
consentCapturedAt: "2026-03-25T00:00:00.000Z",
consentSourcePage: "/contact-us",
}
}
function buildRequest(message: string, intent = "Manuals") {
return new NextRequest("http://localhost/api/chat", {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
pathname: "/manuals",
sessionId: "test-session",
visitor: buildVisitor(intent),
messages: [{ role: "user", content: message }],
}),
})
}
async function runChatRouteWithSpy(
message: string,
intent = "Manuals"
): Promise<{ response: Response; payload: CapturedPayload }> {
process.env.XAI_API_KEY = "test-xai-key"
let capturedPayload: CapturedPayload | null = null
globalThis.fetch = (async (_input: RequestInfo | URL, init?: RequestInit) => {
capturedPayload = JSON.parse(String(init?.body || "{}")) as CapturedPayload
return new Response(
JSON.stringify({
choices: [
{
message: {
content: "Mock Jessica reply.",
},
},
],
}),
{
status: 200,
headers: {
"content-type": "application/json",
},
}
)
}) as typeof fetch
const response = await POST(buildRequest(message, intent))
assert.ok(capturedPayload)
return { response, payload: capturedPayload }
}
test.afterEach(() => {
globalThis.fetch = ORIGINAL_FETCH
if (typeof ORIGINAL_XAI_KEY === "string") {
process.env.XAI_API_KEY = ORIGINAL_XAI_KEY
} else {
delete process.env.XAI_API_KEY
}
})
test("chat route includes grounded manual context for RVV alias lookups", async () => {
const { response, payload } = await runChatRouteWithSpy(
"RVV 660 service manual"
)
assert.equal(response.status, 200)
assert.equal(
payload.messages.some(
(message) =>
message.role === "system" &&
message.content.includes("Manual knowledge context:")
),
true
)
assert.equal(
payload.messages.some(
(message) =>
message.role === "system" &&
/Royal Vendors|660/i.test(message.content)
),
true
)
})
test("chat route resolves Narco alias lookups into manual context", async () => {
const { payload } = await runChatRouteWithSpy("Narco bevmax not cooling")
const manualContext = payload.messages.find(
(message) =>
message.role === "system" &&
message.content.includes("Manual knowledge context:")
)
assert.ok(manualContext)
assert.match(manualContext.content, /Dixie-Narco|Narco/i)
})
test("chat route low-confidence manual queries instruct Jessica to ask for brand model or photo", async () => {
const { payload } = await runChatRouteWithSpy(
"manual for flibbertigibbet machine"
)
const manualContext = payload.messages.find(
(message) =>
message.role === "system" &&
message.content.includes("Manual knowledge context:")
)
assert.ok(manualContext)
assert.match(
manualContext.content,
/brand on the front|model sticker|photo\/video/i
)
})
test("chat route risky technical manual queries inject conservative safety context", async () => {
const { payload } = await runChatRouteWithSpy(
"Royal wiring diagram voltage manual",
"Repairs"
)
const systemPrompt = payload.messages[0]?.content || ""
const manualContext = payload.messages.find(
(message) =>
message.role === "system" &&
message.content.includes("Manual knowledge context:")
)
assert.match(
systemPrompt,
/Do not provide step-by-step repair procedures, wiring guidance, voltage guidance/i
)
assert.ok(manualContext)
assert.match(manualContext.content, /technical or risky/i)
})
test("chat route skips manuals retrieval for non-manual conversations", async () => {
const { payload } = await runChatRouteWithSpy(
"Can someone call me back about free placement?",
"Free Placement"
)
const systemMessages = payload.messages.filter(
(message) => message.role === "system"
)
assert.equal(systemMessages.length, 1)
assert.equal(
systemMessages.some((message) =>
message.content.includes("Manual knowledge context:")
),
false
)
})