Redis

@crvouga/mockingbird-service-redis

In-process Redis RESP server for tests: strings, hashes, lists, sets, sorted sets, streams, transactions, pub/sub, and a Lua subset, with an injected clock. Not an HTTP emulator.

Parity: RESP replies, expiration, transactions, and streamsDatabasesNode servermockingbird-redis
$
npm install -D @crvouga/mockingbird-service-redis

Documentation

The package README, the same file that ships in the npm tarball.Edit on GitHub

Local emulators. Real API contracts. Part of Mockingbird.

In-process Redis for tests. createRedis() is a pure TypeScript RESP store: call commands on redis.client(), or speak RESP to the TCP server (mockingbird-redis, or serve from @crvouga/mockingbird-service-redis/server). It is not an HTTP API emulator and has no createRuntime. ./server also exports serveTarget, so serve --config can boot it: GET /__admin/health reports service: redis, and RESP listens on an ephemeral redis://127.0.0.1:<port> printed at startup. mockingbird-redis still binds 6379 unless --port is set.

Replies follow the Redis command reference for a single standalone node and were spot-checked against Redis 8.4.0. The advertised version is Redis 7.2.4 so clients do not probe Redis 8 modules. This package is a work in progress.

Install

Shell
npm install -D @crvouga/mockingbird-service-redis

Usage

TypeScript
import { Redis } from "ioredis"
import { createRedis, manualClock } from "@crvouga/mockingbird-service-redis"
import { serve } from "@crvouga/mockingbird-service-redis/server"

// Test setup: an emulator on a manual clock, listening on a free port. `as never` is needed
// because the two entry points bundle separate declarations of the `Redis` type.
const emulator = createRedis({ clock: manualClock(1_700_000_000_000) })
const server = await serve(emulator as never, { port: 0 })

// Application code: the ioredis client it already uses, connected to the emulator.
const redis = new Redis({ host: server.host, port: server.port })
console.log(await redis.set("lock", "token", "PX", 1000)) // OK

// Test control: expire the lock without sleeping.
emulator.advance(1000)
console.log(await redis.get("lock")) // null

await redis.quit()
await server.close()

Two createRedis() instances never share keys. SELECT isolates logical databases 0–15 inside one instance. emulator.advance(ms) moves an injected clock. emulator.inspect(), emulator.fault(), emulator.pauseConsumers(), emulator.reset(), and emulator.waiterCount() are the test controls.

ioredis connects to the TCP server unchanged. Cluster MOVED / ASK is not implemented: CLUSTER and READONLY answer that cluster support is disabled.

mockingbird-redis serve --config mockingbird.json supervises mixed HTTP/Postgres/Redis fleets. See fleet configuration and controls for ephemeral discovery, namespace endpoints, readiness, shutdown and coordinated state controls.

API

  • createRedis(options?) returns a Redis instance.
  • Redis holds the store, the clock, and the test controls (client, advance, fault, inspect, pauseConsumers, reset, waiterCount, close).
  • RedisClient is one connection: call, raw, pipeline, quit, onPush, and protocol.
  • RedisPipeline queues calls and runs them in exec.
  • RedisConnectionError is a dropped connection, a timeout fault, or a closed server.
  • RedisReplyError is a Redis error reply raised by call.
  • manualClock(start) moves only when advance is called. wallClock follows Date.now().
  • encodeReply(reply, protocol) writes a RESP2 or RESP3 frame.

Deliberately not modelled

  • Cluster redirects (MOVED, ASK) and replica replication. ROLE stays master.
  • Redis 8 modules, and the full Redis command set. ACL SETUSER/GETUSER/DELUSER/CAT/LOG/ GENPASS/DRYRUN/LOAD/SAVE, MEMORY DOCTOR/STATS/MALLOC-STATS, COMMAND GETKEYSANDFLAGS and COMMAND HELP return an explicit "not supported by the Mockingbird Redis emulator" error.
  • Best guess, not oracle-verified: MEMORY USAGE byte counts, OBJECT ENCODING thresholds, the HSCAN/SSCAN/ZSCAN cursor of collections too large for a compact encoding, and a SLOWLOG that never records. MONITOR is supported; see SUPPORT.md.
  • Lua coroutines (coroutine.* exists and raises an explicit "not supported" error), redis.setresp(3) and a script time limit (SCRIPT KILL cannot interrupt a script that never returns). The rest of Lua 5.1 as Redis ships it (standard libraries, cjson, cmsgpack, struct, bit) is implemented and differentially checked against redis-server 8.4.0; see SUPPORT.md. BullMQ 5.67.1 queue/worker flows are covered by the public-client suite.
  • XADD / XTRIM MAXLEN ~ trims exactly.
  • Set members are returned in byte order so results stay deterministic.
↑↓ to navigate↵ to open