Files
KrisHD_AI/test/api.test.js
T
Fretux c48e1b17c4
CI / check (22) (push) Successful in 10m3s
CI / check (24) (push) Successful in 9m34s
Fix admin authorization and harden chat requests
2026-10-05 22:49:29 +02:00

72 lines
4.8 KiB
JavaScript

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");
});