





















@@ -59,6 +59,21 @@ describe("lmstudio-models", () => {
5959}
6060return JSON.parse(init.body) as unknown;
6161};
62+// The model fetch/load helpers now read bodies through the shared byte-capped
63+// reader, so success-path mocks must be real Response objects with a body
64+// stream rather than bare `{ ok, json }` placeholders.
65+const jsonResponse = (payload: unknown, init?: ResponseInit): Response =>
66+new Response(JSON.stringify(payload), {
67+status: 200,
68+headers: { "content-type": "application/json" },
69+ ...init,
70+});
71+const malformedJsonResponse = (init?: ResponseInit): Response =>
72+new Response("{ this is not valid json", {
73+status: 200,
74+headers: { "content-type": "application/json" },
75+ ...init,
76+});
6277const cancelTrackedResponse = (
6378text: string,
6479init: ResponseInit,
@@ -89,6 +104,7 @@ describe("lmstudio-models", () => {
89104}) =>
90105vi.fn(async (url: string | URL, _init?: RequestInit) => {
91106const key = params?.key ?? "qwen3-8b-instruct";
107+void init;
92108if (String(url).endsWith("/api/v1/models")) {
93109return jsonResponse({
94110models: [
@@ -582,7 +598,53 @@ describe("lmstudio-models", () => {
582598baseUrl: "http://localhost:1234/v1",
583599modelKey: "qwen3-8b-instruct",
584600}),
585-).rejects.toThrow("LM Studio model load returned malformed JSON");
601+).rejects.toThrow("LM Studio model load: malformed JSON response");
602+});
603+604+it("bounds oversized model load success bodies", async () => {
605+// A misbehaving server may stream an unbounded success JSON body; the load
606+// path must stop reading at the byte cap instead of buffering it all.
607+let canceled = false;
608+let bytesEmitted = 0;
609+const oversizedStream = new ReadableStream<Uint8Array>({
610+pull(controller) {
611+// Far exceeds the 16 MiB provider JSON cap if read to completion.
612+if (bytesEmitted >= 32 * 1024 * 1024) {
613+controller.close();
614+return;
615+}
616+bytesEmitted += 64 * 1024;
617+controller.enqueue(new Uint8Array(64 * 1024).fill(0x61));
618+},
619+cancel() {
620+canceled = true;
621+},
622+});
623+const fetchMock = vi.fn(async (url: string | URL) => {
624+if (String(url).endsWith("/api/v1/models")) {
625+return jsonResponse({
626+models: [{ type: "llm", key: "qwen3-8b-instruct", loaded_instances: [] }],
627+});
628+}
629+if (String(url).endsWith("/api/v1/models/load")) {
630+return new Response(oversizedStream, {
631+status: 200,
632+headers: { "content-type": "application/json" },
633+});
634+}
635+throw new Error(`Unexpected fetch URL: ${String(url)}`);
636+});
637+vi.stubGlobal("fetch", asFetch(fetchMock));
638+639+const error = await ensureLmstudioModelLoaded({
640+baseUrl: "http://localhost:1234/v1",
641+modelKey: "qwen3-8b-instruct",
642+}).catch((caught: unknown) => caught);
643+644+expect(error).toBeInstanceOf(Error);
645+expect((error as Error).message).toMatch(/JSON response exceeds \d+ bytes/);
646+expect(canceled).toBe(true);
647+expect(bytesEmitted).toBeLessThan(32 * 1024 * 1024);
586648});
587649588650it("bounds model load error bodies", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。