# Test-Driven Development (TDD) Guide — MeshiTrack

This guide serves as the definitive reference for writing, executing, and maintaining tests using a Test-Driven Development (TDD) methodology across all packages in the MeshiTrack monorepo.

---

## 1. Core Principles

TDD is a software development workflow where you **write tests first, before writing implementation code**. This ensures high reliability, enforces strict scoping of features, prevents untested paths, and results in cleaner designs.

### The Red-Green-Refactor Cycle

For every feature, schema, service, or route you build, follow this continuous loop:

1. **RED (Write a Failing Test)**:
   - Identify the small increment of behavior you want to add.
   - Write a test expressing that behavior.
   - Run the test and see it fail. **Compilation failures in TypeScript count as a valid "Red" state** for new types, interfaces, or class signatures. For logic, stub the signature and ensure it fails at runtime.
2. **GREEN (Write Minimal Code)**:
   - Write the absolute minimum production code required to satisfy the test (even if it's returning hardcoded values temporarily).
   - Run the test and see it pass.
3. **REFACTOR (Clean Up and Optimize)**:
   - Review your code (both production and test files). Remove duplication, improve readability, refine types, and optimize structure.
   - Run the test suite continuously to guarantee your refactoring did not break existing behavior (remains Green).

---

## 2. Directory Structure (Option B)

To keep production bundles completely clean, all test files and mock utilities live in a root-level `tests/` folder matching the structure of `src/`. **Never place tests inside `src/`.**

```
packages/api/
├── src/                          # 100% Pure Production Code
│   ├── modules/
│   │   └── products/
│   │       ├── products.service.ts
│   │       └── products.repository.ts
│   └── main.ts
└── tests/                        # 100% Test-First Files
    ├── helpers/
    │   └── mock-repository.ts    # Reusable mocking helpers
    ├── modules/
    │   └── products/
    │       ├── products.service.test.ts
    │       └── products.repository.test.ts
    └── schemas/
        └── product.schema.test.ts
```

---

## 3. Layer-by-Layer TDD Guide

### Layer 1: Shared Package (`packages/shared`)
The shared package contains domain types, enums, and Zod schemas.

1. **Red**: Write a test in `packages/shared/tests/validation/feature.schemas.test.ts` asserting validation behaviors for correct, incorrect, and edge-case payloads.
2. **Green**: Implement the validation schema and types in `packages/shared/src/validation/feature.schemas.ts`. Run `npm run test -w packages/shared` to verify it passes.
3. **Refactor**: Clean up custom messages, dry up repeated Zod rules, and ensure exports are barrelled correctly in `index.ts`.

---

### Layer 2: Schema & Repository (`packages/api`)
This layer handles persistence via Mongoose schemas and repositories.

1. **Red**: Write an integration test in `packages/api/tests/modules/<module>/<module>.repository.test.ts` using `mongodb-memory-server` asserting query conditions, indexes, and `householdId` filtering.
2. **Green**: Create the Mongoose schema inside `src/schemas/` and the Repository class in `src/modules/<module>/`. All reads must use `.lean().exec()`. Verify tests pass.
3. **Refactor**: Optimize indexes and verify schema options (e.g. `timestamps: true`).

---

### Layer 3: Service Layer (`packages/api`)
Services contain core business logic, transactional bounds, and use constructor-injected dependencies via Awilix.

To eliminate boilerplate, import the `createMockRepository` helper from `tests/helpers/mock-repository.ts` to mock all repository methods automatically:

```typescript
// packages/api/tests/modules/products/products.service.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ProductsService } from '../../../src/modules/products/products.service.js';
import { ProductsRepository } from '../../../src/modules/products/products.repository.js';
import { createMockRepository } from '../../helpers/mock-repository.js';

describe(ProductsService.name, () => {
  let service: ProductsService;
  let mockRepo: ReturnType<typeof createMockRepository<ProductsRepository>>;

  beforeEach(() => {
    vi.clearAllMocks();
    mockRepo = createMockRepository(ProductsRepository);
    service = new ProductsService({ productsRepository: mockRepo as any });
  });

  it('should find a product by household scope', async () => {
    // 1. RED: Write the test first, before service.findById exists
    mockRepo.findById.mockResolvedValue({ _id: '1', name: 'Apples' });
    const result = await service.getById('1', 'hh1');
    expect(result.name).toBe('Apples');
  });
});
```

1. **Red**: Run the test. Verify it fails due to compilation (if the service/method is missing) or runtime failure (if stubbed).
2. **Green**: Implement constructor injection and the method in `src/modules/<module>/<module>.service.ts`. Run the test to confirm it passes.
3. **Refactor**: Extract logic, reduce cognitive complexity, and ensure proper typing.

---

### Layer 4: Route Layer (`packages/api`)
Fastify routes define endpoints, validate payloads, and resolve services.

1. **Red**: Write a route test in `tests/modules/<module>/<module>.routes.test.ts` using Fastify's `app.inject()`. Mock the resolved service from the request DI scope. Assert status codes and payloads.
2. **Green**: Register the route plugin in `src/modules/<module>/<module>.routes.ts`, wire validations via Zod type providers, and call the service. Verify it passes.
3. **Refactor**: Clean up endpoint structures, route prefixes, and schema schemas.

---

### Layer 5: Web API Client (`packages/web`)
Frontend fetch wrapper utilizing MSW to mock backend requests.

1. **Red**: Write a service test in `packages/web/tests/services/<service>.test.ts`. Configure MSW to mock the HTTP responses. Assert response modeling and mapping.
2. **Green**: Implement the service wrapper using fetch in `packages/web/src/services/`. Verify it passes.
3. **Refactor**: Clean up payload mappings and parameter serialization.

---

### Layer 6: Web UI Components (`packages/web`)
React components and Next.js pages.

1. **Red**: Write a component test in `packages/web/tests/app/(dashboard)/<feature>/<Component>.test.tsx` using React Testing Library. Use accessible role queries (`screen.getByRole`) to assert loading/rendering states and user interactions.
2. **Green**: Write the minimal TSX/JSX inside `packages/web/src/` to satisfy the tests.
3. **Refactor**: Refine tailwind utility classes, modularize components, and check accessibility (ARIA tags).

---

## 4. Tooling & DX Guidelines

To make TDD fast and effective, use interactive watch-mode running continuously in the background. **Never run `npx`.**

### Continuous File-Specific Watch Mode (Fastest DX)
To start watch mode for a specific test file during a Red-Green cycle:
```bash
# Watch API Service tests
npm run test:watch -w packages/api -- tests/modules/products/products.service.test.ts

# Watch Web Component tests
npm run test:watch -w packages/web -- tests/app/dashboard/ProductCard.test.tsx
```

### Continuous Package-Wide Watch Mode
To start watch mode for all tests in a package:
```bash
# Continuous API package tests
npm run test:watch -w packages/api

# Continuous Web package tests
npm run test:watch -w packages/web
```

### Full-Suite Verification
Before concluding any implementation phase, confirm that the entire verification gate is fully operational:
```bash
npm run build      # Enforces typescript compile across all packages
npm run test:cov   # Verifies all tests pass and meet strict coverage gates
npm run lint       # Validates static conventions
```
