Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:06:46 +08:00
commit 867df4fed0
20 changed files with 4979 additions and 0 deletions

View File

@@ -0,0 +1,621 @@
---
name: systematic-testing
description: Use when writing tests, debugging failures, or investigating bugs - provides TDD guidance, test generation patterns, systematic debugging framework. Activates when user says "write tests", "this test fails", "debug this", mentions "TDD", "test coverage", or encounters errors/bugs.
---
# Systematic Testing Skill
## Purpose
Guide test-driven development (TDD), generate comprehensive test suites, and provide systematic debugging frameworks. Ensures code is well-tested and bugs are resolved methodically rather than through trial-and-error.
## Activation Triggers
Activate this skill when:
- User implements new functionality (auto-suggest tests)
- Tests fail (activate debugging framework)
- User says "write tests for this"
- User mentions "TDD" or "test-driven"
- User asks about debugging or troubleshooting
- User says "this bug..." or "error..."
- Before marking feature complete (verify test coverage)
## Core Capabilities
### 1. Test-Driven Development (TDD)
**For complete TDD workflow, use Skill tool to invoke: `dev-workflow:test-driven-development`**
**How to activate:**
```
Use Skill tool: Skill(skill: "dev-workflow:test-driven-development")
```
The `test-driven-development` skill provides:
- Full RED-GREEN-REFACTOR cycle explanation
- Strict TDD enforcement and discipline
- Anti-patterns and rationalizations to avoid
- Detailed examples and verification checklist
**Quick TDD Summary:**
1. **RED:** Write failing test first
2. **GREEN:** Write minimal code to pass
3. **REFACTOR:** Clean up while tests stay green
This skill focuses on test generation strategies and systematic debugging. For TDD methodology details, use the dedicated skill.
---
### 2. Test Generation
**Goal:** Generate comprehensive test suites covering all scenarios
### Test Categories
#### Normal Cases (Happy Path)
Test expected, typical usage:
```javascript
describe('calculatePositionSize', () => {
it('should calculate correct position size for valid inputs', () => {
const result = calculatePositionSize(
10000, // account balance
0.02, // 2% risk
150.50, // entry price
148.00 // stop loss
);
expect(result).toBe(80); // $200 risk / $2.50 per share
});
it('should handle large account balances', () => {
const result = calculatePositionSize(
1000000, // $1M account
0.01, // 1% risk
100,
99
);
expect(result).toBe(10000);
});
});
```
#### Edge Cases (Boundary Conditions)
Test limits and boundaries:
```javascript
describe('calculatePositionSize - edge cases', () => {
it('should handle very small risk percentage', () => {
const result = calculatePositionSize(
10000,
0.001, // 0.1% risk
100,
99
);
expect(result).toBe(10); // $10 risk / $1 per share
});
it('should handle entry price equal to stop loss', () => {
expect(() => {
calculatePositionSize(10000, 0.02, 100, 100);
}).toThrow('Entry price cannot equal stop loss');
});
it('should handle very small price differences', () => {
const result = calculatePositionSize(
10000,
0.02,
100.10,
100.00
);
expect(result).toBe(2000); // $200 / $0.10
});
it('should round down fractional shares', () => {
const result = calculatePositionSize(
10000,
0.02,
150.75, // Creates fractional result
148.00
);
// Should be whole number, not fractional
expect(Number.isInteger(result)).toBe(true);
});
});
```
#### Error Cases (Invalid Inputs)
Test error handling:
```javascript
describe('calculatePositionSize - error cases', () => {
it('should reject negative account balance', () => {
expect(() => {
calculatePositionSize(-10000, 0.02, 100, 99);
}).toThrow('Account balance must be positive');
});
it('should reject zero account balance', () => {
expect(() => {
calculatePositionSize(0, 0.02, 100, 99);
}).toThrow('Account balance must be positive');
});
it('should reject risk percentage over 100%', () => {
expect(() => {
calculatePositionSize(10000, 1.5, 100, 99);
}).toThrow('Risk percentage must be between 0 and 1');
});
it('should reject negative risk percentage', () => {
expect(() => {
calculatePositionSize(10000, -0.02, 100, 99);
}).toThrow('Risk percentage must be between 0 and 1');
});
it('should handle null inputs gracefully', () => {
expect(() => {
calculatePositionSize(null, 0.02, 100, 99);
}).toThrow();
});
it('should handle undefined inputs gracefully', () => {
expect(() => {
calculatePositionSize(undefined, 0.02, 100, 99);
}).toThrow();
});
});
```
#### Integration Cases
Test component interactions:
```javascript
describe('login flow - integration', () => {
it('should complete full authentication flow', async () => {
// Test entire flow from request to response
const response = await request(app)
.post('/api/auth/login')
.send({
email: 'user@example.com',
password: 'SecureP@ss123'
});
// Verify response
expect(response.status).toBe(200);
expect(response.body.data.accessToken).toBeTruthy();
// Verify token is valid
const decoded = jwt.verify(
response.body.data.accessToken,
process.env.JWT_SECRET
);
expect(decoded.id).toBeTruthy();
// Verify user can access protected route
const protectedResponse = await request(app)
.get('/api/user/profile')
.set('Authorization', `Bearer ${response.body.data.accessToken}`);
expect(protectedResponse.status).toBe(200);
});
it('should prevent access with expired token', async () => {
// Create expired token
const expiredToken = jwt.sign(
{ id: 'user-id' },
process.env.JWT_SECRET,
{ expiresIn: '-1h' } // Already expired
);
// Attempt to access protected route
const response = await request(app)
.get('/api/user/profile')
.set('Authorization', `Bearer ${expiredToken}`);
expect(response.status).toBe(401);
expect(response.body.error.code).toBe('TOKEN_EXPIRED');
});
});
```
### Test Generation Template
```javascript
describe('[Component/Function Name]', () => {
// Setup and teardown
beforeEach(() => {
// Reset state, create test data
});
afterEach(() => {
// Clean up, reset mocks
});
// NORMAL CASES
describe('normal operation', () => {
it('should [expected behavior for typical input]', () => {
// Test implementation
});
});
// EDGE CASES
describe('edge cases', () => {
it('should handle [boundary condition]', () => {
// Test implementation
});
});
// ERROR CASES
describe('error handling', () => {
it('should reject [invalid input]', () => {
expect(() => {
// Call with invalid input
}).toThrow('Expected error message');
});
});
// INTEGRATION
describe('integration', () => {
it('should work with [other component]', () => {
// Test interaction
});
});
});
```
---
## 3. Systematic Debugging Framework
**Goal:** Resolve bugs methodically, not through random trial-and-error
**Four-Phase Framework:**
### Phase 1: Root Cause Investigation
**Goal:** Understand exactly what's going wrong
**Process:**
1. **Reproduce Bug Consistently**
```
Steps to reproduce:
1. Navigate to login page
2. Enter email: user@example.com
3. Enter password: TestPass123
4. Click submit
5. Observe: Error message "Network request failed"
Reproducibility: 10/10 attempts failed
```
2. **Identify Symptoms**
- What's the visible error?
- What error messages appear?
- What's the expected vs actual behavior?
3. **Gather Evidence**
```bash
# Check logs
tail -f logs/app.log
# Check network requests (browser dev tools)
# Check console errors
# Check server logs
```
Collect:
- Error messages (full text)
- Stack traces
- Log entries
- Network requests/responses
- Input values that trigger bug
4. **Form Initial Hypothesis**
```
Hypothesis: API endpoint is returning 500 error instead of 400
Evidence:
- Console shows "Network request failed"
- Server logs show 500 error at time of attempt
- Database logs show constraint violation
Next step: Check what's causing the 500
```
### Phase 2: Pattern Analysis
**Goal:** Identify when bug occurs and when it doesn't
**Questions to Answer:**
1. **When Does It Fail?**
- Specific inputs?
- Certain users?
- Particular time of day?
- After specific sequence of actions?
2. **When Does It Work?**
- Any inputs that succeed?
- Any users unaffected?
- Worked before, broken now?
3. **What Changed Recently?**
```bash
# Check recent commits
git log --since="2 days ago" --oneline
# Check what changed in specific file
git log -p path/to/file.js
# See when bug was introduced
git bisect
```
4. **Environmental Factors?**
- Works locally, fails in production?
- Browser-specific?
- Network conditions?
- Load-related (works with 1 user, fails with 100)?
**Pattern Analysis Example:**
```
Bug Pattern Analysis:
FAILS when:
- Email contains + symbol (e.g., user+test@example.com)
- Password is exactly 8 characters
- User account was created after 2025-01-01
WORKS when:
- Email is standard format (user@example.com)
- Password is 9+ characters
- User account is older
Pattern identified: Email validation regex doesn't handle + symbol
```
### Phase 3: Hypothesis Testing
**Goal:** Test theories systematically until root cause found
**Process:**
1. **Create Minimal Test Case**
```javascript
// Isolate the bug
it('should accept email with plus symbol', () => {
const email = 'user+test@example.com';
const result = validateEmail(email);
expect(result).toBe(true); // Currently fails
});
```
2. **Add Instrumentation**
```javascript
function validateEmail(email) {
console.log('Input email:', email);
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const isValid = regex.test(email);
console.log('Regex test result:', isValid);
console.log('Regex used:', regex);
return isValid;
}
```
3. **Test Hypothesis**
```javascript
// Hypothesis: Regex doesn't handle + symbol
// Test with minimal input
validateEmail('user+test@example.com');
// Output:
// Input email: user+test@example.com
// Regex test result: true
//
// Hypothesis REJECTED: Regex accepts + symbol
// Form new hypothesis: Something else is rejecting it
// Check database constraint
// Found: Database column has CHECK constraint excluding +
// Hypothesis CONFIRMED!
```
4. **Iterate Until Root Cause Found**
Keep forming and testing hypotheses until you identify exact cause.
### Phase 4: Implementation
**Goal:** Fix bug permanently with regression protection
**Process:**
1. **Write Test Reproducing Bug**
```javascript
// This test should FAIL before fix
it('should accept email with plus symbol', async () => {
const email = 'user+test@example.com';
const password = 'SecureP@ss123';
const response = await request(app)
.post('/api/auth/register')
.send({ email, password });
expect(response.status).toBe(201);
expect(response.body.user.email).toBe(email);
});
```
Run test - verify it fails:
```bash
npm test
# ✗ should accept email with plus symbol
# Expected: 201, Received: 400
```
2. **Fix the Bug**
```sql
-- Remove overly restrictive database constraint
ALTER TABLE users DROP CONSTRAINT email_format_check;
-- Or update to correct constraint
ALTER TABLE users ADD CONSTRAINT email_format_check
CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$');
```
3. **Verify Test Passes**
```bash
npm test
# ✓ should accept email with plus symbol
```
4. **Add Regression Tests**
```javascript
// Add more tests for similar edge cases
describe('email validation - special characters', () => {
const validEmails = [
'user+test@example.com',
'user.name@example.com',
'user_name@example.com',
'user-name@example.com',
'user123@example.com'
];
validEmails.forEach(email => {
it(`should accept ${email}`, async () => {
const response = await request(app)
.post('/api/auth/register')
.send({ email, password: 'SecureP@ss123' });
expect(response.status).toBe(201);
});
});
});
```
5. **Document Root Cause**
```javascript
/*
* Bug Fix: Email validation now accepts RFC-compliant characters
*
* Root Cause:
* Database CHECK constraint was too restrictive, excluding the + symbol
* which is valid in email addresses per RFC 5322.
*
* Fix:
* Updated CHECK constraint to use proper regex pattern matching RFC 5322.
*
* Date: 2025-01-17
* Related Issue: #234
*/
```
6. **Commit Fix with Context**
```bash
git add migrations/003_fix_email_constraint.sql
git add tests/auth/register.test.js
git commit -m "fix: Accept + symbol in email addresses
Database CHECK constraint was rejecting valid emails containing
the + symbol. Updated constraint to match RFC 5322 standard.
Closes #234"
```
---
## Test Coverage Analysis
**Goal:** Identify untested code
**Check Coverage:**
```bash
# Generate coverage report
npm run test:coverage
# Typical output:
# File | % Stmts | % Branch | % Funcs | % Lines |
# ---------------|---------|----------|---------|---------|
# src/auth.js | 87.5 | 75.0 | 100.0 | 87.5 |
# src/orders.js | 45.2 | 33.3 | 60.0 | 45.2 |
```
**Identify Gaps:**
- Functions without any tests
- Branches not covered (if statements, error paths)
- Edge cases not tested
- Integration paths missing
**Prioritize:**
1. Critical business logic (highest priority)
2. Security-sensitive code
3. Complex algorithms
4. Error handling paths
5. Edge cases
## Best Practices
### TDD Best Practices
1. **Always write test first**: No exceptions
2. **One test at a time**: Don't write multiple tests before implementing
3. **Smallest possible step**: Each cycle should be quick (5-10 minutes)
4. **Test behavior, not implementation**: Don't test private methods
5. **Keep tests simple**: Tests should be easier to understand than code
### Test Quality
1. **Clear test names**: Should read like documentation
2. **Arrange-Act-Assert**: Structure tests consistently
3. **One assertion per test**: Makes failures clear
4. **No logic in tests**: Tests should be simple data
5. **Independent tests**: No test depends on another
### Debugging Best Practices
1. **Reproduce first**: Can't fix what you can't reproduce
2. **Understand before fixing**: Don't guess and check
3. **Fix root cause**: Don't just treat symptoms
4. **Add regression test**: Prevent bug from returning
5. **Document why**: Help future debuggers
## Integration with Superpowers
**If `superpowers:test-driven-development` available:**
- Use for guided TDD workflow
- Enhanced RED-GREEN-REFACTOR cycle
- Additional TDD best practices
**If `superpowers:systematic-debugging` available:**
- Use for complex debugging scenarios
- Root cause tracing framework
- Advanced instrumentation guidance
## Common Anti-Patterns to Avoid
❌ Writing tests after implementation
❌ Changing tests to match implementation
❌ Testing implementation details instead of behavior
❌ Skipping refactor phase
❌ Testing code that's already tested (overwriting working tests)
❌ Making multiple changes before testing
❌ Debugging by randomly changing code
❌ Committing "console.log" debugging code
## Notes
- TDD is slower initially but faster overall (fewer bugs, less debugging)
- Good tests are documentation that never gets outdated
- Debugging is detective work, not guessing
- Always add regression tests after fixing bugs
- Test coverage is a minimum bar, not a goal