Module Test Patterns
Module Test Patterns
Relevant source files
The following files were used as context for generating this wiki page:
The @primebrick/sdk employs a testing strategy focused on isolation, portability, and high-fidelity simulation of external dependencies. By leveraging the hexagonal architecture (Ports & Adapters), the SDK ensures that core logic remains testable without requiring a live database or network connection, while integration-level tests verify real HTTP and process-level behaviors.
Port Isolation via Mock Adapters
The primary pattern for testing business logic in the Config, Service, and Migrations modules is the use of in-memory mock adapters for their respective ports. This allows for precise control over the data returned to the SDK and the verification of side effects.
Config & Service Repository Mocks
The ConfigLoader and ServiceRegistrar are tested using factory functions that generate mock implementations of ConfigRepositoryPort and ServiceRegistryPort.
makeRepo(Config): Creates a mock that returns a predefined set of configuration rows src/config/tests/config-loader.test.ts:5-9.makeRepo(Service): Creates a mock usingvi.fn()to track calls toinsert,updateByCode, andfindByCodesrc/service/tests/service-registrar.test.ts:6-16.
Database Port Mock
The Migrations module requires a more sophisticated mock to simulate transaction blocks and query tracking. The makeDb utility captures every SQL statement and parameter sent to the adapter src/migrations/tests/apply-patches.test.ts:10-23.
Data Flow: Mock Adapter Pattern
| Component | Port Mock Implementation | Verification Strategy |
|---|---|---|
| ConfigLoader | Returns static array of { key, value } src/config/tests/config-loader.test.ts:15-19 | Assert getTyped converts types correctly src/config/tests/config-loader.test.ts:50-53 |
| ServiceRegistrar | vi.fn() for CRUD operations src/service/tests/service-registrar.test.ts:11-15 | Assert insert vs update based on findByCode result src/service/tests/service-registrar.test.ts:33-58 |
| Migrations | Tracks SQL strings in a calls array src/migrations/tests/apply-patches.test.ts:13-17 | Verify BEGIN and COMMIT wrap the DDL execution src/migrations/tests/apply-patches.test.ts:57-61 |
Sources: src/config/tests/config-loader.test.ts:5-20, src/service/tests/service-registrar.test.ts:6-31, src/migrations/tests/apply-patches.test.ts:10-23
Filesystem & Environment Fixtures
Modules that interact with the environment or the local disk use temporary fixtures and process state management to ensure test idempotency.
Temporary Patch Directories
To test applyPatches, the test suite creates a unique temporary directory for each test case using mkdtempSync src/migrations/tests/apply-patches.test.ts:28-30. This directory is populated with .sql files to simulate different migration scenarios (new patches, duplicate SHAs, or modified content) and is recursively deleted after each test src/migrations/tests/apply-patches.test.ts:31-33.
Environment Isolation
The env module tests preserve the original process.env by cloning it before each test and restoring it afterward src/env/tests/env-validator.test.ts:5-13. This prevents cross-test contamination when validating required variables or default values.
Code Entity Space: Migration Test Setup
Code
Sources: src/migrations/tests/apply-patches.test.ts:25-61, src/env/tests/env-validator.test.ts:1-13
Singleton & Lifecycle Patterns
Testing global state and process-level events requires specialized mocking of Node.js internals.
NatsClient Singleton Reset
The NatsClient is a singleton. To test its lazy-connection logic, the test suite invokes a private-access style close() method in beforeEach to nullify the internal connection reference src/nats/tests/nats-client.test.ts:16-20. The nats module itself is mocked using vi.mock to return a fake connection object src/nats/tests/nats-client.test.ts:4-10.
GracefulShutdown Process Mocking
Testing GracefulShutdown requires preventing the test runner from actually exiting.
process.exit: Spied on and mocked to throw a specific__EXIT__error, which is then caught by the test's expectation src/lifecycle/tests/graceful-shutdown.test.ts:9-11.- Signal Handlers:
process.onis spied on to verify thatSIGTERM,SIGINT, anduncaughtExceptionhandlers are correctly registered duringinstall()src/lifecycle/tests/graceful-shutdown.test.ts:47-56.
Sources: src/nats/tests/nats-client.test.ts:1-20, src/lifecycle/tests/graceful-shutdown.test.ts:4-57
Integration: Real HTTP Binding
Unlike other modules, the HttpServer tests perform a "black-box" integration test by binding to a real network port.
Dynamic Port Assignment
To avoid port conflicts during parallel test execution, the server is configured to bind to port 0 src/http/tests/http-server.test.ts:19. The OS assigns a random free port, which is then retrieved via server.address() for use in test requests src/http/tests/http-server.test.ts:23-24.
HealthCheck Verification
The HttpServer test wires a real HealthCheck instance to a mocked HealthCheckPort src/http/tests/http-server.test.ts:20-21. This allows testing the full HTTP lifecycle:
- Mock
HealthCheckPort.pingreturnstrue. - Test performs
fetch()againsthttp://127.0.0.1:{port}/healthsrc/http/tests/http-server.test.ts:32. - Asserts
200 OKand JSON bodystatus: "healthy"src/http/tests/http-server.test.ts:33-36.
Code Entity Space: HTTP Test Lifecycle
Code
Sources: src/http/tests/http-server.test.ts:13-43, src/http/tests/http-server.test.ts:67-101