agentleFS
Sign inSign up

reskill / rules

kanyun-inc/reskill/.cursor/rules/testing.mdc

reskill uses a two-layer testing strategy to ensure code quality and functionality: Core Principle: Integration tests are the ultimate guarantee of functionality. Unit tests may all pass, but if integration tests fail, users will encounter issues in actual use. All CLI feature development must follow the Spec-Driven TDD workflow (see docs/cli-spec.md): Integration tests verify actual user behavior: Unit tests may cover all internal functions, but cannot guarantee: - CLI argument parsing is correct - Command output format meets expectations -…

Cursor rule59 starsChanged 7 months ago
---
alwaysApply: true
---

# Testing Rules

## Testing Philosophy

reskill uses a **two-layer testing strategy** to ensure code quality and functionality:

| Test Type | Purpose | Frequency |
|-----------|---------|-----------|
| **Unit Tests** | Ensure code quality and logic correctness | Run continuously during development |
| **Integration Tests** | Ensure CLI API usability | Must pass before commit |

**Core Principle: Integration tests are the ultimate guarantee of functionality.** Unit tests may all pass, but if integration tests fail, users will encounter issues in actual use.

## Spec-Driven TDD Workflow

All CLI feature development must follow the **Spec-Driven TDD** workflow (see `docs/cli-spec.md`):

```
┌─────────────────────────────────────────────────────────────────┐
│  1. SPEC      Modify docs/cli-spec.md to define expected behavior│
│  2. REVIEW    Review spec changes                                │
│  3. TEST      Write failing tests based on spec (RED)            │
│  4. CODE      Write minimal code to pass tests (GREEN)           │
│  5. REFACTOR  Refactor and optimize, keep tests passing          │
│  6. VERIFY    pnpm test:run && pnpm test:integration             │
└─────────────────────────────────────────────────────────────────┘
```

### Benefits of Test-First Approach

| Benefit | Description |
|---------|-------------|
| **Spec as Documentation** | `docs/cli-spec.md` is the single source of truth for command behavior |
| **Tests as Specification** | Test code verifies correct implementation of spec |
| **Prevent Regression** | Any breaking changes will be caught by tests |
| **Design-Driven** | Think about interfaces first, then implement details |

### Example: Adding `--dry-run` Option

```bash
# Step 1: Modify docs/cli-spec.md, add --dry-run option definition
# Step 2: PR Review spec changes
# Step 3: Write failing tests
#   - src/cli/commands/__integration__/install-*.test.ts (integration)
#   - src/cli/commands/install.test.ts (unit)
# Step 4: Implement --dry-run logic
# Step 5: Refactor code
# Step 6: Verify all tests pass
```

## Integration Tests

### Why Integration Tests Matter

Integration tests verify **actual user behavior**:

```bash
# These are the commands users actually run
reskill init -y
reskill install github:user/skill@v1.0.0
reskill list
reskill uninstall skill-name -y
```

Unit tests may cover all internal functions, but cannot guarantee:
- CLI argument parsing is correct
- Command output format meets expectations
- File creation/modification behavior is correct
- Exit codes conform to specification

### Integration Test Location

```
src/cli/commands/__integration__/
├── helpers.ts              # Shared test utilities
├── basic.test.ts           # --version, --help, init, list, info, outdated
├── install-symlink.test.ts # install with symlink mode
├── install-copy.test.ts    # install with copy mode
├── install-global.test.ts  # install -g (global)
├── uninstall.test.ts       # uninstall command
└── update.test.ts          # update command
```

### Integration Test Helpers

Use shared utilities from `helpers.ts`:

```typescript
import {
  runCli,              // Execute CLI command
  getOutput,           // Get combined stdout + stderr
  createTempDir,       // Create isolated temp directory
  removeTempDir,       // Clean up temp directory
  createMockSkill,     // Create mock skill directory
  createLocalGitRepo,  // Create local git repo for testing
  setupSkillsJson,     // Setup skills.json config
  readSkillsJson,      // Read skills.json
  pathExists,          // Check path exists
  isSymlink,           // Check if path is symlink
} from './helpers.js';
```

### Integration Test Pattern

```typescript
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import {
  createTempDir,
  removeTempDir,
  runCli,
  pathExists,
} from './helpers.js';

describe('CLI Integration: command-name', () => {
  let tempDir: string;

  beforeEach(() => {
    tempDir = createTempDir();
    runCli('init -y', tempDir);  // Setup project
  });

  afterEach(() => {
    removeTempDir(tempDir);  // Clean up
  });

  describe('basic functionality', () => {
    it('should perform expected action', () => {
      const { stdout, exitCode } = runCli('command args', tempDir);
      
      expect(exitCode).toBe(0);
      expect(stdout).toContain('expected output');
    });
  });

  describe('error handling', () => {
    it('should handle invalid input gracefully', () => {
      const { exitCode, stderr } = runCli('command invalid', tempDir);
      
      expect(exitCode).toBe(1);
      expect(stderr).toContain('error message');
    });
  });
});
```

### When to Add Integration Tests

**MUST add integration tests when:**

1. **Adding new CLI command** - Test all options and arguments
2. **Modifying command behavior** - Verify output and side effects
3. **Changing file operations** - Verify files are created/modified correctly
4. **Fixing user-reported bugs** - Reproduce the issue at CLI level

**Integration test checklist for new commands:**

- [ ] `--help` shows correct usage
- [ ] Success case returns exit code 0
- [ ] Error case returns exit code 1
- [ ] Output messages are correct
- [ ] Files are created/modified as expected
- [ ] Works with different options combinations

### Running Integration Tests

```bash
# Build first, then run integration tests
pnpm test:integration

# Or manually
pnpm build
pnpm vitest run --config vitest.integration.config.ts
```

## Unit Tests

### Purpose

Unit tests ensure **internal code logic** correctness:

- Function input/output meets expectations
- Edge cases are handled correctly
- Error conditions are properly caught
- Type safety is ensured

### Unit Test Location

Unit tests are placed in the same directory as source files:

```
src/core/
├── skill-manager.ts
├── skill-manager.test.ts     # Unit test for skill-manager
├── config-loader.ts
├── config-loader.test.ts     # Unit test for config-loader
```

### Unit Test Pattern

```typescript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';

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

  beforeEach(() => {
    tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'reskill-test-'));
  });

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

  describe('methodName', () => {
    it('should return expected value for valid input', () => {
      // Arrange
      const input = 'valid-input';
      
      // Act
      const result = methodName(input);
      
      // Assert
      expect(result).toBe('expected');
    });

    it('should throw error for invalid input', () => {
      expect(() => methodName('invalid')).toThrow('error message');
    });
  });
});
```

### Running Unit Tests

```bash
# Watch mode (development)
pnpm test

# Single run
pnpm test:run

# With coverage
pnpm test:coverage
```

## Bug Fix Protocol (TDD)

All bug fixes **MUST** follow this protocol:

### Step 1: Reproduce with Test

Write a **failing test** that reproduces the bug:

```typescript
describe('bug fixes', () => {
  it('should handle edge case X (fixes #123)', () => {
    // This test reproduces the reported bug
    // It should FAIL before the fix is applied
    
    const result = functionWithBug('edge-case-input');
    expect(result).toBe('correct-output');
  });
});
```

### Step 2: Fix the Bug

Implement the **minimal fix** to make the test pass.

### Step 3: Verify

- Unit test passes
- Integration test passes (if CLI-related)
- No regression in other tests

### Step 4: Document

The test itself serves as documentation:

```typescript
it('should list skills from .agents/skills/ directory (fixes #42)', () => {
  // Bug: list() was checking .skills/ instead of .agents/skills/
  // Fix: Updated getCanonicalSkillsDir() to return correct path
});
```

## Test Coverage Guidelines

### Coverage Targets

| Category | Target | Rationale |
|----------|--------|-----------|
| Core modules (`src/core/`) | 80%+ | Business logic must be well-tested |
| CLI commands (`src/cli/`) | 70%+ | Commands have integration test coverage |
| Utilities (`src/utils/`) | 90%+ | Utilities are foundational |

### What to Test

**Always test:**
- Public API functions
- Error handling paths
- Edge cases (empty input, null, undefined)
- Boundary conditions

**Skip testing:**
- Private implementation details
- Simple getters/setters
- Third-party library wrappers (mock instead)

## Test Commands Reference

```bash
# Unit tests
pnpm test              # Watch mode
pnpm test:run          # Single run
pnpm test:coverage     # With coverage report

# Integration tests
pnpm test:integration  # Build + run integration tests

# All tests
pnpm test:run && pnpm test:integration

# Specific test file
pnpm vitest run src/core/skill-manager.test.ts
pnpm vitest run src/cli/commands/__integration__/basic.test.ts
```

## Pre-commit Checklist

Before committing changes:

1. [ ] `pnpm test:run` - All unit tests pass
2. [ ] `pnpm test:integration` - All integration tests pass
3. [ ] `pnpm typecheck` - No type errors
4. [ ] `pnpm lint` - No linting errors

For CLI-related changes, also verify manually:

```bash
pnpm build
node dist/cli/index.js --help
node dist/cli/index.js <your-command> <args>
```

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.