Testing webhook responses

Webhook pub/sub messaging allows the processing of inbound messages at scale. Testing your webhook handler is important for end-to-end integrations.

  • Table of contents

MailSlurp allows the creation of email webhooks that forward emails, attachments, and new contacts directly to your server URL via HTTP/S POST. When a server responds with a 200 or 201 within 30 seconds the webhook is marked as successfully processed. If not the webhook is marked as unsuccessful and is put into a queue to be retried again every few minutes until successfully processed.

How can we test this process

Manual endpoint testing

We could use a tool like ngrok to create HTTP endpoints that redirect to a local server we are running.

Automated endpoint testing

For automated testing we can use MailSlurp @mailslurp/test-webhooks library to setup real http endpoints that can respond with a set of responses we configure. This means we can simulate functional or non-functional webhooks and see how MailSlurp handles each.

Test webhook example

Let's see how to use the MailSlurp test webhooks library:

import fetchApi from 'isomorphic-fetch';
import { CreateWebhookOptionsEventNameEnum, MailSlurp } from 'mailslurp-client';
import {
  Configuration as TestWebhookConfiguration,
  CreateRulesetOptionsStrategyEnum,
  EndpointControllerApi,
} from '@mailslurp/test-webhooks';

jest.setTimeout(60000);
const apiKey = process.env.API_KEY!!;
const mailslurp = new MailSlurp({ apiKey, fetchApi });
const devhooksEndpointController = new EndpointControllerApi(
  new TestWebhookConfiguration({
    basePath: 'http://api.infrahooks.com',
    fetchApi,
  })
);

describe.skip('NEW_EMAIL webhooks', () => {
  test('can create NEW_EMAIL webhook and receive successfully', async () => {
    // create an inbox, webhook, and a test endpoint
    const testEndpoint = await devhooksEndpointController.createEndpoint({});
    const inbox = await mailslurp.createInbox();
    const webhook = await mailslurp.webhookController.createWebhook({
      createWebhookOptions: {
        eventName: CreateWebhookOptionsEventNameEnum.NEW_EMAIL,
        url: testEndpoint.url,
      },
      inboxId: inbox.id!,
    });
    // can see that endpoint has not received an event
    const endpointHistory = await devhooksEndpointController.getEndpointHistory(
      {
        endpointId: testEndpoint.id!,
      }
    );
    expect(endpointHistory.items?.length).toEqual(0);
    // send email to inbox
    await mailslurp.sendEmail(inbox.id!, {
      to: [inbox.emailAddress!],
      subject: 'email1',
    });
    // can fetch the email directly
    const email = await mailslurp.waitForLatestEmail(inbox.id!, 60000, true);
    expect(email.subject).toEqual('email1');
    // endpoint receives the payload (note the expected length to wait for)
    const endpointHistory2 =
      await devhooksEndpointController.getEndpointHistory({
        endpointId: testEndpoint.id!,
        expectedLength: 1,
      });
    expect(endpointHistory2.items?.length).toEqual(1);

    // assert correct payload was sent to endpoint
    const payload = JSON.parse(endpointHistory2.items?.[0]?.request?.body!);
    expect(payload.webhookId).toEqual(webhook.id);
    expect(payload.eventName).toEqual('NEW_EMAIL');
    expect(payload.inboxId).toEqual(inbox.id);
    expect(payload.emailId).toEqual(email.id);
    expect(payload.to).toEqual([inbox.emailAddress]);
    expect(payload.from).toEqual(inbox.emailAddress);
    expect(payload.subject).toEqual('email1');

    // can see webhook results via mailslurp
    const results = await mailslurp.webhookController.getWebhookResults({
      webhookId: webhook.id!,
    });
    expect(results.totalElements).toEqual(1);
    const result = await mailslurp.webhookController.getWebhookResult({
      webhookResultId: results.content?.[0]?.id!,
    });
    expect(result.resultType).toEqual('SUCCESS');
    expect(result.responseStatus).toEqual(200);

    await mailslurp.webhookController.deleteWebhook({
      inboxId: inbox.id!,
      webhookId: webhook.id!,
    });
  });
});

Testing failed webhooks

Now what about testing a failed result:

test.skip('can create NEW_EMAIL webhook and see failed results when endpoint fails to accept payload', async () => {
  // create a test endpoint that always returns a 401 error
  const testEndpoint = await devhooksEndpointController.createEndpoint({});
  await devhooksEndpointController.createEndpointRuleset({
    endpointId: testEndpoint.id!,
    createRulesetOptions: {
      strategy: CreateRulesetOptionsStrategyEnum.SINGULAR,
      responses: [
        {
          statusCode: 401,
        },
      ],
    },
  });

  // create inbox and webhook
  const inbox = await mailslurp.createInbox();
  const webhook = await mailslurp.webhookController.createWebhook({
    inboxId: inbox.id!,
    createWebhookOptions: {
      eventName: CreateWebhookOptionsEventNameEnum.NEW_EMAIL,
      url: testEndpoint.url,
    },
  });

  // send email to inbox
  await mailslurp.sendEmail(inbox.id!, {
    to: [inbox.emailAddress!],
    subject: 'email2',
  });

  // wait for endpoint to receive payload
  const endpointHistory = await devhooksEndpointController.getEndpointHistory({
    endpointId: testEndpoint.id!,
    expectedLength: 1,
  });
  expect(endpointHistory.items?.length).toEqual(1);

  // can see webhook results via mailslurp
  const results = await mailslurp.webhookController.getWebhookResults({
    webhookId: webhook.id!,
  });
  expect(results.totalElements).toEqual(1);
  const result = await mailslurp.webhookController.getWebhookResult({
    webhookResultId: results.content?.[0]?.id!,
  });
  expect(result.resultType).toEqual('BAD_RESPONSE');
  expect(result.responseStatus).toEqual(401);

  await mailslurp.webhookController.deleteWebhook({
    inboxId: inbox.id!,
    webhookId: webhook.id!,
  });
});