Skip to content
Payment Emulator LabDocumentationOpen console

Operations and validation

Test Payment Emulator Lab

The API workspace uses Vitest 4. The suite combines pure unit tests, propertytests, NestJS/Supertest integration tests, provider conformance and official-SDKcompatibility checks. T

4 min read

Índice / Index · Español

The API workspace uses Vitest 4. The suite combines pure unit tests, property tests, NestJS/Supertest integration tests, provider conformance and official-SDK compatibility checks. The Nuxt applications currently have no standalone browser test suite; they are protected by strict typecheck, production builds and the whole-stack smoke.

#Commands

Run the complete workspace suite:

bash
npm test

The root delegates to each workspace with a test script. At present only @payment-emulator/api defines one. Scenario JSON is compiled automatically by the API's pretest script.

Run one API file or use watch mode:

bash
npm test --workspace @payment-emulator/api -- test/money.spec.ts
npm run test:watch --workspace @payment-emulator/api

Run the broader verification gates:

bash
npm run docs:check
npm run typecheck
npm run lint
npm run build
npm run smoke

No coverage command or coverage threshold is configured. Do not claim a coverage percentage from the installed coverage package alone.

#Test layers

Layer Examples What it proves
Pure/unit money.spec.ts, scenario-matcher.spec.ts, credentials.spec.ts Deterministic transformations without infrastructure
Property ledger-invariants.spec.ts Fees and arbitrary posting sets preserve accounting invariants
Static conformance conformance.spec.ts Every plugin declares total states/errors, valid routes and verifiable signatures
Integration test/integration/*.spec.ts Real Nest app, HTTP, PostgreSQL migrations, transactions, roles and concurrency
Official SDK stripe-sdk.spec.ts, mercadopago-sdk.spec.ts, integration/paypal.spec.ts Provider libraries accept selected wire behaviour/signatures
Contract test/integration/webhook-queue.spec.ts One suite run against every configured webhook transport, so the drivers stay alternatives rather than lookalikes
Whole-stack smoke scripts/stack-smoke.mjs HTTP flows through the API and both Nitro apps, with ledger checks; does not independently prove worker delivery

#Integration database

Integration files use describe.skipIf(!hasDatabase). Without DATABASE_URL, npm test still runs the pure and static suites and reports database tests as skipped. To run everything locally, expose a disposable PostgreSQL URL:

powershell
$env:DATABASE_URL = 'postgresql://payment:payment@localhost:54329/payment_emulator_test'
npm test

A driver is skipped when the thing it needs is not configured, the same rule hasDatabase applies: MONGO_URL for mongo, REDIS_URL for bullmq. The postgres transport runs whenever the database does, which is the point of it.

The harness in apps/api/test/integration/harness.ts builds the real AppModule, calls the same configureApp() as production and applies migrations before the tests. Integration files are serialized by vitest.config.ts because they share one database and would otherwise race to initialize migration metadata.

Use a test database, not a developer database containing data worth keeping. Tests create and remove application state through real endpoints and migrations — though not all of them clean up: several specs leave applications behind, and the harness's own Harness ARS/Harness USD people are never removed. Pointing the suite at a database whose console you are about to look at will fill its listings.

#Conformance suites

apps/api/test/conformance.spec.ts iterates every registered plugin for static declarations. apps/api/test/integration/conformance.spec.ts runs the same behavioural guarantees for Mercado Pago and Stripe:

  • replay for the same idempotency key and body;
  • provider-shaped conflict for a different body;
  • one resource under concurrent identical requests;
  • one refund under concurrent retries;
  • authorization separated from capture;
  • balanced accounting and partial/full refund behaviour;
  • deterministic scenario faults and state projection.

Add a provider to these parameterized cases. A separate bespoke happy-path test does not replace conformance.

#Official SDK tests

The installed test SDKs are currently stripe@22.6.0 and mercadopago@3.6.0 (resolved from the lockfile).

  • Stripe can target a custom host/port, so stripe-sdk.spec.ts drives the live Nest test server through the unmodified SDK. Telemetry is disabled to preserve the no-provider-network guarantee.
  • Mercado Pago's SDK cannot target the emulator for requests, but its stateless webhook validator can verify emulator signatures. That limitation is explicit in docs/en/fidelity/mercadopago.md.

#Writing a useful test

  1. Choose the narrowest layer that can fail for the rule.
  2. Reuse startEmulator(), operator(), anonymous() and fundingSource() for integration setup.
  3. For concurrency rules, actually overlap several calls; two requests can miss each other by timing and make a broken lock look correct.
  4. Assert provider-shaped output on provider routes and emulator vocabulary on control/checkout routes.
  5. For authorization, include the wrong role and a different tenant, not only a missing token.
  6. For ledger writes, assert debit equals credit and inspect the economically relevant account balances.

#CI

.github/workflows/ci.yml has two jobs:

  • checks: installs from lockfile, then typechecks, lints, tests, builds and runs the pure smoke against PostgreSQL 17.
  • stack: builds the Compose stack, waits for health checks, executes scripts/stack-smoke.mjs, prints logs and removes its volumes.

The stack smoke covers console login, application registration through Nitro, anonymous checkout payment, balance-payment refusal without a session, balanced ledger settlement and a Stripe form-encoded/manual-capture/refund flow.

The stack smoke also exercises PayPal OAuth, approval and merchant capture. A successful smoke does not verify every optional webhook transport or prove that a worker delivered an event; use the queue integration tests for those paths.

Payment Emulator Lab · RonuSoftwareMIT