refactor: share test request helpers

This commit is contained in:
Peter Steinberger
2026-03-13 20:25:08 +00:00
parent 28a49aaa34
commit 48853f875b
3 changed files with 64 additions and 64 deletions

View File

@@ -27,6 +27,28 @@ function createMockFetch(response?: { status?: number; body?: unknown; contentTy
return { mockFetch: mockFetch as unknown as typeof fetch, calls };
}
function createTestClient(response?: { status?: number; body?: unknown; contentType?: string }) {
const { mockFetch, calls } = createMockFetch(response);
const client = createMattermostClient({
baseUrl: "http://localhost:8065",
botToken: "tok",
fetchImpl: mockFetch,
});
return { client, calls };
}
async function updatePostAndCapture(
update: Parameters<typeof updateMattermostPost>[2],
response?: { status?: number; body?: unknown; contentType?: string },
) {
const { client, calls } = createTestClient(response ?? { body: { id: "post1" } });
await updateMattermostPost(client, "post1", update);
return {
calls,
body: JSON.parse(calls[0].init?.body as string) as Record<string, unknown>,
};
}
// ── normalizeMattermostBaseUrl ────────────────────────────────────────
describe("normalizeMattermostBaseUrl", () => {
@@ -229,68 +251,38 @@ describe("createMattermostPost", () => {
describe("updateMattermostPost", () => {
it("sends PUT to /posts/{id}", async () => {
const { mockFetch, calls } = createMockFetch({ body: { id: "post1" } });
const client = createMattermostClient({
baseUrl: "http://localhost:8065",
botToken: "tok",
fetchImpl: mockFetch,
});
await updateMattermostPost(client, "post1", { message: "Updated" });
const { calls } = await updatePostAndCapture({ message: "Updated" });
expect(calls[0].url).toContain("/posts/post1");
expect(calls[0].init?.method).toBe("PUT");
});
it("includes post id in the body", async () => {
const { mockFetch, calls } = createMockFetch({ body: { id: "post1" } });
const client = createMattermostClient({
baseUrl: "http://localhost:8065",
botToken: "tok",
fetchImpl: mockFetch,
});
await updateMattermostPost(client, "post1", { message: "Updated" });
const body = JSON.parse(calls[0].init?.body as string);
const { body } = await updatePostAndCapture({ message: "Updated" });
expect(body.id).toBe("post1");
expect(body.message).toBe("Updated");
});
it("includes props for button completion updates", async () => {
const { mockFetch, calls } = createMockFetch({ body: { id: "post1" } });
const client = createMattermostClient({
baseUrl: "http://localhost:8065",
botToken: "tok",
fetchImpl: mockFetch,
});
await updateMattermostPost(client, "post1", {
const { body } = await updatePostAndCapture({
message: "Original message",
props: {
attachments: [{ text: "✓ **do_now** selected by @tony" }],
},
});
const body = JSON.parse(calls[0].init?.body as string);
expect(body.message).toBe("Original message");
expect(body.props.attachments[0].text).toContain("✓");
expect(body.props.attachments[0].text).toContain("do_now");
expect(body.props).toMatchObject({
attachments: [{ text: expect.stringContaining("✓") }],
});
expect(body.props).toMatchObject({
attachments: [{ text: expect.stringContaining("do_now") }],
});
});
it("omits message when not provided", async () => {
const { mockFetch, calls } = createMockFetch({ body: { id: "post1" } });
const client = createMattermostClient({
baseUrl: "http://localhost:8065",
botToken: "tok",
fetchImpl: mockFetch,
});
await updateMattermostPost(client, "post1", {
const { body } = await updatePostAndCapture({
props: { attachments: [] },
});
const body = JSON.parse(calls[0].init?.body as string);
expect(body.id).toBe("post1");
expect(body.message).toBeUndefined();
expect(body.props).toEqual({ attachments: [] });

View File

@@ -45,6 +45,27 @@ describe("uploadImageFromUrl", () => {
});
}
async function setupSuccessfulUpload(params?: {
sourceUrl?: string;
contentType?: string;
uploadedUrl?: string;
}) {
const { mockFetch, mockUploadFile, uploadImageFromUrl } = await loadUploadMocks();
const sourceUrl = params?.sourceUrl ?? "https://example.com/image.png";
const contentType = params?.contentType ?? "image/png";
const mockBlob = new Blob(["fake-image"], { type: contentType });
mockSuccessfulFetch({
mockFetch,
blob: mockBlob,
finalUrl: sourceUrl,
contentType,
});
if (params?.uploadedUrl) {
mockUploadFile.mockResolvedValue({ url: params.uploadedUrl });
}
return { mockBlob, mockUploadFile, uploadImageFromUrl };
}
beforeEach(() => {
vi.clearAllMocks();
});
@@ -54,16 +75,9 @@ describe("uploadImageFromUrl", () => {
});
it("fetches image and calls uploadFile, returns uploaded URL", async () => {
const { mockFetch, mockUploadFile, uploadImageFromUrl } = await loadUploadMocks();
const mockBlob = new Blob(["fake-image"], { type: "image/png" });
mockSuccessfulFetch({
mockFetch,
blob: mockBlob,
finalUrl: "https://example.com/image.png",
contentType: "image/png",
const { mockBlob, mockUploadFile, uploadImageFromUrl } = await setupSuccessfulUpload({
uploadedUrl: "https://memex.tlon.network/uploaded.png",
});
mockUploadFile.mockResolvedValue({ url: "https://memex.tlon.network/uploaded.png" });
const result = await uploadImageFromUrl("https://example.com/image.png");
@@ -95,15 +109,7 @@ describe("uploadImageFromUrl", () => {
});
it("returns original URL if upload fails", async () => {
const { mockFetch, mockUploadFile, uploadImageFromUrl } = await loadUploadMocks();
const mockBlob = new Blob(["fake-image"], { type: "image/png" });
mockSuccessfulFetch({
mockFetch,
blob: mockBlob,
finalUrl: "https://example.com/image.png",
contentType: "image/png",
});
const { mockUploadFile, uploadImageFromUrl } = await setupSuccessfulUpload();
mockUploadFile.mockRejectedValue(new Error("Upload failed"));
const result = await uploadImageFromUrl("https://example.com/image.png");

View File

@@ -21,6 +21,12 @@ function createContext(rawBody: string, query?: WebhookContext["query"]): Webhoo
};
}
function expectStreamingTwiml(body: string) {
expect(body).toContain(STREAM_URL);
expect(body).toContain('<Parameter name="token" value="');
expect(body).toContain("<Connect>");
}
describe("TwilioProvider", () => {
it("returns streaming TwiML for outbound conversation calls before in-progress", () => {
const provider = createProvider();
@@ -30,9 +36,7 @@ describe("TwilioProvider", () => {
const result = provider.parseWebhookEvent(ctx);
expect(result.providerResponseBody).toContain(STREAM_URL);
expect(result.providerResponseBody).toContain('<Parameter name="token" value="');
expect(result.providerResponseBody).toContain("<Connect>");
expectStreamingTwiml(result.providerResponseBody);
});
it("returns empty TwiML for status callbacks", () => {
@@ -55,9 +59,7 @@ describe("TwilioProvider", () => {
const result = provider.parseWebhookEvent(ctx);
expect(result.providerResponseBody).toContain(STREAM_URL);
expect(result.providerResponseBody).toContain('<Parameter name="token" value="');
expect(result.providerResponseBody).toContain("<Connect>");
expectStreamingTwiml(result.providerResponseBody);
});
it("returns queue TwiML for second inbound call when first call is active", () => {