ima-claude 2.9.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.
Files changed (182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +463 -0
  3. package/dist/cli.js +1064 -0
  4. package/package.json +49 -0
  5. package/platforms/claude/adapter.ts +115 -0
  6. package/platforms/junie/adapter.ts +254 -0
  7. package/platforms/junie/agents-template.md +113 -0
  8. package/platforms/junie/hook-translations.md +84 -0
  9. package/platforms/shared/detector.ts +27 -0
  10. package/platforms/shared/installer.ts +202 -0
  11. package/platforms/shared/types.ts +78 -0
  12. package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
  13. package/plugins/ima-claude/agents/explorer.md +30 -0
  14. package/plugins/ima-claude/agents/implementer.md +30 -0
  15. package/plugins/ima-claude/agents/memory.md +42 -0
  16. package/plugins/ima-claude/agents/reviewer.md +53 -0
  17. package/plugins/ima-claude/agents/tester.md +33 -0
  18. package/plugins/ima-claude/agents/wp-developer.md +46 -0
  19. package/plugins/ima-claude/hooks/README.md +145 -0
  20. package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
  21. package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
  22. package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
  23. package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
  24. package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
  25. package/plugins/ima-claude/hooks/docs_organization.py +104 -0
  26. package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
  27. package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
  28. package/plugins/ima-claude/hooks/hook_logger.py +69 -0
  29. package/plugins/ima-claude/hooks/hooks.json +239 -0
  30. package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
  31. package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
  32. package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
  33. package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
  34. package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
  35. package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
  36. package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
  37. package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
  38. package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
  39. package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
  40. package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
  41. package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
  42. package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
  43. package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
  44. package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
  45. package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
  46. package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
  47. package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
  48. package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
  49. package/plugins/ima-claude/personalities/README.md +45 -0
  50. package/plugins/ima-claude/personalities/enable-40k.md +69 -0
  51. package/plugins/ima-claude/personalities/enable-templars.md +69 -0
  52. package/plugins/ima-claude/skills/.research-summary.md +340 -0
  53. package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
  54. package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
  55. package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
  56. package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
  57. package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
  58. package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
  59. package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
  60. package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
  61. package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
  62. package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
  63. package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
  64. package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
  65. package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
  66. package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
  67. package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
  68. package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
  69. package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
  70. package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
  71. package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
  72. package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
  73. package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
  74. package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
  75. package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
  76. package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
  77. package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
  78. package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
  79. package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
  80. package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
  81. package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
  82. package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
  83. package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
  84. package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
  85. package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
  86. package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
  87. package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
  88. package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
  89. package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
  90. package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
  91. package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
  92. package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
  93. package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
  94. package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
  95. package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
  96. package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
  97. package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
  98. package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
  99. package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
  100. package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
  101. package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
  102. package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
  103. package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
  104. package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
  105. package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
  106. package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
  107. package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
  108. package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
  109. package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
  110. package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
  111. package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
  112. package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
  113. package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
  114. package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
  115. package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
  116. package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
  117. package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
  118. package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
  119. package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
  120. package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
  121. package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
  122. package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
  123. package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
  124. package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
  125. package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
  126. package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
  127. package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
  128. package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
  129. package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
  130. package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
  131. package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
  132. package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
  133. package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
  134. package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
  135. package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
  136. package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
  137. package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
  138. package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
  139. package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
  140. package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
  141. package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
  142. package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
  143. package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
  144. package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
  145. package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
  146. package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
  147. package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
  148. package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
  149. package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
  150. package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
  151. package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
  152. package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
  153. package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
  154. package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
  155. package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
  156. package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
  157. package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
  158. package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
  159. package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
  160. package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
  161. package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
  162. package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
  163. package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
  164. package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
  165. package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
  166. package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
  167. package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
  168. package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
  169. package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
  170. package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
  171. package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
  172. package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
  173. package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
  174. package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
  175. package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
  176. package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
  177. package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
  178. package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
  179. package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
  180. package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
  181. package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
  182. package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
@@ -0,0 +1,181 @@
1
+ # Mock Patterns for FP Codebases
2
+
3
+ ## The FP Alternative to Mocking
4
+
5
+ Before reaching for a mock, ask: **can I extract the pure logic instead?**
6
+
7
+ ### Before (Hard to Test — Needs Mocks)
8
+
9
+ ```javascript
10
+ // Impure: mixed logic + side effects
11
+ function processOrder(orderId) {
12
+ const order = db.getOrder(orderId) // side effect
13
+ const total = order.items.reduce((s, i) => s + i.price * i.qty, 0)
14
+ const tax = total * 0.08
15
+ const discount = total > 100 ? total * 0.1 : 0
16
+ db.updateOrder(orderId, { total, tax, discount }) // side effect
17
+ return { total, tax, discount }
18
+ }
19
+ ```
20
+
21
+ ### After (Easy to Test — No Mocks Needed)
22
+
23
+ ```javascript
24
+ // Pure: extract the logic
25
+ const calculateOrderTotals = (items, taxRate = 0.08, discountThreshold = 100) => {
26
+ const total = items.reduce((s, i) => s + i.price * i.qty, 0)
27
+ const tax = total * taxRate
28
+ const discount = total > discountThreshold ? total * 0.1 : 0
29
+ return { total, tax, discount }
30
+ }
31
+
32
+ // Impure shell: thin wrapper
33
+ function processOrder(orderId) {
34
+ const order = db.getOrder(orderId)
35
+ const totals = calculateOrderTotals(order.items)
36
+ db.updateOrder(orderId, totals)
37
+ return totals
38
+ }
39
+ ```
40
+
41
+ Now `calculateOrderTotals` is tested with zero mocks. The impure shell is thin enough to verify by inspection or a single integration test.
42
+
43
+ ## When Mocking IS Appropriate
44
+
45
+ Mock when the dependency is:
46
+
47
+ | Dependency | Mock? | Why |
48
+ |-----------|-------|-----|
49
+ | External API (Stripe, Twilio) | Yes | Unreliable, slow, costs money |
50
+ | Database | Yes (for unit tests) | Slow, requires setup/teardown |
51
+ | Filesystem | Yes | Non-deterministic, platform-specific |
52
+ | Time/Date | Yes | Non-deterministic |
53
+ | Random/UUID | Yes | Non-deterministic |
54
+ | Your own pure functions | **No** | Use the real thing |
55
+ | Your own classes | **Rarely** | Extract the logic instead |
56
+
57
+ ## Mock Types: When to Use Each
58
+
59
+ ### Stub (Default Choice)
60
+
61
+ Returns canned data. Use for most cases.
62
+
63
+ ```javascript
64
+ // JS: Simple stub
65
+ const getUser = vi.fn().mockReturnValue({ id: 1, name: 'Alice' })
66
+
67
+ // PHP: PHPUnit stub
68
+ $repo = $this->createStub(UserRepository::class);
69
+ $repo->method('find')->willReturn(new User(1, 'Alice'));
70
+ ```
71
+
72
+ ### Spy (Verify Calls)
73
+
74
+ Records calls for later assertion. Use when you need to verify a side effect happened.
75
+
76
+ ```javascript
77
+ // JS: Spy on a method
78
+ const sendEmail = vi.fn()
79
+ processSignup(user, { sendEmail })
80
+ expect(sendEmail).toHaveBeenCalledWith(user.email, expect.any(String))
81
+
82
+ // PHP: PHPUnit expects
83
+ $mailer = $this->createMock(Mailer::class);
84
+ $mailer->expects($this->once())
85
+ ->method('send')
86
+ ->with($this->equalTo('alice@example.com'));
87
+ ```
88
+
89
+ ### Fake (Simplified Implementation)
90
+
91
+ A working but simplified version. Use for complex interfaces.
92
+
93
+ ```javascript
94
+ // JS: In-memory store as a fake
95
+ const createFakeStore = () => {
96
+ const data = new Map()
97
+ return {
98
+ get: (key) => data.get(key),
99
+ set: (key, value) => data.set(key, value),
100
+ delete: (key) => data.delete(key),
101
+ }
102
+ }
103
+ ```
104
+
105
+ ### Full Mock (Almost Never)
106
+
107
+ Strict expectations on exact call sequences. Couples tests tightly to implementation.
108
+
109
+ **Use only when:** the exact call sequence IS the behavior (e.g., protocol compliance, ordered operations).
110
+
111
+ ## Language-Specific Tooling
112
+
113
+ ### JavaScript/TypeScript
114
+
115
+ | Tool | Best For |
116
+ |------|----------|
117
+ | `vi.fn()` / `jest.fn()` | Stubs and spies |
118
+ | `vi.mock()` / `jest.mock()` | Module-level mocking |
119
+ | `msw` (Mock Service Worker) | HTTP API mocking (integration/E2E) |
120
+ | `@testing-library/*` | Component testing without implementation details |
121
+
122
+ ### PHP
123
+
124
+ | Tool | Best For |
125
+ |------|----------|
126
+ | PHPUnit `createStub()` | Simple return values |
127
+ | PHPUnit `createMock()` | Stubs with expectations |
128
+ | `Brain\Monkey` | WordPress function mocking |
129
+ | `WP_Mock` | WordPress hook/filter mocking |
130
+ | `Mockery` | Complex mocking (use sparingly) |
131
+
132
+ ## Anti-Patterns
133
+
134
+ ### Mocking What You Own
135
+
136
+ ```javascript
137
+ // BAD: Mocking your own utility
138
+ const calculateTax = vi.fn().mockReturnValue(8.0)
139
+ const result = processOrder(items, { calculateTax })
140
+ expect(result.tax).toBe(8.0) // This proves nothing!
141
+ ```
142
+
143
+ You're testing that your test setup works, not that the code works. Use the real function.
144
+
145
+ ### Deep Mock Chains
146
+
147
+ ```javascript
148
+ // BAD: Mock chain hell
149
+ const mockDb = {
150
+ getConnection: vi.fn().mockReturnValue({
151
+ query: vi.fn().mockReturnValue({
152
+ rows: vi.fn().mockReturnValue([{ id: 1 }])
153
+ })
154
+ })
155
+ }
156
+ ```
157
+
158
+ If you need this, the code needs refactoring, not more mocks. Extract the pure logic.
159
+
160
+ ### Mocking to Achieve Coverage
161
+
162
+ ```javascript
163
+ // BAD: Mocking just to hit a coverage number
164
+ const fs = vi.mock('fs')
165
+ fs.readFileSync.mockReturnValue('data')
166
+ // ... test that proves readFileSync was called with the right path
167
+ ```
168
+
169
+ This tests that you called `readFileSync`, not that your logic is correct. Extract the logic that processes the data and test that instead.
170
+
171
+ ### Over-Specified Mocks
172
+
173
+ ```javascript
174
+ // BAD: Testing implementation, not behavior
175
+ expect(mockDb.query).toHaveBeenCalledWith(
176
+ 'SELECT * FROM users WHERE id = ? AND active = ?',
177
+ [1, true]
178
+ )
179
+ ```
180
+
181
+ This breaks if you rename a column or change the query. Test the behavior: "given user ID 1, returns the user's data."
@@ -0,0 +1,177 @@
1
+ # TDD Workflow
2
+
3
+ ## Red-Green-Refactor Mechanics
4
+
5
+ ### Red: Write a Failing Test
6
+
7
+ Write the test FIRST. It must fail for the right reason.
8
+
9
+ ```javascript
10
+ // 1. Start with the desired behavior
11
+ test('filters out inactive users', () => {
12
+ const users = [
13
+ { name: 'Alice', active: true },
14
+ { name: 'Bob', active: false },
15
+ { name: 'Carol', active: true },
16
+ ]
17
+ expect(getActiveUsers(users)).toEqual([
18
+ { name: 'Alice', active: true },
19
+ { name: 'Carol', active: true },
20
+ ])
21
+ })
22
+ // Fails: getActiveUsers is not defined ✓ (correct failure)
23
+ ```
24
+
25
+ **Key:** The test should fail because the feature doesn't exist yet — not because of a typo or setup error.
26
+
27
+ ### Green: Make It Pass (Minimum Code)
28
+
29
+ Write the simplest code that makes the test pass. Fight the urge to generalize.
30
+
31
+ ```javascript
32
+ // 2. Simplest passing implementation
33
+ const getActiveUsers = (users) => users.filter(u => u.active)
34
+ ```
35
+
36
+ **Key:** Don't optimize. Don't handle edge cases you haven't tested. Just make it green.
37
+
38
+ ### Refactor: Clean Up
39
+
40
+ With a passing test as your safety net, improve the code.
41
+
42
+ - Rename for clarity
43
+ - Extract shared logic
44
+ - Remove duplication
45
+ - Improve performance (only if needed)
46
+
47
+ **Key:** Tests stay green throughout refactoring. If they go red, you broke something.
48
+
49
+ ## TDD with Pure Functions
50
+
51
+ Pure functions are TDD's sweet spot. No setup, no teardown, no mocking.
52
+
53
+ ```
54
+ Input → Function → Output
55
+ Test: Input → Function → Assert Output
56
+ ```
57
+
58
+ The cycle is fast:
59
+ 1. Write test with input/output pair (10 seconds)
60
+ 2. Write function (10 seconds)
61
+ 3. Run test (1 second)
62
+ 4. Refactor if needed (30 seconds)
63
+
64
+ ### Table-Driven TDD
65
+
66
+ For pure functions, use parameterized tests to cover multiple cases efficiently:
67
+
68
+ ```javascript
69
+ test.each([
70
+ [0, 'F'],
71
+ [59, 'F'],
72
+ [60, 'D'],
73
+ [70, 'C'],
74
+ [80, 'B'],
75
+ [90, 'A'],
76
+ [100, 'A'],
77
+ ])('score %i gets grade %s', (score, expected) => {
78
+ expect(getGrade(score)).toBe(expected)
79
+ })
80
+ ```
81
+
82
+ Write all cases first (red), then implement until all pass (green).
83
+
84
+ ## TDD for Impure Boundaries
85
+
86
+ When TDD meets side effects, use the **functional core / imperative shell** pattern:
87
+
88
+ ### 1. Define the Interface
89
+
90
+ ```typescript
91
+ // What does the boundary need to provide?
92
+ type UserRepository = {
93
+ findById: (id: string) => Promise<User | null>
94
+ save: (user: User) => Promise<void>
95
+ }
96
+ ```
97
+
98
+ ### 2. TDD the Pure Logic
99
+
100
+ ```javascript
101
+ // Test the decision logic, not the I/O
102
+ test('deactivates user when last login > 90 days ago', () => {
103
+ const user = { lastLogin: daysAgo(91), active: true }
104
+ expect(shouldDeactivate(user)).toBe(true)
105
+ })
106
+
107
+ test('keeps user active when last login < 90 days ago', () => {
108
+ const user = { lastLogin: daysAgo(30), active: true }
109
+ expect(shouldDeactivate(user)).toBe(false)
110
+ })
111
+ ```
112
+
113
+ ### 3. Wire the Shell (Minimal Integration Test)
114
+
115
+ ```javascript
116
+ // One integration test to verify wiring
117
+ test('deactivation job processes inactive users', async () => {
118
+ const fakeRepo = createFakeUserRepo([
119
+ { id: '1', lastLogin: daysAgo(91), active: true },
120
+ ])
121
+ await runDeactivationJob(fakeRepo)
122
+ expect(fakeRepo.findById('1')).resolves.toMatchObject({ active: false })
123
+ })
124
+ ```
125
+
126
+ ## When TDD Slows You Down
127
+
128
+ TDD is a tool, not a dogma. Skip it when:
129
+
130
+ ### Exploratory / Prototype Code
131
+
132
+ You're figuring out what the code should even do. TDD assumes you know the desired behavior.
133
+
134
+ **Instead:** Spike first, extract and test after.
135
+
136
+ ### Trivial Glue Code
137
+
138
+ Code that just wires things together with no logic.
139
+
140
+ ```javascript
141
+ // No TDD needed for this
142
+ app.get('/users', authenticate, userController.list)
143
+ ```
144
+
145
+ ### UI Layout / Styling
146
+
147
+ Visual output is better verified by looking at it, not by asserting CSS properties.
148
+
149
+ **Instead:** Use visual regression tests (screenshot comparison) after the layout stabilizes.
150
+
151
+ ### Rapidly Changing Requirements
152
+
153
+ If the spec changes daily, tests written today are deleted tomorrow.
154
+
155
+ **Instead:** Wait for requirements to stabilize, then add tests.
156
+
157
+ ## TDD Anti-Patterns
158
+
159
+ ### Testing Too Much at Once
160
+
161
+ Write one test, make it pass. Repeat. Don't write 10 failing tests and try to make them all pass simultaneously.
162
+
163
+ ### Gold-Plating in Green Phase
164
+
165
+ The green phase is about making it work, not making it elegant. Save elegance for refactor.
166
+
167
+ ### Skipping Refactor Phase
168
+
169
+ Red-green without refactor accumulates technical debt. The refactor step is where design emerges.
170
+
171
+ ### Testing Private Methods
172
+
173
+ If you feel the need to test a private method, it's a sign that the method should be extracted as a separate pure function.
174
+
175
+ ### Assertion-Free Tests
176
+
177
+ A test that only checks "it doesn't throw" is not a test. Assert on the output.
@@ -0,0 +1,126 @@
1
+ # Test Strategy for FP Codebases
2
+
3
+ ## The Test Pyramid (FP Edition)
4
+
5
+ FP codebases are bottom-heavy by nature. Pure functions are trivially testable — lean into this.
6
+
7
+ ```
8
+ / E2E \ Few: critical user journeys only
9
+ /----------\
10
+ / Integration \ Some: boundary wiring, API contracts
11
+ /----------------\
12
+ / Unit Tests \ Many: pure functions, transformations, validators
13
+ /____________________\
14
+ ```
15
+
16
+ ### Why FP Pyramids Are Bottom-Heavy
17
+
18
+ - Pure functions need zero setup → unit tests are cheap
19
+ - Side effects are isolated at boundaries → integration tests are focused
20
+ - Business logic is testable without infrastructure → fast feedback
21
+
22
+ ## Coverage Strategy
23
+
24
+ **Coverage is a floor, not a ceiling.**
25
+
26
+ ### Setting the Floor
27
+
28
+ - New projects: 80% line coverage as a starting point
29
+ - Legacy projects: measure current coverage, set floor 5% below, ratchet up
30
+ - Never chase 100% — the last 20% costs more than the first 80%
31
+
32
+ ### What to Cover
33
+
34
+ | Priority | What | Why |
35
+ |----------|------|-----|
36
+ | High | Pure business logic | Core value, easy to test |
37
+ | High | Data transformations | Bugs here corrupt everything downstream |
38
+ | High | Validation functions | Security and correctness boundary |
39
+ | Medium | Integration wiring | Verify components connect correctly |
40
+ | Medium | Error handling paths | Prevent silent failures |
41
+ | Low | Glue code / configuration | Low logic density |
42
+ | Skip | Framework boilerplate | Trust the framework |
43
+
44
+ ### What NOT to Cover
45
+
46
+ - Trivial getters/setters with no logic
47
+ - Framework-generated code (migrations, stubs)
48
+ - Configuration files
49
+ - Type definitions (TypeScript interfaces, PHP type hints)
50
+
51
+ ## When to Write Each Test Type
52
+
53
+ ### Unit Tests
54
+
55
+ Write when:
56
+ - Function takes input, returns output (pure)
57
+ - Logic has branches (if/else, switch, pattern matching)
58
+ - Data transformation or validation
59
+ - Algorithm implementation
60
+
61
+ Skip when:
62
+ - Code is pure glue (just wires things together)
63
+ - Logic is trivially obvious (`return this.name`)
64
+
65
+ ### Integration Tests
66
+
67
+ Write when:
68
+ - Verifying database queries return correct data
69
+ - API endpoint contracts (request → response shape)
70
+ - Multiple components must work together
71
+ - Third-party service integration points
72
+
73
+ Skip when:
74
+ - You can test the logic as a pure function instead
75
+ - The integration is trivial and well-typed
76
+
77
+ ### E2E Tests
78
+
79
+ Write when:
80
+ - Critical user journeys (login, checkout, signup)
81
+ - Complex multi-step workflows
82
+ - Regression tests for bugs that slipped through unit/integration
83
+
84
+ Skip when:
85
+ - The behavior is fully covered by unit + integration tests
86
+ - The UI is rapidly changing (tests will be constantly rewritten)
87
+ - The test would be flaky by nature (animations, timing)
88
+
89
+ ## Retrofitting Tests onto Legacy Code
90
+
91
+ Legacy code without tests is the norm. Don't try to add 100% coverage at once.
92
+
93
+ ### The Characterization Test Approach
94
+
95
+ 1. **Identify the change point** — what code are you about to modify?
96
+ 2. **Write characterization tests** — tests that document current behavior (even if buggy)
97
+ 3. **Extract pure logic** — pull testable functions out of the tangled code
98
+ 4. **Test the extracted logic** — proper unit tests for the new pure functions
99
+ 5. **Make your change** — now you have a safety net
100
+ 6. **Update tests** — adjust characterization tests if behavior intentionally changed
101
+
102
+ ### Priority Order for Legacy Code
103
+
104
+ 1. Code you're about to change (safety net)
105
+ 2. Code with known bugs (prevent regression)
106
+ 3. Business-critical paths (high impact if broken)
107
+ 4. Frequently modified code (high churn = high risk)
108
+ 5. Everything else (only if you have time)
109
+
110
+ ### The Strangler Pattern for Tests
111
+
112
+ Don't rewrite all tests at once. Instead:
113
+ - Every time you touch legacy code, add tests for what you changed
114
+ - Over time, coverage grows organically where it matters most
115
+ - Old untested code stays untested until it needs to change
116
+
117
+ ## When NOT to Test
118
+
119
+ Testing has diminishing returns. Don't test:
120
+
121
+ - **Prototype/spike code** — it's throwaway by definition
122
+ - **One-off scripts** — run it, verify manually, done
123
+ - **Configuration** — JSON/YAML files don't need unit tests
124
+ - **Type system guarantees** — if TypeScript/PHP types prevent it, don't test it
125
+ - **Third-party library internals** — test your usage, not their code
126
+ - **Obvious glue code** — `app.use(cors())` doesn't need a test