AgentSkillsCN

test-app

在Turborepo单体仓库中,使用Vitest运行并验证测试套件。测试服务器(Hono + tRPC + Drizzle)、客户端(React + TanStack Router/Query),以及共享(Zod模式)等软件包。在实现新功能、修复Bug,或在创建Pull Request之前,可使用此技能。

SKILL.md
--- frontmatter
name: test-app
description: Run and validate test suites across a Turborepo monorepo with Vitest. Tests server (Hono + tRPC + Drizzle), client (React + TanStack Router/Query), and shared (Zod schemas) packages. Use this skill after implementing features, fixing bugs, or before creating pull requests.
version: 1.0.0

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:

code
requirements --> proposal --> design --> classify --> plan --> implement --> [test-app] --> deploy
                                                                              |
                                                                              +--> Can also be invoked standalone

When to Use This Skill

Required When

  1. After Implementation - A feature, fix, or refactor has been implemented and needs validation
  2. Before Pull Request - Verify all tests pass before opening or updating a PR
  3. After Dependency Update - Confirm nothing broke after upgrading packages
  4. After Merge Conflict Resolution - Re-validate after resolving conflicts
  5. 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, or full (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:

  1. Check git status - Identify changed files since the last passing state
  2. Read agentic/{branch-name}/state.json - Understand the workflow context (what was just implemented)
  3. Identify affected packages - Map changed files to server, client, or shared
code
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:

  1. Check Vitest is installed - pnpm vitest --version from the workspace root
  2. Check turbo scripts exist - Verify test, typecheck scripts in root package.json and per-package package.json files
  3. Check test configuration - Confirm vitest.config.ts exists 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:

bash
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
code
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)

bash
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)

bash
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)

bash
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

bash
pnpm turbo test

Runs all package tests in dependency order (shared -> server -> client).

4e: Specific File

bash
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:

  1. Zod schema -> tRPC router: Changing a Zod schema field name should produce a TypeScript error in the tRPC procedure that uses it
  2. tRPC router -> client hook: Changing a tRPC procedure output shape should produce a TypeScript error in the React component consuming it
  3. 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)

bash
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
code
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:

  1. Read the failure output - Capture the full error message and stack trace
  2. 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
  3. Identify root cause - Trace from the error back to the source
  4. Suggest fix - Provide a specific, actionable fix for each failure
code
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 for test, 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

typescript
// 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

typescript
// 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

typescript
// 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

typescript
// 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:

markdown
# 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

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:

  1. Run type checking: pnpm turbo typecheck
  2. Run tests: Per scope and type instructions above
  3. Collect coverage: If type is full
  4. Analyze failures: Categorize and suggest fixes for every failure
  5. Create Footprint: Document results in agentic/{branch-name}/footprints/foot-test-app-{descriptive-name}.md
  6. Update State: Update agentic/{branch-name}/state.json

Report

Return a JSON object with the following structure:

json
{
  "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)

code
/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

code
/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

code
/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

code
/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:

code
/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

code
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

code
/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:

json
{
  "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:

json
{
  "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"
  }
}