Carbonteq
Best Practices/Backend/Architecture/Infrastructure

Infrastructure Testing

Setup and patterns for repository/infrastructure integration tests with Drizzle + Effect + Vitest

Infrastructure Testing Overview

What we test

  • CRUD: add, fetchByUserId, update, remove
  • Queries: existsByUserId, fetchByPhoneNumber, fetchWithCompleteProfiles (pagination)
  • Edge cases: non-existent userId, unique userId constraint, cascade delete
  • Serialization: nested VOs flattened to columns (address, social links)
1
Spin up a fresh DB and repository (per test)

Before each test, we boot a disposable database, instantiate the repository, and wipe tables so every run starts from zero. After each test, we clean up the database resources. In short: beforeEach(async () => { ... }) brings up a clean state, and afterEach(async () => { ... }) tears it down.

2
First Test: Basic CREATE and READ Testing

Verifies add(...) persists and fetchByUserId(...) returns Option.Some with the same entity.

3
Update: change fields and persist

Uses update(...) to change persisted fields and re-fetches to assert the mutations are saved.

4
Query by phone number

Exercises fetchByPhoneNumber(...): expects a non-empty list containing the created host.

5
Remove and verify deletion

Calls remove(id) and verifies fetchByUserId(...) returns Option.None afterward.

6
Existence check

Validates existsByUserId(...) toggles from false to true after inserting a host.

7
Pagination: only complete profiles

Ensures fetchWithCompleteProfiles(...) returns only hosts with complete profile fields (dob, phoneNumber, profileImage).

8
Non-existent userId

Checks fetchByUserId(...) for a non-existent UUID returns Option.None.

import { beforeEach, afterEach } from "vitest";
import { HostDrizzleRepository } from "../../app/infrastrcture/db/drizzle/repositories/host.drizzle.repository";
import { setupTestDatabase, cleanupDatabase } from "../setup/database";
describe("HostDrizzleRepository Integration", () => {
let testDb: TestDatabase;
let hostRepository: HostDrizzleRepository;
beforeEach(async () => {
testDb = await setupTestDatabase();
hostRepository = new HostDrizzleRepository(testDb.db);
await cleanupDatabase(testDb.db); // start clean
});
afterEach(async () => {
await testDb.cleanup();
});
});

Running the tests

  • Run: pnpm vitest (or your workspace script)

Key Lessons

  • Fresh DB per test: Use setupTestDatabase() and cleanupDatabase() for isolation
  • Test all CRUD: Cover add, fetch, update, remove operations
  • Effect patterns: Use E.runPromise for assertions, E.runSync for factories
  • Option handling: Verify O.isSome and O.isNone for repository results
  • Edge cases: Test non-existent data scenarios