@rigour-labs/core 6.7.10 → 6.8.1-rc.1

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 (346) hide show
  1. package/dist/brief/briefing.d.ts +59 -0
  2. package/dist/brief/briefing.js +126 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +2 -0
  5. package/dist/inference/cloud-provider.js +12 -2
  6. package/dist/review/backtest-init.d.ts +8 -5
  7. package/dist/review/backtest-init.js +27 -9
  8. package/dist/review/backtest-last.js +3 -1
  9. package/dist/review/reviewer/adapters.d.ts +4 -0
  10. package/dist/review/reviewer/adapters.js +23 -0
  11. package/dist/review/reviewer/inputs.d.ts +4 -1
  12. package/dist/review/reviewer/inputs.js +36 -2
  13. package/dist/review/reviewer/record.d.ts +5 -0
  14. package/dist/review/reviewer/record.js +3 -3
  15. package/dist/review/reviewer/verdict.d.ts +14 -0
  16. package/dist/review/reviewer/verdict.js +46 -6
  17. package/dist/review/reviewer.d.ts +2 -0
  18. package/dist/review/reviewer.js +24 -5
  19. package/dist/review-learning/repo-rules.d.ts +14 -2
  20. package/dist/review-learning/repo-rules.js +59 -9
  21. package/dist/task/thread.d.ts +47 -0
  22. package/dist/task/thread.js +234 -0
  23. package/dist/templates/universal-config.js +4 -0
  24. package/dist/types/index.d.ts +21 -0
  25. package/dist/types/index.js +7 -0
  26. package/package.json +11 -8
  27. package/dist/context/automatic-index-cache.test.d.ts +0 -1
  28. package/dist/context/automatic-index-cache.test.js +0 -30
  29. package/dist/context/automatic-index.test.d.ts +0 -1
  30. package/dist/context/automatic-index.test.js +0 -45
  31. package/dist/context/cache-engine.test.d.ts +0 -1
  32. package/dist/context/cache-engine.test.js +0 -99
  33. package/dist/context/dependency-graph.test.d.ts +0 -1
  34. package/dist/context/dependency-graph.test.js +0 -13
  35. package/dist/context/index-status.test.d.ts +0 -1
  36. package/dist/context/index-status.test.js +0 -26
  37. package/dist/context.test.d.ts +0 -1
  38. package/dist/context.test.js +0 -228
  39. package/dist/deep/agent-review.test.d.ts +0 -1
  40. package/dist/deep/agent-review.test.js +0 -53
  41. package/dist/deep/code-context.test.d.ts +0 -1
  42. package/dist/deep/code-context.test.js +0 -48
  43. package/dist/deep/code-pass.test.d.ts +0 -1
  44. package/dist/deep/code-pass.test.js +0 -112
  45. package/dist/deep/code-review-prompt.test.d.ts +0 -1
  46. package/dist/deep/code-review-prompt.test.js +0 -20
  47. package/dist/deep/code-verifier.test.d.ts +0 -1
  48. package/dist/deep/code-verifier.test.js +0 -54
  49. package/dist/deep/diff-tests/calls.test.d.ts +0 -1
  50. package/dist/deep/diff-tests/calls.test.js +0 -33
  51. package/dist/deep/diff-tests/run.test.d.ts +0 -1
  52. package/dist/deep/diff-tests/run.test.js +0 -58
  53. package/dist/deep/fact-extractor.test.d.ts +0 -1
  54. package/dist/deep/fact-extractor.test.js +0 -581
  55. package/dist/deep/parse-findings.test.d.ts +0 -1
  56. package/dist/deep/parse-findings.test.js +0 -25
  57. package/dist/deep/pr-review.test.d.ts +0 -1
  58. package/dist/deep/pr-review.test.js +0 -80
  59. package/dist/deep/prompts.test.d.ts +0 -1
  60. package/dist/deep/prompts.test.js +0 -235
  61. package/dist/deep/reference-pack.test.d.ts +0 -1
  62. package/dist/deep/reference-pack.test.js +0 -46
  63. package/dist/deep/related-changes.test.d.ts +0 -1
  64. package/dist/deep/related-changes.test.js +0 -33
  65. package/dist/deep/review-context-export.test.d.ts +0 -1
  66. package/dist/deep/review-context-export.test.js +0 -32
  67. package/dist/deep/review-tools.test.d.ts +0 -1
  68. package/dist/deep/review-tools.test.js +0 -39
  69. package/dist/deep/risk.test.d.ts +0 -1
  70. package/dist/deep/risk.test.js +0 -101
  71. package/dist/deep/verifier.test.d.ts +0 -1
  72. package/dist/deep/verifier.test.js +0 -635
  73. package/dist/discovery.test.d.ts +0 -1
  74. package/dist/discovery.test.js +0 -93
  75. package/dist/environment.test.d.ts +0 -1
  76. package/dist/environment.test.js +0 -94
  77. package/dist/firewall/firewall.test.d.ts +0 -1
  78. package/dist/firewall/firewall.test.js +0 -117
  79. package/dist/firewall/trust-boundaries.test.d.ts +0 -1
  80. package/dist/firewall/trust-boundaries.test.js +0 -64
  81. package/dist/firewall/trusted-control.test.d.ts +0 -1
  82. package/dist/firewall/trusted-control.test.js +0 -188
  83. package/dist/gates/agent-team.test.d.ts +0 -1
  84. package/dist/gates/agent-team.test.js +0 -113
  85. package/dist/gates/ast.test.d.ts +0 -1
  86. package/dist/gates/ast.test.js +0 -112
  87. package/dist/gates/checkpoint.test.d.ts +0 -1
  88. package/dist/gates/checkpoint.test.js +0 -105
  89. package/dist/gates/content.test.d.ts +0 -1
  90. package/dist/gates/content.test.js +0 -73
  91. package/dist/gates/coverage.test.d.ts +0 -1
  92. package/dist/gates/coverage.test.js +0 -53
  93. package/dist/gates/dedupe-failures.test.d.ts +0 -1
  94. package/dist/gates/dedupe-failures.test.js +0 -12
  95. package/dist/gates/deep-analysis.test.d.ts +0 -1
  96. package/dist/gates/deep-analysis.test.js +0 -86
  97. package/dist/gates/deep-intent.test.d.ts +0 -1
  98. package/dist/gates/deep-intent.test.js +0 -51
  99. package/dist/gates/deep-timeout.test.d.ts +0 -1
  100. package/dist/gates/deep-timeout.test.js +0 -10
  101. package/dist/gates/deprecated-apis.test.d.ts +0 -1
  102. package/dist/gates/deprecated-apis.test.js +0 -318
  103. package/dist/gates/deprecated-dependencies.test.d.ts +0 -1
  104. package/dist/gates/deprecated-dependencies.test.js +0 -55
  105. package/dist/gates/frontend-secret-exposure.test.d.ts +0 -1
  106. package/dist/gates/frontend-secret-exposure.test.js +0 -148
  107. package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.d.ts +0 -1
  108. package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.js +0 -27
  109. package/dist/gates/hallucinated-imports/js-resolver-types.test.d.ts +0 -1
  110. package/dist/gates/hallucinated-imports/js-resolver-types.test.js +0 -14
  111. package/dist/gates/hallucinated-imports-sveltekit.test.d.ts +0 -1
  112. package/dist/gates/hallucinated-imports-sveltekit.test.js +0 -132
  113. package/dist/gates/hallucinated-imports.test.d.ts +0 -1
  114. package/dist/gates/hallucinated-imports.test.js +0 -1206
  115. package/dist/gates/js-style-context.test.d.ts +0 -1
  116. package/dist/gates/js-style-context.test.js +0 -35
  117. package/dist/gates/logic-drift.test.d.ts +0 -1
  118. package/dist/gates/logic-drift.test.js +0 -52
  119. package/dist/gates/phantom-apis.test.d.ts +0 -1
  120. package/dist/gates/phantom-apis.test.js +0 -396
  121. package/dist/gates/promise-safety.test.d.ts +0 -1
  122. package/dist/gates/promise-safety.test.js +0 -34
  123. package/dist/gates/runner.test.d.ts +0 -1
  124. package/dist/gates/runner.test.js +0 -77
  125. package/dist/gates/scoped-gates.test.d.ts +0 -1
  126. package/dist/gates/scoped-gates.test.js +0 -52
  127. package/dist/gates/security-patterns-owasp.test.d.ts +0 -1
  128. package/dist/gates/security-patterns-owasp.test.js +0 -186
  129. package/dist/gates/security-patterns.test.d.ts +0 -1
  130. package/dist/gates/security-patterns.test.js +0 -194
  131. package/dist/gates/semantic-bugs.test.d.ts +0 -1
  132. package/dist/gates/semantic-bugs.test.js +0 -76
  133. package/dist/gates/side-effect-analysis.test.d.ts +0 -1
  134. package/dist/gates/side-effect-analysis.test.js +0 -162
  135. package/dist/gates/style-drift.test.d.ts +0 -1
  136. package/dist/gates/style-drift.test.js +0 -26
  137. package/dist/gates/test-quality.test.d.ts +0 -1
  138. package/dist/gates/test-quality.test.js +0 -325
  139. package/dist/gates/trusted-reviews.test.d.ts +0 -1
  140. package/dist/gates/trusted-reviews.test.js +0 -16
  141. package/dist/gates/unindexed-reads/queries.test.d.ts +0 -1
  142. package/dist/gates/unindexed-reads/queries.test.js +0 -54
  143. package/dist/gates/unindexed-reads/schema.test.d.ts +0 -1
  144. package/dist/gates/unindexed-reads/schema.test.js +0 -81
  145. package/dist/gates/unindexed-reads/unindexed-reads.test.d.ts +0 -1
  146. package/dist/gates/unindexed-reads/unindexed-reads.test.js +0 -90
  147. package/dist/hooks/checker.test.d.ts +0 -1
  148. package/dist/hooks/checker.test.js +0 -159
  149. package/dist/hooks/dlp-confidence.test.d.ts +0 -1
  150. package/dist/hooks/dlp-confidence.test.js +0 -51
  151. package/dist/hooks/dlp-feedback.test.d.ts +0 -1
  152. package/dist/hooks/dlp-feedback.test.js +0 -131
  153. package/dist/hooks/input-validator.test.d.ts +0 -1
  154. package/dist/hooks/input-validator.test.js +0 -329
  155. package/dist/hooks/templates.test.d.ts +0 -1
  156. package/dist/hooks/templates.test.js +0 -27
  157. package/dist/inference/brain-placeholder.test.d.ts +0 -1
  158. package/dist/inference/brain-placeholder.test.js +0 -28
  159. package/dist/inference/cloud-provider.test.d.ts +0 -1
  160. package/dist/inference/cloud-provider.test.js +0 -139
  161. package/dist/inference/executable.test.d.ts +0 -1
  162. package/dist/inference/executable.test.js +0 -41
  163. package/dist/inference/http-download.test.d.ts +0 -1
  164. package/dist/inference/http-download.test.js +0 -109
  165. package/dist/inference/llama-engine-checksum.test.d.ts +0 -1
  166. package/dist/inference/llama-engine-checksum.test.js +0 -27
  167. package/dist/inference/llama-engine.test.d.ts +0 -1
  168. package/dist/inference/llama-engine.test.js +0 -51
  169. package/dist/inference/llama-process.test.d.ts +0 -1
  170. package/dist/inference/llama-process.test.js +0 -61
  171. package/dist/inference/local-model.test.d.ts +0 -1
  172. package/dist/inference/local-model.test.js +0 -23
  173. package/dist/inference/model-download.test.d.ts +0 -1
  174. package/dist/inference/model-download.test.js +0 -125
  175. package/dist/inference/model-manager.test.d.ts +0 -1
  176. package/dist/inference/model-manager.test.js +0 -24
  177. package/dist/inference/types.test.d.ts +0 -1
  178. package/dist/inference/types.test.js +0 -19
  179. package/dist/memory/recall.test.d.ts +0 -1
  180. package/dist/memory/recall.test.js +0 -36
  181. package/dist/pattern-index/indexer.test.d.ts +0 -6
  182. package/dist/pattern-index/indexer.test.js +0 -197
  183. package/dist/pattern-index/matcher.test.d.ts +0 -6
  184. package/dist/pattern-index/matcher.test.js +0 -238
  185. package/dist/pattern-index/pattern-reuse.test.d.ts +0 -1
  186. package/dist/pattern-index/pattern-reuse.test.js +0 -76
  187. package/dist/pattern-index/semantic-runtime.test.d.ts +0 -1
  188. package/dist/pattern-index/semantic-runtime.test.js +0 -31
  189. package/dist/pattern-index/staleness.test.d.ts +0 -6
  190. package/dist/pattern-index/staleness.test.js +0 -211
  191. package/dist/review/agent-fixes.test.d.ts +0 -1
  192. package/dist/review/agent-fixes.test.js +0 -32
  193. package/dist/review/backtest-init.test.d.ts +0 -1
  194. package/dist/review/backtest-init.test.js +0 -98
  195. package/dist/review/backtest-judges.test.d.ts +0 -1
  196. package/dist/review/backtest-judges.test.js +0 -32
  197. package/dist/review/backtest-last.test.d.ts +0 -1
  198. package/dist/review/backtest-last.test.js +0 -109
  199. package/dist/review/backtest.test.d.ts +0 -1
  200. package/dist/review/backtest.test.js +0 -183
  201. package/dist/review/baseline.test.d.ts +0 -1
  202. package/dist/review/baseline.test.js +0 -22
  203. package/dist/review/branch-checks.test.d.ts +0 -1
  204. package/dist/review/branch-checks.test.js +0 -48
  205. package/dist/review/check-outcomes.test.d.ts +0 -1
  206. package/dist/review/check-outcomes.test.js +0 -56
  207. package/dist/review/code-patterns.test.d.ts +0 -1
  208. package/dist/review/code-patterns.test.js +0 -142
  209. package/dist/review/dead-code.test.d.ts +0 -1
  210. package/dist/review/dead-code.test.js +0 -154
  211. package/dist/review/deep-runs.test.d.ts +0 -1
  212. package/dist/review/deep-runs.test.js +0 -23
  213. package/dist/review/effectiveness.test.d.ts +0 -1
  214. package/dist/review/effectiveness.test.js +0 -42
  215. package/dist/review/fix-scope.test.d.ts +0 -1
  216. package/dist/review/fix-scope.test.js +0 -104
  217. package/dist/review/generated-files.test.d.ts +0 -1
  218. package/dist/review/generated-files.test.js +0 -25
  219. package/dist/review/migration-order.test.d.ts +0 -1
  220. package/dist/review/migration-order.test.js +0 -62
  221. package/dist/review/quiet.test.d.ts +0 -1
  222. package/dist/review/quiet.test.js +0 -38
  223. package/dist/review/receipt.test.d.ts +0 -1
  224. package/dist/review/receipt.test.js +0 -60
  225. package/dist/review/review-task.test.d.ts +0 -1
  226. package/dist/review/review-task.test.js +0 -89
  227. package/dist/review/review.test.d.ts +0 -1
  228. package/dist/review/review.test.js +0 -203
  229. package/dist/review/reviewer/adapters.test.d.ts +0 -1
  230. package/dist/review/reviewer/adapters.test.js +0 -60
  231. package/dist/review/reviewer/api-judge.test.d.ts +0 -1
  232. package/dist/review/reviewer/api-judge.test.js +0 -108
  233. package/dist/review/reviewer/background.test.d.ts +0 -1
  234. package/dist/review/reviewer/background.test.js +0 -118
  235. package/dist/review/reviewer/context.test.d.ts +0 -1
  236. package/dist/review/reviewer/context.test.js +0 -45
  237. package/dist/review/reviewer/exec.test.d.ts +0 -1
  238. package/dist/review/reviewer/exec.test.js +0 -59
  239. package/dist/review/reviewer/inputs.test.d.ts +0 -1
  240. package/dist/review/reviewer/inputs.test.js +0 -26
  241. package/dist/review/reviewer/panel.test.d.ts +0 -1
  242. package/dist/review/reviewer/panel.test.js +0 -110
  243. package/dist/review/reviewer/record.test.d.ts +0 -1
  244. package/dist/review/reviewer/record.test.js +0 -32
  245. package/dist/review/reviewer/rule-writer.test.d.ts +0 -1
  246. package/dist/review/reviewer/rule-writer.test.js +0 -52
  247. package/dist/review/reviewer/settings.test.d.ts +0 -1
  248. package/dist/review/reviewer/settings.test.js +0 -57
  249. package/dist/review/reviewer/usage.test.d.ts +0 -1
  250. package/dist/review/reviewer/usage.test.js +0 -14
  251. package/dist/review/reviewer.test.d.ts +0 -1
  252. package/dist/review/reviewer.test.js +0 -705
  253. package/dist/review/stories.test.d.ts +0 -1
  254. package/dist/review/stories.test.js +0 -58
  255. package/dist/review/toolchain.test.d.ts +0 -1
  256. package/dist/review/toolchain.test.js +0 -108
  257. package/dist/review/typed/redundancy.test.d.ts +0 -1
  258. package/dist/review/typed/redundancy.test.js +0 -315
  259. package/dist/review/typed/schema-nullability.test.d.ts +0 -1
  260. package/dist/review/typed/schema-nullability.test.js +0 -63
  261. package/dist/review-learning/human-edits.test.d.ts +0 -1
  262. package/dist/review-learning/human-edits.test.js +0 -47
  263. package/dist/review-learning/repo-rules.test.d.ts +0 -1
  264. package/dist/review-learning/repo-rules.test.js +0 -55
  265. package/dist/review-learning/review-learning.test.d.ts +0 -1
  266. package/dist/review-learning/review-learning.test.js +0 -249
  267. package/dist/safety.test.d.ts +0 -1
  268. package/dist/safety.test.js +0 -42
  269. package/dist/semantic/benchmark.test.d.ts +0 -1
  270. package/dist/semantic/benchmark.test.js +0 -22
  271. package/dist/semantic/intent/intent.test.d.ts +0 -1
  272. package/dist/semantic/intent/intent.test.js +0 -46
  273. package/dist/semantic/learn/learn.test.d.ts +0 -1
  274. package/dist/semantic/learn/learn.test.js +0 -67
  275. package/dist/semantic/origins.test.d.ts +0 -1
  276. package/dist/semantic/origins.test.js +0 -77
  277. package/dist/semantic/project-facts.test.d.ts +0 -1
  278. package/dist/semantic/project-facts.test.js +0 -45
  279. package/dist/semantic/sites/call-sites.test.d.ts +0 -1
  280. package/dist/semantic/sites/call-sites.test.js +0 -46
  281. package/dist/services/adaptive-thresholds.test.d.ts +0 -1
  282. package/dist/services/adaptive-thresholds.test.js +0 -53
  283. package/dist/services/agent-history.test.d.ts +0 -1
  284. package/dist/services/agent-history.test.js +0 -69
  285. package/dist/services/context-scope-summary.test.d.ts +0 -1
  286. package/dist/services/context-scope-summary.test.js +0 -17
  287. package/dist/services/context-telemetry-service.test.d.ts +0 -1
  288. package/dist/services/context-telemetry-service.test.js +0 -182
  289. package/dist/services/cursor-usage-sync.test.d.ts +0 -1
  290. package/dist/services/cursor-usage-sync.test.js +0 -173
  291. package/dist/services/engineering-knowledge-graph.test.d.ts +0 -1
  292. package/dist/services/engineering-knowledge-graph.test.js +0 -76
  293. package/dist/services/model-pricing.test.d.ts +0 -1
  294. package/dist/services/model-pricing.test.js +0 -44
  295. package/dist/services/observed-savings.test.d.ts +0 -1
  296. package/dist/services/observed-savings.test.js +0 -37
  297. package/dist/services/score-history.test.d.ts +0 -1
  298. package/dist/services/score-history.test.js +0 -61
  299. package/dist/smoke.test.d.ts +0 -1
  300. package/dist/smoke.test.js +0 -17
  301. package/dist/storage/cache-cleanup.test.d.ts +0 -1
  302. package/dist/storage/cache-cleanup.test.js +0 -54
  303. package/dist/storage/context-telemetry.test.d.ts +0 -1
  304. package/dist/storage/context-telemetry.test.js +0 -80
  305. package/dist/storage/db.test.d.ts +0 -1
  306. package/dist/storage/db.test.js +0 -46
  307. package/dist/storage/fix-lessons.test.d.ts +0 -1
  308. package/dist/storage/fix-lessons.test.js +0 -61
  309. package/dist/storage/lessons.test.d.ts +0 -1
  310. package/dist/storage/lessons.test.js +0 -81
  311. package/dist/storage/local-encryption.test.d.ts +0 -1
  312. package/dist/storage/local-encryption.test.js +0 -34
  313. package/dist/storage/local-memory.test.d.ts +0 -1
  314. package/dist/storage/local-memory.test.js +0 -55
  315. package/dist/storage/share-memory.test.d.ts +0 -1
  316. package/dist/storage/share-memory.test.js +0 -34
  317. package/dist/storage/team-diagnostics.test.d.ts +0 -1
  318. package/dist/storage/team-diagnostics.test.js +0 -51
  319. package/dist/storage/team-scope.test.d.ts +0 -1
  320. package/dist/storage/team-scope.test.js +0 -22
  321. package/dist/storage/team-store.test.d.ts +0 -1
  322. package/dist/storage/team-store.test.js +0 -11
  323. package/dist/storage/team-sync-scope.test.d.ts +0 -1
  324. package/dist/storage/team-sync-scope.test.js +0 -100
  325. package/dist/storage/team-vector-store.test.d.ts +0 -1
  326. package/dist/storage/team-vector-store.test.js +0 -56
  327. package/dist/storage/telemetry-scope.test.d.ts +0 -1
  328. package/dist/storage/telemetry-scope.test.js +0 -38
  329. package/dist/telemetry/telemetry.test.d.ts +0 -1
  330. package/dist/telemetry/telemetry.test.js +0 -64
  331. package/dist/types/index.test.d.ts +0 -1
  332. package/dist/types/index.test.js +0 -33
  333. package/dist/utils/command-line.test.d.ts +0 -1
  334. package/dist/utils/command-line.test.js +0 -8
  335. package/dist/utils/diff-removed.test.d.ts +0 -1
  336. package/dist/utils/diff-removed.test.js +0 -29
  337. package/dist/utils/diff.test.d.ts +0 -1
  338. package/dist/utils/diff.test.js +0 -38
  339. package/dist/utils/glob-paths.test.d.ts +0 -1
  340. package/dist/utils/glob-paths.test.js +0 -40
  341. package/dist/utils/profile.test.d.ts +0 -1
  342. package/dist/utils/profile.test.js +0 -68
  343. package/dist/utils/scanner.test.d.ts +0 -1
  344. package/dist/utils/scanner.test.js +0 -48
  345. package/dist/utils/scope.test.d.ts +0 -1
  346. package/dist/utils/scope.test.js +0 -38
@@ -1,325 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- const mockFindFiles = vi.hoisted(() => vi.fn());
3
- const mockReadFile = vi.hoisted(() => vi.fn());
4
- vi.mock('../utils/scanner.js', () => ({
5
- FileScanner: { findFiles: mockFindFiles },
6
- }));
7
- vi.mock('fs-extra', () => ({
8
- default: {
9
- readFile: mockReadFile,
10
- pathExists: vi.fn().mockResolvedValue(false),
11
- pathExistsSync: vi.fn().mockReturnValue(false),
12
- readFileSync: vi.fn().mockReturnValue(''),
13
- readJson: vi.fn().mockResolvedValue(null),
14
- readdirSync: vi.fn().mockReturnValue([]),
15
- },
16
- }));
17
- import { TestQualityGate } from './test-quality.js';
18
- describe('TestQualityGate — JS/TS tests', () => {
19
- let gate;
20
- beforeEach(() => {
21
- gate = new TestQualityGate();
22
- vi.clearAllMocks();
23
- });
24
- it('should flag empty test bodies', async () => {
25
- mockFindFiles.mockResolvedValue(['src/utils.test.ts']);
26
- mockReadFile.mockResolvedValue(`
27
- describe('Utils', () => {
28
- it('should do something', () => {
29
- });
30
- });
31
- `);
32
- const failures = await gate.run({ cwd: '/project' });
33
- expect(failures).toHaveLength(1);
34
- expect(failures[0].details).toContain('empty-test');
35
- });
36
- it('should flag tests with no assertions', async () => {
37
- mockFindFiles.mockResolvedValue(['src/api.test.ts']);
38
- mockReadFile.mockResolvedValue(`
39
- describe('API', () => {
40
- it('should fetch data', () => {
41
- const data = fetchData();
42
- console.log(data);
43
- });
44
- });
45
- `);
46
- const failures = await gate.run({ cwd: '/project' });
47
- expect(failures).toHaveLength(1);
48
- expect(failures[0].details).toContain('no-assertion');
49
- });
50
- it('should not flag type-level tests, where the compiler is the assertion', async () => {
51
- mockFindFiles.mockResolvedValue(['src/types.test.ts']);
52
- mockReadFile.mockResolvedValue(`
53
- it('our document type accepts the published one', () => {
54
- const _published: Document = null as unknown as Published.Document;
55
- });
56
- it('infers the output', () => {
57
- expectTypeOf(output).toEqualTypeOf<string>();
58
- });
59
- `);
60
- const failures = await gate.run({ cwd: '/project' });
61
- expect(failures).toHaveLength(0);
62
- });
63
- it('should flag tautological assertions', async () => {
64
- mockFindFiles.mockResolvedValue(['src/basic.test.ts']);
65
- mockReadFile.mockResolvedValue(`
66
- describe('Basic', () => {
67
- it('should pass', () => {
68
- expect(true).toBe(true);
69
- });
70
- });
71
- `);
72
- const failures = await gate.run({ cwd: '/project' });
73
- expect(failures).toHaveLength(1);
74
- expect(failures[0].details).toContain('tautological');
75
- });
76
- it('should flag mock-heavy tests', async () => {
77
- mockFindFiles.mockResolvedValue(['src/service.test.ts']);
78
- mockReadFile.mockResolvedValue(`
79
- describe('Service', () => {
80
- it('should process', () => {
81
- const mock1 = vi.fn();
82
- const mock2 = vi.fn();
83
- const mock3 = vi.fn();
84
- const mock4 = vi.fn();
85
- const mock5 = vi.fn();
86
- const mock6 = vi.fn();
87
- expect(mock1).toBeDefined();
88
- });
89
- });
90
- `);
91
- const failures = await gate.run({ cwd: '/project' });
92
- expect(failures).toHaveLength(1);
93
- expect(failures[0].details).toContain('mock-heavy');
94
- });
95
- it('should NOT flag well-written tests', async () => {
96
- mockFindFiles.mockResolvedValue(['src/good.test.ts']);
97
- mockReadFile.mockResolvedValue(`
98
- describe('Calculator', () => {
99
- it('should add two numbers', () => {
100
- const result = add(2, 3);
101
- expect(result).toBe(5);
102
- });
103
-
104
- it('should handle negative numbers', () => {
105
- const result = add(-1, -2);
106
- expect(result).toBe(-3);
107
- });
108
- });
109
- `);
110
- const failures = await gate.run({ cwd: '/project' });
111
- expect(failures).toHaveLength(0);
112
- });
113
- it('should NOT flag tests with expect().toEqual()', async () => {
114
- mockFindFiles.mockResolvedValue(['src/valid.test.ts']);
115
- mockReadFile.mockResolvedValue(`
116
- describe('Parser', () => {
117
- it('should parse JSON', () => {
118
- const result = parseConfig('{"key": "value"}');
119
- expect(result).toEqual({ key: 'value' });
120
- });
121
- });
122
- `);
123
- const failures = await gate.run({ cwd: '/project' });
124
- expect(failures).toHaveLength(0);
125
- });
126
- it('should not scan when disabled', async () => {
127
- const disabled = new TestQualityGate({ enabled: false });
128
- const failures = await disabled.run({ cwd: '/project' });
129
- expect(failures).toHaveLength(0);
130
- expect(mockFindFiles).not.toHaveBeenCalled();
131
- });
132
- });
133
- describe('TestQualityGate — Python tests', () => {
134
- let gate;
135
- beforeEach(() => {
136
- gate = new TestQualityGate();
137
- vi.clearAllMocks();
138
- });
139
- it('should flag empty test functions', async () => {
140
- mockFindFiles.mockResolvedValue(['test_utils.py']);
141
- mockReadFile.mockResolvedValue(`
142
- def test_something():
143
- pass
144
- `);
145
- const failures = await gate.run({ cwd: '/project' });
146
- expect(failures).toHaveLength(1);
147
- expect(failures[0].details).toContain('empty-test');
148
- });
149
- it('should flag tests with no assertions', async () => {
150
- mockFindFiles.mockResolvedValue(['test_api.py']);
151
- mockReadFile.mockResolvedValue(`
152
- def test_fetch_data():
153
- data = fetch_data()
154
- print(data)
155
- result = process(data)
156
- `);
157
- const failures = await gate.run({ cwd: '/project' });
158
- expect(failures).toHaveLength(1);
159
- expect(failures[0].details).toContain('no-assertion');
160
- });
161
- it('should flag tautological Python assertions', async () => {
162
- mockFindFiles.mockResolvedValue(['test_basic.py']);
163
- mockReadFile.mockResolvedValue(`
164
- def test_always_passes():
165
- assert True
166
- `);
167
- const failures = await gate.run({ cwd: '/project' });
168
- expect(failures).toHaveLength(1);
169
- expect(failures[0].details).toContain('tautological');
170
- });
171
- it('should NOT flag Python tests with real assertions', async () => {
172
- mockFindFiles.mockResolvedValue(['test_calc.py']);
173
- mockReadFile.mockResolvedValue(`
174
- def test_addition():
175
- result = add(2, 3)
176
- assert result == 5
177
-
178
- def test_subtraction():
179
- result = subtract(5, 3)
180
- assert result == 2
181
- `);
182
- const failures = await gate.run({ cwd: '/project' });
183
- expect(failures).toHaveLength(0);
184
- });
185
- it('should NOT flag tests using pytest.raises', async () => {
186
- mockFindFiles.mockResolvedValue(['test_errors.py']);
187
- mockReadFile.mockResolvedValue(`
188
- def test_invalid_input():
189
- with pytest.raises(ValueError):
190
- parse_int("not a number")
191
- `);
192
- const failures = await gate.run({ cwd: '/project' });
193
- expect(failures).toHaveLength(0);
194
- });
195
- });
196
- describe('TestQualityGate — Go tests', () => {
197
- let gate;
198
- beforeEach(() => {
199
- gate = new TestQualityGate();
200
- vi.clearAllMocks();
201
- });
202
- it('should flag empty Go test functions', async () => {
203
- mockFindFiles.mockResolvedValue(['utils_test.go']);
204
- mockReadFile.mockResolvedValue(`
205
- package utils
206
-
207
- import "testing"
208
-
209
- func TestSomething(t *testing.T) {
210
- }
211
- `);
212
- const failures = await gate.run({ cwd: '/project' });
213
- expect(failures).toHaveLength(1);
214
- expect(failures[0].details).toContain('empty-test');
215
- });
216
- it('should flag Go tests without assertions', async () => {
217
- mockFindFiles.mockResolvedValue(['calc_test.go']);
218
- mockReadFile.mockResolvedValue(`
219
- package calc
220
-
221
- import "testing"
222
-
223
- func TestAdd(t *testing.T) {
224
- result := Add(2, 3)
225
- _ = result
226
- }
227
- `);
228
- const failures = await gate.run({ cwd: '/project' });
229
- expect(failures).toHaveLength(1);
230
- expect(failures[0].details).toContain('no-assertion');
231
- });
232
- it('should NOT flag Go tests with t.Error assertions', async () => {
233
- mockFindFiles.mockResolvedValue(['good_test.go']);
234
- mockReadFile.mockResolvedValue(`
235
- package calc
236
-
237
- import "testing"
238
-
239
- func TestAdd(t *testing.T) {
240
- result := Add(2, 3)
241
- if result != 5 {
242
- t.Errorf("expected 5, got %d", result)
243
- }
244
- }
245
- `);
246
- const failures = await gate.run({ cwd: '/project' });
247
- expect(failures).toHaveLength(0);
248
- });
249
- it('should NOT flag Go tests with testify assertions', async () => {
250
- mockFindFiles.mockResolvedValue(['testify_test.go']);
251
- mockReadFile.mockResolvedValue(`
252
- package main
253
-
254
- import (
255
- "testing"
256
- "github.com/stretchr/testify/assert"
257
- )
258
-
259
- func TestAdd(t *testing.T) {
260
- result := Add(2, 3)
261
- assert.Equal(t, 5, result)
262
- }
263
- `);
264
- const failures = await gate.run({ cwd: '/project' });
265
- expect(failures).toHaveLength(0);
266
- });
267
- });
268
- describe('TestQualityGate — Java tests', () => {
269
- let gate;
270
- beforeEach(() => {
271
- gate = new TestQualityGate();
272
- vi.clearAllMocks();
273
- });
274
- it('should flag Java tests without assertions', async () => {
275
- mockFindFiles.mockResolvedValue(['CalcTest.java']);
276
- mockReadFile.mockResolvedValue(`
277
- import org.junit.jupiter.api.Test;
278
-
279
- class CalcTest {
280
- @Test
281
- void testAdd() {
282
- int result = Calculator.add(2, 3);
283
- System.out.println(result);
284
- }
285
- }
286
- `);
287
- const failures = await gate.run({ cwd: '/project' });
288
- expect(failures).toHaveLength(1);
289
- expect(failures[0].details).toContain('no-assertion');
290
- });
291
- it('should NOT flag Java tests with assertions', async () => {
292
- mockFindFiles.mockResolvedValue(['GoodTest.java']);
293
- mockReadFile.mockResolvedValue(`
294
- import org.junit.jupiter.api.Test;
295
- import static org.junit.jupiter.api.Assertions.*;
296
-
297
- class GoodTest {
298
- @Test
299
- void testAdd() {
300
- int result = Calculator.add(2, 3);
301
- assertEquals(5, result);
302
- }
303
- }
304
- `);
305
- const failures = await gate.run({ cwd: '/project' });
306
- expect(failures).toHaveLength(0);
307
- });
308
- it('should flag tautological Java assertions', async () => {
309
- mockFindFiles.mockResolvedValue(['TautTest.java']);
310
- mockReadFile.mockResolvedValue(`
311
- import org.junit.jupiter.api.Test;
312
- import static org.junit.jupiter.api.Assertions.*;
313
-
314
- class TautTest {
315
- @Test
316
- void testAlwaysPasses() {
317
- assertTrue(true);
318
- }
319
- }
320
- `);
321
- const failures = await gate.run({ cwd: '/project' });
322
- expect(failures.length).toBeGreaterThanOrEqual(1);
323
- expect(failures[0].details).toContain('tautological');
324
- });
325
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,16 +0,0 @@
1
- import fs from 'fs';
2
- import os from 'os';
3
- import path from 'path';
4
- import { afterEach, describe, expect, it } from 'vitest';
5
- import { trustedReviews } from './deep-analysis.js';
6
- let cwd;
7
- afterEach(() => fs.rmSync(cwd, { recursive: true, force: true }));
8
- describe('trustedReviews', () => {
9
- it('lets a normal run skip what agents recorded as reviewed, and an independent run skip nothing', () => {
10
- cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'trusted-reviews-'));
11
- fs.mkdirSync(path.join(cwd, '.rigour'));
12
- fs.writeFileSync(path.join(cwd, '.rigour', 'reviewed.json'), JSON.stringify({ version: 1, entries: [{ file: 'src/a.ts', function: 'f', hash: 'h', reviewer: 'agent', verdict: 'no_issue' }] }));
13
- expect(trustedReviews(cwd, {}).map(r => r.function)).toEqual(['f']);
14
- expect(trustedReviews(cwd, { independent: true })).toEqual([]);
15
- });
16
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,54 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { findReads } from './queries.js';
3
- const reads = (code) => findReads('a.ts', code);
4
- describe('findReads', () => {
5
- it('models a literal supabase-js read: filters, first order column, line', () => {
6
- const [read] = reads(`
7
- const page = await supabase
8
- .from('orders')
9
- .select('id, placed_at')
10
- .eq('status', 'open')
11
- .not('customer_id', 'is', null)
12
- .gt('placed_at', since)
13
- .lte('placed_at', until)
14
- .order('placed_at')
15
- .order('id')
16
- .limit(100);`);
17
- expect(read).toEqual({
18
- table: 'orders', line: 3, uncertain: false, orderBy: 'placed_at',
19
- filters: [
20
- { column: 'status', op: 'eq', value: 'open' },
21
- { column: 'customer_id', op: 'notnull' },
22
- { column: 'placed_at', op: 'range' },
23
- { column: 'placed_at', op: 'range' },
24
- ],
25
- });
26
- });
27
- it('reads .in, .is(null), .match and .filter, and qualifies a non-public schema', () => {
28
- const [read] = reads(`db.schema('billing').from('invoices').select('*').in('id', ids).is('paid_at', null).match({ kind: 'card' }).filter('total', 'gte', 10);`);
29
- expect(read.table).toBe('billing.invoices');
30
- expect(read.filters).toEqual([
31
- { column: 'id', op: 'in' },
32
- { column: 'paid_at', op: 'isnull' },
33
- { column: 'kind', op: 'eq', value: 'card' },
34
- { column: 'total', op: 'range' },
35
- ]);
36
- expect(read.uncertain).toBe(false);
37
- });
38
- it('is uncertain when anything is computed, OR-ed, embedded or unknown', () => {
39
- const uncertain = [
40
- `db.from(table).select('*').eq('a', 1);`,
41
- `db.from('t').select('*').eq(column, 1);`,
42
- `db.from('t').select('*').or('a.eq.1,b.eq.2');`,
43
- `db.from('t').select('*, u!inner(*)').eq('u.id', 1);`,
44
- `db.from('t').select('*').someNewFilter('a', 1);`,
45
- `db.schema(name).from('t').select('*').eq('a', 1);`,
46
- ];
47
- expect(uncertain.map(code => reads(code)[0].uncertain)).toEqual(uncertain.map(() => true));
48
- });
49
- it('ignores writes and calls that are not reads', () => {
50
- expect(reads(`db.from('t').insert({ a: 1 }).select();`)).toEqual([]);
51
- expect(reads(`db.from('t').update({ a: 1 }).eq('id', 1);`)).toEqual([]);
52
- expect(reads(`Array.from(items).filter(Boolean);`)).toEqual([]);
53
- });
54
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,81 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { policyColumns, policyFilters, replayMigrations } from './schema.js';
3
- import { splitStatements } from './sql.js';
4
- describe('splitStatements', () => {
5
- it('keeps comments, quotes and dollar bodies from splitting a statement', () => {
6
- const sql = `-- a; comment\ncreate table a (x text default ';');\n/* b; */ do $$ begin perform 1; end $$;\nselect 'it''s; fine';`;
7
- expect(splitStatements(sql)).toEqual(["create table a (x text default ';')", 'do $$ begin perform 1; end $$', "select 'it''s; fine'"]);
8
- });
9
- });
10
- describe('replayMigrations', () => {
11
- it('collects primary keys, unique columns and table constraints as indexes', () => {
12
- const schema = replayMigrations([`
13
- create table public.orders (
14
- id uuid primary key default gen_random_uuid(),
15
- code text not null unique,
16
- customer_id uuid not null references customers(id),
17
- placed_at timestamptz,
18
- constraint orders_customer_code unique (customer_id, code)
19
- );`]);
20
- expect(schema.get('orders').indexes.map(i => i.leading)).toEqual(['id', 'code', 'customer_id']);
21
- expect(schema.get('orders').uncertain).toBe(false);
22
- });
23
- it('applies indexes, partial predicates, drops and renames in order', () => {
24
- const schema = replayMigrations([
25
- 'create table orders (id uuid primary key, status text, placed_at timestamptz, customer_id uuid);',
26
- `create index concurrently orders_open_idx on public.orders (placed_at desc, id) where status = 'open' and customer_id is not null;`,
27
- 'create index orders_customer_idx on orders using btree (customer_id);',
28
- 'create index orders_lower_idx on orders (lower(status));',
29
- 'drop index if exists public.orders_customer_idx;',
30
- 'alter index orders_open_idx rename to orders_open_v2_idx;',
31
- ]);
32
- const indexes = schema.get('orders').indexes;
33
- expect(indexes.map(i => [i.name, i.leading])).toEqual([[undefined, 'id'], ['orders_open_v2_idx', 'placed_at'], ['orders_lower_idx', null]]);
34
- expect(indexes[1].predicate).toEqual([{ kind: 'eq', column: 'status', value: 'open' }, { kind: 'notnull', column: 'customer_id' }]);
35
- });
36
- it('follows ALTER TABLE: added keys, table renames, dropped tables', () => {
37
- const schema = replayMigrations([
38
- 'create table events (id bigint, kind text);',
39
- 'alter table events add constraint events_pkey primary key (id);',
40
- 'alter table events add column ref text unique;',
41
- 'alter table events rename to audit_events;',
42
- 'create table scratch (x int);',
43
- 'drop table scratch;',
44
- ]);
45
- expect(schema.get('audit_events').indexes.map(i => i.leading)).toEqual(['id', 'ref']);
46
- expect([...schema.keys()]).toEqual(['audit_events']);
47
- });
48
- it('marks a table uncertain when DDL is beyond what it models', () => {
49
- const schema = replayMigrations([
50
- 'create table a (id int, x int); create table b (id int); create table c (like a including indexes); create table d (id int) partition by range (id);',
51
- 'alter table a rename column x to y;',
52
- `create or replace function f() returns void language plpgsql as $$ begin create index on b (id); end $$;`,
53
- ]);
54
- expect(['a', 'b', 'c', 'd'].map(t => schema.get(t).uncertain)).toEqual([true, true, true, true]);
55
- });
56
- it('treats an OR or an unreadable predicate as unknown', () => {
57
- const schema = replayMigrations([
58
- 'create table t (a int, b int);',
59
- `create index t_or on t (a) where a = 1 or b = 2;`,
60
- `create index t_fn on t (a) where coalesce(b, 0) > 0;`,
61
- ]);
62
- expect(schema.get('t').indexes.map(i => i.predicate)).toEqual([[{ kind: 'unknown' }], [{ kind: 'unknown' }]]);
63
- });
64
- it('keeps SELECT and ALL policies as the columns they compare to the user', () => {
65
- const schema = replayMigrations([
66
- 'create table subs (id text primary key, user_id uuid, status text);',
67
- `create policy "own" on subs for select using (auth.uid() = user_id);`,
68
- `create policy "writes" on subs for insert with check (auth.uid() = user_id);`,
69
- `create policy "all" on subs to authenticated using (true);`,
70
- ]);
71
- expect(policyFilters(schema.get('subs'))).toEqual({ columns: ['user_id'], unknown: false });
72
- });
73
- });
74
- describe('policyColumns', () => {
75
- it('reads comparisons to the current user and gives up on anything else', () => {
76
- expect(policyColumns('(select auth.uid()) = owner_id')).toEqual(['owner_id']);
77
- expect(policyColumns('(owner_id = auth.uid()) or (editor_id = auth.uid())')).toEqual(['owner_id', 'editor_id']);
78
- expect(policyColumns('true')).toEqual([]);
79
- expect(policyColumns('exists (select 1 from members m where m.org_id = org_id)')).toBeNull();
80
- });
81
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,90 +0,0 @@
1
- import fs from 'fs';
2
- import os from 'os';
3
- import path from 'path';
4
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
- import { UnindexedReadsGate } from './index.js';
6
- let repo;
7
- const write = (rel, body) => {
8
- fs.mkdirSync(path.dirname(path.join(repo, rel)), { recursive: true });
9
- fs.writeFileSync(path.join(repo, rel), body);
10
- };
11
- const run = (config = { enabled: true }) => new UnindexedReadsGate(config).run({ cwd: repo, patterns: ['src/**/*.ts'] });
12
- const TABLE = `create table orders (
13
- id uuid primary key,
14
- customer_id uuid,
15
- status text not null,
16
- placed_at timestamptz,
17
- campaign_id uuid
18
- );
19
- create index orders_customer_idx on orders (customer_id, status, placed_at desc);
20
- create index orders_campaign_window_idx on orders (placed_at, id) where status = 'open' and campaign_id is not null;
21
- `;
22
- const WINDOW_READ = `
23
- export async function openOrders(db: Db, since: string, until: string) {
24
- return db
25
- .from('orders')
26
- .select('id, placed_at')
27
- .eq('status', 'open')
28
- .not('customer_id', 'is', null)
29
- .gt('placed_at', since)
30
- .lte('placed_at', until)
31
- .order('placed_at')
32
- .order('id');
33
- }
34
- `;
35
- beforeEach(() => { repo = fs.mkdtempSync(path.join(os.tmpdir(), 'unindexed-')); });
36
- afterEach(() => fs.rmSync(repo, { recursive: true, force: true }));
37
- describe('UnindexedReadsGate', () => {
38
- it('flags a window read whose only candidate index is partial on a filter the read does not have', async () => {
39
- write('supabase/migrations/20260101000000_orders.sql', TABLE);
40
- write('src/orders.ts', WINDOW_READ);
41
- const failures = await run();
42
- expect(failures).toHaveLength(1);
43
- expect(failures[0]).toMatchObject({ id: 'unindexed-reads', files: ['src/orders.ts'], line: 4 });
44
- expect(failures[0].details).toContain('`orders`');
45
- expect(failures[0].details).toContain('`placed_at`');
46
- });
47
- it('is silent once a later migration adds an index that serves the read', async () => {
48
- write('supabase/migrations/20260101000000_orders.sql', TABLE);
49
- write('supabase/migrations/20260102000000_orders_open.sql', `create index concurrently orders_open_window_idx on public.orders (placed_at, id) where status = 'open' and customer_id is not null;`);
50
- write('src/orders.ts', WINDOW_READ);
51
- expect(await run()).toEqual([]);
52
- });
53
- it('is silent for a primary-key lookup, a table outside the migrations, and an uncertain table', async () => {
54
- write('db/migrations/001_orders.sql', TABLE + 'create table notes (id int, body text);\nalter table notes rename column body to text;\n');
55
- write('src/reads.ts', `
56
- db.from('orders').select('*').eq('id', id);
57
- db.from('auth_users').select('*').eq('email', email);
58
- db.from('notes').select('*').eq('text', q);
59
- `);
60
- expect(await run()).toEqual([]);
61
- });
62
- it('is silent unless enabled, and for a read that narrows nothing', async () => {
63
- write('supabase/migrations/20260101000000_orders.sql', TABLE);
64
- write('src/orders.ts', WINDOW_READ + `\ndb.from('orders').select('*').limit(10);\n`);
65
- expect(await run({ enabled: false })).toEqual([]);
66
- expect(await new UnindexedReadsGate({ enabled: true }).run({ cwd: repo, patterns: ['src/**/*.ts'] })).toHaveLength(1);
67
- });
68
- it('needs every migration set that defines the table to lack a serving index', async () => {
69
- write('apps/a/supabase/migrations/1_orders.sql', TABLE);
70
- write('apps/b/supabase/migrations/1_orders.sql', TABLE + `create index orders_status_idx on orders (status);`);
71
- write('src/orders.ts', WINDOW_READ);
72
- expect(await run()).toEqual([]);
73
- });
74
- it('counts a row-level policy as a filter: names its column, and is silent once that column is indexed', async () => {
75
- const subs = `create table subs (id text primary key, user_id uuid not null, status text);
76
- create policy "own subs" on subs for select using (auth.uid() = user_id);`;
77
- write('supabase/migrations/1_subs.sql', subs);
78
- write('src/subs.ts', `export const active = (db: Db) => db.from('subs').select('*').in('status', ['active']).maybeSingle();\n`);
79
- const [finding] = await run();
80
- expect(finding.details).toContain('`user_id`');
81
- write('supabase/migrations/2_subs_user.sql', 'create index subs_user_idx on subs (user_id);');
82
- expect(await run()).toEqual([]);
83
- });
84
- it('is silent for a table whose policy it cannot read', async () => {
85
- write('supabase/migrations/1_docs.sql', `create table docs (id int primary key, org_id int, title text);
86
- create policy "members" on docs for select using (exists (select 1 from members m where m.org_id = docs.org_id));`);
87
- write('src/docs.ts', `db.from('docs').select('*').eq('title', t);\n`);
88
- expect(await run()).toEqual([]);
89
- });
90
- });
@@ -1 +0,0 @@
1
- export {};