72 lines
2.3 KiB
JavaScript
72 lines
2.3 KiB
JavaScript
/**
|
|
* Robustness/input-hygiene: non-string message bodies must not crash the
|
|
* signed-in OR anonymous chat route or leak stack traces. The validator
|
|
* previously called text.trim() without a typeof check, so `{"text":123}` etc.
|
|
* threw synchronously outside the try/catch → an unhandled 500.
|
|
*/
|
|
import test, { before, after } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import express from "express";
|
|
import { createChatRouter } from "../src/routes/chat.js";
|
|
import { optionalAuth } from "../src/middleware/supabaseAuth.js";
|
|
|
|
let app;
|
|
let server;
|
|
let base;
|
|
const streamCalls = [];
|
|
async function fakeStream() {
|
|
streamCalls.push(1);
|
|
return "ok";
|
|
}
|
|
|
|
before(async () => {
|
|
app = express();
|
|
app.use(express.json());
|
|
app.use("/api/chat", optionalAuth, createChatRouter({ streamFn: fakeStream }));
|
|
server = app.listen(0, "127.0.0.1");
|
|
await new Promise((r) => server.once("listening", r));
|
|
base = `http://127.0.0.1:${server.address().port}`;
|
|
});
|
|
after(async () => {
|
|
if (server) await new Promise((r) => server.close(r));
|
|
});
|
|
|
|
const badTexts = [
|
|
123,
|
|
["hello"],
|
|
{},
|
|
true,
|
|
{ toString: () => "x" },
|
|
[],
|
|
];
|
|
|
|
test("non-string `text` values are rejected cleanly (400), never a 500 crash", async () => {
|
|
for (const bad of badTexts) {
|
|
const res = await fetch(`${base}/api/chat`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ text: bad }),
|
|
});
|
|
const body = await res.text();
|
|
assert.equal(res.status, 400, `text=${JSON.stringify(bad)} -> 400, got ${res.status} ${body}`);
|
|
assert.ok(!body.includes("at "), "no stack-trace leak in the response");
|
|
}
|
|
assert.equal(streamCalls.length, 0, "no AI call was made for any rejected body");
|
|
});
|
|
|
|
test("non-string grade/subject are rejected cleanly, valid text still works", async () => {
|
|
const badMeta = await fetch(`${base}/api/chat`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ text: "hello", grade: 5, subject: ["math"] }),
|
|
});
|
|
assert.equal(badMeta.status, 400, "invalid grade/subject types -> 400");
|
|
|
|
const ok = await fetch(`${base}/api/chat`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ text: "valid question" }),
|
|
});
|
|
assert.equal(ok.status, 200, "valid text reaches the stream");
|
|
});
|