Test Application
Run, validate, and report on test suites across the monorepo. Covers unit tests, integration tests, and end-to-end type safety verification for the Hono + tRPC + Drizzle + React + TanStack stack.
Overview
This skill executes the testing phase of the SDLC workflow. It runs Vitest test suites across all monorepo packages, validates end-to-end type safety, and produces a structured test report with pass/fail counts, coverage data, and actionable failure analysis.
Position in SDLC workflow:
requirements --> proposal --> design --> classify --> plan --> implement --> [test-app] --> deploy
|
+--> Can also be invoked standalone
When to Use This Skill
Required When
- •After Implementation - A feature, fix, or refactor has been implemented and needs validation
- •Before Pull Request - Verify all tests pass before opening or updating a PR
- •After Dependency Update - Confirm nothing broke after upgrading packages
- •After Merge Conflict Resolution - Re-validate after resolving conflicts
- •CI Failure Investigation - Diagnose and fix failing tests in CI
Skip When
- •No code changes have been made (documentation-only changes)
- •Running a quick type-check is sufficient (
pnpm turbo typecheck) - •The change is limited to configuration files with no runtime impact
Variables
- •
scope: $1 - What to test:all,server,client,shared, or a specific test file path (optional, default:all) - •
type: $2 - Test type:unit,integration,e2e,typecheck, orfull(optional, default:full) - •
branch_name: Current git branch name (auto-detected) - •
descriptive_name: Inherited from state.json if running in SDLC flow (auto-detected)
Instructions
Step 1: Assess the Current State
Before running tests, understand what needs testing:
- •Check git status - Identify changed files since the last passing state
- •Read
agentic/{branch-name}/state.json- Understand the workflow context (what was just implemented) - •Identify affected packages - Map changed files to
server,client, orshared
DECISION POINT: - If `scope` is specified, test only that scope - If `scope` is "all" or unspecified, test all packages but prioritize packages with changes - If running in SDLC flow, focus on packages affected by the latest implementation
Step 2: Verify Test Infrastructure
Confirm the test tooling is operational:
- •Check Vitest is installed -
pnpm vitest --versionfrom the workspace root - •Check turbo scripts exist - Verify
test,typecheckscripts in rootpackage.jsonand per-packagepackage.jsonfiles - •Check test configuration - Confirm
vitest.config.tsexists in relevant packages
If test infrastructure is missing or broken, stop and report. Do not attempt to fix test configuration as part of this skill -- that is a separate task.
Step 3: Run Type Checking
Type safety is the first line of defense in this stack. Run type checking before test execution:
pnpm turbo typecheck
What this validates:
- •Zod schemas in
shared/produce correct inferred types - •tRPC routers in
server/match the shared schemas - •tRPC client hooks in
client/match the server router types - •TanStack Router route params are type-safe
- •Drizzle table schemas align with Zod schemas
DECISION POINT: - If type checking fails, STOP. Report type errors immediately. Type errors in this stack indicate broken contracts between packages. They must be fixed before running runtime tests. - If type checking passes, proceed to Step 4.
Step 4: Run Tests by Scope
Execute tests based on the requested scope and type.
4a: Shared Package Tests (packages/shared)
pnpm turbo test --filter=shared
What to test:
- •Zod schema validation: valid inputs pass, invalid inputs reject with correct errors
- •Schema edge cases: boundary values, missing fields, extra fields
- •Type inference: schemas produce expected TypeScript types (compile-time, verified by typecheck)
- •Shared utilities: any helper functions in the shared package
4b: Server Package Tests (packages/server)
pnpm turbo test --filter=server
What to test:
- •tRPC procedures: Each query/mutation with valid and invalid inputs
- •Drizzle queries: Database operations return expected results (use in-memory SQLite or test database)
- •Hono middleware: Authentication, error handling, CORS
- •Input validation: tRPC + Zod integration rejects bad inputs with correct error shapes
- •Business logic: Domain-specific logic in service layers
Server integration tests (if type is integration or full):
- •tRPC router mounted on Hono responds correctly to HTTP requests
- •Database migrations apply cleanly
- •Full request lifecycle: HTTP request -> Hono -> tRPC -> Drizzle -> response
4c: Client Package Tests (packages/client)
pnpm turbo test --filter=client
What to test:
- •React components: Render correctly with expected props (React Testing Library)
- •TanStack Router routes: Route matching, parameter extraction, loader behavior
- •TanStack Query + tRPC hooks: Data fetching hooks return expected states (loading, success, error)
- •Form validation: Client-side Zod validation matches shared schemas
- •UI interactions: User events trigger correct state changes
4d: All Packages
pnpm turbo test
Runs all package tests in dependency order (shared -> server -> client).
4e: Specific File
pnpm vitest run {file_path}
Runs tests for a specific file. Useful for focused debugging.
Step 5: Run End-to-End Type Safety Verification
This is unique to this stack. Verify that the type chain is unbroken:
- •Zod schema -> tRPC router: Changing a Zod schema field name should produce a TypeScript error in the tRPC procedure that uses it
- •tRPC router -> client hook: Changing a tRPC procedure output shape should produce a TypeScript error in the React component consuming it
- •Drizzle schema -> Zod schema: If Drizzle and Zod schemas should match, verify consistency
This step is satisfied by Step 3 (typecheck) passing. If typecheck passes, the end-to-end type chain is intact.
Step 6: Collect Coverage (if type is full)
pnpm turbo test -- --coverage
Parse coverage output and extract:
- •Line coverage per package
- •Branch coverage per package
- •Uncovered files - Files with 0% coverage that should have tests
COVERAGE THRESHOLDS (recommended): - shared/: 90%+ line coverage (schemas and utilities should be well-tested) - server/: 80%+ line coverage (procedures and business logic) - client/: 70%+ line coverage (components, lower due to UI complexity)
Step 7: Analyze Failures
For any failing tests:
- •Read the failure output - Capture the full error message and stack trace
- •Categorize the failure:
- •Type error: Broken contract between packages (fix in shared schemas first)
- •Assertion failure: Logic bug (fix in the implementation)
- •Timeout: Async issue or missing mock (check test setup)
- •Import error: Missing dependency or incorrect path (check package.json)
- •Snapshot mismatch: Intentional change needs snapshot update, or unintentional regression
- •Identify root cause - Trace from the error back to the source
- •Suggest fix - Provide a specific, actionable fix for each failure
DECISION POINT: - If failures are minor (< 3 test failures, clear fixes): Include fixes in the report - If failures are systemic (type errors cascading, many failures): Report the root cause and recommend running /implement to fix before re-testing - If failures are in test infrastructure: Report and recommend fixing test setup separately
Step 8: Create Footprint and Update State
Document the test results and update state for workflow continuity.
Relevant Files
Test Configuration
- •
vitest.config.ts(root) - Workspace-level Vitest configuration - •
packages/server/vitest.config.ts- Server test configuration - •
packages/client/vitest.config.ts- Client test configuration - •
packages/shared/vitest.config.ts- Shared package test configuration
Test Files
- •
packages/shared/src/**/*.test.ts- Shared schema and utility tests - •
packages/server/src/**/*.test.ts- Server procedure and integration tests - •
packages/client/src/**/*.test.ts- Component tests - •
packages/client/src/**/*.test.tsx- React component tests
Configuration
- •
package.json(root) - Turbo scripts fortest,typecheck - •
tsconfig.json(root and per-package) - TypeScript configuration - •
turbo.json- Turborepo pipeline for test tasks
Stack-Specific
- •
packages/shared/src/schemas/- Zod schemas (source of truth for types) - •
packages/server/src/routers/- tRPC routers (consume shared schemas) - •
packages/server/src/db/schema.ts- Drizzle schema (should align with Zod) - •
packages/client/src/routes/- TanStack Router routes (consume tRPC types)
Test Patterns for This Stack
Testing a tRPC Procedure
// packages/server/src/routers/user.test.ts
import { createCaller } from "../trpc";
import { userRouter } from "./user";
const caller = createCaller({ db, session: mockSession });
test("getUser returns user by id", async () => {
const result = await caller.user.getById({ id: "123" });
expect(result).toMatchObject({ id: "123", name: "Test User" });
});
test("getUser rejects invalid id", async () => {
await expect(caller.user.getById({ id: "" })).rejects.toThrow();
});
Testing a Zod Schema
// packages/shared/src/schemas/user.test.ts
import { UserSchema } from "./user";
test("valid user passes validation", () => {
const result = UserSchema.safeParse({ name: "Alice", email: "alice@example.com" });
expect(result.success).toBe(true);
});
test("missing email fails validation", () => {
const result = UserSchema.safeParse({ name: "Alice" });
expect(result.success).toBe(false);
});
Testing a React Component with tRPC
// packages/client/src/components/UserProfile.test.tsx
import { render, screen } from "@testing-library/react";
import { UserProfile } from "./UserProfile";
import { createTRPCMsw } from "../test/utils";
test("renders user name", async () => {
render(<UserProfile userId="123" />, { wrapper: TestProviders });
expect(await screen.findByText("Alice")).toBeInTheDocument();
});
Testing a Drizzle Query
// packages/server/src/db/queries/user.test.ts
import { db } from "../test-db";
import { users } from "../schema";
import { eq } from "drizzle-orm";
test("insert and select user", async () => {
await db.insert(users).values({ id: "1", name: "Alice", email: "alice@test.com" });
const result = await db.select().from(users).where(eq(users.id, "1"));
expect(result[0].name).toBe("Alice");
});
Footprint and State Management
Footprint Creation
Create a footprint file at: agentic/{branch-name}/footprints/foot-test-app-{descriptive-name}.md
Footprint Template:
# Test Footprint: {descriptive-name}
**Date**: {ISO 8601 timestamp}
**Scope**: {all | server | client | shared | file path}
**Type**: {unit | integration | e2e | typecheck | full}
## Results Summary
| Package | Tests | Passed | Failed | Skipped | Duration |
|---------|-------|--------|--------|---------|----------|
| shared | {n} | {n} | {n} | {n} | {time} |
| server | {n} | {n} | {n} | {n} | {time} |
| client | {n} | {n} | {n} | {n} | {time} |
| **Total** | **{n}** | **{n}** | **{n}** | **{n}** | **{time}** |
## Type Check Result
- **Status**: {PASS | FAIL}
- **Errors**: {count} (0 if PASS)
- **Error details**: {list of type errors if any}
## Coverage (if collected)
| Package | Lines | Branches | Functions |
|---------|-------|----------|-----------|
| shared | {%} | {%} | {%} |
| server | {%} | {%} | {%} |
| client | {%} | {%} | {%} |
## Failures (if any)
### Failure 1: {test name}
- **Package**: {package}
- **File**: {file path}
- **Category**: {type error | assertion | timeout | import | snapshot}
- **Error**: {error message}
- **Root Cause**: {analysis}
- **Suggested Fix**: {actionable fix}
## Verdict
**{PASS | FAIL | PARTIAL}**
- PASS: All tests pass, type check clean, coverage meets thresholds
- FAIL: Test failures or type errors present
- PARTIAL: Some tests pass, non-critical failures remain
State File Update
Update the state file at: agentic/{branch-name}/state.json
{
"latest_footprint": "agentic/{branch-name}/footprints/foot-test-app-{descriptive-name}.md",
"latest_command": "test-app",
"next_command_metadata": {
"command": "/deploy",
"category": "testing",
"confidence": "{HIGH|MEDIUM|LOW}",
"reasoning": "Tests {passed|failed}, {ready|not ready} for deployment",
"required_context": "agentic/{branch-name}/footprints/foot-test-app-{descriptive-name}.md"
},
"next_command": "/deploy",
"test_timestamp": "{ISO 8601 timestamp}",
"branch_name": "{branch-name}",
"descriptive_name": "{descriptive-name}",
"test_scope": "{scope}",
"test_type": "{type}",
"test_verdict": "{PASS|FAIL|PARTIAL}",
"test_summary": {
"total_tests": "{count}",
"passed": "{count}",
"failed": "{count}",
"skipped": "{count}",
"typecheck_status": "{PASS|FAIL}",
"coverage_meets_threshold": "{true|false|not_collected}"
}
}
Required Actions
After completing the test run, you MUST:
- •Run type checking:
pnpm turbo typecheck - •Run tests: Per scope and type instructions above
- •Collect coverage: If
typeisfull - •Analyze failures: Categorize and suggest fixes for every failure
- •Create Footprint: Document results in
agentic/{branch-name}/footprints/foot-test-app-{descriptive-name}.md - •Update State: Update
agentic/{branch-name}/state.json
Report
Return a JSON object with the following structure:
{
"verdict": "PASS | FAIL | PARTIAL",
"scope": "all | server | client | shared | {file_path}",
"type": "unit | integration | e2e | typecheck | full",
"typecheck": {
"status": "PASS | FAIL",
"error_count": 0
},
"tests": {
"total": "{count}",
"passed": "{count}",
"failed": "{count}",
"skipped": "{count}",
"duration_ms": "{milliseconds}"
},
"coverage": {
"collected": true,
"shared_lines": "{percent}",
"server_lines": "{percent}",
"client_lines": "{percent}",
"meets_threshold": true
},
"failures": [
{
"test_name": "{name}",
"package": "{package}",
"file": "{file_path}",
"category": "type_error | assertion | timeout | import | snapshot",
"error": "{message}",
"suggested_fix": "{actionable fix}"
}
],
"next_command": "/deploy | /implement (if failures need fixing)",
"footprint_path": "agentic/{branch-name}/footprints/foot-test-app-{descriptive-name}.md",
"state_path": "agentic/{branch-name}/state.json"
}
Examples
Example 1: Full Test Run (All Packages)
/test-app all full
Runs typecheck, then all Vitest suites across shared, server, and client with coverage collection. Produces a comprehensive footprint with pass/fail counts, coverage percentages, and failure analysis.
Example 2: Server-Only Unit Tests
/test-app server unit
Runs only server package unit tests. Skips type checking and coverage. Useful for rapid iteration on tRPC procedures or Drizzle queries.
Example 3: Type Safety Verification
/test-app all typecheck
Runs only pnpm turbo typecheck across all packages. Verifies the Zod -> tRPC -> TanStack Query type chain is intact. Fast check before committing.
Example 4: Specific Test File
/test-app packages/server/src/routers/user.test.ts unit
Runs a single test file. Ideal for debugging a specific failing test.
Example 5: Post-Implementation in SDLC Flow
After /implement completes and updates state.json:
/test-app
Auto-detects scope from state.json (which packages were changed during implementation), runs a full test suite, and reports results. If all pass, recommends /deploy as the next command.
Integration with SDLC
In Full SDLC Flow
requirements --> proposal --> design --> classify --> plan --> implement --> [test-app] --> deploy
The test skill validates the implementation before deployment. It reads context from state.json to know what was implemented and focuses testing accordingly.
Standalone Usage
/test-app {scope} {type}
Can be used independently to test any part of the codebase at any time.
Feeding Back to Implementation
If tests fail, the next command becomes /implement instead of /deploy:
{
"next_command": "/implement",
"next_command_metadata": {
"reasoning": "3 test failures in server package require fixes before deployment",
"required_context": "agentic/{branch-name}/footprints/foot-test-app-{descriptive-name}.md"
}
}
The implementation skill should reference the test footprint to understand what needs fixing.
Feeding into Deployment
If tests pass, the state signals readiness for deployment:
{
"next_command": "/deploy",
"next_command_metadata": {
"reasoning": "All 47 tests pass, typecheck clean, coverage meets thresholds",
"required_context": "agentic/{branch-name}/footprints/foot-test-app-{descriptive-name}.md"
}
}