ankit.pandey
Flagship SDET Case Study608 / 608 Core Baseline Tests Verified Passing

WrightPay: Cross-Border Payments Test Architecture

A comprehensive quality engineering case study validating an enterprise cross-border payment platform from the Next.js frontend to NestJS microservices, BullMQ settlement queues, and PostgreSQL ledger transactions.

Live API / Swagger — protected endpoints require authentication.

Core Test Suite608 / 608100% Passing in CI
Backend & API277 TestsPessimistic Locking & DTOs
Known Regressions9 TrackedAsserted via test.fail()
k6 Benchmark8,272 req/sLocal Load Profile
// Chapter 01 — System

System Architecture & Transfer Lifecycle

WrightPay simulates multi-currency cross-border money movement. Testing it requires understanding the exact data and control flow across distributed components.

// Full-Stack Architecture & Test Injection Topology

How Tests Attach to WrightPay's Distributed System

Real Data & Control Flow

2. Backend REST API Layer(NestJS / TypeScript / Express)

Route handling, JWT auth guards, DTO validation pipes, controller orchestration.

Tests Attached: Jest / Supertest API Integration (277 tests, 100% pass)

State Invariants Asserted at this Layer:

  • ✓Strict Bearer token validation and user ownership checks
  • ✓Deterministic status codes (200, 201, 400, 401, 404, 409)
  • ✓UUID path parameter validation via ParseUUIDPipe
// State Transition Pipeline

WrightPay Transfer Execution Lifecycle

Trace how a cross-border payment transitions from client initiation to database locking, queue handoff, and background settlement.

Step 03 Analysis

Pessimistic Debit & Persistence

Subsystem: NestJS -> PostgreSQL Transaction

State Invariant & Mechanism

Source wallet row locked. Balance checked >= sendAmount + 25.00 EUR fee. Wallet debited and transaction row inserted with status 'PENDING'.

Failure Path & Defect Analysis

400 Insufficient Funds if balance < total deduction; TypeORM transaction rolls back.

Financial Domain Specifications

  • • Multi-Currency Engine: Base EUR balances with on-the-fly FX conversion across USD, GBP, AED, PLN, and INR.
  • • Pessimistic Concurrency: Database uses SELECT ... FOR UPDATE on wallet rows to prevent race-condition overdrawing.
  • • Fixed Fee Rule: Flat 25.00 EUR transfer deduction applied strictly to all transfer orders.

Asynchronous Queue Model

  • • BullMQ Worker: TransfersProcessor consumes jobs from Redis queue transfersQueue.
  • • Retry Backoff: Configured with 3 retry attempts with exponential backoff on simulated partner bank timeouts.
  • • Idempotency TTL: 24-hour distributed lock key window managed in Redis.
// Chapter 02 — Test Strategy

Multi-Layer Test Architecture & Testing Matrix

The testing pyramid is balanced to maximize execution speed and failure localization while minimizing UI test flakiness.

// Multi-Layer Test Architecture

Different Testing Layers Answer Different Questions

Baseline:608 / 608 Core Pass
Active Layer Inspection

API & Integration Testing Tier

Jest / Supertest / PostgreSQL277 / 277 PASS (100%)

Core Engineering Question Answered:

"Do the controllers, database transactions, and auth guards enforce state integrity under HTTP load?"

Scope of Coverage:All 8 NestJS REST controllers, PostgreSQL entity relationships, pessimistic locking, TypeORM rollback.
Execution Speed:8.4 seconds

Key Invariants Verified at this Tier:

  • ✓SELECT ... FOR UPDATE serializes concurrent debits (prevents double-spend)
  • ✓Strict 401 on missing Bearer tokens
  • ✓Idempotency-Key returns cached response on replay

Full Test Matrix (Layer • Tool • System Area • Invariant)

Showing 20 matrix entries (383 tests represented)Evidence-linked to repository specs and QA audit documents
LayerToolSystem AreaPurpose & ScopeState / Invariant TestedEvidence SourceCount
UnitJestCryptography & AuthValidate password hashing, token generation, and account accessArgon2 salt generation uniqueness; deterministic token claimsbackend/src/modules/auth/auth.service.spec.ts14
UnitJestCurrency Exchange EngineVerify multi-currency arithmetic and rate calculationsTriangular conversion exactness; zero IEEE-754 precision driftbackend/src/modules/exchange-rates/exchange-rates.service.spec.ts10
UnitJestCard & Payment ValidationVerify card tokenization, safe PAN masking, and lifecycle stateTokenizes and stores only last-4 digits; strict active/frozen/deactivated lifecyclebackend/src/modules/cards/cards.service.spec.ts14
UnitJestTransfer Fees & LedgerVerify fixed transfer fee deduction and balance checksFlat 25.00 EUR fee strictly applied to all transfer amountsbackend/src/modules/transfers/transfers.service.spec.ts10
API / IntegrationPlaywright APIWallets Controller & ServiceValidate wallet balances, schema, and currency equivalentsInitial balance 0.00 EUR; multi-currency equivalent calculation; Postgres cross-layer validationqa/automation/tests/wallet/wallet.spec.ts8
API / IntegrationPlaywright APITransfers Initiation EndpointValidate transfer creation, debit, and queue schedulingAtomic debit, fixed 25.00 EUR fee, BullMQ enqueue, and PENDING/COMPLETED states in DBqa/automation/tests/transfers/transfers.spec.ts30
API / IntegrationPlaywright APIBeneficiaries CRUD APIValidate recipient persistence, validation, and deletionUnique account numbers per user; bank account and UPI validation; 3-beneficiary limitqa/automation/tests/beneficiaries/beneficiaries.spec.ts25
API / IntegrationPlaywright APICard Management APIValidate card tokenization, freeze, and unfreezeFrozen cards reject subsequent payout authorization; safe PAN storageqa/automation/tests/cards/cards.spec.ts19
API / IntegrationPlaywright APIAuthentication & SecurityValidate login, registration, and email verification lifecycleInvalid credentials return 401; unverified emails restricted; JWT token issuanceqa/automation/tests/auth/auth.spec.ts19
API / IntegrationPlaywright APIExchange Rates Public APIValidate public currency quote endpoints and schemasRates return HTTP 200 with all 6 supported fiat currencies; triangular conversionqa/automation/tests/exchange-rates/exchange-rates.spec.ts42
E2E FunctionalPlaywrightAuthentication UI FlowsVerify registration, login, and dashboard redirectSession persists across refresh; logout clears local storageqa/automation/tests/ui/auth/auth.spec.ts5
E2E FunctionalPlaywrightSend Money WizardComplete multi-step transfer wizard with live quoteWallet balance updates in real-time on transfer submissionqa/automation/tests/ui/transfers/send-money.spec.ts8
E2E FunctionalPlaywrightBeneficiary Management UIAdd, edit, and select beneficiaries for payoutNew beneficiaries appear immediately in transfer selection; form validationqa/automation/tests/ui/beneficiaries/beneficiaries.spec.ts6
E2E FunctionalPlaywrightCards Management UIAdd virtual cards and toggle freeze statusFreeze status badge toggles instantly on UI without reloadqa/automation/tests/ui/cards/cards.spec.ts6
Accessibility@axe-core/playwrightAll 12 Application ViewsAutomated scan for WCAG 2.1 AA accessibility violationsZero critical WCAG AA automated rule violationsqa/automation/tests/ui/accessibility/ (qa/ACCESSIBILITY_AUDIT.md)26
ResponsivePlaywright ViewportsLayout & Navigation ShellVerify layout stability at 375px, 390px, 768px, 1024px, 1440pxNo horizontal scrollbar; stepper and tables fit viewportqa/automation/tests/ui/responsive/responsive.spec.ts36
Error HandlingPlaywright MocksNetwork Fault ResilienceInject HTTP 400, 404, 409, 500 and network disconnectsUI displays informative error messages; avoids infinite loadersqa/automation/tests/ui/errors/error-handling.spec.ts44
Session & AuthPlaywrightSession Lifecycle & ExpiryVerify 401 response handling, token eviction, and polling cancel401 immediately purges storage and cancels active poll loopsqa/automation/tests/ui/session/session-expiry.spec.ts (qa/SESSION_EXPIRY_AUDIT.md)46
ConcurrencyPlaywright / RedisIdempotency & ParallelismSimultaneous transfer calls with duplicate and distinct keysOnly 1 execution processed; duplicates return cached responseqa/automation/tests/concurrency/idempotency-race.spec.ts6
RegressionPlaywright test.fail()Verified Defect RegressionContinuous assertion of known open defects in CIExpected failure status asserted; triggers alert if defect closesqa/automation/tests/defects/ (via playwright.defects.config.ts)9
// Engineering Judgment & Rationale

Why I Tested It This Way

Demonstrating the technical reasoning, architectural trade-offs, and risk mitigations behind every testing decision.

Why API tests instead of only UI testsArchitecture

Built 277 API integration tests covering 100% of controller routes and parameter combinations, keeping the E2E UI suite lean at 49 tests.

Trade-off: Writing 200+ UI tests would provide visual confirmation, but at the cost of 10x slower execution times and frequent locator flakiness.

Risk Mitigated: Eliminates combinatorial explosion of browser tests while proving every HTTP status code (200, 201, 400, 401, 404, 409) executes in under 8 seconds.

> API tests isolate the business domain from rendering quirks. When an API test fails, the defect location is immediately known. UI tests should validate user journeys, not HTTP parameter validation.

Why direct database/state validationData & State

Executed direct SQL queries against PostgreSQL in integration tests rather than relying solely on API response JSON.

Trade-off: Couples test code to database schema, requiring maintenance if column names change.

Risk Mitigated: Prevents false-positive tests where an API returns HTTP 201 Created while failing to commit database rows or leaving foreign keys in an inconsistent state.

> In financial software, the database is the source of truth, not the HTTP response payload. Verifying row existence, decimal column types, and foreign key cascades is mandatory for ledger integrity.

Why test idempotencyReliability

Implemented dedicated replay tests transmitting duplicate Idempotency-Key headers with both identical and mutated payloads.

Trade-off: Requires managing Redis test keys and ensuring TTL eviction during test runs.

Risk Mitigated: Guarantees that network retries by users, mobile apps, or reverse proxies never execute duplicate monetary deductions from customer wallets.

> Payment networks are inherently unreliable. If a user clicks 'Transfer' and loses connectivity before the HTTP response arrives, their retry MUST return the existing transaction rather than initiating a second debit.

Why test concurrency & race conditionsReliability

Fired parallel execution bursts using Promise.all against single wallet resources to verify pessimistic row-level locking.

Trade-off: Concurrent tests require careful isolation to avoid deadlocks in the test database.

Risk Mitigated: Eliminates double-spend exploits where rapid parallel requests read the same balance before either writes the deduction, driving account balances below zero.

> Traditional single-threaded test suites will NEVER catch race conditions. Asserting that PostgreSQL SELECT ... FOR UPDATE serializes concurrent debits is essential for any financial application.

Why inspect Redis directlyData & State

Connected directly to Redis via ioredis during tests to verify lock creation, TTL expiration, and key state transitions.

Trade-off: Introduces external Redis client dependency in the test harness.

Risk Mitigated: Uncovered WP-QA-007, where early failure deleted the idempotency key prematurely, leaving concurrent polling callers hanging for 2.5 seconds.

> Black-box testing cannot verify if a cache key has the proper 24-hour TTL or whether locks are released cleanly after an exception. Direct cache inspection verifies coordination mechanics.

Why test BullMQ asynchronous queuesArchitecture

Simulated worker delays, queue backlog bursts, and job completion events to validate decoupled background processing.

Trade-off: Requires asynchronous polling helpers (waitForTransactionStatus) in test specs.

Risk Mitigated: Discovered WP-QA-002: an unhandled Redis enqueue error after a database commit risks permanently stranding transactions in PENDING state.

> Synchronous HTTP endpoints that offload work to background queues must be verified from job scheduling to worker execution. A 201 response only means the request was received, not settled.

Why test asynchronous failure pathsReliability

Injected SIMULATE_FAILURE flags to force settlement failures and observe worker terminal behavior.

Trade-off: Requires backend support for test-only failure simulation flags.

Risk Mitigated: Directly uncovered flagship defect WP-QA-001: failed asynchronous transfers transition to FAILED status in PostgreSQL, but customer funds remain permanently debited without a refund.

> Happy paths are easy; failure paths are where money is lost. When a payout fails at partner banks, the system MUST execute a compensating transaction to restore wallet balances.

Why test malformed input & UUIDsNon-Functional

Submitted non-UUID strings ('not-a-uuid', '123', 'NaN') to all URL path parameters across all controllers.

Trade-off: Time investment in negative boundary sweeps.

Risk Mitigated: Uncovered WP-QA-003, where malformed UUIDs bypass NestJS validation and crash PostgreSQL with fatal 22P02 syntax errors, leaking database internals in HTTP 500 responses.

> Robust APIs fail gracefully at the transport layer. A malformed URL should return HTTP 400 Bad Request via NestJS ParseUUIDPipe before a single database query is dispatched.

Why test API contract discrepanciesArchitecture

Validated serialized JSON types against OpenAPI specifications using automated schema validation.

Trade-off: Requires maintaining synced OpenAPI documentation with backend DTOs.

Risk Mitigated: Uncovered WP-QA-004: exchange rates declared as type: number in OpenAPI were serialized as strings by pg, risking crashes in type-strict mobile/frontend clients.

> An API is a binding contract. If backend serialization diverges from published schemas, consumer clients (mobile, frontend, partner gateways) suffer unhandled deserialization crashes.

Why test accessibility (WCAG 2.1 AA)Non-Functional

Executed automated @axe-core/playwright scans across all 12 frontend views and validated keyboard navigation.

Trade-off: Automated a11y tools only detect ~40% of WCAG issues; required supplemental keyboard audits.

Risk Mitigated: Uncovered 9 verified accessibility defects, including missing form label associations on /dashboard/profile and missing focus traps in modal dialogs.

> Financial interfaces must be usable by everyone. If a user with a screen reader cannot identify an input field or get trapped inside a modal dialog, the software fails fundamental regulatory and human standards.

Why test responsive layout & breakpointsNon-Functional

Automated 36 viewport test runs across 375px, 390px, 768px, 1024px, and 1440px widths.

Trade-off: Increases test execution time across multiple device profiles.

Risk Mitigated: Uncovered WP-QA-RESP-001: fixed-width 256px sidebar blocked 68% of screen real estate on mobile phones without a collapsible hamburger menu.

> Over 60% of modern fintech transactions occur on mobile devices. Ensuring tables scroll internally and wizards do not clip is critical for operational usability.

Why test session expiration & 401sNon-Functional

Audited token expiry lifecycles, unauthorized route interception, and polling timer cleanup.

Trade-off: Requires mock token generation and timer manipulation in Playwright.

Risk Mitigated: Uncovered WP-QA-AUTH-001 (React AuthContext desync on 401) and WP-QA-AUTH-002 (polling timers firing infinitely after unauthorized response).

> A client that retains visual authenticated state after its token expires is a major security and UX risk. Polling timers that survive 401s waste server bandwidth and lock user sessions.

Why use known-defect regression (test.fail())Strategy

Tracked the 9 active defects via Playwright test.fail() annotations in a dedicated regression suite.

Trade-off: Requires writing and maintaining tests for bugs that are not yet fixed in the application code.

Risk Mitigated: Prevents defect forgetting. When a developer fixes the bug, test.fail() turns RED, immediately alerting the team that the bug was resolved and can graduate to passing.

> Disabling failing tests is bad practice. test.fail() documents the expected behavior, proves the defect exists, and ensures zero broken builds on main.

Why performance testing is treated separatelyStrategy

Utilized k6 for isolated performance benchmarks, explicitly classifying results as local-machine containerized evidence rather than production SLA claims.

Trade-off: Running load tests in CI requires isolated runner infrastructure to prevent noisy-neighbor throttling.

Risk Mitigated: Prevents deceptive marketing claims. A local machine running 8,272 req/s demonstrates endpoint efficiency, not cloud autoscaling capacity.

> Performance testing answers 'where does the system bottleneck under stress?'. Functional automation answers 'does the system behave correctly?'. Keeping them distinct preserves engineering integrity.

// Chapter 03 — Deep Technical Testing

Idempotency, Concurrency & Distributed Queues

Deep architectural testing into Redis distributed lock mechanics, BullMQ worker failure paths, and PostgreSQL transaction isolation.

// Concurrency & Lock Coordination Architecture

Redis Distributed Idempotency & The WP-QA-007 Race Hazard

How WrightPay coordinates concurrent transfers using Redis atomic locks, and the exact race condition discovered during automated concurrency testing.

Request A (Primary)

Idempotency-Key: "trf-998"

T+0ms Arrives

Request B (Network Retry)

Idempotency-Key: "trf-998"

T+20ms Concurrent

Request C (Parallel Tab)

Idempotency-Key: "trf-998"

T+35ms Concurrent
TransfersService passes key & payload hash to IdempotencyService
Redis Distributed Coordination (SET NX EX 86400)
TTL: 24 Hours

Request A acquires Lock

SET NX succeeds. State set to 'PROCESSING'. Proceeds to PostgreSQL pessimistic wallet debit.

Requests B & C enter Polling Waiter

SET NX returns nil. Secondary callers enter a polling backoff loop (up to 2,500ms) waiting for Request A to write cached response.

The WP-QA-007 Concurrency Bug Discovered During QA Automation

What happens when Request A fails early (e.g. invalid recipient or insufficient balance)?

In WrightPay V1, when Request A encounters an early validation error, the catch handler executes await redis.del(key) immediately.

However, Requests B and C are already asleep inside the polling waiter awaiting lock resolution. Because the key vanished completely instead of updating to a terminal state, the polling loop fails to detect resolution and loops for the full 2,500ms timeout, eventually throwing an erroneous 409 Conflict: Concurrent request in progress.

✓ Automated Proof: Concurrency spec asserts response latency and captures the 2,500ms stalling behavior.

PostgreSQL State Invariants

Direct SQL assertions verify that wallet deductions match transfer sums plus fees. When insufficient balance is provided, TypeORM transaction rollback is asserted at the database layer.

Redis Coordination Invariants

Direct ioredis queries assert that keys are written with 24-hour TTL and SHA-256 payload hashes. Replay tests ensure cached HTTP 201 responses are returned without duplicate debits.

BullMQ Queue Invariants

Fault injection tests verify that transient failures trigger exactly 3 worker retries with backoff, ensuring transfer records capture failureReason upon permanent partner rejection.

// Chapter 04 — Findings & Investigations

Authoritative Flagship Defect Dossiers

8 selected representative defect investigations and architectural risks from the 28 confirmed defects register. Each dossier includes complete root-cause analysis, risk assessment, and reproduction code.

WP-QA-001Confirmed Functional DefectHighOPEN — Tracked in Regression Suite

Missing Automatic Refund on Asynchronous BullMQ Settlement Failure

Symptom: When an asynchronous transfer reaches a terminal failure state in the BullMQ worker, customer funds remain permanently debited from their EUR balance without automatic restitution.

Affected Component:TransfersProcessor (Worker) & WalletsService
Affected Endpoint:POST /api/v1/transfers (Async settlement queue)
Active Regression Test Spec:qa/automation/tests/defects/wp-qa-001-settlement-refund.spec.ts (Declared via test.fail())
Expected Specification

Upon permanent settlement failure, the system must atomically dispatch a compensating refund transaction, restoring the wallet balance to its pre-debit state (400.00 EUR) with a corresponding ledger audit entry.

Actual Behavior (Current V1)

Transaction status updates to 'FAILED', but zero wallet balance adjustments occur and zero refund ledger rows are inserted.

Root-Cause Analysis

In backend/src/modules/transfers/transfers.processor.ts, the catch block updates the transaction record with failureReason, but lacks any call to WalletsService.credit() or an automated compensating refund saga.

Step-by-Step Reproduction

  1. Create authenticated user with funded wallet (400.00 EUR balance).
  2. Initiate a cross-border transfer of 50.00 EUR with fee 25.00 EUR (total deduction 75.00 EUR).
  3. Inject SIMULATE_FAILURE in transfer metadata to trigger settlement failure.
  4. Allow BullMQ worker to exhaust its 3 automatic retry attempts with exponential backoff.
  5. Inspect transactions table: status is 'FAILED' and failureReason is populated.
  6. Inspect wallets table: balance remains 325.00 EUR (75.00 EUR permanently missing).

Business & Operational Risk

Direct loss of customer funds without administrative manual intervention. Critical financial compliance violation and customer trust breach.

Engineering Remediation Plan

Implement an automated compensation handler within the worker's terminal failure block: wrap wallet crediting and compensating transaction creation in a dedicated TypeORM transaction.

Reproduction Test Code ExcerptPlaywright TypeScript
// Reproduction from qa/automation/tests/defects/wp-qa-001-settlement-refund.spec.ts
test('WP-QA-001: failed async transfer must restore debited wallet balance', async ({ authUser, db }) => {
  test.fail(true, 'Known defect: TransfersProcessor does not issue compensating refund on permanent failure');

  const initialBalance = 400;
  const transfer = await authUser.api.transfers.create({
    beneficiaryId,
    sourceCurrency: 'EUR',
    targetCurrency: 'USD',
    sourceAmount: 50,
    metadata: { testScenario: 'SIMULATE_FAILURE' }
  });

  // Wait for BullMQ worker retries to reach terminal FAILED status
  await waitForTransactionStatus(db, transfer.id, 'FAILED', 15000);

  // Financial Invariant: Customer funds MUST be restored to initial balance
  const finalWallet = await db.queryOne('SELECT balance FROM wallets WHERE id = $1', [walletId]);
  expect(Number(finalWallet.balance)).toBe(initialBalance); // Fails: 325.00 !== 400.00
});
WP-QA-002Architectural RiskHighDOCUMENTED RISK

PostgreSQL Commit Precedes BullMQ Enqueue (Dual-Write Window)

Symptom: If Redis suffers a transient network outage or queue memory exhaustion immediately following a database transaction commit, transfers become permanently stranded in 'PENDING_EXECUTION' with no worker pickup.

WP-QA-003Confirmed Functional DefectMediumOPEN — Tracked in Regression Suite

Malformed Non-UUID Path Parameters Trigger Unhandled HTTP 500

Symptom: Supplying non-UUID values (e.g., 'invalid-uuid-123', 'test', 'NaN') in URL path parameters crashes with HTTP 500 Internal Server Error instead of 400 Bad Request or 404 Not Found.

WP-QA-004API Contract DiscrepancyLowOPEN — Tracked in Regression Suite

Serialized Exchange Rate Returns String Conflict With OpenAPI Number Contract

Symptom: Exchange rate endpoints return decimal values serialized as JSON strings (e.g., 'rate': '1.0845') while the declared OpenAPI specification specifies type: number.

WP-QA-005Confirmed Functional DefectLowOPEN — Tracked in Regression Suite

Beneficiary Creation Accepts Whitespace-Only Name and Persists Empty String

Symptom: Submitting a beneficiary with a name consisting exclusively of whitespace characters (' ') is accepted and saved into PostgreSQL with name: ''.

WP-QA-007Confirmed Functional DefectLowOPEN — Tracked in Regression Suite

Idempotency Lock Key Deletion on Early Failure Stalls Concurrent Callers

Symptom: When a primary transfer request fails early (e.g., validation or balance error), the idempotency lock is deleted prematurely, causing secondary concurrent callers to block for 2.5s and receive a misleading 409 Conflict.

WP-QA-008Architectural RiskMediumDOCUMENTED RISK

Low 32-Bit Entropy in Transfer Reference Generation Risks Collision Under Volume

Symptom: Transfer references are generated using 32 bits of entropy (crypto.randomBytes(4).toString('hex')), creating mathematical collision hazards under scale.

WP-QA-009Confirmed Functional DefectMediumOPEN — Tracked in Regression Suite

Frontend Card Creation Sends Uppercase Type Mismatching Backend Enum

Symptom: Submitting a new card from the web UI returns HTTP 400 with error 'cardType must be a valid enum value'.

Specialized QA Audit Findings (Documented in Master Defect Register)

9 Accessibility Findings

Form label omissions, modal focus trapping gaps, low contrast on currency codes (WCAG AA).

6 Responsive Findings

Fixed 256px sidebar blocking mobile viewports, transaction table 2D page bleed.

5 Error Handling Findings

Backend 500 errors masked as 'No recent transactions' empty states.

2 Session / 401 Findings

React AuthContext token desync, infinite transfer polling loop after 401.

Canonical Hierarchy: 28 Confirmed Findings (27 Functional Defects + 1 API Contract Discrepancy) across backend, frontend, and specialized audits, plus 2 separate Architectural Risks and 14 Documented Observations (qa/DEFECT_INVENTORY.md). The 8 flagship dossiers above represent selected investigations, not the total defect count.

// Chapter 05 — Evidence & Benchmarks

Verified Test Evidence & Execution Telemetry

Concrete execution numbers across the 608 core baseline tests, the 9 known defect regression cases, and k6 performance benchmarks.

Jest
VERIFIED PASS
130/ 130 tests

Jest Unit Suite

Pure business logic, Argon2 hashing, FX currency math, DTO transformation, and fee calculation algorithms.

Key Invariants Verified:

  • •Zero floating point rounding error on multi-currency conversions
  • •Deterministic Argon2 password salt/hash verification
  • •Valid card Luhn algorithm validation
Jest / Supertest
VERIFIED PASS
277/ 277 tests

Backend API & Integration

End-to-end HTTP contract testing across all 8 NestJS REST controllers with PostgreSQL transactions.

Key Invariants Verified:

  • •Strict bearer token authentication guards on private endpoints
  • •Pessimistic row-level lock (SELECT FOR UPDATE) prevents double-spend
  • •TypeORM transaction rollback on balance exhaustion
Playwright
VERIFIED PASS
49/ 49 tests

Frontend Functional / E2E

Multi-step user journey workflows across Next.js UI from authentication through transfer execution.

Key Invariants Verified:

  • •Recipient creation, validation, and auto-population in transfer form
  • •Real-time exchange rate quote recalculation on amount change
  • •Wallet multi-currency equivalent tab rendering
@axe-core/playwright
VERIFIED PASS
26/ 26 tests

Accessibility (WCAG 2.1 AA)

Automated WCAG 2.1 AA accessibility scans across all 12 application routes.

Key Invariants Verified:

  • •Input form control label associations (htmlFor/id matching)
  • •Focus trapping within active modal dialogs
  • •Color contrast minimum ratio >= 4.5:1 for normal text
Playwright
VERIFIED PASS
36/ 36 tests

Responsive Viewport Suite

Cross-breakpoint visual and interaction testing across 375px, 390px, 768px, 1024px, and 1440px.

Key Invariants Verified:

  • •Zero unscrollable horizontal 2D viewport overflow
  • •Send Money step wizard layout integrity at 375px
  • •Card detail rendering without PAN truncation
Playwright / Mock Interceptors
VERIFIED PASS
44/ 44 tests

Error Handling & Edge Cases

Fault injection and HTTP error simulations validating UI resilience and error state rendering.

Key Invariants Verified:

  • •Graceful degradation on backend 500 without crashing React tree
  • •Clear error messaging instead of misleading empty states
  • •Network dropout handling during transfer polling
Playwright
VERIFIED PASS
46/ 46 tests

Session Expiry & 401 Interceptors

Token lifecycle, automatic eviction, 401 response handling, and polling termination audits.

Key Invariants Verified:

  • •Immediate localStorage token purge on 401 response
  • •React AuthContext synchronization preventing zombie authenticated states
  • •Transfer polling timer cancellation on unauthorized responses
Playwright / test.fail()
EXPECTED FAIL
9/ 9 tests

Known Defect Regression Suite

9 automated test specifications asserting verified open defects using test.fail() to track fix readiness without breaking CI.

Key Invariants Verified:

  • •WP-QA-001: Async transfer failure wallet refund omission
  • •WP-QA-003: Non-UUID path parameter 500 error handling (3 tests)
  • •WP-QA-004: Decimal exchange rate type discrepancy
// Performance & Load Testing Telemetry

k6 Load Testing: 6 Scenarios, 142,280 Total Requests

Benchmark Evidence (Local Load Test)
Engineering Integrity Notice: Local containerized load test environment (Docker Desktop, single host). Measured for application bottleneck discovery and concurrency verification — not a production cloud capacity claim.
Scenario A0.00% Errors

Baseline latency profiling against /wallets/me, /transactions, /exchange-rates.

4,820 reqsp95: 1.24 ms
Scenario B0.00% Errors

Sustained read concurrency across exchange rate and public configuration endpoints.

124,441 reqsp95: 2.18 ms
Scenario C0.00% Errors

Stress testing CPU-intensive Argon2 password hashing pipeline under auth spikes.

3,180 reqsp95: 27.94 ms
Scenario D0.00% Errors

Simultaneous transfer creations validating Redis idempotency lock allocation.

4,210 reqsp95: 4.60 ms
Scenario E0.00% Errors

Identical Idempotency-Key replays under burst conditions verifying cache hit rate.

3,820 reqsp95: 1.05 ms
Scenario F0.00% Errors

Rapid BullMQ job scheduling evaluating Redis queue memory & pipeline depth.

1,809 reqsp95: 6.80 ms
// Chapter 06 — Engineering Artifacts

Real Test Code & Project Retrospective

Examine 6 production-grade test specifications and read the engineering retrospective on what worked, scope limitations, and future improvements.

spec:tests/api/transfers-schema.spec.ts

What this test does

Submits a transfer request using Playwright's API request context with Bearer authentication and asserts strict HTTP 201 response schema conformity.

Why it exists

Validates that the HTTP transport layer enforces correct status codes, header responses, and JSON payload serialization before the frontend touches it.

Risk / Bug Prevented

Catches undeclared response property changes, missing authorization header enforcement, and unhandled 500 crashes on missing fields.

import { test, expect } from '../fixtures/api.fixtures';

test('POST /api/v1/transfers creates transfer and returns compliant DTO', async ({ authUser }) => {
  const payload = {
    beneficiaryId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
    sourceCurrency: 'EUR',
    targetCurrency: 'USD',
    sourceAmount: 100.00
  };

  const response = await authUser.api.transfers.create(payload, {
    headers: { 'Idempotency-Key': crypto.randomUUID() }
  });

  expect(response.status()).toBe(201);
  const body = await response.json();
  
  expect(body).toMatchObject({
    id: expect.stringMatching(/^[0-9a-f-]{36}$/),
    status: 'PENDING',
    sourceCurrency: 'EUR',
    targetCurrency: 'USD',
    sourceAmount: 100.00,
    fee: 25.00
  });
});

Key Recommendations

  • • Implement the Transactional Outbox Pattern to close the dual-write window between PostgreSQL commit and BullMQ enqueue.
  • • Add ParseUUIDPipe validation across all controller route handlers to eliminate unhandled 22P02 database crashes.
  • • Synchronize CreateCardDto casing with frontend select values to restore web payment card creation.

Documented Limitations

  • • /onboarding flow is currently a client-side mock without actual KYC document verification.
  • • Fixed 25.00 EUR fee structure regardless of transfer volume (intended V1 business logic constraint).
  • • Single base EUR ledger with real-time FX equivalents rather than a multi-currency double-entry core.

Engineering Retrospective

  • • What worked: Using test.fail() kept CI green while maintaining visibility on all 9 open defect regressions.
  • • What worked: Direct database and Redis queries caught critical bugs that black-box UI testing would have completely missed.
  • • What I would improve: Add containerized WireMock stubs for partner bank clearing networks.