























@@ -2,11 +2,11 @@ import { withFetchPreconnect } from "openclaw/plugin-sdk/test-env";
22import { afterEach, describe, expect, it, vi, type Mock } from "vitest";
33import { probeTelegram, resetTelegramProbeFetcherCacheForTests } from "./probe.js";
445-const resolveTelegramFetch = vi.hoisted(() => vi.fn());
5+const resolveTelegramTransport = vi.hoisted(() => vi.fn());
66const makeProxyFetch = vi.hoisted(() => vi.fn());
7788vi.mock("./fetch.js", () => ({
9-resolveTelegramFetch,
9+resolveTelegramTransport,
1010resolveTelegramApiBase: (apiRoot?: string) =>
1111apiRoot?.trim()?.replace(/\/+$/, "") || "https://api.telegram.org",
1212}));
@@ -19,11 +19,18 @@ describe("probeTelegram retry logic", () => {
1919const token = "test-token";
2020const timeoutMs = 5000;
2121const originalFetch = global.fetch;
22+let forceFallbackMock: Mock;
22232324const installFetchMock = (): Mock => {
2425const fetchMock = vi.fn();
2526global.fetch = withFetchPreconnect(fetchMock);
26-resolveTelegramFetch.mockImplementation((proxyFetch?: typeof fetch) => proxyFetch ?? fetch);
27+forceFallbackMock = vi.fn().mockReturnValue(true);
28+resolveTelegramTransport.mockImplementation((proxyFetch?: typeof fetch) => ({
29+fetch: proxyFetch ?? fetch,
30+sourceFetch: proxyFetch ?? fetch,
31+forceFallback: forceFallbackMock,
32+close: async () => {},
33+}));
2734makeProxyFetch.mockImplementation(() => fetchMock as unknown as typeof fetch);
2835return fetchMock;
2936};
@@ -72,7 +79,7 @@ describe("probeTelegram retry logic", () => {
72797380afterEach(() => {
7481resetTelegramProbeFetcherCacheForTests();
75-resolveTelegramFetch.mockReset();
82+resolveTelegramTransport.mockReset();
7683makeProxyFetch.mockReset();
7784vi.unstubAllEnvs();
7885vi.clearAllMocks();
@@ -151,7 +158,12 @@ describe("probeTelegram retry logic", () => {
151158});
152159});
153160global.fetch = withFetchPreconnect(fetchMock as unknown as typeof fetch);
154-resolveTelegramFetch.mockImplementation((proxyFetch?: typeof fetch) => proxyFetch ?? fetch);
161+resolveTelegramTransport.mockImplementation((proxyFetch?: typeof fetch) => ({
162+fetch: proxyFetch ?? fetch,
163+sourceFetch: proxyFetch ?? fetch,
164+forceFallback: vi.fn().mockReturnValue(true),
165+close: async () => {},
166+}));
155167makeProxyFetch.mockImplementation(() => fetchMock as unknown as typeof fetch);
156168vi.useFakeTimers();
157169try {
@@ -226,7 +238,7 @@ describe("probeTelegram retry logic", () => {
226238});
227239228240expect(makeProxyFetch).toHaveBeenCalledWith("http://127.0.0.1:8888");
229-expect(resolveTelegramFetch).toHaveBeenCalledWith(fetchMock, {
241+expect(resolveTelegramTransport).toHaveBeenCalledWith(fetchMock, {
230242network: {
231243autoSelectFamily: false,
232244dnsResultOrder: "ipv4first",
@@ -257,7 +269,7 @@ describe("probeTelegram retry logic", () => {
257269},
258270});
259271260-expect(resolveTelegramFetch).toHaveBeenCalledTimes(1);
272+expect(resolveTelegramTransport).toHaveBeenCalledTimes(1);
261273});
262274263275it("does not reuse probe fetcher cache when network settings differ", async () => {
@@ -283,7 +295,7 @@ describe("probeTelegram retry logic", () => {
283295},
284296});
285297286-expect(resolveTelegramFetch).toHaveBeenCalledTimes(2);
298+expect(resolveTelegramTransport).toHaveBeenCalledTimes(2);
287299});
288300289301it("reuses probe fetcher cache across token rotation when accountId is stable", async () => {
@@ -311,6 +323,48 @@ describe("probeTelegram retry logic", () => {
311323},
312324});
313325314-expect(resolveTelegramFetch).toHaveBeenCalledTimes(1);
326+expect(resolveTelegramTransport).toHaveBeenCalledTimes(1);
327+});
328+329+it("calls forceFallback on the transport when getMe times out so subsequent probes use IPv4", async () => {
330+const fetchMock = vi.fn();
331+const localForceFallback = vi.fn().mockReturnValue(true);
332+resolveTelegramTransport.mockImplementation(() => ({
333+fetch: withFetchPreconnect(fetchMock),
334+sourceFetch: fetchMock,
335+forceFallback: localForceFallback,
336+close: async () => {},
337+}));
338+339+// First call: timeout (simulate IPv6 hang)
340+const timeoutError = new Error("request timed out");
341+timeoutError.name = "TimeoutError";
342+fetchMock.mockRejectedValueOnce(timeoutError);
343+// Second call (retry after forceFallback): success on IPv4
344+fetchMock.mockResolvedValueOnce({
345+ok: true,
346+json: vi.fn().mockResolvedValue({
347+ok: true,
348+result: { id: 1, is_bot: true, first_name: "Bot", username: "bot" },
349+}),
350+});
351+// Webhook info
352+fetchMock.mockResolvedValueOnce({
353+ok: true,
354+json: vi.fn().mockResolvedValue({ ok: true, result: { url: "" } }),
355+});
356+357+vi.useFakeTimers();
358+try {
359+const probePromise = probeTelegram(token, 30_000);
360+await vi.advanceTimersByTimeAsync(1000);
361+362+const result = await probePromise;
363+expect(result.ok).toBe(true);
364+expect(localForceFallback).toHaveBeenCalledWith("probe timeout/network error");
365+expect(fetchMock).toHaveBeenCalledTimes(3); // 1 failed + 1 getMe success + 1 webhook
366+} finally {
367+vi.useRealTimers();
368+}
315369});
316370});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。