agentic-team-templates 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -1
- package/bin/cli.js +4 -1
- package/package.json +8 -3
- package/src/index.js +471 -7
- package/src/index.test.js +947 -0
- package/templates/testing/.cursorrules/advanced-techniques.md +596 -0
- package/templates/testing/.cursorrules/ci-cd-integration.md +603 -0
- package/templates/testing/.cursorrules/overview.md +163 -0
- package/templates/testing/.cursorrules/performance-testing.md +536 -0
- package/templates/testing/.cursorrules/quality-metrics.md +456 -0
- package/templates/testing/.cursorrules/reliability.md +557 -0
- package/templates/testing/.cursorrules/tdd-methodology.md +294 -0
- package/templates/testing/.cursorrules/test-data.md +565 -0
- package/templates/testing/.cursorrules/test-design.md +511 -0
- package/templates/testing/.cursorrules/test-types.md +398 -0
- package/templates/testing/CLAUDE.md +1134 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# Test Types
|
|
2
|
+
|
|
3
|
+
Guidelines for understanding and applying different types of tests effectively.
|
|
4
|
+
|
|
5
|
+
## Testing Trophy Strategy
|
|
6
|
+
|
|
7
|
+
The Testing Trophy prioritizes tests by confidence-to-cost ratio:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────┐
|
|
11
|
+
│ E2E │ Highest confidence, highest cost
|
|
12
|
+
├─────────────┤
|
|
13
|
+
│ │
|
|
14
|
+
│ Integration │ Best confidence/cost ratio
|
|
15
|
+
│ │
|
|
16
|
+
├─────────────┤
|
|
17
|
+
│ Unit │ Fast but narrow scope
|
|
18
|
+
├─────────────┤
|
|
19
|
+
│ Static │ Zero runtime cost
|
|
20
|
+
└─────────────┘
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Static Analysis (~10%)
|
|
24
|
+
|
|
25
|
+
Catch errors at compile/lint time before tests even run.
|
|
26
|
+
|
|
27
|
+
### What It Catches
|
|
28
|
+
|
|
29
|
+
- Type mismatches
|
|
30
|
+
- Null/undefined access
|
|
31
|
+
- Unused variables
|
|
32
|
+
- Import errors
|
|
33
|
+
- Formatting inconsistencies
|
|
34
|
+
- Common security issues
|
|
35
|
+
|
|
36
|
+
### Tools
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// TypeScript strict mode
|
|
40
|
+
{
|
|
41
|
+
"compilerOptions": {
|
|
42
|
+
"strict": true,
|
|
43
|
+
"noImplicitAny": true,
|
|
44
|
+
"strictNullChecks": true,
|
|
45
|
+
"noUncheckedIndexedAccess": true
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
// ESLint configuration
|
|
52
|
+
module.exports = {
|
|
53
|
+
extends: [
|
|
54
|
+
'eslint:recommended',
|
|
55
|
+
'plugin:@typescript-eslint/strict',
|
|
56
|
+
'plugin:security/recommended',
|
|
57
|
+
],
|
|
58
|
+
rules: {
|
|
59
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
60
|
+
'@typescript-eslint/no-unused-vars': 'error',
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Cost-Benefit
|
|
66
|
+
|
|
67
|
+
- **Cost**: Near zero (runs in editor/CI)
|
|
68
|
+
- **Confidence**: Catches ~20% of bugs
|
|
69
|
+
- **Speed**: Instant feedback
|
|
70
|
+
|
|
71
|
+
## Unit Tests (~20%)
|
|
72
|
+
|
|
73
|
+
Test isolated pieces of logic without dependencies.
|
|
74
|
+
|
|
75
|
+
### What to Unit Test
|
|
76
|
+
|
|
77
|
+
- Pure functions (input → output)
|
|
78
|
+
- Utility functions
|
|
79
|
+
- Data transformations
|
|
80
|
+
- Validation logic
|
|
81
|
+
- Algorithms
|
|
82
|
+
- Business rules
|
|
83
|
+
|
|
84
|
+
### Characteristics
|
|
85
|
+
|
|
86
|
+
- **Fast**: < 10ms per test
|
|
87
|
+
- **Isolated**: No I/O, no databases, no network
|
|
88
|
+
- **Deterministic**: Same result every time
|
|
89
|
+
- **Focused**: One assertion per concept
|
|
90
|
+
|
|
91
|
+
### Example
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { describe, it, expect } from 'vitest';
|
|
95
|
+
import { calculateDiscount, formatCurrency, validateEmail } from './utils';
|
|
96
|
+
|
|
97
|
+
describe('calculateDiscount', () => {
|
|
98
|
+
it('applies percentage discount correctly', () => {
|
|
99
|
+
expect(calculateDiscount(100, { type: 'percentage', value: 10 })).toBe(90);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('applies fixed discount correctly', () => {
|
|
103
|
+
expect(calculateDiscount(100, { type: 'fixed', value: 15 })).toBe(85);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('does not produce negative values', () => {
|
|
107
|
+
expect(calculateDiscount(10, { type: 'fixed', value: 100 })).toBe(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('handles zero amount', () => {
|
|
111
|
+
expect(calculateDiscount(0, { type: 'percentage', value: 50 })).toBe(0);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('validateEmail', () => {
|
|
116
|
+
it.each([
|
|
117
|
+
['test@example.com', true],
|
|
118
|
+
['user.name@domain.org', true],
|
|
119
|
+
['invalid', false],
|
|
120
|
+
['@nodomain.com', false],
|
|
121
|
+
['spaces in@email.com', false],
|
|
122
|
+
])('validateEmail(%s) returns %s', (email, expected) => {
|
|
123
|
+
expect(validateEmail(email)).toBe(expected);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### What NOT to Unit Test
|
|
129
|
+
|
|
130
|
+
- Simple getters/setters
|
|
131
|
+
- Framework code
|
|
132
|
+
- Third-party libraries
|
|
133
|
+
- Trivial wrappers
|
|
134
|
+
|
|
135
|
+
## Integration Tests (~60%)
|
|
136
|
+
|
|
137
|
+
Test multiple components working together. **This is where you get the most value.**
|
|
138
|
+
|
|
139
|
+
### What to Integration Test
|
|
140
|
+
|
|
141
|
+
- Service layer with real database
|
|
142
|
+
- API routes end-to-end (minus external services)
|
|
143
|
+
- Repository functions with database
|
|
144
|
+
- Message handlers with real queues
|
|
145
|
+
- Multiple services interacting
|
|
146
|
+
|
|
147
|
+
### Characteristics
|
|
148
|
+
|
|
149
|
+
- **Realistic**: Uses real dependencies (database, cache)
|
|
150
|
+
- **Valuable**: Catches integration issues
|
|
151
|
+
- **Moderate speed**: 100-500ms per test
|
|
152
|
+
- **Isolated data**: Each test has clean state
|
|
153
|
+
|
|
154
|
+
### Example: Service Integration
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
158
|
+
import { db } from '../lib/db';
|
|
159
|
+
import { orderService } from './orderService';
|
|
160
|
+
import { createTestUser, createTestProduct } from '../test/factories';
|
|
161
|
+
|
|
162
|
+
describe('OrderService', () => {
|
|
163
|
+
let user: User;
|
|
164
|
+
let products: Product[];
|
|
165
|
+
|
|
166
|
+
beforeAll(async () => {
|
|
167
|
+
await db.$connect();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
afterAll(async () => {
|
|
171
|
+
await db.$disconnect();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
beforeEach(async () => {
|
|
175
|
+
// Clean slate
|
|
176
|
+
await db.$transaction([
|
|
177
|
+
db.orderItem.deleteMany(),
|
|
178
|
+
db.order.deleteMany(),
|
|
179
|
+
db.product.deleteMany(),
|
|
180
|
+
db.user.deleteMany(),
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
// Setup test data
|
|
184
|
+
user = await createTestUser();
|
|
185
|
+
products = await Promise.all([
|
|
186
|
+
createTestProduct({ price: 100, inventory: 10 }),
|
|
187
|
+
createTestProduct({ price: 50, inventory: 5 }),
|
|
188
|
+
]);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe('createOrder', () => {
|
|
192
|
+
it('creates order with correct total', async () => {
|
|
193
|
+
const order = await orderService.createOrder({
|
|
194
|
+
userId: user.id,
|
|
195
|
+
items: [
|
|
196
|
+
{ productId: products[0].id, quantity: 2 },
|
|
197
|
+
{ productId: products[1].id, quantity: 1 },
|
|
198
|
+
],
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(order.total).toBe(250); // (100 * 2) + (50 * 1)
|
|
202
|
+
expect(order.status).toBe('pending');
|
|
203
|
+
expect(order.items).toHaveLength(2);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('decrements product inventory', async () => {
|
|
207
|
+
await orderService.createOrder({
|
|
208
|
+
userId: user.id,
|
|
209
|
+
items: [{ productId: products[0].id, quantity: 3 }],
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const updatedProduct = await db.product.findUnique({
|
|
213
|
+
where: { id: products[0].id },
|
|
214
|
+
});
|
|
215
|
+
expect(updatedProduct?.inventory).toBe(7); // 10 - 3
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('rejects order when inventory insufficient', async () => {
|
|
219
|
+
await expect(
|
|
220
|
+
orderService.createOrder({
|
|
221
|
+
userId: user.id,
|
|
222
|
+
items: [{ productId: products[0].id, quantity: 100 }],
|
|
223
|
+
})
|
|
224
|
+
).rejects.toThrow('Insufficient inventory');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('rolls back on partial failure', async () => {
|
|
228
|
+
const initialInventory = products[0].inventory;
|
|
229
|
+
|
|
230
|
+
await expect(
|
|
231
|
+
orderService.createOrder({
|
|
232
|
+
userId: user.id,
|
|
233
|
+
items: [
|
|
234
|
+
{ productId: products[0].id, quantity: 1 },
|
|
235
|
+
{ productId: 'nonexistent', quantity: 1 },
|
|
236
|
+
],
|
|
237
|
+
})
|
|
238
|
+
).rejects.toThrow();
|
|
239
|
+
|
|
240
|
+
// Inventory should not have changed
|
|
241
|
+
const product = await db.product.findUnique({ where: { id: products[0].id } });
|
|
242
|
+
expect(product?.inventory).toBe(initialInventory);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Example: API Integration
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
252
|
+
import request from 'supertest';
|
|
253
|
+
import { app } from '../app';
|
|
254
|
+
import { db } from '../lib/db';
|
|
255
|
+
import { createTestUser, createAuthToken } from '../test/factories';
|
|
256
|
+
|
|
257
|
+
describe('POST /api/orders', () => {
|
|
258
|
+
let authToken: string;
|
|
259
|
+
let userId: string;
|
|
260
|
+
|
|
261
|
+
beforeEach(async () => {
|
|
262
|
+
await db.order.deleteMany();
|
|
263
|
+
await db.user.deleteMany();
|
|
264
|
+
|
|
265
|
+
const user = await createTestUser();
|
|
266
|
+
userId = user.id;
|
|
267
|
+
authToken = createAuthToken(user);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('creates order for authenticated user', async () => {
|
|
271
|
+
const response = await request(app)
|
|
272
|
+
.post('/api/orders')
|
|
273
|
+
.set('Authorization', `Bearer ${authToken}`)
|
|
274
|
+
.send({
|
|
275
|
+
items: [{ productId: 'prod-1', quantity: 2 }],
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
expect(response.status).toBe(201);
|
|
279
|
+
expect(response.body.data.userId).toBe(userId);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('returns 401 without authentication', async () => {
|
|
283
|
+
const response = await request(app)
|
|
284
|
+
.post('/api/orders')
|
|
285
|
+
.send({ items: [] });
|
|
286
|
+
|
|
287
|
+
expect(response.status).toBe(401);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('returns 422 for invalid request body', async () => {
|
|
291
|
+
const response = await request(app)
|
|
292
|
+
.post('/api/orders')
|
|
293
|
+
.set('Authorization', `Bearer ${authToken}`)
|
|
294
|
+
.send({ items: 'not-an-array' });
|
|
295
|
+
|
|
296
|
+
expect(response.status).toBe(422);
|
|
297
|
+
expect(response.body.error.code).toBe('VALIDATION_ERROR');
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## End-to-End Tests (~10%)
|
|
303
|
+
|
|
304
|
+
Test critical user journeys through the full stack.
|
|
305
|
+
|
|
306
|
+
### What to E2E Test
|
|
307
|
+
|
|
308
|
+
- Critical revenue paths (checkout, payment)
|
|
309
|
+
- Authentication flows
|
|
310
|
+
- Core user journeys
|
|
311
|
+
- Cross-browser compatibility (sparse)
|
|
312
|
+
|
|
313
|
+
### What NOT to E2E Test
|
|
314
|
+
|
|
315
|
+
- Every UI state
|
|
316
|
+
- Form validation (use integration tests)
|
|
317
|
+
- Edge cases (use unit tests)
|
|
318
|
+
- Error handling (use integration tests)
|
|
319
|
+
|
|
320
|
+
### Characteristics
|
|
321
|
+
|
|
322
|
+
- **Slow**: 10-60 seconds per test
|
|
323
|
+
- **Expensive**: Full environment needed
|
|
324
|
+
- **Brittle**: More moving parts = more failure points
|
|
325
|
+
- **High confidence**: Tests the real thing
|
|
326
|
+
|
|
327
|
+
### Example
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
import { test, expect } from '@playwright/test';
|
|
331
|
+
|
|
332
|
+
test.describe('Checkout Flow', () => {
|
|
333
|
+
test.beforeEach(async ({ page }) => {
|
|
334
|
+
// Setup test user via API
|
|
335
|
+
await page.request.post('/api/test/seed-user');
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
test('user can complete purchase', async ({ page }) => {
|
|
339
|
+
// Login
|
|
340
|
+
await page.goto('/login');
|
|
341
|
+
await page.fill('[name="email"]', 'test@example.com');
|
|
342
|
+
await page.fill('[name="password"]', 'password123');
|
|
343
|
+
await page.click('button[type="submit"]');
|
|
344
|
+
await expect(page).toHaveURL('/dashboard');
|
|
345
|
+
|
|
346
|
+
// Add to cart
|
|
347
|
+
await page.goto('/products');
|
|
348
|
+
await page.click('[data-testid="product-1"] >> text=Add to Cart');
|
|
349
|
+
await expect(page.locator('[data-testid="cart-count"]')).toHaveText('1');
|
|
350
|
+
|
|
351
|
+
// Checkout
|
|
352
|
+
await page.click('text=Checkout');
|
|
353
|
+
await page.fill('[name="address"]', '123 Main St');
|
|
354
|
+
await page.fill('[name="city"]', 'Anytown');
|
|
355
|
+
await page.fill('[name="zip"]', '12345');
|
|
356
|
+
await page.click('text=Place Order');
|
|
357
|
+
|
|
358
|
+
// Verify
|
|
359
|
+
await expect(page.locator('h1')).toHaveText('Order Confirmed');
|
|
360
|
+
await expect(page.locator('[data-testid="order-number"]')).toBeVisible();
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test('handles payment failure gracefully', async ({ page }) => {
|
|
364
|
+
await page.goto('/checkout');
|
|
365
|
+
// Use test card that triggers decline
|
|
366
|
+
await page.fill('[name="card-number"]', '4000000000000002');
|
|
367
|
+
await page.click('text=Pay');
|
|
368
|
+
|
|
369
|
+
await expect(page.locator('.error')).toHaveText('Payment declined');
|
|
370
|
+
await expect(page).toHaveURL('/checkout'); // Stays on page
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Comparison Matrix
|
|
376
|
+
|
|
377
|
+
| Aspect | Static | Unit | Integration | E2E |
|
|
378
|
+
|--------|--------|------|-------------|-----|
|
|
379
|
+
| Speed | Instant | <10ms | 100-500ms | 10-60s |
|
|
380
|
+
| Confidence | Low | Medium | High | Highest |
|
|
381
|
+
| Maintenance | Very Low | Low | Medium | High |
|
|
382
|
+
| Isolation | N/A | Complete | Partial | None |
|
|
383
|
+
| Real deps | No | No | Yes | Yes |
|
|
384
|
+
| When to run | Always | Always | Always | Critical paths |
|
|
385
|
+
|
|
386
|
+
## Decision Guide
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
Is it pure logic with no dependencies?
|
|
390
|
+
├── Yes → Unit test
|
|
391
|
+
└── No
|
|
392
|
+
└── Does it involve database/services?
|
|
393
|
+
├── Yes → Integration test
|
|
394
|
+
└── No
|
|
395
|
+
└── Is it a critical user journey?
|
|
396
|
+
├── Yes → E2E test
|
|
397
|
+
└── No → Integration test
|
|
398
|
+
```
|