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.ts—createTestApp()returns the Hono app + a scoped DB.client.ts—TestClientwraps requests with a valid session JWT header (issued using the testSESSION_SECRET).factories.ts—seedUser(),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 soPOST /auth/sessioncan verify test id_tokens.
- Requires a local Postgres. Start it with
cd apps/api && docker compose up -d --wait, thenpnpm test. Tests reach it through the Hyperdrive binding and rebuild the schema from migrations on every run (pgcryptois enabled by migration0009). Override the default connection withTEST_DATABASE_URLif 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-expopreset +@testing-library/react-native. - Metro's
blockListexcludes*.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).