mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 06:37:28 +00:00
refactor: share test request helpers
This commit is contained in:
@@ -27,6 +27,28 @@ function createMockFetch(response?: { status?: number; body?: unknown; contentTy
|
|||||||
return { mockFetch: mockFetch as unknown as typeof fetch, calls };
|
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 ────────────────────────────────────────
|
// ── normalizeMattermostBaseUrl ────────────────────────────────────────
|
||||||
|
|
||||||
describe("normalizeMattermostBaseUrl", () => {
|
describe("normalizeMattermostBaseUrl", () => {
|
||||||
@@ -229,68 +251,38 @@ describe("createMattermostPost", () => {
|
|||||||
|
|
||||||
describe("updateMattermostPost", () => {
|
describe("updateMattermostPost", () => {
|
||||||
it("sends PUT to /posts/{id}", async () => {
|
it("sends PUT to /posts/{id}", async () => {
|
||||||
const { mockFetch, calls } = createMockFetch({ body: { id: "post1" } });
|
const { calls } = await updatePostAndCapture({ message: "Updated" });
|
||||||
const client = createMattermostClient({
|
|
||||||
baseUrl: "http://localhost:8065",
|
|
||||||
botToken: "tok",
|
|
||||||
fetchImpl: mockFetch,
|
|
||||||
});
|
|
||||||
|
|
||||||
await updateMattermostPost(client, "post1", { message: "Updated" });
|
|
||||||
|
|
||||||
expect(calls[0].url).toContain("/posts/post1");
|
expect(calls[0].url).toContain("/posts/post1");
|
||||||
expect(calls[0].init?.method).toBe("PUT");
|
expect(calls[0].init?.method).toBe("PUT");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes post id in the body", async () => {
|
it("includes post id in the body", async () => {
|
||||||
const { mockFetch, calls } = createMockFetch({ body: { id: "post1" } });
|
const { body } = await updatePostAndCapture({ message: "Updated" });
|
||||||
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);
|
|
||||||
expect(body.id).toBe("post1");
|
expect(body.id).toBe("post1");
|
||||||
expect(body.message).toBe("Updated");
|
expect(body.message).toBe("Updated");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes props for button completion updates", async () => {
|
it("includes props for button completion updates", async () => {
|
||||||
const { mockFetch, calls } = createMockFetch({ body: { id: "post1" } });
|
const { body } = await updatePostAndCapture({
|
||||||
const client = createMattermostClient({
|
|
||||||
baseUrl: "http://localhost:8065",
|
|
||||||
botToken: "tok",
|
|
||||||
fetchImpl: mockFetch,
|
|
||||||
});
|
|
||||||
|
|
||||||
await updateMattermostPost(client, "post1", {
|
|
||||||
message: "Original message",
|
message: "Original message",
|
||||||
props: {
|
props: {
|
||||||
attachments: [{ text: "✓ **do_now** selected by @tony" }],
|
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.message).toBe("Original message");
|
||||||
expect(body.props.attachments[0].text).toContain("✓");
|
expect(body.props).toMatchObject({
|
||||||
expect(body.props.attachments[0].text).toContain("do_now");
|
attachments: [{ text: expect.stringContaining("✓") }],
|
||||||
|
});
|
||||||
|
expect(body.props).toMatchObject({
|
||||||
|
attachments: [{ text: expect.stringContaining("do_now") }],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("omits message when not provided", async () => {
|
it("omits message when not provided", async () => {
|
||||||
const { mockFetch, calls } = createMockFetch({ body: { id: "post1" } });
|
const { body } = await updatePostAndCapture({
|
||||||
const client = createMattermostClient({
|
|
||||||
baseUrl: "http://localhost:8065",
|
|
||||||
botToken: "tok",
|
|
||||||
fetchImpl: mockFetch,
|
|
||||||
});
|
|
||||||
|
|
||||||
await updateMattermostPost(client, "post1", {
|
|
||||||
props: { attachments: [] },
|
props: { attachments: [] },
|
||||||
});
|
});
|
||||||
|
|
||||||
const body = JSON.parse(calls[0].init?.body as string);
|
|
||||||
expect(body.id).toBe("post1");
|
expect(body.id).toBe("post1");
|
||||||
expect(body.message).toBeUndefined();
|
expect(body.message).toBeUndefined();
|
||||||
expect(body.props).toEqual({ attachments: [] });
|
expect(body.props).toEqual({ attachments: [] });
|
||||||
|
|||||||
@@ -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(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
@@ -54,16 +75,9 @@ describe("uploadImageFromUrl", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("fetches image and calls uploadFile, returns uploaded URL", async () => {
|
it("fetches image and calls uploadFile, returns uploaded URL", async () => {
|
||||||
const { mockFetch, mockUploadFile, uploadImageFromUrl } = await loadUploadMocks();
|
const { mockBlob, mockUploadFile, uploadImageFromUrl } = await setupSuccessfulUpload({
|
||||||
|
uploadedUrl: "https://memex.tlon.network/uploaded.png",
|
||||||
const mockBlob = new Blob(["fake-image"], { type: "image/png" });
|
|
||||||
mockSuccessfulFetch({
|
|
||||||
mockFetch,
|
|
||||||
blob: mockBlob,
|
|
||||||
finalUrl: "https://example.com/image.png",
|
|
||||||
contentType: "image/png",
|
|
||||||
});
|
});
|
||||||
mockUploadFile.mockResolvedValue({ url: "https://memex.tlon.network/uploaded.png" });
|
|
||||||
|
|
||||||
const result = await uploadImageFromUrl("https://example.com/image.png");
|
const result = await uploadImageFromUrl("https://example.com/image.png");
|
||||||
|
|
||||||
@@ -95,15 +109,7 @@ describe("uploadImageFromUrl", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns original URL if upload fails", async () => {
|
it("returns original URL if upload fails", async () => {
|
||||||
const { mockFetch, mockUploadFile, uploadImageFromUrl } = await loadUploadMocks();
|
const { mockUploadFile, uploadImageFromUrl } = await setupSuccessfulUpload();
|
||||||
|
|
||||||
const mockBlob = new Blob(["fake-image"], { type: "image/png" });
|
|
||||||
mockSuccessfulFetch({
|
|
||||||
mockFetch,
|
|
||||||
blob: mockBlob,
|
|
||||||
finalUrl: "https://example.com/image.png",
|
|
||||||
contentType: "image/png",
|
|
||||||
});
|
|
||||||
mockUploadFile.mockRejectedValue(new Error("Upload failed"));
|
mockUploadFile.mockRejectedValue(new Error("Upload failed"));
|
||||||
|
|
||||||
const result = await uploadImageFromUrl("https://example.com/image.png");
|
const result = await uploadImageFromUrl("https://example.com/image.png");
|
||||||
|
|||||||
@@ -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", () => {
|
describe("TwilioProvider", () => {
|
||||||
it("returns streaming TwiML for outbound conversation calls before in-progress", () => {
|
it("returns streaming TwiML for outbound conversation calls before in-progress", () => {
|
||||||
const provider = createProvider();
|
const provider = createProvider();
|
||||||
@@ -30,9 +36,7 @@ describe("TwilioProvider", () => {
|
|||||||
|
|
||||||
const result = provider.parseWebhookEvent(ctx);
|
const result = provider.parseWebhookEvent(ctx);
|
||||||
|
|
||||||
expect(result.providerResponseBody).toContain(STREAM_URL);
|
expectStreamingTwiml(result.providerResponseBody);
|
||||||
expect(result.providerResponseBody).toContain('<Parameter name="token" value="');
|
|
||||||
expect(result.providerResponseBody).toContain("<Connect>");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns empty TwiML for status callbacks", () => {
|
it("returns empty TwiML for status callbacks", () => {
|
||||||
@@ -55,9 +59,7 @@ describe("TwilioProvider", () => {
|
|||||||
|
|
||||||
const result = provider.parseWebhookEvent(ctx);
|
const result = provider.parseWebhookEvent(ctx);
|
||||||
|
|
||||||
expect(result.providerResponseBody).toContain(STREAM_URL);
|
expectStreamingTwiml(result.providerResponseBody);
|
||||||
expect(result.providerResponseBody).toContain('<Parameter name="token" value="');
|
|
||||||
expect(result.providerResponseBody).toContain("<Connect>");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns queue TwiML for second inbound call when first call is active", () => {
|
it("returns queue TwiML for second inbound call when first call is active", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user