test: standardize test suite with Test Table Pattern and shared infrastructure - #15
Merged
Merged
Conversation
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
TestCase<Input, Output>arrays replace scatteredit()blocks, making scenarios and their assertions visible in one placerunTests,setupStubs,assertStubs,getError) and path aliases (@test/helpers,@test/builders,@test/fakes) used consistently across every spec fileflatMapforProcessWebhookService(charge state × event type) andcharge-state-machine(4×4 full transition matrix) — no combination can be silently omittedAuthenticationError(SE01) andValidationExceptionFilter(VE01) with full test coverage; migratesdomain-errorsanddomain-exception.filterto the new patternTest plan
npm test— 122 tests, 0 failuresnpm run test:e2e— all e2e flows pass (create, webhook, dedup, invalid state, validation)validation-exception.filter.spec.tsAuthenticationErrorrow appears indomain-exception.filterresults