mirror of https://github.com/openclaw/openclaw.git
test: move telegram fetch coverage into extensions
This commit is contained in:
parent
e0c4458a2f
commit
bb3ea2137b
|
|
@ -320,7 +320,12 @@ describe("resolveMedia getFile retry", () => {
|
|||
it("uses caller-provided fetch impl for file downloads", async () => {
|
||||
const getFile = vi.fn().mockResolvedValue({ file_path: "documents/file_42.pdf" });
|
||||
const callerFetch = vi.fn() as unknown as typeof fetch;
|
||||
const callerTransport = { fetch: callerFetch, sourceFetch: callerFetch };
|
||||
const dispatcherAttempts = [{ dispatcherPolicy: { mode: "direct" as const } }];
|
||||
const callerTransport = {
|
||||
fetch: callerFetch,
|
||||
sourceFetch: callerFetch,
|
||||
dispatcherAttempts,
|
||||
};
|
||||
fetchRemoteMedia.mockResolvedValueOnce({
|
||||
buffer: Buffer.from("pdf-data"),
|
||||
contentType: "application/pdf",
|
||||
|
|
@ -339,6 +344,13 @@ describe("resolveMedia getFile retry", () => {
|
|||
expect(fetchRemoteMedia).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
fetchImpl: callerFetch,
|
||||
dispatcherAttempts,
|
||||
shouldRetryFetchError: expect.any(Function),
|
||||
readIdleTimeoutMs: 30_000,
|
||||
ssrfPolicy: {
|
||||
allowRfc2544BenchmarkRange: true,
|
||||
hostnameAllowlist: ["api.telegram.org"],
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
@ -369,6 +381,36 @@ describe("resolveMedia getFile retry", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("allows an explicit Telegram apiRoot host without broadening the default SSRF allowlist", async () => {
|
||||
const getFile = vi.fn().mockResolvedValue({ file_path: "documents/file_42.pdf" });
|
||||
fetchRemoteMedia.mockResolvedValueOnce({
|
||||
buffer: Buffer.from("pdf-data"),
|
||||
contentType: "application/pdf",
|
||||
fileName: "file_42.pdf",
|
||||
});
|
||||
saveMediaBuffer.mockResolvedValueOnce({
|
||||
path: "/tmp/file_42---uuid.pdf",
|
||||
contentType: "application/pdf",
|
||||
});
|
||||
|
||||
await resolveMediaWithDefaults(makeCtx("document", getFile), {
|
||||
apiRoot: "https://telegram.internal:8443/custom/",
|
||||
dangerouslyAllowPrivateNetwork: true,
|
||||
});
|
||||
|
||||
expect(fetchRemoteMedia).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: `https://telegram.internal:8443/custom/file/bot${BOT_TOKEN}/documents/file_42.pdf`,
|
||||
ssrfPolicy: {
|
||||
hostnameAllowlist: ["api.telegram.org", "telegram.internal"],
|
||||
allowedHostnames: ["telegram.internal"],
|
||||
allowPrivateNetwork: true,
|
||||
allowRfc2544BenchmarkRange: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("uses local absolute file paths directly for media downloads", async () => {
|
||||
const getFile = vi.fn().mockResolvedValue({ file_path: "/var/lib/telegram-bot-api/file.pdf" });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,347 +0,0 @@
|
|||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const undiciMocks = vi.hoisted(() => {
|
||||
const createDispatcherCtor = <T extends Record<string, unknown> | string>() =>
|
||||
vi.fn(function MockDispatcher(this: { options?: T }, options?: T) {
|
||||
this.options = options;
|
||||
});
|
||||
|
||||
return {
|
||||
fetch: vi.fn(),
|
||||
agentCtor: createDispatcherCtor<Record<string, unknown>>(),
|
||||
envHttpProxyAgentCtor: createDispatcherCtor<Record<string, unknown>>(),
|
||||
proxyAgentCtor: createDispatcherCtor<Record<string, unknown> | string>(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("undici", () => ({
|
||||
Agent: undiciMocks.agentCtor,
|
||||
EnvHttpProxyAgent: undiciMocks.envHttpProxyAgentCtor,
|
||||
ProxyAgent: undiciMocks.proxyAgentCtor,
|
||||
fetch: undiciMocks.fetch,
|
||||
}));
|
||||
|
||||
let fetchRemoteMedia: typeof import("../../../src/media/fetch.js").fetchRemoteMedia;
|
||||
let resolveTelegramTransport: typeof import("./fetch.js").resolveTelegramTransport;
|
||||
let shouldRetryTelegramTransportFallback: typeof import("./fetch.js").shouldRetryTelegramTransportFallback;
|
||||
let makeProxyFetch: typeof import("./proxy.js").makeProxyFetch;
|
||||
let TEST_UNDICI_RUNTIME_DEPS_KEY: typeof import("../../../src/infra/net/undici-runtime.js").TEST_UNDICI_RUNTIME_DEPS_KEY;
|
||||
|
||||
describe("fetchRemoteMedia telegram network policy", () => {
|
||||
type LookupFn = NonNullable<Parameters<typeof fetchRemoteMedia>[0]["lookupFn"]>;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ TEST_UNDICI_RUNTIME_DEPS_KEY } = await import("../../../src/infra/net/undici-runtime.js"));
|
||||
({ fetchRemoteMedia } = await import("../../../src/media/fetch.js"));
|
||||
({ resolveTelegramTransport, shouldRetryTelegramTransportFallback } =
|
||||
await import("./fetch.js"));
|
||||
({ makeProxyFetch } = await import("./proxy.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
undiciMocks.fetch.mockReset();
|
||||
undiciMocks.agentCtor.mockClear();
|
||||
undiciMocks.envHttpProxyAgentCtor.mockClear();
|
||||
undiciMocks.proxyAgentCtor.mockClear();
|
||||
(globalThis as Record<string, unknown>)[TEST_UNDICI_RUNTIME_DEPS_KEY] = {
|
||||
Agent: undiciMocks.agentCtor,
|
||||
EnvHttpProxyAgent: undiciMocks.envHttpProxyAgentCtor,
|
||||
ProxyAgent: undiciMocks.proxyAgentCtor,
|
||||
};
|
||||
});
|
||||
|
||||
function createTelegramFetchFailedError(code: string): Error {
|
||||
return Object.assign(new TypeError("fetch failed"), {
|
||||
cause: { code },
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
Reflect.deleteProperty(globalThis as object, TEST_UNDICI_RUNTIME_DEPS_KEY);
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Reflect.deleteProperty(globalThis as object, TEST_UNDICI_RUNTIME_DEPS_KEY);
|
||||
});
|
||||
|
||||
it("preserves Telegram resolver transport policy for file downloads", async () => {
|
||||
const lookupFn = vi.fn(async () => [
|
||||
{ address: "149.154.167.220", family: 4 },
|
||||
]) as unknown as LookupFn;
|
||||
undiciMocks.fetch.mockResolvedValueOnce(
|
||||
new Response(new Uint8Array([0xff, 0xd8, 0xff, 0x00]), {
|
||||
status: 200,
|
||||
headers: { "content-type": "image/jpeg" },
|
||||
}),
|
||||
);
|
||||
|
||||
const telegramTransport = resolveTelegramTransport(undefined, {
|
||||
network: {
|
||||
autoSelectFamily: true,
|
||||
dnsResultOrder: "verbatim",
|
||||
},
|
||||
});
|
||||
|
||||
await fetchRemoteMedia({
|
||||
url: "https://api.telegram.org/file/bottok/photos/1.jpg",
|
||||
fetchImpl: telegramTransport.sourceFetch,
|
||||
dispatcherAttempts: telegramTransport.dispatcherAttempts,
|
||||
lookupFn,
|
||||
maxBytes: 1024,
|
||||
ssrfPolicy: {
|
||||
allowedHostnames: ["api.telegram.org"],
|
||||
allowRfc2544BenchmarkRange: true,
|
||||
},
|
||||
});
|
||||
|
||||
const init = undiciMocks.fetch.mock.calls[0]?.[1] as
|
||||
| (RequestInit & {
|
||||
dispatcher?: {
|
||||
options?: {
|
||||
connect?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
})
|
||||
| undefined;
|
||||
|
||||
expect(init?.dispatcher?.options?.connect).toEqual(
|
||||
expect.objectContaining({
|
||||
autoSelectFamily: true,
|
||||
autoSelectFamilyAttemptTimeout: 300,
|
||||
lookup: expect.any(Function),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps explicit proxy routing for file downloads", async () => {
|
||||
const lookupFn = vi.fn(async () => [
|
||||
{ address: "149.154.167.220", family: 4 },
|
||||
]) as unknown as LookupFn;
|
||||
undiciMocks.fetch.mockResolvedValueOnce(
|
||||
new Response(new Uint8Array([0x25, 0x50, 0x44, 0x46]), {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/pdf" },
|
||||
}),
|
||||
);
|
||||
|
||||
const telegramTransport = resolveTelegramTransport(makeProxyFetch("http://127.0.0.1:7890"), {
|
||||
network: {
|
||||
autoSelectFamily: false,
|
||||
dnsResultOrder: "ipv4first",
|
||||
},
|
||||
});
|
||||
|
||||
await fetchRemoteMedia({
|
||||
url: "https://api.telegram.org/file/bottok/files/1.pdf",
|
||||
fetchImpl: telegramTransport.sourceFetch,
|
||||
dispatcherAttempts: telegramTransport.dispatcherAttempts,
|
||||
lookupFn,
|
||||
maxBytes: 1024,
|
||||
ssrfPolicy: {
|
||||
allowedHostnames: ["api.telegram.org"],
|
||||
allowRfc2544BenchmarkRange: true,
|
||||
},
|
||||
});
|
||||
|
||||
const init = undiciMocks.fetch.mock.calls[0]?.[1] as
|
||||
| (RequestInit & {
|
||||
dispatcher?: {
|
||||
options?: {
|
||||
uri?: string;
|
||||
requestTls?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
})
|
||||
| undefined;
|
||||
|
||||
expect(init?.dispatcher?.options?.uri).toBe("http://127.0.0.1:7890");
|
||||
expect(init?.dispatcher?.options?.requestTls).toEqual(
|
||||
expect.objectContaining({
|
||||
autoSelectFamily: false,
|
||||
lookup: expect.any(Function),
|
||||
}),
|
||||
);
|
||||
expect(undiciMocks.proxyAgentCtor).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("retries Telegram file downloads with IPv4 fallback when the first fetch fails", async () => {
|
||||
const lookupFn = vi.fn(async () => [
|
||||
{ address: "149.154.167.220", family: 4 },
|
||||
{ address: "2001:67c:4e8:f004::9", family: 6 },
|
||||
]) as unknown as LookupFn;
|
||||
undiciMocks.fetch
|
||||
.mockRejectedValueOnce(createTelegramFetchFailedError("EHOSTUNREACH"))
|
||||
.mockResolvedValueOnce(
|
||||
new Response(new Uint8Array([0xff, 0xd8, 0xff, 0x00]), {
|
||||
status: 200,
|
||||
headers: { "content-type": "image/jpeg" },
|
||||
}),
|
||||
);
|
||||
|
||||
const telegramTransport = resolveTelegramTransport(undefined, {
|
||||
network: {
|
||||
autoSelectFamily: true,
|
||||
dnsResultOrder: "ipv4first",
|
||||
},
|
||||
});
|
||||
|
||||
await fetchRemoteMedia({
|
||||
url: "https://api.telegram.org/file/bottok/photos/2.jpg",
|
||||
fetchImpl: telegramTransport.sourceFetch,
|
||||
dispatcherAttempts: telegramTransport.dispatcherAttempts,
|
||||
shouldRetryFetchError: shouldRetryTelegramTransportFallback,
|
||||
lookupFn,
|
||||
maxBytes: 1024,
|
||||
ssrfPolicy: {
|
||||
allowedHostnames: ["api.telegram.org"],
|
||||
allowRfc2544BenchmarkRange: true,
|
||||
},
|
||||
});
|
||||
|
||||
const firstInit = undiciMocks.fetch.mock.calls[0]?.[1] as
|
||||
| (RequestInit & {
|
||||
dispatcher?: {
|
||||
options?: {
|
||||
connect?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
})
|
||||
| undefined;
|
||||
const secondInit = undiciMocks.fetch.mock.calls[1]?.[1] as
|
||||
| (RequestInit & {
|
||||
dispatcher?: {
|
||||
options?: {
|
||||
connect?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
})
|
||||
| undefined;
|
||||
|
||||
expect(undiciMocks.fetch).toHaveBeenCalledTimes(2);
|
||||
expect(firstInit?.dispatcher?.options?.connect).toEqual(
|
||||
expect.objectContaining({
|
||||
autoSelectFamily: true,
|
||||
autoSelectFamilyAttemptTimeout: 300,
|
||||
lookup: expect.any(Function),
|
||||
}),
|
||||
);
|
||||
expect(secondInit?.dispatcher?.options?.connect).toEqual(
|
||||
expect.objectContaining({
|
||||
family: 4,
|
||||
autoSelectFamily: false,
|
||||
lookup: expect.any(Function),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("retries Telegram file downloads with pinned Telegram IP after IPv4 fallback fails", async () => {
|
||||
const lookupFn = vi.fn(async () => [
|
||||
{ address: "149.154.167.221", family: 4 },
|
||||
{ address: "2001:67c:4e8:f004::9", family: 6 },
|
||||
]) as unknown as LookupFn;
|
||||
undiciMocks.fetch
|
||||
.mockRejectedValueOnce(createTelegramFetchFailedError("EHOSTUNREACH"))
|
||||
.mockRejectedValueOnce(createTelegramFetchFailedError("ETIMEDOUT"))
|
||||
.mockResolvedValueOnce(
|
||||
new Response(new Uint8Array([0xff, 0xd8, 0xff, 0x00]), {
|
||||
status: 200,
|
||||
headers: { "content-type": "image/jpeg" },
|
||||
}),
|
||||
);
|
||||
|
||||
const telegramTransport = resolveTelegramTransport(undefined, {
|
||||
network: {
|
||||
autoSelectFamily: true,
|
||||
dnsResultOrder: "ipv4first",
|
||||
},
|
||||
});
|
||||
|
||||
await fetchRemoteMedia({
|
||||
url: "https://api.telegram.org/file/bottok/photos/3.jpg",
|
||||
fetchImpl: telegramTransport.sourceFetch,
|
||||
dispatcherAttempts: telegramTransport.dispatcherAttempts,
|
||||
shouldRetryFetchError: shouldRetryTelegramTransportFallback,
|
||||
lookupFn,
|
||||
maxBytes: 1024,
|
||||
ssrfPolicy: {
|
||||
allowedHostnames: ["api.telegram.org"],
|
||||
allowRfc2544BenchmarkRange: true,
|
||||
},
|
||||
});
|
||||
|
||||
const thirdInit = undiciMocks.fetch.mock.calls[2]?.[1] as
|
||||
| (RequestInit & {
|
||||
dispatcher?: {
|
||||
options?: {
|
||||
connect?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
})
|
||||
| undefined;
|
||||
const callback = vi.fn();
|
||||
(
|
||||
thirdInit?.dispatcher?.options?.connect?.lookup as
|
||||
| ((
|
||||
hostname: string,
|
||||
callback: (err: null, address: string, family: number) => void,
|
||||
) => void)
|
||||
| undefined
|
||||
)?.("api.telegram.org", callback);
|
||||
|
||||
expect(undiciMocks.fetch).toHaveBeenCalledTimes(3);
|
||||
expect(thirdInit?.dispatcher?.options?.connect).toEqual(
|
||||
expect.objectContaining({
|
||||
family: 4,
|
||||
autoSelectFamily: false,
|
||||
lookup: expect.any(Function),
|
||||
}),
|
||||
);
|
||||
expect(callback).toHaveBeenCalledWith(null, "149.154.167.220", 4);
|
||||
});
|
||||
|
||||
it("preserves both primary and final fallback errors when Telegram media retry chain fails", async () => {
|
||||
const lookupFn = vi.fn(async () => [
|
||||
{ address: "149.154.167.220", family: 4 },
|
||||
{ address: "2001:67c:4e8:f004::9", family: 6 },
|
||||
]) as unknown as LookupFn;
|
||||
const primaryError = createTelegramFetchFailedError("EHOSTUNREACH");
|
||||
const ipv4Error = createTelegramFetchFailedError("ETIMEDOUT");
|
||||
const fallbackError = createTelegramFetchFailedError("ETIMEDOUT");
|
||||
undiciMocks.fetch
|
||||
.mockRejectedValueOnce(primaryError)
|
||||
.mockRejectedValueOnce(ipv4Error)
|
||||
.mockRejectedValueOnce(fallbackError);
|
||||
|
||||
const telegramTransport = resolveTelegramTransport(undefined, {
|
||||
network: {
|
||||
autoSelectFamily: true,
|
||||
dnsResultOrder: "ipv4first",
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
fetchRemoteMedia({
|
||||
url: "https://api.telegram.org/file/bottok/photos/4.jpg",
|
||||
fetchImpl: telegramTransport.sourceFetch,
|
||||
dispatcherAttempts: telegramTransport.dispatcherAttempts,
|
||||
shouldRetryFetchError: shouldRetryTelegramTransportFallback,
|
||||
lookupFn,
|
||||
maxBytes: 1024,
|
||||
ssrfPolicy: {
|
||||
allowedHostnames: ["api.telegram.org"],
|
||||
allowRfc2544BenchmarkRange: true,
|
||||
},
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
name: "MediaFetchError",
|
||||
code: "fetch_failed",
|
||||
cause: expect.objectContaining({
|
||||
name: "Error",
|
||||
cause: fallbackError,
|
||||
attemptErrors: [primaryError, ipv4Error, fallbackError],
|
||||
primaryError,
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -79,6 +79,10 @@ let resolveFetch: typeof import("../../../src/infra/fetch.js").resolveFetch;
|
|||
let resolveTelegramFetch: typeof import("./fetch.js").resolveTelegramFetch;
|
||||
let resolveTelegramTransport: typeof import("./fetch.js").resolveTelegramTransport;
|
||||
|
||||
type TelegramDispatcherPolicy = NonNullable<
|
||||
ReturnType<typeof resolveTelegramTransport>["dispatcherAttempts"]
|
||||
>[number]["dispatcherPolicy"];
|
||||
|
||||
beforeAll(async () => {
|
||||
({ resolveFetch } = await import("../../../src/infra/fetch.js"));
|
||||
({ resolveTelegramFetch, resolveTelegramTransport } = await import("./fetch.js"));
|
||||
|
|
@ -393,6 +397,58 @@ describe("resolveTelegramFetch", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("exports fallback dispatcher attempts for Telegram media downloads", () => {
|
||||
const transport = resolveTelegramTransport(undefined, {
|
||||
network: {
|
||||
autoSelectFamily: true,
|
||||
dnsResultOrder: "ipv4first",
|
||||
},
|
||||
});
|
||||
|
||||
expect(transport.sourceFetch).toBeDefined();
|
||||
expect(transport.fetch).not.toBe(transport.sourceFetch);
|
||||
expect(transport.dispatcherAttempts).toHaveLength(3);
|
||||
|
||||
const [defaultAttempt, ipv4Attempt, pinnedAttempt] = transport.dispatcherAttempts as Array<{
|
||||
dispatcherPolicy?: TelegramDispatcherPolicy;
|
||||
}>;
|
||||
|
||||
expect(defaultAttempt.dispatcherPolicy).toEqual(
|
||||
expect.objectContaining({
|
||||
mode: "direct",
|
||||
connect: expect.objectContaining({
|
||||
autoSelectFamily: true,
|
||||
autoSelectFamilyAttemptTimeout: 300,
|
||||
lookup: expect.any(Function),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(ipv4Attempt.dispatcherPolicy).toEqual(
|
||||
expect.objectContaining({
|
||||
mode: "direct",
|
||||
connect: expect.objectContaining({
|
||||
family: 4,
|
||||
autoSelectFamily: false,
|
||||
lookup: expect.any(Function),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(pinnedAttempt.dispatcherPolicy).toEqual(
|
||||
expect.objectContaining({
|
||||
mode: "direct",
|
||||
pinnedHostname: {
|
||||
hostname: "api.telegram.org",
|
||||
addresses: ["149.154.167.220"],
|
||||
},
|
||||
connect: expect.objectContaining({
|
||||
family: 4,
|
||||
autoSelectFamily: false,
|
||||
lookup: expect.any(Function),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not blind-retry when sticky IPv4 fallback is disallowed for explicit proxy paths", async () => {
|
||||
const { makeProxyFetch } = await import("./proxy.js");
|
||||
const proxyFetch = makeProxyFetch("http://127.0.0.1:7890");
|
||||
|
|
|
|||
|
|
@ -106,10 +106,6 @@
|
|||
"file": "extensions/telegram/src/bot.test.ts",
|
||||
"reason": "This Telegram bot runtime suite measured ~819.9 MiB RSS growth locally; keep it in its own forked channel lane so the shared channels worker can recycle immediately after the hotspot file."
|
||||
},
|
||||
{
|
||||
"file": "extensions/telegram/src/fetch.test.ts",
|
||||
"reason": "This Telegram transport suite measured ~759.3 MiB RSS growth locally; keep it in its own forked channel lane so the shared channels worker can recycle immediately after the hotspot file."
|
||||
},
|
||||
{
|
||||
"file": "extensions/telegram/src/sendchataction-401-backoff.test.ts",
|
||||
"reason": "This Telegram send-chat-action backoff suite hoists infra-runtime sleep mocks and remains a relatively heavy shared hotspot; keep it isolated so top-level concurrency can overlap it instead of extending the shared channels batch."
|
||||
|
|
@ -210,6 +206,10 @@
|
|||
"file": "extensions/line/src/send.test.ts",
|
||||
"reason": "This LINE send suite hoists SDK and account/token mocks; keep it isolated so shared extension workers do not reuse cached send modules that bypass those mocks."
|
||||
},
|
||||
{
|
||||
"file": "extensions/telegram/src/fetch.test.ts",
|
||||
"reason": "This Telegram transport suite is extension-owned and still measures high RSS locally; keep it in its own forked extensions lane so the shared worker can recycle immediately after the hotspot."
|
||||
},
|
||||
{
|
||||
"file": "extensions/tavily/src/tavily-extract-tool.test.ts",
|
||||
"reason": "This extract-tool suite hoists the Tavily client mock and imports lazily; keep it isolated so shared extension workers do not reuse cached tool modules and hit real API-key resolution."
|
||||
|
|
|
|||
|
|
@ -60,15 +60,9 @@
|
|||
"extensions/discord/src/monitor/message-handler.queue.test.ts": {
|
||||
"durationMs": 3900
|
||||
},
|
||||
"extensions/telegram/src/fetch.test.ts": {
|
||||
"durationMs": 3500
|
||||
},
|
||||
"extensions/whatsapp/src/monitor-inbox.captures-media-path-image-messages.test.ts": {
|
||||
"durationMs": 3200
|
||||
},
|
||||
"extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts": {
|
||||
"durationMs": 15830
|
||||
},
|
||||
"extensions/telegram/src/webhook.test.ts": {
|
||||
"durationMs": 2900
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1047,9 +1047,13 @@
|
|||
"durationMs": 2.155517578125,
|
||||
"testCount": 5
|
||||
},
|
||||
"extensions/telegram/src/fetch.network-policy.test.ts": {
|
||||
"durationMs": 3260,
|
||||
"testCount": 5
|
||||
"extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts": {
|
||||
"durationMs": 10360,
|
||||
"testCount": 27
|
||||
},
|
||||
"extensions/telegram/src/fetch.test.ts": {
|
||||
"durationMs": 750,
|
||||
"testCount": 23
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -447,14 +447,14 @@ describe("scripts/test-parallel lane planning", () => {
|
|||
expect(output).not.toContain("vitest.unit.config.ts");
|
||||
});
|
||||
|
||||
it("routes telegram fetch network policy through the extensions config", () => {
|
||||
it("routes telegram fetch transport coverage through the extensions config", () => {
|
||||
const output = runPlannerPlan([
|
||||
"--explain",
|
||||
bundledPluginFile("telegram", "src/fetch.network-policy.test.ts"),
|
||||
bundledPluginFile("telegram", "src/fetch.test.ts"),
|
||||
]);
|
||||
|
||||
expect(output).toContain("surface=extensions");
|
||||
expect(output).toContain("reasons=extensions-surface");
|
||||
expect(output).toContain("extensions-surface");
|
||||
expect(output).toContain("vitest.extensions.config.ts");
|
||||
expect(output).not.toContain("vitest.channels.config.ts");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -48,6 +48,15 @@ describe("createScopedVitestConfig", () => {
|
|||
expect(config.test?.include).toEqual(["**/*.test.ts"]);
|
||||
expect(config.test?.exclude).toEqual(expect.arrayContaining(["channel/**", "dist/**"]));
|
||||
});
|
||||
|
||||
it("overrides setup files when a scoped config requests them", () => {
|
||||
const config = createScopedVitestConfig(["src/example.test.ts"], {
|
||||
env: {},
|
||||
setupFiles: ["test/setup.extensions.ts"],
|
||||
});
|
||||
|
||||
expect(config.test?.setupFiles).toEqual(["test/setup.extensions.ts"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("scoped vitest configs", () => {
|
||||
|
|
@ -97,19 +106,23 @@ describe("scoped vitest configs", () => {
|
|||
expect(defaultExtensionsConfig.test?.include).toEqual(["**/*.test.ts"]);
|
||||
});
|
||||
|
||||
it("keeps telegram fetch network policy in extensions while excluding other telegram channel suites", () => {
|
||||
it("keeps telegram fetch transport coverage in extensions while excluding other telegram channel suites", () => {
|
||||
const extensionExcludes = defaultExtensionsConfig.test?.exclude ?? [];
|
||||
expect(
|
||||
extensionExcludes.some((pattern) => path.matchesGlob("telegram/src/fetch.test.ts", pattern)),
|
||||
).toBe(true);
|
||||
).toBe(false);
|
||||
expect(
|
||||
extensionExcludes.some((pattern) =>
|
||||
path.matchesGlob("telegram/src/fetch.network-policy.test.ts", pattern),
|
||||
path.matchesGlob("telegram/src/bot/delivery.resolve-media-retry.test.ts", pattern),
|
||||
),
|
||||
).toBe(false);
|
||||
expect(defaultChannelsConfig.test?.exclude).toContain(
|
||||
bundledPluginFile("telegram", "src/fetch.network-policy.test.ts"),
|
||||
bundledPluginFile("telegram", "src/fetch.test.ts"),
|
||||
);
|
||||
expect(defaultChannelsConfig.test?.exclude).toContain(
|
||||
bundledPluginFile("telegram", "src/bot/delivery.resolve-media-retry.test.ts"),
|
||||
);
|
||||
expect(defaultExtensionsConfig.test?.setupFiles).toEqual(["test/setup.extensions.ts"]);
|
||||
});
|
||||
|
||||
it("normalizes gateway include patterns relative to the scoped dir", () => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import {
|
|||
const normalizeRepoPath = (value) => value.split(path.sep).join("/");
|
||||
|
||||
export const extensionRoutedChannelTestFiles = [
|
||||
bundledPluginFile("telegram", "src/fetch.network-policy.test.ts"),
|
||||
bundledPluginFile("telegram", "src/bot/delivery.resolve-media-retry.test.ts"),
|
||||
bundledPluginFile("telegram", "src/fetch.test.ts"),
|
||||
];
|
||||
|
||||
const extensionRoutedChannelTestFileSet = new Set(extensionRoutedChannelTestFiles);
|
||||
|
|
|
|||
Loading…
Reference in New Issue