|
1 | | -// Telegram tests cover inbound media-error classification for ingress retry. |
2 | 1 | import { MediaFetchError } from "openclaw/plugin-sdk/media-runtime"; |
3 | 2 | import { describe, expect, it } from "vitest"; |
4 | | -import { isRecoverableMediaGroupError } from "./bot-handlers.media.js"; |
| 3 | +import { |
| 4 | + isDurablyRetryableInboundMediaError, |
| 5 | + isRecoverableMediaGroupError, |
| 6 | +} from "./bot-handlers.media.js"; |
| 7 | + |
| 8 | +describe("isDurablyRetryableInboundMediaError", () => { |
| 9 | + const networkCause = () => Object.assign(new Error("read ECONNRESET"), { code: "ECONNRESET" }); |
| 10 | + const abortCause = () => Object.assign(new Error("aborted"), { name: "AbortError" }); |
| 11 | + |
| 12 | + it("retries transient network and shutdown abort fetch failures", () => { |
| 13 | + expect( |
| 14 | + isDurablyRetryableInboundMediaError( |
| 15 | + new MediaFetchError("fetch_failed", "x", { cause: networkCause() }), |
| 16 | + ), |
| 17 | + ).toBe(true); |
| 18 | + expect( |
| 19 | + isDurablyRetryableInboundMediaError( |
| 20 | + new MediaFetchError("fetch_failed", "x", { cause: abortCause() }), |
| 21 | + ), |
| 22 | + ).toBe(true); |
| 23 | + }); |
| 24 | + |
| 25 | + it("retries 408 and 5xx HTTP fetch failures", () => { |
| 26 | + for (const status of [408, 500, 502, 503, 504]) { |
| 27 | + expect( |
| 28 | + isDurablyRetryableInboundMediaError(new MediaFetchError("http_error", "x", { status })), |
| 29 | + ).toBe(true); |
| 30 | + } |
| 31 | + }); |
| 32 | + |
| 33 | + it("does not retry permanent media failures", () => { |
| 34 | + expect( |
| 35 | + isDurablyRetryableInboundMediaError( |
| 36 | + new MediaFetchError("fetch_failed", "blocked: private address", { |
| 37 | + cause: new Error("blocked: private address"), |
| 38 | + }), |
| 39 | + ), |
| 40 | + ).toBe(false); |
| 41 | + for (const status of [400, 401, 403, 404, 429]) { |
| 42 | + expect( |
| 43 | + isDurablyRetryableInboundMediaError(new MediaFetchError("http_error", "x", { status })), |
| 44 | + ).toBe(false); |
| 45 | + } |
| 46 | + expect(isDurablyRetryableInboundMediaError(new MediaFetchError("max_bytes", "too big"))).toBe( |
| 47 | + false, |
| 48 | + ); |
| 49 | + }); |
| 50 | +}); |
5 | 51 |
|
6 | 52 | describe("isRecoverableMediaGroupError preserves album partial delivery (#55216)", () => { |
7 | 53 | it("still skips-and-warns transient and permanent album fetch failures", () => { |
8 | | - // Media groups intentionally skip the unfetchable photo and warn (#55216); |
9 | | - // only the single-message path durably retries transient failures (#98076, |
10 | | - // via isDurablyRetryableMediaFetchError). Keep this path unchanged. |
11 | 54 | expect(isRecoverableMediaGroupError(new MediaFetchError("fetch_failed", "x"))).toBe(true); |
12 | 55 | expect(isRecoverableMediaGroupError(new MediaFetchError("max_bytes", "x"))).toBe(true); |
13 | 56 | }); |
|
0 commit comments