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,424 @@
1
+ # Authorize.Net Sandbox & Testing Guide
2
+
3
+ Testing patterns, sandbox configuration, test card numbers, and PHPUnit strategies for Authorize.Net PHP SDK.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Sandbox Setup](#sandbox-setup)
8
+ - [Test Card Numbers](#test-card-numbers)
9
+ - [Sandbox Gotchas](#sandbox-gotchas)
10
+ - [PHPUnit Testing Patterns](#phpunit-testing-patterns)
11
+ - [Test Data Reference](#test-data-reference)
12
+
13
+ ---
14
+
15
+ ## Sandbox Setup
16
+
17
+ ### Sandbox Credentials
18
+
19
+ 1. Create sandbox account at https://developer.authorize.net/hello_world/sandbox.html
20
+ 2. Get API Login ID + Transaction Key from Account → Settings → API Credentials & Keys
21
+ 3. Get Public Client Key for Accept.js from Account → Settings → Manage Public Client Key
22
+ 4. Get Signature Key for webhooks from Account → Settings → API Credentials & Keys → New Signature Key
23
+
24
+ ### Environment URLs
25
+
26
+ ```php
27
+ use net\authorize\api\constants\ANetEnvironment;
28
+
29
+ // Sandbox
30
+ $api_url = ANetEnvironment::SANDBOX;
31
+ // https://apitest.authorize.net/xml/v1/request.api
32
+
33
+ // Production
34
+ $api_url = ANetEnvironment::PRODUCTION;
35
+ // https://api.authorize.net/xml/v1/request.api
36
+ ```
37
+
38
+ ### Accept.js Script URLs
39
+
40
+ ```html
41
+ <!-- Sandbox -->
42
+ <script src="https://jstest.authorize.net/v1/Accept.js"></script>
43
+
44
+ <!-- Production -->
45
+ <script src="https://js.authorize.net/v1/Accept.js"></script>
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Test Card Numbers
51
+
52
+ ### Credit Cards
53
+
54
+ | Card Type | Number | CVV | Result |
55
+ |-----------|--------|-----|--------|
56
+ | Visa | `4111111111111111` | Any 3-digit | Approved |
57
+ | Visa | `4222222222222` | Any 3-digit | Approved |
58
+ | Mastercard | `5424000000000015` | Any 3-digit | Approved |
59
+ | Amex | `370000000000002` | Any 4-digit | Approved |
60
+ | Discover | `6011000000000012` | Any 3-digit | Approved |
61
+ | JCB | `3088000000000017` | Any 3-digit | Approved |
62
+ | Diners Club | `38000000000006` | Any 3-digit | Approved |
63
+
64
+ ### Triggering Declines
65
+
66
+ Use specific amounts to trigger decline responses:
67
+
68
+ | Amount ending in | Response |
69
+ |-----------------|----------|
70
+ | `.00` | Approved (default) |
71
+ | `.01` | Approved |
72
+ | `.02` | Declined |
73
+ | `.03` | Error |
74
+ | `.04` | Held for review |
75
+
76
+ Example: `$50.02` triggers a decline.
77
+
78
+ ### Expiration Dates
79
+
80
+ Any future date works in sandbox. Use `12/2030` or similar.
81
+
82
+ ### eCheck (ACH)
83
+
84
+ | Field | Test Value |
85
+ |-------|-----------|
86
+ | Routing Number | `121042882` |
87
+ | Account Number | Any 6-17 digit number |
88
+ | Account Type | `checking` or `savings` |
89
+ | Name on Account | Any name |
90
+ | eCheck Type | `WEB` (internet) or `PPD` (prearranged) |
91
+
92
+ ---
93
+
94
+ ## Sandbox Gotchas
95
+
96
+ ### CIM → ARB Propagation Delay
97
+
98
+ **Problem**: Creating a CIM profile then immediately creating an ARB subscription with that profile fails with "The record cannot be found."
99
+
100
+ **Cause**: Sandbox takes ~15 seconds to propagate CIM profiles to the ARB subsystem.
101
+
102
+ **Solution**: Retry with delay (sandbox only):
103
+
104
+ ```php
105
+ function create_subscription_with_retry(
106
+ string $customer_profile_id,
107
+ string $payment_profile_id,
108
+ array $plan,
109
+ array $credentials,
110
+ string $environment
111
+ ): array {
112
+ $max_attempts = ($environment === 'sandbox') ? 3 : 1;
113
+
114
+ for ($attempt = 1; $attempt <= $max_attempts; $attempt++) {
115
+ $result = create_subscription_via_sdk(
116
+ $customer_profile_id, $payment_profile_id, $plan, $credentials
117
+ );
118
+
119
+ if ($result['success']) return $result;
120
+ if ($attempt === $max_attempts) return $result;
121
+ if (!str_contains($result['error'] ?? '', 'record cannot be found')) return $result;
122
+
123
+ sleep(15); // wait for propagation
124
+ }
125
+
126
+ return ['success' => false, 'error' => 'Failed after retries'];
127
+ }
128
+ ```
129
+
130
+ ### Validation Mode with Accept.js Tokens
131
+
132
+ **Problem**: `liveMode` validation fails with Accept.js opaque data in sandbox.
133
+
134
+ **Solution**: Use `testMode` in sandbox, `liveMode` in production:
135
+
136
+ ```php
137
+ $validation_mode = ($environment === 'sandbox') ? 'testMode' : 'liveMode';
138
+ ```
139
+
140
+ ### Settlement Timing
141
+
142
+ Sandbox settles transactions daily at a specific time (not real-time). This affects:
143
+ - Refunds: Can only refund settled transactions
144
+ - Voids: Can only void unsettled transactions
145
+ - You cannot test both refund and void on the same transaction in the same test run
146
+
147
+ ### Webhook Delivery in Sandbox
148
+
149
+ - Webhooks work in sandbox but may have higher latency
150
+ - Webhook URL must be publicly accessible (use ngrok or similar for local dev)
151
+ - Sandbox webhook signature key is different from production
152
+
153
+ ---
154
+
155
+ ## PHPUnit Testing Patterns
156
+
157
+ ### Testing the Three-Layer Architecture
158
+
159
+ The FP architecture makes testing straightforward — pure functions need no mocks.
160
+
161
+ #### Testing Pure Builders (No Mocks Needed)
162
+
163
+ ```php
164
+ <?php declare(strict_types=1);
165
+
166
+ use PHPUnit\Framework\TestCase;
167
+
168
+ class RequestBuilderTest extends TestCase
169
+ {
170
+ public function test_build_charge_request_normalizes_billing(): void
171
+ {
172
+ $result = build_charge_request([
173
+ 'amount' => 29.99,
174
+ 'payment_token' => 'token123',
175
+ 'billing' => ['first_name' => 'Jane', 'last_name' => 'Doe'],
176
+ ]);
177
+
178
+ $this->assertSame(29.99, $result['amount']);
179
+ $this->assertSame('token123', $result['payment_token']);
180
+ $this->assertSame('Jane', $result['billing']['first_name']);
181
+ }
182
+
183
+ public function test_build_charge_request_handles_empty_billing(): void
184
+ {
185
+ $result = build_charge_request([
186
+ 'amount' => 10.00,
187
+ 'payment_token' => 'token456',
188
+ 'billing' => [],
189
+ ]);
190
+
191
+ $this->assertNull($result['billing']);
192
+ }
193
+
194
+ public function test_build_subscription_request_truncates_name(): void
195
+ {
196
+ $long_name = str_repeat('A', 60);
197
+ $result = build_subscription_request('cp1', 'pp1', [
198
+ 'amount' => 9.99,
199
+ 'name' => $long_name,
200
+ ], '2025-01-01');
201
+
202
+ $this->assertSame(50, strlen($result['name']));
203
+ }
204
+ }
205
+ ```
206
+
207
+ #### Testing Pure Parsers (No Mocks Needed)
208
+
209
+ ```php
210
+ class ResponseParserTest extends TestCase
211
+ {
212
+ public function test_parse_charge_success(): void
213
+ {
214
+ $result = parse_charge_response([
215
+ 'result_code' => 'Ok',
216
+ 'transaction_id' => 'TX123',
217
+ 'has_transaction_messages' => true,
218
+ 'auth_code' => 'AUTH1',
219
+ 'message' => 'Approved',
220
+ 'response_code' => '1',
221
+ 'account_number' => 'XXXX1234',
222
+ 'error_text' => null,
223
+ 'error_code' => null,
224
+ ]);
225
+
226
+ $this->assertTrue($result['success']);
227
+ $this->assertSame('TX123', $result['transaction_id']);
228
+ }
229
+
230
+ public function test_parse_charge_null_response(): void
231
+ {
232
+ $result = parse_charge_response([
233
+ 'result_code' => null,
234
+ 'transaction_id' => null,
235
+ 'has_transaction_messages' => false,
236
+ 'error_text' => null,
237
+ 'error_code' => null,
238
+ ]);
239
+
240
+ $this->assertFalse($result['success']);
241
+ $this->assertSame('No response from payment gateway', $result['error']);
242
+ }
243
+
244
+ public function test_parse_create_profile_duplicate_recovery(): void
245
+ {
246
+ $result = parse_create_profile_response([
247
+ 'result_code' => 'Error',
248
+ 'customer_profile_id' => null,
249
+ 'payment_profile_id' => null,
250
+ 'error_code' => 'E00039',
251
+ 'error_text' => 'A duplicate record with ID 12345 already exists.',
252
+ ]);
253
+
254
+ $this->assertFalse($result['success']);
255
+ $this->assertSame('E00039', $result['error_code']);
256
+ $this->assertSame('12345', $result['existing_profile_id']);
257
+ }
258
+ }
259
+ ```
260
+
261
+ #### Testing Pure Webhook Functions (No Mocks Needed)
262
+
263
+ ```php
264
+ class WebhookPureFunctionsTest extends TestCase
265
+ {
266
+ public function test_validate_signature_valid(): void
267
+ {
268
+ $body = '{"eventType":"net.authorize.payment.authcapture.created"}';
269
+ $key = 'test-signature-key';
270
+ $hash = strtoupper(hash_hmac('sha512', $body, $key));
271
+
272
+ $this->assertTrue(
273
+ validate_webhook_signature($body, "sha512={$hash}", $key)
274
+ );
275
+ }
276
+
277
+ public function test_validate_signature_invalid(): void
278
+ {
279
+ $this->assertFalse(
280
+ validate_webhook_signature('body', 'sha512=WRONG', 'key')
281
+ );
282
+ }
283
+
284
+ /**
285
+ * @dataProvider eventCategoryProvider
286
+ */
287
+ public function test_get_event_category(string $event, string|false $expected): void
288
+ {
289
+ $this->assertSame($expected, get_event_category($event));
290
+ }
291
+
292
+ public static function eventCategoryProvider(): array
293
+ {
294
+ return [
295
+ ['net.authorize.payment.authcapture.created', 'payment'],
296
+ ['net.authorize.customer.subscription.created', 'subscription'],
297
+ ['net.authorize.payment.fraud.held', 'fraud'],
298
+ ['net.authorize.unknown.something', false],
299
+ ];
300
+ }
301
+ }
302
+ ```
303
+
304
+ #### Testing Validation Functions (No Mocks Needed)
305
+
306
+ ```php
307
+ class ValidationTest extends TestCase
308
+ {
309
+ /**
310
+ * @dataProvider amountProvider
311
+ */
312
+ public function test_validate_amount(mixed $amount, bool $expected_valid): void
313
+ {
314
+ $result = validate_amount($amount);
315
+ $this->assertSame($expected_valid, $result['valid']);
316
+ }
317
+
318
+ public static function amountProvider(): array
319
+ {
320
+ return [
321
+ 'valid' => [29.99, true],
322
+ 'minimum' => [1.00, true],
323
+ 'below' => [0.50, false],
324
+ 'zero' => [0, false],
325
+ 'negative' => [-10, false],
326
+ 'string' => ['abc', false],
327
+ 'max' => [25000.00, true],
328
+ 'over_max' => [25001.00, false],
329
+ ];
330
+ }
331
+
332
+ public function test_validate_payment_token_too_short(): void
333
+ {
334
+ $result = validate_payment_token('short');
335
+ $this->assertFalse($result['valid']);
336
+ $this->assertSame('Payment token format is invalid', $result['message']);
337
+ }
338
+ }
339
+ ```
340
+
341
+ ### Testing SDK Adapters (Integration Tests)
342
+
343
+ SDK adapter functions require the actual SDK. Test these as integration tests against the sandbox:
344
+
345
+ ```php
346
+ /**
347
+ * @group integration
348
+ * @group requires-sandbox
349
+ */
350
+ class SdkAdapterIntegrationTest extends TestCase
351
+ {
352
+ private array $credentials;
353
+
354
+ protected function setUp(): void
355
+ {
356
+ $this->credentials = [
357
+ 'api_login_id' => getenv('AUTHNET_API_LOGIN_ID'),
358
+ 'transaction_key' => getenv('AUTHNET_TRANSACTION_KEY'),
359
+ 'api_url' => ANetEnvironment::SANDBOX,
360
+ ];
361
+
362
+ if (empty($this->credentials['api_login_id'])) {
363
+ $this->markTestSkipped('Sandbox credentials not configured');
364
+ }
365
+ }
366
+
367
+ public function test_connection(): void
368
+ {
369
+ $result = sdk_test_connection($this->credentials);
370
+ $this->assertSame('Ok', $result['result_code']);
371
+ }
372
+ }
373
+ ```
374
+
375
+ Run integration tests separately:
376
+
377
+ ```bash
378
+ # Unit tests only (fast, no network)
379
+ phpunit --exclude-group=integration
380
+
381
+ # Integration tests (requires sandbox credentials)
382
+ AUTHNET_API_LOGIN_ID=xxx AUTHNET_TRANSACTION_KEY=yyy phpunit --group=integration
383
+ ```
384
+
385
+ ---
386
+
387
+ ## Test Data Reference
388
+
389
+ ### Billing Address (Test)
390
+
391
+ ```php
392
+ $test_billing = [
393
+ 'first_name' => 'Test',
394
+ 'last_name' => 'User',
395
+ 'email' => 'test@example.com',
396
+ 'company' => 'Test Company',
397
+ 'address' => '123 Test St',
398
+ 'city' => 'Testville',
399
+ 'state' => 'CA',
400
+ 'zip' => '90210',
401
+ 'country' => 'US',
402
+ ];
403
+ ```
404
+
405
+ ### Subscription Plan (Test)
406
+
407
+ ```php
408
+ $test_plan = [
409
+ 'amount' => 9.99,
410
+ 'name' => 'Test Monthly',
411
+ 'interval_length' => 1,
412
+ 'interval_unit' => 'months',
413
+ 'total_occurrences' => 12,
414
+ 'start_date' => date('Y-m-d', strtotime('+1 day')),
415
+ ];
416
+ ```
417
+
418
+ ### Accept.js Token (Test - Sandbox Only)
419
+
420
+ In sandbox, you can generate test tokens using the Accept.js test page or by calling Accept.js with test card data. For unit tests that need a token format but won't hit the API, use a placeholder:
421
+
422
+ ```php
423
+ $test_token = str_repeat('A', 100); // meets length validation
424
+ ```