

















@@ -42,6 +42,24 @@ function responseFromText(text: string): Response {
4242);
4343}
444445+function responseFromReaderText(text: string, releaseLock: () => void): Response {
46+const chunks: Array<ReadableStreamReadResult<Uint8Array>> = [
47+{ done: false, value: new TextEncoder().encode(text) },
48+{ done: true, value: undefined },
49+];
50+const reader = {
51+read: async () => chunks.shift() ?? { done: true, value: undefined },
52+cancel: async () => undefined,
53+ releaseLock,
54+} as ReadableStreamDefaultReader<Uint8Array>;
55+56+return {
57+ok: true,
58+status: 200,
59+body: { getReader: () => reader },
60+} as Response;
61+}
62+4563describe("streamProxy", () => {
4664afterEach(() => {
4765vi.unstubAllGlobals();
@@ -119,6 +137,37 @@ describe("streamProxy", () => {
119137});
120138});
121139140+it("releases the proxy response reader after a terminal stream", async () => {
141+let resolveReleased: (() => void) | undefined;
142+const released = new Promise<void>((resolve) => {
143+resolveReleased = resolve;
144+});
145+const releaseLock = vi.fn(() => {
146+resolveReleased?.();
147+});
148+vi.stubGlobal(
149+"fetch",
150+vi.fn(async () =>
151+responseFromReaderText(
152+`data: ${JSON.stringify({
153+ type: "done",
154+ reason: "stop",
155+ usage,
156+ })}\n\n`,
157+releaseLock,
158+),
159+),
160+);
161+162+await streamProxy(model, context, {
163+authToken: "token",
164+proxyUrl: "https://proxy.example",
165+}).result();
166+await released;
167+168+expect(releaseLock).toHaveBeenCalledTimes(1);
169+});
170+122171it("returns an error result when EOF arrives without a terminal event", async () => {
123172vi.stubGlobal(
124173"fetch",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。