






























@@ -0,0 +1,176 @@
1+import { describe, expect, it, vi } from "vitest";
2+import { createDraftStreamLoop } from "./draft-stream-loop.js";
3+4+const flushMicrotasks = async () => {
5+await Promise.resolve();
6+await Promise.resolve();
7+};
8+9+const flushMacrotask = async () => {
10+await new Promise((resolve) => setTimeout(resolve, 0));
11+};
12+13+async function captureUnhandledRejections(
14+run: (rejections: unknown[]) => Promise<void>,
15+settle: () => Promise<void> = flushMacrotask,
16+) {
17+const rejections: unknown[] = [];
18+const onUnhandledRejection = (reason: unknown) => {
19+rejections.push(reason);
20+};
21+process.on("unhandledRejection", onUnhandledRejection);
22+try {
23+await run(rejections);
24+await settle();
25+} finally {
26+process.off("unhandledRejection", onUnhandledRejection);
27+}
28+}
29+30+describe("createDraftStreamLoop", () => {
31+it("contains immediate background flush rejections and preserves pending text", async () => {
32+await captureUnhandledRejections(async (rejections) => {
33+const error = new Error("send failed");
34+const onBackgroundFlushError = vi.fn<(err: unknown) => void>();
35+const sendOrEditStreamMessage = vi
36+.fn<(text: string) => Promise<boolean>>()
37+.mockRejectedValueOnce(error)
38+.mockResolvedValueOnce(true);
39+40+const loop = createDraftStreamLoop({
41+throttleMs: 0,
42+isStopped: () => false,
43+ sendOrEditStreamMessage,
44+ onBackgroundFlushError,
45+});
46+47+loop.update("hello");
48+await flushMicrotasks();
49+await flushMacrotask();
50+await loop.flush();
51+52+expect(rejections).toStrictEqual([]);
53+expect(onBackgroundFlushError).toHaveBeenCalledWith(error);
54+expect(sendOrEditStreamMessage).toHaveBeenNthCalledWith(1, "hello");
55+expect(sendOrEditStreamMessage).toHaveBeenNthCalledWith(2, "hello");
56+});
57+});
58+59+it("contains scheduled background flush rejections and preserves pending text", async () => {
60+vi.useFakeTimers();
61+try {
62+await captureUnhandledRejections(
63+async (rejections) => {
64+const error = new Error("send failed");
65+const onBackgroundFlushError = vi.fn<(err: unknown) => void>();
66+const sendOrEditStreamMessage = vi
67+.fn<(text: string) => Promise<boolean>>()
68+.mockRejectedValueOnce(error)
69+.mockResolvedValueOnce(true);
70+71+const loop = createDraftStreamLoop({
72+throttleMs: 100,
73+isStopped: () => false,
74+ sendOrEditStreamMessage,
75+ onBackgroundFlushError,
76+});
77+78+loop.update("scheduled");
79+await vi.advanceTimersByTimeAsync(100);
80+await flushMicrotasks();
81+await loop.flush();
82+83+expect(rejections).toStrictEqual([]);
84+expect(onBackgroundFlushError).toHaveBeenCalledWith(error);
85+expect(sendOrEditStreamMessage).toHaveBeenNthCalledWith(1, "scheduled");
86+expect(sendOrEditStreamMessage).toHaveBeenNthCalledWith(2, "scheduled");
87+},
88+async () => {
89+await vi.advanceTimersByTimeAsync(0);
90+},
91+);
92+} finally {
93+vi.useRealTimers();
94+}
95+});
96+97+it("contains synchronous sender failures from background flushes", async () => {
98+await captureUnhandledRejections(async (rejections) => {
99+const error = new Error("send failed");
100+const onBackgroundFlushError = vi.fn<(err: unknown) => void>();
101+const sendOrEditStreamMessage = vi
102+.fn<(text: string) => Promise<boolean>>()
103+.mockImplementationOnce(() => {
104+throw error;
105+})
106+.mockResolvedValueOnce(true);
107+108+const loop = createDraftStreamLoop({
109+throttleMs: 0,
110+isStopped: () => false,
111+ sendOrEditStreamMessage,
112+ onBackgroundFlushError,
113+});
114+115+loop.update("hello");
116+await flushMicrotasks();
117+await flushMacrotask();
118+await loop.flush();
119+120+expect(rejections).toStrictEqual([]);
121+expect(onBackgroundFlushError).toHaveBeenCalledWith(error);
122+expect(sendOrEditStreamMessage).toHaveBeenNthCalledWith(1, "hello");
123+expect(sendOrEditStreamMessage).toHaveBeenNthCalledWith(2, "hello");
124+});
125+});
126+127+it("contains background flush error reporter failures", async () => {
128+await captureUnhandledRejections(async (rejections) => {
129+const error = new Error("send failed");
130+const onBackgroundFlushError = vi.fn<(err: unknown) => void>(() => {
131+throw new Error("report failed");
132+});
133+const sendOrEditStreamMessage = vi
134+.fn<(text: string) => Promise<boolean>>()
135+.mockRejectedValueOnce(error)
136+.mockResolvedValueOnce(true);
137+138+const loop = createDraftStreamLoop({
139+throttleMs: 0,
140+isStopped: () => false,
141+ sendOrEditStreamMessage,
142+ onBackgroundFlushError,
143+});
144+145+loop.update("hello");
146+await flushMicrotasks();
147+await flushMacrotask();
148+await loop.flush();
149+150+expect(rejections).toStrictEqual([]);
151+expect(onBackgroundFlushError).toHaveBeenCalledWith(error);
152+expect(sendOrEditStreamMessage).toHaveBeenNthCalledWith(2, "hello");
153+});
154+});
155+156+it("keeps explicit flush rejections visible and preserves pending text", async () => {
157+const error = new Error("send failed");
158+const sendOrEditStreamMessage = vi
159+.fn<(text: string) => Promise<boolean>>()
160+.mockRejectedValueOnce(error)
161+.mockResolvedValueOnce(true);
162+163+const loop = createDraftStreamLoop({
164+throttleMs: 100,
165+isStopped: () => false,
166+ sendOrEditStreamMessage,
167+});
168+169+loop.update("hello");
170+await expect(loop.flush()).rejects.toThrow(error);
171+await loop.flush();
172+173+expect(sendOrEditStreamMessage).toHaveBeenNthCalledWith(1, "hello");
174+expect(sendOrEditStreamMessage).toHaveBeenNthCalledWith(2, "hello");
175+});
176+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。