Skip to main content

Testing

Philosophy

  • Black-box. Assert on inputs and outputs, not internals. A refactor that doesn't change behavior shouldn't break tests.
  • No mocks unless necessary. Prefer real implementations (real DB via vitest-pool-workers, real HTTP). Only mock external services we don't control (Infinia, Apple / Google JWKS).
  • Focus on I/O boundaries. API request/response, function I/O, component render + interaction. Don't test private functions or intermediate state.
  • Non-brittle assertions. Don't snapshot markup or match exact error strings.

What runs where

Running tests

# All packages
pnpm test

# One package
pnpm --filter @gringo-pay/api test
pnpm --filter @gringo-pay/mobile test

# Coverage
pnpm test:coverage

# Mobile E2E (needs a running dev build + Maestro CLI)
pnpm test:e2e

API tests

  • Runner: Vitest + @cloudflare/vitest-pool-workers.
  • Fixtures in apps/api/src/test/:
    • app.tscreateTestApp() returns the Hono app + a scoped DB.
    • client.tsTestClient wraps requests with a valid session JWT header (issued using the test SESSION_SECRET).
    • factories.tsseedUser(), seedProviderAccount(), seedKycVerification(), etc.
    • infinia-mock.ts — mock Infinia API responses. HTTP mocking at the fetch boundary; each Infinia endpoint we depend on has a handler.
    • oauth-mock.ts — mock Apple / Google JWKS endpoints so POST /auth/session can verify test id_tokens.
  • Requires a local Postgres. Start it with cd apps/api && docker compose up -d --wait, then pnpm test. Tests reach it through the Hyperdrive binding and rebuild the schema from migrations on every run (pgcrypto is enabled by migration 0009). Override the default connection with TEST_DATABASE_URL if needed. See testing-db-strategy.

Colocation: apps/api/src/pix/routes.test.ts lives next to apps/api/src/pix/routes.ts.

Testing encrypted columns

Tests write to the same encrypted columns as production (kyc_verifications.verified_identity_encrypted, user_bank_details.*_encrypted) using a test-only PII_ENCRYPTION_KEY. Factories handle the encrypt / decrypt transparently — don't reach around them.

Mobile tests

  • Runner: Jest with jest-expo preset + @testing-library/react-native.
  • Metro's blockList excludes *.test.* from bundles.

E2E (Maestro)

  • Scenarios in maestro/*.yaml (e.g. maestro/smoke.yaml).
  • Run against a running dev build (iOS simulator or Android emulator).
  • Not currently blocking in CI — runs as part of the CD mobile preview build.

Coverage

Coverage reports (Istanbul for API, V8 elsewhere) are written to <pkg>/coverage/ and uploaded as a CI artifact (coverage-reports, 14-day retention).