


















@@ -53,6 +53,28 @@ function readUrlEncodedRequestBody(init: RequestInit | undefined): URLSearchPara
5353throw new Error("Expected Twilio request body to be URL-encoded.");
5454}
555556+function cancelTrackedTextResponse(
57+text: string,
58+init?: ResponseInit,
59+): {
60+response: Response;
61+wasCanceled: () => boolean;
62+} {
63+let canceled = false;
64+const stream = new ReadableStream<Uint8Array>({
65+start(controller) {
66+controller.enqueue(new TextEncoder().encode(text));
67+},
68+cancel() {
69+canceled = true;
70+},
71+});
72+return {
73+response: new Response(stream, init),
74+wasCanceled: () => canceled,
75+};
76+}
77+5678describe("Twilio SMS helpers", () => {
5779afterEach(() => {
5880fetchWithSsrFGuardMock.mockReset();
@@ -472,6 +494,35 @@ describe("Twilio SMS helpers", () => {
472494expect(release).toHaveBeenCalledTimes(1);
473495});
474496497+it("bounds and cancels oversized guarded Twilio error bodies", async () => {
498+const release = vi.fn(async () => {});
499+const tracked = cancelTrackedTextResponse(`${"upstream unavailable ".repeat(512)}tail`, {
500+status: 503,
501+});
502+fetchWithSsrFGuardMock.mockResolvedValue({
503+response: tracked.response,
504+ release,
505+});
506+507+let caught: Error | undefined;
508+try {
509+await sendSmsViaTwilio({
510+account: createAccount(),
511+to: "+15551234567",
512+text: "hello",
513+});
514+} catch (error) {
515+caught = error as Error;
516+}
517+518+expect(caught?.message).toContain("Twilio SMS send failed (503): upstream unavailable");
519+expect(caught?.message).toContain("... [truncated]");
520+expect(caught?.message).not.toContain("tail");
521+expect(caught?.message.length).toBeLessThan(8_300);
522+expect(tracked.wasCanceled()).toBe(true);
523+expect(release).toHaveBeenCalledTimes(1);
524+});
525+475526it("rejects malformed JSON from successful Twilio sends", async () => {
476527const fetchImpl = vi.fn<typeof fetch>(async () => new Response("not json", { status: 201 }));
477528@@ -503,6 +554,28 @@ describe("Twilio SMS helpers", () => {
503554expect(release).toHaveBeenCalledTimes(1);
504555});
505556557+it("bounds and cancels oversized guarded Twilio success bodies", async () => {
558+const release = vi.fn(async () => {});
559+const tracked = cancelTrackedTextResponse("x".repeat(1024 * 1024 + 1), { status: 201 });
560+fetchWithSsrFGuardMock.mockResolvedValue({
561+response: tracked.response,
562+ release,
563+});
564+565+await expect(
566+sendSmsViaTwilio({
567+account: createAccount(),
568+to: "+15551234567",
569+text: "hello",
570+}),
571+).rejects.toThrow(
572+"Twilio SMS API response body too large: 1048577 bytes (limit: 1048576 bytes)",
573+);
574+575+expect(tracked.wasCanceled()).toBe(true);
576+expect(release).toHaveBeenCalledTimes(1);
577+});
578+506579it("exposes a typed Twilio SMS API error", () => {
507580const error = new TwilioSmsApiError(
508581429,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。