9.8 KiB
TDD Transition Plan — MeshiTrack
This document details the blueprint for transitioning the MeshiTrack monorepo to a strict Test-Driven Development (TDD) workflow. It outlines the specific steps, tooling enhancements, repository guidelines, and architectural patterns required to shift from "test-after" to "test-first" development.
1. Objectives
- Maintain Strict Quality Bars: Ensure all new code seamlessly meets our high coverage gates (100% lines/functions/statements, 90% branches for
packages/api&packages/shared, and 90% lines, 85% functions, 75% branches, 85% statements forpackages/web). - Prevent Architecture Regression: Stop implementing business logic before definitions and type interfaces are established in
packages/shared. - Elevate DX and Iteration Speed: Speed up the local feedback loop (Vitest/RTL runtimes) to enable a continuous Red-Green-Refactor cycle.
- Standardize Mocks and Factories: Establish reusable test builders and automated dependency mocks to reduce boilerplate.
2. The MeshiTrack TDD Loop (Red-Green-Refactor)
TDD requires writing tests before writing the corresponding implementation. In MeshiTrack, this cycle is applied iteratively across each vertical slice.
graph TD
A[1. Write Failing Test - RED] --> B[2. Run Test & Verify Failure]
B --> C[3. Write Minimal Code - GREEN]
C --> D[4. Run Test & Verify Pass]
D --> E[5. Refactor Code & Clean Boilerplate]
E --> F[6. Run All Tests to Prevent Regression]
F --> A
The Rules of TDD in MeshiTrack:
- Write no production code unless it is to make a failing unit or integration test pass.
- Write only enough of a test to demonstrate a failure (compilation failure counts as a failure).
- Write only enough production code to make the single failing test pass.
- Refactor immediately after going Green, while both the unit test and existing regression suite are passing.
3. Layer-by-Layer TDD Guide
Layer 1: Shared Package (packages/shared)
The shared package contains domain types, enums, and Zod schemas. It must remain pure TypeScript.
- Red: Define the interface or enum signature in TypeScript. Write a test in
src/validation/*.test.tsasserting how the validation schema should handle valid, invalid, and edge-case payloads. - Green: Implement the Zod v4 validation schema in
src/validation/*.schemas.ts. Run the test to verify it passes. - Refactor: Clean up the schema declarations, refine custom error messages, and ensure barrel exports are updated in
index.ts.
Layer 2: Database Schema & Repository (packages/api)
This layer handles persistence via Mongoose schemas and Awilix-registered repositories.
- Red: Write an integration test (
*.repository.test.ts) usingmongodb-memory-serverthat tests data storage, constraints, index behavior, andhouseholdIdfiltering. - Green: Implement the Mongoose schema, compile the model, and write the Repository class methods using
.lean().exec()on all reads. Verify the integration test passes. - Refactor: Optimize database indexes, ensure appropriate Mongoose options are set, and check for memory leaks or unclosed connections.
Layer 3: Service Layer (packages/api)
Services contain the core business logic, including validation, authorization checks, and transactional tasks.
- Red: Write unit tests (
*.service.test.ts) using Vitest. Mock all dependencies (e.g., repositories, event emitters) usingvi.fn(). Write tests asserting correct handling of successful cases, expected errors (NotFoundError,ConflictError, etc.), and household isolation boundaries. - Green: Create the Service class with constructor injection via Awilix. Implement only the minimum logic required to pass the test cases.
- Refactor: Extract duplicate logic into helper functions, improve type safety, and simplify constructor dependencies.
Layer 4: Route Layer (packages/api)
Fastify routes define endpoints, validate incoming payloads via Zod, resolve services, and return responses.
- Red: Write a route test (
*.routes.test.ts) using Fastify'sapp.inject(). Assert status codes, headers, and the response body structure. Mock the service layer resolved from the request DI scope (request.diScope.resolve). - Green: Implement the Fastify route plugin, declare path validation schemas, resolve the service from the DI container, and call it. Register the plugin.
- Refactor: Optimize error mappings, clean up route paths, verify route configuration options (e.g., household scope exclusions).
Layer 5: Web API Client (packages/web)
The client wrapper calls the backend API and handles client-side caching or state synchronization (e.g., via SWR).
- Red: Write a service unit test (
*.test.ts) using Vitest. Configure MSW (Mock Service Worker) to mock the API endpoint response. Write assertions checking payload formatting, query serialization, and error transformations. - Green: Implement the frontend service in
src/services/using fetch. Run tests and verify MSW handlers return correctly. - Refactor: Standardize request configuration, dry up common header settings, and enhance client type-safety.
Layer 6: Web UI (packages/web)
React components and Next.js pages display data, capture input, and handle interactions.
- Red: Write component tests (
*.test.tsx) using React Testing Library. Query elements by accessible roles (e.g.,screen.getByRole('button', { name: /save/i })). Assert correct rendering of states (loading, empty, success) and verify user interactions fire expected service callbacks. - Green: Implement the React component (Server/Client split as appropriate). Write the bare minimum JSX/TSX to satisfy the test roles and events.
- Refactor: Refine CSS structures, optimize components for re-renders, clean up accessibility attributes, and verify responsive design parameters.
4. DX Tooling Enhancements (Bridges)
To enable developers to practice TDD seamlessly, we must upgrade our test execution loop and reduce boilerplate.
Bridge A: Immediate Watch Mode for Active Files
Running npm run test or npm run test:cov across the entire workspace takes too long for the continuous TDD loop. We need scripts to instantly watch specific directories or single files.
We will update package-level package.json scripts to introduce:
- Root package.json:
- Add
"test:watch": "turbo run test:watch"
- Add
- packages/web/package.json:
- Add
"test:watch": "vitest"(mirroring packages/api)
- Add
This will allow developers to run:
npm run test:watch -w packages/apito start a continuous, interactive Vitest runner for API files.npm run test:watch -w packages/api -- src/modules/products/products.service.test.tsto focus only on a single service during the Red-Green cycle.
Bridge B: Automated Mock Helpers
Creating manual mocks for every repository in every service test adds substantial boilerplate. We will introduce a standard mock utility packages/api/src/common/test/mock-repository.ts that dynamically mocks any repository interface.
import { vi } from 'vitest';
export function createMockRepository<T>(repoClass: new (...args: any[]) => T): Record<keyof T, any> {
const methods = Object.getOwnPropertyNames(repoClass.prototype).filter(
(name) => name !== 'constructor' && typeof repoClass.prototype[name] === 'function'
);
const mock: Record<string, any> = {};
for (const method of methods) {
mock[method] = vi.fn();
}
return mock as Record<keyof T, any>;
}
This reduces the boilerplate in service tests from a manual 15-line structure to a single line:
const mockRepo = createMockRepository(ProductsRepository);
5. TDD Commit & Git Conventions
To document and encourage step-by-step TDD, we will adopt atomic commit structures matching the Red-Green-Refactor steps:
- Red Commit: When a new test suite is created and fails.
test(pantry): add failing test for spoilage calculation (RED)
- Green Commit: When the production code is completed and tests pass.
feat(pantry): implement spoilage calculation logic (GREEN)
- Refactor Commit: When the code is cleaned up while staying green.
refactor(pantry): optimize date difference utility in spoilage
These atomic commits make peer reviews and rollbacks extremely clean and make it simple to track development momentum.
6. Guidelines Alignment & Updates
To formalize the transition, we will execute the following file updates:
- Update
ANTIGRAVITY.md:- Modify the "Implementation Workflow" section to make TDD mandatory.
- Replace steps with the Red-Green-Refactor workflow per layer.
- Introduce the
test:watchcommands and mock helper directives.
- Update
docs/instructions/conventions.md:- Re-orient the "Implementation Workflow" from "test-after" to TDD-first.
- Introduce
docs/instructions/tdd.md:- A dedicated developer tutorial on TDD in the MeshiTrack codebase, explaining mocks, databases, and Next.js testing patterns in detail.
7. Immediate Action Plan
To bridge the TDD gap immediately, we will execute these concrete steps in this task:
- Step 1: Codebase Tooling Infrastructure
- Add
"test:watch": "vitest"topackages/web/package.json. - Add
"test:watch": "turbo run test:watch"to rootpackage.json.
- Add
- Step 2: Reusable Mock Helper
- Create
packages/api/src/common/test/mock-repository.tscontaining the automated mock builder utility.
- Create
- Step 3: Document TDD & Update Guidelines
- Create
docs/instructions/tdd.mdas the definitive guide. - Update
ANTIGRAVITY.md's workflow section. - Update
docs/instructions/conventions.md's workflow section.
- Create
- Step 4: Verification
- Verify the codebase build and tests are completely operational.