@funnycode/myclaude 0.1.55 → 0.1.60

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 (499) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +63 -0
  3. package/README.zh-CN.md +63 -0
  4. package/dist/SKILL-r7zmg5v7.md +3 -0
  5. package/dist/cli-mvr5k580.md +3 -0
  6. package/dist/myclaude.js +14230 -8799
  7. package/dist/myclaude.mjs +14230 -8799
  8. package/dist/server-zyhc2a9z.md +3 -0
  9. package/package.json +128 -128
  10. package/seed/marketplaces/ecc/.opencode/package-lock.json +169 -169
  11. package/seed/marketplaces/ecc/commands/aside.md +164 -164
  12. package/seed/marketplaces/ecc/commands/auto-update.md +28 -28
  13. package/seed/marketplaces/ecc/commands/build-fix.md +66 -66
  14. package/seed/marketplaces/ecc/commands/checkpoint.md +78 -78
  15. package/seed/marketplaces/ecc/commands/code-review.md +289 -289
  16. package/seed/marketplaces/ecc/commands/cost-report.md +107 -107
  17. package/seed/marketplaces/ecc/commands/cpp-build.md +173 -173
  18. package/seed/marketplaces/ecc/commands/cpp-review.md +132 -132
  19. package/seed/marketplaces/ecc/commands/cpp-test.md +251 -251
  20. package/seed/marketplaces/ecc/commands/ecc-guide.md +93 -93
  21. package/seed/marketplaces/ecc/commands/evolve.md +178 -178
  22. package/seed/marketplaces/ecc/commands/fastapi-review.md +39 -39
  23. package/seed/marketplaces/ecc/commands/feature-dev.md +49 -49
  24. package/seed/marketplaces/ecc/commands/flutter-build.md +164 -164
  25. package/seed/marketplaces/ecc/commands/flutter-review.md +116 -116
  26. package/seed/marketplaces/ecc/commands/flutter-test.md +144 -144
  27. package/seed/marketplaces/ecc/commands/gan-build.md +103 -103
  28. package/seed/marketplaces/ecc/commands/gan-design.md +39 -39
  29. package/seed/marketplaces/ecc/commands/go-build.md +183 -183
  30. package/seed/marketplaces/ecc/commands/go-review.md +148 -148
  31. package/seed/marketplaces/ecc/commands/go-test.md +268 -268
  32. package/seed/marketplaces/ecc/commands/gradle-build.md +70 -70
  33. package/seed/marketplaces/ecc/commands/harness-audit.md +84 -84
  34. package/seed/marketplaces/ecc/commands/hookify-configure.md +14 -14
  35. package/seed/marketplaces/ecc/commands/hookify-help.md +46 -46
  36. package/seed/marketplaces/ecc/commands/hookify-list.md +21 -21
  37. package/seed/marketplaces/ecc/commands/hookify.md +50 -50
  38. package/seed/marketplaces/ecc/commands/instinct-export.md +66 -66
  39. package/seed/marketplaces/ecc/commands/instinct-import.md +114 -114
  40. package/seed/marketplaces/ecc/commands/instinct-status.md +59 -59
  41. package/seed/marketplaces/ecc/commands/jira.md +106 -106
  42. package/seed/marketplaces/ecc/commands/kotlin-build.md +174 -174
  43. package/seed/marketplaces/ecc/commands/kotlin-review.md +140 -140
  44. package/seed/marketplaces/ecc/commands/kotlin-test.md +312 -312
  45. package/seed/marketplaces/ecc/commands/learn-eval.md +116 -116
  46. package/seed/marketplaces/ecc/commands/learn.md +74 -74
  47. package/seed/marketplaces/ecc/commands/loop-start.md +36 -36
  48. package/seed/marketplaces/ecc/commands/loop-status.md +77 -77
  49. package/seed/marketplaces/ecc/commands/marketing-campaign.md +129 -129
  50. package/seed/marketplaces/ecc/commands/model-route.md +30 -30
  51. package/seed/marketplaces/ecc/commands/multi-backend.md +162 -162
  52. package/seed/marketplaces/ecc/commands/multi-execute.md +319 -319
  53. package/seed/marketplaces/ecc/commands/multi-frontend.md +162 -162
  54. package/seed/marketplaces/ecc/commands/multi-plan.md +272 -272
  55. package/seed/marketplaces/ecc/commands/multi-workflow.md +195 -195
  56. package/seed/marketplaces/ecc/commands/plan-prd.md +160 -160
  57. package/seed/marketplaces/ecc/commands/plan.md +200 -200
  58. package/seed/marketplaces/ecc/commands/pm2.md +276 -276
  59. package/seed/marketplaces/ecc/commands/pr.md +184 -184
  60. package/seed/marketplaces/ecc/commands/project-init.md +86 -86
  61. package/seed/marketplaces/ecc/commands/projects.md +39 -39
  62. package/seed/marketplaces/ecc/commands/promote.md +41 -41
  63. package/seed/marketplaces/ecc/commands/prp-commit.md +112 -112
  64. package/seed/marketplaces/ecc/commands/prp-implement.md +385 -385
  65. package/seed/marketplaces/ecc/commands/prp-plan.md +502 -502
  66. package/seed/marketplaces/ecc/commands/prp-pr.md +184 -184
  67. package/seed/marketplaces/ecc/commands/prp-prd.md +447 -447
  68. package/seed/marketplaces/ecc/commands/prune.md +31 -31
  69. package/seed/marketplaces/ecc/commands/python-review.md +297 -297
  70. package/seed/marketplaces/ecc/commands/quality-gate.md +33 -33
  71. package/seed/marketplaces/ecc/commands/refactor-clean.md +84 -84
  72. package/seed/marketplaces/ecc/commands/resume-session.md +156 -156
  73. package/seed/marketplaces/ecc/commands/review-pr.md +37 -37
  74. package/seed/marketplaces/ecc/commands/rust-build.md +187 -187
  75. package/seed/marketplaces/ecc/commands/rust-review.md +142 -142
  76. package/seed/marketplaces/ecc/commands/rust-test.md +308 -308
  77. package/seed/marketplaces/ecc/commands/santa-loop.md +175 -175
  78. package/seed/marketplaces/ecc/commands/save-session.md +275 -275
  79. package/seed/marketplaces/ecc/commands/security-scan.md +92 -92
  80. package/seed/marketplaces/ecc/commands/sessions.md +339 -339
  81. package/seed/marketplaces/ecc/commands/setup-pm.md +80 -80
  82. package/seed/marketplaces/ecc/commands/skill-create.md +174 -174
  83. package/seed/marketplaces/ecc/commands/skill-health.md +54 -54
  84. package/seed/marketplaces/ecc/commands/test-coverage.md +73 -73
  85. package/seed/marketplaces/ecc/commands/update-codemaps.md +76 -76
  86. package/seed/marketplaces/ecc/commands/update-docs.md +88 -88
  87. package/seed/marketplaces/ecc/skills/accessibility/SKILL.md +146 -146
  88. package/seed/marketplaces/ecc/skills/agent-architecture-audit/SKILL.md +256 -256
  89. package/seed/marketplaces/ecc/skills/agent-eval/SKILL.md +145 -145
  90. package/seed/marketplaces/ecc/skills/agent-harness-construction/SKILL.md +73 -73
  91. package/seed/marketplaces/ecc/skills/agent-introspection-debugging/SKILL.md +153 -153
  92. package/seed/marketplaces/ecc/skills/agent-payment-x402/SKILL.md +224 -224
  93. package/seed/marketplaces/ecc/skills/agent-sort/SKILL.md +215 -215
  94. package/seed/marketplaces/ecc/skills/agentic-engineering/SKILL.md +63 -63
  95. package/seed/marketplaces/ecc/skills/agentic-os/SKILL.md +387 -387
  96. package/seed/marketplaces/ecc/skills/ai-first-engineering/SKILL.md +51 -51
  97. package/seed/marketplaces/ecc/skills/ai-regression-testing/SKILL.md +385 -385
  98. package/seed/marketplaces/ecc/skills/android-clean-architecture/SKILL.md +339 -339
  99. package/seed/marketplaces/ecc/skills/angular-developer/SKILL.md +154 -154
  100. package/seed/marketplaces/ecc/skills/angular-developer/references/angular-animations.md +160 -160
  101. package/seed/marketplaces/ecc/skills/angular-developer/references/angular-aria.md +410 -410
  102. package/seed/marketplaces/ecc/skills/angular-developer/references/cli.md +86 -86
  103. package/seed/marketplaces/ecc/skills/angular-developer/references/component-harnesses.md +59 -59
  104. package/seed/marketplaces/ecc/skills/angular-developer/references/component-styling.md +91 -91
  105. package/seed/marketplaces/ecc/skills/angular-developer/references/components.md +117 -117
  106. package/seed/marketplaces/ecc/skills/angular-developer/references/creating-services.md +97 -97
  107. package/seed/marketplaces/ecc/skills/angular-developer/references/data-resolvers.md +69 -69
  108. package/seed/marketplaces/ecc/skills/angular-developer/references/define-routes.md +67 -67
  109. package/seed/marketplaces/ecc/skills/angular-developer/references/defining-providers.md +72 -72
  110. package/seed/marketplaces/ecc/skills/angular-developer/references/di-fundamentals.md +120 -120
  111. package/seed/marketplaces/ecc/skills/angular-developer/references/e2e-testing.md +56 -56
  112. package/seed/marketplaces/ecc/skills/angular-developer/references/effects.md +83 -83
  113. package/seed/marketplaces/ecc/skills/angular-developer/references/hierarchical-injectors.md +43 -43
  114. package/seed/marketplaces/ecc/skills/angular-developer/references/host-elements.md +80 -80
  115. package/seed/marketplaces/ecc/skills/angular-developer/references/injection-context.md +63 -63
  116. package/seed/marketplaces/ecc/skills/angular-developer/references/inputs.md +101 -101
  117. package/seed/marketplaces/ecc/skills/angular-developer/references/linked-signal.md +59 -59
  118. package/seed/marketplaces/ecc/skills/angular-developer/references/loading-strategies.md +61 -61
  119. package/seed/marketplaces/ecc/skills/angular-developer/references/mcp.md +108 -108
  120. package/seed/marketplaces/ecc/skills/angular-developer/references/navigate-to-routes.md +69 -69
  121. package/seed/marketplaces/ecc/skills/angular-developer/references/outputs.md +86 -86
  122. package/seed/marketplaces/ecc/skills/angular-developer/references/reactive-forms.md +122 -122
  123. package/seed/marketplaces/ecc/skills/angular-developer/references/rendering-strategies.md +44 -44
  124. package/seed/marketplaces/ecc/skills/angular-developer/references/resource.md +77 -77
  125. package/seed/marketplaces/ecc/skills/angular-developer/references/route-animations.md +56 -56
  126. package/seed/marketplaces/ecc/skills/angular-developer/references/route-guards.md +52 -52
  127. package/seed/marketplaces/ecc/skills/angular-developer/references/router-lifecycle.md +45 -45
  128. package/seed/marketplaces/ecc/skills/angular-developer/references/router-testing.md +87 -87
  129. package/seed/marketplaces/ecc/skills/angular-developer/references/show-routes-with-outlets.md +68 -68
  130. package/seed/marketplaces/ecc/skills/angular-developer/references/signal-forms.md +795 -795
  131. package/seed/marketplaces/ecc/skills/angular-developer/references/signals-overview.md +94 -94
  132. package/seed/marketplaces/ecc/skills/angular-developer/references/tailwind-css.md +69 -69
  133. package/seed/marketplaces/ecc/skills/angular-developer/references/template-driven-forms.md +114 -114
  134. package/seed/marketplaces/ecc/skills/angular-developer/references/testing-fundamentals.md +65 -65
  135. package/seed/marketplaces/ecc/skills/api-connector-builder/SKILL.md +120 -120
  136. package/seed/marketplaces/ecc/skills/api-design/SKILL.md +523 -523
  137. package/seed/marketplaces/ecc/skills/architecture-decision-records/SKILL.md +179 -179
  138. package/seed/marketplaces/ecc/skills/article-writing/SKILL.md +79 -79
  139. package/seed/marketplaces/ecc/skills/automation-audit-ops/SKILL.md +142 -142
  140. package/seed/marketplaces/ecc/skills/autonomous-agent-harness/SKILL.md +273 -273
  141. package/seed/marketplaces/ecc/skills/autonomous-loops/SKILL.md +610 -610
  142. package/seed/marketplaces/ecc/skills/backend-patterns/SKILL.md +561 -561
  143. package/seed/marketplaces/ecc/skills/benchmark/SKILL.md +93 -93
  144. package/seed/marketplaces/ecc/skills/benchmark-optimization-loop/SKILL.md +69 -69
  145. package/seed/marketplaces/ecc/skills/blender-motion-state-inspection/SKILL.md +164 -164
  146. package/seed/marketplaces/ecc/skills/blueprint/SKILL.md +105 -105
  147. package/seed/marketplaces/ecc/skills/brand-voice/SKILL.md +97 -97
  148. package/seed/marketplaces/ecc/skills/brand-voice/references/voice-profile-schema.md +55 -55
  149. package/seed/marketplaces/ecc/skills/browser-qa/SKILL.md +87 -87
  150. package/seed/marketplaces/ecc/skills/bun-runtime/SKILL.md +84 -84
  151. package/seed/marketplaces/ecc/skills/canary-watch/SKILL.md +107 -107
  152. package/seed/marketplaces/ecc/skills/carrier-relationship-management/SKILL.md +212 -212
  153. package/seed/marketplaces/ecc/skills/cisco-ios-patterns/SKILL.md +163 -163
  154. package/seed/marketplaces/ecc/skills/ck/SKILL.md +147 -147
  155. package/seed/marketplaces/ecc/skills/ck/commands/forget.mjs +44 -44
  156. package/seed/marketplaces/ecc/skills/ck/commands/info.mjs +24 -24
  157. package/seed/marketplaces/ecc/skills/ck/commands/init.mjs +143 -143
  158. package/seed/marketplaces/ecc/skills/ck/commands/list.mjs +40 -40
  159. package/seed/marketplaces/ecc/skills/ck/commands/migrate.mjs +202 -202
  160. package/seed/marketplaces/ecc/skills/ck/commands/resume.mjs +36 -36
  161. package/seed/marketplaces/ecc/skills/ck/commands/save.mjs +210 -210
  162. package/seed/marketplaces/ecc/skills/ck/commands/shared.mjs +387 -387
  163. package/seed/marketplaces/ecc/skills/ck/hooks/session-start.mjs +224 -224
  164. package/seed/marketplaces/ecc/skills/claude-devfleet/SKILL.md +103 -103
  165. package/seed/marketplaces/ecc/skills/click-path-audit/SKILL.md +244 -244
  166. package/seed/marketplaces/ecc/skills/clickhouse-io/SKILL.md +439 -439
  167. package/seed/marketplaces/ecc/skills/code-tour/SKILL.md +236 -236
  168. package/seed/marketplaces/ecc/skills/codebase-onboarding/SKILL.md +233 -233
  169. package/seed/marketplaces/ecc/skills/coding-standards/SKILL.md +549 -549
  170. package/seed/marketplaces/ecc/skills/compose-multiplatform-patterns/SKILL.md +299 -299
  171. package/seed/marketplaces/ecc/skills/configure-ecc/SKILL.md +384 -384
  172. package/seed/marketplaces/ecc/skills/connections-optimizer/SKILL.md +189 -189
  173. package/seed/marketplaces/ecc/skills/content-engine/SKILL.md +131 -131
  174. package/seed/marketplaces/ecc/skills/content-hash-cache-pattern/SKILL.md +161 -161
  175. package/seed/marketplaces/ecc/skills/context-budget/SKILL.md +135 -135
  176. package/seed/marketplaces/ecc/skills/continuous-agent-loop/SKILL.md +45 -45
  177. package/seed/marketplaces/ecc/skills/continuous-learning/SKILL.md +131 -131
  178. package/seed/marketplaces/ecc/skills/continuous-learning/config.json +18 -18
  179. package/seed/marketplaces/ecc/skills/continuous-learning/evaluate-session.sh +69 -69
  180. package/seed/marketplaces/ecc/skills/continuous-learning-v2/SKILL.md +360 -360
  181. package/seed/marketplaces/ecc/skills/continuous-learning-v2/agents/observer-loop.sh +322 -322
  182. package/seed/marketplaces/ecc/skills/continuous-learning-v2/agents/observer.md +198 -198
  183. package/seed/marketplaces/ecc/skills/continuous-learning-v2/agents/session-guardian.sh +150 -150
  184. package/seed/marketplaces/ecc/skills/continuous-learning-v2/agents/start-observer.sh +248 -248
  185. package/seed/marketplaces/ecc/skills/continuous-learning-v2/config.json +8 -8
  186. package/seed/marketplaces/ecc/skills/continuous-learning-v2/hooks/observe.sh +498 -498
  187. package/seed/marketplaces/ecc/skills/continuous-learning-v2/scripts/detect-project.sh +322 -322
  188. package/seed/marketplaces/ecc/skills/continuous-learning-v2/scripts/instinct-cli.py +1826 -1826
  189. package/seed/marketplaces/ecc/skills/continuous-learning-v2/scripts/lib/homunculus-dir.sh +31 -31
  190. package/seed/marketplaces/ecc/skills/continuous-learning-v2/scripts/migrate-homunculus.sh +62 -62
  191. package/seed/marketplaces/ecc/skills/continuous-learning-v2/scripts/test_parse_instinct.py +1018 -1018
  192. package/seed/marketplaces/ecc/skills/cost-aware-llm-pipeline/SKILL.md +183 -183
  193. package/seed/marketplaces/ecc/skills/cost-tracking/SKILL.md +147 -147
  194. package/seed/marketplaces/ecc/skills/council/SKILL.md +203 -203
  195. package/seed/marketplaces/ecc/skills/cpp-coding-standards/SKILL.md +723 -723
  196. package/seed/marketplaces/ecc/skills/cpp-testing/SKILL.md +324 -324
  197. package/seed/marketplaces/ecc/skills/crosspost/SKILL.md +111 -111
  198. package/seed/marketplaces/ecc/skills/csharp-testing/SKILL.md +321 -321
  199. package/seed/marketplaces/ecc/skills/customer-billing-ops/SKILL.md +140 -140
  200. package/seed/marketplaces/ecc/skills/customs-trade-compliance/SKILL.md +263 -263
  201. package/seed/marketplaces/ecc/skills/dart-flutter-patterns/SKILL.md +563 -563
  202. package/seed/marketplaces/ecc/skills/dashboard-builder/SKILL.md +108 -108
  203. package/seed/marketplaces/ecc/skills/data-scraper-agent/SKILL.md +764 -764
  204. package/seed/marketplaces/ecc/skills/data-throughput-accelerator/SKILL.md +72 -72
  205. package/seed/marketplaces/ecc/skills/database-migrations/SKILL.md +429 -429
  206. package/seed/marketplaces/ecc/skills/deep-research/SKILL.md +159 -159
  207. package/seed/marketplaces/ecc/skills/defi-amm-security/SKILL.md +166 -166
  208. package/seed/marketplaces/ecc/skills/deployment-patterns/SKILL.md +427 -427
  209. package/seed/marketplaces/ecc/skills/design-system/SKILL.md +82 -82
  210. package/seed/marketplaces/ecc/skills/django-celery/SKILL.md +457 -457
  211. package/seed/marketplaces/ecc/skills/django-patterns/SKILL.md +734 -734
  212. package/seed/marketplaces/ecc/skills/django-security/SKILL.md +593 -593
  213. package/seed/marketplaces/ecc/skills/django-tdd/SKILL.md +729 -729
  214. package/seed/marketplaces/ecc/skills/django-verification/SKILL.md +469 -469
  215. package/seed/marketplaces/ecc/skills/dmux-workflows/SKILL.md +191 -191
  216. package/seed/marketplaces/ecc/skills/docker-patterns/SKILL.md +364 -364
  217. package/seed/marketplaces/ecc/skills/documentation-lookup/SKILL.md +90 -90
  218. package/seed/marketplaces/ecc/skills/dotnet-patterns/SKILL.md +321 -321
  219. package/seed/marketplaces/ecc/skills/e2e-testing/SKILL.md +326 -326
  220. package/seed/marketplaces/ecc/skills/ecc-guide/SKILL.md +189 -189
  221. package/seed/marketplaces/ecc/skills/ecc-tools-cost-audit/SKILL.md +160 -160
  222. package/seed/marketplaces/ecc/skills/email-ops/SKILL.md +121 -121
  223. package/seed/marketplaces/ecc/skills/energy-procurement/SKILL.md +228 -228
  224. package/seed/marketplaces/ecc/skills/enterprise-agent-ops/SKILL.md +50 -50
  225. package/seed/marketplaces/ecc/skills/error-handling/SKILL.md +376 -376
  226. package/seed/marketplaces/ecc/skills/eval-harness/SKILL.md +270 -270
  227. package/seed/marketplaces/ecc/skills/evm-token-decimals/SKILL.md +130 -130
  228. package/seed/marketplaces/ecc/skills/exa-search/SKILL.md +107 -107
  229. package/seed/marketplaces/ecc/skills/fal-ai-media/SKILL.md +288 -288
  230. package/seed/marketplaces/ecc/skills/fastapi-patterns/SKILL.md +327 -327
  231. package/seed/marketplaces/ecc/skills/finance-billing-ops/SKILL.md +127 -127
  232. package/seed/marketplaces/ecc/skills/flox-environments/SKILL.md +496 -496
  233. package/seed/marketplaces/ecc/skills/flutter-dart-code-review/SKILL.md +435 -435
  234. package/seed/marketplaces/ecc/skills/foundation-models-on-device/SKILL.md +243 -243
  235. package/seed/marketplaces/ecc/skills/frontend-a11y/SKILL.md +446 -446
  236. package/seed/marketplaces/ecc/skills/frontend-design-direction/SKILL.md +92 -92
  237. package/seed/marketplaces/ecc/skills/frontend-patterns/SKILL.md +642 -642
  238. package/seed/marketplaces/ecc/skills/frontend-slides/SKILL.md +184 -184
  239. package/seed/marketplaces/ecc/skills/frontend-slides/STYLE_PRESETS.md +330 -330
  240. package/seed/marketplaces/ecc/skills/frontend-slides/animation-patterns.md +122 -122
  241. package/seed/marketplaces/ecc/skills/frontend-slides/html-template.md +419 -419
  242. package/seed/marketplaces/ecc/skills/frontend-slides/scripts/export-pdf.sh +418 -418
  243. package/seed/marketplaces/ecc/skills/frontend-slides/scripts/extract-pptx.py +96 -96
  244. package/seed/marketplaces/ecc/skills/frontend-slides/viewport-base.css +153 -153
  245. package/seed/marketplaces/ecc/skills/fsharp-testing/SKILL.md +280 -280
  246. package/seed/marketplaces/ecc/skills/gan-style-harness/SKILL.md +278 -278
  247. package/seed/marketplaces/ecc/skills/gateguard/SKILL.md +125 -125
  248. package/seed/marketplaces/ecc/skills/git-workflow/SKILL.md +715 -715
  249. package/seed/marketplaces/ecc/skills/github-ops/SKILL.md +144 -144
  250. package/seed/marketplaces/ecc/skills/golang-patterns/SKILL.md +674 -674
  251. package/seed/marketplaces/ecc/skills/golang-testing/SKILL.md +720 -720
  252. package/seed/marketplaces/ecc/skills/google-workspace-ops/SKILL.md +95 -95
  253. package/seed/marketplaces/ecc/skills/healthcare-cdss-patterns/SKILL.md +245 -245
  254. package/seed/marketplaces/ecc/skills/healthcare-emr-patterns/SKILL.md +159 -159
  255. package/seed/marketplaces/ecc/skills/healthcare-eval-harness/SKILL.md +207 -207
  256. package/seed/marketplaces/ecc/skills/healthcare-phi-compliance/SKILL.md +145 -145
  257. package/seed/marketplaces/ecc/skills/hermes-imports/SKILL.md +88 -88
  258. package/seed/marketplaces/ecc/skills/hexagonal-architecture/SKILL.md +276 -276
  259. package/seed/marketplaces/ecc/skills/hipaa-compliance/SKILL.md +78 -78
  260. package/seed/marketplaces/ecc/skills/homelab-network-readiness/SKILL.md +169 -169
  261. package/seed/marketplaces/ecc/skills/homelab-network-setup/SKILL.md +129 -129
  262. package/seed/marketplaces/ecc/skills/homelab-pihole-dns/SKILL.md +274 -274
  263. package/seed/marketplaces/ecc/skills/homelab-vlan-segmentation/SKILL.md +311 -311
  264. package/seed/marketplaces/ecc/skills/homelab-wireguard-vpn/SKILL.md +305 -305
  265. package/seed/marketplaces/ecc/skills/hookify-rules/SKILL.md +128 -128
  266. package/seed/marketplaces/ecc/skills/inventory-demand-planning/SKILL.md +247 -247
  267. package/seed/marketplaces/ecc/skills/investor-materials/SKILL.md +96 -96
  268. package/seed/marketplaces/ecc/skills/investor-outreach/SKILL.md +91 -91
  269. package/seed/marketplaces/ecc/skills/ios-icon-gen/SKILL.md +157 -157
  270. package/seed/marketplaces/ecc/skills/ios-icon-gen/scripts/generate_icons.swift +258 -258
  271. package/seed/marketplaces/ecc/skills/ios-icon-gen/scripts/iconify_gen.sh +235 -235
  272. package/seed/marketplaces/ecc/skills/iterative-retrieval/SKILL.md +211 -211
  273. package/seed/marketplaces/ecc/skills/ito-basket-compare/SKILL.md +63 -63
  274. package/seed/marketplaces/ecc/skills/ito-data-atlas-agent/SKILL.md +63 -63
  275. package/seed/marketplaces/ecc/skills/ito-market-intelligence/SKILL.md +60 -60
  276. package/seed/marketplaces/ecc/skills/ito-trade-planner/SKILL.md +67 -67
  277. package/seed/marketplaces/ecc/skills/java-coding-standards/SKILL.md +383 -383
  278. package/seed/marketplaces/ecc/skills/jira-integration/SKILL.md +293 -293
  279. package/seed/marketplaces/ecc/skills/jpa-patterns/SKILL.md +151 -151
  280. package/seed/marketplaces/ecc/skills/knowledge-ops/SKILL.md +154 -154
  281. package/seed/marketplaces/ecc/skills/kotlin-coroutines-flows/SKILL.md +284 -284
  282. package/seed/marketplaces/ecc/skills/kotlin-exposed-patterns/SKILL.md +719 -719
  283. package/seed/marketplaces/ecc/skills/kotlin-ktor-patterns/SKILL.md +689 -689
  284. package/seed/marketplaces/ecc/skills/kotlin-patterns/SKILL.md +711 -711
  285. package/seed/marketplaces/ecc/skills/kotlin-testing/SKILL.md +824 -824
  286. package/seed/marketplaces/ecc/skills/laravel-patterns/SKILL.md +415 -415
  287. package/seed/marketplaces/ecc/skills/laravel-plugin-discovery/SKILL.md +229 -229
  288. package/seed/marketplaces/ecc/skills/laravel-security/SKILL.md +285 -285
  289. package/seed/marketplaces/ecc/skills/laravel-tdd/SKILL.md +283 -283
  290. package/seed/marketplaces/ecc/skills/laravel-verification/SKILL.md +179 -179
  291. package/seed/marketplaces/ecc/skills/latency-critical-systems/SKILL.md +73 -73
  292. package/seed/marketplaces/ecc/skills/lead-intelligence/SKILL.md +321 -321
  293. package/seed/marketplaces/ecc/skills/lead-intelligence/agents/enrichment-agent.md +85 -85
  294. package/seed/marketplaces/ecc/skills/lead-intelligence/agents/mutual-mapper.md +75 -75
  295. package/seed/marketplaces/ecc/skills/lead-intelligence/agents/outreach-drafter.md +98 -98
  296. package/seed/marketplaces/ecc/skills/lead-intelligence/agents/signal-scorer.md +60 -60
  297. package/seed/marketplaces/ecc/skills/liquid-glass-design/SKILL.md +279 -279
  298. package/seed/marketplaces/ecc/skills/llm-trading-agent-security/SKILL.md +146 -146
  299. package/seed/marketplaces/ecc/skills/logistics-exception-management/SKILL.md +222 -222
  300. package/seed/marketplaces/ecc/skills/make-interfaces-feel-better/SKILL.md +151 -151
  301. package/seed/marketplaces/ecc/skills/manim-video/SKILL.md +89 -89
  302. package/seed/marketplaces/ecc/skills/manim-video/assets/network_graph_scene.py +52 -52
  303. package/seed/marketplaces/ecc/skills/market-research/SKILL.md +75 -75
  304. package/seed/marketplaces/ecc/skills/marketing-campaign/SKILL.md +113 -113
  305. package/seed/marketplaces/ecc/skills/mcp-server-patterns/SKILL.md +69 -69
  306. package/seed/marketplaces/ecc/skills/messages-ops/SKILL.md +104 -104
  307. package/seed/marketplaces/ecc/skills/mle-workflow/SKILL.md +346 -346
  308. package/seed/marketplaces/ecc/skills/motion-advanced/SKILL.md +596 -596
  309. package/seed/marketplaces/ecc/skills/motion-foundations/SKILL.md +299 -299
  310. package/seed/marketplaces/ecc/skills/motion-patterns/SKILL.md +435 -435
  311. package/seed/marketplaces/ecc/skills/motion-ui/SKILL.md +575 -575
  312. package/seed/marketplaces/ecc/skills/mysql-patterns/SKILL.md +412 -412
  313. package/seed/marketplaces/ecc/skills/nanoclaw-repl/SKILL.md +33 -33
  314. package/seed/marketplaces/ecc/skills/nestjs-patterns/SKILL.md +230 -230
  315. package/seed/marketplaces/ecc/skills/netmiko-ssh-automation/SKILL.md +173 -173
  316. package/seed/marketplaces/ecc/skills/network-bgp-diagnostics/SKILL.md +167 -167
  317. package/seed/marketplaces/ecc/skills/network-config-validation/SKILL.md +210 -210
  318. package/seed/marketplaces/ecc/skills/network-interface-health/SKILL.md +152 -152
  319. package/seed/marketplaces/ecc/skills/nextjs-turbopack/SKILL.md +57 -57
  320. package/seed/marketplaces/ecc/skills/nodejs-keccak256/SKILL.md +102 -102
  321. package/seed/marketplaces/ecc/skills/nutrient-document-processing/SKILL.md +167 -167
  322. package/seed/marketplaces/ecc/skills/nuxt4-patterns/SKILL.md +100 -100
  323. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/SKILL.md +288 -288
  324. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/gacha.py +224 -224
  325. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/gacha.sh +5 -5
  326. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/avatar-style.md +124 -124
  327. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/boundary-rules.md +53 -53
  328. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/error-handling.md +53 -53
  329. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/identity-tension.md +48 -48
  330. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/naming-system.md +39 -39
  331. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/output-template.md +166 -166
  332. package/seed/marketplaces/ecc/skills/opensource-pipeline/SKILL.md +255 -255
  333. package/seed/marketplaces/ecc/skills/parallel-execution-optimizer/SKILL.md +72 -72
  334. package/seed/marketplaces/ecc/skills/perl-patterns/SKILL.md +504 -504
  335. package/seed/marketplaces/ecc/skills/perl-security/SKILL.md +503 -503
  336. package/seed/marketplaces/ecc/skills/perl-testing/SKILL.md +475 -475
  337. package/seed/marketplaces/ecc/skills/plan-orchestrate/SKILL.md +262 -262
  338. package/seed/marketplaces/ecc/skills/plankton-code-quality/SKILL.md +236 -236
  339. package/seed/marketplaces/ecc/skills/postgres-patterns/SKILL.md +147 -147
  340. package/seed/marketplaces/ecc/skills/prediction-market-oracle-research/SKILL.md +63 -63
  341. package/seed/marketplaces/ecc/skills/prediction-market-risk-review/SKILL.md +60 -60
  342. package/seed/marketplaces/ecc/skills/prisma-patterns/SKILL.md +371 -371
  343. package/seed/marketplaces/ecc/skills/product-capability/SKILL.md +141 -141
  344. package/seed/marketplaces/ecc/skills/product-lens/SKILL.md +92 -92
  345. package/seed/marketplaces/ecc/skills/production-audit/SKILL.md +206 -206
  346. package/seed/marketplaces/ecc/skills/production-scheduling/SKILL.md +238 -238
  347. package/seed/marketplaces/ecc/skills/project-flow-ops/SKILL.md +111 -111
  348. package/seed/marketplaces/ecc/skills/prompt-optimizer/SKILL.md +398 -398
  349. package/seed/marketplaces/ecc/skills/python-patterns/SKILL.md +750 -750
  350. package/seed/marketplaces/ecc/skills/python-testing/SKILL.md +816 -816
  351. package/seed/marketplaces/ecc/skills/pytorch-patterns/SKILL.md +396 -396
  352. package/seed/marketplaces/ecc/skills/quality-nonconformance/SKILL.md +260 -260
  353. package/seed/marketplaces/ecc/skills/quarkus-patterns/SKILL.md +722 -722
  354. package/seed/marketplaces/ecc/skills/quarkus-security/SKILL.md +467 -467
  355. package/seed/marketplaces/ecc/skills/quarkus-tdd/SKILL.md +811 -811
  356. package/seed/marketplaces/ecc/skills/quarkus-verification/SKILL.md +479 -479
  357. package/seed/marketplaces/ecc/skills/ralphinho-rfc-pipeline/SKILL.md +67 -67
  358. package/seed/marketplaces/ecc/skills/recsys-pipeline-architect/SKILL.md +114 -114
  359. package/seed/marketplaces/ecc/skills/recursive-decision-ledger/SKILL.md +79 -79
  360. package/seed/marketplaces/ecc/skills/redis-patterns/SKILL.md +403 -403
  361. package/seed/marketplaces/ecc/skills/regex-vs-llm-structured-text/SKILL.md +220 -220
  362. package/seed/marketplaces/ecc/skills/remotion-video-creation/SKILL.md +43 -43
  363. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/3d.md +86 -86
  364. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/animations.md +29 -29
  365. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/assets/charts-bar-chart.tsx +173 -173
  366. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/assets/text-animations-typewriter.tsx +100 -100
  367. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/assets/text-animations-word-highlight.tsx +108 -108
  368. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/assets.md +78 -78
  369. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/audio.md +172 -172
  370. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/calculate-metadata.md +104 -104
  371. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/can-decode.md +75 -75
  372. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/charts.md +58 -58
  373. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/compositions.md +146 -146
  374. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/display-captions.md +126 -126
  375. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/extract-frames.md +229 -229
  376. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/fonts.md +152 -152
  377. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/get-audio-duration.md +58 -58
  378. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/get-video-dimensions.md +68 -68
  379. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/get-video-duration.md +58 -58
  380. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/gifs.md +138 -138
  381. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/images.md +130 -130
  382. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/import-srt-captions.md +67 -67
  383. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/lottie.md +67 -67
  384. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/measuring-dom-nodes.md +34 -34
  385. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/measuring-text.md +143 -143
  386. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/sequencing.md +106 -106
  387. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/tailwind.md +11 -11
  388. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/text-animations.md +20 -20
  389. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/timing.md +179 -179
  390. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/transcribe-captions.md +19 -19
  391. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/transitions.md +122 -122
  392. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/trimming.md +52 -52
  393. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/videos.md +171 -171
  394. package/seed/marketplaces/ecc/skills/repo-scan/SKILL.md +78 -78
  395. package/seed/marketplaces/ecc/skills/research-ops/SKILL.md +112 -112
  396. package/seed/marketplaces/ecc/skills/returns-reverse-logistics/SKILL.md +240 -240
  397. package/seed/marketplaces/ecc/skills/rules-distill/SKILL.md +264 -264
  398. package/seed/marketplaces/ecc/skills/rules-distill/scripts/scan-rules.sh +58 -58
  399. package/seed/marketplaces/ecc/skills/rules-distill/scripts/scan-skills.sh +129 -129
  400. package/seed/marketplaces/ecc/skills/rust-patterns/SKILL.md +499 -499
  401. package/seed/marketplaces/ecc/skills/rust-testing/SKILL.md +500 -500
  402. package/seed/marketplaces/ecc/skills/safety-guard/SKILL.md +75 -75
  403. package/seed/marketplaces/ecc/skills/santa-method/SKILL.md +306 -306
  404. package/seed/marketplaces/ecc/skills/scientific-db-pubmed-database/SKILL.md +175 -175
  405. package/seed/marketplaces/ecc/skills/scientific-db-uspto-database/SKILL.md +177 -177
  406. package/seed/marketplaces/ecc/skills/scientific-pkg-gget/SKILL.md +166 -166
  407. package/seed/marketplaces/ecc/skills/scientific-thinking-literature-review/SKILL.md +192 -192
  408. package/seed/marketplaces/ecc/skills/scientific-thinking-scholar-evaluation/SKILL.md +160 -160
  409. package/seed/marketplaces/ecc/skills/search-first/SKILL.md +182 -182
  410. package/seed/marketplaces/ecc/skills/security-bounty-hunter/SKILL.md +99 -99
  411. package/seed/marketplaces/ecc/skills/security-review/SKILL.md +503 -503
  412. package/seed/marketplaces/ecc/skills/security-review/cloud-infrastructure-security.md +361 -361
  413. package/seed/marketplaces/ecc/skills/security-scan/SKILL.md +165 -165
  414. package/seed/marketplaces/ecc/skills/seo/SKILL.md +154 -154
  415. package/seed/marketplaces/ecc/skills/skill-comply/SKILL.md +58 -58
  416. package/seed/marketplaces/ecc/skills/skill-comply/fixtures/compliant_trace.jsonl +5 -5
  417. package/seed/marketplaces/ecc/skills/skill-comply/fixtures/noncompliant_trace.jsonl +3 -3
  418. package/seed/marketplaces/ecc/skills/skill-comply/fixtures/tdd_spec.yaml +44 -44
  419. package/seed/marketplaces/ecc/skills/skill-comply/prompts/classifier.md +24 -24
  420. package/seed/marketplaces/ecc/skills/skill-comply/prompts/scenario_generator.md +62 -62
  421. package/seed/marketplaces/ecc/skills/skill-comply/prompts/spec_generator.md +42 -42
  422. package/seed/marketplaces/ecc/skills/skill-comply/pyproject.toml +15 -15
  423. package/seed/marketplaces/ecc/skills/skill-comply/scripts/classifier.py +85 -85
  424. package/seed/marketplaces/ecc/skills/skill-comply/scripts/grader.py +124 -124
  425. package/seed/marketplaces/ecc/skills/skill-comply/scripts/parser.py +107 -107
  426. package/seed/marketplaces/ecc/skills/skill-comply/scripts/report.py +170 -170
  427. package/seed/marketplaces/ecc/skills/skill-comply/scripts/run.py +127 -127
  428. package/seed/marketplaces/ecc/skills/skill-comply/scripts/runner.py +186 -186
  429. package/seed/marketplaces/ecc/skills/skill-comply/scripts/scenario_generator.py +70 -70
  430. package/seed/marketplaces/ecc/skills/skill-comply/scripts/spec_generator.py +72 -72
  431. package/seed/marketplaces/ecc/skills/skill-comply/scripts/utils.py +13 -13
  432. package/seed/marketplaces/ecc/skills/skill-comply/tests/test_grader.py +197 -197
  433. package/seed/marketplaces/ecc/skills/skill-comply/tests/test_parser.py +90 -90
  434. package/seed/marketplaces/ecc/skills/skill-comply/tests/test_runner.py +172 -172
  435. package/seed/marketplaces/ecc/skills/skill-scout/SKILL.md +140 -140
  436. package/seed/marketplaces/ecc/skills/skill-stocktake/SKILL.md +194 -194
  437. package/seed/marketplaces/ecc/skills/skill-stocktake/scripts/quick-diff.sh +87 -87
  438. package/seed/marketplaces/ecc/skills/skill-stocktake/scripts/save-results.sh +56 -56
  439. package/seed/marketplaces/ecc/skills/skill-stocktake/scripts/scan.sh +170 -170
  440. package/seed/marketplaces/ecc/skills/social-graph-ranker/SKILL.md +154 -154
  441. package/seed/marketplaces/ecc/skills/social-publisher/SKILL.md +115 -115
  442. package/seed/marketplaces/ecc/skills/springboot-patterns/SKILL.md +314 -314
  443. package/seed/marketplaces/ecc/skills/springboot-security/SKILL.md +272 -272
  444. package/seed/marketplaces/ecc/skills/springboot-tdd/SKILL.md +158 -158
  445. package/seed/marketplaces/ecc/skills/springboot-verification/SKILL.md +231 -231
  446. package/seed/marketplaces/ecc/skills/strategic-compact/SKILL.md +131 -131
  447. package/seed/marketplaces/ecc/skills/swift-actor-persistence/SKILL.md +143 -143
  448. package/seed/marketplaces/ecc/skills/swift-concurrency-6-2/SKILL.md +216 -216
  449. package/seed/marketplaces/ecc/skills/swift-protocol-di-testing/SKILL.md +190 -190
  450. package/seed/marketplaces/ecc/skills/swiftui-patterns/SKILL.md +259 -259
  451. package/seed/marketplaces/ecc/skills/tdd-workflow/SKILL.md +463 -463
  452. package/seed/marketplaces/ecc/skills/team-builder/SKILL.md +168 -168
  453. package/seed/marketplaces/ecc/skills/terminal-ops/SKILL.md +109 -109
  454. package/seed/marketplaces/ecc/skills/tinystruct-patterns/SKILL.md +203 -203
  455. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/architecture.md +90 -90
  456. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/data-handling.md +60 -60
  457. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/database.md +99 -99
  458. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/routing.md +64 -64
  459. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/system-usage.md +97 -97
  460. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/testing.md +72 -72
  461. package/seed/marketplaces/ecc/skills/token-budget-advisor/SKILL.md +133 -133
  462. package/seed/marketplaces/ecc/skills/ui-demo/SKILL.md +465 -465
  463. package/seed/marketplaces/ecc/skills/ui-to-vue/SKILL.md +134 -134
  464. package/seed/marketplaces/ecc/skills/uncloud/SKILL.md +343 -343
  465. package/seed/marketplaces/ecc/skills/unified-notifications-ops/SKILL.md +187 -187
  466. package/seed/marketplaces/ecc/skills/verification-loop/SKILL.md +126 -126
  467. package/seed/marketplaces/ecc/skills/video-editing/SKILL.md +310 -310
  468. package/seed/marketplaces/ecc/skills/videodb/SKILL.md +374 -374
  469. package/seed/marketplaces/ecc/skills/videodb/reference/api-reference.md +550 -550
  470. package/seed/marketplaces/ecc/skills/videodb/reference/capture-reference.md +407 -407
  471. package/seed/marketplaces/ecc/skills/videodb/reference/capture.md +101 -101
  472. package/seed/marketplaces/ecc/skills/videodb/reference/editor.md +443 -443
  473. package/seed/marketplaces/ecc/skills/videodb/reference/generative.md +331 -331
  474. package/seed/marketplaces/ecc/skills/videodb/reference/rtstream-reference.md +564 -564
  475. package/seed/marketplaces/ecc/skills/videodb/reference/rtstream.md +65 -65
  476. package/seed/marketplaces/ecc/skills/videodb/reference/search.md +230 -230
  477. package/seed/marketplaces/ecc/skills/videodb/reference/streaming.md +406 -406
  478. package/seed/marketplaces/ecc/skills/videodb/reference/use-cases.md +118 -118
  479. package/seed/marketplaces/ecc/skills/videodb/scripts/ws_listener.py +282 -282
  480. package/seed/marketplaces/ecc/skills/visa-doc-translate/README.md +86 -86
  481. package/seed/marketplaces/ecc/skills/visa-doc-translate/SKILL.md +117 -117
  482. package/seed/marketplaces/ecc/skills/vite-patterns/SKILL.md +449 -449
  483. package/seed/marketplaces/ecc/skills/windows-desktop-e2e/SKILL.md +887 -887
  484. package/seed/marketplaces/ecc/skills/workspace-surface-audit/SKILL.md +125 -125
  485. package/seed/marketplaces/ecc/skills/x-api/SKILL.md +234 -234
  486. package/seed/marketplaces/ecc/.claude/commands/add-language-rules.md +0 -39
  487. package/seed/marketplaces/ecc/.claude/commands/database-migration.md +0 -36
  488. package/seed/marketplaces/ecc/.claude/commands/feature-development.md +0 -38
  489. package/seed/marketplaces/ecc/.claude/ecc-tools.json +0 -334
  490. package/seed/marketplaces/ecc/.claude/enterprise/controls.md +0 -15
  491. package/seed/marketplaces/ecc/.claude/homunculus/instincts/inherited/everything-claude-code-instincts.yaml +0 -162
  492. package/seed/marketplaces/ecc/.claude/identity.json +0 -14
  493. package/seed/marketplaces/ecc/.claude/package-manager.json +0 -4
  494. package/seed/marketplaces/ecc/.claude/research/everything-claude-code-research-playbook.md +0 -21
  495. package/seed/marketplaces/ecc/.claude/rules/everything-claude-code-guardrails.md +0 -43
  496. package/seed/marketplaces/ecc/.claude/rules/node.md +0 -56
  497. package/seed/marketplaces/ecc/.claude/skills/everything-claude-code/SKILL.md +0 -442
  498. package/seed/marketplaces/ecc/.claude/team/everything-claude-code-team-config.json +0 -15
  499. package/seed/marketplaces/ecc/.vscode/settings.json +0 -17
@@ -1,811 +1,811 @@
1
- ---
2
- name: quarkus-tdd
3
- description: Test-driven development for Quarkus 3.x LTS using JUnit 5, Mockito, REST Assured, Camel testing, and JaCoCo. Use when adding features, fixing bugs, or refactoring event-driven services.
4
- origin: ECC
5
- ---
6
-
7
- # Quarkus TDD Workflow
8
-
9
- TDD guidance for Quarkus 3.x services with 80%+ coverage (unit + integration). Optimized for event-driven architectures with Apache Camel.
10
-
11
- ## When to Use
12
-
13
- - New features or REST endpoints
14
- - Bug fixes or refactors
15
- - Adding data access logic, security rules, or reactive streams
16
- - Testing Apache Camel routes and event handlers
17
- - Testing event-driven services with RabbitMQ
18
- - Testing conditional flow logic
19
- - Validating CompletableFuture async operations
20
- - Testing LogContext propagation
21
-
22
- ## Workflow
23
-
24
- 1. Write tests first (they should fail)
25
- 2. Implement minimal code to pass
26
- 3. Refactor with tests green
27
- 4. Enforce coverage with JaCoCo (80%+ target)
28
-
29
- ## Unit Tests with @Nested Organization
30
-
31
- Follow this structured approach for comprehensive, readable tests:
32
-
33
- ```java
34
- @ExtendWith(MockitoExtension.class)
35
- @DisplayName("OrderService Unit Tests")
36
- class OrderServiceTest {
37
-
38
- @Mock
39
- private OrderRepository orderRepository;
40
-
41
- @Mock
42
- private EventService eventService;
43
-
44
- @Mock
45
- private FulfillmentPublisher fulfillmentPublisher;
46
-
47
- @InjectMocks
48
- private OrderService orderService;
49
-
50
- private CreateOrderCommand validCommand;
51
-
52
- @BeforeEach
53
- void setUp() {
54
- validCommand = new CreateOrderCommand(
55
- "customer-123",
56
- List.of(new OrderLine("sku-123", 2))
57
- );
58
- }
59
-
60
- @Nested
61
- @DisplayName("Tests for createOrder")
62
- class CreateOrder {
63
-
64
- @Test
65
- @DisplayName("Should persist order and publish fulfillment event")
66
- void givenValidCommand_whenCreateOrder_thenPersistsAndPublishes() {
67
- // ARRANGE
68
- doNothing().when(orderRepository).persist(any(Order.class));
69
-
70
- // ACT
71
- OrderReceipt receipt = orderService.createOrder(validCommand);
72
-
73
- // ASSERT
74
- assertThat(receipt).isNotNull();
75
- assertThat(receipt.customerId()).isEqualTo("customer-123");
76
- verify(orderRepository).persist(any(Order.class));
77
- verify(fulfillmentPublisher).publishAsync(receipt);
78
- verify(eventService).createSuccessEvent(receipt, "ORDER_CREATED");
79
- }
80
-
81
- @Test
82
- @DisplayName("Should reject missing customer id")
83
- void givenMissingCustomerId_whenCreateOrder_thenThrowsBadRequest() {
84
- // ARRANGE
85
- CreateOrderCommand invalid = new CreateOrderCommand("", validCommand.lines());
86
-
87
- // ACT & ASSERT
88
- WebApplicationException exception = assertThrows(
89
- WebApplicationException.class,
90
- () -> orderService.createOrder(invalid)
91
- );
92
-
93
- assertThat(exception.getResponse().getStatus()).isEqualTo(400);
94
- verify(orderRepository, never()).persist(any(Order.class));
95
- verify(fulfillmentPublisher, never()).publishAsync(any());
96
- }
97
-
98
- @Test
99
- @DisplayName("Should record error event when persistence fails")
100
- void givenPersistenceFailure_whenCreateOrder_thenRecordsErrorEvent() {
101
- // ARRANGE
102
- doThrow(new PersistenceException("database unavailable"))
103
- .when(orderRepository).persist(any(Order.class));
104
-
105
- // ACT & ASSERT
106
- PersistenceException exception = assertThrows(
107
- PersistenceException.class,
108
- () -> orderService.createOrder(validCommand)
109
- );
110
-
111
- assertThat(exception.getMessage()).contains("database unavailable");
112
- verify(eventService).createErrorEvent(
113
- eq(validCommand),
114
- eq("ORDER_CREATE_FAILED"),
115
- contains("database unavailable")
116
- );
117
- verify(fulfillmentPublisher, never()).publishAsync(any());
118
- }
119
-
120
- @Test
121
- @DisplayName("Should reject null commands")
122
- void givenNullCommand_whenCreateOrder_thenThrowsNullPointerException() {
123
- // ACT & ASSERT
124
- assertThrows(
125
- NullPointerException.class,
126
- () -> orderService.createOrder(null)
127
- );
128
-
129
- verify(orderRepository, never()).persist(any(Order.class));
130
- }
131
- }
132
- }
133
- ```
134
-
135
- ### Key Testing Patterns
136
-
137
- 1. **@Nested Classes**: Group tests by method being tested
138
- 2. **@DisplayName**: Provide readable test descriptions for test reports
139
- 3. **Naming Convention**: `givenX_whenY_thenZ` for clarity
140
- 4. **AAA Pattern**: Explicit `// ARRANGE`, `// ACT`, `// ASSERT` comments
141
- 5. **@BeforeEach**: Setup common test data to reduce duplication
142
- 6. **assertDoesNotThrow**: Test success scenarios without catching exceptions
143
- 7. **assertThrows**: Test exception scenarios with message validation using AssertJ
144
- 8. **Comprehensive Coverage**: Test happy paths, null inputs, edge cases, exceptions
145
- 9. **Verify Interactions**: Use Mockito `verify()` to ensure methods are called correctly
146
- 10. **Never Verify**: Use `never()` to ensure methods are NOT called in error scenarios
147
-
148
- ## Testing Camel Routes
149
-
150
- ```java
151
- @QuarkusTest
152
- @DisplayName("Business Rules Camel Route Tests")
153
- class BusinessRulesRouteTest {
154
-
155
- @Inject
156
- CamelContext camelContext;
157
-
158
- @Inject
159
- ProducerTemplate producerTemplate;
160
-
161
- @InjectMock
162
- EventService eventService;
163
-
164
- @InjectMock
165
- DocumentValidator documentValidator;
166
-
167
- private BusinessRulesPayload testPayload;
168
-
169
- @BeforeEach
170
- void setUp() {
171
- // ARRANGE - Test data
172
- testPayload = new BusinessRulesPayload();
173
- testPayload.setDocumentId(1L);
174
- testPayload.setFlowProfile(FlowProfile.BASIC);
175
- }
176
-
177
- @Nested
178
- @DisplayName("Tests for business-rules-publisher route")
179
- class BusinessRulesPublisher {
180
-
181
- @Test
182
- @DisplayName("Should successfully publish message to RabbitMQ")
183
- void givenValidPayload_whenPublish_thenMessageSentToQueue() throws Exception {
184
- // ARRANGE
185
- MockEndpoint mockRabbitMQ = camelContext.getEndpoint("mock:rabbitmq", MockEndpoint.class);
186
- mockRabbitMQ.expectedMessageCount(1);
187
-
188
- // Replace real endpoint with mock for testing
189
- camelContext.getRouteController().stopRoute("business-rules-publisher");
190
- AdviceWith.adviceWith(camelContext, "business-rules-publisher", advice -> {
191
- advice.replaceFromWith("direct:business-rules-publisher");
192
- advice.weaveByToString(".*spring-rabbitmq.*").replace().to("mock:rabbitmq");
193
- });
194
- camelContext.getRouteController().startRoute("business-rules-publisher");
195
-
196
- // ACT
197
- producerTemplate.sendBody("direct:business-rules-publisher", testPayload);
198
-
199
- // ASSERT — body is a JSON String after .marshal().json(JsonLibrary.Jackson)
200
- mockRabbitMQ.assertIsSatisfied(5000);
201
-
202
- assertThat(mockRabbitMQ.getExchanges()).hasSize(1);
203
- String body = mockRabbitMQ.getExchanges().get(0).getIn().getBody(String.class);
204
- assertThat(body).contains("\"documentId\":1");
205
- }
206
-
207
- @Test
208
- @DisplayName("Should handle marshalling to JSON")
209
- void givenPayload_whenPublish_thenMarshalledToJson() throws Exception {
210
- // ARRANGE
211
- MockEndpoint mockMarshal = new MockEndpoint("mock:marshal");
212
- camelContext.addEndpoint("mock:marshal", mockMarshal);
213
- mockMarshal.expectedMessageCount(1);
214
-
215
- camelContext.getRouteController().stopRoute("business-rules-publisher");
216
- AdviceWith.adviceWith(camelContext, "business-rules-publisher", advice -> {
217
- advice.weaveAddLast().to("mock:marshal");
218
- });
219
- camelContext.getRouteController().startRoute("business-rules-publisher");
220
-
221
- // ACT
222
- producerTemplate.sendBody("direct:business-rules-publisher", testPayload);
223
-
224
- // ASSERT
225
- mockMarshal.assertIsSatisfied(5000);
226
-
227
- String body = mockMarshal.getExchanges().get(0).getIn().getBody(String.class);
228
- assertThat(body).contains("\"documentId\":1");
229
- assertThat(body).contains("\"flowProfile\":\"BASIC\"");
230
- }
231
- }
232
-
233
- @Nested
234
- @DisplayName("Tests for document-processing route")
235
- class DocumentProcessing {
236
-
237
- @Test
238
- @DisplayName("Should route invoice to correct processor")
239
- void givenInvoiceType_whenProcess_thenRoutesToInvoiceProcessor() throws Exception {
240
- // ARRANGE
241
- MockEndpoint mockInvoice = camelContext.getEndpoint("mock:invoice", MockEndpoint.class);
242
- mockInvoice.expectedMessageCount(1);
243
-
244
- camelContext.getRouteController().stopRoute("document-processing");
245
- AdviceWith.adviceWith(camelContext, "document-processing", advice -> {
246
- advice.weaveByToString(".*direct:process-invoice.*").replace().to("mock:invoice");
247
- });
248
- camelContext.getRouteController().startRoute("document-processing");
249
-
250
- // ACT
251
- producerTemplate.sendBodyAndHeader("direct:process-document",
252
- testPayload, "documentType", "INVOICE");
253
-
254
- // ASSERT
255
- mockInvoice.assertIsSatisfied(5000);
256
- }
257
-
258
- @Test
259
- @DisplayName("Should handle validation errors gracefully")
260
- void givenValidationError_whenProcess_thenRoutesToErrorHandler() throws Exception {
261
- // ARRANGE
262
- MockEndpoint mockError = camelContext.getEndpoint("mock:error", MockEndpoint.class);
263
- mockError.expectedMessageCount(1);
264
-
265
- camelContext.getRouteController().stopRoute("document-processing");
266
- AdviceWith.adviceWith(camelContext, "document-processing", advice -> {
267
- advice.weaveByToString(".*direct:validation-error-handler.*")
268
- .replace().to("mock:error");
269
- });
270
- camelContext.getRouteController().startRoute("document-processing");
271
-
272
- // Mock validator bean to throw exception
273
- when(documentValidator.validate(any())).thenThrow(new ValidationException("Invalid document"));
274
-
275
- // ACT
276
- producerTemplate.sendBody("direct:process-document", testPayload);
277
-
278
- // ASSERT
279
- mockError.assertIsSatisfied(5000);
280
-
281
- Exception exception = mockError.getExchanges().get(0).getException();
282
- assertThat(exception).isInstanceOf(ValidationException.class);
283
- assertThat(exception.getMessage()).contains("Invalid document");
284
- }
285
- }
286
- }
287
- ```
288
-
289
- ## Testing Event Services
290
-
291
- ```java
292
- @ExtendWith(MockitoExtension.class)
293
- @DisplayName("EventService Unit Tests")
294
- class EventServiceTest {
295
-
296
- @Mock
297
- private EventRepository eventRepository;
298
-
299
- @Mock
300
- private ObjectMapper objectMapper;
301
-
302
- @InjectMocks
303
- private EventService eventService;
304
-
305
- private BusinessRulesPayload testPayload;
306
-
307
- @BeforeEach
308
- void setUp() {
309
- // ARRANGE
310
- testPayload = new BusinessRulesPayload();
311
- testPayload.setDocumentId(1L);
312
- }
313
-
314
- @Nested
315
- @DisplayName("Tests for createSuccessEvent")
316
- class CreateSuccessEvent {
317
-
318
- @Test
319
- @DisplayName("Should create success event with correct attributes")
320
- void givenValidPayload_whenCreateSuccessEvent_thenEventPersisted() throws Exception {
321
- // ARRANGE
322
- when(objectMapper.writeValueAsString(testPayload)).thenReturn("{\"documentId\":1}");
323
-
324
- // ACT
325
- assertDoesNotThrow(() ->
326
- eventService.createSuccessEvent(testPayload, "DOCUMENT_PROCESSED"));
327
-
328
- // ASSERT
329
- verify(eventRepository).persist(argThat(event ->
330
- event.getType().equals("DOCUMENT_PROCESSED") &&
331
- event.getStatus() == EventStatus.SUCCESS &&
332
- event.getPayload().equals("{\"documentId\":1}") &&
333
- event.getTimestamp() != null
334
- ));
335
- }
336
-
337
- @Test
338
- @DisplayName("Should throw exception when payload is null")
339
- void givenNullPayload_whenCreateSuccessEvent_thenThrowsException() {
340
- // ARRANGE
341
- Object nullPayload = null;
342
-
343
- // ACT & ASSERT
344
- NullPointerException exception = assertThrows(
345
- NullPointerException.class,
346
- () -> eventService.createSuccessEvent(nullPayload, "EVENT_TYPE")
347
- );
348
-
349
- assertThat(exception.getMessage()).isEqualTo("Payload cannot be null");
350
- verify(eventRepository, never()).persist(any());
351
- }
352
- }
353
-
354
- @Nested
355
- @DisplayName("Tests for createErrorEvent")
356
- class CreateErrorEvent {
357
-
358
- @Test
359
- @DisplayName("Should create error event with error message")
360
- void givenError_whenCreateErrorEvent_thenEventPersistedWithMessage() throws Exception {
361
- // ARRANGE
362
- String errorMessage = "Processing failed";
363
- when(objectMapper.writeValueAsString(testPayload)).thenReturn("{\"documentId\":1}");
364
-
365
- // ACT
366
- assertDoesNotThrow(() ->
367
- eventService.createErrorEvent(testPayload, "PROCESSING_ERROR", errorMessage));
368
-
369
- // ASSERT
370
- verify(eventRepository).persist(argThat(event ->
371
- event.getType().equals("PROCESSING_ERROR") &&
372
- event.getStatus() == EventStatus.ERROR &&
373
- event.getErrorMessage().equals(errorMessage) &&
374
- event.getPayload().equals("{\"documentId\":1}")
375
- ));
376
- }
377
-
378
- @ParameterizedTest
379
- @DisplayName("Should reject invalid error messages")
380
- @ValueSource(strings = {"", " "})
381
- void givenBlankErrorMessage_whenCreateErrorEvent_thenThrowsException(String blankMessage) {
382
- // ACT & ASSERT
383
- IllegalArgumentException exception = assertThrows(
384
- IllegalArgumentException.class,
385
- () -> eventService.createErrorEvent(testPayload, "ERROR", blankMessage)
386
- );
387
-
388
- assertThat(exception.getMessage()).contains("Error message cannot be blank");
389
- }
390
- }
391
- }
392
- ```
393
-
394
- ## Testing CompletableFuture
395
-
396
- ```java
397
- @ExtendWith(MockitoExtension.class)
398
- @DisplayName("FileStorageService Unit Tests")
399
- class FileStorageServiceTest {
400
-
401
- @Mock
402
- private S3Client s3Client;
403
-
404
- @Mock
405
- private ExecutorService executorService;
406
-
407
- @InjectMocks
408
- private FileStorageService fileStorageService;
409
-
410
- private InputStream testInputStream;
411
- private LogContext testLogContext;
412
-
413
- @BeforeEach
414
- void setUp() {
415
- // ARRANGE
416
- testInputStream = new ByteArrayInputStream("test content".getBytes());
417
- testLogContext = new LogContext();
418
- testLogContext.put("traceId", "trace-123");
419
- }
420
-
421
- @Nested
422
- @DisplayName("Tests for uploadOriginalFile")
423
- class UploadOriginalFile {
424
-
425
- @Test
426
- @DisplayName("Should successfully upload file and return document info")
427
- void givenValidFile_whenUpload_thenReturnsDocumentInfo() throws Exception {
428
- // ARRANGE
429
- doAnswer(invocation -> {
430
- ((Runnable) invocation.getArgument(0)).run();
431
- return null;
432
- }).when(executorService).execute(any(Runnable.class));
433
-
434
- when(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class)))
435
- .thenReturn(PutObjectResponse.builder().build());
436
-
437
- // ACT
438
- CompletableFuture<StoredDocumentInfo> future =
439
- fileStorageService.uploadOriginalFile(testInputStream, 1024L,
440
- testLogContext, InvoiceFormat.UBL);
441
-
442
- StoredDocumentInfo result = future.join();
443
-
444
- // ASSERT
445
- assertThat(result).isNotNull();
446
- assertThat(result.getPath()).isNotBlank();
447
- assertThat(result.getSize()).isEqualTo(1024L);
448
- assertThat(result.getUploadedAt()).isNotNull();
449
-
450
- verify(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class));
451
- }
452
-
453
- @Test
454
- @DisplayName("Should handle S3 upload failure")
455
- void givenS3Failure_whenUpload_thenCompletableFutureFails() {
456
- // ARRANGE — run synchronously so exception propagates through the future
457
- doAnswer(invocation -> {
458
- ((Runnable) invocation.getArgument(0)).run();
459
- return null;
460
- }).when(executorService).execute(any(Runnable.class));
461
-
462
- when(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class)))
463
- .thenThrow(new StorageException("S3 unavailable"));
464
-
465
- // ACT
466
- CompletableFuture<StoredDocumentInfo> future =
467
- fileStorageService.uploadOriginalFile(testInputStream, 1024L,
468
- testLogContext, InvoiceFormat.UBL);
469
-
470
- // ASSERT
471
- assertThatThrownBy(() -> future.join())
472
- .isInstanceOf(CompletionException.class)
473
- .hasCauseInstanceOf(StorageException.class)
474
- .hasMessageContaining("S3 unavailable");
475
- }
476
-
477
- @Test
478
- @DisplayName("Should propagate LogContext to async operation")
479
- void givenLogContext_whenUpload_thenContextPropagated() throws Exception {
480
- // ARRANGE
481
- AtomicReference<LogContext> capturedContext = new AtomicReference<>();
482
-
483
- doAnswer(invocation -> {
484
- capturedContext.set(CustomLog.getCurrentContext());
485
- ((Runnable) invocation.getArgument(0)).run();
486
- return null;
487
- }).when(executorService).execute(any(Runnable.class));
488
-
489
- // ACT
490
- fileStorageService.uploadOriginalFile(testInputStream, 1024L,
491
- testLogContext, InvoiceFormat.UBL).join();
492
-
493
- // ASSERT
494
- assertThat(capturedContext.get()).isNotNull();
495
- assertThat(capturedContext.get().get("traceId")).isEqualTo("trace-123");
496
- }
497
- }
498
- }
499
- ```
500
-
501
- ## Resource Layer Tests (REST Assured)
502
-
503
- ```java
504
- @QuarkusTest
505
- @DisplayName("DocumentResource API Tests")
506
- class DocumentResourceTest {
507
-
508
- @InjectMock
509
- DocumentService documentService;
510
-
511
- @Nested
512
- @DisplayName("Tests for GET /api/documents")
513
- class ListDocuments {
514
-
515
- @Test
516
- @DisplayName("Should return list of documents")
517
- void givenDocumentsExist_whenList_thenReturnsOk() {
518
- // ARRANGE
519
- List<Document> documents = List.of(createDocument(1L, "DOC-001"));
520
- when(documentService.list(0, 20)).thenReturn(documents);
521
-
522
- // ACT & ASSERT
523
- given()
524
- .when().get("/api/documents")
525
- .then()
526
- .statusCode(200)
527
- .body("$.size()", is(1))
528
- .body("[0].referenceNumber", equalTo("DOC-001"));
529
- }
530
- }
531
-
532
- @Nested
533
- @DisplayName("Tests for POST /api/documents")
534
- class CreateDocument {
535
-
536
- @Test
537
- @DisplayName("Should create document and return 201")
538
- void givenValidRequest_whenCreate_thenReturns201() {
539
- // ARRANGE
540
- Document document = createDocument(1L, "DOC-001");
541
- when(documentService.create(any())).thenReturn(document);
542
-
543
- // ACT & ASSERT
544
- given()
545
- .contentType(ContentType.JSON)
546
- .body("""
547
- {
548
- "referenceNumber": "DOC-001",
549
- "description": "Test document",
550
- "validUntil": "2030-01-01T00:00:00Z",
551
- "categories": ["test"]
552
- }
553
- """)
554
- .when().post("/api/documents")
555
- .then()
556
- .statusCode(201)
557
- .header("Location", containsString("/api/documents/1"))
558
- .body("referenceNumber", equalTo("DOC-001"));
559
- }
560
-
561
- @Test
562
- @DisplayName("Should return 400 for invalid input")
563
- void givenInvalidRequest_whenCreate_thenReturns400() {
564
- // ACT & ASSERT
565
- given()
566
- .contentType(ContentType.JSON)
567
- .body("""
568
- {
569
- "referenceNumber": "",
570
- "description": "Test"
571
- }
572
- """)
573
- .when().post("/api/documents")
574
- .then()
575
- .statusCode(400);
576
- }
577
- }
578
-
579
- private Document createDocument(Long id, String referenceNumber) {
580
- Document document = new Document();
581
- document.setId(id);
582
- document.setReferenceNumber(referenceNumber);
583
- document.setStatus(DocumentStatus.PENDING);
584
- return document;
585
- }
586
- }
587
- ```
588
-
589
- ## Integration Tests with Real Database
590
-
591
- ```java
592
- @QuarkusTest
593
- @TestProfile(IntegrationTestProfile.class)
594
- @DisplayName("Document Integration Tests")
595
- class DocumentIntegrationTest {
596
-
597
- @Test
598
- @Transactional
599
- @DisplayName("Should create and retrieve document via API")
600
- void givenNewDocument_whenCreateAndRetrieve_thenSuccessful() {
601
- // ACT - Create via API
602
- Long id = given()
603
- .contentType(ContentType.JSON)
604
- .body("""
605
- {
606
- "referenceNumber": "INT-001",
607
- "description": "Integration test",
608
- "validUntil": "2030-01-01T00:00:00Z",
609
- "categories": ["test"]
610
- }
611
- """)
612
- .when().post("/api/documents")
613
- .then()
614
- .statusCode(201)
615
- .extract().path("id");
616
-
617
- // ASSERT - Retrieve via API
618
- given()
619
- .when().get("/api/documents/" + id)
620
- .then()
621
- .statusCode(200)
622
- .body("referenceNumber", equalTo("INT-001"));
623
- }
624
- }
625
- ```
626
-
627
- ## Coverage with JaCoCo
628
-
629
- ### Maven Configuration (Complete)
630
-
631
- ```xml
632
- <plugin>
633
- <groupId>org.jacoco</groupId>
634
- <artifactId>jacoco-maven-plugin</artifactId>
635
- <version>0.8.13</version>
636
- <executions>
637
- <!-- Prepare agent for test execution -->
638
- <execution>
639
- <id>prepare-agent</id>
640
- <goals>
641
- <goal>prepare-agent</goal>
642
- </goals>
643
- </execution>
644
-
645
- <!-- Generate coverage report -->
646
- <execution>
647
- <id>report</id>
648
- <phase>verify</phase>
649
- <goals>
650
- <goal>report</goal>
651
- </goals>
652
- </execution>
653
-
654
- <!-- Enforce coverage thresholds -->
655
- <execution>
656
- <id>check</id>
657
- <goals>
658
- <goal>check</goal>
659
- </goals>
660
- <configuration>
661
- <rules>
662
- <rule>
663
- <element>BUNDLE</element>
664
- <limits>
665
- <limit>
666
- <counter>LINE</counter>
667
- <value>COVEREDRATIO</value>
668
- <minimum>0.80</minimum>
669
- </limit>
670
- <limit>
671
- <counter>BRANCH</counter>
672
- <value>COVEREDRATIO</value>
673
- <minimum>0.70</minimum>
674
- </limit>
675
- </limits>
676
- </rule>
677
- </rules>
678
- </configuration>
679
- </execution>
680
- </executions>
681
- </plugin>
682
- ```
683
-
684
- Run tests with coverage:
685
- ```bash
686
- mvn clean test
687
- mvn jacoco:report
688
- mvn jacoco:check
689
-
690
- # Report at: target/site/jacoco/index.html
691
- ```
692
-
693
- ## Test Dependencies
694
-
695
- ```xml
696
- <dependencies>
697
- <!-- Quarkus Testing -->
698
- <dependency>
699
- <groupId>io.quarkus</groupId>
700
- <artifactId>quarkus-junit5</artifactId>
701
- <scope>test</scope>
702
- </dependency>
703
- <dependency>
704
- <groupId>io.quarkus</groupId>
705
- <artifactId>quarkus-junit5-mockito</artifactId>
706
- <scope>test</scope>
707
- </dependency>
708
-
709
- <!-- Mockito -->
710
- <dependency>
711
- <groupId>org.mockito</groupId>
712
- <artifactId>mockito-core</artifactId>
713
- <scope>test</scope>
714
- </dependency>
715
-
716
- <!-- AssertJ (preferred over JUnit assertions) -->
717
- <dependency>
718
- <groupId>org.assertj</groupId>
719
- <artifactId>assertj-core</artifactId>
720
- <version>3.24.2</version>
721
- <scope>test</scope>
722
- </dependency>
723
-
724
- <!-- REST Assured -->
725
- <dependency>
726
- <groupId>io.rest-assured</groupId>
727
- <artifactId>rest-assured</artifactId>
728
- <scope>test</scope>
729
- </dependency>
730
-
731
- <!-- Camel Testing -->
732
- <dependency>
733
- <groupId>org.apache.camel.quarkus</groupId>
734
- <artifactId>camel-quarkus-junit5</artifactId>
735
- <scope>test</scope>
736
- </dependency>
737
- </dependencies>
738
- ```
739
-
740
- ## Best Practices
741
-
742
- ### Test Organization
743
- - Use `@Nested` classes to group tests by method being tested
744
- - Use `@DisplayName` for readable test descriptions visible in reports
745
- - Follow `givenX_whenY_thenZ` naming convention for test methods
746
- - Use `@BeforeEach` for common test data setup to reduce duplication
747
-
748
- ### Test Structure
749
- - Follow AAA pattern with explicit comments (`// ARRANGE`, `// ACT`, `// ASSERT`)
750
- - Use `assertDoesNotThrow` for success scenarios
751
- - Use `assertThrows` for exception scenarios with message validation
752
- - Verify exception messages match expected values using AssertJ `contains()` or `isEqualTo()`
753
-
754
- ### Test Coverage
755
- - Test happy paths for all public methods
756
- - Test null input handling
757
- - Test edge cases (empty collections, boundary values, negative IDs, blank strings)
758
- - Test exception scenarios comprehensively
759
- - Mock all external dependencies (repositories, services, Camel endpoints)
760
- - Aim for 80%+ line coverage, 70%+ branch coverage
761
-
762
- ### Assertions
763
- - **Prefer AssertJ** (`assertThat`) over JUnit assertions for value checks
764
- - Use fluent AssertJ API for readability: `assertThat(list).hasSize(3).contains(item)`
765
- - For exceptions: use JUnit `assertThrows` to capture, then AssertJ to validate the message
766
- - For non-throwing success paths: use JUnit `assertDoesNotThrow`
767
- - For collections: `extracting()`, `filteredOn()`, `containsExactly()`
768
-
769
- ### Testing Integration
770
- - Use `@QuarkusTest` for integration tests
771
- - Use `@InjectMock` to mock dependencies in Quarkus tests
772
- - Prefer REST Assured for API testing
773
- - Use `@TestProfile` for test-specific configuration
774
-
775
- ### Event-Driven Testing
776
- - Test Camel routes with `AdviceWith` and `MockEndpoint`
777
- - Use `@CamelQuarkusTest` annotation (if using standalone Camel tests)
778
- - Verify message content, headers, and routing logic
779
- - Test error handling routes separately
780
- - Mock external systems (RabbitMQ, S3, databases) in unit tests
781
-
782
- ### Camel Route Testing
783
- - Use `MockEndpoint` for asserting message flow
784
- - Use `AdviceWith` to modify routes for testing (replace endpoints with mocks)
785
- - Test message transformation and marshalling
786
- - Test exception handling and dead letter queues
787
-
788
- ### Testing Async Operations
789
- - Test CompletableFuture success and failure scenarios
790
- - Use `.join()` in tests to wait for async completion
791
- - Test exception propagation from CompletableFuture
792
- - Verify LogContext propagation to async operations
793
-
794
- ### Performance
795
- - Keep tests fast and isolated
796
- - Run tests in continuous mode: `mvn quarkus:test`
797
- - Use parameterized tests (`@ParameterizedTest`) for input variations
798
- - Build reusable test data builders or factory methods
799
-
800
- ### Quarkus-Specific
801
- - Stay on latest LTS version (Quarkus 3.x)
802
- - Test native compilation compatibility periodically
803
- - Use Quarkus test profiles for different scenarios
804
- - Leverage Quarkus dev services for local testing
805
- - Use `@InjectMock` instead of `@MockBean` (Quarkus-specific)
806
-
807
- ### Verification Best Practices
808
- - Always verify interactions on mocked dependencies
809
- - Use `verify(mock, never())` to ensure methods are NOT called in error scenarios
810
- - Use `argThat()` for complex argument matching
811
- - Verify the order of calls when it matters: `InOrder` from Mockito
1
+ ---
2
+ name: quarkus-tdd
3
+ description: Test-driven development for Quarkus 3.x LTS using JUnit 5, Mockito, REST Assured, Camel testing, and JaCoCo. Use when adding features, fixing bugs, or refactoring event-driven services.
4
+ origin: ECC
5
+ ---
6
+
7
+ # Quarkus TDD Workflow
8
+
9
+ TDD guidance for Quarkus 3.x services with 80%+ coverage (unit + integration). Optimized for event-driven architectures with Apache Camel.
10
+
11
+ ## When to Use
12
+
13
+ - New features or REST endpoints
14
+ - Bug fixes or refactors
15
+ - Adding data access logic, security rules, or reactive streams
16
+ - Testing Apache Camel routes and event handlers
17
+ - Testing event-driven services with RabbitMQ
18
+ - Testing conditional flow logic
19
+ - Validating CompletableFuture async operations
20
+ - Testing LogContext propagation
21
+
22
+ ## Workflow
23
+
24
+ 1. Write tests first (they should fail)
25
+ 2. Implement minimal code to pass
26
+ 3. Refactor with tests green
27
+ 4. Enforce coverage with JaCoCo (80%+ target)
28
+
29
+ ## Unit Tests with @Nested Organization
30
+
31
+ Follow this structured approach for comprehensive, readable tests:
32
+
33
+ ```java
34
+ @ExtendWith(MockitoExtension.class)
35
+ @DisplayName("OrderService Unit Tests")
36
+ class OrderServiceTest {
37
+
38
+ @Mock
39
+ private OrderRepository orderRepository;
40
+
41
+ @Mock
42
+ private EventService eventService;
43
+
44
+ @Mock
45
+ private FulfillmentPublisher fulfillmentPublisher;
46
+
47
+ @InjectMocks
48
+ private OrderService orderService;
49
+
50
+ private CreateOrderCommand validCommand;
51
+
52
+ @BeforeEach
53
+ void setUp() {
54
+ validCommand = new CreateOrderCommand(
55
+ "customer-123",
56
+ List.of(new OrderLine("sku-123", 2))
57
+ );
58
+ }
59
+
60
+ @Nested
61
+ @DisplayName("Tests for createOrder")
62
+ class CreateOrder {
63
+
64
+ @Test
65
+ @DisplayName("Should persist order and publish fulfillment event")
66
+ void givenValidCommand_whenCreateOrder_thenPersistsAndPublishes() {
67
+ // ARRANGE
68
+ doNothing().when(orderRepository).persist(any(Order.class));
69
+
70
+ // ACT
71
+ OrderReceipt receipt = orderService.createOrder(validCommand);
72
+
73
+ // ASSERT
74
+ assertThat(receipt).isNotNull();
75
+ assertThat(receipt.customerId()).isEqualTo("customer-123");
76
+ verify(orderRepository).persist(any(Order.class));
77
+ verify(fulfillmentPublisher).publishAsync(receipt);
78
+ verify(eventService).createSuccessEvent(receipt, "ORDER_CREATED");
79
+ }
80
+
81
+ @Test
82
+ @DisplayName("Should reject missing customer id")
83
+ void givenMissingCustomerId_whenCreateOrder_thenThrowsBadRequest() {
84
+ // ARRANGE
85
+ CreateOrderCommand invalid = new CreateOrderCommand("", validCommand.lines());
86
+
87
+ // ACT & ASSERT
88
+ WebApplicationException exception = assertThrows(
89
+ WebApplicationException.class,
90
+ () -> orderService.createOrder(invalid)
91
+ );
92
+
93
+ assertThat(exception.getResponse().getStatus()).isEqualTo(400);
94
+ verify(orderRepository, never()).persist(any(Order.class));
95
+ verify(fulfillmentPublisher, never()).publishAsync(any());
96
+ }
97
+
98
+ @Test
99
+ @DisplayName("Should record error event when persistence fails")
100
+ void givenPersistenceFailure_whenCreateOrder_thenRecordsErrorEvent() {
101
+ // ARRANGE
102
+ doThrow(new PersistenceException("database unavailable"))
103
+ .when(orderRepository).persist(any(Order.class));
104
+
105
+ // ACT & ASSERT
106
+ PersistenceException exception = assertThrows(
107
+ PersistenceException.class,
108
+ () -> orderService.createOrder(validCommand)
109
+ );
110
+
111
+ assertThat(exception.getMessage()).contains("database unavailable");
112
+ verify(eventService).createErrorEvent(
113
+ eq(validCommand),
114
+ eq("ORDER_CREATE_FAILED"),
115
+ contains("database unavailable")
116
+ );
117
+ verify(fulfillmentPublisher, never()).publishAsync(any());
118
+ }
119
+
120
+ @Test
121
+ @DisplayName("Should reject null commands")
122
+ void givenNullCommand_whenCreateOrder_thenThrowsNullPointerException() {
123
+ // ACT & ASSERT
124
+ assertThrows(
125
+ NullPointerException.class,
126
+ () -> orderService.createOrder(null)
127
+ );
128
+
129
+ verify(orderRepository, never()).persist(any(Order.class));
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ ### Key Testing Patterns
136
+
137
+ 1. **@Nested Classes**: Group tests by method being tested
138
+ 2. **@DisplayName**: Provide readable test descriptions for test reports
139
+ 3. **Naming Convention**: `givenX_whenY_thenZ` for clarity
140
+ 4. **AAA Pattern**: Explicit `// ARRANGE`, `// ACT`, `// ASSERT` comments
141
+ 5. **@BeforeEach**: Setup common test data to reduce duplication
142
+ 6. **assertDoesNotThrow**: Test success scenarios without catching exceptions
143
+ 7. **assertThrows**: Test exception scenarios with message validation using AssertJ
144
+ 8. **Comprehensive Coverage**: Test happy paths, null inputs, edge cases, exceptions
145
+ 9. **Verify Interactions**: Use Mockito `verify()` to ensure methods are called correctly
146
+ 10. **Never Verify**: Use `never()` to ensure methods are NOT called in error scenarios
147
+
148
+ ## Testing Camel Routes
149
+
150
+ ```java
151
+ @QuarkusTest
152
+ @DisplayName("Business Rules Camel Route Tests")
153
+ class BusinessRulesRouteTest {
154
+
155
+ @Inject
156
+ CamelContext camelContext;
157
+
158
+ @Inject
159
+ ProducerTemplate producerTemplate;
160
+
161
+ @InjectMock
162
+ EventService eventService;
163
+
164
+ @InjectMock
165
+ DocumentValidator documentValidator;
166
+
167
+ private BusinessRulesPayload testPayload;
168
+
169
+ @BeforeEach
170
+ void setUp() {
171
+ // ARRANGE - Test data
172
+ testPayload = new BusinessRulesPayload();
173
+ testPayload.setDocumentId(1L);
174
+ testPayload.setFlowProfile(FlowProfile.BASIC);
175
+ }
176
+
177
+ @Nested
178
+ @DisplayName("Tests for business-rules-publisher route")
179
+ class BusinessRulesPublisher {
180
+
181
+ @Test
182
+ @DisplayName("Should successfully publish message to RabbitMQ")
183
+ void givenValidPayload_whenPublish_thenMessageSentToQueue() throws Exception {
184
+ // ARRANGE
185
+ MockEndpoint mockRabbitMQ = camelContext.getEndpoint("mock:rabbitmq", MockEndpoint.class);
186
+ mockRabbitMQ.expectedMessageCount(1);
187
+
188
+ // Replace real endpoint with mock for testing
189
+ camelContext.getRouteController().stopRoute("business-rules-publisher");
190
+ AdviceWith.adviceWith(camelContext, "business-rules-publisher", advice -> {
191
+ advice.replaceFromWith("direct:business-rules-publisher");
192
+ advice.weaveByToString(".*spring-rabbitmq.*").replace().to("mock:rabbitmq");
193
+ });
194
+ camelContext.getRouteController().startRoute("business-rules-publisher");
195
+
196
+ // ACT
197
+ producerTemplate.sendBody("direct:business-rules-publisher", testPayload);
198
+
199
+ // ASSERT — body is a JSON String after .marshal().json(JsonLibrary.Jackson)
200
+ mockRabbitMQ.assertIsSatisfied(5000);
201
+
202
+ assertThat(mockRabbitMQ.getExchanges()).hasSize(1);
203
+ String body = mockRabbitMQ.getExchanges().get(0).getIn().getBody(String.class);
204
+ assertThat(body).contains("\"documentId\":1");
205
+ }
206
+
207
+ @Test
208
+ @DisplayName("Should handle marshalling to JSON")
209
+ void givenPayload_whenPublish_thenMarshalledToJson() throws Exception {
210
+ // ARRANGE
211
+ MockEndpoint mockMarshal = new MockEndpoint("mock:marshal");
212
+ camelContext.addEndpoint("mock:marshal", mockMarshal);
213
+ mockMarshal.expectedMessageCount(1);
214
+
215
+ camelContext.getRouteController().stopRoute("business-rules-publisher");
216
+ AdviceWith.adviceWith(camelContext, "business-rules-publisher", advice -> {
217
+ advice.weaveAddLast().to("mock:marshal");
218
+ });
219
+ camelContext.getRouteController().startRoute("business-rules-publisher");
220
+
221
+ // ACT
222
+ producerTemplate.sendBody("direct:business-rules-publisher", testPayload);
223
+
224
+ // ASSERT
225
+ mockMarshal.assertIsSatisfied(5000);
226
+
227
+ String body = mockMarshal.getExchanges().get(0).getIn().getBody(String.class);
228
+ assertThat(body).contains("\"documentId\":1");
229
+ assertThat(body).contains("\"flowProfile\":\"BASIC\"");
230
+ }
231
+ }
232
+
233
+ @Nested
234
+ @DisplayName("Tests for document-processing route")
235
+ class DocumentProcessing {
236
+
237
+ @Test
238
+ @DisplayName("Should route invoice to correct processor")
239
+ void givenInvoiceType_whenProcess_thenRoutesToInvoiceProcessor() throws Exception {
240
+ // ARRANGE
241
+ MockEndpoint mockInvoice = camelContext.getEndpoint("mock:invoice", MockEndpoint.class);
242
+ mockInvoice.expectedMessageCount(1);
243
+
244
+ camelContext.getRouteController().stopRoute("document-processing");
245
+ AdviceWith.adviceWith(camelContext, "document-processing", advice -> {
246
+ advice.weaveByToString(".*direct:process-invoice.*").replace().to("mock:invoice");
247
+ });
248
+ camelContext.getRouteController().startRoute("document-processing");
249
+
250
+ // ACT
251
+ producerTemplate.sendBodyAndHeader("direct:process-document",
252
+ testPayload, "documentType", "INVOICE");
253
+
254
+ // ASSERT
255
+ mockInvoice.assertIsSatisfied(5000);
256
+ }
257
+
258
+ @Test
259
+ @DisplayName("Should handle validation errors gracefully")
260
+ void givenValidationError_whenProcess_thenRoutesToErrorHandler() throws Exception {
261
+ // ARRANGE
262
+ MockEndpoint mockError = camelContext.getEndpoint("mock:error", MockEndpoint.class);
263
+ mockError.expectedMessageCount(1);
264
+
265
+ camelContext.getRouteController().stopRoute("document-processing");
266
+ AdviceWith.adviceWith(camelContext, "document-processing", advice -> {
267
+ advice.weaveByToString(".*direct:validation-error-handler.*")
268
+ .replace().to("mock:error");
269
+ });
270
+ camelContext.getRouteController().startRoute("document-processing");
271
+
272
+ // Mock validator bean to throw exception
273
+ when(documentValidator.validate(any())).thenThrow(new ValidationException("Invalid document"));
274
+
275
+ // ACT
276
+ producerTemplate.sendBody("direct:process-document", testPayload);
277
+
278
+ // ASSERT
279
+ mockError.assertIsSatisfied(5000);
280
+
281
+ Exception exception = mockError.getExchanges().get(0).getException();
282
+ assertThat(exception).isInstanceOf(ValidationException.class);
283
+ assertThat(exception.getMessage()).contains("Invalid document");
284
+ }
285
+ }
286
+ }
287
+ ```
288
+
289
+ ## Testing Event Services
290
+
291
+ ```java
292
+ @ExtendWith(MockitoExtension.class)
293
+ @DisplayName("EventService Unit Tests")
294
+ class EventServiceTest {
295
+
296
+ @Mock
297
+ private EventRepository eventRepository;
298
+
299
+ @Mock
300
+ private ObjectMapper objectMapper;
301
+
302
+ @InjectMocks
303
+ private EventService eventService;
304
+
305
+ private BusinessRulesPayload testPayload;
306
+
307
+ @BeforeEach
308
+ void setUp() {
309
+ // ARRANGE
310
+ testPayload = new BusinessRulesPayload();
311
+ testPayload.setDocumentId(1L);
312
+ }
313
+
314
+ @Nested
315
+ @DisplayName("Tests for createSuccessEvent")
316
+ class CreateSuccessEvent {
317
+
318
+ @Test
319
+ @DisplayName("Should create success event with correct attributes")
320
+ void givenValidPayload_whenCreateSuccessEvent_thenEventPersisted() throws Exception {
321
+ // ARRANGE
322
+ when(objectMapper.writeValueAsString(testPayload)).thenReturn("{\"documentId\":1}");
323
+
324
+ // ACT
325
+ assertDoesNotThrow(() ->
326
+ eventService.createSuccessEvent(testPayload, "DOCUMENT_PROCESSED"));
327
+
328
+ // ASSERT
329
+ verify(eventRepository).persist(argThat(event ->
330
+ event.getType().equals("DOCUMENT_PROCESSED") &&
331
+ event.getStatus() == EventStatus.SUCCESS &&
332
+ event.getPayload().equals("{\"documentId\":1}") &&
333
+ event.getTimestamp() != null
334
+ ));
335
+ }
336
+
337
+ @Test
338
+ @DisplayName("Should throw exception when payload is null")
339
+ void givenNullPayload_whenCreateSuccessEvent_thenThrowsException() {
340
+ // ARRANGE
341
+ Object nullPayload = null;
342
+
343
+ // ACT & ASSERT
344
+ NullPointerException exception = assertThrows(
345
+ NullPointerException.class,
346
+ () -> eventService.createSuccessEvent(nullPayload, "EVENT_TYPE")
347
+ );
348
+
349
+ assertThat(exception.getMessage()).isEqualTo("Payload cannot be null");
350
+ verify(eventRepository, never()).persist(any());
351
+ }
352
+ }
353
+
354
+ @Nested
355
+ @DisplayName("Tests for createErrorEvent")
356
+ class CreateErrorEvent {
357
+
358
+ @Test
359
+ @DisplayName("Should create error event with error message")
360
+ void givenError_whenCreateErrorEvent_thenEventPersistedWithMessage() throws Exception {
361
+ // ARRANGE
362
+ String errorMessage = "Processing failed";
363
+ when(objectMapper.writeValueAsString(testPayload)).thenReturn("{\"documentId\":1}");
364
+
365
+ // ACT
366
+ assertDoesNotThrow(() ->
367
+ eventService.createErrorEvent(testPayload, "PROCESSING_ERROR", errorMessage));
368
+
369
+ // ASSERT
370
+ verify(eventRepository).persist(argThat(event ->
371
+ event.getType().equals("PROCESSING_ERROR") &&
372
+ event.getStatus() == EventStatus.ERROR &&
373
+ event.getErrorMessage().equals(errorMessage) &&
374
+ event.getPayload().equals("{\"documentId\":1}")
375
+ ));
376
+ }
377
+
378
+ @ParameterizedTest
379
+ @DisplayName("Should reject invalid error messages")
380
+ @ValueSource(strings = {"", " "})
381
+ void givenBlankErrorMessage_whenCreateErrorEvent_thenThrowsException(String blankMessage) {
382
+ // ACT & ASSERT
383
+ IllegalArgumentException exception = assertThrows(
384
+ IllegalArgumentException.class,
385
+ () -> eventService.createErrorEvent(testPayload, "ERROR", blankMessage)
386
+ );
387
+
388
+ assertThat(exception.getMessage()).contains("Error message cannot be blank");
389
+ }
390
+ }
391
+ }
392
+ ```
393
+
394
+ ## Testing CompletableFuture
395
+
396
+ ```java
397
+ @ExtendWith(MockitoExtension.class)
398
+ @DisplayName("FileStorageService Unit Tests")
399
+ class FileStorageServiceTest {
400
+
401
+ @Mock
402
+ private S3Client s3Client;
403
+
404
+ @Mock
405
+ private ExecutorService executorService;
406
+
407
+ @InjectMocks
408
+ private FileStorageService fileStorageService;
409
+
410
+ private InputStream testInputStream;
411
+ private LogContext testLogContext;
412
+
413
+ @BeforeEach
414
+ void setUp() {
415
+ // ARRANGE
416
+ testInputStream = new ByteArrayInputStream("test content".getBytes());
417
+ testLogContext = new LogContext();
418
+ testLogContext.put("traceId", "trace-123");
419
+ }
420
+
421
+ @Nested
422
+ @DisplayName("Tests for uploadOriginalFile")
423
+ class UploadOriginalFile {
424
+
425
+ @Test
426
+ @DisplayName("Should successfully upload file and return document info")
427
+ void givenValidFile_whenUpload_thenReturnsDocumentInfo() throws Exception {
428
+ // ARRANGE
429
+ doAnswer(invocation -> {
430
+ ((Runnable) invocation.getArgument(0)).run();
431
+ return null;
432
+ }).when(executorService).execute(any(Runnable.class));
433
+
434
+ when(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class)))
435
+ .thenReturn(PutObjectResponse.builder().build());
436
+
437
+ // ACT
438
+ CompletableFuture<StoredDocumentInfo> future =
439
+ fileStorageService.uploadOriginalFile(testInputStream, 1024L,
440
+ testLogContext, InvoiceFormat.UBL);
441
+
442
+ StoredDocumentInfo result = future.join();
443
+
444
+ // ASSERT
445
+ assertThat(result).isNotNull();
446
+ assertThat(result.getPath()).isNotBlank();
447
+ assertThat(result.getSize()).isEqualTo(1024L);
448
+ assertThat(result.getUploadedAt()).isNotNull();
449
+
450
+ verify(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class));
451
+ }
452
+
453
+ @Test
454
+ @DisplayName("Should handle S3 upload failure")
455
+ void givenS3Failure_whenUpload_thenCompletableFutureFails() {
456
+ // ARRANGE — run synchronously so exception propagates through the future
457
+ doAnswer(invocation -> {
458
+ ((Runnable) invocation.getArgument(0)).run();
459
+ return null;
460
+ }).when(executorService).execute(any(Runnable.class));
461
+
462
+ when(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class)))
463
+ .thenThrow(new StorageException("S3 unavailable"));
464
+
465
+ // ACT
466
+ CompletableFuture<StoredDocumentInfo> future =
467
+ fileStorageService.uploadOriginalFile(testInputStream, 1024L,
468
+ testLogContext, InvoiceFormat.UBL);
469
+
470
+ // ASSERT
471
+ assertThatThrownBy(() -> future.join())
472
+ .isInstanceOf(CompletionException.class)
473
+ .hasCauseInstanceOf(StorageException.class)
474
+ .hasMessageContaining("S3 unavailable");
475
+ }
476
+
477
+ @Test
478
+ @DisplayName("Should propagate LogContext to async operation")
479
+ void givenLogContext_whenUpload_thenContextPropagated() throws Exception {
480
+ // ARRANGE
481
+ AtomicReference<LogContext> capturedContext = new AtomicReference<>();
482
+
483
+ doAnswer(invocation -> {
484
+ capturedContext.set(CustomLog.getCurrentContext());
485
+ ((Runnable) invocation.getArgument(0)).run();
486
+ return null;
487
+ }).when(executorService).execute(any(Runnable.class));
488
+
489
+ // ACT
490
+ fileStorageService.uploadOriginalFile(testInputStream, 1024L,
491
+ testLogContext, InvoiceFormat.UBL).join();
492
+
493
+ // ASSERT
494
+ assertThat(capturedContext.get()).isNotNull();
495
+ assertThat(capturedContext.get().get("traceId")).isEqualTo("trace-123");
496
+ }
497
+ }
498
+ }
499
+ ```
500
+
501
+ ## Resource Layer Tests (REST Assured)
502
+
503
+ ```java
504
+ @QuarkusTest
505
+ @DisplayName("DocumentResource API Tests")
506
+ class DocumentResourceTest {
507
+
508
+ @InjectMock
509
+ DocumentService documentService;
510
+
511
+ @Nested
512
+ @DisplayName("Tests for GET /api/documents")
513
+ class ListDocuments {
514
+
515
+ @Test
516
+ @DisplayName("Should return list of documents")
517
+ void givenDocumentsExist_whenList_thenReturnsOk() {
518
+ // ARRANGE
519
+ List<Document> documents = List.of(createDocument(1L, "DOC-001"));
520
+ when(documentService.list(0, 20)).thenReturn(documents);
521
+
522
+ // ACT & ASSERT
523
+ given()
524
+ .when().get("/api/documents")
525
+ .then()
526
+ .statusCode(200)
527
+ .body("$.size()", is(1))
528
+ .body("[0].referenceNumber", equalTo("DOC-001"));
529
+ }
530
+ }
531
+
532
+ @Nested
533
+ @DisplayName("Tests for POST /api/documents")
534
+ class CreateDocument {
535
+
536
+ @Test
537
+ @DisplayName("Should create document and return 201")
538
+ void givenValidRequest_whenCreate_thenReturns201() {
539
+ // ARRANGE
540
+ Document document = createDocument(1L, "DOC-001");
541
+ when(documentService.create(any())).thenReturn(document);
542
+
543
+ // ACT & ASSERT
544
+ given()
545
+ .contentType(ContentType.JSON)
546
+ .body("""
547
+ {
548
+ "referenceNumber": "DOC-001",
549
+ "description": "Test document",
550
+ "validUntil": "2030-01-01T00:00:00Z",
551
+ "categories": ["test"]
552
+ }
553
+ """)
554
+ .when().post("/api/documents")
555
+ .then()
556
+ .statusCode(201)
557
+ .header("Location", containsString("/api/documents/1"))
558
+ .body("referenceNumber", equalTo("DOC-001"));
559
+ }
560
+
561
+ @Test
562
+ @DisplayName("Should return 400 for invalid input")
563
+ void givenInvalidRequest_whenCreate_thenReturns400() {
564
+ // ACT & ASSERT
565
+ given()
566
+ .contentType(ContentType.JSON)
567
+ .body("""
568
+ {
569
+ "referenceNumber": "",
570
+ "description": "Test"
571
+ }
572
+ """)
573
+ .when().post("/api/documents")
574
+ .then()
575
+ .statusCode(400);
576
+ }
577
+ }
578
+
579
+ private Document createDocument(Long id, String referenceNumber) {
580
+ Document document = new Document();
581
+ document.setId(id);
582
+ document.setReferenceNumber(referenceNumber);
583
+ document.setStatus(DocumentStatus.PENDING);
584
+ return document;
585
+ }
586
+ }
587
+ ```
588
+
589
+ ## Integration Tests with Real Database
590
+
591
+ ```java
592
+ @QuarkusTest
593
+ @TestProfile(IntegrationTestProfile.class)
594
+ @DisplayName("Document Integration Tests")
595
+ class DocumentIntegrationTest {
596
+
597
+ @Test
598
+ @Transactional
599
+ @DisplayName("Should create and retrieve document via API")
600
+ void givenNewDocument_whenCreateAndRetrieve_thenSuccessful() {
601
+ // ACT - Create via API
602
+ Long id = given()
603
+ .contentType(ContentType.JSON)
604
+ .body("""
605
+ {
606
+ "referenceNumber": "INT-001",
607
+ "description": "Integration test",
608
+ "validUntil": "2030-01-01T00:00:00Z",
609
+ "categories": ["test"]
610
+ }
611
+ """)
612
+ .when().post("/api/documents")
613
+ .then()
614
+ .statusCode(201)
615
+ .extract().path("id");
616
+
617
+ // ASSERT - Retrieve via API
618
+ given()
619
+ .when().get("/api/documents/" + id)
620
+ .then()
621
+ .statusCode(200)
622
+ .body("referenceNumber", equalTo("INT-001"));
623
+ }
624
+ }
625
+ ```
626
+
627
+ ## Coverage with JaCoCo
628
+
629
+ ### Maven Configuration (Complete)
630
+
631
+ ```xml
632
+ <plugin>
633
+ <groupId>org.jacoco</groupId>
634
+ <artifactId>jacoco-maven-plugin</artifactId>
635
+ <version>0.8.13</version>
636
+ <executions>
637
+ <!-- Prepare agent for test execution -->
638
+ <execution>
639
+ <id>prepare-agent</id>
640
+ <goals>
641
+ <goal>prepare-agent</goal>
642
+ </goals>
643
+ </execution>
644
+
645
+ <!-- Generate coverage report -->
646
+ <execution>
647
+ <id>report</id>
648
+ <phase>verify</phase>
649
+ <goals>
650
+ <goal>report</goal>
651
+ </goals>
652
+ </execution>
653
+
654
+ <!-- Enforce coverage thresholds -->
655
+ <execution>
656
+ <id>check</id>
657
+ <goals>
658
+ <goal>check</goal>
659
+ </goals>
660
+ <configuration>
661
+ <rules>
662
+ <rule>
663
+ <element>BUNDLE</element>
664
+ <limits>
665
+ <limit>
666
+ <counter>LINE</counter>
667
+ <value>COVEREDRATIO</value>
668
+ <minimum>0.80</minimum>
669
+ </limit>
670
+ <limit>
671
+ <counter>BRANCH</counter>
672
+ <value>COVEREDRATIO</value>
673
+ <minimum>0.70</minimum>
674
+ </limit>
675
+ </limits>
676
+ </rule>
677
+ </rules>
678
+ </configuration>
679
+ </execution>
680
+ </executions>
681
+ </plugin>
682
+ ```
683
+
684
+ Run tests with coverage:
685
+ ```bash
686
+ mvn clean test
687
+ mvn jacoco:report
688
+ mvn jacoco:check
689
+
690
+ # Report at: target/site/jacoco/index.html
691
+ ```
692
+
693
+ ## Test Dependencies
694
+
695
+ ```xml
696
+ <dependencies>
697
+ <!-- Quarkus Testing -->
698
+ <dependency>
699
+ <groupId>io.quarkus</groupId>
700
+ <artifactId>quarkus-junit5</artifactId>
701
+ <scope>test</scope>
702
+ </dependency>
703
+ <dependency>
704
+ <groupId>io.quarkus</groupId>
705
+ <artifactId>quarkus-junit5-mockito</artifactId>
706
+ <scope>test</scope>
707
+ </dependency>
708
+
709
+ <!-- Mockito -->
710
+ <dependency>
711
+ <groupId>org.mockito</groupId>
712
+ <artifactId>mockito-core</artifactId>
713
+ <scope>test</scope>
714
+ </dependency>
715
+
716
+ <!-- AssertJ (preferred over JUnit assertions) -->
717
+ <dependency>
718
+ <groupId>org.assertj</groupId>
719
+ <artifactId>assertj-core</artifactId>
720
+ <version>3.24.2</version>
721
+ <scope>test</scope>
722
+ </dependency>
723
+
724
+ <!-- REST Assured -->
725
+ <dependency>
726
+ <groupId>io.rest-assured</groupId>
727
+ <artifactId>rest-assured</artifactId>
728
+ <scope>test</scope>
729
+ </dependency>
730
+
731
+ <!-- Camel Testing -->
732
+ <dependency>
733
+ <groupId>org.apache.camel.quarkus</groupId>
734
+ <artifactId>camel-quarkus-junit5</artifactId>
735
+ <scope>test</scope>
736
+ </dependency>
737
+ </dependencies>
738
+ ```
739
+
740
+ ## Best Practices
741
+
742
+ ### Test Organization
743
+ - Use `@Nested` classes to group tests by method being tested
744
+ - Use `@DisplayName` for readable test descriptions visible in reports
745
+ - Follow `givenX_whenY_thenZ` naming convention for test methods
746
+ - Use `@BeforeEach` for common test data setup to reduce duplication
747
+
748
+ ### Test Structure
749
+ - Follow AAA pattern with explicit comments (`// ARRANGE`, `// ACT`, `// ASSERT`)
750
+ - Use `assertDoesNotThrow` for success scenarios
751
+ - Use `assertThrows` for exception scenarios with message validation
752
+ - Verify exception messages match expected values using AssertJ `contains()` or `isEqualTo()`
753
+
754
+ ### Test Coverage
755
+ - Test happy paths for all public methods
756
+ - Test null input handling
757
+ - Test edge cases (empty collections, boundary values, negative IDs, blank strings)
758
+ - Test exception scenarios comprehensively
759
+ - Mock all external dependencies (repositories, services, Camel endpoints)
760
+ - Aim for 80%+ line coverage, 70%+ branch coverage
761
+
762
+ ### Assertions
763
+ - **Prefer AssertJ** (`assertThat`) over JUnit assertions for value checks
764
+ - Use fluent AssertJ API for readability: `assertThat(list).hasSize(3).contains(item)`
765
+ - For exceptions: use JUnit `assertThrows` to capture, then AssertJ to validate the message
766
+ - For non-throwing success paths: use JUnit `assertDoesNotThrow`
767
+ - For collections: `extracting()`, `filteredOn()`, `containsExactly()`
768
+
769
+ ### Testing Integration
770
+ - Use `@QuarkusTest` for integration tests
771
+ - Use `@InjectMock` to mock dependencies in Quarkus tests
772
+ - Prefer REST Assured for API testing
773
+ - Use `@TestProfile` for test-specific configuration
774
+
775
+ ### Event-Driven Testing
776
+ - Test Camel routes with `AdviceWith` and `MockEndpoint`
777
+ - Use `@CamelQuarkusTest` annotation (if using standalone Camel tests)
778
+ - Verify message content, headers, and routing logic
779
+ - Test error handling routes separately
780
+ - Mock external systems (RabbitMQ, S3, databases) in unit tests
781
+
782
+ ### Camel Route Testing
783
+ - Use `MockEndpoint` for asserting message flow
784
+ - Use `AdviceWith` to modify routes for testing (replace endpoints with mocks)
785
+ - Test message transformation and marshalling
786
+ - Test exception handling and dead letter queues
787
+
788
+ ### Testing Async Operations
789
+ - Test CompletableFuture success and failure scenarios
790
+ - Use `.join()` in tests to wait for async completion
791
+ - Test exception propagation from CompletableFuture
792
+ - Verify LogContext propagation to async operations
793
+
794
+ ### Performance
795
+ - Keep tests fast and isolated
796
+ - Run tests in continuous mode: `mvn quarkus:test`
797
+ - Use parameterized tests (`@ParameterizedTest`) for input variations
798
+ - Build reusable test data builders or factory methods
799
+
800
+ ### Quarkus-Specific
801
+ - Stay on latest LTS version (Quarkus 3.x)
802
+ - Test native compilation compatibility periodically
803
+ - Use Quarkus test profiles for different scenarios
804
+ - Leverage Quarkus dev services for local testing
805
+ - Use `@InjectMock` instead of `@MockBean` (Quarkus-specific)
806
+
807
+ ### Verification Best Practices
808
+ - Always verify interactions on mocked dependencies
809
+ - Use `verify(mock, never())` to ensure methods are NOT called in error scenarios
810
+ - Use `argThat()` for complex argument matching
811
+ - Verify the order of calls when it matters: `InOrder` from Mockito