Fix admin authorization and harden chat requests
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {ApiError, get, post, stream} from "../public/js/api.js";
|
||||
|
||||
test("JSON requests retain HTTP metadata, handle empty success, and distinguish bad bodies from network errors", async t => {
|
||||
let response;
|
||||
t.mock.method(globalThis, "fetch", async () => response);
|
||||
for (const status of [204, 205, 200]) {
|
||||
response = new Response(null, {status});
|
||||
assert.deepEqual(await get("/empty"), {});
|
||||
}
|
||||
response = new Response("<html>Proxy unavailable</html>", {status: 503, statusText: "Service Unavailable", headers: {"X-Request-ID": "proxy-123"}});
|
||||
assert.deepEqual(await get("/proxy"), {error: "HTTP 503 Service Unavailable", status: 503, code: "HTTP_503", requestId: "proxy-123"});
|
||||
response = Response.json({error: "Already exists", code: "USERNAME_TAKEN", status: 400, requestId: "body-123", retryAfter: 5}, {status: 409});
|
||||
assert.deepEqual(await post("/register", {}), {error: "Already exists", code: "USERNAME_TAKEN", status: 409, requestId: "body-123", retryAfter: 5});
|
||||
response = Response.json({error: {message: "Forbidden", code: "DENIED"}}, {status: 403});
|
||||
assert.equal((await get("/private")).code, "DENIED");
|
||||
response = new Response("not json", {headers: {"X-Request-ID": "invalid-body"}});
|
||||
assert.deepEqual(await get("/invalid"), {error: "Invalid JSON response.", status: 200, code: "INVALID_RESPONSE", requestId: "invalid-body"});
|
||||
t.mock.method(globalThis, "fetch", async () => { throw new TypeError("Failed to fetch"); });
|
||||
assert.equal((await get("/offline")).code, "NETWORK_ERROR");
|
||||
});
|
||||
|
||||
test("JSON requests support caller abort and deadlines across headers and body reads", async t => {
|
||||
let signal;
|
||||
t.mock.method(globalThis, "fetch", async (_url, options) => {
|
||||
signal = options.signal;
|
||||
return new Promise(() => {});
|
||||
});
|
||||
const controller = new AbortController();
|
||||
const cancelled = get("/cancel", {signal: controller.signal});
|
||||
controller.abort();
|
||||
assert.equal((await cancelled).code, "ABORTED");
|
||||
assert.equal((await get("/timeout", {timeoutMs: 10})).code, "TIMEOUT");
|
||||
assert.equal(signal.aborted, true);
|
||||
t.mock.method(globalThis, "fetch", async () => new Response(new ReadableStream(), {headers: {"X-Request-ID": "slow-body"}}));
|
||||
const bodyTimeout = await get("/slow-body", {timeoutMs: 10});
|
||||
assert.equal(bodyTimeout.code, "TIMEOUT");
|
||||
assert.equal(bodyTimeout.status, 200);
|
||||
assert.equal(bodyTimeout.requestId, "slow-body");
|
||||
});
|
||||
|
||||
test("SSE errors reject by default and deliver complete metadata to an explicit handler", async t => {
|
||||
const payload = {error: "Model unavailable", status: 503, code: "MODEL_UNAVAILABLE", requestId: "model-123"};
|
||||
t.mock.method(globalThis, "fetch", async () => new Response(`event: error\ndata: ${JSON.stringify(payload)}\n\n`));
|
||||
await assert.rejects(stream("/stream", {}), error => error instanceof ApiError && error.status === 503 && error.code === payload.code && error.requestId === payload.requestId);
|
||||
let handled;
|
||||
await stream("/stream", {}, {onError: error => { handled = error; }});
|
||||
assert.deepEqual(handled, payload);
|
||||
t.mock.method(globalThis, "fetch", async () => new Response("upstream unavailable", {status: 503, headers: {"X-Request-ID": "proxy-123"}}));
|
||||
await assert.rejects(stream("/stream", {}), error => error.status === 503 && error.code === "HTTP_503" && error.requestId === "proxy-123");
|
||||
});
|
||||
|
||||
test("SSE supports fragmented UTF-8, multiline data, replacements, cancellation and stalled reads", async t => {
|
||||
const received = [];
|
||||
const wire = 'event: chunk\r\ndata: {"chunk":"Hello 🌳"}\r\n\r\nevent: replace\ndata: {"target":"reply",\ndata: "text":"Corrected"}\n\nevent: done\ndata: {"reply":"Corrected"}\n\n';
|
||||
t.mock.method(globalThis, "fetch", async () => new Response(new ReadableStream({start(controller) {
|
||||
for (const byte of new TextEncoder().encode(wire)) controller.enqueue(Uint8Array.of(byte));
|
||||
controller.close();
|
||||
}})));
|
||||
await stream("/stream", {}, {onChunk: data => received.push(data.chunk), onReplace: data => received.push(data.text), onDone: data => received.push(data.reply)});
|
||||
assert.deepEqual(received, ["Hello 🌳", "Corrected", "Corrected"]);
|
||||
let cancelled = 0;
|
||||
t.mock.method(globalThis, "fetch", async () => new Response(new ReadableStream({cancel() { cancelled++; }}), {headers: {"X-Request-ID": "stream-123"}}));
|
||||
await assert.rejects(stream("/stream", {}, {}, {timeoutMs: 10}), error => error.code === "TIMEOUT" && error.requestId === "stream-123");
|
||||
assert.equal(cancelled, 1);
|
||||
const controller = new AbortController();
|
||||
const pending = stream("/stream", {}, {}, controller.signal);
|
||||
controller.abort();
|
||||
await assert.rejects(pending, error => error.code === "ABORTED");
|
||||
});
|
||||
Reference in New Issue
Block a user