# Test-Driven Bug Fixing

> Write the failing test first, then fix the code. The test proves the bug exists and prevents regression.

## When to Use

- Fixing any bug that could recur
- When you need to prove a fix actually works
- Before refactoring code that has no tests
- When the bug is subtle and hard to verify manually

## The Pattern

```
1. Write a test that reproduces the bug (RED)
2. Verify the test fails for the right reason
3. Fix the code (GREEN)
4. Verify no other tests broke
5. Commit test + fix together
```

## Step-by-Step

### 1. Write the failing test

Before touching the implementation:

```typescript
// auth.test.ts — Add this test FIRST
it('should handle case-insensitive email login', async () => {
  // Create user with lowercase email
  await createUser({ email: 'user@example.com', password: 'test1234' });

  // Try login with uppercase email — THIS IS THE BUG
  const response = await app.inject({
    method: 'POST',
    url: '/api/auth/login',
    payload: { email: 'User@Example.COM', password: 'test1234' },
  });

  expect(response.statusCode).toBe(200);
  expect(response.json().token).toBeDefined();
});
```

### 2. Verify it fails correctly

Run the test and confirm:

- It fails (good — the bug exists)
- It fails for the **right reason** (401 Unauthorized, not 500 or timeout)
- The failure message matches the reported bug

```bash
npm test -- --grep "case-insensitive"
# Expected: FAIL — 401 Unauthorized (not 200)
```

### 3. Fix the implementation

Now fix the minimal code:

```typescript
// repository.ts — The fix
async findByEmail(email: string): Promise<User | null> {
  const { resources } = await this.container.items
    .query({
      query: 'SELECT * FROM c WHERE LOWER(c.email) = @email',
      parameters: [{ name: '@email', value: email.toLowerCase() }],
    })
    .fetchAll();
  return resources[0] || null;
}
```

### 4. Verify everything passes

```bash
npm test        # All tests pass (including the new one)
npm run typecheck  # No type errors
```

### 5. Commit together

```bash
git add -A
git commit -m "fix(auth): handle case-insensitive email login

Added LOWER() to email query in findByEmail.
Test: case-insensitive email login added to auth.test.ts"
```

## Testing Patterns by Domain

### API endpoint tests (Fastify)

```typescript
import { build } from '../test-helpers.js';

describe('POST /api/auth/login', () => {
  let app: FastifyInstance;

  beforeAll(async () => {
    app = await build(); // Factory that creates test app with mocks
  });

  afterAll(() => app.close());

  it('returns 200 with valid credentials', async () => {
    const res = await app.inject({
      method: 'POST',
      url: '/api/auth/login',
      payload: { email: 'admin@test.com', password: 'password123' },
    });
    expect(res.statusCode).toBe(200);
    expect(res.json()).toHaveProperty('token');
  });

  it('returns 401 with wrong password', async () => {
    const res = await app.inject({
      method: 'POST',
      url: '/api/auth/login',
      payload: { email: 'admin@test.com', password: 'wrong' },
    });
    expect(res.statusCode).toBe(401);
  });

  it('returns 400 with invalid email format', async () => {
    const res = await app.inject({
      method: 'POST',
      url: '/api/auth/login',
      payload: { email: 'not-an-email', password: 'password123' },
    });
    expect(res.statusCode).toBe(400);
  });
});
```

### Pure function tests (engine/library code)

```typescript
describe('calculateAutophagyConfidence', () => {
  it('returns 0 for 0 hours fasted', () => {
    expect(calculateAutophagyConfidence({ durationHours: 0 })).toBe(0);
  });

  it('returns high confidence after 24+ hours', () => {
    const result = calculateAutophagyConfidence({ durationHours: 30 });
    expect(result).toBeGreaterThan(0.7);
  });

  it('weighs duration at 40%', () => {
    const base = calculateAutophagyConfidence({ durationHours: 20 });
    const longer = calculateAutophagyConfidence({ durationHours: 30 });
    expect(longer).toBeGreaterThan(base);
  });
});
```

### Repository tests (with mocked DB)

```typescript
describe('UserRepository', () => {
  let repo: UserRepository;
  let mockContainer: MockContainer;

  beforeEach(() => {
    mockContainer = createMockContainer();
    repo = new UserRepository(mockContainer);
  });

  it('creates user with productId', async () => {
    const user = await repo.createUser({
      email: 'test@example.com',
      name: 'Test User',
    });

    expect(user.productId).toBe('lysnrai');
    expect(user.id).toBeDefined();
    expect(user.createdAt).toBeDefined();
  });
});
```

## Test Count as a Health Metric

Track test counts across sessions:

| Module           | Tests | Last Verified |
| ---------------- | ----- | ------------- |
| platform-service | 847   | 2026-02-28    |
| ChronoMind web   | 373   | 2026-02-27    |
| NomGap           | 419   | 2026-02-27    |

**If test count drops, something was deleted or broken.** Investigate immediately.

## Anti-Patterns

- **Modifying tests to make them pass** — The #1 rule: fix the code, not the test
- **Testing after fixing** — You lose the proof that the bug existed
- **Tests that test nothing** — `expect(true).toBe(true)` counts as a test but catches nothing
- **Snapshot tests for logic** — Snapshots are for UI; use assertions for business logic
- **Skipping tests in CI** — `.skip()` accumulates. Track and resolve skipped tests
- **Tests that depend on order** — Each test should be independent (use `beforeEach` not `beforeAll` for state)

## Agent-Specific Tips

When asking an agent to fix a bug:

```
Bug: Users with uppercase email can't log in.
1. First, write a failing test in auth.test.ts that reproduces this
2. Then fix the code
3. Run npm test to verify
4. Don't modify any existing tests
```

This structure prevents the agent from:

- Fixing without a test (no regression protection)
- Modifying existing tests to match broken behavior
- Over-engineering the fix

## Related Skills

- [07 — Systematic Debugging](./07-systematic-debugging.md)
- [12 — Pre-Release Quality Gates](./12-quality-gates.md)
- [13 — Module Pattern](./13-module-pattern.md)
