██████╗ ██████╗ ██████╗ ███████╗██████╗
██╔═══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗
██║ ██║██████╔╝██║ ██║█████╗ ██████╔╝
██║ ██║██╔══██╗██║ ██║██╔══╝ ██╔══██╗
╚██████╔╝██║ ██║██████╔╝███████╗██║ ██║
╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═╝
███████╗███████╗███╗ ██╗████████╗██╗███╗ ██╗███████╗██╗
██╔════╝██╔════╝████╗ ██║╚══██╔══╝██║████╗ ██║██╔════╝██║
███████╗█████╗ ██╔██╗ ██║ ██║ ██║██╔██╗ ██║█████╗ ██║
╚════██║██╔══╝ ██║╚██╗██║ ██║ ██║██║╚██╗██║██╔══╝ ██║
███████║███████╗██║ ╚████║ ██║ ██║██║ ╚████║███████╗███████╗
╚══════╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝
Production-grade QA framework for Order & Profile Management Systems in prop trading.
Tests order lifecycle correctness, execution accuracy, and trading logic validity
across REST APIs, NATS message flows, and PostgreSQL state — built from the ground up.
In a prop trading firm, a bug in order execution logic isn't a UX issue — it's a financial loss event. order-sentinel is the testing layer that stands between your OMS/PMS codebase and production risk.
It validates:
- Order lifecycle correctness — every state transition from NEW → FILLED follows the rules
- Execution accuracy — weighted average fill prices are mathematically correct
- Risk enforcement — position limits, daily loss limits, and order size caps actually block bad orders
- Message contract integrity — NATS events published match the schemas consumers expect
- Time-in-force behaviour — IOC, FOK, and DAY orders expire correctly
Fully standalone — runs out of the box with npm test. No external services required.
| Layer | Technology | Why |
|---|---|---|
| Test runner | Vitest 4 | Native TypeScript, fast, great async support |
| Mock OMS | Fastify | Lightweight, fast, realistic in-process OMS |
| Message broker | NATS (primary) | Flagship JS client, low-latency, pub/sub |
| Broker abstraction | Custom BrokerAdapter |
Swap to Kafka/RabbitMQ via one env var |
| Schema validation | Zod | Compile-time + runtime contract safety |
| Infrastructure | Testcontainers | Real NATS in Docker for broker tests |
| HTTP client | Axios | Typed API client, non-throwing on 4xx/5xx |
| CI/CD | GitHub Actions | 6-stage pipeline, coverage gates |
tests/
├── contracts/ Schema validation — no infra needed
│ └── schema-validation.test.ts
│ ├── All API request/response schemas
│ └── All NATS event schemas
│
├── api/ REST API tests — uses built-in mock OMS
│ ├── order-lifecycle.test.ts Order CRUD, cancellation, fill retrieval
│ └── trader-profiles.test.ts Profile CRUD, risk limits, suspension
│
├── trading-logic/ Business rule validation — uses mock OMS
│ └── order-state-machine.test.ts
│ ├── State machine transitions (valid + invalid)
│ ├── Fill price accuracy (weighted average math)
│ ├── Risk check enforcement (position/daily loss/order count)
│ └── Time-in-force behavior (IOC, FOK, DAY expiry)
│
└── integration/ NATS pub/sub contract tests — Testcontainers
└── nats-message-flow.test.ts
├── orders.created event schema + field validation
├── orders.filled event with correct payload
├── Partial fill event collection (multi-fill orders)
├── orders.rejected with all rejection reason types
└── traders.limit.breached notification
- Node.js >= 20
- Docker (only for
test:integration— Testcontainers spins up NATS)
git clone https://github.com/your-username/order-sentinel.git
cd order-sentinel
npm install
cp .env.example .envnpm testThe mock OMS server starts automatically — no setup needed.
npm run test:contracts # Schema tests — no Docker, runs in <2s
npm run test:api # API tests — mock OMS, no Docker
npm run test:trading # Business logic — mock OMS, no Docker
npm run test:integration # NATS pub/sub — requires Docker for Testcontainersnpm run mock # Start mock OMS on port 3000 for manual testingnpm run test:coverage
# Coverage report at: coverage/index.html
# Thresholds: 80% lines/functions, 75% branchesorder-sentinel includes a built-in Fastify mock of an OMS that starts automatically before each test run. It enforces real trading business rules:
- Risk checks on every order: position limits, daily loss limits, symbol whitelists, trader suspension
- State machine enforcement: FILLED → NEW is rejected, CANCELLED → PENDING is rejected
/resetendpoint for clean test isolation betweenbeforeEachcalls?force=trueparam onPATCH /orders/:idfor direct state setup in tests
Mock OMS (Fastify)
┌──────────────────┐
test ──────► │ POST /orders │ Risk engine validates
│ GET /traders │ State machine enforced
│ DELETE /orders │ In-memory store
│ POST /reset │ Zero external deps
└──────────────────┘
The integration suite uses Testcontainers to spin up a real NATS server, then validates message schemas and pub/sub mechanics directly — no OMS involvement needed.
Testcontainers
┌──────────────────────────────────┐
│ nats:2.10-alpine (real Docker) │
│ │
│ publish ──► subscribe │
│ assert schema ✓ │
│ assert delivery ✓ │
└──────────────────────────────────┘
Every valid state transition is tested. Every invalid one is tested to be rejected.
NEW ──risk check──► PENDING ──partial fill──► PARTIALLY_FILLED
│ │ │
│ risk fails full fill
│ │ │
▼ ▼ ▼
CANCELLED REJECTED FILLED
(cancel from NEW, PENDING, or PARTIALLY_FILLED)
EXPIRED (time-in-force elapsed)
All tests are written against a BrokerAdapter interface. NATS is the primary implementation. Kafka and RabbitMQ adapters are interface-compatible — swap via one environment variable.
BROKER_TYPE=nats NATS_URL=nats://localhost:4222
BROKER_TYPE=kafka KAFKA_BROKERS=localhost:9092
BROKER_TYPE=rabbitmq RABBITMQ_URL=amqp://localhost:5672 push / PR
│
▼
┌─────────────────────────┐
│ 1. Lint + Typecheck │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ 2. Contract Tests │ No infrastructure — fastest gate
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ 3. API Tests │ Mock OMS auto-starts, no Docker
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ 4. Trading Logic │ Mock OMS auto-starts, no Docker
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ 5. Integration Tests │ Real NATS via Testcontainers (Docker)
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ 6. Coverage Report │
└─────────────────────────┘
Suites run sequentially — the mock OMS store is shared in-process, so parallel file execution would cause state bleed between test files.
- Architecture — system design, mock OMS routes, NATS subject map
- Mock OMS API — full REST reference with curl examples for manual testing
- Test Guide — deep dive into each suite, extension and contribution guide
- Adding Tests — quick reference for contributing new test scenarios
- Changelog — version history and roadmap
order-sentinel/
├── src/
│ ├── types.ts Core domain types
│ ├── broker/ Broker abstraction (NATS/Kafka/RabbitMQ)
│ ├── db/client.ts PostgreSQL client + repositories
│ ├── api/client.ts Typed HTTP client
│ ├── contracts/schemas.ts Zod schemas
│ └── fixtures/factories.ts Trading data factories
├── mock/
│ ├── server.ts Mock OMS (Fastify)
│ ├── store.ts In-memory state
│ └── risk.ts Risk engine + state machine
├── tests/ All test suites
├── migrations/runner.ts SQL migrations
├── infra/docker-compose.yml Local dev infrastructure
├── vitest.setup.ts Global test setup (starts mock OMS)
└── .github/workflows/ci.yml GitHub Actions CI
Built to validate prop trading systems at the level they deserve.
If order-sentinel has saved you from a bad deploy, a ⭐ on GitHub tells the next engineer it's worth their time.