# Test Generator

> **Purpose:** Analyze source code and generate comprehensive unit tests following the project's testing conventions.

---

## Invocation

```
/test-gen <file_path> [--framework vitest|jest|pytest|go]
```

---

## Process

### Step 1: Analyze the Target

Read the source file and identify:
- All exported functions/classes/methods
- Input types and return types
- Edge cases from conditional branches
- Dependencies that need mocking
- Existing test file (if any) to avoid duplicates

### Step 2: Determine Testing Framework

Auto-detect from project config:
- `vitest.config.*` → Vitest
- `jest.config.*` or `package.json[jest]` → Jest
- `pytest.ini` or `pyproject.toml[tool.pytest]` → pytest
- `*_test.go` files → Go testing

### Step 3: Generate Test Cases

For each function, generate:

1. **Happy path** — typical valid input → expected output
2. **Edge cases** — empty input, zero, null/undefined, boundary values
3. **Error paths** — invalid input → expected error/exception
4. **Type variations** — different valid types if function accepts union types

### Step 4: Structure the Test File

```typescript
import { describe, it, expect, vi } from 'vitest'
import { targetFunction } from '../path/to/source'

describe('targetFunction', () => {
  it('returns expected result for valid input', () => {
    expect(targetFunction('valid')).toBe('expected')
  })

  it('throws on null input', () => {
    expect(() => targetFunction(null)).toThrow()
  })

  it('handles empty string', () => {
    expect(targetFunction('')).toBe('')
  })
})
```

---

## Mocking Strategy

- **External APIs**: Always mock with `vi.mock()` or `jest.mock()`
- **Database calls**: Mock the client/ORM, not the database
- **File system**: Mock `fs` operations, never touch real files
- **Time**: Use `vi.useFakeTimers()` for time-dependent logic
- **Environment variables**: Set via `vi.stubEnv()` or test setup

---

## Rules

- Match the project's existing test style (describe/it vs test, assertion style)
- Place test files adjacent to source (`*.test.ts`) or in `tests/` mirror
- Never generate snapshot tests unless explicitly asked
- Aim for branch coverage, not just line coverage
- Each test should be independent — no shared mutable state
- Test names should describe behavior, not implementation

## Playground

<!DOCTYPE html><html><head><meta charset='utf-8'><style>*{box-sizing:border-box;margin:0;padding:0}body{background:#0d1117;color:#e6edf3;font-family:monospace;font-size:12px;height:100vh;display:flex;flex-direction:column;overflow:hidden}.header{background:#161b22;border-bottom:1px solid #30363d;padding:8px 14px;font-size:11px;color:#8b949e;display:flex;justify-content:space-between;align-items:center;flex-shrink:0}.title{color:#58a6ff;font-weight:bold;font-size:13px}.panels{display:flex;flex:1;overflow:hidden}.panel{flex:1;overflow:auto;padding:12px;border-right:1px solid #30363d}.panel:last-child{border-right:none}.label{font-size:10px;color:#8b949e;text-transform:uppercase;letter-spacing:.08em;margin-bottom:6px}pre{white-space:pre-wrap;word-break:break-word;line-height:1.5}</style></head><body><div class='header'><span class='title'>Test Generator</span><span>Example · SkillSlap</span></div><div class='panels'><div class='panel'><div class='label'>Input: Function spec</div><pre><span style='color:#8b949e'>Function: parseAmount(str)</span>
<span style='color:#8b949e'>Language: TypeScript / Vitest</span>

<span style='color:#8b949e'>Parses a currency string like</span>
<span style='color:#8b949e'>"$1,234.56" → 1234.56 (number).</span>
<span style='color:#8b949e'>Throws if input is not a valid</span>
<span style='color:#8b949e'>currency string.</span></pre></div><div class='panel'><div class='label'>Output: Test suite</div><pre><span style='color:#58a6ff'>describe('parseAmount', () =&gt; {</span>
<span style='color:#3fb950'>  it('parses USD with commas', () =&gt;</span>
<span style='color:#8b949e'>    expect(parseAmount('$1,234.56'))</span>
<span style='color:#8b949e'>      .toBe(1234.56))</span>

<span style='color:#3fb950'>  it('parses zero', () =&gt;</span>
<span style='color:#8b949e'>    expect(parseAmount('$0.00')).toBe(0))</span>

<span style='color:#3fb950'>  it('throws on empty string', () =&gt;</span>
<span style='color:#8b949e'>    expect(() =&gt; parseAmount(''))</span>
<span style='color:#8b949e'>      .toThrow())</span>

<span style='color:#3fb950'>  it('throws on plain text', () =&gt;</span>
<span style='color:#8b949e'>    expect(() =&gt; parseAmount('hello'))</span>
<span style='color:#8b949e'>      .toThrow())</span>
<span style='color:#58a6ff'>})</span></pre></div></div></body></html>