Skip to content
Latchkey

How to Test a Webhook Handler in CI

A webhook handler test must cover both a valid signature and a tampered one, so CI catches a broken verification path.

Load a fixture payload, compute the expected signature with the test secret, and assert the handler accepts it. Add a second case with a wrong signature that must be rejected.

Steps

  • Save a real payload as a fixture JSON file.
  • Compute the signature over the exact fixture bytes with the test secret.
  • Assert 2xx for the valid case and 401 for a tampered signature.

Test

handler.test.js
const crypto = require('crypto');
const { handler } = require('./handler');

const secret = 'test-secret';
const body = JSON.stringify({ action: 'opened' });
const sig = 'sha256=' + crypto.createHmac('sha256', secret).update(body).digest('hex');

test('accepts a valid signature', async () => {
  const res = await handler(body, { 'x-hub-signature-256': sig });
  expect(res.status).toBe(200);
});

test('rejects a tampered signature', async () => {
  const res = await handler(body, { 'x-hub-signature-256': 'sha256=deadbeef' });
  expect(res.status).toBe(401);
});

In CI

.github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test

Gotchas

  • Sign the exact bytes the handler will read; a trailing newline changes the digest.
  • Do not commit the production secret; use a dedicated test secret in the fixture.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →