opencode-swarm-plugin 0.44.0 → 0.44.2

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.
Files changed (215) hide show
  1. package/bin/swarm.serve.test.ts +6 -4
  2. package/bin/swarm.ts +18 -12
  3. package/dist/compaction-prompt-scoring.js +139 -0
  4. package/dist/eval-capture.js +12811 -0
  5. package/dist/hive.d.ts.map +1 -1
  6. package/dist/hive.js +14834 -0
  7. package/dist/index.d.ts +18 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +7743 -62593
  10. package/dist/plugin.js +24052 -78907
  11. package/dist/swarm-orchestrate.d.ts.map +1 -1
  12. package/dist/swarm-prompts.d.ts.map +1 -1
  13. package/dist/swarm-prompts.js +39407 -0
  14. package/dist/swarm-review.d.ts.map +1 -1
  15. package/dist/swarm-validation.d.ts +127 -0
  16. package/dist/swarm-validation.d.ts.map +1 -0
  17. package/dist/validators/index.d.ts +7 -0
  18. package/dist/validators/index.d.ts.map +1 -0
  19. package/dist/validators/schema-validator.d.ts +58 -0
  20. package/dist/validators/schema-validator.d.ts.map +1 -0
  21. package/package.json +17 -5
  22. package/.changeset/swarm-insights-data-layer.md +0 -63
  23. package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
  24. package/.hive/analysis/session-data-quality-audit.md +0 -320
  25. package/.hive/eval-results.json +0 -483
  26. package/.hive/issues.jsonl +0 -138
  27. package/.hive/memories.jsonl +0 -729
  28. package/.opencode/eval-history.jsonl +0 -327
  29. package/.turbo/turbo-build.log +0 -9
  30. package/CHANGELOG.md +0 -2286
  31. package/SCORER-ANALYSIS.md +0 -598
  32. package/docs/analysis/subagent-coordination-patterns.md +0 -902
  33. package/docs/analysis-socratic-planner-pattern.md +0 -504
  34. package/docs/planning/ADR-001-monorepo-structure.md +0 -171
  35. package/docs/planning/ADR-002-package-extraction.md +0 -393
  36. package/docs/planning/ADR-003-performance-improvements.md +0 -451
  37. package/docs/planning/ADR-004-message-queue-features.md +0 -187
  38. package/docs/planning/ADR-005-devtools-observability.md +0 -202
  39. package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
  40. package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
  41. package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
  42. package/docs/planning/ADR-010-cass-inhousing.md +0 -1215
  43. package/docs/planning/ROADMAP.md +0 -368
  44. package/docs/semantic-memory-cli-syntax.md +0 -123
  45. package/docs/swarm-mail-architecture.md +0 -1147
  46. package/docs/testing/context-recovery-test.md +0 -470
  47. package/evals/ARCHITECTURE.md +0 -1189
  48. package/evals/README.md +0 -768
  49. package/evals/compaction-prompt.eval.ts +0 -149
  50. package/evals/compaction-resumption.eval.ts +0 -289
  51. package/evals/coordinator-behavior.eval.ts +0 -307
  52. package/evals/coordinator-session.eval.ts +0 -154
  53. package/evals/evalite.config.ts.bak +0 -15
  54. package/evals/example.eval.ts +0 -31
  55. package/evals/fixtures/cass-baseline.ts +0 -217
  56. package/evals/fixtures/compaction-cases.ts +0 -350
  57. package/evals/fixtures/compaction-prompt-cases.ts +0 -311
  58. package/evals/fixtures/coordinator-sessions.ts +0 -328
  59. package/evals/fixtures/decomposition-cases.ts +0 -105
  60. package/evals/lib/compaction-loader.test.ts +0 -248
  61. package/evals/lib/compaction-loader.ts +0 -320
  62. package/evals/lib/data-loader.evalite-test.ts +0 -289
  63. package/evals/lib/data-loader.test.ts +0 -345
  64. package/evals/lib/data-loader.ts +0 -281
  65. package/evals/lib/llm.ts +0 -115
  66. package/evals/scorers/compaction-prompt-scorers.ts +0 -145
  67. package/evals/scorers/compaction-scorers.ts +0 -305
  68. package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
  69. package/evals/scorers/coordinator-discipline.ts +0 -325
  70. package/evals/scorers/index.test.ts +0 -146
  71. package/evals/scorers/index.ts +0 -328
  72. package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
  73. package/evals/scorers/outcome-scorers.ts +0 -349
  74. package/evals/swarm-decomposition.eval.ts +0 -121
  75. package/examples/commands/swarm.md +0 -745
  76. package/examples/plugin-wrapper-template.ts +0 -2515
  77. package/examples/skills/hive-workflow/SKILL.md +0 -212
  78. package/examples/skills/skill-creator/SKILL.md +0 -223
  79. package/examples/skills/swarm-coordination/SKILL.md +0 -292
  80. package/global-skills/cli-builder/SKILL.md +0 -344
  81. package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
  82. package/global-skills/learning-systems/SKILL.md +0 -644
  83. package/global-skills/skill-creator/LICENSE.txt +0 -202
  84. package/global-skills/skill-creator/SKILL.md +0 -352
  85. package/global-skills/skill-creator/references/output-patterns.md +0 -82
  86. package/global-skills/skill-creator/references/workflows.md +0 -28
  87. package/global-skills/swarm-coordination/SKILL.md +0 -995
  88. package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
  89. package/global-skills/swarm-coordination/references/strategies.md +0 -138
  90. package/global-skills/system-design/SKILL.md +0 -213
  91. package/global-skills/testing-patterns/SKILL.md +0 -430
  92. package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
  93. package/opencode-swarm-plugin-0.30.7.tgz +0 -0
  94. package/opencode-swarm-plugin-0.31.0.tgz +0 -0
  95. package/scripts/cleanup-test-memories.ts +0 -346
  96. package/scripts/init-skill.ts +0 -222
  97. package/scripts/migrate-unknown-sessions.ts +0 -349
  98. package/scripts/validate-skill.ts +0 -204
  99. package/src/agent-mail.ts +0 -1724
  100. package/src/anti-patterns.test.ts +0 -1167
  101. package/src/anti-patterns.ts +0 -448
  102. package/src/compaction-capture.integration.test.ts +0 -257
  103. package/src/compaction-hook.test.ts +0 -838
  104. package/src/compaction-hook.ts +0 -1204
  105. package/src/compaction-observability.integration.test.ts +0 -139
  106. package/src/compaction-observability.test.ts +0 -187
  107. package/src/compaction-observability.ts +0 -324
  108. package/src/compaction-prompt-scorers.test.ts +0 -475
  109. package/src/compaction-prompt-scoring.ts +0 -300
  110. package/src/contributor-tools.test.ts +0 -133
  111. package/src/contributor-tools.ts +0 -201
  112. package/src/dashboard.test.ts +0 -611
  113. package/src/dashboard.ts +0 -462
  114. package/src/error-enrichment.test.ts +0 -403
  115. package/src/error-enrichment.ts +0 -219
  116. package/src/eval-capture.test.ts +0 -1015
  117. package/src/eval-capture.ts +0 -929
  118. package/src/eval-gates.test.ts +0 -306
  119. package/src/eval-gates.ts +0 -218
  120. package/src/eval-history.test.ts +0 -508
  121. package/src/eval-history.ts +0 -214
  122. package/src/eval-learning.test.ts +0 -378
  123. package/src/eval-learning.ts +0 -360
  124. package/src/eval-runner.test.ts +0 -223
  125. package/src/eval-runner.ts +0 -402
  126. package/src/export-tools.test.ts +0 -476
  127. package/src/export-tools.ts +0 -257
  128. package/src/hive.integration.test.ts +0 -2241
  129. package/src/hive.ts +0 -1628
  130. package/src/index.ts +0 -940
  131. package/src/learning.integration.test.ts +0 -1815
  132. package/src/learning.ts +0 -1079
  133. package/src/logger.test.ts +0 -189
  134. package/src/logger.ts +0 -135
  135. package/src/mandate-promotion.test.ts +0 -473
  136. package/src/mandate-promotion.ts +0 -239
  137. package/src/mandate-storage.integration.test.ts +0 -601
  138. package/src/mandate-storage.test.ts +0 -578
  139. package/src/mandate-storage.ts +0 -794
  140. package/src/mandates.ts +0 -540
  141. package/src/memory-tools.test.ts +0 -195
  142. package/src/memory-tools.ts +0 -344
  143. package/src/memory.integration.test.ts +0 -334
  144. package/src/memory.test.ts +0 -158
  145. package/src/memory.ts +0 -527
  146. package/src/model-selection.test.ts +0 -188
  147. package/src/model-selection.ts +0 -68
  148. package/src/observability-tools.test.ts +0 -359
  149. package/src/observability-tools.ts +0 -871
  150. package/src/output-guardrails.test.ts +0 -438
  151. package/src/output-guardrails.ts +0 -381
  152. package/src/pattern-maturity.test.ts +0 -1160
  153. package/src/pattern-maturity.ts +0 -525
  154. package/src/planning-guardrails.test.ts +0 -491
  155. package/src/planning-guardrails.ts +0 -438
  156. package/src/plugin.ts +0 -23
  157. package/src/post-compaction-tracker.test.ts +0 -251
  158. package/src/post-compaction-tracker.ts +0 -237
  159. package/src/query-tools.test.ts +0 -636
  160. package/src/query-tools.ts +0 -324
  161. package/src/rate-limiter.integration.test.ts +0 -466
  162. package/src/rate-limiter.ts +0 -774
  163. package/src/replay-tools.test.ts +0 -496
  164. package/src/replay-tools.ts +0 -240
  165. package/src/repo-crawl.integration.test.ts +0 -441
  166. package/src/repo-crawl.ts +0 -610
  167. package/src/schemas/cell-events.test.ts +0 -347
  168. package/src/schemas/cell-events.ts +0 -807
  169. package/src/schemas/cell.ts +0 -257
  170. package/src/schemas/evaluation.ts +0 -166
  171. package/src/schemas/index.test.ts +0 -199
  172. package/src/schemas/index.ts +0 -286
  173. package/src/schemas/mandate.ts +0 -232
  174. package/src/schemas/swarm-context.ts +0 -115
  175. package/src/schemas/task.ts +0 -161
  176. package/src/schemas/worker-handoff.test.ts +0 -302
  177. package/src/schemas/worker-handoff.ts +0 -131
  178. package/src/sessions/agent-discovery.test.ts +0 -137
  179. package/src/sessions/agent-discovery.ts +0 -112
  180. package/src/sessions/index.ts +0 -15
  181. package/src/skills.integration.test.ts +0 -1192
  182. package/src/skills.test.ts +0 -643
  183. package/src/skills.ts +0 -1549
  184. package/src/storage.integration.test.ts +0 -341
  185. package/src/storage.ts +0 -884
  186. package/src/structured.integration.test.ts +0 -817
  187. package/src/structured.test.ts +0 -1046
  188. package/src/structured.ts +0 -762
  189. package/src/swarm-decompose.test.ts +0 -188
  190. package/src/swarm-decompose.ts +0 -1302
  191. package/src/swarm-deferred.integration.test.ts +0 -157
  192. package/src/swarm-deferred.test.ts +0 -38
  193. package/src/swarm-insights.test.ts +0 -214
  194. package/src/swarm-insights.ts +0 -459
  195. package/src/swarm-mail.integration.test.ts +0 -970
  196. package/src/swarm-mail.ts +0 -739
  197. package/src/swarm-orchestrate.integration.test.ts +0 -282
  198. package/src/swarm-orchestrate.test.ts +0 -548
  199. package/src/swarm-orchestrate.ts +0 -3084
  200. package/src/swarm-prompts.test.ts +0 -1270
  201. package/src/swarm-prompts.ts +0 -2077
  202. package/src/swarm-research.integration.test.ts +0 -701
  203. package/src/swarm-research.test.ts +0 -698
  204. package/src/swarm-research.ts +0 -472
  205. package/src/swarm-review.integration.test.ts +0 -285
  206. package/src/swarm-review.test.ts +0 -879
  207. package/src/swarm-review.ts +0 -709
  208. package/src/swarm-strategies.ts +0 -407
  209. package/src/swarm-worktree.test.ts +0 -501
  210. package/src/swarm-worktree.ts +0 -575
  211. package/src/swarm.integration.test.ts +0 -2377
  212. package/src/swarm.ts +0 -38
  213. package/src/tool-adapter.integration.test.ts +0 -1221
  214. package/src/tool-availability.ts +0 -461
  215. package/tsconfig.json +0 -28
@@ -1,430 +0,0 @@
1
- ---
2
- name: testing-patterns
3
- description: Patterns for testing code effectively. Use when breaking dependencies for testability, adding tests to existing code, understanding unfamiliar code through characterization tests, or deciding how to structure tests. Covers seams, dependency injection, test doubles, and safe refactoring techniques from Michael Feathers.
4
- ---
5
-
6
- # Testing Patterns
7
-
8
- **Core insight**: Code without tests is hard to change safely. The Testing Dilemma: to change code safely you need tests, but to add tests you often need to change code first.
9
-
10
- ## The Seam Model
11
-
12
- A **seam** is a place where you can alter behavior without editing the source file.
13
-
14
- Every seam has an **enabling point** - the place where you decide which behavior to use.
15
-
16
- ### Seam Types (Best to Worst)
17
-
18
- **Object Seams** (preferred in OO languages):
19
-
20
- ```typescript
21
- // Original - hard dependency
22
- class PaymentProcessor {
23
- process(amount: number) {
24
- const gateway = new StripeGateway(); // untestable
25
- return gateway.charge(amount);
26
- }
27
- }
28
-
29
- // With seam - injectable dependency
30
- class PaymentProcessor {
31
- constructor(private gateway: PaymentGateway = new StripeGateway()) {}
32
-
33
- process(amount: number) {
34
- return this.gateway.charge(amount); // enabling point: constructor
35
- }
36
- }
37
- ```
38
-
39
- **Link Seams** (classpath/module resolution):
40
-
41
- - Enabling point is outside program text (build scripts, import maps)
42
- - Swap implementations at link time
43
- - Useful but harder to notice
44
-
45
- **Preprocessing Seams** (C/C++ only):
46
-
47
- - `#include` and `#define` manipulation
48
- - Last resort - avoid in modern languages
49
-
50
- ## Characterization Tests
51
-
52
- **Purpose**: Document what code actually does, not what it should do.
53
-
54
- **Process**:
55
-
56
- 1. Write a test you know will fail
57
- 2. Run it - let the failure tell you actual behavior
58
- 3. Change the test to expect actual behavior
59
- 4. Repeat until you've characterized the code
60
-
61
- ```typescript
62
- // Step 1: Write failing test
63
- test("calculateFee returns... something", () => {
64
- const result = calculateFee(100, "premium");
65
- expect(result).toBe(0); // will fail, tells us actual value
66
- });
67
-
68
- // Step 2: After failure shows "Expected 0, got 15"
69
- test("calculateFee returns 15 for premium with 100", () => {
70
- const result = calculateFee(100, "premium");
71
- expect(result).toBe(15); // now documents actual behavior
72
- });
73
- ```
74
-
75
- **Key insight**: Characterization tests verify behaviors ARE present, enabling safe refactoring. They're not about correctness - they're about preservation.
76
-
77
- ## Breaking Dependencies
78
-
79
- ### When You Can't Instantiate a Class
80
-
81
- **Parameterize Constructor** - externalize dependencies:
82
-
83
- ```typescript
84
- // Before
85
- class MailChecker {
86
- constructor(checkPeriod: number) {
87
- this.receiver = new MailReceiver(); // hidden dependency
88
- }
89
- }
90
-
91
- // After - add parameter with default
92
- class MailChecker {
93
- constructor(
94
- checkPeriod: number,
95
- receiver: MailReceiver = new MailReceiver(),
96
- ) {
97
- this.receiver = receiver;
98
- }
99
- }
100
- ```
101
-
102
- **Extract Interface** - safest dependency break:
103
-
104
- ```typescript
105
- // 1. Create interface from class
106
- interface MessageReceiver {
107
- receive(): Message[];
108
- }
109
-
110
- // 2. Have original implement it
111
- class MailReceiver implements MessageReceiver { ... }
112
-
113
- // 3. Create test double
114
- class FakeReceiver implements MessageReceiver {
115
- messages: Message[] = [];
116
- receive() { return this.messages; }
117
- }
118
- ```
119
-
120
- **Subclass and Override Method** - core technique:
121
-
122
- ```typescript
123
- // Production class with problematic method
124
- class OrderProcessor {
125
- protected getDatabase(): Database {
126
- return new ProductionDatabase(); // can't use in tests
127
- }
128
-
129
- process(order: Order) {
130
- const db = this.getDatabase();
131
- // ... processing logic
132
- }
133
- }
134
-
135
- // Testing subclass
136
- class TestableOrderProcessor extends OrderProcessor {
137
- protected getDatabase(): Database {
138
- return new InMemoryDatabase(); // test-friendly
139
- }
140
- }
141
- ```
142
-
143
- ### Sensing vs Separation
144
-
145
- **Sensing**: Need to verify effects of code (what did it do?)
146
- **Separation**: Need to run code independently (isolate from dependencies)
147
-
148
- Choose technique based on which problem you're solving.
149
-
150
- ## Adding New Behavior Safely
151
-
152
- ### Sprout Method
153
-
154
- Add new behavior in a new method, call it from existing code:
155
-
156
- ```typescript
157
- // Before - need to add validation
158
- function processOrder(order: Order) {
159
- // ... 200 lines of untested code
160
- saveOrder(order);
161
- }
162
-
163
- // After - sprout new tested method
164
- function validateOrder(order: Order): ValidationResult {
165
- // New code, fully tested
166
- }
167
-
168
- function processOrder(order: Order) {
169
- const validation = validateOrder(order); // one new line
170
- if (!validation.valid) return;
171
- // ... 200 lines of untested code
172
- saveOrder(order);
173
- }
174
- ```
175
-
176
- ### Sprout Class
177
-
178
- When new behavior doesn't fit existing class:
179
-
180
- ```typescript
181
- // New class for new behavior
182
- class OrderValidator {
183
- validate(order: Order): ValidationResult { ... }
184
- }
185
-
186
- // Minimal change to existing code
187
- function processOrder(order: Order) {
188
- const validator = new OrderValidator();
189
- if (!validator.validate(order).valid) return;
190
- // ... existing untested code
191
- }
192
- ```
193
-
194
- ### Wrap Method
195
-
196
- Rename existing method, create new method that wraps it:
197
-
198
- ```typescript
199
- // Before
200
- function pay(employees: Employee[]) {
201
- for (const e of employees) {
202
- e.pay();
203
- }
204
- }
205
-
206
- // After - wrap with logging
207
- function pay(employees: Employee[]) {
208
- logPayment(employees); // new behavior
209
- dispatchPay(employees); // renamed original
210
- }
211
-
212
- function dispatchPay(employees: Employee[]) {
213
- for (const e of employees) {
214
- e.pay();
215
- }
216
- }
217
- ```
218
-
219
- ## Finding Test Points
220
-
221
- ### Effect Sketches
222
-
223
- Draw what a method affects:
224
-
225
- ```
226
- method()
227
- → modifies field1
228
- → calls helper() → modifies field2
229
- → returns value based on field3
230
- ```
231
-
232
- ### Pinch Points
233
-
234
- A **pinch point** is a narrow place where many effects can be detected.
235
-
236
- Look for methods that:
237
-
238
- - Are called by many paths
239
- - Aggregate results from multiple operations
240
- - Sit at natural boundaries
241
-
242
- **Pinch points are ideal test locations** - one test covers many code paths.
243
-
244
- ### Interception Points
245
-
246
- Where you can detect effects of changes:
247
-
248
- 1. Return values
249
- 2. Modified state (fields, globals)
250
- 3. Calls to other objects (mock/spy)
251
-
252
- ## Safe Refactoring Techniques
253
-
254
- ### Preserve Signatures
255
-
256
- When breaking dependencies without tests:
257
-
258
- - Copy/paste method signatures exactly
259
- - Don't change parameter types or order
260
- - Lean on the compiler to catch mistakes
261
-
262
- ```typescript
263
- // Safe: copy signature exactly
264
- function process(order: Order, options: Options): Result;
265
- // becomes
266
- function processInternal(order: Order, options: Options): Result;
267
- ```
268
-
269
- ### Scratch Refactoring
270
-
271
- Refactor to understand, then throw it away:
272
-
273
- 1. Make aggressive changes to understand structure
274
- 2. Don't commit - this is exploration
275
- 3. Revert everything
276
- 4. Now you understand the code
277
- 5. Make real changes with tests
278
-
279
- ### Lean on the Compiler
280
-
281
- Use type system as safety net:
282
-
283
- 1. Make change that should cause compile errors
284
- 2. Compiler shows all affected locations
285
- 3. Fix each location
286
- 4. If it compiles, change is likely safe
287
-
288
- ## Decision Tree
289
-
290
- ```
291
- Need to add tests to code?
292
-
293
- ├─ Can you write a test for it now?
294
- │ └─ YES → Write test, make change, done
295
-
296
- └─ NO → What's blocking you?
297
-
298
- ├─ Can't instantiate class
299
- │ ├─ Hidden dependency → Parameterize Constructor
300
- │ ├─ Too many dependencies → Extract Interface
301
- │ └─ Constructor does work → Extract and Override Factory Method
302
-
303
- ├─ Can't call method in test
304
- │ ├─ Private method → Test through public interface (or make protected)
305
- │ ├─ Side effects → Extract and Override Call
306
- │ └─ Global state → Introduce Static Setter (carefully)
307
-
308
- └─ Don't understand the code
309
- ├─ Write Characterization Tests
310
- ├─ Do Scratch Refactoring (then revert)
311
- └─ Draw Effect Sketches
312
- ```
313
-
314
- ## Kent Beck's 4 Rules of Simple Design
315
-
316
- From Beck (codified by Corey Haines) - in priority order:
317
-
318
- 1. **Tests Pass** - Code must work. Without this, nothing else matters.
319
- 2. **Reveals Intention** - Code should express what it does clearly.
320
- 3. **No Duplication** - DRY, but specifically _duplication of knowledge_, not just structure.
321
- 4. **Fewest Elements** - Remove anything that doesn't serve the above three.
322
-
323
- ### Test Names Should Influence API
324
-
325
- Test names reveal design problems:
326
-
327
- ```typescript
328
- // Bad: test name doesn't match code
329
- test("a]live cell with 2 neighbors stays alive", () => {
330
- const cell = new Cell(true);
331
- cell.setNeighborCount(2);
332
- expect(cell.aliveInNextGeneration()).toBe(true);
333
- });
334
-
335
- // Better: API matches the language of the test
336
- test("alive cell with 2 neighbors stays alive", () => {
337
- const cell = Cell.alive();
338
- expect(cell.aliveInNextGeneration({ neighbors: 2 })).toBe(true);
339
- });
340
- ```
341
-
342
- ### Test Behavior, Not State
343
-
344
- ```typescript
345
- // Testing state (fragile)
346
- test("sets alive to false", () => {
347
- const cell = new Cell();
348
- cell.die();
349
- expect(cell.alive).toBe(false);
350
- });
351
-
352
- // Testing behavior (robust)
353
- test("dead cell stays dead with no neighbors", () => {
354
- const cell = Cell.dead();
355
- expect(cell.aliveInNextGeneration({ neighbors: 0 })).toBe(false);
356
- });
357
- ```
358
-
359
- ### Duplication of Knowledge vs Structure
360
-
361
- Not all duplication is bad. Two pieces of code that look the same but represent different concepts should NOT be merged:
362
-
363
- ```typescript
364
- // These look similar but represent different domain concepts
365
- const MINIMUM_NEIGHBORS_TO_SURVIVE = 2;
366
- const MINIMUM_NEIGHBORS_TO_REPRODUCE = 3;
367
-
368
- // DON'T merge just because the numbers are close
369
- // They change for different reasons
370
- ```
371
-
372
- ## Self-Testing Code (Fowler)
373
-
374
- > "Self-testing code not only enables refactoring—it also makes it much safer to add new features."
375
-
376
- **The key insight**: When a test fails, you know exactly what broke because you just changed it. Tests are a "powerful bug detector that decapitates the time it takes to find bugs."
377
-
378
- ### Test Isolation
379
-
380
- - Each test should be independent
381
- - Don't have tests depend on previous tests
382
- - Tests should be able to run in any order
383
-
384
- ### Breaking Abstraction Level
385
-
386
- Tests become fragile when they test at the wrong level:
387
-
388
- ```typescript
389
- // Fragile: tests implementation details
390
- test("stores user in database", () => {
391
- createUser({ name: "Joel" });
392
- expect(db.query("SELECT * FROM users")).toContain({ name: "Joel" });
393
- });
394
-
395
- // Robust: tests behavior through public API
396
- test("created user can be retrieved", () => {
397
- const id = createUser({ name: "Joel" });
398
- expect(getUser(id).name).toBe("Joel");
399
- });
400
- ```
401
-
402
- ## Test Doubles
403
-
404
- **Fake**: Working implementation with shortcuts (in-memory DB)
405
- **Stub**: Returns canned answers to calls
406
- **Mock**: Verifies interactions happened
407
- **Spy**: Records calls for later verification
408
-
409
- **Prefer fakes over mocks** - they're more realistic and less brittle.
410
-
411
- ```typescript
412
- // Fake - actually works, just simpler
413
- class FakeEmailService implements EmailService {
414
- sent: Email[] = [];
415
- send(email: Email) {
416
- this.sent.push(email);
417
- }
418
- }
419
-
420
- // Mock - verifies interaction
421
- const mockEmail = mock<EmailService>();
422
- // ... code runs ...
423
- expect(mockEmail.send).toHaveBeenCalledWith(expectedEmail);
424
- ```
425
-
426
- ## References
427
-
428
- For detailed patterns and examples:
429
-
430
- - `references/dependency-breaking-catalog.md` - All 25 techniques with examples