Skip to content

test: standardize test suite with Test Table Pattern and shared infrastructure - #15

Merged
lmoraesdev merged 11 commits into
mainfrom
chore/standardize-test-infrastructure
May 25, 2026
Merged

test: standardize test suite with Test Table Pattern and shared infrastructure#15
lmoraesdev merged 11 commits into
mainfrom
chore/standardize-test-infrastructure

Conversation

@lmoraesdev

Copy link
Copy Markdown
Owner

Summary

  • Introduces Test Table Pattern across all unit tests: typed TestCase<Input, Output> arrays replace scattered it() blocks, making scenarios and their assertions visible in one place
  • Adds shared test infrastructure (runTests, setupStubs, assertStubs, getError) and path aliases (@test/helpers, @test/builders, @test/fakes) used consistently across every spec file
  • Generates decision matrices programmatically with flatMap for ProcessWebhookService (charge state × event type) and charge-state-machine (4×4 full transition matrix) — no combination can be silently omitted
  • Adds AuthenticationError (SE01) and ValidationExceptionFilter (VE01) with full test coverage; migrates domain-errors and domain-exception.filter to the new pattern
  • Documents the approach, trade-offs, and infrastructure in README

Test plan

  • npm test — 122 tests, 0 failures
  • npm run test:e2e — all e2e flows pass (create, webhook, dedup, invalid state, validation)
  • Confirm new spec files are picked up: validation-exception.filter.spec.ts
  • Confirm AuthenticationError row appears in domain-exception.filter results

lmoraesdev added 11 commits May 24, 2026 23:06
…onFilter

- Error codes: SE01 (authentication failure), VE01 (validation error)
- AuthenticationError extends DomainError for webhook signature failures
- ValidationExceptionFilter catches ZodValidationException and responds
  with { statusCode: 400, code: VE01, message } in the same envelope
  as other domain errors
- DomainExceptionFilter STATUS_MAP updated with AE06 (MissingIdempotencyKey)
  and SE01 mappings
…en Zod schema

- WebhookSignatureGuard now throws AuthenticationError (SE01) instead of
  UnauthorizedException so the response envelope is consistent with other
  domain errors handled by DomainExceptionFilter
- CreateChargeDto: currency is now z.enum(['BRL']) (rejects USD/etc.),
  amount uses .safe() to prevent values above Number.MAX_SAFE_INTEGER
- AppModule registers GlobalExceptionFilter, DomainExceptionFilter, and
  ValidationExceptionFilter as APP_FILTER providers (applied in reverse order)
- HealthController runs SELECT 1 via DataSource; responds 200 { status: ok }
  on success or 503 { status: degraded } when the database is unreachable
- @faker-js/faker as devDependency for realistic randomized test data
- tsconfig.json and vitest.config.ts gain @test/helpers, @test/builders,
  and @test/fakes aliases so unit and e2e tests can import from a single
  canonical location without brittle relative paths
Builders (test/builders/):
  - ChargeBuilder — aCharge() with status shortcuts (awaitingPayment, paid, expired)
  - CreateChargeDtoBuilder — aCreateChargeDto() with withoutCurrency/withoutDescription
  - WebhookEventDtoBuilder — aWebhookEvent() with confirmed/expired shortcuts
  - IdempotencyKeyBuilder — anIdempotencyKey()

Fakes (test/fakes/):
  - InMemoryChargeRepository — seed/seedMany/all/count, save() preserves transitionTo
  - InMemoryIdempotencyRepository — findByKey/save/count/clear
  - InMemoryWebhookEventRepository — dedup via Set, throws WebhookEventAlreadyProcessedError
  - createMockLogger() — returns vi.fn() stubs for log/warn/error/debug/forContext
Introduces test/helpers/ as the third pillar of the test infrastructure:

  - types.ts       — StubConfig, CallMatchConfig, TestCase<I, O>
  - run-tests.ts   — runTests() iterates testCases and calls it/it.only/it.skip
  - setup-stubs.ts — setupMock() and setupStubs() configure vi.fn() from StubConfig
  - assert-stubs.ts — assertMockCalls() and assertStubs() validate call expectations
  - get-error.ts   — getError<E>() captures thrown errors for typed assertions
- charges.controller.e2e-spec.ts: uses InMemoryChargeRepository,
  InMemoryIdempotencyRepository, and aCreateChargeDto() builder;
  withoutCurrency() replaces the awkward double-cast destructuring
- webhooks.controller.e2e-spec.ts: uses shared fakes and aCharge()/
  aWebhookEvent() builders; removes inline makeCharge, confirmedBody,
  expiredBody helpers
- payment-flow.e2e-spec.ts: uses shared fakes and builders; removes
  inline InMemoryXxx classes and patchSave workaround
- charges.e2e-spec.ts: deleted (all scenarios covered by
  charges.controller.e2e-spec.ts)
get-charge.service.spec.ts:
  - 2 TestCases via runTests (found/not-found), asserting DTO shape and
    error id via getError + assertStubs

create-charge.service.spec.ts:
  - 3 TestCases via runTests (new key, cache hit, conflict) with
    setupStubs/assertStubs/getError; canonicalization describe preserved

process-webhook.service.spec.ts:
  - 6 TestCases via runTests covering all service branches; each
    mutable charge uses a separate instance to prevent cross-test mutation
  - 8-row decision matrix (charge status × event type) via it.each

charge-state-machine.spec.ts:
  - Decision matrix replaced with flatMap over all 4 statuses generating
    16 combinations; valid transitions declared in VALID_TRANSITIONS array

domain-exception.filter.spec.ts:
  - Updated imports to use createMockLogger from @test/fakes
- domain-errors.spec.ts: add VE01/SE01 to enum table, AuthenticationError
  class tests, replace hardcoded code strings with ErrorCode.* constants
- domain-exception.filter.spec.ts: consolidate 3 describe blocks into one
  it.each covering all 6 exceptions (including AuthenticationError) with
  HTTP status + response envelope + structured log asserted per row;
  import createMockLogger from @test/fakes
- validation-exception.filter.spec.ts: new file with 4-row table covering
  simple field, nested field, root-level, and multi-error scenarios
…-offs

Adds a Testing section explaining the pattern adopted, the helper
infrastructure (runTests, setupStubs, assertStubs, getError), programmatic
decision matrices via flatMap, and an honest trade-off comparison against
plain it() blocks. Also updates the project layout tree and roadmap.
@lmoraesdev
lmoraesdev merged commit c036478 into main May 25, 2026
1 check passed
@lmoraesdev
lmoraesdev deleted the chore/standardize-test-infrastructure branch May 25, 2026 02:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant