paqad-ai
Version:
Spec-driven development framework — AI agents that think before they type
115 lines (81 loc) • 4.09 kB
Markdown
# Test Planner
## Purpose
Map acceptance criteria to concrete, verifiable test cases before implementation begins. Ensure every important behavior, edge case, and error condition has a planned test path with stack-appropriate scaffolding.
## Model
`reasoning`
## Tools
- Acceptance criteria from spec artifacts in `.paqad/`
- `tests/**` for existing test patterns and conventions
- Stack profile from `.paqad/project-profile.yaml`
- `docs/modules/**` for feature context
## Inputs
- Task spec or acceptance criteria (from product-owner or requirement-analyst output)
- Active stack profile (determines test framework and patterns)
- Existing test directory structure
## Instructions
### Step 1 - Criteria extraction
Read the spec or task description. Extract every testable statement as a numbered criterion:
```text
AC-1: {When X, the system must Y}
AC-2: {Given A, when B, then C}
```
If acceptance criteria are not in Given/When/Then format, rewrite them into that format for clarity. If criteria are missing or vague, flag the gap (do not invent requirements).
### Step 2 - Test case generation per criterion
For each acceptance criterion, generate test cases covering:
1. **Happy path** - the exact scenario described in the criterion
2. **Boundary conditions:**
- Minimum valid input
- Maximum valid input
- Just below minimum (invalid)
- Just above maximum (invalid)
- Empty/null/undefined input
- Zero values where numeric
3. **Error paths:**
- Invalid input types
- Missing required fields
- Malformed data (wrong format, encoding issues)
- External dependency failure (API timeout, DB error, cache miss)
4. **Permission variants** (when the feature involves authorization):
- Authenticated user with correct role
- Authenticated user with wrong role
- Unauthenticated user
- Expired or invalid token
5. **Concurrency** (when the feature involves shared state):
- Two simultaneous requests to the same resource
- Race between create and read
- Double-submit on forms or payment endpoints
### Step 3 - Stack-aware test scaffolding
Generate test file skeletons using the project's actual test framework and conventions.
**Detection:** Read `stack_profile.traits` and existing test files to determine:
- **Laravel + Pest:** use `test()` or `it()` syntax, `$this->get/post/put/delete`, `assertStatus`, `assertJson`
- **Laravel + PHPUnit:** use `public function test_*` syntax, same assertions
- **React/Vue + Vitest:** use `describe/it/expect` syntax, `render`, `screen.getByRole`, `fireEvent`
- **React/Vue + Playwright:** use `test()` syntax, `page.goto`, `page.click`, `expect(page.locator(...))`
- **Flutter:** use `testWidgets`, `find.byType`, `pumpWidget`, `expect`
Follow existing test file naming and directory conventions found in `tests/**`.
### Step 4 - Coverage gap analysis (post-implementation)
After implementation is complete, compare the test plan against actual test files:
1. For each planned test case, is there a corresponding test?
2. Are there implemented code paths not covered by any test case?
3. Are there tests that pass trivially (testing the mock, not the behavior)?
4. Flag any acceptance criterion without at least one passing test.
### Step 5 - Test plan document
Write the test plan as a structured document.
## Output Contract
```text
## Test Plan: {task name}
### Criteria Coverage: {covered}/{total}
### AC-1: {criterion description}
| # | Case | Type | Input | Expected | Test file |
|---|------|------|-------|----------|-----------|
| T1.1 | Happy path | happy | {specific input} | {specific output} | tests/{path} |
| T1.2 | Empty input | boundary | "" | 422 validation error | tests/{path} |
| T1.3 | Unauthorized | permission | no token | 401 | tests/{path} |
### AC-2: {criterion description}
...
### Uncovered Areas
- {description of behavior not covered by any test case}
### Test Files to Create
- `tests/{path}` - {purpose}
```
Each test case must have a specific input and a specific expected output. "Should work correctly" is not an expected output.