---
name: testing-guidelines
description: Guide for writing tests. Use when adding new functionality, fixing bugs, or when tests are needed. Emphasizes integration tests, real-world fixtures, and regression coverage.
---

# Testing Guidelines

Follow these principles when writing tests for this codebase.

## Core Principles

### 1. Mock External Services, Use Real Fixtures

**ALWAYS** mock third-party network services. **ALWAYS** use fixtures based on real-world data.

- Fixtures must be scrubbed of PII (use dummy data like `foo@example.com`, `user-123`)
- Capture real API responses, then sanitize them
- Never make actual network calls in tests

### 2. Prefer Integration Tests Over Unit Tests

Focus on **end-to-end style tests** that validate inputs and outputs, not implementation details.

- Test the public interface, not internal methods
- Unit tests are valuable for edge cases in pure functions, but integration tests are the priority
- If refactoring breaks tests but behavior is unchanged, the tests were too coupled to implementation

### 3. Minimize Edge Case Testing

Don't test every variant of a problem.

- Cover the **common path** thoroughly
- Skip exhaustive input permutations
- Skip unlikely edge cases that add maintenance burden without value
- One representative test per category of input is usually sufficient

### 4. Always Add Regression Tests for Bugs

When a **bug** is identified, **ALWAYS** add a test that would have caught it.

- The test should fail before the fix and pass after
- Name it descriptively to document the bug
- This prevents the same bug from recurring

**Note:** Regression tests are for unintentional broken behavior (bugs), not intentional changes. Intentional feature removals, deprecations, or breaking changes do NOT need regression tests—these are design decisions, not defects.

### 5. Cover Every User Entry Point

**ALWAYS** have at least one basic test for each customer/user entry point.

- CLI commands, API endpoints, public/exported functions
- Test the common/happy path first
- This proves the entry point works at all

**Note:** "Entry point" means the public interface—exported functions, CLI commands, API routes. Internal/private functions are NOT entry points, even if they handle user-facing flags or options. Test entry points; internal functions get coverage through those tests.

### 6. Tests Validate Before Manual QA

Tests are how we validate **ANY** functionality works before manual testing.

- Write tests first or alongside code, not as an afterthought
- If you can't test it, reconsider the design
- Passing tests should give confidence to ship

## Technical Guidelines

### File Organization

- Test files use `*.test.ts` extension
- Co-locate tests with source: `foo.ts` → `foo.test.ts`

### Test Isolation

Every test must:
- Run independently without affecting other tests
- Use temporary directories for file operations
- Clean up resources in `afterEach` hooks

```typescript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';

describe('my feature', () => {
  let tempDir: string;

  beforeEach(() => {
    tempDir = join(tmpdir(), `warden-test-${Date.now()}`);
    mkdirSync(tempDir, { recursive: true });
  });

  afterEach(() => {
    rmSync(tempDir, { recursive: true, force: true });
  });

  it('does something with files', () => {
    writeFileSync(join(tempDir, 'test.ts'), 'content');
    // ... test code
  });
});
```

### Pure Function Tests

For pure functions without side effects, no special setup is needed:

```typescript
import { describe, it, expect } from 'vitest';
import { matchGlob } from './matcher.js';

describe('matchGlob', () => {
  it('matches exact paths', () => {
    expect(matchGlob('src/index.ts', 'src/index.ts')).toBe(true);
  });
});
```

## Running Tests

```bash
pnpm test              # Run all tests in watch mode
pnpm test:run          # Run all tests once
```

## Checklist Before Submitting

- [ ] New entry points have at least one happy-path test
- [ ] Bug fixes (not intentional changes) include a regression test
- [ ] External services are mocked with sanitized fixtures
- [ ] Tests validate behavior, not implementation
- [ ] No shared state between tests
