@luanpdd/kit-mcp 1.26.0 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +168 -914
  3. package/gates/agent-no-recursive-dispatch.md +45 -11
  4. package/kit/COMANDOS.md +138 -138
  5. package/kit/README.md +76 -76
  6. package/kit/agents/advisor-researcher.md +106 -106
  7. package/kit/agents/assumptions-analyzer.md +107 -107
  8. package/kit/agents/audit-log-implementer.md +1 -1
  9. package/kit/agents/auditor-consistencia-isolamento.md +1 -1
  10. package/kit/agents/b2b-saas-architect.md +1 -1
  11. package/kit/agents/cascading-failures-auditor.md +1 -1
  12. package/kit/agents/codebase-mapper.md +768 -768
  13. package/kit/agents/crm-pipeline-implementer.md +1 -1
  14. package/kit/agents/debugger.md +813 -813
  15. package/kit/agents/detector-tenant-quente.md +1 -1
  16. package/kit/agents/evolution-go-integrator.md +1 -1
  17. package/kit/agents/example-reviewer.md +21 -21
  18. package/kit/agents/executor.md +564 -564
  19. package/kit/agents/integration-checker.md +200 -200
  20. package/kit/agents/invite-flow-implementer.md +1 -1
  21. package/kit/agents/legacy-characterizer.md +1 -1
  22. package/kit/agents/lgpd-compliance-auditor.md +1 -1
  23. package/kit/agents/multi-tenant-isolation-auditor.md +1 -1
  24. package/kit/agents/multi-tenant-rls-writer.md +1 -1
  25. package/kit/agents/nyquist-auditor.md +178 -178
  26. package/kit/agents/observability-coverage-auditor.md +1 -1
  27. package/kit/agents/org-onboarding-implementer.md +1 -1
  28. package/kit/agents/payload-capture-instrumenter.md +1 -1
  29. package/kit/agents/phase-researcher.md +696 -696
  30. package/kit/agents/plan-checker.md +272 -272
  31. package/kit/agents/planner.md +922 -922
  32. package/kit/agents/project-researcher.md +652 -652
  33. package/kit/agents/refactor-safety-auditor.md +1 -1
  34. package/kit/agents/release-pipeline-auditor.md +11 -0
  35. package/kit/agents/research-synthesizer.md +245 -245
  36. package/kit/agents/roadmapper.md +677 -677
  37. package/kit/agents/seam-finder.md +1 -1
  38. package/kit/agents/shotgun-surgery-detector.md +1 -1
  39. package/kit/agents/supabase-architect.md +14 -0
  40. package/kit/agents/supabase-branching-architect.md +562 -0
  41. package/kit/agents/supabase-cicd-pipeline-implementer.md +777 -0
  42. package/kit/agents/supabase-column-privileges-writer.md +1 -1
  43. package/kit/agents/supabase-migration-writer.md +13 -1
  44. package/kit/agents/supabase-rbac-implementer.md +1 -1
  45. package/kit/agents/supabase-rls-hardener.md +1 -1
  46. package/kit/agents/supabase-rls-writer.md +1 -1
  47. package/kit/agents/supabase-roles-implementer.md +1 -1
  48. package/kit/agents/super-admin-implementer.md +1 -1
  49. package/kit/agents/ui-auditor.md +437 -437
  50. package/kit/agents/ui-checker.md +302 -302
  51. package/kit/agents/ui-researcher.md +355 -355
  52. package/kit/agents/user-profiler.md +175 -175
  53. package/kit/agents/validador-evolucao-schema.md +1 -1
  54. package/kit/agents/verifier.md +728 -728
  55. package/kit/commands/adicionar-backlog.md +75 -75
  56. package/kit/commands/adicionar-fase.md +42 -42
  57. package/kit/commands/adicionar-tarefa.md +45 -45
  58. package/kit/commands/adicionar-testes.md +41 -41
  59. package/kit/commands/ajuda.md +21 -21
  60. package/kit/commands/atualizar.md +37 -37
  61. package/kit/commands/auditar-cascading.md +1 -1
  62. package/kit/commands/auditar-marco.md +179 -179
  63. package/kit/commands/auditar-observabilidade-cobertura.md +1 -1
  64. package/kit/commands/auditar-refactor.md +1 -1
  65. package/kit/commands/auditar-release.md +1 -1
  66. package/kit/commands/auditar-uat.md +23 -23
  67. package/kit/commands/autonomo.md +40 -40
  68. package/kit/commands/branch-pr.md +24 -24
  69. package/kit/commands/burn-rate-status.md +1 -1
  70. package/kit/commands/capturar-payloads.md +1 -1
  71. package/kit/commands/caracterizar.md +1 -1
  72. package/kit/commands/concluir-marco.md +247 -247
  73. package/kit/commands/configuracoes.md +36 -36
  74. package/kit/commands/dados-distribuidos.md +1 -1
  75. package/kit/commands/definir-perfil.md +10 -10
  76. package/kit/commands/depurar.md +190 -190
  77. package/kit/commands/detectar-duplicacao.md +1 -1
  78. package/kit/commands/discutir-fase.md +131 -131
  79. package/kit/commands/encontrar-seams.md +1 -1
  80. package/kit/commands/entrar-discord.md +17 -17
  81. package/kit/commands/estatisticas.md +18 -18
  82. package/kit/commands/example-greeting.md +33 -33
  83. package/kit/commands/executar-fase.md +58 -58
  84. package/kit/commands/expresso.md +56 -56
  85. package/kit/commands/fase-ui.md +34 -34
  86. package/kit/commands/fazer.md +57 -57
  87. package/kit/commands/fio.md +125 -125
  88. package/kit/commands/fluxos-trabalho.md +64 -64
  89. package/kit/commands/forense.md +176 -176
  90. package/kit/commands/gerenciador.md +38 -38
  91. package/kit/commands/inserir-fase.md +31 -31
  92. package/kit/commands/legacy.md +1 -1
  93. package/kit/commands/limpeza.md +17 -17
  94. package/kit/commands/listar-hipoteses-fase.md +45 -45
  95. package/kit/commands/listar-workspaces.md +18 -18
  96. package/kit/commands/load-shedding.md +1 -1
  97. package/kit/commands/mapear-codebase.md +70 -70
  98. package/kit/commands/multi-tenant.md +1 -1
  99. package/kit/commands/nota.md +33 -33
  100. package/kit/commands/novo-marco.md +43 -43
  101. package/kit/commands/novo-projeto.md +41 -41
  102. package/kit/commands/novo-workspace.md +43 -43
  103. package/kit/commands/pausar-trabalho.md +37 -37
  104. package/kit/commands/perfil-usuario.md +45 -45
  105. package/kit/commands/pesquisar-fase.md +195 -195
  106. package/kit/commands/planejar-fase.md +67 -67
  107. package/kit/commands/planejar-lacunas.md +33 -33
  108. package/kit/commands/plantar-ideia.md +25 -25
  109. package/kit/commands/progresso.md +24 -24
  110. package/kit/commands/proximo.md +30 -30
  111. package/kit/commands/publicar.md +490 -490
  112. package/kit/commands/rapido.md +35 -35
  113. package/kit/commands/reaplicar-patches.md +124 -124
  114. package/kit/commands/refactor-seguro.md +1 -1
  115. package/kit/commands/relatorio-sessao.md +19 -19
  116. package/kit/commands/remover-fase.md +31 -31
  117. package/kit/commands/remover-workspace.md +26 -26
  118. package/kit/commands/resumo-marco.md +50 -50
  119. package/kit/commands/retomar-trabalho.md +40 -40
  120. package/kit/commands/revisar-backlog.md +60 -60
  121. package/kit/commands/revisar-ui.md +32 -32
  122. package/kit/commands/revisar.md +37 -37
  123. package/kit/commands/saude.md +21 -21
  124. package/kit/commands/setup-notion.md +93 -93
  125. package/kit/commands/storytelling.md +1 -1
  126. package/kit/commands/supabase.md +1 -1
  127. package/kit/commands/sync-main.md +68 -68
  128. package/kit/commands/validar-fase.md +35 -35
  129. package/kit/commands/verificar-tarefas.md +44 -44
  130. package/kit/commands/verificar-trabalho.md +64 -64
  131. package/kit/file-manifest.json +93 -86
  132. package/kit/framework/bin/lib/commands.cjs +959 -959
  133. package/kit/framework/bin/lib/config.cjs +442 -442
  134. package/kit/framework/bin/lib/core.cjs +1230 -1230
  135. package/kit/framework/bin/lib/frontmatter.cjs +336 -336
  136. package/kit/framework/bin/lib/init.cjs +1442 -1442
  137. package/kit/framework/bin/lib/milestone.cjs +252 -252
  138. package/kit/framework/bin/lib/model-profiles.cjs +68 -68
  139. package/kit/framework/bin/lib/phase.cjs +888 -888
  140. package/kit/framework/bin/lib/profile-output.cjs +952 -952
  141. package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
  142. package/kit/framework/bin/lib/roadmap.cjs +329 -329
  143. package/kit/framework/bin/lib/security.cjs +382 -382
  144. package/kit/framework/bin/lib/state.cjs +1031 -1031
  145. package/kit/framework/bin/lib/template.cjs +222 -222
  146. package/kit/framework/bin/lib/uat.cjs +282 -282
  147. package/kit/framework/bin/lib/verify.cjs +888 -888
  148. package/kit/framework/bin/lib/workstream.cjs +491 -491
  149. package/kit/framework/bin/tools.cjs +918 -918
  150. package/kit/framework/commands/workstreams.md +63 -63
  151. package/kit/framework/references/checkpoints.md +778 -778
  152. package/kit/framework/references/continuation-format.md +249 -249
  153. package/kit/framework/references/decimal-phase-calculation.md +64 -64
  154. package/kit/framework/references/git-integration.md +295 -295
  155. package/kit/framework/references/git-planning-commit.md +38 -38
  156. package/kit/framework/references/model-profile-resolution.md +36 -36
  157. package/kit/framework/references/model-profiles.md +139 -139
  158. package/kit/framework/references/phase-argument-parsing.md +61 -61
  159. package/kit/framework/references/planning-config.md +202 -202
  160. package/kit/framework/references/questioning.md +162 -162
  161. package/kit/framework/references/tdd.md +263 -263
  162. package/kit/framework/references/ui-brand.md +160 -160
  163. package/kit/framework/references/user-profiling.md +657 -657
  164. package/kit/framework/references/verification-patterns.md +612 -612
  165. package/kit/framework/references/workstream-flag.md +58 -58
  166. package/kit/framework/templates/DEBUG.md +164 -164
  167. package/kit/framework/templates/UAT.md +265 -265
  168. package/kit/framework/templates/UI-SPEC.md +100 -100
  169. package/kit/framework/templates/VALIDATION.md +76 -76
  170. package/kit/framework/templates/claude-md.md +122 -122
  171. package/kit/framework/templates/codebase/architecture.md +185 -185
  172. package/kit/framework/templates/codebase/concerns.md +205 -205
  173. package/kit/framework/templates/codebase/conventions.md +204 -204
  174. package/kit/framework/templates/codebase/integrations.md +192 -192
  175. package/kit/framework/templates/codebase/stack.md +158 -158
  176. package/kit/framework/templates/codebase/structure.md +199 -199
  177. package/kit/framework/templates/codebase/testing.md +301 -301
  178. package/kit/framework/templates/config.json +44 -44
  179. package/kit/framework/templates/context.md +352 -352
  180. package/kit/framework/templates/continue-here.md +78 -78
  181. package/kit/framework/templates/copilot-instructions.md +7 -7
  182. package/kit/framework/templates/debug-subagent-prompt.md +91 -91
  183. package/kit/framework/templates/dev-preferences.md +20 -20
  184. package/kit/framework/templates/discovery.md +146 -146
  185. package/kit/framework/templates/discussion-log.md +63 -63
  186. package/kit/framework/templates/milestone-archive.md +123 -123
  187. package/kit/framework/templates/milestone.md +115 -115
  188. package/kit/framework/templates/phase-prompt.md +610 -610
  189. package/kit/framework/templates/planner-subagent-prompt.md +117 -117
  190. package/kit/framework/templates/project.md +186 -186
  191. package/kit/framework/templates/requirements.md +231 -231
  192. package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
  193. package/kit/framework/templates/research-project/FEATURES.md +147 -147
  194. package/kit/framework/templates/research-project/PITFALLS.md +200 -200
  195. package/kit/framework/templates/research-project/STACK.md +120 -120
  196. package/kit/framework/templates/research-project/SUMMARY.md +170 -170
  197. package/kit/framework/templates/research.md +419 -419
  198. package/kit/framework/templates/retrospective.md +54 -54
  199. package/kit/framework/templates/roadmap.md +202 -202
  200. package/kit/framework/templates/state.md +176 -176
  201. package/kit/framework/templates/summary-complex.md +59 -59
  202. package/kit/framework/templates/summary-minimal.md +41 -41
  203. package/kit/framework/templates/summary-standard.md +48 -48
  204. package/kit/framework/templates/summary.md +209 -209
  205. package/kit/framework/templates/user-profile.md +146 -146
  206. package/kit/framework/templates/user-setup.md +256 -256
  207. package/kit/framework/templates/verification-report.md +258 -258
  208. package/kit/framework/workflows/add-phase.md +112 -112
  209. package/kit/framework/workflows/add-tests.md +351 -351
  210. package/kit/framework/workflows/add-todo.md +158 -158
  211. package/kit/framework/workflows/audit-milestone.md +340 -340
  212. package/kit/framework/workflows/audit-uat.md +109 -109
  213. package/kit/framework/workflows/autonomous.md +891 -891
  214. package/kit/framework/workflows/check-todos.md +177 -177
  215. package/kit/framework/workflows/cleanup.md +152 -152
  216. package/kit/framework/workflows/complete-milestone.md +696 -696
  217. package/kit/framework/workflows/diagnose-issues.md +231 -231
  218. package/kit/framework/workflows/discovery-phase.md +289 -289
  219. package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
  220. package/kit/framework/workflows/discuss-phase.md +784 -784
  221. package/kit/framework/workflows/do.md +104 -104
  222. package/kit/framework/workflows/execute-phase.md +838 -838
  223. package/kit/framework/workflows/execute-plan.md +510 -510
  224. package/kit/framework/workflows/fast.md +102 -102
  225. package/kit/framework/workflows/forensics.md +265 -265
  226. package/kit/framework/workflows/health.md +181 -181
  227. package/kit/framework/workflows/help.md +619 -619
  228. package/kit/framework/workflows/insert-phase.md +130 -130
  229. package/kit/framework/workflows/list-phase-assumptions.md +178 -178
  230. package/kit/framework/workflows/list-workspaces.md +56 -56
  231. package/kit/framework/workflows/manager.md +362 -362
  232. package/kit/framework/workflows/map-codebase.md +377 -377
  233. package/kit/framework/workflows/milestone-summary.md +223 -223
  234. package/kit/framework/workflows/new-milestone.md +486 -486
  235. package/kit/framework/workflows/new-project.md +1159 -1159
  236. package/kit/framework/workflows/new-workspace.md +237 -237
  237. package/kit/framework/workflows/next.md +97 -97
  238. package/kit/framework/workflows/node-repair.md +92 -92
  239. package/kit/framework/workflows/note.md +156 -156
  240. package/kit/framework/workflows/pause-work.md +176 -176
  241. package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
  242. package/kit/framework/workflows/plan-phase.md +765 -765
  243. package/kit/framework/workflows/plant-seed.md +169 -169
  244. package/kit/framework/workflows/pr-branch.md +129 -129
  245. package/kit/framework/workflows/profile-user.md +450 -450
  246. package/kit/framework/workflows/progress.md +507 -507
  247. package/kit/framework/workflows/quick.md +757 -757
  248. package/kit/framework/workflows/remove-phase.md +155 -155
  249. package/kit/framework/workflows/remove-workspace.md +90 -90
  250. package/kit/framework/workflows/research-phase.md +82 -82
  251. package/kit/framework/workflows/resume-project.md +326 -326
  252. package/kit/framework/workflows/review.md +228 -228
  253. package/kit/framework/workflows/session-report.md +146 -146
  254. package/kit/framework/workflows/settings.md +283 -283
  255. package/kit/framework/workflows/ship.md +228 -228
  256. package/kit/framework/workflows/stats.md +60 -60
  257. package/kit/framework/workflows/transition.md +671 -671
  258. package/kit/framework/workflows/ui-phase.md +302 -302
  259. package/kit/framework/workflows/ui-review.md +165 -165
  260. package/kit/framework/workflows/update.md +323 -323
  261. package/kit/framework/workflows/validate-phase.md +174 -174
  262. package/kit/framework/workflows/verify-phase.md +252 -252
  263. package/kit/framework/workflows/verify-work.md +637 -637
  264. package/kit/hooks/check-update.js +118 -118
  265. package/kit/hooks/context-monitor.js +163 -163
  266. package/kit/hooks/prompt-guard.js +103 -103
  267. package/kit/hooks/statusline.js +125 -125
  268. package/kit/hooks/workflow-guard.js +101 -101
  269. package/kit/settings.json +45 -45
  270. package/kit/skills/_shared-supabase/glossary.md +10 -0
  271. package/kit/skills/ai-prompt-characterization/SKILL.md +1 -1
  272. package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +1 -1
  273. package/kit/skills/audit-log-multi-tenant/SKILL.md +1 -1
  274. package/kit/skills/b2b-saas-architecture/SKILL.md +1 -1
  275. package/kit/skills/consistencia-leitura-replica/SKILL.md +1 -1
  276. package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +1 -1
  277. package/kit/skills/escolha-modelo-consistencia/SKILL.md +1 -1
  278. package/kit/skills/evolucao-schema-compativel/SKILL.md +1 -1
  279. package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +1 -1
  280. package/kit/skills/example-skill/SKILL.md +42 -42
  281. package/kit/skills/legacy-api-only-applications/SKILL.md +1 -1
  282. package/kit/skills/legacy-characterization-tests/SKILL.md +1 -1
  283. package/kit/skills/legacy-effect-analysis/SKILL.md +1 -1
  284. package/kit/skills/legacy-extract-class/SKILL.md +1 -1
  285. package/kit/skills/legacy-programming-by-difference/SKILL.md +1 -1
  286. package/kit/skills/legacy-seams-and-test-harness/SKILL.md +1 -1
  287. package/kit/skills/legacy-shotgun-surgery/SKILL.md +1 -1
  288. package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +1 -1
  289. package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +1 -1
  290. package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +1 -1
  291. package/kit/skills/member-invite-flow/SKILL.md +1 -1
  292. package/kit/skills/member-management-react-shadcn/SKILL.md +1 -1
  293. package/kit/skills/multi-tenant-performance-scaling/SKILL.md +1 -1
  294. package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +1 -1
  295. package/kit/skills/org-onboarding-flow/SKILL.md +1 -1
  296. package/kit/skills/org-switcher-react-pattern/SKILL.md +1 -1
  297. package/kit/skills/permission-gate-react-pattern/SKILL.md +1 -1
  298. package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +1 -1
  299. package/kit/skills/pre-refactor-characterization/SKILL.md +1 -1
  300. package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +1 -1
  301. package/kit/skills/streams-eventos-cdc/SKILL.md +1 -1
  302. package/kit/skills/supabase-branching-workflow/SKILL.md +544 -0
  303. package/kit/skills/supabase-ci-cd-github-actions/SKILL.md +880 -0
  304. package/kit/skills/supabase-column-level-security/SKILL.md +1 -1
  305. package/kit/skills/supabase-config-toml-remotes/SKILL.md +807 -0
  306. package/kit/skills/supabase-custom-claims-rbac/SKILL.md +1 -1
  307. package/kit/skills/supabase-migration-repair/SKILL.md +823 -0
  308. package/kit/skills/supabase-migrations/SKILL.md +1 -1
  309. package/kit/skills/supabase-pgtap-testing/SKILL.md +1053 -0
  310. package/kit/skills/supabase-postgres-roles/SKILL.md +1 -1
  311. package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +1 -1
  312. package/kit/skills/supabase-rls-policies/SKILL.md +1 -1
  313. package/kit/skills/super-admin-platform-pattern/SKILL.md +1 -1
  314. package/kit/skills/tenant-quente-mitigacao/SKILL.md +1 -1
  315. package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +1 -1
  316. package/package.json +63 -63
  317. package/src/cli/index.js +345 -6
  318. package/src/cli/render.js +7 -0
  319. package/src/core/kit.js +216 -216
  320. package/src/core/logger.js +170 -0
  321. package/src/core/notify.js +60 -0
  322. package/src/core/reflect.js +247 -247
  323. package/src/core/reverse-sync.js +372 -372
  324. package/src/core/sync.js +418 -418
  325. package/src/core/watch.js +121 -121
  326. package/src/mcp-server/index.js +65 -2
@@ -0,0 +1,1053 @@
1
+ ---
2
+ name: supabase-pgtap-testing
3
+ description: Use ao escrever testes de database/Edge Function em Supabase — pgTAP extension (TAP…
4
+ ---
5
+
6
+ # Supabase — pgTAP & Deno Testing
7
+
8
+ ## Quando usar
9
+
10
+ Esta skill cobre **testing-shift-left para Supabase** — testar schema, RLS, PG functions e Edge Functions ANTES de deploy via CI. Dois runners canônicos:
11
+
12
+ 1. **pgTAP** (`supabase test db`) — database tests via TAP (Test Anything Protocol) Postgres extension
13
+ 2. **Deno test** (`deno test --allow-all`) — Edge Functions tests via Deno runtime nativo
14
+
15
+ Trigger phrases:
16
+
17
+ - "testes Supabase", "test Supabase", "testing Supabase"
18
+ - "pgTAP", "supabase test db", "TAP Postgres extension"
19
+ - "plan ok is throws_ok results_eq pgTAP", "has_table has_column col_type_is"
20
+ - "Deno test Edge Function", "deno test --allow-all"
21
+ - "testar RLS Supabase", "characterization tests PG function"
22
+ - "supabase/tests/", "supabase/functions/_tests/"
23
+ - "testar trigger Postgres", "testar function Postgres"
24
+ - ".env.local Deno tests", "SUPABASE_URL SUPABASE_ANON_KEY tests"
25
+ - "characterization PG legado", "refatorar function Postgres com tests"
26
+
27
+ **Use APENAS para:**
28
+
29
+ - Validar schema, columns, tipos, FKs via pgTAP (`has_table`, `has_column`, `col_type_is`)
30
+ - Validar RLS policies retornando `permission denied` quando esperado (`throws_ok 42501`)
31
+ - Validar PG functions retornando outputs esperados (`results_eq`)
32
+ - Validar constraints, triggers, defaults (`throws_ok 23502 not_null_violation`, `throws_ok 23505 unique_violation`)
33
+ - Testar Edge Functions HTTP handlers (status, response body, side effects)
34
+ - Capturar comportamento atual de PG function legada como **characterization oracle** (Feathers cap 13)
35
+ - Integrar com workflow CI `database-tests.yml` (Phase 151 CI-05) e `functions-tests.yml` (CI-06)
36
+
37
+ **NÃO use para:**
38
+
39
+ - Substituir integration tests no app layer (frontend ↔ Supabase via client SDK) — pgTAP cobre só DB; Deno tests cobrem só Edge Function isolado
40
+ - Performance tests (latência, throughput) — pgTAP é assertion-based, não benchmark; use `pgbench` ou Vegeta para load testing
41
+ - Tests em produção sem `rollback` — sempre `begin; ... rollback;` (cross-ref Anti-pattern 1)
42
+ - Substituir auditoria via `auditor-consistencia-isolamento` (v1.22) — tests validam comportamento esperado; auditor detecta anti-patterns estáticos no SQL
43
+ - Substituir mutation testing (`ai-mutation-tester`) — pgTAP é characterization; mutation testing valida que tests realmente detectam regressão
44
+
45
+ ## Princípio canônico
46
+
47
+ Três princípios canônicos:
48
+
49
+ 1. **Testing shift-left.** Cada PR valida schema + RLS + PG functions + Edge Functions ANTES do merge. Gate canônico = required status check `database-tests / build` e `functions-tests / build` enforced via branch protection (cross-ref skill `supabase-ci-cd-github-actions` Phase 151 patterns 5 e 6).
50
+
51
+ 2. **Tests são schema mutations transacionais.** Todo teste pgTAP roda dentro de `begin; ... rollback;` — assertions executam, fixtures são inseridas, RLS é exercitada, mas DB volta ao estado original. Nunca polui DB compartilhado; tests podem rodar em paralelo sem race.
52
+
53
+ 3. **pgTAP é o oracle imutável para refactor de PG legado.** Quando PG function complexa (> 100 linhas, sem tests) precisa ser refatorada, pgTAP captura comportamento atual como characterization (Feathers cap 13). Tests viram oracle congelado — qualquer mudança que altere comportamento previamente capturado falha o build. Sem characterization, refactor de PG é "edit and pray" (cross-ref skill `legacy-characterization-tests` v1.16).
54
+
55
+ ### Distinção canônica vs outros runners
56
+
57
+ | | pgTAP (`supabase test db`) | Deno test (`deno test`) | Vitest/Jest (app layer) |
58
+ |---|---|---|---|
59
+ | Escopo | Schema, RLS, PG functions, triggers | Edge Functions HTTP handlers | Frontend + cliente Supabase |
60
+ | Linguagem | SQL (TAP syntax) | TypeScript (Deno runtime) | TypeScript (Node runtime) |
61
+ | Isolamento | `begin; ... rollback;` (atomic) | Function call por test | Mocked client OR real (E2E) |
62
+ | Onde roda | Postgres local (via `supabase start`) | Deno runtime + Postgres local | Node + browser environment |
63
+ | CI workflow | `database-tests.yml` (Phase 151 CI-05) | `functions-tests.yml` (CI-06) | Custom `vitest.yml` ou `jest.yml` |
64
+ | Trigger canônico | PR (gate) | PR (gate) | PR + post-merge (canary) |
65
+
66
+ Os três runners são **complementares** — pgTAP valida DB, Deno valida Edge Function HTTP layer, Vitest/Jest valida cliente. Nenhum substitui os outros.
67
+
68
+ ## Pattern 1: pgTAP extension setup + sintaxe canônica (TEST-01)
69
+
70
+ ### Habilitar pgTAP
71
+
72
+ Em migration dedicada (uma única vez por projeto):
73
+
74
+ ```sql
75
+ -- supabase/migrations/YYYYMMDDHHmmss_enable_pgtap.sql
76
+ create extension if not exists pgtap with schema extensions;
77
+ ```
78
+
79
+ **Por que `with schema extensions`:** convenção Supabase canônica — todas extensions ficam em schema dedicado (`extensions`), separadas de `public`. Permite namespace isolation + GRANT EXECUTE granular.
80
+
81
+ ### Diretório canônico `supabase/tests/`
82
+
83
+ Supabase CLI busca automaticamente arquivos `*.sql` em `supabase/tests/` quando executa `supabase test db`:
84
+
85
+ ```text
86
+ supabase/
87
+ ├── migrations/
88
+ │ ├── 20260101000000_initial_schema.sql
89
+ │ └── 20260102000000_enable_pgtap.sql
90
+ ├── tests/
91
+ │ ├── employees_test.sql # ← descoberto automaticamente
92
+ │ ├── rls_organizations_test.sql # ← descoberto automaticamente
93
+ │ └── triggers_audit_test.sql # ← descoberto automaticamente
94
+ └── functions/
95
+ ```
96
+
97
+ Naming convention: `<entity>_test.sql` ou `<concern>_test.sql` — nome descritivo + sufixo `_test.sql`. CLI roda em ordem alfabética.
98
+
99
+ ### Estrutura canônica de um teste pgTAP
100
+
101
+ ```sql
102
+ -- supabase/tests/employees_test.sql
103
+ begin; -- inicia transação
104
+ select plan(4); -- declara 4 testes (CRÍTICO — sem plan, falha silencioso)
105
+
106
+ -- Test 1: tabela existe
107
+ select has_table('public', 'employees', 'employees table should exist');
108
+
109
+ -- Test 2: coluna existe com tipo correto
110
+ select has_column('public', 'employees', 'name', 'name column should exist');
111
+ select col_type_is('public', 'employees', 'name', 'text', 'name should be text');
112
+
113
+ -- Test 3: comportamento de seed
114
+ select results_eq(
115
+ 'select count(*) from public.employees',
116
+ 'values (3::bigint)',
117
+ 'employees count should be 3 after seed'
118
+ );
119
+
120
+ -- Test 4: constraint NOT NULL
121
+ select throws_ok(
122
+ 'insert into public.employees (name) values (null)',
123
+ '23502', -- SQLSTATE not_null_violation
124
+ 'null in name should violate not-null constraint'
125
+ );
126
+
127
+ select * from finish(); -- finaliza plano (gera summary TAP)
128
+ rollback; -- desfaz qualquer side effect
129
+ ```
130
+
131
+ **Crítico:** `plan(N)` declara o número exato de assertions executadas. Se você roda 5 mas declarou 4, pgTAP reporta `# Looks like you planned 4 tests but ran 5.` — teste falha. Se rodar menos do que planejou, idem.
132
+
133
+ ### Funções pgTAP canônicas
134
+
135
+ #### Schema/structure assertions
136
+
137
+ ```sql
138
+ -- tabela existe
139
+ select has_table('public', 'orders', 'orders table exists');
140
+
141
+ -- tabela NÃO existe
142
+ select hasnt_table('public', 'old_table', 'old_table was dropped');
143
+
144
+ -- coluna existe
145
+ select has_column('public', 'orders', 'total', 'total column exists');
146
+
147
+ -- tipo da coluna
148
+ select col_type_is('public', 'orders', 'total', 'numeric(10,2)', 'total is numeric(10,2)');
149
+
150
+ -- NOT NULL
151
+ select col_not_null('public', 'orders', 'customer_id', 'customer_id is NOT NULL');
152
+
153
+ -- DEFAULT
154
+ select col_has_default('public', 'orders', 'created_at', 'created_at has default');
155
+
156
+ -- PRIMARY KEY
157
+ select has_pk('public', 'orders', 'orders has primary key');
158
+
159
+ -- FOREIGN KEY
160
+ select has_fk('public', 'orders', 'orders.customer_id references customers');
161
+
162
+ -- INDEX
163
+ select has_index('public', 'orders', 'orders_customer_id_idx', 'index exists');
164
+ ```
165
+
166
+ #### Function/trigger assertions
167
+
168
+ ```sql
169
+ -- function existe
170
+ select has_function('public', 'calculate_total', array['uuid'], 'function exists');
171
+
172
+ -- return type
173
+ select function_returns('public', 'calculate_total', 'numeric', 'returns numeric');
174
+
175
+ -- trigger existe
176
+ select has_trigger('public', 'orders', 'set_updated_at', 'trigger exists');
177
+
178
+ -- enum value
179
+ select has_enum('public', 'order_status', array['pending', 'paid', 'shipped'], 'enum values');
180
+ ```
181
+
182
+ #### Behavioral assertions
183
+
184
+ ```sql
185
+ -- equality (simple)
186
+ select is(1 + 1, 2, '1+1 equals 2');
187
+
188
+ -- inequality
189
+ select isnt(now(), '2020-01-01'::timestamp, 'now is not 2020');
190
+
191
+ -- boolean check
192
+ select ok(exists(select 1 from public.users where id = '...'), 'user exists');
193
+
194
+ -- equality (query result)
195
+ select results_eq(
196
+ 'select id, name from public.users where active = true order by id limit 2',
197
+ $$values ('uuid-1'::uuid, 'Alice'), ('uuid-2'::uuid, 'Bob')$$,
198
+ 'active users are Alice and Bob'
199
+ );
200
+
201
+ -- inequality (query result)
202
+ select results_ne(
203
+ 'select count(*) from public.deleted_users',
204
+ 'values (0::bigint)',
205
+ 'deleted_users is not empty'
206
+ );
207
+
208
+ -- subset (query result is subset)
209
+ select set_eq(
210
+ 'select email from public.users',
211
+ $$values ('a@x.com'), ('b@x.com'), ('c@x.com')$$,
212
+ 'all users have correct emails'
213
+ );
214
+ ```
215
+
216
+ #### Error/exception assertions
217
+
218
+ ```sql
219
+ -- erro específico
220
+ select throws_ok(
221
+ 'insert into public.employees (name) values (null)',
222
+ '23502', -- SQLSTATE not_null_violation
223
+ 'null name should fail not-null constraint'
224
+ );
225
+
226
+ -- error matching pattern
227
+ select throws_like(
228
+ 'select * from public.private_table',
229
+ '%permission denied%',
230
+ 'cross-org SELECT raises permission denied'
231
+ );
232
+
233
+ -- NÃO lança erro
234
+ select lives_ok(
235
+ $$insert into public.employees (name, email) values ('Alice', 'alice@x.com')$$,
236
+ 'valid insert should succeed'
237
+ );
238
+ ```
239
+
240
+ #### Custom test helpers — fixtures
241
+
242
+ ```sql
243
+ -- helper para criar user JWT context
244
+ create or replace function tests.set_jwt_claim(claim_name text, claim_value text)
245
+ returns void
246
+ language sql
247
+ as $$
248
+ select set_config('request.jwt.claim.' || claim_name, claim_value, true);
249
+ $$;
250
+
251
+ -- uso em teste
252
+ select tests.set_jwt_claim('sub', 'uuid-of-user-1');
253
+ select tests.set_jwt_claim('user_role', 'admin');
254
+
255
+ -- agora policies que consultam auth.uid() retornam 'uuid-of-user-1'
256
+ select results_eq(
257
+ 'select count(*) from public.posts',
258
+ 'values (10::bigint)', -- esperando 10 posts visíveis para este user
259
+ 'user-1 admin sees all posts'
260
+ );
261
+ ```
262
+
263
+ ### SQLSTATE codes canônicos para `throws_ok`
264
+
265
+ | Code | Nome | Caso |
266
+ |------|------|------|
267
+ | `23502` | `not_null_violation` | INSERT/UPDATE com null em column NOT NULL |
268
+ | `23503` | `foreign_key_violation` | Referência inexistente em FK |
269
+ | `23505` | `unique_violation` | Duplicate em UNIQUE/PK |
270
+ | `23514` | `check_violation` | Falha em CHECK constraint |
271
+ | `42501` | `insufficient_privilege` | RLS bloqueando OR GRANT ausente |
272
+ | `22001` | `string_data_right_truncation` | String muito longa para column |
273
+ | `22008` | `datetime_field_overflow` | Date inválida |
274
+ | `P0001` | `raise_exception` | RAISE EXCEPTION explícito em PG function |
275
+
276
+ ## Pattern 2: `supabase test db` runner + integração CI (TEST-02)
277
+
278
+ ### Execução local
279
+
280
+ ```bash
281
+ # Garantir Postgres local rodando (sobe via Docker)
282
+ supabase start
283
+
284
+ # Rodar todos os tests em supabase/tests/
285
+ supabase test db
286
+
287
+ # Output (TAP format):
288
+ # 1..4
289
+ # ok 1 - employees table should exist
290
+ # ok 2 - name column should exist
291
+ # ok 3 - name should be text
292
+ # ok 4 - employees count should be 3 after seed
293
+ # # Pass: 4
294
+ # # Fail: 0
295
+ ```
296
+
297
+ ### Output TAP — anatomia
298
+
299
+ ```text
300
+ 1..4 ← plan (4 tests esperados)
301
+ ok 1 - test description ← test passou
302
+ not ok 2 - test description ← test falhou
303
+ # Failed test 2: 'test description' ← detalhe da falha
304
+ # got: 'actual_value'
305
+ # expected: 'expected_value'
306
+ ok 3 - test description
307
+ ok 4 - test description
308
+ # Looks like you failed 1 test of 4. ← summary
309
+ ```
310
+
311
+ Exit code:
312
+ - **0** — todos tests passaram
313
+ - **1** — algum teste falhou (CI fails → required check fails → merge bloqueado)
314
+ - **3** — erro fatal (DB não disponível, syntax error em arquivo de test)
315
+
316
+ ### Rodar test específico
317
+
318
+ ```bash
319
+ # Apenas um arquivo
320
+ supabase test db supabase/tests/employees_test.sql
321
+
322
+ # Útil para debug rápido durante TDD
323
+ ```
324
+
325
+ ### Integração CI (cross-ref Phase 151 CI-05 `database-tests.yml`)
326
+
327
+ Workflow canônico em `.github/workflows/database-tests.yml`:
328
+
329
+ ```yaml
330
+ name: database-tests
331
+ on:
332
+ pull_request:
333
+ jobs:
334
+ build:
335
+ runs-on: ubuntu-latest
336
+ steps:
337
+ - uses: actions/checkout@v4
338
+ - uses: supabase/setup-cli@v1
339
+ with:
340
+ version: latest
341
+ - run: supabase db start
342
+ - run: supabase test db
343
+ ```
344
+
345
+ **Crítico:**
346
+ - `supabase db start` (não `supabase start`) — sobe apenas Postgres, mais rápido que stack completa
347
+ - `supabase test db` consome `supabase/tests/*.sql` em ordem alfabética
348
+ - Falha em qualquer teste = exit code 1 = workflow falha = required check falha = merge bloqueado
349
+
350
+ Required check `database-tests / build` deve ser obrigatório em branch protection rule para `main` (cross-ref Phase 151 lista de required checks).
351
+
352
+ ### Caveat — fixtures via `supabase/seed.sql`
353
+
354
+ `supabase db start` aplica todas migrations + executa `supabase/seed.sql` (se existir). Use seed para popular dados de fixture compartilhados:
355
+
356
+ ```sql
357
+ -- supabase/seed.sql (rodado uma vez em db start)
358
+ insert into public.employees (id, name, email) values
359
+ ('00000000-0000-0000-0000-000000000001', 'Alice', 'alice@x.com'),
360
+ ('00000000-0000-0000-0000-000000000002', 'Bob', 'bob@x.com'),
361
+ ('00000000-0000-0000-0000-000000000003', 'Carol', 'carol@x.com');
362
+ ```
363
+
364
+ Testes referenciam UUIDs determinísticos:
365
+
366
+ ```sql
367
+ select results_eq(
368
+ 'select name from public.employees where id = ''00000000-0000-0000-0000-000000000001'' ',
369
+ $$values ('Alice')$$,
370
+ 'Alice exists'
371
+ );
372
+ ```
373
+
374
+ **Caveat:** seed roda APENAS em `db start` (não a cada test). Tests que precisam de fixture específica devem inserir dentro do `begin; ... rollback;` para isolar.
375
+
376
+ ### Caveat — paralelismo
377
+
378
+ `supabase test db` roda arquivos **sequencialmente** (não em paralelo). Cada arquivo tem `begin; ... rollback;` próprio — isolamento OK. Mas tempo total cresce linearmente com N arquivos.
379
+
380
+ Mitigação para suítes grandes (> 50 arquivos):
381
+ - Particionar em jobs paralelos no GitHub Actions (matrix strategy)
382
+ - Cada job roda subset de `supabase/tests/<group>/`
383
+
384
+ ## Pattern 3: Deno Edge Function tests (TEST-03)
385
+
386
+ ### Estrutura canônica
387
+
388
+ ```text
389
+ supabase/
390
+ └── functions/
391
+ ├── hello/
392
+ │ └── index.ts # Edge Function code
393
+ ├── create-invite/
394
+ │ └── index.ts
395
+ └── _tests/ # ← convenção (_ prefix evita conflito com fn name)
396
+ ├── hello_test.ts
397
+ └── create-invite_test.ts
398
+ ```
399
+
400
+ Naming convention: `<fn-name>_test.ts` em `supabase/functions/_tests/`.
401
+
402
+ ### Anatomy de um teste Deno
403
+
404
+ ```typescript
405
+ // supabase/functions/_tests/hello_test.ts
406
+ import { assertEquals } from "https://deno.land/std@0.221.0/assert/mod.ts";
407
+
408
+ Deno.test("hello function returns 200", async () => {
409
+ const response = await fetch("http://127.0.0.1:54321/functions/v1/hello", {
410
+ method: "POST",
411
+ headers: {
412
+ "Authorization": `Bearer ${Deno.env.get("SUPABASE_ANON_KEY")}`,
413
+ "Content-Type": "application/json",
414
+ },
415
+ body: JSON.stringify({ name: "World" }),
416
+ });
417
+
418
+ assertEquals(response.status, 200);
419
+ const data = await response.json();
420
+ assertEquals(data.message, "Hello World!");
421
+ });
422
+
423
+ Deno.test("hello function rejects empty body", async () => {
424
+ const response = await fetch("http://127.0.0.1:54321/functions/v1/hello", {
425
+ method: "POST",
426
+ headers: {
427
+ "Authorization": `Bearer ${Deno.env.get("SUPABASE_ANON_KEY")}`,
428
+ "Content-Type": "application/json",
429
+ },
430
+ body: JSON.stringify({}),
431
+ });
432
+
433
+ assertEquals(response.status, 400);
434
+ });
435
+ ```
436
+
437
+ ### Execução local
438
+
439
+ ```bash
440
+ # Sobe stack completa (Postgres + Auth + Edge Functions runtime + Storage)
441
+ supabase start
442
+
443
+ # Rodar tests
444
+ deno test --allow-all supabase/functions/_tests/ --env-file .env.local
445
+ ```
446
+
447
+ `--allow-all` permite acesso file system + network + env vars (Deno é sandboxed por default).
448
+
449
+ `--env-file .env.local` carrega env vars de arquivo local. Sem essa flag, `Deno.env.get(...)` retorna `undefined`.
450
+
451
+ ### Estrutura `.env.local` (gitignored)
452
+
453
+ ```bash
454
+ # supabase/functions/.env.local (gitignored — gerado dinamicamente)
455
+ SUPABASE_URL=http://127.0.0.1:54321
456
+ SUPABASE_ANON_KEY=eyJ...local-anon-key...
457
+ SUPABASE_SERVICE_ROLE_KEY=eyJ...local-service-role-key...
458
+ ```
459
+
460
+ **Caveat:** `supabase start` imprime essas keys no output. Capture-as automaticamente:
461
+
462
+ ```bash
463
+ # Linux/macOS
464
+ supabase status -o env > supabase/functions/.env.local
465
+
466
+ # Windows PowerShell
467
+ supabase status -o env | Out-File -Encoding utf8 supabase/functions/.env.local
468
+ ```
469
+
470
+ Atualizar `.env.local` a cada `supabase start` — keys mudam se containers forem recriados.
471
+
472
+ ### Importar handler diretamente (test unitário)
473
+
474
+ Alternativa ao test via HTTP — importar handler direto:
475
+
476
+ ```typescript
477
+ // supabase/functions/_tests/hello_unit_test.ts
478
+ import { assertEquals } from "https://deno.land/std@0.221.0/assert/mod.ts";
479
+
480
+ // Assumindo hello/index.ts exporta handler
481
+ import { handler } from "../hello/index.ts";
482
+
483
+ Deno.test("handler returns 200 for valid input", async () => {
484
+ const req = new Request("http://localhost/hello", {
485
+ method: "POST",
486
+ headers: { "Content-Type": "application/json" },
487
+ body: JSON.stringify({ name: "World" }),
488
+ });
489
+
490
+ const res = await handler(req);
491
+ assertEquals(res.status, 200);
492
+
493
+ const data = await res.json();
494
+ assertEquals(data.message, "Hello World!");
495
+ });
496
+
497
+ Deno.test("handler returns 400 for invalid input", async () => {
498
+ const req = new Request("http://localhost/hello", {
499
+ method: "POST",
500
+ headers: { "Content-Type": "application/json" },
501
+ body: JSON.stringify({}),
502
+ });
503
+
504
+ const res = await handler(req);
505
+ assertEquals(res.status, 400);
506
+ });
507
+ ```
508
+
509
+ **Vantagens vs HTTP test:**
510
+ - Mais rápido (sem network roundtrip)
511
+ - Testa lógica isolada do handler
512
+ - Não requer `supabase start` (apenas Deno runtime)
513
+
514
+ **Desvantagens:**
515
+ - Não exercita auth middleware (JWT verification)
516
+ - Não exercita rate limiting / CORS / Deno.serve setup
517
+ - Mais frágil a mudanças no boilerplate da Edge Function
518
+
519
+ **Canônico:** combinar AMBOS — unit tests do handler (rápidos) + smoke test via HTTP (validação E2E mínima).
520
+
521
+ ### Integração CI (cross-ref Phase 151 CI-06 `functions-tests.yml`)
522
+
523
+ Workflow canônico em `.github/workflows/functions-tests.yml`:
524
+
525
+ ```yaml
526
+ name: functions-tests
527
+ on:
528
+ pull_request:
529
+ jobs:
530
+ build:
531
+ runs-on: ubuntu-latest
532
+ steps:
533
+ - uses: actions/checkout@v4
534
+ - uses: supabase/setup-cli@v1
535
+ with:
536
+ version: latest
537
+ - uses: denoland/setup-deno@v2
538
+ with:
539
+ deno-version: latest
540
+ - run: supabase start
541
+ - name: Generate .env.local
542
+ run: supabase status -o env > supabase/functions/.env.local
543
+ - run: deno test --allow-all supabase/functions/_tests/ --env-file supabase/functions/.env.local
544
+ ```
545
+
546
+ Required check `functions-tests / build` deve ser obrigatório em branch protection rule para `main`.
547
+
548
+ ### Caveat — fixtures DB para Edge Function tests
549
+
550
+ Edge Function pode escrever em DB. Tests precisam:
551
+
552
+ 1. Inserir fixtures via SQL antes do test
553
+ 2. Limpar após o test (ou cada test recria estado)
554
+
555
+ ```typescript
556
+ import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
557
+
558
+ const supabase = createClient(
559
+ Deno.env.get("SUPABASE_URL")!,
560
+ Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!, // bypass RLS para setup
561
+ );
562
+
563
+ Deno.test("create-invite writes to org_invites table", async () => {
564
+ // Setup
565
+ await supabase.from("organizations").insert({ id: "org-1", name: "Test" });
566
+
567
+ // Act
568
+ const { data } = await supabase.functions.invoke("create-invite", {
569
+ body: { org_id: "org-1", email: "new@x.com", role: "member" },
570
+ });
571
+
572
+ // Assert
573
+ assertEquals(data.success, true);
574
+
575
+ const { data: invites } = await supabase
576
+ .from("org_invites")
577
+ .select("*")
578
+ .eq("org_id", "org-1");
579
+ assertEquals(invites?.length, 1);
580
+
581
+ // Teardown
582
+ await supabase.from("org_invites").delete().eq("org_id", "org-1");
583
+ await supabase.from("organizations").delete().eq("id", "org-1");
584
+ });
585
+ ```
586
+
587
+ **Caveat:** ao contrário de pgTAP (transactional rollback automático), Deno tests precisam de cleanup MANUAL. Considere padrão `beforeEach/afterEach`:
588
+
589
+ ```typescript
590
+ async function cleanup() {
591
+ await supabase.from("org_invites").delete().neq("id", "00000000-0000-0000-0000-000000000000");
592
+ await supabase.from("organizations").delete().neq("id", "00000000-0000-0000-0000-000000000000");
593
+ }
594
+
595
+ Deno.test({
596
+ name: "create-invite test",
597
+ async fn() {
598
+ await cleanup(); // estado limpo antes
599
+ // ... test body ...
600
+ await cleanup(); // teardown
601
+ },
602
+ });
603
+ ```
604
+
605
+ Ou usar `Deno.test.beforeEach` (Deno 1.40+):
606
+
607
+ ```typescript
608
+ Deno.test.beforeEach(cleanup);
609
+ Deno.test.afterEach(cleanup);
610
+
611
+ Deno.test("create-invite writes to org_invites", async () => {
612
+ // ...
613
+ });
614
+ ```
615
+
616
+ ## Pattern 4: Cross-ref legacy-characterizer — pgTAP como mecanismo de characterization (TEST-04)
617
+
618
+ ### Princípio canônico (Feathers cap 13)
619
+
620
+ **Legacy code = código sem testes** (definição Feathers, não estética). PG function/trigger/policy com > 100 linhas e sem tests = legacy, mesmo se escrita ontem.
621
+
622
+ Antes de refatorar PG function legada, **characterize first** — capture comportamento atual como pgTAP tests. Tests viram oracle imutável. Refactor preserva oracle. Bug fix vem em PR separado depois.
623
+
624
+ ### Workflow canônico de characterization de PG function
625
+
626
+ ```text
627
+ 1. Identificar PG function alvo do refactor
628
+ Exemplo: public.calculate_invoice_total(uuid) — 200 linhas, sem tests
629
+
630
+ 2. Inventariar inputs/outputs
631
+ Inputs:
632
+ - parâmetro: invoice_id uuid
633
+ - reads: invoices, line_items, discounts, taxes (4 tabelas)
634
+ - reads globais: current_setting('app.tax_rate')
635
+ Outputs:
636
+ - return: numeric(10,2)
637
+ - side effects: insert em audit_log + update em invoices.calculated_at
638
+
639
+ 3. Para cada grupo de equivalência (5+ inputs):
640
+ a. Construir input (fixture)
641
+ b. Executar function REAL — sem mocks ainda
642
+ c. Capturar output completo + side effects
643
+ d. REVISAR linha por linha — marcar bugs conhecidos como comments
644
+ e. Salvar como pgTAP assertion
645
+
646
+ 4. Escrever pgTAP test file
647
+ - plan(N) declara N grupos × M assertions cada
648
+ - results_eq para return value
649
+ - results_eq para side effects (linhas em audit_log)
650
+ - throws_ok para edge cases que esperam erro
651
+
652
+ 5. supabase test db — TODOS verdes = baseline estabelecido
653
+
654
+ 6. Refactor pode começar
655
+ ```
656
+
657
+ ### Exemplo canônico
658
+
659
+ ```sql
660
+ -- supabase/tests/calculate_invoice_total_characterization_test.sql
661
+ begin;
662
+ select plan(10);
663
+
664
+ -- ===== GRUPO 1: invoice típica com 1 line item =====
665
+ -- Setup
666
+ insert into public.invoices (id, customer_id, status) values
667
+ ('inv-001', 'cust-001', 'pending');
668
+ insert into public.line_items (invoice_id, sku, qty, unit_price) values
669
+ ('inv-001', 'SKU-1', 2, 50.00);
670
+
671
+ -- Test 1: return value
672
+ select results_eq(
673
+ $$select public.calculate_invoice_total('inv-001'::uuid)$$,
674
+ $$values (100.00::numeric)$$,
675
+ 'GROUP 1: typical invoice 1 line item — total 100.00'
676
+ );
677
+
678
+ -- Test 2: side effect — audit_log row created
679
+ select results_eq(
680
+ $$select event_type, target_id from public.audit_log where target_id = 'inv-001'::uuid$$,
681
+ $$values ('invoice_total_calculated'::text, 'inv-001'::uuid)$$,
682
+ 'GROUP 1: audit_log row created'
683
+ );
684
+
685
+ -- ===== GRUPO 2: invoice com discount =====
686
+ insert into public.invoices (id, customer_id, status) values
687
+ ('inv-002', 'cust-001', 'pending');
688
+ insert into public.line_items (invoice_id, sku, qty, unit_price) values
689
+ ('inv-002', 'SKU-2', 1, 200.00);
690
+ insert into public.discounts (invoice_id, percentage) values
691
+ ('inv-002', 10.0); -- 10% discount
692
+
693
+ select results_eq(
694
+ $$select public.calculate_invoice_total('inv-002'::uuid)$$,
695
+ $$values (180.00::numeric)$$,
696
+ 'GROUP 2: invoice with 10% discount — total 180.00'
697
+ );
698
+
699
+ -- ===== GRUPO 3: invoice vazia (BUG #1 conhecido — retorna 0, deveria raise) =====
700
+ insert into public.invoices (id, customer_id, status) values
701
+ ('inv-003', 'cust-001', 'pending');
702
+ -- NO line_items inseridos
703
+
704
+ -- BUG #1: function retorna 0 para invoice vazia, deveria raise 'invalid_invoice'
705
+ -- Preservar comportamento bugado durante refactor; fix em PR separado.
706
+ select results_eq(
707
+ $$select public.calculate_invoice_total('inv-003'::uuid)$$,
708
+ $$values (0.00::numeric)$$,
709
+ 'GROUP 3 [BUG #1]: empty invoice returns 0 (should raise — preserved as oracle)'
710
+ );
711
+
712
+ -- ===== GRUPO 4: invoice inexistente — espera erro =====
713
+ select throws_ok(
714
+ $$select public.calculate_invoice_total('00000000-0000-0000-0000-000000000000'::uuid)$$,
715
+ 'P0001',
716
+ 'GROUP 4: non-existent invoice raises P0001'
717
+ );
718
+
719
+ -- ===== GRUPO 5: invoice com NEG line items (edge case histórico) =====
720
+ insert into public.invoices (id, customer_id, status) values
721
+ ('inv-005', 'cust-001', 'pending');
722
+ insert into public.line_items (invoice_id, sku, qty, unit_price) values
723
+ ('inv-005', 'SKU-3', -1, 100.00); -- qty NEGATIVO (devolução)
724
+
725
+ select results_eq(
726
+ $$select public.calculate_invoice_total('inv-005'::uuid)$$,
727
+ $$values (-100.00::numeric)$$,
728
+ 'GROUP 5: negative qty produces negative total (refund flow)'
729
+ );
730
+
731
+ -- ... outros grupos (boundary valid, side-effect heavy, etc.) ...
732
+
733
+ select * from finish();
734
+ rollback;
735
+ ```
736
+
737
+ ### Bugs preservados como comments
738
+
739
+ **Crítico:** characterization captura o que código FAZ, não o que DEVERIA fazer. Bugs conhecidos viram comments inline (`-- BUG #X: deveria Y, é Z`). Refactor preserva o oracle (incluindo bugs). Bug fix vem em PR separado **depois** do refactor, com seu próprio teste.
740
+
741
+ Exemplo do test acima: GROUP 3 marca `BUG #1` — função retorna 0 para invoice vazia em vez de raise exception. Refactor manterá esse comportamento. PR de bug fix subsequente alterará a assertion para `throws_ok` e fixará a função.
742
+
743
+ ### Behavioral coverage check (mutation testing — recomendado)
744
+
745
+ pgTAP cobre o **que** o código faz; mutation testing valida que tests **detectam regressão**. Para PG functions críticas:
746
+
747
+ 1. Rodar `supabase test db` — baseline verde
748
+ 2. Aplicar mutation (manualmente — não há tool padrão para PG mutation; use search-replace deliberado):
749
+ - Trocar `+` por `-` na cálculo
750
+ - Trocar `>` por `>=` em CHECK
751
+ - Comentar `RAISE EXCEPTION` em edge case
752
+ 3. Rodar `supabase test db` — esperado vermelho. Se ficar verde, characterization tem ponto cego.
753
+ 4. Adicionar test que cobre o ponto cego
754
+ 5. Reverter mutation; suite volta a verde
755
+
756
+ Cross-ref skill `ai-mutation-tester` (v1.20) — pattern análogo para JavaScript/TypeScript.
757
+
758
+ ### Quando pgTAP characterization é mandatório
759
+
760
+ Aplicar pgTAP characterization (cross-ref skill `pre-refactor-characterization` v1.18) ANTES de refactor de:
761
+
762
+ - PG function > 100 linhas sem tests existentes
763
+ - PG function consumida por > 3 callers (alto blast radius se regredir)
764
+ - RLS policy complexa (> 5 OR conditions, ou referencia auth.uid() + claim + subquery)
765
+ - Trigger BEFORE/AFTER que muta dados em outras tabelas
766
+ - Function exposta via Edge Function ou PostgREST RPC (contrato externo)
767
+
768
+ ## Anti-patterns
769
+
770
+ ### Anti-pattern 1: Tests sem `rollback`
771
+
772
+ **Errado:**
773
+
774
+ ```sql
775
+ -- supabase/tests/employees_test.sql
776
+ select plan(2);
777
+
778
+ select has_table('public', 'employees', 'employees exists');
779
+
780
+ insert into public.employees (name) values ('Test User'); -- SEM begin/rollback!
781
+
782
+ select results_eq(
783
+ 'select count(*) from public.employees where name = ''Test User''',
784
+ 'values (1::bigint)',
785
+ 'inserted user found'
786
+ );
787
+
788
+ select * from finish();
789
+ ```
790
+
791
+ **Por quê:** `INSERT` sem `begin; ... rollback;` é **persistido** no DB local. Após rodar `supabase test db`:
792
+
793
+ - Teste pode "passar" na primeira run mas falhar na segunda (`count` já não é 1, é 2)
794
+ - DB local poluído com dados sintéticos — afeta outros tests
795
+ - Se rodar em CI, novo container DB cada run = OK; mas localmente é problema
796
+
797
+ **Certo:**
798
+
799
+ ```sql
800
+ -- supabase/tests/employees_test.sql
801
+ begin; -- inicia transação
802
+ select plan(2);
803
+
804
+ select has_table('public', 'employees', 'employees exists');
805
+
806
+ insert into public.employees (name) values ('Test User');
807
+
808
+ select results_eq(
809
+ 'select count(*) from public.employees where name = ''Test User''',
810
+ 'values (1::bigint)',
811
+ 'inserted user found'
812
+ );
813
+
814
+ select * from finish();
815
+ rollback; -- desfaz INSERT
816
+ ```
817
+
818
+ `rollback` é **incondicional** — mesmo se `finish()` falhar, transação aborta no rollback. Estado DB inalterado.
819
+
820
+ ### Anti-pattern 2: Esquecer `plan(N)` — testes silenciosos
821
+
822
+ **Errado:**
823
+
824
+ ```sql
825
+ begin;
826
+ -- SEM plan() declarado
827
+
828
+ select has_table('public', 'employees', 'test 1');
829
+ select has_column('public', 'employees', 'name', 'test 2');
830
+ select col_type_is('public', 'employees', 'name', 'text', 'test 3');
831
+
832
+ select * from finish();
833
+ rollback;
834
+ ```
835
+
836
+ **Por quê:** sem `plan(N)`, pgTAP **não sabe** quantos tests esperar. Output ainda mostra `ok 1`, `ok 2`, `ok 3` mas:
837
+
838
+ - TAP harness pode interpretar como "0 tests planned, 3 executed" → falha silenciosa em CI
839
+ - `finish()` reporta `1..0` (planejados zero) — exit code pode ser 0 (sucesso) mesmo se tests **falharem** silenciosamente
840
+ - Adicionar test novo não é notado (deveria mudar `plan(N)` para `plan(N+1)`, mas como não tem plan, esquecer é invisível)
841
+
842
+ **Certo:**
843
+
844
+ ```sql
845
+ begin;
846
+ select plan(3); -- DECLARA EXPLICITAMENTE 3 tests
847
+
848
+ select has_table('public', 'employees', 'test 1');
849
+ select has_column('public', 'employees', 'name', 'test 2');
850
+ select col_type_is('public', 'employees', 'name', 'text', 'test 3');
851
+
852
+ select * from finish();
853
+ rollback;
854
+ ```
855
+
856
+ `plan(N)` é **obrigatório**. Se rodar 4 mas declarou 3, pgTAP reporta `# Looks like you planned 3 tests but ran 4.` — falha.
857
+
858
+ Manter `plan(N)` atualizado é parte do contrato — se adiciona test, atualiza plan.
859
+
860
+ ### Anti-pattern 3: Tests Deno sem `.env.local`
861
+
862
+ **Errado:**
863
+
864
+ ```bash
865
+ # rodar tests sem env file
866
+ deno test --allow-all supabase/functions/_tests/
867
+ ```
868
+
869
+ ```typescript
870
+ // dentro do test
871
+ const response = await fetch(`${Deno.env.get("SUPABASE_URL")}/functions/v1/hello`);
872
+ // Deno.env.get("SUPABASE_URL") retorna undefined
873
+ // fetch("undefined/functions/v1/hello") = URL inválida
874
+ // Error: Invalid URL: 'undefined/functions/v1/hello'
875
+ ```
876
+
877
+ **Por quê:** sem `--env-file`, `Deno.env.get(...)` retorna `undefined`. `fetch` com URL inválida lança erro genérico. Mensagem confusa — dev pensa que tem bug no handler, na verdade é env config.
878
+
879
+ **Certo:**
880
+
881
+ ```bash
882
+ # Gerar .env.local
883
+ supabase status -o env > supabase/functions/.env.local
884
+
885
+ # Rodar com env file
886
+ deno test --allow-all supabase/functions/_tests/ --env-file supabase/functions/.env.local
887
+ ```
888
+
889
+ ```typescript
890
+ // Defensive: assert env vars existem (fail-fast com mensagem clara)
891
+ const SUPABASE_URL = Deno.env.get("SUPABASE_URL");
892
+ const SUPABASE_ANON_KEY = Deno.env.get("SUPABASE_ANON_KEY");
893
+
894
+ if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
895
+ throw new Error(
896
+ "Missing SUPABASE_URL or SUPABASE_ANON_KEY. " +
897
+ "Run: supabase status -o env > supabase/functions/.env.local"
898
+ );
899
+ }
900
+ ```
901
+
902
+ Em CI, geração de `.env.local` deve ser **step explícito** do workflow (cross-ref Phase 151 CI-06):
903
+
904
+ ```yaml
905
+ - name: Generate .env.local
906
+ run: supabase status -o env > supabase/functions/.env.local
907
+ - run: deno test --allow-all supabase/functions/_tests/ --env-file supabase/functions/.env.local
908
+ ```
909
+
910
+ ### Anti-pattern 4: Tratar pgTAP como "testes completos"
911
+
912
+ **Errado:** time considera que pgTAP + Deno tests cobrem TODO o sistema → para CI: "se database-tests e functions-tests passam, deploy é seguro".
913
+
914
+ **Por quê:** pgTAP cobre **schema e PG logic**; Deno tests cobrem **Edge Function HTTP handlers**. NENHUM dos dois cobre:
915
+
916
+ - Cliente frontend interagindo com Supabase via `@supabase/supabase-js` (race conditions, retry, optimistic UI)
917
+ - Comportamento Realtime (subscriptions, broadcast, presence)
918
+ - Auth flows end-to-end (signup → email confirm → first login → setup wizard)
919
+ - RLS visto do ponto de vista do cliente autenticado (JWT real, não simulado via `set_config`)
920
+ - Performance / latência sob carga
921
+ - Edge cases de browser (CORS, cookies, localStorage)
922
+
923
+ **Certo:** treat pgTAP + Deno como **two layers of defense-in-depth**, não substituto de integration tests:
924
+
925
+ ```text
926
+ Pirâmide de testes Supabase (canônica):
927
+
928
+ /\
929
+ /E2E\ ← Playwright/Cypress: 5-10 critical user journeys
930
+ /------\
931
+ / INT. \ ← Vitest/Jest com Supabase real: auth flow, multi-tenant
932
+ /----------\
933
+ / DENO TESTS \ ← pattern 3: Edge Functions HTTP handlers
934
+ /--------------\
935
+ / pgTAP \ ← pattern 1: schema + RLS + PG functions
936
+ /------------------\
937
+ | Unit tests app | ← Vitest/Jest cliente: componentes React, helpers
938
+ ```
939
+
940
+ Cada camada cobre **incidentes diferentes**:
941
+ - pgTAP: regressão em schema/policy/function
942
+ - Deno: regressão em Edge Function logic
943
+ - Integration: regressão em cliente ↔ Supabase wire
944
+ - E2E: regressão em user journey
945
+
946
+ Sem todas as camadas, gaps de cobertura existem. CI deve ter **separate required checks** por camada.
947
+
948
+ ### Anti-pattern 5: Snapshot pgTAP sem revisão (characterization shortcut)
949
+
950
+ **Errado:** copy-paste output bruto de query como `results_eq` expected sem inspecionar — "se runs verde, está OK".
951
+
952
+ ```sql
953
+ -- characterization sem revisão
954
+ select results_eq(
955
+ $$select public.complex_function('input-1')$$,
956
+ $$values ('a', '2026-05-11T10:23:45.123456Z'::timestamptz, 'token_abc123xyz', 42, '00000000-1234-5678-9abc-def012345678'::uuid)$$,
957
+ 'preserved as-is'
958
+ );
959
+ ```
960
+
961
+ **Por quê:** output bruto pode incluir:
962
+
963
+ - **PII** (emails, names) — vazam para git history
964
+ - **Tokens/secrets** — vazam para git history (irreversível)
965
+ - **Timestamps voláteis** — test será flaky em run subsequente
966
+ - **UUIDs locais** — gerados por `gen_random_uuid()`, mudam a cada run
967
+
968
+ CI fica "verde" porque snapshot bate consigo mesmo da última run, mas:
969
+ - Adicionar fixture nova quebra (timestamp diferente)
970
+ - PR review humano não detecta bugs (não inspecionou output)
971
+ - Secrets podem ser commitados sem aviso
972
+
973
+ **Certo:** revisão linha-por-linha + sanitização antes de salvar (cross-ref skill `legacy-characterization-tests` Pattern 6):
974
+
975
+ ```sql
976
+ -- characterization com sanitização determinística
977
+ begin;
978
+ -- 1. Fixar clock para determinismo
979
+ select set_config('app.fake_now', '2026-05-11T10:00:00Z', true);
980
+
981
+ -- 2. Fixar UUIDs determinísticos via fixture
982
+ insert into public.things (id, name, created_at) values
983
+ ('00000000-0000-0000-0000-000000000001', 'Test', '2026-05-11T10:00:00Z'::timestamptz);
984
+
985
+ -- 3. Snapshot review:
986
+ -- - Email 'alice@x.com' está no fixture (não real PII)
987
+ -- - Timestamp '2026-05-11T10:00:00Z' está congelado (fake clock)
988
+ -- - UUID '00000000...001' está determinístico (fixture)
989
+ -- - Token 'token_test_abc' está sanitized (não real)
990
+ select results_eq(
991
+ $$select id, name, created_at from public.things where id = '00000000-0000-0000-0000-000000000001'::uuid$$,
992
+ $$values ('00000000-0000-0000-0000-000000000001'::uuid, 'Test'::text, '2026-05-11T10:00:00Z'::timestamptz)$$,
993
+ 'thing fixture matches characterization oracle (reviewed 2026-05-11)'
994
+ );
995
+
996
+ select * from finish();
997
+ rollback;
998
+ ```
999
+
1000
+ **Crítico:** comentar quando snapshot foi revisado + por quem. Commit message: "characterize complex_function — reviewed 2026-05-11, bugs noted in comments".
1001
+
1002
+ ## Cross-suite integration (v1.27)
1003
+
1004
+ Esta skill é **complemento essencial** da skill Phase 151 `supabase-ci-cd-github-actions`:
1005
+
1006
+ - Phase 151 estabelece workflows CI (`database-tests.yml` + `functions-tests.yml`) que executam `supabase test db` + `deno test --allow-all`
1007
+ - Phase 152 (ESTA) detalha a **sintaxe canônica** dos tests que esses workflows consomem
1008
+ - Forward-ref de Phase 151 é fechado por esta skill (Pattern 5 da Phase 151 referenciava "skill futura `supabase-pgtap-testing` Phase 152")
1009
+
1010
+ Cross-refs com skills existentes v1.x:
1011
+
1012
+ - **`legacy-characterization-tests` (v1.16)** — pgTAP é o mecanismo canônico para implementar characterization (cap 13 Feathers) em PG legado; Pattern 4 desta skill é especialização para Postgres
1013
+ - **`pre-refactor-characterization` (v1.18)** — gate auto-trigger que bloqueia refactor sem characterization; pgTAP satisfaz a pré-condição para PG functions
1014
+ - **`ai-mutation-tester` (v1.20)** — mutation testing complementar; valida que pgTAP detecta regressão
1015
+ - **`supabase-database-functions`** — PG functions criadas seguindo essa skill são prime target de pgTAP (SECURITY INVOKER + SET search_path = '' são testáveis)
1016
+ - **`supabase-rls-policies` (v1.23)** — RLS policies validadas via `throws_like '%permission denied%'` + `results_eq` com diferentes JWT claims
1017
+ - **`supabase-edge-functions`** — Edge Functions cobertas por Deno tests (Pattern 3)
1018
+ - **`supabase-postgres-roles` (v1.26)** — testes de roles validáveis via `has_role`, `set role test_role; ...; reset role`
1019
+ - **`supabase-custom-claims-rbac` (v1.25)** — testes de auth hook validáveis via fixture JWT + `set_config('request.jwt.claim.user_role', 'admin', true)`
1020
+
1021
+ Base para agent futuro v1.27:
1022
+
1023
+ - **`supabase-cicd-pipeline-implementer` (Phase 154, futura)** — agent que materializa workflows + tests; consome esta skill para gerar test files iniciais junto com migrations
1024
+
1025
+ Pattern de handoff cooperativo herdado v1.23-v1.26: **architect** projeta strategy → **test-writer** materializa pgTAP/Deno tests → **release-pipeline-auditor** (v1.10) audita coverage do pipeline. Nenhum agente descarta upstream — handoff cooperativo (princípio canônico v1.23).
1026
+
1027
+ ### Casos de uso por agent
1028
+
1029
+ | Agent | Como consome esta skill |
1030
+ |-------|-------------------------|
1031
+ | `supabase-migration-writer` | Gera migration + pgTAP test inicial (has_table, has_column, has_pk) |
1032
+ | `supabase-rls-writer` (v1.23) | Gera RLS policies + pgTAP test (throws_like '%permission denied%' para cross-tenant) |
1033
+ | `supabase-edge-fn-writer` | Gera Edge Function + Deno test (handler retorna 200/400 esperado) |
1034
+ | `legacy-characterizer` | Gera pgTAP characterization tests para PG function legada (cap 13 Feathers) |
1035
+ | `refactor-safety-auditor` | Verifica existência de pgTAP tests antes de permitir refactor PG |
1036
+ | `supabase-cicd-pipeline-implementer` (Phase 154 futura) | Gera workflows `database-tests.yml` + `functions-tests.yml` + tests iniciais |
1037
+
1038
+ ## Ver também
1039
+
1040
+ - [supabase-ci-cd-github-actions](../supabase-ci-cd-github-actions/SKILL.md) (v1.27, Phase 151) — workflows CI que executam `supabase test db` + `deno test`
1041
+ - [legacy-characterization-tests](../legacy-characterization-tests/SKILL.md) (v1.16) — cap 13 Feathers; pgTAP é mecanismo canônico para Postgres
1042
+ - [pre-refactor-characterization](../pre-refactor-characterization/SKILL.md) (v1.18) — gate que pgTAP satisfaz para PG functions
1043
+ - [supabase-database-functions](../supabase-database-functions/SKILL.md) — PG functions testáveis via pgTAP
1044
+ - [supabase-rls-policies](../supabase-rls-policies/SKILL.md) (v1.23) — RLS testável via `throws_like` + `set_config jwt claim`
1045
+ - [supabase-rls-defense-in-depth](../supabase-rls-defense-in-depth/SKILL.md) (v1.23) — Camadas testáveis em pgTAP
1046
+ - [supabase-edge-functions](../supabase-edge-functions/SKILL.md) — Edge Functions cobertas por Deno tests
1047
+ - [supabase-postgres-roles](../supabase-postgres-roles/SKILL.md) (v1.26) — `has_role`, `set role` testáveis
1048
+ - [supabase-custom-claims-rbac](../supabase-custom-claims-rbac/SKILL.md) (v1.25) — fixture JWT via `set_config('request.jwt.claim.*')`
1049
+ - [supabase-migrations](../supabase-migrations/SKILL.md) (v1.23) — migration cria tabela; pgTAP test valida
1050
+ - [ai-mutation-tester](../../agents/ai-mutation-tester.md) (v1.20) — mutation testing complementar
1051
+ - [glossário compartilhado](../_shared-supabase/glossary.md) — termos pgTAP, TAP, plan(N), throws_ok, results_eq, supabase test db, deno test --allow-all
1052
+ - Doc oficial pgTAP: [pgTAP Documentation](https://pgtap.org/documentation.html)
1053
+ - Doc oficial Supabase: [Testing with pgTAP](https://supabase.com/docs/guides/database/extensions/pgtap), [Testing Edge Functions](https://supabase.com/docs/guides/functions/unit-test)