fix(telegram): land #34238 from @hal-crackbot

Landed from contributor PR #34238 by @hal-crackbot.

Co-authored-by: Hal Crackbot <hal@crackbot.dev>
This commit is contained in:
Peter Steinberger
2026-03-08 00:56:44 +00:00
parent 09cfcf9dd5
commit eb09d8dd71
6 changed files with 177 additions and 6 deletions

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { isRecoverableTelegramNetworkError } from "./network-errors.js";
import { isRecoverableTelegramNetworkError, isSafeToRetrySendError } from "./network-errors.js";
describe("isRecoverableTelegramNetworkError", () => {
it("detects recoverable error codes", () => {
@@ -106,3 +106,61 @@ describe("isRecoverableTelegramNetworkError", () => {
});
});
});
describe("isSafeToRetrySendError", () => {
it("allows retry for ECONNREFUSED (pre-connect, message not sent)", () => {
const err = Object.assign(new Error("connect ECONNREFUSED"), { code: "ECONNREFUSED" });
expect(isSafeToRetrySendError(err)).toBe(true);
});
it("allows retry for ENOTFOUND (DNS failure, message not sent)", () => {
const err = Object.assign(new Error("getaddrinfo ENOTFOUND"), { code: "ENOTFOUND" });
expect(isSafeToRetrySendError(err)).toBe(true);
});
it("allows retry for EAI_AGAIN (transient DNS, message not sent)", () => {
const err = Object.assign(new Error("getaddrinfo EAI_AGAIN"), { code: "EAI_AGAIN" });
expect(isSafeToRetrySendError(err)).toBe(true);
});
it("allows retry for ENETUNREACH (no route to host, message not sent)", () => {
const err = Object.assign(new Error("connect ENETUNREACH"), { code: "ENETUNREACH" });
expect(isSafeToRetrySendError(err)).toBe(true);
});
it("allows retry for EHOSTUNREACH (host unreachable, message not sent)", () => {
const err = Object.assign(new Error("connect EHOSTUNREACH"), { code: "EHOSTUNREACH" });
expect(isSafeToRetrySendError(err)).toBe(true);
});
it("does NOT allow retry for ECONNRESET (message may already be delivered)", () => {
const err = Object.assign(new Error("read ECONNRESET"), { code: "ECONNRESET" });
expect(isSafeToRetrySendError(err)).toBe(false);
});
it("does NOT allow retry for ETIMEDOUT (message may already be delivered)", () => {
const err = Object.assign(new Error("connect ETIMEDOUT"), { code: "ETIMEDOUT" });
expect(isSafeToRetrySendError(err)).toBe(false);
});
it("does NOT allow retry for EPIPE (connection broken mid-transfer, message may be delivered)", () => {
const err = Object.assign(new Error("write EPIPE"), { code: "EPIPE" });
expect(isSafeToRetrySendError(err)).toBe(false);
});
it("does NOT allow retry for UND_ERR_CONNECT_TIMEOUT (ambiguous timing)", () => {
const err = Object.assign(new Error("connect timeout"), { code: "UND_ERR_CONNECT_TIMEOUT" });
expect(isSafeToRetrySendError(err)).toBe(false);
});
it("does NOT allow retry for non-network errors", () => {
expect(isSafeToRetrySendError(new Error("400: Bad Request"))).toBe(false);
expect(isSafeToRetrySendError(null)).toBe(false);
});
it("detects pre-connect error nested in cause chain", () => {
const root = Object.assign(new Error("ECONNREFUSED"), { code: "ECONNREFUSED" });
const wrapped = Object.assign(new Error("fetch failed"), { cause: root });
expect(isSafeToRetrySendError(wrapped)).toBe(true);
});
});