npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

haraka-test-fixtures

v1.7.2

Published

Haraka Test Fixtures

Readme

Test Cover Qlty

haraka-test-fixtures

Fixtures for testing Haraka and plugins

Usage

const fixtures = require('haraka-test-fixtures')

Quick-start

const { beforeEach, describe, it } = require('node:test')
const { makePlugin, makeConnection, callMail, callRcpt, assertDeny, assertResult } = require('haraka-test-fixtures')

describe('my-plugin', () => {
  let plugin, conn

  beforeEach(() => {
    plugin = makePlugin('my-plugin', { configDir: __dirname })
    conn = makeConnection({ ip: '1.2.3.4', mailFrom: '[email protected]' })
  })

  it('rejects relay attempts', async () => {
    const r = await callRcpt(plugin, conn, '[email protected]')
    assertDeny(r, /relay/)
    assertResult(conn, plugin, 'fail', /^no_relay/)
  })
})

That's the whole pattern: makePlugin + makeConnection + a typed call<Hook> + optional assert<...> helpers. Compose into domain-specific helpers per test file when it pays off (see plugin/aliases/test/index.js for a good example).

Exports

  • connection
  • dns
  • helpers, hooks, assertions (see below)
  • line_socket
  • logger
  • plugin
  • results
  • stub
  • transaction
  • util_hmailitem

These fixtures are analogs of their like-named siblings in Haraka with varying levels of completeness. If there are functions necessary to enhance your ability to test, please do add them.


helpers — makePlugin, makeConnection, getResult, callHook

makePlugin(name, opts?) → Plugin

makePlugin('helo.checks') // + register()
makePlugin('helo.checks', { register: false }) // skip register()
makePlugin('access', { configDir: __dirname }) // override plugin.config root

configDir runs plugin.config.module_config(path.resolve(configDir)) before register(), replacing the very common idiom plugin.config = plugin.config.module_config(path.resolve('test')).

makeConnection(opts?) → Connection

| Option | Default | Description | | ---------- | ------------- | ------------------------------------------------------------------------------------------------- | | ip | '127.0.0.1' | remote.ip | | relaying | false | connection._relaying | | helo | (unset) | sets hello.host + hello.verb = 'EHLO' | | withTxn | false | calls init_transaction() | | mailFrom | — | string | Address; coerced and assigned to transaction.mail_from. Implies a transaction. | | rcptTo | — | array of string | Address; coerced and assigned to transaction.rcpt_to. Implies a transaction. | | notes | — | object merged into connection.notes | | txNotes | — | object merged into transaction.notes. Implies a transaction. | | server | {} | becomes connection.server; use for server.notes.* injection (e.g. shared redis handle). | | host | — | remote.host (rDNS hostname) | | silent | false | replace every log method (loginfo, logwarn, etc.) with a no-op | | stubLogs | false | replace every log method with a recording stub(); assert on conn.loginfo.args etc. |

const conn = makeConnection({
  ip: '1.2.3.4',
  mailFrom: '[email protected]',
  rcptTo: ['[email protected]'],
  txNotes: { spamd_user: 'tx' },
})

getResult(connection, pluginOrName) → result object

Returns connection.results.get(plugin) with an empty-bucket fallback so callers can read .pass.length etc. without null checks.

callHook(plugin, method, connection, ...args)Promise<{rc, msg}>

The generic Promise-wrap of a hook callback. Prefer the typed callXxx helpers below when one fits — they're more readable and resolve the method name automatically. Reach for callHook for hooks without a typed wrapper, or to target a specific method by name.


hooks — typed call<Hook> helpers

Each helper accepts natural args (string addresses instead of [new Address('<x>'), {}]), resolves the method name automatically via plugin.hooks[hookName] (with hook_<name> and bare <name> fallbacks), and returns Promise<{rc, msg}>.

const { rc, msg } = await callMail(plugin, conn, '[email protected]')
const { rc } = await callRcpt(plugin, conn, '[email protected]')
const { rc } = await callConnect(plugin, conn)
const { rc } = await callHelo(plugin, conn, 'mail.example.com')
const { rc } = await callDataPost(plugin, conn)

| Helper | Hook | Hook args | | ------------------------------- | ------------ | ------------------------------ | | callConnect(p, c) | connect | (next, c) | | callHelo(p, c, host) | helo | (next, c, host) | | callEhlo(p, c, host) | ehlo | (next, c, host) | | callMail(p, c, from, params?) | mail | (next, c, [Address, params]) | | callRcpt(p, c, rcpt, params?) | rcpt | (next, c, [Address, params]) | | callRcptOk(p, c, rcpt) | rcpt_ok | (next, c, Address) | | callData(p, c) | data | (next, c) | | callDataPost(p, c) | data_post | (next, c) | | callQueue(p, c) | queue | (next, c) | | callQueueOk(p, c, msg) | queue_ok | (next, c, msg) | | callDisconnect(p, c) | disconnect | (next, c) |

All callXxx helpers (and callHook) accept an explicit method-name override as their last argument — useful when a plugin registers multiple methods on one hook.

Forward-compat with Haraka 4.0 promise hooks

The dispatcher under the hood accepts both the 3.x callback style and an async/return-value style anticipated for 4.0. A plugin author can write either:

// 3.x
exports.hook_mail = function (next, connection, params) {
  next(DENY, 'no thanks')
}

// 4.0 (any of these forms work today through the test helpers)
exports.hook_mail = async function (connection, params) {
  return DENY // bare rc
}
exports.hook_mail = async function (connection, params) {
  return { rc: DENY, msg: 'no thanks' }
}
exports.hook_mail = async function (connection, params) {
  return [DENY, 'no thanks']
}

…and the test code stays identical:

const r = await callMail(plugin, conn, '[email protected]')
assertDeny(r, /no thanks/)

When Haraka 4 ships, the test suites you write today migrate without changes.


assertions

Small wrappers around the most common assertion patterns. All accept either a raw rc or the {rc, msg} result object returned by callXxx, so destructuring is optional.

const { assertCont, assertOk, assertDeny, assertResult } = require('haraka-test-fixtures')

assertCont(r) // rc is undefined or CONT
assertOk(r, /accepted/) // rc === OK + optional msg match
assertDeny(r) // rc is any DENY* code
assertDeny(r, /no thanks/) //   + msg matches
assertDeny(r, /no thanks/, DENYSOFT) //   + specific code

assertResult(conn, plugin, 'pass') // results.get(plugin).pass non-empty
assertResult(conn, plugin, 'pass', /^connect:/)

Anything more specific (e.g. "asserts an alias mapped to the right address") belongs as a test-local helper, not in this library. See plugin/aliases/test/index.js for the pattern.


stub

fixtures.stub creates a stub function that records all calls.

const { stub } = require('haraka-test-fixtures')

const s = stub('return-value')
s('arg1', 'arg2')

s.called // true
s.callCount // 1
s.args // ['arg1', 'arg2']  (last call)
s.calls // [['arg1', 'arg2']] (every call)
s.reset() // clears all tracking state

dns

A tiny, zero-dependency test DNS server (UDP) for exercising the real node:dnsharaka-net-utils → plugin path. Configure per-name records and failure modes, then point a resolver at it.

const fixtures = require('haraka-test-fixtures')

const dns = await fixtures.dns.start({
  'good.example': {
    a: ['1.2.3.4'],
    mx: [{ preference: 10, exchange: 'mx.good.example' }],
  },
  'broken.example': { rcode: 'SERVFAIL' }, // also NXDOMAIN | REFUSED | NOERROR
  'slow.example': { a: ['1.2.3.4'], delayMs: 200 },
  'gone.example': { drop: true }, // never answers -> resolver timeout
})

// a fresh node:dns Resolver pointed at the test server
const resolver = dns.resolver({ timeout: 500 })
await resolver.resolveMx('good.example')

// or point an existing Resolver / haraka-net-utils dns_config at it:
const restore = dns.patch(require('haraka-net-utils/lib/dns_config'))
// ... run code that calls net_utils.get_mx() ...
restore()

dns.setZone('good.example', { rcode: 'SERVFAIL' }) // mutate at runtime
await dns.close()