mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-30 03:11:10 +00:00
201 lines
5.9 KiB
TypeScript
201 lines
5.9 KiB
TypeScript
import { createRequire } from "node:module";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { fetchTelegramChatId } from "./api-fetch.js";
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const EnvHttpProxyAgent = require("undici/lib/dispatcher/env-http-proxy-agent.js") as {
|
|
new (opts?: Record<string, unknown>): Record<PropertyKey, unknown>;
|
|
};
|
|
const { kHttpsProxyAgent, kNoProxyAgent } = require("undici/lib/core/symbols.js") as {
|
|
kHttpsProxyAgent: symbol;
|
|
kNoProxyAgent: symbol;
|
|
};
|
|
const proxyMocks = vi.hoisted(() => {
|
|
const undiciFetch = vi.fn();
|
|
const proxyAgentSpy = vi.fn();
|
|
const setGlobalDispatcher = vi.fn();
|
|
class ProxyAgent {
|
|
static lastCreated: ProxyAgent | undefined;
|
|
proxyUrl: string;
|
|
constructor(proxyUrl: string) {
|
|
this.proxyUrl = proxyUrl;
|
|
ProxyAgent.lastCreated = this;
|
|
proxyAgentSpy(proxyUrl);
|
|
}
|
|
}
|
|
|
|
return {
|
|
ProxyAgent,
|
|
undiciFetch,
|
|
proxyAgentSpy,
|
|
setGlobalDispatcher,
|
|
getLastAgent: () => ProxyAgent.lastCreated,
|
|
};
|
|
});
|
|
|
|
let getProxyUrlFromFetch: typeof import("./proxy.js").getProxyUrlFromFetch;
|
|
let makeProxyFetch: typeof import("./proxy.js").makeProxyFetch;
|
|
|
|
function getOwnSymbolValue(
|
|
target: Record<PropertyKey, unknown>,
|
|
description: string,
|
|
): Record<string, unknown> | undefined {
|
|
const symbol = Object.getOwnPropertySymbols(target).find(
|
|
(entry) => entry.description === description,
|
|
);
|
|
const value = symbol ? target[symbol] : undefined;
|
|
return value && typeof value === "object" ? (value as Record<string, unknown>) : undefined;
|
|
}
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllEnvs();
|
|
});
|
|
|
|
describe("fetchTelegramChatId", () => {
|
|
const cases = [
|
|
{
|
|
name: "returns stringified id when Telegram getChat succeeds",
|
|
fetchImpl: vi.fn(async () => ({
|
|
ok: true,
|
|
json: async () => ({ ok: true, result: { id: 12345 } }),
|
|
})),
|
|
expected: "12345",
|
|
},
|
|
{
|
|
name: "returns null when response is not ok",
|
|
fetchImpl: vi.fn(async () => ({
|
|
ok: false,
|
|
json: async () => ({}),
|
|
})),
|
|
expected: null,
|
|
},
|
|
{
|
|
name: "returns null on transport failures",
|
|
fetchImpl: vi.fn(async () => {
|
|
throw new Error("network failed");
|
|
}),
|
|
expected: null,
|
|
},
|
|
] as const;
|
|
|
|
for (const testCase of cases) {
|
|
it(testCase.name, async () => {
|
|
vi.stubGlobal("fetch", testCase.fetchImpl);
|
|
|
|
const id = await fetchTelegramChatId({
|
|
token: "abc",
|
|
chatId: "@user",
|
|
});
|
|
|
|
expect(id).toBe(testCase.expected);
|
|
});
|
|
}
|
|
|
|
it("calls Telegram getChat endpoint", async () => {
|
|
const fetchMock = vi.fn(async () => ({
|
|
ok: true,
|
|
json: async () => ({ ok: true, result: { id: 12345 } }),
|
|
}));
|
|
vi.stubGlobal("fetch", fetchMock);
|
|
|
|
await fetchTelegramChatId({ token: "abc", chatId: "@user" });
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
"https://api.telegram.org/botabc/getChat?chat_id=%40user",
|
|
undefined,
|
|
);
|
|
});
|
|
|
|
it("uses caller-provided fetch impl when present", async () => {
|
|
const customFetch = vi.fn(async () => ({
|
|
ok: true,
|
|
json: async () => ({ ok: true, result: { id: 12345 } }),
|
|
}));
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async () => {
|
|
throw new Error("global fetch should not be called");
|
|
}),
|
|
);
|
|
|
|
await fetchTelegramChatId({
|
|
token: "abc",
|
|
chatId: "@user",
|
|
fetchImpl: customFetch as unknown as typeof fetch,
|
|
});
|
|
|
|
expect(customFetch).toHaveBeenCalledWith(
|
|
"https://api.telegram.org/botabc/getChat?chat_id=%40user",
|
|
undefined,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("undici env proxy semantics", () => {
|
|
it("uses proxyTls rather than connect for proxied HTTPS transport settings", () => {
|
|
vi.stubEnv("HTTPS_PROXY", "http://127.0.0.1:7890");
|
|
const connect = {
|
|
family: 4,
|
|
autoSelectFamily: false,
|
|
};
|
|
|
|
const withoutProxyTls = new EnvHttpProxyAgent({ connect });
|
|
const noProxyAgent = withoutProxyTls[kNoProxyAgent] as Record<PropertyKey, unknown>;
|
|
const httpsProxyAgent = withoutProxyTls[kHttpsProxyAgent] as Record<PropertyKey, unknown>;
|
|
|
|
expect(getOwnSymbolValue(noProxyAgent, "options")?.connect).toEqual(
|
|
expect.objectContaining(connect),
|
|
);
|
|
expect(getOwnSymbolValue(httpsProxyAgent, "proxy tls settings")).toBeUndefined();
|
|
|
|
const withProxyTls = new EnvHttpProxyAgent({
|
|
connect,
|
|
proxyTls: connect,
|
|
});
|
|
const httpsProxyAgentWithProxyTls = withProxyTls[kHttpsProxyAgent] as Record<
|
|
PropertyKey,
|
|
unknown
|
|
>;
|
|
|
|
expect(getOwnSymbolValue(httpsProxyAgentWithProxyTls, "proxy tls settings")).toEqual(
|
|
expect.objectContaining(connect),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("makeProxyFetch", () => {
|
|
beforeEach(async () => {
|
|
vi.resetModules();
|
|
proxyMocks.undiciFetch.mockReset();
|
|
proxyMocks.proxyAgentSpy.mockClear();
|
|
proxyMocks.setGlobalDispatcher.mockClear();
|
|
vi.doMock("undici", () => ({
|
|
ProxyAgent: proxyMocks.ProxyAgent,
|
|
fetch: proxyMocks.undiciFetch,
|
|
setGlobalDispatcher: proxyMocks.setGlobalDispatcher,
|
|
}));
|
|
({ getProxyUrlFromFetch, makeProxyFetch } = await import("./proxy.js"));
|
|
});
|
|
|
|
it("uses undici fetch with ProxyAgent dispatcher", async () => {
|
|
const proxyUrl = "http://proxy.test:8080";
|
|
proxyMocks.undiciFetch.mockResolvedValue({ ok: true });
|
|
|
|
const proxyFetch = makeProxyFetch(proxyUrl);
|
|
await proxyFetch("https://api.telegram.org/bot123/getMe");
|
|
|
|
expect(proxyMocks.proxyAgentSpy).toHaveBeenCalledWith(proxyUrl);
|
|
expect(proxyMocks.undiciFetch).toHaveBeenCalledWith(
|
|
"https://api.telegram.org/bot123/getMe",
|
|
expect.objectContaining({ dispatcher: proxyMocks.getLastAgent() }),
|
|
);
|
|
expect(proxyMocks.setGlobalDispatcher).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("attaches proxy metadata for resolver transport handling", () => {
|
|
const proxyUrl = "http://proxy.test:8080";
|
|
const proxyFetch = makeProxyFetch(proxyUrl);
|
|
|
|
expect(getProxyUrlFromFetch(proxyFetch)).toBe(proxyUrl);
|
|
});
|
|
});
|