refactor(telegram-tests): share webhook settlement helper

This commit is contained in:
Peter Steinberger 2026-03-17 06:36:11 +00:00
parent 1ff10690e7
commit e1ca5d9cc4

View File

@ -40,6 +40,35 @@ function collectResponseBody(
});
}
function createSingleSettlement<T>(params: {
resolve: (value: T) => void;
reject: (error: unknown) => void;
clear: () => void;
}) {
let settled = false;
return {
isSettled() {
return settled;
},
resolve(value: T) {
if (settled) {
return;
}
settled = true;
params.clear();
params.resolve(value);
},
reject(error: unknown) {
if (settled) {
return;
}
settled = true;
params.clear();
params.reject(error);
},
};
}
vi.mock("grammy", async (importOriginal) => {
const actual = await importOriginal<typeof import("grammy")>();
return {
@ -96,23 +125,11 @@ async function postWebhookHeadersOnly(params: {
timeoutMs?: number;
}): Promise<{ statusCode: number; body: string }> {
return await new Promise((resolve, reject) => {
let settled = false;
const finishResolve = (value: { statusCode: number; body: string }) => {
if (settled) {
return;
}
settled = true;
clearTimeout(timeout);
resolve(value);
};
const finishReject = (error: unknown) => {
if (settled) {
return;
}
settled = true;
clearTimeout(timeout);
reject(error);
};
const settle = createSingleSettlement({
resolve,
reject,
clear: () => clearTimeout(timeout),
});
const req = request(
{
@ -128,7 +145,7 @@ async function postWebhookHeadersOnly(params: {
},
(res) => {
collectResponseBody(res, (payload) => {
finishResolve(payload);
settle.resolve(payload);
req.destroy();
});
},
@ -138,14 +155,14 @@ async function postWebhookHeadersOnly(params: {
req.destroy(
new Error(`webhook header-only post timed out after ${params.timeoutMs ?? 5_000}ms`),
);
finishReject(new Error("timed out waiting for webhook response"));
settle.reject(new Error("timed out waiting for webhook response"));
}, params.timeoutMs ?? 5_000);
req.on("error", (error) => {
if (settled && (error as NodeJS.ErrnoException).code === "ECONNRESET") {
if (settle.isSettled() && (error as NodeJS.ErrnoException).code === "ECONNRESET") {
return;
}
finishReject(error);
settle.reject(error);
});
req.flushHeaders();
@ -173,23 +190,11 @@ async function postWebhookPayloadWithChunkPlan(params: {
let bytesQueued = 0;
let chunksQueued = 0;
let phase: "writing" | "awaiting-response" = "writing";
let settled = false;
const finishResolve = (value: { statusCode: number; body: string }) => {
if (settled) {
return;
}
settled = true;
clearTimeout(timeout);
resolve(value);
};
const finishReject = (error: unknown) => {
if (settled) {
return;
}
settled = true;
clearTimeout(timeout);
reject(error);
};
const settle = createSingleSettlement({
resolve,
reject,
clear: () => clearTimeout(timeout),
});
const req = request(
{
@ -204,12 +209,12 @@ async function postWebhookPayloadWithChunkPlan(params: {
},
},
(res) => {
collectResponseBody(res, finishResolve);
collectResponseBody(res, settle.resolve);
},
);
const timeout = setTimeout(() => {
finishReject(
settle.reject(
new Error(
`webhook post timed out after ${params.timeoutMs ?? 15_000}ms (phase=${phase}, bytesQueued=${bytesQueued}, chunksQueued=${chunksQueued}, totalBytes=${payloadBuffer.length})`,
),
@ -218,7 +223,7 @@ async function postWebhookPayloadWithChunkPlan(params: {
}, params.timeoutMs ?? 15_000);
req.on("error", (error) => {
finishReject(error);
settle.reject(error);
});
const writeAll = async () => {
@ -251,7 +256,7 @@ async function postWebhookPayloadWithChunkPlan(params: {
};
void writeAll().catch((error) => {
finishReject(error);
settle.reject(error);
});
});
}