codex-genesis-harness 0.1.5 → 0.1.7
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/.codebase/ARCHITECTURE_REVIEW_COMPLETE.md +216 -216
- package/.codebase/CURRENT_STATE.md +8 -2
- package/.codebase/FILE_NAMING_CLARIFICATION.md +161 -161
- package/.codebase/HARNESS_COMPLETENESS_AUDIT.md +613 -613
- package/.codebase/IMPLEMENTATION_COMPLETE.md +429 -429
- package/.codebase/IMPLEMENTATION_HANDOFF.md +351 -351
- package/.codebase/IMPROVEMENTS_SUMMARY.md +419 -419
- package/.codebase/PHASE3_SKILLS_NAMING_COMPLETE.md +292 -292
- package/.codebase/PHASE_DEPENDENCY_MAP.md +486 -486
- package/.codebase/QUICK_START_SPEC_IMPACT.md +456 -456
- package/.codebase/README.md +139 -139
- package/.codebase/RECOVERY_POINTS.md +83 -438
- package/.codebase/beads.json +16 -0
- package/.codex/skills/genesis-ai-provider/SKILL.md +1 -1
- package/.codex/skills/genesis-api-contract/SKILL.md +1 -1
- package/.codex/skills/genesis-api-sync/SKILL.md +354 -354
- package/.codex/skills/genesis-api-sync/checklists/api-sync-checklist.md +101 -101
- package/.codex/skills/genesis-api-sync/templates/api-change-template.md +257 -257
- package/.codex/skills/genesis-architecture/SKILL.md +1 -1
- package/.codex/skills/genesis-codebase-map/SKILL.md +1 -1
- package/.codex/skills/genesis-debug-guide/SKILL.md +479 -479
- package/.codex/skills/genesis-debug-guide/checklists/flaky-test-investigation.md +339 -339
- package/.codex/skills/genesis-debug-guide/checklists/production-bug-debug.md +210 -210
- package/.codex/skills/genesis-debug-guide/checklists/test-failure-debug.md +158 -158
- package/.codex/skills/genesis-debug-guide/observability/debug-commands.md +365 -365
- package/.codex/skills/genesis-debug-guide/playbooks/unit-test-failures.md +289 -289
- package/.codex/skills/genesis-debug-guide/templates/debug-investigation-log.md +288 -288
- package/.codex/skills/genesis-design-spec/SKILL.md +3 -3
- package/.codex/skills/genesis-docs-automation/SKILL.md +1003 -1003
- package/.codex/skills/genesis-docs-automation/checklists/docs-validation.md +359 -359
- package/.codex/skills/genesis-docs-automation/checklists/spec-alignment.md +312 -312
- package/.codex/skills/genesis-docs-automation/observability/docs-tracking.md +382 -382
- package/.codex/skills/genesis-docs-automation/playbooks/auto-update-flow.md +851 -851
- package/.codex/skills/genesis-docs-automation/playbooks/changelog-generation.md +491 -491
- package/.codex/skills/genesis-docs-automation/templates/changelog-entry-template.md +187 -187
- package/.codex/skills/genesis-docs-automation/templates/handoff-template.md +297 -297
- package/.codex/skills/genesis-harness/SKILL.md +1428 -1427
- package/.codex/skills/genesis-harness/agents/openai.yaml +7 -7
- package/.codex/skills/genesis-harness/checklists/bug-fix-qa.md +169 -169
- package/.codex/skills/genesis-harness/checklists/new-feature-qa.md +157 -157
- package/.codex/skills/genesis-harness/checklists/refactor-qa.md +216 -216
- package/.codex/skills/genesis-harness/checklists/requirements-validation.md +211 -211
- package/.codex/skills/genesis-harness/references/planning-schema.md +35 -35
- package/.codex/skills/genesis-harness/references/quality-rubric.md +21 -21
- package/.codex/skills/genesis-harness/references/research-rubric.md +41 -41
- package/.codex/skills/genesis-harness/references/workflows.md +33 -33
- package/.codex/skills/genesis-harness/resources/agents-template.md +27 -27
- package/.codex/skills/genesis-harness/resources/api-docs-template.md +32 -32
- package/.codex/skills/genesis-harness/resources/architecture-template.md +30 -30
- package/.codex/skills/genesis-harness/resources/audit-template.md +26 -26
- package/.codex/skills/genesis-harness/resources/bug-template.md +34 -34
- package/.codex/skills/genesis-harness/resources/change-impact-matrix-template.md +204 -204
- package/.codex/skills/genesis-harness/resources/check-template.md +21 -21
- package/.codex/skills/genesis-harness/resources/conventions-template.md +42 -42
- package/.codex/skills/genesis-harness/resources/decision-template.md +33 -33
- package/.codex/skills/genesis-harness/resources/design-template.md +26 -26
- package/.codex/skills/genesis-harness/resources/escalation-template.md +21 -21
- package/.codex/skills/genesis-harness/resources/feature-template.md +49 -49
- package/.codex/skills/genesis-harness/resources/foundation-phase-template.md +131 -131
- package/.codex/skills/genesis-harness/resources/integrations-template.md +32 -32
- package/.codex/skills/genesis-harness/resources/journeys-template.md +13 -13
- package/.codex/skills/genesis-harness/resources/lessons-learned-template.md +12 -12
- package/.codex/skills/genesis-harness/resources/observability-template.md +34 -34
- package/.codex/skills/genesis-harness/resources/phase-00-foundation-template.md +76 -76
- package/.codex/skills/genesis-harness/resources/phase-template.md +34 -34
- package/.codex/skills/genesis-harness/resources/pitfalls-template.md +22 -22
- package/.codex/skills/genesis-harness/resources/planning-tree-template.md +39 -39
- package/.codex/skills/genesis-harness/resources/post-implementation-guide.md +347 -347
- package/.codex/skills/genesis-harness/resources/project-template.md +38 -38
- package/.codex/skills/genesis-harness/resources/quality-score-template.md +11 -11
- package/.codex/skills/genesis-harness/resources/requirements-template.md +26 -26
- package/.codex/skills/genesis-harness/resources/research-template.md +26 -26
- package/.codex/skills/genesis-harness/resources/review-template.md +22 -22
- package/.codex/skills/genesis-harness/resources/spec-changelog-template.md +6 -6
- package/.codex/skills/genesis-harness/resources/stack-template.md +33 -33
- package/.codex/skills/genesis-harness/resources/verification-template.md +26 -26
- package/.codex/skills/genesis-harness/scripts/check-architecture-boundaries.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/check-docs-sync.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/check-no-debug-logs.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/check-required-planning-files.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/check-spec-changelog.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/check-task-tracking.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/compact-context.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/create-adr.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/create-bug.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/create-feature.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/detect-stack.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/init-planning.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/list-changed-files.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/offload-log.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/run-verification.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/run-verify-loop.sh +0 -0
- package/.codex/skills/genesis-harness/scripts/update-state.sh +0 -0
- package/.codex/skills/genesis-harness-engineering/SKILL.md +1 -1
- package/.codex/skills/genesis-new-design/SKILL.md +2 -1
- package/.codex/skills/genesis-new-design/agents/openai.yaml +3 -3
- package/.codex/skills/genesis-observability-automation/checklists/.gitkeep +0 -0
- package/.codex/skills/genesis-observability-automation/observability/.gitkeep +0 -0
- package/.codex/skills/genesis-observability-automation/playbooks/.gitkeep +0 -0
- package/.codex/skills/genesis-observability-automation/templates/.gitkeep +0 -0
- package/.codex/skills/genesis-pipeline-orchestration/SKILL.md +1 -1
- package/.codex/skills/genesis-planning/SKILL.md +26 -1
- package/.codex/skills/genesis-planning/checklists/mvp-readiness.md +18 -0
- package/.codex/skills/genesis-planning/examples/5-phase-roadmap-example.md +43 -0
- package/.codex/skills/genesis-planning/templates/phase-1-core.md +17 -0
- package/.codex/skills/genesis-planning/templates/phase-2-auth.md +17 -0
- package/.codex/skills/genesis-planning/templates/phase-3-features.md +17 -0
- package/.codex/skills/genesis-planning/templates/phase-4-integrations.md +17 -0
- package/.codex/skills/genesis-planning/templates/phase-5-readiness.md +17 -0
- package/.codex/skills/genesis-release/SKILL.md +24 -1
- package/.codex/skills/{genesis-release-orchestration → genesis-release}/checklists/post-deployment-verification.md +274 -274
- package/.codex/skills/{genesis-release-orchestration → genesis-release}/checklists/pre-release-validation.md +220 -220
- package/.codex/skills/{genesis-release-orchestration → genesis-release}/observability/release-tracking.md +253 -253
- package/.codex/skills/{genesis-release-orchestration → genesis-release}/playbooks/canary-deployment-orchestration.md +472 -472
- package/.codex/skills/{genesis-release-orchestration → genesis-release}/playbooks/semantic-versioning-automation.md +494 -494
- package/.codex/skills/{genesis-release-orchestration → genesis-release}/templates/deployment-strategy-template.md +303 -303
- package/.codex/skills/{genesis-release-orchestration → genesis-release}/templates/release-runbook-template.md +420 -420
- package/.codex/skills/genesis-research-first/SKILL.md +237 -237
- package/.codex/skills/genesis-research-first/templates/.gitkeep +0 -0
- package/.codex/skills/genesis-spec-propagation/SKILL.md +534 -534
- package/.codex/skills/genesis-spec-propagation/checklists/phase-update-verification.md +384 -384
- package/.codex/skills/genesis-spec-propagation/checklists/spec-change-detection.md +257 -257
- package/.codex/skills/genesis-spec-propagation/observability/propagation-tracking.md +373 -373
- package/.codex/skills/genesis-spec-propagation/playbooks/breaking-change-propagation.md +692 -692
- package/.codex/skills/genesis-spec-propagation/playbooks/feature-change-propagation.md +434 -434
- package/.codex/skills/genesis-spec-propagation/templates/migration-guide-template.md +407 -407
- package/.codex/skills/{ui-ux-test-skill → genesis-ui-ux-test}/SKILL.md +1 -1
- package/.codex/skills/genesis-upgrade-design/agents/openai.yaml +3 -3
- package/.codex/skills/spec-impact-engine/SKILL.md +504 -504
- package/.codex/skills/spec-impact-engine/detect-spec-changes.sh +0 -0
- package/.codex-plugin/plugin.json +19 -19
- package/CHANGELOG.md +56 -0
- package/LICENSE +22 -22
- package/README.EN.md +780 -730
- package/README.VI.md +772 -723
- package/README.md +102 -247
- package/VERSION +2 -2
- package/bin/genesis-harness.js +695 -92
- package/package.json +9 -3
- package/scripts/README.md +342 -342
- package/scripts/compact-context.sh +0 -0
- package/scripts/contract_integrity_gate.js +83 -0
- package/scripts/detect-changes.sh +0 -0
- package/scripts/healing_telemetry.js +118 -0
- package/scripts/install.sh +5 -6
- package/scripts/offload-log.sh +0 -0
- package/scripts/prompt_sentinel.js +84 -0
- package/scripts/run-evals.sh +20 -24
- package/scripts/run-verify-loop.sh +11 -0
- package/scripts/spec_visual_sync.js +157 -0
- package/scripts/test_generator.js +142 -0
- package/scripts/transition_state.sh +0 -0
- package/scripts/uninstall.sh +2 -5
- package/scripts/validation_gates.sh +40 -1
- package/scripts/verify.sh +6 -61
- package/tests/unit/contract_integrity_gate.test.js +74 -0
- package/tests/unit/healing_telemetry.test.js +58 -0
- package/tests/unit/prompt_sentinel.test.js +50 -0
- package/tests/unit/spec_visual_sync.test.js +77 -0
- package/tests/unit/test_generator.test.js +62 -0
- package/.codex/skills/genesis-docs/SKILL.md +0 -46
- package/.codex/skills/genesis-docs/agents/openai.yaml +0 -7
- package/.codex/skills/genesis-release-orchestration/SKILL.md +0 -653
- package/.codex/skills/genesis-release-orchestration/agents/openai.yaml +0 -7
- package/.codex/skills/genesis-research/SKILL.md +0 -46
- package/.codex/skills/genesis-research/agents/openai.yaml +0 -7
- /package/.codex/skills/{genesis-docs/checklists/checklist.md → genesis-docs-automation/checklists/manual-docs-checklist.md} +0 -0
- /package/.codex/skills/{genesis-docs/examples/example.md → genesis-docs-automation/examples/manual-docs-example.md} +0 -0
- /package/.codex/skills/{genesis-docs → genesis-docs-automation}/templates/docs-update-template.md +0 -0
- /package/.codex/skills/{genesis-state-machine/SKILL.md → genesis-harness/references/state-machine.md} +0 -0
- /package/.codex/skills/{genesis-release-orchestration/examples/example.md → genesis-release/examples/orchestration-example.md} +0 -0
- /package/.codex/skills/{genesis-research → genesis-research-first}/checklists/checklist.md +0 -0
- /package/.codex/skills/{genesis-research/examples/example.md → genesis-research-first/examples/manual-research-example.md} +0 -0
- /package/.codex/skills/{genesis-research → genesis-research-first}/templates/research-note-template.md +0 -0
- /package/.codex/skills/{ui-ux-test-skill → genesis-ui-ux-test}/agents/openai.yaml +0 -0
- /package/.codex/skills/{ui-ux-test-skill → genesis-ui-ux-test}/checklists/checklist.md +0 -0
- /package/.codex/skills/{ui-ux-test-skill → genesis-ui-ux-test}/examples/example.md +0 -0
- /package/.codex/skills/{ui-ux-test-skill → genesis-ui-ux-test}/templates/playwright-test-template.md +0 -0
|
@@ -1,339 +1,339 @@
|
|
|
1
|
-
# Flaky Test Investigation Checklist
|
|
2
|
-
|
|
3
|
-
**Purpose**: Systematic debugging for tests that fail intermittently. These are notoriously hard to fix but follow patterns.
|
|
4
|
-
|
|
5
|
-
**Status**: MANDATORY - Complete before attempting flaky test fix.
|
|
6
|
-
|
|
7
|
-
## Characteristics: Is It Actually Flaky?
|
|
8
|
-
|
|
9
|
-
- [ ] **Fails inconsistently**:
|
|
10
|
-
- Run test 10 times: `for i in {1..10}; do npm test -- test.js; done`
|
|
11
|
-
- Fails ~X times out of 10
|
|
12
|
-
- Not always the same assertion fails
|
|
13
|
-
- Not 0% or 100% (if always fails, see test-failure-debug.md)
|
|
14
|
-
|
|
15
|
-
- [ ] **Failure pattern exists**:
|
|
16
|
-
- Fails more often under load?
|
|
17
|
-
- Fails in CI but not locally?
|
|
18
|
-
- Fails after other tests?
|
|
19
|
-
- Fails at specific time of day?
|
|
20
|
-
|
|
21
|
-
- [ ] **Previous attempts failed**:
|
|
22
|
-
- Simple retry didn't fix it
|
|
23
|
-
- Disabling parallelization didn't fix it
|
|
24
|
-
- Increasing timeout didn't fix it
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## Root Cause Pattern Analysis
|
|
29
|
-
|
|
30
|
-
### Pattern 1: Race Condition (Most Common)
|
|
31
|
-
```javascript
|
|
32
|
-
// ❌ WRONG: Race condition
|
|
33
|
-
test('user updates profile', async () => {
|
|
34
|
-
updateUser({ name: 'New Name' }); // Async, not awaited!
|
|
35
|
-
expect(userName).toBe('New Name'); // Might run before update completes
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// ✅ CORRECT: Wait for async operation
|
|
39
|
-
test('user updates profile', async () => {
|
|
40
|
-
await updateUser({ name: 'New Name' }); // Wait for it
|
|
41
|
-
expect(userName).toBe('New Name');
|
|
42
|
-
});
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
**Signs**:
|
|
46
|
-
- [ ] Test has async operations
|
|
47
|
-
- [ ] Not all async operations awaited
|
|
48
|
-
- [ ] Uses `setTimeout` or `setInterval`
|
|
49
|
-
- [ ] Fails under high CPU load
|
|
50
|
-
- [ ] Fails more in CI (slower machines)
|
|
51
|
-
|
|
52
|
-
**Fix**:
|
|
53
|
-
```javascript
|
|
54
|
-
// Add explicit waits
|
|
55
|
-
await waitFor(() => expect(element).toBeVisible());
|
|
56
|
-
|
|
57
|
-
// Use async/await properly
|
|
58
|
-
const result = await asyncFunction();
|
|
59
|
-
|
|
60
|
-
// Don't mix promises with callbacks
|
|
61
|
-
// Use either async/await OR .then(), not both
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### Pattern 2: Test Order Dependency
|
|
65
|
-
```javascript
|
|
66
|
-
// ❌ WRONG: Tests affect each other
|
|
67
|
-
let globalState = 0;
|
|
68
|
-
test('first test', () => { globalState = 1; });
|
|
69
|
-
test('second test', () => { expect(globalState).toBe(0); }); // Fails if first runs first!
|
|
70
|
-
|
|
71
|
-
// ✅ CORRECT: Each test is isolated
|
|
72
|
-
beforeEach(() => { state = 0; }); // Reset before each test
|
|
73
|
-
test('first test', () => { state = 1; });
|
|
74
|
-
test('second test', () => { expect(state).toBe(0); });
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
**Signs**:
|
|
78
|
-
- [ ] Test passes when run alone
|
|
79
|
-
- [ ] Test fails when run with others
|
|
80
|
-
- [ ] Test fails when run in random order
|
|
81
|
-
- [ ] Global variables used
|
|
82
|
-
- [ ] Database not cleaned between tests
|
|
83
|
-
|
|
84
|
-
**Fix**:
|
|
85
|
-
```javascript
|
|
86
|
-
beforeEach(() => {
|
|
87
|
-
// Reset all state
|
|
88
|
-
jest.clearAllMocks();
|
|
89
|
-
db.clear();
|
|
90
|
-
localStorage.clear();
|
|
91
|
-
});
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Pattern 3: Timing Issues
|
|
95
|
-
```javascript
|
|
96
|
-
// ❌ WRONG: Fixed timeout too short
|
|
97
|
-
test('animation completes', async () => {
|
|
98
|
-
await new Promise(r => setTimeout(r, 50)); // Sometimes not enough time
|
|
99
|
-
expect(element.style.opacity).toBe(1);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// ✅ CORRECT: Wait for actual condition
|
|
103
|
-
test('animation completes', async () => {
|
|
104
|
-
await waitFor(() => expect(element).toHaveStyle('opacity: 1'), { timeout: 5000 });
|
|
105
|
-
});
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
**Signs**:
|
|
109
|
-
- [ ] Uses `setTimeout` with fixed delays
|
|
110
|
-
- [ ] Fails randomly
|
|
111
|
-
- [ ] Fails when machine is busy
|
|
112
|
-
- [ ] Fails in CI (which is slower)
|
|
113
|
-
- [ ] Increasing timeout "fixes" it temporarily
|
|
114
|
-
|
|
115
|
-
**Fix**:
|
|
116
|
-
```javascript
|
|
117
|
-
// Instead of sleep:
|
|
118
|
-
await new Promise(r => setTimeout(r, 100));
|
|
119
|
-
|
|
120
|
-
// Use condition-based waiting:
|
|
121
|
-
await waitFor(() => expect(element).toBeVisible());
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Pattern 4: External Service/Network
|
|
125
|
-
```javascript
|
|
126
|
-
// ❌ WRONG: Calls actual API
|
|
127
|
-
test('fetch user', async () => {
|
|
128
|
-
const user = await fetchRealAPI('/users/1'); // Network unreliable!
|
|
129
|
-
expect(user.name).toBe('John');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// ✅ CORRECT: Mock external service
|
|
133
|
-
test('fetch user', async () => {
|
|
134
|
-
jest.mock('api', () => ({
|
|
135
|
-
fetch: jest.fn().mockResolvedValue({ name: 'John' })
|
|
136
|
-
}));
|
|
137
|
-
const user = await fetchUser(1);
|
|
138
|
-
expect(user.name).toBe('John');
|
|
139
|
-
});
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
**Signs**:
|
|
143
|
-
- [ ] Test makes network requests
|
|
144
|
-
- [ ] Mock not configured properly
|
|
145
|
-
- [ ] Network-dependent test
|
|
146
|
-
- [ ] Fails in offline environment
|
|
147
|
-
- [ ] Fails randomly (network issues)
|
|
148
|
-
|
|
149
|
-
**Fix**:
|
|
150
|
-
```javascript
|
|
151
|
-
// Mock all external APIs
|
|
152
|
-
jest.mock('node-fetch');
|
|
153
|
-
nodeFetch.mockResolvedValue({ json: () => ({ ... }) });
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### Pattern 5: Randomness/Seeding
|
|
157
|
-
```javascript
|
|
158
|
-
// ❌ WRONG: Random data without seed
|
|
159
|
-
test('shuffle array', () => {
|
|
160
|
-
const result = shuffle([1, 2, 3]);
|
|
161
|
-
expect(result).toEqual([2, 1, 3]); // Different order each time!
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
// ✅ CORRECT: Seed random for reproducible tests
|
|
165
|
-
test('shuffle array', () => {
|
|
166
|
-
Math.seedrandom = () => 0.5; // Set seed
|
|
167
|
-
const result = shuffle([1, 2, 3]);
|
|
168
|
-
expect(result).toEqual([3, 1, 2]); // Same every time with same seed
|
|
169
|
-
});
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
**Signs**:
|
|
173
|
-
- [ ] Test uses `Math.random()`
|
|
174
|
-
- [ ] Test uses `Date.now()`
|
|
175
|
-
- [ ] Test uses `UUID.generate()`
|
|
176
|
-
- [ ] Different assertions fail each run
|
|
177
|
-
- [ ] Works locally but fails in CI
|
|
178
|
-
|
|
179
|
-
**Fix**:
|
|
180
|
-
```javascript
|
|
181
|
-
// Seed random number generator
|
|
182
|
-
beforeEach(() => {
|
|
183
|
-
jest.spyOn(Math, 'random').mockReturnValue(0.5);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// Mock Date.now()
|
|
187
|
-
jest.spyOn(Date, 'now').mockReturnValue(1234567890);
|
|
188
|
-
|
|
189
|
-
// Generate UUIDs deterministically
|
|
190
|
-
jest.mock('uuid', () => ({ v4: () => 'test-uuid-123' }));
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
## Investigation Checklist
|
|
196
|
-
|
|
197
|
-
### Documentation
|
|
198
|
-
- [ ] When test was added
|
|
199
|
-
- [ ] When it started failing
|
|
200
|
-
- [ ] Failure frequency recorded
|
|
201
|
-
- [ ] Specific assertion that fails (changes?)
|
|
202
|
-
|
|
203
|
-
### Reproduction
|
|
204
|
-
- [ ] Test fails when run 10 times in sequence
|
|
205
|
-
- [ ] Test fails when run in random order with others
|
|
206
|
-
- [ ] Test fails in CI but not locally (or vice versa)
|
|
207
|
-
- [ ] Test fails under high system load
|
|
208
|
-
|
|
209
|
-
### Code Analysis
|
|
210
|
-
- [ ] All async operations have `await`
|
|
211
|
-
- [ ] No global state modifications
|
|
212
|
-
- [ ] All mocks are configured
|
|
213
|
-
- [ ] No `setTimeout` with fixed delays
|
|
214
|
-
- [ ] No external service calls
|
|
215
|
-
- [ ] No random values without seeding
|
|
216
|
-
|
|
217
|
-
### Environment
|
|
218
|
-
- [ ] Node version documented (local vs CI)
|
|
219
|
-
- [ ] Operating system noted (different on CI?)
|
|
220
|
-
- [ ] CI environment variables checked
|
|
221
|
-
- [ ] Parallelization disabled for test
|
|
222
|
-
- [ ] Test isolation verified
|
|
223
|
-
|
|
224
|
-
### Fix Validation
|
|
225
|
-
- [ ] Test passes 20 times consecutively
|
|
226
|
-
- [ ] Test passes in random order with other tests
|
|
227
|
-
- [ ] Test passes in CI
|
|
228
|
-
- [ ] Test passes under load
|
|
229
|
-
- [ ] No other tests broken
|
|
230
|
-
|
|
231
|
-
---
|
|
232
|
-
|
|
233
|
-
## Debugging Strategy
|
|
234
|
-
|
|
235
|
-
### If Pattern 1: Race Condition
|
|
236
|
-
```bash
|
|
237
|
-
# Add debug logging to see timing
|
|
238
|
-
npm test -- --verbose path/to/test.js
|
|
239
|
-
|
|
240
|
-
# Add explicit waits
|
|
241
|
-
await new Promise(r => setTimeout(r, 100));
|
|
242
|
-
|
|
243
|
-
# Use waitFor with debug
|
|
244
|
-
await waitFor(() => {
|
|
245
|
-
console.log('Checking condition...');
|
|
246
|
-
expect(element).toBeVisible();
|
|
247
|
-
});
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### If Pattern 2: Test Order Dependency
|
|
251
|
-
```bash
|
|
252
|
-
# Run tests in random order
|
|
253
|
-
npm test -- --randomize path/to/test.js
|
|
254
|
-
|
|
255
|
-
# Run test alone vs with others
|
|
256
|
-
npm test -- test1.js test2.js test3.js # Together
|
|
257
|
-
npm test -- test2.js # Alone
|
|
258
|
-
|
|
259
|
-
# Check beforeEach/afterEach setup
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
### If Pattern 3: Timing Issues
|
|
263
|
-
```bash
|
|
264
|
-
# Check system load
|
|
265
|
-
# Monitor: CPU, Memory, Disk I/O during test run
|
|
266
|
-
|
|
267
|
-
# Increase timeouts
|
|
268
|
-
await waitFor(() => expect(...), { timeout: 10000 });
|
|
269
|
-
|
|
270
|
-
# Replace sleep with condition
|
|
271
|
-
// Instead of: await sleep(1000);
|
|
272
|
-
await waitFor(() => expect(element).toBeVisible());
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### If Pattern 4: External Service
|
|
276
|
-
```bash
|
|
277
|
-
# Verify all external calls are mocked
|
|
278
|
-
grep -r "fetch\|axios\|http" src/__tests__/
|
|
279
|
-
|
|
280
|
-
# Check mock setup
|
|
281
|
-
console.log(jest.mock(...));
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### If Pattern 5: Randomness
|
|
285
|
-
```bash
|
|
286
|
-
# Set seed for randomness
|
|
287
|
-
jest.spyOn(Math, 'random').mockReturnValue(0.5);
|
|
288
|
-
|
|
289
|
-
# Mock Date.now()
|
|
290
|
-
jest.spyOn(Date, 'now').mockReturnValue(1234567890);
|
|
291
|
-
|
|
292
|
-
# Use deterministic test data
|
|
293
|
-
const testUser = { id: '123', name: 'Test' }; // Same each time
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
---
|
|
297
|
-
|
|
298
|
-
## Verification: Flaky Test Fixed
|
|
299
|
-
|
|
300
|
-
- [ ] Test passes 20 times consecutively
|
|
301
|
-
- [ ] Test passes 20 times in random order
|
|
302
|
-
- [ ] Test passes in CI environment
|
|
303
|
-
- [ ] Test passes under system load
|
|
304
|
-
- [ ] No debug code left
|
|
305
|
-
- [ ] Updated RECOVERY_POINTS.md with pattern
|
|
306
|
-
- [ ] Similar tests checked for same issue
|
|
307
|
-
|
|
308
|
-
---
|
|
309
|
-
|
|
310
|
-
## Recovery: Still Flaky?
|
|
311
|
-
|
|
312
|
-
If still failing after fixes:
|
|
313
|
-
|
|
314
|
-
1. **Add extensive logging**:
|
|
315
|
-
```javascript
|
|
316
|
-
test('flaky test', async () => {
|
|
317
|
-
console.log('Start:', Date.now());
|
|
318
|
-
const value = getSomeValue();
|
|
319
|
-
console.log('Value:', value);
|
|
320
|
-
await waitForCondition();
|
|
321
|
-
console.log('Condition met:', Date.now());
|
|
322
|
-
expect(...).toBe(...);
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
2. **Disable test temporarily**:
|
|
327
|
-
```javascript
|
|
328
|
-
test.skip('flaky test', async () => { // Temporarily skip
|
|
329
|
-
// ...
|
|
330
|
-
});
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
3. **Escalate**: Mark test with TODO comment for team
|
|
334
|
-
```javascript
|
|
335
|
-
// TODO: Fix flaky test - likely timing issue, investigate when time permits
|
|
336
|
-
test.skip('should handle concurrent updates', async () => {
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
4. **Create issue**: Document flaky test pattern for future fix
|
|
1
|
+
# Flaky Test Investigation Checklist
|
|
2
|
+
|
|
3
|
+
**Purpose**: Systematic debugging for tests that fail intermittently. These are notoriously hard to fix but follow patterns.
|
|
4
|
+
|
|
5
|
+
**Status**: MANDATORY - Complete before attempting flaky test fix.
|
|
6
|
+
|
|
7
|
+
## Characteristics: Is It Actually Flaky?
|
|
8
|
+
|
|
9
|
+
- [ ] **Fails inconsistently**:
|
|
10
|
+
- Run test 10 times: `for i in {1..10}; do npm test -- test.js; done`
|
|
11
|
+
- Fails ~X times out of 10
|
|
12
|
+
- Not always the same assertion fails
|
|
13
|
+
- Not 0% or 100% (if always fails, see test-failure-debug.md)
|
|
14
|
+
|
|
15
|
+
- [ ] **Failure pattern exists**:
|
|
16
|
+
- Fails more often under load?
|
|
17
|
+
- Fails in CI but not locally?
|
|
18
|
+
- Fails after other tests?
|
|
19
|
+
- Fails at specific time of day?
|
|
20
|
+
|
|
21
|
+
- [ ] **Previous attempts failed**:
|
|
22
|
+
- Simple retry didn't fix it
|
|
23
|
+
- Disabling parallelization didn't fix it
|
|
24
|
+
- Increasing timeout didn't fix it
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Root Cause Pattern Analysis
|
|
29
|
+
|
|
30
|
+
### Pattern 1: Race Condition (Most Common)
|
|
31
|
+
```javascript
|
|
32
|
+
// ❌ WRONG: Race condition
|
|
33
|
+
test('user updates profile', async () => {
|
|
34
|
+
updateUser({ name: 'New Name' }); // Async, not awaited!
|
|
35
|
+
expect(userName).toBe('New Name'); // Might run before update completes
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// ✅ CORRECT: Wait for async operation
|
|
39
|
+
test('user updates profile', async () => {
|
|
40
|
+
await updateUser({ name: 'New Name' }); // Wait for it
|
|
41
|
+
expect(userName).toBe('New Name');
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Signs**:
|
|
46
|
+
- [ ] Test has async operations
|
|
47
|
+
- [ ] Not all async operations awaited
|
|
48
|
+
- [ ] Uses `setTimeout` or `setInterval`
|
|
49
|
+
- [ ] Fails under high CPU load
|
|
50
|
+
- [ ] Fails more in CI (slower machines)
|
|
51
|
+
|
|
52
|
+
**Fix**:
|
|
53
|
+
```javascript
|
|
54
|
+
// Add explicit waits
|
|
55
|
+
await waitFor(() => expect(element).toBeVisible());
|
|
56
|
+
|
|
57
|
+
// Use async/await properly
|
|
58
|
+
const result = await asyncFunction();
|
|
59
|
+
|
|
60
|
+
// Don't mix promises with callbacks
|
|
61
|
+
// Use either async/await OR .then(), not both
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Pattern 2: Test Order Dependency
|
|
65
|
+
```javascript
|
|
66
|
+
// ❌ WRONG: Tests affect each other
|
|
67
|
+
let globalState = 0;
|
|
68
|
+
test('first test', () => { globalState = 1; });
|
|
69
|
+
test('second test', () => { expect(globalState).toBe(0); }); // Fails if first runs first!
|
|
70
|
+
|
|
71
|
+
// ✅ CORRECT: Each test is isolated
|
|
72
|
+
beforeEach(() => { state = 0; }); // Reset before each test
|
|
73
|
+
test('first test', () => { state = 1; });
|
|
74
|
+
test('second test', () => { expect(state).toBe(0); });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Signs**:
|
|
78
|
+
- [ ] Test passes when run alone
|
|
79
|
+
- [ ] Test fails when run with others
|
|
80
|
+
- [ ] Test fails when run in random order
|
|
81
|
+
- [ ] Global variables used
|
|
82
|
+
- [ ] Database not cleaned between tests
|
|
83
|
+
|
|
84
|
+
**Fix**:
|
|
85
|
+
```javascript
|
|
86
|
+
beforeEach(() => {
|
|
87
|
+
// Reset all state
|
|
88
|
+
jest.clearAllMocks();
|
|
89
|
+
db.clear();
|
|
90
|
+
localStorage.clear();
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Pattern 3: Timing Issues
|
|
95
|
+
```javascript
|
|
96
|
+
// ❌ WRONG: Fixed timeout too short
|
|
97
|
+
test('animation completes', async () => {
|
|
98
|
+
await new Promise(r => setTimeout(r, 50)); // Sometimes not enough time
|
|
99
|
+
expect(element.style.opacity).toBe(1);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// ✅ CORRECT: Wait for actual condition
|
|
103
|
+
test('animation completes', async () => {
|
|
104
|
+
await waitFor(() => expect(element).toHaveStyle('opacity: 1'), { timeout: 5000 });
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Signs**:
|
|
109
|
+
- [ ] Uses `setTimeout` with fixed delays
|
|
110
|
+
- [ ] Fails randomly
|
|
111
|
+
- [ ] Fails when machine is busy
|
|
112
|
+
- [ ] Fails in CI (which is slower)
|
|
113
|
+
- [ ] Increasing timeout "fixes" it temporarily
|
|
114
|
+
|
|
115
|
+
**Fix**:
|
|
116
|
+
```javascript
|
|
117
|
+
// Instead of sleep:
|
|
118
|
+
await new Promise(r => setTimeout(r, 100));
|
|
119
|
+
|
|
120
|
+
// Use condition-based waiting:
|
|
121
|
+
await waitFor(() => expect(element).toBeVisible());
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Pattern 4: External Service/Network
|
|
125
|
+
```javascript
|
|
126
|
+
// ❌ WRONG: Calls actual API
|
|
127
|
+
test('fetch user', async () => {
|
|
128
|
+
const user = await fetchRealAPI('/users/1'); // Network unreliable!
|
|
129
|
+
expect(user.name).toBe('John');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ✅ CORRECT: Mock external service
|
|
133
|
+
test('fetch user', async () => {
|
|
134
|
+
jest.mock('api', () => ({
|
|
135
|
+
fetch: jest.fn().mockResolvedValue({ name: 'John' })
|
|
136
|
+
}));
|
|
137
|
+
const user = await fetchUser(1);
|
|
138
|
+
expect(user.name).toBe('John');
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Signs**:
|
|
143
|
+
- [ ] Test makes network requests
|
|
144
|
+
- [ ] Mock not configured properly
|
|
145
|
+
- [ ] Network-dependent test
|
|
146
|
+
- [ ] Fails in offline environment
|
|
147
|
+
- [ ] Fails randomly (network issues)
|
|
148
|
+
|
|
149
|
+
**Fix**:
|
|
150
|
+
```javascript
|
|
151
|
+
// Mock all external APIs
|
|
152
|
+
jest.mock('node-fetch');
|
|
153
|
+
nodeFetch.mockResolvedValue({ json: () => ({ ... }) });
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Pattern 5: Randomness/Seeding
|
|
157
|
+
```javascript
|
|
158
|
+
// ❌ WRONG: Random data without seed
|
|
159
|
+
test('shuffle array', () => {
|
|
160
|
+
const result = shuffle([1, 2, 3]);
|
|
161
|
+
expect(result).toEqual([2, 1, 3]); // Different order each time!
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// ✅ CORRECT: Seed random for reproducible tests
|
|
165
|
+
test('shuffle array', () => {
|
|
166
|
+
Math.seedrandom = () => 0.5; // Set seed
|
|
167
|
+
const result = shuffle([1, 2, 3]);
|
|
168
|
+
expect(result).toEqual([3, 1, 2]); // Same every time with same seed
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Signs**:
|
|
173
|
+
- [ ] Test uses `Math.random()`
|
|
174
|
+
- [ ] Test uses `Date.now()`
|
|
175
|
+
- [ ] Test uses `UUID.generate()`
|
|
176
|
+
- [ ] Different assertions fail each run
|
|
177
|
+
- [ ] Works locally but fails in CI
|
|
178
|
+
|
|
179
|
+
**Fix**:
|
|
180
|
+
```javascript
|
|
181
|
+
// Seed random number generator
|
|
182
|
+
beforeEach(() => {
|
|
183
|
+
jest.spyOn(Math, 'random').mockReturnValue(0.5);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Mock Date.now()
|
|
187
|
+
jest.spyOn(Date, 'now').mockReturnValue(1234567890);
|
|
188
|
+
|
|
189
|
+
// Generate UUIDs deterministically
|
|
190
|
+
jest.mock('uuid', () => ({ v4: () => 'test-uuid-123' }));
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Investigation Checklist
|
|
196
|
+
|
|
197
|
+
### Documentation
|
|
198
|
+
- [ ] When test was added
|
|
199
|
+
- [ ] When it started failing
|
|
200
|
+
- [ ] Failure frequency recorded
|
|
201
|
+
- [ ] Specific assertion that fails (changes?)
|
|
202
|
+
|
|
203
|
+
### Reproduction
|
|
204
|
+
- [ ] Test fails when run 10 times in sequence
|
|
205
|
+
- [ ] Test fails when run in random order with others
|
|
206
|
+
- [ ] Test fails in CI but not locally (or vice versa)
|
|
207
|
+
- [ ] Test fails under high system load
|
|
208
|
+
|
|
209
|
+
### Code Analysis
|
|
210
|
+
- [ ] All async operations have `await`
|
|
211
|
+
- [ ] No global state modifications
|
|
212
|
+
- [ ] All mocks are configured
|
|
213
|
+
- [ ] No `setTimeout` with fixed delays
|
|
214
|
+
- [ ] No external service calls
|
|
215
|
+
- [ ] No random values without seeding
|
|
216
|
+
|
|
217
|
+
### Environment
|
|
218
|
+
- [ ] Node version documented (local vs CI)
|
|
219
|
+
- [ ] Operating system noted (different on CI?)
|
|
220
|
+
- [ ] CI environment variables checked
|
|
221
|
+
- [ ] Parallelization disabled for test
|
|
222
|
+
- [ ] Test isolation verified
|
|
223
|
+
|
|
224
|
+
### Fix Validation
|
|
225
|
+
- [ ] Test passes 20 times consecutively
|
|
226
|
+
- [ ] Test passes in random order with other tests
|
|
227
|
+
- [ ] Test passes in CI
|
|
228
|
+
- [ ] Test passes under load
|
|
229
|
+
- [ ] No other tests broken
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Debugging Strategy
|
|
234
|
+
|
|
235
|
+
### If Pattern 1: Race Condition
|
|
236
|
+
```bash
|
|
237
|
+
# Add debug logging to see timing
|
|
238
|
+
npm test -- --verbose path/to/test.js
|
|
239
|
+
|
|
240
|
+
# Add explicit waits
|
|
241
|
+
await new Promise(r => setTimeout(r, 100));
|
|
242
|
+
|
|
243
|
+
# Use waitFor with debug
|
|
244
|
+
await waitFor(() => {
|
|
245
|
+
console.log('Checking condition...');
|
|
246
|
+
expect(element).toBeVisible();
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### If Pattern 2: Test Order Dependency
|
|
251
|
+
```bash
|
|
252
|
+
# Run tests in random order
|
|
253
|
+
npm test -- --randomize path/to/test.js
|
|
254
|
+
|
|
255
|
+
# Run test alone vs with others
|
|
256
|
+
npm test -- test1.js test2.js test3.js # Together
|
|
257
|
+
npm test -- test2.js # Alone
|
|
258
|
+
|
|
259
|
+
# Check beforeEach/afterEach setup
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### If Pattern 3: Timing Issues
|
|
263
|
+
```bash
|
|
264
|
+
# Check system load
|
|
265
|
+
# Monitor: CPU, Memory, Disk I/O during test run
|
|
266
|
+
|
|
267
|
+
# Increase timeouts
|
|
268
|
+
await waitFor(() => expect(...), { timeout: 10000 });
|
|
269
|
+
|
|
270
|
+
# Replace sleep with condition
|
|
271
|
+
// Instead of: await sleep(1000);
|
|
272
|
+
await waitFor(() => expect(element).toBeVisible());
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### If Pattern 4: External Service
|
|
276
|
+
```bash
|
|
277
|
+
# Verify all external calls are mocked
|
|
278
|
+
grep -r "fetch\|axios\|http" src/__tests__/
|
|
279
|
+
|
|
280
|
+
# Check mock setup
|
|
281
|
+
console.log(jest.mock(...));
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### If Pattern 5: Randomness
|
|
285
|
+
```bash
|
|
286
|
+
# Set seed for randomness
|
|
287
|
+
jest.spyOn(Math, 'random').mockReturnValue(0.5);
|
|
288
|
+
|
|
289
|
+
# Mock Date.now()
|
|
290
|
+
jest.spyOn(Date, 'now').mockReturnValue(1234567890);
|
|
291
|
+
|
|
292
|
+
# Use deterministic test data
|
|
293
|
+
const testUser = { id: '123', name: 'Test' }; // Same each time
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Verification: Flaky Test Fixed
|
|
299
|
+
|
|
300
|
+
- [ ] Test passes 20 times consecutively
|
|
301
|
+
- [ ] Test passes 20 times in random order
|
|
302
|
+
- [ ] Test passes in CI environment
|
|
303
|
+
- [ ] Test passes under system load
|
|
304
|
+
- [ ] No debug code left
|
|
305
|
+
- [ ] Updated RECOVERY_POINTS.md with pattern
|
|
306
|
+
- [ ] Similar tests checked for same issue
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Recovery: Still Flaky?
|
|
311
|
+
|
|
312
|
+
If still failing after fixes:
|
|
313
|
+
|
|
314
|
+
1. **Add extensive logging**:
|
|
315
|
+
```javascript
|
|
316
|
+
test('flaky test', async () => {
|
|
317
|
+
console.log('Start:', Date.now());
|
|
318
|
+
const value = getSomeValue();
|
|
319
|
+
console.log('Value:', value);
|
|
320
|
+
await waitForCondition();
|
|
321
|
+
console.log('Condition met:', Date.now());
|
|
322
|
+
expect(...).toBe(...);
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
2. **Disable test temporarily**:
|
|
327
|
+
```javascript
|
|
328
|
+
test.skip('flaky test', async () => { // Temporarily skip
|
|
329
|
+
// ...
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
3. **Escalate**: Mark test with TODO comment for team
|
|
334
|
+
```javascript
|
|
335
|
+
// TODO: Fix flaky test - likely timing issue, investigate when time permits
|
|
336
|
+
test.skip('should handle concurrent updates', async () => {
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
4. **Create issue**: Document flaky test pattern for future fix
|