# Testing Rule

> **Enforced on all implementations.**

---

## When Applied

- Any new feature implementation
- Any bug fix
- Any code modification

---

## TDD Enforcement (Strict)

### Write Tests FIRST

```
1. Write failing test
2. Verify test fails
3. Write minimal code to pass
4. Verify test passes
5. Refactor
6. Repeat
```

### No Code Without Tests

```python
# ❌ NEVER
def new_feature():
    # Implementation without test
    ...

# ✅ ALWAYS
# First: tests/test_feature.py
def test_new_feature_does_x():
    result = new_feature()
    assert result == expected

# Then: src/feature.py
def new_feature():
    ...
```

---

## Coverage Target

### Aim for >80%

```bash
# Python
pytest --cov=src --cov-report=term-missing tests/

# TypeScript
jest --coverage
```

### What Must Be Covered

| Priority | Coverage |
|----------|----------|
| Business logic | Required |
| API endpoints | Required |
| Error paths | Required |
| Edge cases | Required |
| Utilities | Recommended |

---

## Test Structure

### Directory Layout
```
tests/
├── unit/           # Fast, isolated
├── integration/    # Multi-component
├── e2e/            # End-to-end
└── fixtures/       # Test data
```

### Test Naming
```python
def test_<what>_<when>_<expected>():
    pass

# Examples
def test_login_with_valid_credentials_returns_token():
def test_login_with_invalid_password_raises_error():
def test_user_creation_with_duplicate_email_fails():
```

---

## Test Patterns

### Python (pytest)
```python
# Parametrized
@pytest.mark.parametrize("input,expected", [
    (1, 2),
    (2, 4),
])
def test_double(input, expected):
    assert double(input) == expected

# Fixtures
@pytest.fixture
def user():
    return User(email="test@example.com")

def test_with_user(user):
    assert user.email == "test@example.com"
```

### TypeScript (Jest/Vitest)
```typescript
describe('UserService', () => {
  it('creates user with valid data', async () => {
    const user = await service.create(validData);
    expect(user.id).toBeDefined();
  });

  it.each([
    ['invalid@', 'Invalid email format'],
    ['', 'Email required'],
  ])('rejects %s with %s', async (email, error) => {
    await expect(service.create({ email })).rejects.toThrow(error);
  });
});
```

---

## Bug Fix Testing

### Always Write Regression Test First

```python
def test_bug_123_special_chars_in_password():
    """Regression test for issue #123.

    This test must FAIL before the fix is applied.
    """
    auth = AuthService()
    # This should fail before fix, pass after
    result = auth.login("user@test.com", "p@ss!#$%")
    assert result.success
```

---

## Enforcement

Violations trigger:
1. Code reviewer rejects PR
2. Coverage check fails
3. TDD guide agent intervenes
