clearctx 3.0.0 → 3.1.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.
@@ -0,0 +1,842 @@
1
+ ---
2
+ name: testing-qa
3
+ description: Production-grade testing strategy, unit/integration/e2e patterns, mocking, test data, and CI integration
4
+ domain: testing
5
+ keywords: [testing, unit-tests, integration-tests, e2e, mocking, fixtures, coverage, ci, jest, vitest]
6
+ version: 1.0.0
7
+ ---
8
+
9
+ # Testing & QA — Expertise Guide
10
+
11
+ ## Worker Context
12
+
13
+ You are a testing specialist. Your role: design test strategies, write comprehensive test suites, and ensure code quality through automated testing. You apply the testing pyramid, write fast deterministic tests, and integrate with CI/CD pipelines.
14
+
15
+ ### Testing Pyramid (Token Budget Distribution)
16
+
17
+ | Level | Coverage | Speed | Isolation | Cost | When to Use |
18
+ |-------|----------|-------|-----------|------|-------------|
19
+ | **Unit** | 70% | <10ms | Full | Low | Business logic, utilities, pure functions |
20
+ | **Integration** | 20% | <500ms | Partial | Medium | API endpoints, DB operations, service interactions |
21
+ | **E2E** | 10% | >2s | None | High | Critical user flows only (checkout, signup, payment) |
22
+
23
+ **Decision Tree:**
24
+ 1. Testing pure function/utility? → Unit test
25
+ 2. Testing API endpoint? → Integration test (real DB, mocked external APIs)
26
+ 3. Testing multi-page user flow? → E2E test (only if critical business path)
27
+ 4. Testing UI component? → Unit test (render + interactions), NOT snapshot unless stable contract
28
+
29
+ ### AAA Pattern (CRITICAL: Use for ALL tests)
30
+
31
+ ```javascript
32
+ // GOOD: Clear AAA structure
33
+ describe('OrderService', () => {
34
+ describe('createOrder', () => {
35
+ it('should return 201 when valid order data provided', async () => {
36
+ // Arrange
37
+ const validOrder = { userId: 1, items: [{ id: 101, qty: 2 }] };
38
+ const mockDb = { insert: jest.fn().mockResolvedValue({ id: 5 }) };
39
+ const service = new OrderService(mockDb);
40
+
41
+ // Act
42
+ const result = await service.createOrder(validOrder);
43
+
44
+ // Assert
45
+ expect(result.status).toBe(201);
46
+ expect(result.data.id).toBe(5);
47
+ expect(mockDb.insert).toHaveBeenCalledWith(validOrder);
48
+ });
49
+ });
50
+ });
51
+
52
+ // BAD: Mixed concerns, unclear phases
53
+ it('order test', async () => {
54
+ const service = new OrderService(mockDb);
55
+ const result = await service.createOrder({ userId: 1, items: [] });
56
+ expect(mockDb.insert).toHaveBeenCalled();
57
+ expect(result.status).toBe(201); // Assert before full setup clear
58
+ const validOrder = { userId: 1, items: [{ id: 101, qty: 2 }] };
59
+ });
60
+ ```
61
+
62
+ ### Mocking Decision Tree (CRITICAL: Follow this exactly)
63
+
64
+ ```
65
+ Is this what I'm testing?
66
+ ├─ YES → NEVER mock it
67
+ └─ NO → Continue...
68
+
69
+ Is this an external dependency?
70
+ ├─ External API/service → ALWAYS mock (network unreliable)
71
+ ├─ Database
72
+ │ ├─ Unit test → Mock (speed + isolation)
73
+ │ └─ Integration test → Real DB with transaction rollback
74
+ ├─ Time/Date → ALWAYS mock (determinism)
75
+ ├─ File system → Mock for unit, real tmpdir for integration
76
+ └─ Internal module
77
+ ├─ Simple logic → Use real implementation (test integration)
78
+ └─ Complex/slow → Mock ONLY if needed for isolation
79
+ ```
80
+
81
+ **Examples:**
82
+
83
+ ```javascript
84
+ // GOOD: Mock external API, use real internal services
85
+ describe('PaymentProcessor', () => {
86
+ it('should charge customer via Stripe', async () => {
87
+ // Mock external Stripe API
88
+ const mockStripe = { charge: jest.fn().mockResolvedValue({ id: 'ch_123' }) };
89
+
90
+ // Use REAL internal services (no mocking)
91
+ const realOrderService = new OrderService(testDb);
92
+ const processor = new PaymentProcessor(mockStripe, realOrderService);
93
+
94
+ const result = await processor.processPayment(orderId);
95
+
96
+ expect(mockStripe.charge).toHaveBeenCalledWith(expect.objectContaining({
97
+ amount: 5000,
98
+ currency: 'usd'
99
+ }));
100
+ });
101
+ });
102
+
103
+ // BAD: Overmocking — test proves nothing
104
+ it('should process payment', async () => {
105
+ const mockStripe = { charge: jest.fn().mockResolvedValue({ id: 'ch_123' }) };
106
+ const mockOrderService = { getOrder: jest.fn().mockResolvedValue({ total: 50 }) };
107
+ const mockLogger = { log: jest.fn() };
108
+ const processor = new PaymentProcessor(mockStripe, mockOrderService, mockLogger);
109
+
110
+ // You've mocked everything — this test only verifies your mocks work
111
+ const result = await processor.processPayment(1);
112
+ expect(result.id).toBe('ch_123'); // Circular: you mocked this response
113
+ });
114
+
115
+ // GOOD: Always mock time
116
+ describe('SubscriptionService', () => {
117
+ beforeEach(() => {
118
+ jest.useFakeTimers();
119
+ jest.setSystemTime(new Date('2026-02-15'));
120
+ });
121
+
122
+ afterEach(() => {
123
+ jest.useRealTimers();
124
+ });
125
+
126
+ it('should expire subscription after 30 days', () => {
127
+ const service = new SubscriptionService();
128
+ const sub = service.createSubscription();
129
+
130
+ jest.advanceTimersByTime(30 * 24 * 60 * 60 * 1000);
131
+
132
+ expect(service.isExpired(sub)).toBe(true);
133
+ });
134
+ });
135
+ ```
136
+
137
+ ### Test Data Factories (IMPORTANT: Use for ALL test data)
138
+
139
+ ```javascript
140
+ // GOOD: Deterministic factory functions
141
+ function createTestUser(overrides = {}) {
142
+ return {
143
+ id: 1,
144
+ email: 'test@example.com',
145
+ name: 'Test User',
146
+ createdAt: new Date('2026-01-01'),
147
+ ...overrides
148
+ };
149
+ }
150
+
151
+ function createTestOrder(overrides = {}) {
152
+ return {
153
+ id: 100,
154
+ userId: 1,
155
+ items: [{ productId: 10, quantity: 2, price: 25.00 }],
156
+ total: 50.00,
157
+ status: 'pending',
158
+ ...overrides
159
+ };
160
+ }
161
+
162
+ // Usage
163
+ it('should apply discount to order', () => {
164
+ const order = createTestOrder({ total: 100.00 });
165
+ const result = applyDiscount(order, 0.1);
166
+ expect(result.total).toBe(90.00);
167
+ });
168
+
169
+ // BAD: Inline data, hard to maintain, not reusable
170
+ it('test discount', () => {
171
+ const order = {
172
+ id: 999,
173
+ userId: 42,
174
+ items: [{ productId: 88, quantity: 1, price: 100.00 }],
175
+ total: 100.00,
176
+ status: 'pending'
177
+ };
178
+ // ... test continues
179
+ });
180
+
181
+ // BAD: Non-deterministic (breaks randomly)
182
+ function createTestUser() {
183
+ return {
184
+ id: Math.floor(Math.random() * 10000), // NEVER use random in tests
185
+ email: `user${Date.now()}@example.com`, // NEVER use current time
186
+ name: 'Test User'
187
+ };
188
+ }
189
+ ```
190
+
191
+ ### Assertion Patterns (Prefer specific over generic)
192
+
193
+ ```javascript
194
+ // GOOD: Specific assertions
195
+ expect(response.status).toBe(201);
196
+ expect(response.body).toEqual({ id: 5, name: 'Widget' });
197
+ expect(user.roles).toContain('admin');
198
+ expect(result.errors).toHaveLength(2);
199
+
200
+ // BAD: Generic assertions (low signal)
201
+ expect(response).toBeTruthy(); // What are you actually testing?
202
+ expect(response.status).toBeGreaterThan(0); // Too broad
203
+ expect(result).toBeDefined(); // Useless — undefined would throw earlier
204
+
205
+ // GOOD: Custom matchers for domain concepts
206
+ expect.extend({
207
+ toBeValidOrder(received) {
208
+ const pass = received.id > 0 &&
209
+ Array.isArray(received.items) &&
210
+ received.total >= 0;
211
+ return {
212
+ pass,
213
+ message: () => `Expected ${received} to be a valid order`
214
+ };
215
+ }
216
+ });
217
+
218
+ expect(order).toBeValidOrder();
219
+
220
+ // GOOD: Snapshot testing (use sparingly)
221
+ // ONLY for: API response schemas, component trees, serialized config
222
+ // NEVER for: dates, IDs, timestamps, random data
223
+ it('should return correct API response shape', () => {
224
+ const response = api.getUserProfile(1);
225
+ expect(response).toMatchSnapshot({
226
+ id: expect.any(Number),
227
+ createdAt: expect.any(String), // Ignore dynamic values
228
+ email: expect.any(String)
229
+ });
230
+ });
231
+
232
+ // BAD: Snapshot with dynamic data (flaky)
233
+ it('snapshot test', () => {
234
+ const response = { id: Date.now(), user: 'test' };
235
+ expect(response).toMatchSnapshot(); // Fails every run
236
+ });
237
+ ```
238
+
239
+ ### Integration Testing Pattern
240
+
241
+ ```javascript
242
+ // GOOD: Integration test with real DB, transaction rollback
243
+ describe('POST /api/orders', () => {
244
+ let app, db;
245
+
246
+ beforeAll(async () => {
247
+ db = await createTestDatabase(); // Real test DB
248
+ app = createApp(db);
249
+ });
250
+
251
+ beforeEach(async () => {
252
+ await db.raw('BEGIN'); // Start transaction
253
+ });
254
+
255
+ afterEach(async () => {
256
+ await db.raw('ROLLBACK'); // Rollback after each test (clean slate)
257
+ });
258
+
259
+ afterAll(async () => {
260
+ await db.destroy();
261
+ });
262
+
263
+ it('should create order and return 201', async () => {
264
+ // Arrange: Seed test data
265
+ await db('users').insert({ id: 1, email: 'test@example.com' });
266
+ const orderData = { userId: 1, items: [{ id: 10, qty: 2 }] };
267
+
268
+ // Act: Make real HTTP request
269
+ const response = await request(app)
270
+ .post('/api/orders')
271
+ .send(orderData);
272
+
273
+ // Assert: Check response + DB state
274
+ expect(response.status).toBe(201);
275
+ expect(response.body.id).toBeGreaterThan(0);
276
+
277
+ const dbOrder = await db('orders').where({ id: response.body.id }).first();
278
+ expect(dbOrder.user_id).toBe(1);
279
+ expect(dbOrder.status).toBe('pending');
280
+ });
281
+
282
+ it('should return 400 when userId missing', async () => {
283
+ const response = await request(app)
284
+ .post('/api/orders')
285
+ .send({ items: [] });
286
+
287
+ expect(response.status).toBe(400);
288
+ expect(response.body.error).toContain('userId');
289
+ });
290
+ });
291
+
292
+ // BAD: Mocking DB in "integration" test (not actually integrated)
293
+ it('should create order', async () => {
294
+ const mockDb = { insert: jest.fn().mockResolvedValue({ id: 5 }) };
295
+ // This is a UNIT test pretending to be integration
296
+ });
297
+ ```
298
+
299
+ ### Coverage Strategy (IMPORTANT: Branch coverage > line coverage)
300
+
301
+ | Code Type | Target | Priority | Why |
302
+ |-----------|--------|----------|-----|
303
+ | Business logic | 90%+ | CRITICAL | Core value, high bug impact |
304
+ | API routes | 80%+ | High | User-facing contracts |
305
+ | Utils/helpers | 85%+ | High | Reused everywhere |
306
+ | Config files | 0% | Ignore | Static data |
307
+ | Generated code | 0% | Ignore | Not hand-written |
308
+ | UI components | 70%+ | Medium | Snapshot + interaction tests |
309
+
310
+ **Branch coverage example:**
311
+
312
+ ```javascript
313
+ // This function has 4 branches
314
+ function calculateDiscount(user, orderTotal) {
315
+ if (!user) return 0; // Branch 1
316
+ if (user.isPremium) return 0.2; // Branch 2
317
+ if (orderTotal > 100) return 0.1; // Branch 3
318
+ return 0; // Branch 4
319
+ }
320
+
321
+ // GOOD: Test ALL branches
322
+ describe('calculateDiscount', () => {
323
+ it('should return 0 when user is null', () => {
324
+ expect(calculateDiscount(null, 50)).toBe(0);
325
+ });
326
+
327
+ it('should return 0.2 when user is premium', () => {
328
+ expect(calculateDiscount({ isPremium: true }, 50)).toBe(0.2);
329
+ });
330
+
331
+ it('should return 0.1 when order total > 100', () => {
332
+ expect(calculateDiscount({ isPremium: false }, 150)).toBe(0.1);
333
+ });
334
+
335
+ it('should return 0 when order total <= 100 and not premium', () => {
336
+ expect(calculateDiscount({ isPremium: false }, 50)).toBe(0);
337
+ });
338
+ });
339
+
340
+ // BAD: Only 50% branch coverage
341
+ describe('calculateDiscount', () => {
342
+ it('should calculate discount', () => {
343
+ expect(calculateDiscount({ isPremium: true }, 50)).toBe(0.2);
344
+ expect(calculateDiscount({ isPremium: false }, 50)).toBe(0);
345
+ // Missing: null user, orderTotal > 100 cases
346
+ });
347
+ });
348
+ ```
349
+
350
+ **NEVER chase 100% coverage:**
351
+ - 80-90% is optimal (diminishing returns after)
352
+ - Focus on business-critical paths first
353
+ - Ignore defensive error handling you can't trigger
354
+ - Coverage is a guide, not a goal
355
+
356
+ ### CI Integration Pattern
357
+
358
+ ```yaml
359
+ # GOOD: Optimized test pipeline
360
+ name: Test
361
+ on: [push, pull_request]
362
+
363
+ jobs:
364
+ test:
365
+ runs-on: ubuntu-latest
366
+ steps:
367
+ - uses: actions/checkout@v3
368
+ - uses: actions/setup-node@v3
369
+
370
+ # Step 1: Unit tests (fail fast)
371
+ - name: Unit tests
372
+ run: npm run test:unit
373
+ timeout-minutes: 2
374
+
375
+ # Step 2: Integration tests (parallel)
376
+ - name: Integration tests
377
+ run: npm run test:integration -- --maxWorkers=4
378
+ timeout-minutes: 5
379
+
380
+ # Step 3: E2E tests (only if unit + integration pass)
381
+ - name: E2E tests
382
+ run: npm run test:e2e
383
+ timeout-minutes: 10
384
+
385
+ # Step 4: Coverage check
386
+ - name: Coverage check
387
+ run: |
388
+ npm run test:coverage
389
+ if [ $(cat coverage/coverage-summary.json | jq '.total.branches.pct') -lt 80 ]; then
390
+ echo "Branch coverage below 80%"
391
+ exit 1
392
+ fi
393
+
394
+ # Step 5: Flaky test detection
395
+ - name: Re-run failures once
396
+ if: failure()
397
+ run: npm test -- --onlyFailures
398
+ ```
399
+
400
+ **CI Rules:**
401
+ - Run unit tests FIRST (fastest feedback)
402
+ - Parallelize test suites (--maxWorkers)
403
+ - Fail build on coverage regression (compare against main branch)
404
+ - Re-run failures ONCE (detect flaky tests, don't mask real failures)
405
+ - Separate unit/integration/e2e jobs (fail fast on unit tests)
406
+
407
+ ## Conventions
408
+
409
+ ### Test File Naming
410
+
411
+ ```
412
+ src/
413
+ services/
414
+ OrderService.js
415
+ OrderService.test.js ← Unit tests next to source
416
+ api/
417
+ routes/
418
+ orders.js
419
+ orders.test.js ← Integration tests next to routes
420
+ __tests__/
421
+ e2e/
422
+ checkout-flow.e2e.test.js ← E2E tests in separate directory
423
+ ```
424
+
425
+ **Rules:**
426
+ - Unit tests: `{module}.test.js` next to source file
427
+ - Integration tests: `{module}.test.js` or `{module}.integration.test.js`
428
+ - E2E tests: `{flow}.e2e.test.js` in `__tests__/e2e/`
429
+ - Factories: `__tests__/factories/{entity}.js`
430
+ - Fixtures: `__tests__/fixtures/{data}.json`
431
+
432
+ ### Test Naming Pattern (CRITICAL: Describe behavior, not implementation)
433
+
434
+ ```javascript
435
+ // GOOD: Behavior-focused names
436
+ describe('OrderService', () => {
437
+ describe('createOrder', () => {
438
+ it('should return 201 when valid order data provided', () => {});
439
+ it('should return 400 when userId is missing', () => {});
440
+ it('should return 400 when items array is empty', () => {});
441
+ it('should calculate total by summing item prices', () => {});
442
+ });
443
+
444
+ describe('cancelOrder', () => {
445
+ it('should set status to cancelled when order is pending', () => {});
446
+ it('should return 400 when order is already shipped', () => {});
447
+ });
448
+ });
449
+
450
+ // BAD: Implementation-focused names (brittle)
451
+ describe('OrderService', () => {
452
+ it('test createOrder method', () => {});
453
+ it('should call db.insert', () => {}); // Testing implementation detail
454
+ it('works correctly', () => {}); // Vague
455
+ it('order test 1', () => {}); // Meaningless
456
+ });
457
+ ```
458
+
459
+ **Template:** `should [expected behavior] when [condition]`
460
+
461
+ ### Assertion Conventions
462
+
463
+ ```javascript
464
+ // Prefer toBe for primitives
465
+ expect(result.status).toBe(201);
466
+ expect(result.isValid).toBe(true);
467
+
468
+ // Prefer toEqual for objects/arrays (deep equality)
469
+ expect(result.data).toEqual({ id: 5, name: 'Widget' });
470
+
471
+ // Prefer toContain for arrays
472
+ expect(user.roles).toContain('admin');
473
+
474
+ // Prefer toHaveLength for length checks
475
+ expect(errors).toHaveLength(2);
476
+
477
+ // Prefer toHaveBeenCalledWith for mock verification
478
+ expect(mockDb.insert).toHaveBeenCalledWith(expectedData);
479
+ expect(mockDb.insert).toHaveBeenCalledTimes(1);
480
+
481
+ // NEVER use toBeTruthy/toBeFalsy for specific values
482
+ expect(result.status).toBe(201); // NOT .toBeTruthy()
483
+ ```
484
+
485
+ ## Common Patterns
486
+
487
+ ### 1. Test Isolation (beforeEach/afterEach)
488
+
489
+ ```javascript
490
+ describe('UserService', () => {
491
+ let service, mockDb;
492
+
493
+ beforeEach(() => {
494
+ mockDb = createMockDb();
495
+ service = new UserService(mockDb);
496
+ });
497
+
498
+ afterEach(() => {
499
+ jest.clearAllMocks(); // Clear call history
500
+ });
501
+
502
+ it('test 1', () => {
503
+ // service and mockDb are fresh for EACH test
504
+ });
505
+
506
+ it('test 2', () => {
507
+ // No shared state from test 1
508
+ });
509
+ });
510
+ ```
511
+
512
+ ### 2. Async Testing (async/await)
513
+
514
+ ```javascript
515
+ // GOOD: async/await (modern, readable)
516
+ it('should fetch user', async () => {
517
+ const user = await service.getUser(1);
518
+ expect(user.id).toBe(1);
519
+ });
520
+
521
+ // BAD: Nested promises (callback hell)
522
+ it('should fetch user', (done) => {
523
+ service.getUser(1).then((user) => {
524
+ expect(user.id).toBe(1);
525
+ done();
526
+ });
527
+ });
528
+ ```
529
+
530
+ ### 3. Error Testing
531
+
532
+ ```javascript
533
+ // GOOD: Test error cases explicitly
534
+ it('should throw when user not found', async () => {
535
+ mockDb.findById.mockResolvedValue(null);
536
+
537
+ await expect(service.getUser(999)).rejects.toThrow('User not found');
538
+ });
539
+
540
+ it('should return 404 when resource missing', async () => {
541
+ const response = await request(app).get('/api/users/999');
542
+
543
+ expect(response.status).toBe(404);
544
+ expect(response.body.error).toBe('User not found');
545
+ });
546
+
547
+ // GOOD: Test error validation
548
+ it('should validate email format', async () => {
549
+ const response = await request(app)
550
+ .post('/api/users')
551
+ .send({ email: 'invalid-email' });
552
+
553
+ expect(response.status).toBe(400);
554
+ expect(response.body.errors).toContainEqual(
555
+ expect.objectContaining({ field: 'email', message: 'Invalid email format' })
556
+ );
557
+ });
558
+ ```
559
+
560
+ ### 4. Parameterized Tests (test.each)
561
+
562
+ ```javascript
563
+ // GOOD: Reduce duplication with test.each
564
+ describe('calculateDiscount', () => {
565
+ test.each([
566
+ [{ isPremium: true }, 100, 0.2],
567
+ [{ isPremium: false }, 150, 0.1],
568
+ [{ isPremium: false }, 50, 0],
569
+ [null, 100, 0]
570
+ ])('should return %p for user %p with total %p', (user, total, expected) => {
571
+ expect(calculateDiscount(user, total)).toBe(expected);
572
+ });
573
+ });
574
+
575
+ // BAD: Repetitive tests
576
+ it('test case 1', () => {
577
+ expect(calculateDiscount({ isPremium: true }, 100)).toBe(0.2);
578
+ });
579
+ it('test case 2', () => {
580
+ expect(calculateDiscount({ isPremium: false }, 150)).toBe(0.1);
581
+ });
582
+ // ... repeated pattern
583
+ ```
584
+
585
+ ### 5. Spy Pattern (Verify behavior without mocking)
586
+
587
+ ```javascript
588
+ // GOOD: Spy on real implementation
589
+ it('should call logger when error occurs', async () => {
590
+ const logger = { error: jest.fn() };
591
+ const service = new PaymentService(realStripe, logger);
592
+
593
+ realStripe.charge = jest.fn().mockRejectedValue(new Error('Payment failed'));
594
+
595
+ await expect(service.processPayment(1)).rejects.toThrow();
596
+
597
+ expect(logger.error).toHaveBeenCalledWith(
598
+ expect.stringContaining('Payment failed')
599
+ );
600
+ });
601
+ ```
602
+
603
+ ## Anti-Patterns
604
+
605
+ ### 1. Testing Implementation Details (NEVER do this)
606
+
607
+ ```javascript
608
+ // BAD: Test breaks on refactor even if behavior unchanged
609
+ it('should call db.insert with correct params', () => {
610
+ service.createOrder(orderData);
611
+ expect(mockDb.insert).toHaveBeenCalled(); // Implementation detail
612
+ });
613
+
614
+ // GOOD: Test observable behavior
615
+ it('should create order and return ID', async () => {
616
+ const result = await service.createOrder(orderData);
617
+ expect(result.id).toBeGreaterThan(0);
618
+
619
+ // Verify side effect (DB state)
620
+ const order = await db('orders').where({ id: result.id }).first();
621
+ expect(order.user_id).toBe(orderData.userId);
622
+ });
623
+ ```
624
+
625
+ **Why:** Implementation can change (switch from INSERT to UPSERT, change ORM) but behavior stays same. Test behavior, not implementation.
626
+
627
+ ### 2. Shared Mutable State (NEVER share state between tests)
628
+
629
+ ```javascript
630
+ // BAD: Shared state causes order-dependent failures
631
+ describe('OrderService', () => {
632
+ let orders = []; // SHARED across tests
633
+
634
+ it('test 1', () => {
635
+ orders.push({ id: 1 });
636
+ expect(orders).toHaveLength(1);
637
+ });
638
+
639
+ it('test 2', () => {
640
+ expect(orders).toHaveLength(0); // FAILS if test 1 runs first
641
+ });
642
+ });
643
+
644
+ // GOOD: Fresh state for each test
645
+ describe('OrderService', () => {
646
+ let orders;
647
+
648
+ beforeEach(() => {
649
+ orders = []; // Fresh for EACH test
650
+ });
651
+
652
+ it('test 1', () => {
653
+ orders.push({ id: 1 });
654
+ expect(orders).toHaveLength(1);
655
+ });
656
+
657
+ it('test 2', () => {
658
+ expect(orders).toHaveLength(0); // Always passes
659
+ });
660
+ });
661
+ ```
662
+
663
+ ### 3. Testing Framework Code (NEVER do this)
664
+
665
+ ```javascript
666
+ // BAD: Testing that Express works (not your code)
667
+ it('should have a POST /api/orders route', () => {
668
+ const routes = app._router.stack.filter(r => r.route);
669
+ expect(routes.some(r => r.route.path === '/api/orders')).toBe(true);
670
+ });
671
+
672
+ // GOOD: Test that YOUR route handler works correctly
673
+ it('should create order via POST /api/orders', async () => {
674
+ const response = await request(app)
675
+ .post('/api/orders')
676
+ .send({ userId: 1, items: [{ id: 10, qty: 2 }] });
677
+
678
+ expect(response.status).toBe(201);
679
+ expect(response.body.id).toBeGreaterThan(0);
680
+ });
681
+ ```
682
+
683
+ ### 4. Overmocking (Test proves nothing)
684
+
685
+ ```javascript
686
+ // BAD: Everything is mocked
687
+ it('should process payment', async () => {
688
+ const mockStripe = { charge: jest.fn().mockResolvedValue({ id: 'ch_123' }) };
689
+ const mockOrderService = { getOrder: jest.fn().mockResolvedValue({ total: 50 }) };
690
+ const mockUserService = { getUser: jest.fn().mockResolvedValue({ id: 1 }) };
691
+
692
+ const processor = new PaymentProcessor(mockStripe, mockOrderService, mockUserService);
693
+ const result = await processor.processPayment(1);
694
+
695
+ expect(result.id).toBe('ch_123'); // Circular: you defined this response
696
+ });
697
+
698
+ // GOOD: Mock only external dependencies
699
+ it('should process payment via Stripe', async () => {
700
+ const mockStripe = { charge: jest.fn().mockResolvedValue({ id: 'ch_123' }) };
701
+
702
+ // Use REAL internal services
703
+ const realOrderService = new OrderService(testDb);
704
+ const realUserService = new UserService(testDb);
705
+
706
+ // Seed test data
707
+ await testDb('users').insert({ id: 1, email: 'test@example.com' });
708
+ await testDb('orders').insert({ id: 1, user_id: 1, total: 50.00 });
709
+
710
+ const processor = new PaymentProcessor(mockStripe, realOrderService, realUserService);
711
+ const result = await processor.processPayment(1);
712
+
713
+ expect(mockStripe.charge).toHaveBeenCalledWith(
714
+ expect.objectContaining({ amount: 5000 }) // Verify integration
715
+ );
716
+ });
717
+ ```
718
+
719
+ ### 5. Slow Tests in Unit Suite (Move to integration)
720
+
721
+ ```javascript
722
+ // BAD: Unit test that hits real DB (slow, 500ms+)
723
+ describe('OrderService (unit)', () => {
724
+ it('should create order', async () => {
725
+ await db('orders').insert({ userId: 1, total: 50 }); // Real DB call
726
+ // This is an INTEGRATION test
727
+ });
728
+ });
729
+
730
+ // GOOD: Fast unit test (<10ms)
731
+ describe('OrderService (unit)', () => {
732
+ it('should calculate total correctly', () => {
733
+ const items = [{ price: 10, qty: 2 }, { price: 15, qty: 1 }];
734
+ const total = OrderService.calculateTotal(items);
735
+ expect(total).toBe(35);
736
+ });
737
+ });
738
+
739
+ // GOOD: Slow test in integration suite
740
+ describe('OrderService (integration)', () => {
741
+ it('should persist order to database', async () => {
742
+ const result = await service.createOrder(orderData);
743
+ const dbOrder = await db('orders').where({ id: result.id }).first();
744
+ expect(dbOrder.total).toBe(50.00);
745
+ });
746
+ });
747
+ ```
748
+
749
+ ## Integration Notes
750
+
751
+ **Multi-Session Workflow:**
752
+
753
+ You are `testing-qa` worker in a clearctx orchestrated team. Follow this protocol:
754
+
755
+ 1. **Check inbox FIRST** (ENFORCED):
756
+ ```javascript
757
+ await mcp__multi_session__team_check_inbox({ name: "testing-qa" })
758
+ ```
759
+ Required before using artifact/contract tools.
760
+
761
+ 2. **Read shared artifacts**:
762
+ - `db-schema` from db-dev worker (for test fixtures matching real schema)
763
+ - `api-contracts` from backend-dev worker (for endpoint integration tests)
764
+ - `shared-conventions` (for response format, error format, status codes)
765
+
766
+ 3. **Publish test-results artifact**:
767
+ ```javascript
768
+ await mcp__multi_session__artifact_publish({
769
+ artifactId: "test-results",
770
+ type: "test-results",
771
+ name: "Test Suite Results",
772
+ publisher: "testing-qa",
773
+ data: {
774
+ summary: {
775
+ total: 156,
776
+ passed: 154,
777
+ failed: 2,
778
+ skipped: 0
779
+ },
780
+ coverage: {
781
+ lines: 87.5,
782
+ branches: 82.3,
783
+ functions: 91.2
784
+ },
785
+ duration: "12.4s",
786
+ failedTests: [
787
+ { file: "services/OrderService.test.js", test: "should handle concurrent orders", error: "..." }
788
+ ],
789
+ files: [
790
+ "services/OrderService.test.js",
791
+ "api/routes/orders.test.js",
792
+ "__tests__/e2e/checkout-flow.e2e.test.js"
793
+ ]
794
+ }
795
+ })
796
+ ```
797
+
798
+ 4. **Broadcast completion**:
799
+ ```javascript
800
+ await mcp__multi_session__team_broadcast({
801
+ from: "testing-qa",
802
+ content: "✅ Test suite complete: 154/156 passed (87.5% coverage). 2 failures in OrderService concurrent handling."
803
+ })
804
+ ```
805
+
806
+ 5. **File paths**: ALWAYS use relative paths
807
+ - ✅ `src/services/OrderService.test.js`
808
+ - ❌ `E:/Projects/app/src/services/OrderService.test.js`
809
+
810
+ 6. **Test file creation**: Create test files NEXT to source files (unless e2e)
811
+ - Unit: `src/services/OrderService.test.js` (next to `OrderService.js`)
812
+ - Integration: `src/api/routes/orders.test.js` (next to `orders.js`)
813
+ - E2E: `__tests__/e2e/checkout-flow.e2e.test.js`
814
+
815
+ 7. **Communicate test failures**: If tests fail due to API contract mismatch, use `team_send_message` to backend-dev:
816
+ ```javascript
817
+ await mcp__multi_session__team_send_message({
818
+ from: "testing-qa",
819
+ to: "backend-dev",
820
+ content: "POST /api/orders returning 200 instead of 201 per shared conventions. Expected statusCodes.create=201."
821
+ })
822
+ ```
823
+
824
+ **Dependency Chain:**
825
+ 1. db-dev publishes `db-schema` artifact
826
+ 2. backend-dev publishes `api-contracts` artifact
827
+ 3. testing-qa reads both artifacts → writes tests → publishes `test-results`
828
+ 4. Orchestrator verifies via `artifact_readers` that testing-qa consumed upstream artifacts
829
+
830
+ **Status Management:**
831
+ - Auto-set to "active" on spawn (no action needed)
832
+ - Auto-set to "idle" when session stops (no action needed)
833
+ - Only use `team_update_status` for custom statuses like "blocked"
834
+
835
+ **Convention Compliance:**
836
+ Always follow `shared-conventions` artifact for:
837
+ - Response format (e.g., `{ data: <result> }`)
838
+ - Error format (e.g., `{ error: <message> }`)
839
+ - Status codes (create=201, read=200, etc.)
840
+ - Date formats, enum values, boolean handling
841
+
842
+ If a test fails due to convention mismatch, it's likely the implementation is wrong (not your test). Report to orchestrator.