home / skills / jmagly / aiwg / test-sync

This skill helps maintain test-suite alignment by detecting orphaned tests, missing tests, and implementation-coupled tests to streamline cleanup and

npx playbooks add skill jmagly/aiwg --skill test-sync

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
8.1 KB
---
name: test-sync
description: Detect orphaned tests, obsolete assertions, and test-code misalignment. Use for test suite maintenance, cleanup, and traceability validation.
version: 1.0.0
---

# Test Sync Skill

## Purpose

Maintain alignment between test files and source code. Detect orphaned tests (code deleted but tests remain), missing tests, and implementation-coupled tests. Based on UTRefactor research showing automated test maintenance can achieve 89% smell reduction.

## Research Foundation

| Concept | Source | Reference |
|---------|--------|-----------|
| Test Refactoring | UTRefactor (ACM 2024) | [89% smell reduction](https://dl.acm.org/doi/10.1145/3715750) |
| Test Smells | Meszaros (2007) | "xUnit Test Patterns" |
| Test-Code Traceability | IEEE TSE | Test maintenance research |

## When This Skill Applies

- After major refactoring
- During test suite health audits
- When tests fail for deleted code
- Before releases (cleanup validation)
- When test count seems disconnected from codebase

## Trigger Phrases

| Natural Language | Action |
|------------------|--------|
| "Find orphaned tests" | Detect tests for deleted code |
| "Sync tests with code" | Full alignment analysis |
| "Are my tests up to date?" | Test-code sync check |
| "Clean up test suite" | Find removable tests |
| "Test coverage gaps" | Find missing tests |

## Sync Analysis Types

### 1. Orphaned Test Detection

Tests that reference deleted or renamed code:

```python
def find_orphaned_tests(project_dir):
    """Find tests for code that no longer exists"""
    orphans = []

    for test_file in glob(f"{project_dir}/test/**/*.test.ts"):
        # Extract tested module from import/path
        tested_module = infer_tested_module(test_file)

        if not exists(tested_module):
            orphans.append({
                "test_file": test_file,
                "expected_source": tested_module,
                "status": "source_deleted"
            })

        # Check for unused test helpers
        for helper in extract_test_helpers(test_file):
            if not is_used_in_assertions(test_file, helper):
                orphans.append({
                    "test_file": test_file,
                    "item": helper,
                    "status": "unused_helper"
                })

    return orphans
```

### 2. Missing Test Detection

Source files without corresponding tests:

```python
def find_missing_tests(project_dir):
    """Find source files without tests"""
    missing = []

    for src_file in glob(f"{project_dir}/src/**/*.ts"):
        if is_testable(src_file):  # Exclude types, index files
            test_file = get_test_path(src_file)
            if not exists(test_file):
                missing.append({
                    "source": src_file,
                    "expected_test": test_file,
                    "functions": extract_public_functions(src_file),
                    "priority": assess_priority(src_file)
                })

    return missing
```

### 3. Implementation-Coupled Test Detection

Tests that test implementation details rather than behavior:

```python
COUPLING_PATTERNS = [
    # Testing private methods
    (r'\.\_\w+\(', "Tests private method"),

    # Testing internal state
    (r'\.__\w+', "Accesses internal state"),

    # Mocking too deeply
    (r'mock.*mock.*mock', "Over-mocking"),

    # Testing exact implementation
    (r'toHaveBeenCalledWith.*\{.*\{', "Assertion on implementation details"),
]

def find_coupled_tests(test_file):
    """Detect implementation-coupled tests"""
    content = read_file(test_file)
    issues = []

    for pattern, description in COUPLING_PATTERNS:
        matches = re.findall(pattern, content)
        if matches:
            issues.append({
                "pattern": pattern,
                "description": description,
                "count": len(matches),
                "risk": "Tests may break on safe refactors"
            })

    return issues
```

### 4. Test-Code Mapping

Verify traceability between tests and source:

```python
def build_test_map(project_dir):
    """Build mapping of tests to source files"""
    mapping = {}

    for test_file in glob(f"{project_dir}/test/**/*.test.ts"):
        source_file = infer_source(test_file)
        imports = extract_imports(test_file)

        mapping[test_file] = {
            "inferred_source": source_file,
            "actual_imports": imports,
            "coverage": get_coverage_for(test_file),
            "alignment": "aligned" if source_file in imports else "misaligned"
        }

    return mapping
```

## Output Format

```markdown
## Test Sync Report

**Project**: my-project
**Analysis Date**: 2024-12-12
**Test Files**: 45
**Source Files**: 78

### Summary

| Category | Count | Action |
|----------|-------|--------|
| Orphaned tests | 3 | Delete |
| Missing tests | 8 | Create |
| Implementation-coupled | 5 | Refactor |
| Aligned | 37 | None |

### Orphaned Tests (Safe to Delete)

#### 1. `test/auth/legacy-login.test.ts`
**Status**: Source deleted
**Original Source**: `src/auth/legacy-login.ts` (deleted in commit abc123)
**Last Modified**: 45 days ago
**Action**: DELETE

```bash
rm test/auth/legacy-login.test.ts
```

#### 2. `test/utils/string-helpers.test.ts`
**Status**: Function removed
**Details**: Tests `formatCurrency()` which was removed
**Action**: DELETE specific test, keep file

```typescript
// Remove this test block:
describe('formatCurrency', () => { ... });
```

### Missing Tests (Should Create)

#### 1. `src/payment/processor.ts` (HIGH PRIORITY)

**Reason**: Payment processing - critical path
**Public Functions**:
- `processPayment(amount, method)` - No test
- `refundPayment(transactionId)` - No test
- `validateCard(cardInfo)` - No test

**Suggested Test File**: `test/payment/processor.test.ts`

```typescript
// Scaffold
describe('PaymentProcessor', () => {
  describe('processPayment', () => {
    it('should process valid payment');
    it('should reject insufficient funds');
    it('should handle network errors');
  });

  describe('refundPayment', () => {
    it('should refund valid transaction');
    it('should reject invalid transaction');
  });
});
```

### Implementation-Coupled Tests (Refactor)

#### 1. `test/api/user-service.test.ts:45`

**Issue**: Tests private method `_validateEmail`
**Risk**: Will break on internal refactoring
**Current**:
```typescript
it('should validate email', () => {
  expect(service._validateEmail('[email protected]')).toBe(true);
});
```

**Suggested Fix**:
```typescript
it('should reject user with invalid email', () => {
  expect(() => service.createUser({ email: 'invalid' }))
    .toThrow('Invalid email');
});
```

### Test-Code Mapping

| Test File | Source File | Status |
|-----------|-------------|--------|
| test/auth/login.test.ts | src/auth/login.ts | ✅ Aligned |
| test/user/profile.test.ts | src/user/profile.ts | ✅ Aligned |
| test/api/old-client.test.ts | (deleted) | ❌ Orphaned |
| (missing) | src/payment/processor.ts | ⚠️ Missing |

### Recommendations

1. **Immediate**: Delete 3 orphaned test files
2. **This Sprint**: Create tests for `processor.ts` (critical)
3. **Debt Reduction**: Refactor 5 implementation-coupled tests
4. **Ongoing**: Add test-sync to CI pipeline

### CI Integration

Add to pre-commit or CI:

```yaml
- name: Test Sync Check
  run: |
    npx test-sync --project . --strict
    # Fails if orphaned tests or missing critical tests
```
```

## Cleanup Actions

### Safe Deletions (Automated)

```bash
# Delete orphaned test files
rm test/auth/legacy-login.test.ts
rm test/utils/old-helpers.test.ts

# Remove orphaned test blocks
sed -i '/describe.*formatCurrency/,/^});$/d' test/utils/string.test.ts
```

### Manual Review Required

- Tests for code moved to different module
- Tests that may cover shared utilities
- Tests with unclear naming

## Integration Points

- Works with `/check-traceability` command
- Reports to Test Architect
- Feeds into test health metrics
- Part of `/project-health-check`

## Script Reference

### test_sync.py
Run sync analysis:
```bash
python scripts/test_sync.py --project . --output report.md
```

### cleanup_orphans.py
Remove orphaned tests:
```bash
python scripts/cleanup_orphans.py --project . --dry-run
```

Overview

This skill detects orphaned tests, obsolete assertions, and test-to-code misalignment to keep TypeScript test suites healthy. It focuses on finding tests that reference deleted or moved code, identifying missing tests for testable source files, and flagging tests that are overly coupled to implementation details. Use it to maintain traceability, reduce test smells, and simplify test-suite cleanup before releases.

How this skill works

The analyzer scans project test and src directories to infer tested modules from imports and file paths, then checks whether those source files still exist. It extracts public functions to identify missing tests and applies pattern-based heuristics to detect implementation-coupled assertions (private method calls, internal-state access, over-mocking). Results are returned as a mapping of test files to inferred source, alignment status, and categorized findings (orphaned, missing, coupled).

When to use it

  • After large refactors or file renames
  • During test-suite health audits or sprint cleanup
  • When tests fail due to deleted or moved code
  • Before releases to remove stale tests and reduce noise
  • When test counts or coverage don’t reflect recent code changes

Best practices

  • Run the sync check in CI or pre-commit with a dry-run option before automated deletion
  • Treat findings as signals for human review—automated deletes only for clear orphaned files
  • Prioritize fixes by risk (critical production paths first) and add missing tests for public APIs
  • Refactor implementation-coupled tests to assert observable behavior, not private state
  • Record traceability in test metadata to simplify future mappings

Example use cases

  • Find and safely remove tests whose source files were deleted in a refactor
  • List source files that lack tests and scaffold suggested test files and test cases
  • Detect tests that call private helpers or assert internal structure, recommending behavior-driven rewrites
  • Build a test-to-source map for reporting and gating in CI pipelines
  • Run periodic test-sync audits and feed results into test health dashboards

FAQ

Will it automatically delete test files?

It can suggest and optionally remove clear orphaned test files, but automated deletion should be gated behind a dry-run and human review step.

How does it detect implementation-coupled tests?

It uses regex-based patterns to find private method access, internal-state references, deep mocking, and brittle assertions, then flags tests likely to break on safe refactors.