182 lines
9.8 KiB
Markdown
182 lines
9.8 KiB
Markdown
|
|
# 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
|
||
|
|
|
||
|
|
1. **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 for `packages/web`).
|
||
|
|
2. **Prevent Architecture Regression**: Stop implementing business logic before definitions and type interfaces are established in `packages/shared`.
|
||
|
|
3. **Elevate DX and Iteration Speed**: Speed up the local feedback loop (Vitest/RTL runtimes) to enable a continuous Red-Green-Refactor cycle.
|
||
|
|
4. **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.
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
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:
|
||
|
|
1. **Write no production code** unless it is to make a failing unit or integration test pass.
|
||
|
|
2. **Write only enough of a test** to demonstrate a failure (compilation failure counts as a failure).
|
||
|
|
3. **Write only enough production code** to make the single failing test pass.
|
||
|
|
4. **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.
|
||
|
|
|
||
|
|
1. **Red**: Define the interface or enum signature in TypeScript. Write a test in `src/validation/*.test.ts` asserting how the validation schema should handle valid, invalid, and edge-case payloads.
|
||
|
|
2. **Green**: Implement the Zod v4 validation schema in `src/validation/*.schemas.ts`. Run the test to verify it passes.
|
||
|
|
3. **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.
|
||
|
|
|
||
|
|
1. **Red**: Write an integration test (`*.repository.test.ts`) using `mongodb-memory-server` that tests data storage, constraints, index behavior, and `householdId` filtering.
|
||
|
|
2. **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.
|
||
|
|
3. **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.
|
||
|
|
|
||
|
|
1. **Red**: Write unit tests (`*.service.test.ts`) using Vitest. Mock all dependencies (e.g., repositories, event emitters) using `vi.fn()`. Write tests asserting correct handling of successful cases, expected errors (`NotFoundError`, `ConflictError`, etc.), and household isolation boundaries.
|
||
|
|
2. **Green**: Create the Service class with constructor injection via Awilix. Implement only the minimum logic required to pass the test cases.
|
||
|
|
3. **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.
|
||
|
|
|
||
|
|
1. **Red**: Write a route test (`*.routes.test.ts`) using Fastify's `app.inject()`. Assert status codes, headers, and the response body structure. Mock the service layer resolved from the request DI scope (`request.diScope.resolve`).
|
||
|
|
2. **Green**: Implement the Fastify route plugin, declare path validation schemas, resolve the service from the DI container, and call it. Register the plugin.
|
||
|
|
3. **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).
|
||
|
|
|
||
|
|
1. **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.
|
||
|
|
2. **Green**: Implement the frontend service in `src/services/` using fetch. Run tests and verify MSW handlers return correctly.
|
||
|
|
3. **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.
|
||
|
|
|
||
|
|
1. **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.
|
||
|
|
2. **Green**: Implement the React component (Server/Client split as appropriate). Write the bare minimum JSX/TSX to satisfy the test roles and events.
|
||
|
|
3. **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:
|
||
|
|
1. **Root package.json**:
|
||
|
|
- Add `"test:watch": "turbo run test:watch"`
|
||
|
|
2. **packages/web/package.json**:
|
||
|
|
- Add `"test:watch": "vitest"` (mirroring packages/api)
|
||
|
|
|
||
|
|
This will allow developers to run:
|
||
|
|
* `npm run test:watch -w packages/api` to start a continuous, interactive Vitest runner for API files.
|
||
|
|
* `npm run test:watch -w packages/api -- src/modules/products/products.service.test.ts` to 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.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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:
|
||
|
|
```typescript
|
||
|
|
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:
|
||
|
|
|
||
|
|
1. **Red Commit**: When a new test suite is created and fails.
|
||
|
|
- `test(pantry): add failing test for spoilage calculation (RED)`
|
||
|
|
2. **Green Commit**: When the production code is completed and tests pass.
|
||
|
|
- `feat(pantry): implement spoilage calculation logic (GREEN)`
|
||
|
|
3. **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:
|
||
|
|
|
||
|
|
1. **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:watch` commands and mock helper directives.
|
||
|
|
2. **Update `docs/instructions/conventions.md`**:
|
||
|
|
- Re-orient the "Implementation Workflow" from "test-after" to TDD-first.
|
||
|
|
3. **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:
|
||
|
|
|
||
|
|
1. **Step 1: Codebase Tooling Infrastructure**
|
||
|
|
- Add `"test:watch": "vitest"` to `packages/web/package.json`.
|
||
|
|
- Add `"test:watch": "turbo run test:watch"` to root `package.json`.
|
||
|
|
2. **Step 2: Reusable Mock Helper**
|
||
|
|
- Create `packages/api/src/common/test/mock-repository.ts` containing the automated mock builder utility.
|
||
|
|
3. **Step 3: Document TDD & Update Guidelines**
|
||
|
|
- Create `docs/instructions/tdd.md` as the definitive guide.
|
||
|
|
- Update `ANTIGRAVITY.md`'s workflow section.
|
||
|
|
- Update `docs/instructions/conventions.md`'s workflow section.
|
||
|
|
4. **Step 4: Verification**
|
||
|
|
- Verify the codebase build and tests are completely operational.
|