@rigour-labs/core 6.8.0 → 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 (326) hide show
  1. package/dist/inference/cloud-provider.js +12 -2
  2. package/package.json +11 -8
  3. package/dist/brief/briefing.test.d.ts +0 -1
  4. package/dist/brief/briefing.test.js +0 -107
  5. package/dist/context/automatic-index-cache.test.d.ts +0 -1
  6. package/dist/context/automatic-index-cache.test.js +0 -30
  7. package/dist/context/automatic-index.test.d.ts +0 -1
  8. package/dist/context/automatic-index.test.js +0 -45
  9. package/dist/context/cache-engine.test.d.ts +0 -1
  10. package/dist/context/cache-engine.test.js +0 -99
  11. package/dist/context/dependency-graph.test.d.ts +0 -1
  12. package/dist/context/dependency-graph.test.js +0 -13
  13. package/dist/context/index-status.test.d.ts +0 -1
  14. package/dist/context/index-status.test.js +0 -26
  15. package/dist/context.test.d.ts +0 -1
  16. package/dist/context.test.js +0 -228
  17. package/dist/deep/agent-review.test.d.ts +0 -1
  18. package/dist/deep/agent-review.test.js +0 -53
  19. package/dist/deep/code-context.test.d.ts +0 -1
  20. package/dist/deep/code-context.test.js +0 -48
  21. package/dist/deep/code-pass.test.d.ts +0 -1
  22. package/dist/deep/code-pass.test.js +0 -112
  23. package/dist/deep/code-review-prompt.test.d.ts +0 -1
  24. package/dist/deep/code-review-prompt.test.js +0 -20
  25. package/dist/deep/code-verifier.test.d.ts +0 -1
  26. package/dist/deep/code-verifier.test.js +0 -54
  27. package/dist/deep/diff-tests/calls.test.d.ts +0 -1
  28. package/dist/deep/diff-tests/calls.test.js +0 -33
  29. package/dist/deep/diff-tests/run.test.d.ts +0 -1
  30. package/dist/deep/diff-tests/run.test.js +0 -58
  31. package/dist/deep/fact-extractor.test.d.ts +0 -1
  32. package/dist/deep/fact-extractor.test.js +0 -581
  33. package/dist/deep/parse-findings.test.d.ts +0 -1
  34. package/dist/deep/parse-findings.test.js +0 -25
  35. package/dist/deep/pr-review.test.d.ts +0 -1
  36. package/dist/deep/pr-review.test.js +0 -80
  37. package/dist/deep/prompts.test.d.ts +0 -1
  38. package/dist/deep/prompts.test.js +0 -235
  39. package/dist/deep/reference-pack.test.d.ts +0 -1
  40. package/dist/deep/reference-pack.test.js +0 -46
  41. package/dist/deep/related-changes.test.d.ts +0 -1
  42. package/dist/deep/related-changes.test.js +0 -33
  43. package/dist/deep/review-context-export.test.d.ts +0 -1
  44. package/dist/deep/review-context-export.test.js +0 -32
  45. package/dist/deep/review-tools.test.d.ts +0 -1
  46. package/dist/deep/review-tools.test.js +0 -39
  47. package/dist/deep/risk.test.d.ts +0 -1
  48. package/dist/deep/risk.test.js +0 -101
  49. package/dist/deep/verifier.test.d.ts +0 -1
  50. package/dist/deep/verifier.test.js +0 -635
  51. package/dist/discovery.test.d.ts +0 -1
  52. package/dist/discovery.test.js +0 -93
  53. package/dist/environment.test.d.ts +0 -1
  54. package/dist/environment.test.js +0 -94
  55. package/dist/firewall/firewall.test.d.ts +0 -1
  56. package/dist/firewall/firewall.test.js +0 -117
  57. package/dist/firewall/trust-boundaries.test.d.ts +0 -1
  58. package/dist/firewall/trust-boundaries.test.js +0 -64
  59. package/dist/firewall/trusted-control.test.d.ts +0 -1
  60. package/dist/firewall/trusted-control.test.js +0 -188
  61. package/dist/gates/agent-team.test.d.ts +0 -1
  62. package/dist/gates/agent-team.test.js +0 -113
  63. package/dist/gates/ast.test.d.ts +0 -1
  64. package/dist/gates/ast.test.js +0 -112
  65. package/dist/gates/checkpoint.test.d.ts +0 -1
  66. package/dist/gates/checkpoint.test.js +0 -105
  67. package/dist/gates/content.test.d.ts +0 -1
  68. package/dist/gates/content.test.js +0 -73
  69. package/dist/gates/coverage.test.d.ts +0 -1
  70. package/dist/gates/coverage.test.js +0 -53
  71. package/dist/gates/dedupe-failures.test.d.ts +0 -1
  72. package/dist/gates/dedupe-failures.test.js +0 -12
  73. package/dist/gates/deep-analysis.test.d.ts +0 -1
  74. package/dist/gates/deep-analysis.test.js +0 -86
  75. package/dist/gates/deep-intent.test.d.ts +0 -1
  76. package/dist/gates/deep-intent.test.js +0 -51
  77. package/dist/gates/deep-timeout.test.d.ts +0 -1
  78. package/dist/gates/deep-timeout.test.js +0 -10
  79. package/dist/gates/deprecated-apis.test.d.ts +0 -1
  80. package/dist/gates/deprecated-apis.test.js +0 -318
  81. package/dist/gates/deprecated-dependencies.test.d.ts +0 -1
  82. package/dist/gates/deprecated-dependencies.test.js +0 -55
  83. package/dist/gates/frontend-secret-exposure.test.d.ts +0 -1
  84. package/dist/gates/frontend-secret-exposure.test.js +0 -148
  85. package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.d.ts +0 -1
  86. package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.js +0 -27
  87. package/dist/gates/hallucinated-imports/js-resolver-types.test.d.ts +0 -1
  88. package/dist/gates/hallucinated-imports/js-resolver-types.test.js +0 -14
  89. package/dist/gates/hallucinated-imports-sveltekit.test.d.ts +0 -1
  90. package/dist/gates/hallucinated-imports-sveltekit.test.js +0 -132
  91. package/dist/gates/hallucinated-imports.test.d.ts +0 -1
  92. package/dist/gates/hallucinated-imports.test.js +0 -1206
  93. package/dist/gates/js-style-context.test.d.ts +0 -1
  94. package/dist/gates/js-style-context.test.js +0 -35
  95. package/dist/gates/logic-drift.test.d.ts +0 -1
  96. package/dist/gates/logic-drift.test.js +0 -52
  97. package/dist/gates/phantom-apis.test.d.ts +0 -1
  98. package/dist/gates/phantom-apis.test.js +0 -396
  99. package/dist/gates/promise-safety.test.d.ts +0 -1
  100. package/dist/gates/promise-safety.test.js +0 -34
  101. package/dist/gates/runner.test.d.ts +0 -1
  102. package/dist/gates/runner.test.js +0 -77
  103. package/dist/gates/scoped-gates.test.d.ts +0 -1
  104. package/dist/gates/scoped-gates.test.js +0 -52
  105. package/dist/gates/security-patterns-owasp.test.d.ts +0 -1
  106. package/dist/gates/security-patterns-owasp.test.js +0 -186
  107. package/dist/gates/security-patterns.test.d.ts +0 -1
  108. package/dist/gates/security-patterns.test.js +0 -194
  109. package/dist/gates/semantic-bugs.test.d.ts +0 -1
  110. package/dist/gates/semantic-bugs.test.js +0 -76
  111. package/dist/gates/side-effect-analysis.test.d.ts +0 -1
  112. package/dist/gates/side-effect-analysis.test.js +0 -162
  113. package/dist/gates/style-drift.test.d.ts +0 -1
  114. package/dist/gates/style-drift.test.js +0 -26
  115. package/dist/gates/test-quality.test.d.ts +0 -1
  116. package/dist/gates/test-quality.test.js +0 -325
  117. package/dist/gates/trusted-reviews.test.d.ts +0 -1
  118. package/dist/gates/trusted-reviews.test.js +0 -16
  119. package/dist/gates/unindexed-reads/queries.test.d.ts +0 -1
  120. package/dist/gates/unindexed-reads/queries.test.js +0 -54
  121. package/dist/gates/unindexed-reads/schema.test.d.ts +0 -1
  122. package/dist/gates/unindexed-reads/schema.test.js +0 -81
  123. package/dist/gates/unindexed-reads/unindexed-reads.test.d.ts +0 -1
  124. package/dist/gates/unindexed-reads/unindexed-reads.test.js +0 -90
  125. package/dist/hooks/checker.test.d.ts +0 -1
  126. package/dist/hooks/checker.test.js +0 -159
  127. package/dist/hooks/dlp-confidence.test.d.ts +0 -1
  128. package/dist/hooks/dlp-confidence.test.js +0 -51
  129. package/dist/hooks/dlp-feedback.test.d.ts +0 -1
  130. package/dist/hooks/dlp-feedback.test.js +0 -131
  131. package/dist/hooks/input-validator.test.d.ts +0 -1
  132. package/dist/hooks/input-validator.test.js +0 -329
  133. package/dist/hooks/templates.test.d.ts +0 -1
  134. package/dist/hooks/templates.test.js +0 -27
  135. package/dist/inference/brain-placeholder.test.d.ts +0 -1
  136. package/dist/inference/brain-placeholder.test.js +0 -28
  137. package/dist/inference/cloud-provider.test.d.ts +0 -1
  138. package/dist/inference/cloud-provider.test.js +0 -139
  139. package/dist/inference/executable.test.d.ts +0 -1
  140. package/dist/inference/executable.test.js +0 -41
  141. package/dist/inference/http-download.test.d.ts +0 -1
  142. package/dist/inference/http-download.test.js +0 -109
  143. package/dist/inference/llama-engine-checksum.test.d.ts +0 -1
  144. package/dist/inference/llama-engine-checksum.test.js +0 -27
  145. package/dist/inference/llama-engine.test.d.ts +0 -1
  146. package/dist/inference/llama-engine.test.js +0 -51
  147. package/dist/inference/llama-process.test.d.ts +0 -1
  148. package/dist/inference/llama-process.test.js +0 -61
  149. package/dist/inference/local-model.test.d.ts +0 -1
  150. package/dist/inference/local-model.test.js +0 -23
  151. package/dist/inference/model-download.test.d.ts +0 -1
  152. package/dist/inference/model-download.test.js +0 -125
  153. package/dist/inference/model-manager.test.d.ts +0 -1
  154. package/dist/inference/model-manager.test.js +0 -24
  155. package/dist/inference/types.test.d.ts +0 -1
  156. package/dist/inference/types.test.js +0 -19
  157. package/dist/memory/recall.test.d.ts +0 -1
  158. package/dist/memory/recall.test.js +0 -36
  159. package/dist/pattern-index/indexer.test.d.ts +0 -6
  160. package/dist/pattern-index/indexer.test.js +0 -197
  161. package/dist/pattern-index/matcher.test.d.ts +0 -6
  162. package/dist/pattern-index/matcher.test.js +0 -238
  163. package/dist/pattern-index/pattern-reuse.test.d.ts +0 -1
  164. package/dist/pattern-index/pattern-reuse.test.js +0 -76
  165. package/dist/pattern-index/semantic-runtime.test.d.ts +0 -1
  166. package/dist/pattern-index/semantic-runtime.test.js +0 -31
  167. package/dist/pattern-index/staleness.test.d.ts +0 -6
  168. package/dist/pattern-index/staleness.test.js +0 -211
  169. package/dist/review/agent-fixes.test.d.ts +0 -1
  170. package/dist/review/agent-fixes.test.js +0 -32
  171. package/dist/review/backtest-init.test.d.ts +0 -1
  172. package/dist/review/backtest-init.test.js +0 -136
  173. package/dist/review/backtest-judges.test.d.ts +0 -1
  174. package/dist/review/backtest-judges.test.js +0 -32
  175. package/dist/review/backtest-last.test.d.ts +0 -1
  176. package/dist/review/backtest-last.test.js +0 -109
  177. package/dist/review/backtest.test.d.ts +0 -1
  178. package/dist/review/backtest.test.js +0 -183
  179. package/dist/review/baseline.test.d.ts +0 -1
  180. package/dist/review/baseline.test.js +0 -22
  181. package/dist/review/branch-checks.test.d.ts +0 -1
  182. package/dist/review/branch-checks.test.js +0 -48
  183. package/dist/review/check-outcomes.test.d.ts +0 -1
  184. package/dist/review/check-outcomes.test.js +0 -56
  185. package/dist/review/code-patterns.test.d.ts +0 -1
  186. package/dist/review/code-patterns.test.js +0 -142
  187. package/dist/review/dead-code.test.d.ts +0 -1
  188. package/dist/review/dead-code.test.js +0 -154
  189. package/dist/review/deep-runs.test.d.ts +0 -1
  190. package/dist/review/deep-runs.test.js +0 -23
  191. package/dist/review/effectiveness.test.d.ts +0 -1
  192. package/dist/review/effectiveness.test.js +0 -42
  193. package/dist/review/fix-scope.test.d.ts +0 -1
  194. package/dist/review/fix-scope.test.js +0 -104
  195. package/dist/review/generated-files.test.d.ts +0 -1
  196. package/dist/review/generated-files.test.js +0 -25
  197. package/dist/review/migration-order.test.d.ts +0 -1
  198. package/dist/review/migration-order.test.js +0 -62
  199. package/dist/review/quiet.test.d.ts +0 -1
  200. package/dist/review/quiet.test.js +0 -38
  201. package/dist/review/receipt.test.d.ts +0 -1
  202. package/dist/review/receipt.test.js +0 -60
  203. package/dist/review/review-task.test.d.ts +0 -1
  204. package/dist/review/review-task.test.js +0 -89
  205. package/dist/review/review.test.d.ts +0 -1
  206. package/dist/review/review.test.js +0 -203
  207. package/dist/review/reviewer/adapters.test.d.ts +0 -1
  208. package/dist/review/reviewer/adapters.test.js +0 -100
  209. package/dist/review/reviewer/api-judge.test.d.ts +0 -1
  210. package/dist/review/reviewer/api-judge.test.js +0 -108
  211. package/dist/review/reviewer/background.test.d.ts +0 -1
  212. package/dist/review/reviewer/background.test.js +0 -118
  213. package/dist/review/reviewer/context.test.d.ts +0 -1
  214. package/dist/review/reviewer/context.test.js +0 -45
  215. package/dist/review/reviewer/exec.test.d.ts +0 -1
  216. package/dist/review/reviewer/exec.test.js +0 -59
  217. package/dist/review/reviewer/inputs.test.d.ts +0 -1
  218. package/dist/review/reviewer/inputs.test.js +0 -52
  219. package/dist/review/reviewer/panel.test.d.ts +0 -1
  220. package/dist/review/reviewer/panel.test.js +0 -110
  221. package/dist/review/reviewer/record.test.d.ts +0 -1
  222. package/dist/review/reviewer/record.test.js +0 -40
  223. package/dist/review/reviewer/rule-writer.test.d.ts +0 -1
  224. package/dist/review/reviewer/rule-writer.test.js +0 -52
  225. package/dist/review/reviewer/settings.test.d.ts +0 -1
  226. package/dist/review/reviewer/settings.test.js +0 -57
  227. package/dist/review/reviewer/usage.test.d.ts +0 -1
  228. package/dist/review/reviewer/usage.test.js +0 -14
  229. package/dist/review/reviewer.test.d.ts +0 -1
  230. package/dist/review/reviewer.test.js +0 -792
  231. package/dist/review/stories.test.d.ts +0 -1
  232. package/dist/review/stories.test.js +0 -58
  233. package/dist/review/toolchain.test.d.ts +0 -1
  234. package/dist/review/toolchain.test.js +0 -108
  235. package/dist/review/typed/redundancy.test.d.ts +0 -1
  236. package/dist/review/typed/redundancy.test.js +0 -315
  237. package/dist/review/typed/schema-nullability.test.d.ts +0 -1
  238. package/dist/review/typed/schema-nullability.test.js +0 -63
  239. package/dist/review-learning/human-edits.test.d.ts +0 -1
  240. package/dist/review-learning/human-edits.test.js +0 -47
  241. package/dist/review-learning/repo-rules.test.d.ts +0 -1
  242. package/dist/review-learning/repo-rules.test.js +0 -93
  243. package/dist/review-learning/review-learning.test.d.ts +0 -1
  244. package/dist/review-learning/review-learning.test.js +0 -249
  245. package/dist/safety.test.d.ts +0 -1
  246. package/dist/safety.test.js +0 -42
  247. package/dist/semantic/benchmark.test.d.ts +0 -1
  248. package/dist/semantic/benchmark.test.js +0 -22
  249. package/dist/semantic/intent/intent.test.d.ts +0 -1
  250. package/dist/semantic/intent/intent.test.js +0 -46
  251. package/dist/semantic/learn/learn.test.d.ts +0 -1
  252. package/dist/semantic/learn/learn.test.js +0 -67
  253. package/dist/semantic/origins.test.d.ts +0 -1
  254. package/dist/semantic/origins.test.js +0 -77
  255. package/dist/semantic/project-facts.test.d.ts +0 -1
  256. package/dist/semantic/project-facts.test.js +0 -45
  257. package/dist/semantic/sites/call-sites.test.d.ts +0 -1
  258. package/dist/semantic/sites/call-sites.test.js +0 -46
  259. package/dist/services/adaptive-thresholds.test.d.ts +0 -1
  260. package/dist/services/adaptive-thresholds.test.js +0 -53
  261. package/dist/services/agent-history.test.d.ts +0 -1
  262. package/dist/services/agent-history.test.js +0 -69
  263. package/dist/services/context-scope-summary.test.d.ts +0 -1
  264. package/dist/services/context-scope-summary.test.js +0 -17
  265. package/dist/services/context-telemetry-service.test.d.ts +0 -1
  266. package/dist/services/context-telemetry-service.test.js +0 -182
  267. package/dist/services/cursor-usage-sync.test.d.ts +0 -1
  268. package/dist/services/cursor-usage-sync.test.js +0 -173
  269. package/dist/services/engineering-knowledge-graph.test.d.ts +0 -1
  270. package/dist/services/engineering-knowledge-graph.test.js +0 -76
  271. package/dist/services/model-pricing.test.d.ts +0 -1
  272. package/dist/services/model-pricing.test.js +0 -44
  273. package/dist/services/observed-savings.test.d.ts +0 -1
  274. package/dist/services/observed-savings.test.js +0 -37
  275. package/dist/services/score-history.test.d.ts +0 -1
  276. package/dist/services/score-history.test.js +0 -61
  277. package/dist/smoke.test.d.ts +0 -1
  278. package/dist/smoke.test.js +0 -17
  279. package/dist/storage/cache-cleanup.test.d.ts +0 -1
  280. package/dist/storage/cache-cleanup.test.js +0 -54
  281. package/dist/storage/context-telemetry.test.d.ts +0 -1
  282. package/dist/storage/context-telemetry.test.js +0 -80
  283. package/dist/storage/db.test.d.ts +0 -1
  284. package/dist/storage/db.test.js +0 -46
  285. package/dist/storage/fix-lessons.test.d.ts +0 -1
  286. package/dist/storage/fix-lessons.test.js +0 -61
  287. package/dist/storage/lessons.test.d.ts +0 -1
  288. package/dist/storage/lessons.test.js +0 -81
  289. package/dist/storage/local-encryption.test.d.ts +0 -1
  290. package/dist/storage/local-encryption.test.js +0 -34
  291. package/dist/storage/local-memory.test.d.ts +0 -1
  292. package/dist/storage/local-memory.test.js +0 -55
  293. package/dist/storage/share-memory.test.d.ts +0 -1
  294. package/dist/storage/share-memory.test.js +0 -34
  295. package/dist/storage/team-diagnostics.test.d.ts +0 -1
  296. package/dist/storage/team-diagnostics.test.js +0 -51
  297. package/dist/storage/team-scope.test.d.ts +0 -1
  298. package/dist/storage/team-scope.test.js +0 -22
  299. package/dist/storage/team-store.test.d.ts +0 -1
  300. package/dist/storage/team-store.test.js +0 -11
  301. package/dist/storage/team-sync-scope.test.d.ts +0 -1
  302. package/dist/storage/team-sync-scope.test.js +0 -100
  303. package/dist/storage/team-vector-store.test.d.ts +0 -1
  304. package/dist/storage/team-vector-store.test.js +0 -56
  305. package/dist/storage/telemetry-scope.test.d.ts +0 -1
  306. package/dist/storage/telemetry-scope.test.js +0 -38
  307. package/dist/task/thread.test.d.ts +0 -1
  308. package/dist/task/thread.test.js +0 -133
  309. package/dist/telemetry/telemetry.test.d.ts +0 -1
  310. package/dist/telemetry/telemetry.test.js +0 -64
  311. package/dist/types/index.test.d.ts +0 -1
  312. package/dist/types/index.test.js +0 -33
  313. package/dist/utils/command-line.test.d.ts +0 -1
  314. package/dist/utils/command-line.test.js +0 -8
  315. package/dist/utils/diff-removed.test.d.ts +0 -1
  316. package/dist/utils/diff-removed.test.js +0 -29
  317. package/dist/utils/diff.test.d.ts +0 -1
  318. package/dist/utils/diff.test.js +0 -38
  319. package/dist/utils/glob-paths.test.d.ts +0 -1
  320. package/dist/utils/glob-paths.test.js +0 -40
  321. package/dist/utils/profile.test.d.ts +0 -1
  322. package/dist/utils/profile.test.js +0 -68
  323. package/dist/utils/scanner.test.d.ts +0 -1
  324. package/dist/utils/scanner.test.js +0 -48
  325. package/dist/utils/scope.test.d.ts +0 -1
  326. package/dist/utils/scope.test.js +0 -38
@@ -1,228 +0,0 @@
1
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
- import { GateRunner } from '../src/gates/runner.js';
3
- import fs from 'fs-extra';
4
- import path from 'path';
5
- import os from 'os';
6
- const TEST_CWD = path.join(os.tmpdir(), 'rigour-temp-test-context-' + process.pid);
7
- describe('Context Awareness Engine', () => {
8
- beforeAll(async () => {
9
- await fs.ensureDir(TEST_CWD);
10
- });
11
- afterAll(async () => {
12
- await fs.remove(TEST_CWD);
13
- });
14
- it('should detect context drift for redundant env suffixes (Golden Example)', async () => {
15
- // Setup: Define standard GCP_PROJECT_ID
16
- await fs.writeFile(path.join(TEST_CWD, '.env.example'), 'GCP_PROJECT_ID=my-project\n');
17
- // Setup: Use drifted GCP_PROJECT_ID_PRODUCTION
18
- await fs.writeFile(path.join(TEST_CWD, 'feature.js'), `
19
- const id = process.env.GCP_PROJECT_ID_PRODUCTION;
20
- console.log(id);
21
- `);
22
- const config = {
23
- version: 1,
24
- commands: {},
25
- gates: {
26
- context: {
27
- enabled: true,
28
- sensitivity: 0.8,
29
- mining_depth: 10,
30
- ignored_patterns: [],
31
- cross_file_patterns: true,
32
- naming_consistency: true,
33
- import_relationships: true,
34
- max_cross_file_depth: 50,
35
- },
36
- },
37
- output: { report_path: 'rigour-report.json' }
38
- };
39
- const runner = new GateRunner(config);
40
- const report = await runner.run(TEST_CWD);
41
- const driftFailures = report.failures.filter(f => f.id === 'context-drift');
42
- expect(driftFailures.length).toBeGreaterThan(0);
43
- expect(driftFailures[0].details).toContain('GCP_PROJECT_ID_PRODUCTION');
44
- expect(driftFailures[0].hint).toContain('GCP_PROJECT_ID');
45
- });
46
- it('should not flag valid environment variables', async () => {
47
- await fs.writeFile(path.join(TEST_CWD, 'valid.js'), `
48
- const id = process.env.GCP_PROJECT_ID;
49
- `);
50
- const config = {
51
- version: 1,
52
- commands: {},
53
- gates: {
54
- context: {
55
- enabled: true,
56
- sensitivity: 0.8,
57
- mining_depth: 100,
58
- ignored_patterns: [],
59
- cross_file_patterns: true,
60
- naming_consistency: true,
61
- import_relationships: true,
62
- max_cross_file_depth: 50,
63
- },
64
- },
65
- output: { report_path: 'rigour-report.json' }
66
- };
67
- const runner = new GateRunner(config);
68
- const report = await runner.run(TEST_CWD);
69
- const driftFailures = report.failures.filter(f => f.id === 'context-drift');
70
- // Filter out failures from other files if they still exist in TEST_CWD
71
- const specificFailures = driftFailures.filter(f => f.files?.includes('valid.js'));
72
- expect(specificFailures.length).toBe(0);
73
- });
74
- it('should classify arrow function exports as camelCase, not unknown', async () => {
75
- // Create files with arrow function patterns that previously returned 'unknown'
76
- await fs.writeFile(path.join(TEST_CWD, 'api.ts'), `
77
- export const fetchData = async () => { return []; };
78
- export const getUserProfile = async (id: string) => { return {}; };
79
- export const use = () => {};
80
- export const get = async () => {};
81
- const handleClick = (e: Event) => {};
82
- let processItem = async (item: any) => {};
83
- `);
84
- // Create a second file with consistent arrow function naming
85
- await fs.writeFile(path.join(TEST_CWD, 'service.ts'), `
86
- export const createUser = async (data: any) => {};
87
- export const deleteUser = async (id: string) => {};
88
- export const updateUser = async (id: string, data: any) => {};
89
- `);
90
- const config = {
91
- version: 1,
92
- commands: {},
93
- gates: {
94
- context: {
95
- enabled: true,
96
- sensitivity: 0.8,
97
- mining_depth: 10,
98
- ignored_patterns: [],
99
- cross_file_patterns: true,
100
- naming_consistency: true,
101
- import_relationships: true,
102
- max_cross_file_depth: 50,
103
- },
104
- },
105
- output: { report_path: 'rigour-report.json' }
106
- };
107
- const runner = new GateRunner(config);
108
- const report = await runner.run(TEST_CWD);
109
- // Should NOT have any "unknown" naming convention failures
110
- const namingFailures = report.failures.filter(f => f.id === 'context-drift' && f.details?.includes('unknown'));
111
- expect(namingFailures.length).toBe(0);
112
- });
113
- it('should not classify plain variable declarations as function patterns', async () => {
114
- // Create file with non-function const declarations
115
- await fs.writeFile(path.join(TEST_CWD, 'constants.ts'), `
116
- export const API_URL = 'https://api.example.com';
117
- export const MAX_RETRIES = 3;
118
- const config = { timeout: 5000 };
119
- let count = 0;
120
- `);
121
- // Create file with actual functions for a dominant pattern
122
- await fs.writeFile(path.join(TEST_CWD, 'utils.ts'), `
123
- function getData() { return []; }
124
- function setData(d: any) { return d; }
125
- function processRequest(req: any) { return req; }
126
- `);
127
- const config = {
128
- version: 1,
129
- commands: {},
130
- gates: {
131
- context: {
132
- enabled: true,
133
- sensitivity: 0.8,
134
- mining_depth: 10,
135
- ignored_patterns: [],
136
- cross_file_patterns: true,
137
- naming_consistency: true,
138
- import_relationships: true,
139
- max_cross_file_depth: 50,
140
- },
141
- },
142
- output: { report_path: 'rigour-report.json' }
143
- };
144
- const runner = new GateRunner(config);
145
- const report = await runner.run(TEST_CWD);
146
- // SCREAMING_SNAKE constants should NOT create naming drift failures
147
- // because they should not be in the 'function' pattern bucket at all
148
- const namingFailures = report.failures.filter(f => f.id === 'context-drift' && f.details?.includes('SCREAMING_SNAKE'));
149
- expect(namingFailures.length).toBe(0);
150
- });
151
- });
152
- /**
153
- * Direct unit tests for detectCasing logic
154
- */
155
- describe('detectCasing classification', () => {
156
- // We test the regex rules directly since detectCasing is private
157
- function detectCasing(name) {
158
- if (/^[A-Z][a-z]/.test(name) && /[a-z][A-Z]/.test(name))
159
- return 'PascalCase';
160
- if (/^[a-z]/.test(name) && /[a-z][A-Z]/.test(name))
161
- return 'camelCase';
162
- if (/^[a-z][a-zA-Z0-9]*$/.test(name))
163
- return 'camelCase'; // single-word lowercase
164
- if (/^[a-z]+(_[a-z]+)+$/.test(name))
165
- return 'snake_case';
166
- if (/^[A-Z]+(_[A-Z]+)*$/.test(name))
167
- return 'SCREAMING_SNAKE';
168
- if (/^[A-Z][a-zA-Z]*$/.test(name))
169
- return 'PascalCase';
170
- return 'unknown';
171
- }
172
- // Multi-word camelCase
173
- it('classifies multi-word camelCase', () => {
174
- expect(detectCasing('fetchData')).toBe('camelCase');
175
- expect(detectCasing('getUserProfile')).toBe('camelCase');
176
- expect(detectCasing('handleClick')).toBe('camelCase');
177
- expect(detectCasing('processItem')).toBe('camelCase');
178
- expect(detectCasing('createNewUser')).toBe('camelCase');
179
- });
180
- // Single-word lowercase (the bug fix)
181
- it('classifies single-word lowercase as camelCase', () => {
182
- expect(detectCasing('fetch')).toBe('camelCase');
183
- expect(detectCasing('use')).toBe('camelCase');
184
- expect(detectCasing('get')).toBe('camelCase');
185
- expect(detectCasing('set')).toBe('camelCase');
186
- expect(detectCasing('run')).toBe('camelCase');
187
- expect(detectCasing('a')).toBe('camelCase');
188
- expect(detectCasing('x')).toBe('camelCase');
189
- expect(detectCasing('id')).toBe('camelCase');
190
- });
191
- // Single-word lowercase with digits
192
- it('classifies lowercase with digits as camelCase', () => {
193
- expect(detectCasing('handler2')).toBe('camelCase');
194
- expect(detectCasing('config3')).toBe('camelCase');
195
- expect(detectCasing('v2')).toBe('camelCase');
196
- });
197
- // PascalCase
198
- it('classifies PascalCase', () => {
199
- expect(detectCasing('MyComponent')).toBe('PascalCase');
200
- expect(detectCasing('UserService')).toBe('PascalCase');
201
- expect(detectCasing('App')).toBe('PascalCase');
202
- expect(detectCasing('A')).toBe('SCREAMING_SNAKE'); // single uppercase letter
203
- });
204
- // snake_case
205
- it('classifies snake_case', () => {
206
- expect(detectCasing('my_func')).toBe('snake_case');
207
- expect(detectCasing('get_data')).toBe('snake_case');
208
- expect(detectCasing('process_all_items')).toBe('snake_case');
209
- });
210
- // SCREAMING_SNAKE
211
- it('classifies SCREAMING_SNAKE_CASE', () => {
212
- expect(detectCasing('API_URL')).toBe('SCREAMING_SNAKE');
213
- expect(detectCasing('MAX_RETRIES')).toBe('SCREAMING_SNAKE');
214
- expect(detectCasing('A')).toBe('SCREAMING_SNAKE');
215
- expect(detectCasing('DB')).toBe('SCREAMING_SNAKE');
216
- });
217
- // Edge cases that should NOT be unknown
218
- it('does not return unknown for valid identifiers', () => {
219
- const validIdentifiers = [
220
- 'fetch', 'getData', 'MyClass', 'my_func', 'API_KEY',
221
- 'use', 'run', 'a', 'x', 'id', 'App', 'handler2',
222
- 'processItem', 'UserProfile', 'get_all_data', 'MAX_SIZE',
223
- ];
224
- for (const name of validIdentifiers) {
225
- expect(detectCasing(name)).not.toBe('unknown');
226
- }
227
- });
228
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,53 +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 { reviewWithTools } from './agent-review.js';
6
- import { verifyCodeFindings } from './code-verifier.js';
7
- let repo;
8
- beforeEach(() => {
9
- repo = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-review-'));
10
- fs.writeFileSync(path.join(repo, 'limits.ts'), 'export const MAX_ROWS = 0;\n');
11
- fs.writeFileSync(path.join(repo, 'page.ts'), Array.from({ length: 60 }, (_, i) => `// line ${i + 1}`).join('\n'));
12
- });
13
- afterEach(() => { fs.rmSync(repo, { recursive: true, force: true }); });
14
- const context = { file: 'page.ts', language: 'typescript', text: '1| export function page() {}', ranges: [[1, 5]], source: 'export function page() {}' };
15
- function scripted(replies) {
16
- const seen = [];
17
- const provider = {
18
- name: 'fake', isAvailable: async () => true, setup: async () => { }, dispose: () => { }, analyze: async () => '',
19
- chat: async (messages, tools, options) => {
20
- seen.push({ messages: [...messages], tools: tools.length, toolChoice: options?.toolChoice });
21
- return replies.shift() ?? { text: '{"findings": []}', toolCalls: [] };
22
- },
23
- };
24
- return { provider, seen };
25
- }
26
- const findingJson = (line, description) => JSON.stringify({ findings: [{ category: 'correctness', severity: 'high', file: 'page.ts', line, description, suggestion: 's', confidence: 0.9 }] });
27
- describe('reviewWithTools', () => {
28
- it('runs the tools the model asks for, then grounds its finding in what it read', async () => {
29
- const { provider, seen } = scripted([
30
- { text: '', toolCalls: [{ id: 't1', name: 'read_file', arguments: { path: 'limits.ts' } }, { id: 't2', name: 'read_file', arguments: { path: 'page.ts', start_line: 40, end_line: 45 } }] },
31
- { text: findingJson(42, '`MAX_ROWS` is 0, so the page loop never runs.'), toolCalls: [] },
32
- ]);
33
- const result = await reviewWithTools(provider, context, '', repo, {});
34
- expect(result.toolCalls).toBe(2);
35
- const toolResults = seen[1].messages.filter(m => m.role === 'tool').map(m => m.content);
36
- expect(toolResults[0]).toContain('export const MAX_ROWS = 0;');
37
- // Line 42 was outside the sent source, but the model read it, and it read MAX_ROWS.
38
- expect(verifyCodeFindings(result.findings, [result.context])).toHaveLength(1);
39
- expect(verifyCodeFindings(result.findings, [context])).toHaveLength(0);
40
- });
41
- it('forbids tool calls after the budget, keeping the tools defined for the history, and asks for the answer', async () => {
42
- const greedy = Array.from({ length: 20 }, (_, i) => ({ text: '', toolCalls: [{ id: `t${i}`, name: 'grep', arguments: { pattern: 'line' } }] }));
43
- const { provider, seen } = scripted(greedy);
44
- const result = await reviewWithTools(provider, context, '', repo, {});
45
- expect(result.toolCalls).toBeLessThanOrEqual(12);
46
- expect(seen.at(-1)).toMatchObject({ tools: 2, toolChoice: 'none' });
47
- expect(result.findings).toEqual([]);
48
- });
49
- it('refuses a provider that cannot call tools', async () => {
50
- const provider = { name: 'local', isAvailable: async () => true, setup: async () => { }, dispose: () => { }, analyze: async () => '' };
51
- await expect(reviewWithTools(provider, context, '', repo, {})).rejects.toThrow('cannot call tools');
52
- });
53
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,48 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { buildCodeContext } from './code-context.js';
3
- function fn(name, lineStart, lineEnd) {
4
- return {
5
- name, lineStart, lineEnd, lineCount: lineEnd - lineStart + 1, paramCount: 0, params: [],
6
- maxNesting: 1, hasReturn: true, isAsync: false, isExported: true,
7
- };
8
- }
9
- function facts(path, functions) {
10
- return {
11
- path, language: 'typescript', lineCount: 0, classes: [], functions, imports: [], exports: [],
12
- errorHandling: [], testAssertions: 0, hasTests: false,
13
- };
14
- }
15
- const source = (n) => Array.from({ length: n }, (_, i) => `const line${i + 1} = ${i + 1};`).join('\n');
16
- describe('buildCodeContext', () => {
17
- it('sends a small file whole, with line numbers', () => {
18
- const ctx = buildCodeContext(facts('src/a.ts', []), 'const a = 1;\nexport { a };', { maxChars: 10_000 });
19
- expect(ctx.text).toBe('FILE: src/a.ts (typescript, 2 lines)\n1| const a = 1;\n2| export { a };');
20
- expect(ctx.ranges).toEqual([[1, 2]]);
21
- expect(ctx.source).toContain('export { a }');
22
- });
23
- it('cuts a large file into function segments and names what it omitted', () => {
24
- const content = source(300);
25
- const ctx = buildCodeContext(facts('src/big.ts', [fn('first', 1, 100), fn('second', 101, 200), fn('third', 201, 300)]), content, { maxChars: 4_000 });
26
- expect(ctx.ranges[0]).toEqual([1, 100]);
27
- expect(ctx.text).toMatch(/omitted for size: .*function second \(lines 101-200\)/);
28
- expect(ctx.text).toContain('function third (lines 201-300)');
29
- expect(ctx.source).not.toContain('line150 ');
30
- });
31
- it('puts the segment around changed lines first', () => {
32
- const content = source(300);
33
- const ctx = buildCodeContext(facts('src/big.ts', [fn('first', 1, 100), fn('second', 101, 200), fn('third', 201, 300)]), content, {
34
- maxChars: 4_000, focusLines: [250],
35
- });
36
- expect(ctx.ranges[0]).toEqual([201, 300]);
37
- expect(ctx.text).toContain('function first (lines 1-100)');
38
- });
39
- it('truncates a single oversized function explicitly instead of dropping it', () => {
40
- const content = source(2_000);
41
- const ctx = buildCodeContext(facts('src/huge.ts', [fn('huge', 1, 2_000)]), content, { maxChars: 2_000 });
42
- expect(ctx.ranges).toHaveLength(1);
43
- expect(ctx.ranges[0][0]).toBe(1);
44
- expect(ctx.ranges[0][1]).toBeLessThan(2_000);
45
- expect(ctx.text).toMatch(/… \[\d+ lines omitted\]$/);
46
- expect(ctx.text.length).toBeLessThanOrEqual(2_000);
47
- });
48
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,112 +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 { runCodePass } from './code-pass.js';
6
- import { parseVerdicts } from './self-check.js';
7
- let dir;
8
- afterEach(() => { if (dir)
9
- fs.rmSync(dir, { recursive: true, force: true }); dir = undefined; });
10
- const SOURCE = 'export function total(items: number[]) {\n let sum = 0;\n for (let i = 0; i <= items.length; i++) sum += items[i];\n return sum;\n}\n';
11
- const facts = { path: 'total.ts', language: 'typescript', classes: [], functions: [] };
12
- function finding(line, category, description) {
13
- return { category, severity: 'high', file: 'total.ts', line, description, suggestion: 's', confidence: 0.9 };
14
- }
15
- const isCheck = (prompt) => /^You reported \d+ defect/.test(prompt);
16
- /** Review passes answer from `reviews` in turn; the self-check withdraws findings whose line is in `withdraw`. */
17
- function fakeProvider(reviews, withdraw) {
18
- const prompts = [];
19
- const provider = {
20
- name: 'fake', isAvailable: async () => true, setup: async () => { }, dispose: () => { },
21
- analyze: async (prompt) => {
22
- prompts.push(prompt);
23
- if (isCheck(prompt)) {
24
- const listed = [...prompt.matchAll(/^(\d+)\. total\.ts:(\d+):/gm)];
25
- return JSON.stringify({ verdicts: listed.map(m => ({ n: Number(m[1]), real: !withdraw.includes(Number(m[2])) })) });
26
- }
27
- return JSON.stringify({ findings: reviews.shift() ?? [] });
28
- },
29
- };
30
- return { provider, prompts };
31
- }
32
- function run(provider, reference) {
33
- dir = fs.mkdtempSync(path.join(os.tmpdir(), 'code-pass-'));
34
- fs.writeFileSync(path.join(dir, 'total.ts'), SOURCE);
35
- return runCodePass(provider, [facts], { cwd: dir, inference: {}, maxSourceChars: 10_000, focusLines: { 'total.ts': [3] }, reference });
36
- }
37
- describe('runCodePass', () => {
38
- it('reviews once without reference material (the small tiers)', async () => {
39
- const { provider, prompts } = fakeProvider([[finding(3, 'correctness', 'off by one on `items`')]], []);
40
- const result = await run(provider);
41
- expect(prompts).toHaveLength(1);
42
- expect(result.findings.map(f => f.line)).toEqual([3]);
43
- expect([result.proposed, result.withdrawn]).toEqual([1, 0]);
44
- });
45
- it('does not repeat an identical pass when there is only one reference section', async () => {
46
- const { provider, prompts } = fakeProvider([[]], []);
47
- await run(provider, { maxChars: 4000, prBody: 'Sum the basket.' });
48
- expect(prompts).toHaveLength(1);
49
- });
50
- it('with reference material, reviews twice in opposite orders, merges, and keeps what one self-check call confirms', async () => {
51
- const { provider, prompts } = fakeProvider([
52
- [finding(3, 'correctness', 'reads past the end of `items`')],
53
- [finding(3, 'correctness', 'same bug seen again'), finding(2, 'consistency', 'style-ish claim about `sum`')],
54
- ], [2]);
55
- const result = await run(provider, {
56
- maxChars: 4000, prBody: 'Sum the basket.',
57
- removed: { 'total.ts': [{ line: 3, text: [' for (let i = 0; i < items.length; i++) sum += items[i];'] }] },
58
- });
59
- const reviews = prompts.filter(p => !isCheck(p));
60
- expect(reviews).toHaveLength(2);
61
- expect(reviews[0].indexOf('PR DESCRIPTION')).toBeLessThan(reviews[0].indexOf('REMOVED by this change'));
62
- expect(reviews[1].indexOf('REMOVED by this change')).toBeLessThan(reviews[1].indexOf('PR DESCRIPTION'));
63
- expect(prompts.filter(isCheck)).toHaveLength(1);
64
- expect(result.findings.map(f => [f.line, f.category])).toEqual([[3, 'correctness']]);
65
- expect([result.proposed, result.withdrawn]).toEqual([2, 1]); // two distinct findings, one withdrawn
66
- });
67
- it('reads only well-formed verdicts from a self-check reply', () => {
68
- expect([...parseVerdicts('Sure! {"verdicts": [{"n": 1, "real": false}, {"n": 2, "real": true}, {"n": "3", "real": false}]}')])
69
- .toEqual([[1, false], [2, true]]);
70
- expect(parseVerdicts('I am not sure').size).toBe(0);
71
- });
72
- it('keeps every finding when the self-check call fails', async () => {
73
- const { provider } = fakeProvider([[finding(3, 'correctness', 'reads past the end of `items`')], []], []);
74
- const analyze = provider.analyze;
75
- provider.analyze = async (prompt, options) => isCheck(prompt) ? Promise.reject(new Error('timeout')) : analyze(prompt, options);
76
- const result = await run(provider, { maxChars: 4000, prBody: 'Sum the basket.', removed: { 'total.ts': [{ line: 3, text: ['x'] }] } });
77
- expect(result.findings.map(f => f.line)).toEqual([3]);
78
- expect(result.withdrawn).toBe(0);
79
- });
80
- it('starts no file after the deadline, and counts what it skipped', async () => {
81
- const { provider, prompts } = fakeProvider([[]], []);
82
- dir = fs.mkdtempSync(path.join(os.tmpdir(), 'code-pass-'));
83
- fs.writeFileSync(path.join(dir, 'total.ts'), SOURCE);
84
- const result = await runCodePass(provider, [facts, facts], { cwd: dir, inference: {}, maxSourceChars: 10_000, deadline: Date.now() - 1 });
85
- expect(prompts).toHaveLength(0);
86
- expect(result.skipped).toBe(2);
87
- });
88
- it('reviews files concurrently but reports them in file order', async () => {
89
- dir = fs.mkdtempSync(path.join(os.tmpdir(), 'code-pass-'));
90
- const names = ['a.ts', 'b.ts', 'c.ts'];
91
- for (const name of names)
92
- fs.writeFileSync(path.join(dir, name), SOURCE);
93
- let inFlight = 0;
94
- let peak = 0;
95
- const provider = {
96
- name: 'fake', isAvailable: async () => true, setup: async () => { }, dispose: () => { },
97
- analyze: async (prompt) => {
98
- inFlight++;
99
- peak = Math.max(peak, inFlight);
100
- const file = names.find(n => prompt.includes(n));
101
- await new Promise(resolve => setTimeout(resolve, file === 'a.ts' ? 30 : 5));
102
- inFlight--;
103
- return JSON.stringify({ findings: [{ ...finding(3, 'correctness', 'off by one'), file }] });
104
- },
105
- };
106
- const many = names.map(name => ({ ...facts, path: name }));
107
- const result = await runCodePass(provider, many, { cwd: dir, inference: {}, maxSourceChars: 10_000, concurrency: 2 });
108
- expect(peak).toBe(2);
109
- expect(result.findings.map(f => f.file)).toEqual(names);
110
- expect(result.contexts.map(c => c.file)).toEqual(names);
111
- });
112
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,20 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { buildCodeReviewPrompt, REVIEW_CATEGORIES } from './code-review-prompt.js';
3
- const context = { file: 'src/a.ts', language: 'typescript', text: 'FILE: src/a.ts\n1| export const a = 1;', ranges: [[1, 1]], source: '' };
4
- describe('buildCodeReviewPrompt', () => {
5
- it('includes every category, the rules and the numbered source', () => {
6
- const prompt = buildCodeReviewPrompt(context);
7
- for (const c of REVIEW_CATEGORIES)
8
- expect(prompt).toContain(`- ${c.key}: ${c.focus}`);
9
- expect(prompt).toContain('Report only defects you can point to');
10
- expect(prompt).toContain('1| export const a = 1;');
11
- expect(prompt).toContain('Review src/a.ts');
12
- });
13
- it('allows an empty answer so the model is not pushed to invent findings', () => {
14
- expect(buildCodeReviewPrompt(context)).toContain('{"findings": []}');
15
- });
16
- it('uses unique category keys', () => {
17
- const keys = REVIEW_CATEGORIES.map(c => c.key);
18
- expect(new Set(keys).size).toBe(keys.length);
19
- });
20
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,54 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { quotedIdentifiers, verifyCodeFindings } from './code-verifier.js';
3
- const context = {
4
- file: 'src/api/report.ts',
5
- language: 'typescript',
6
- text: '',
7
- ranges: [[10, 40]],
8
- source: 'export async function loadReport(days) {\n const rows = await fetchAll(days);\n}',
9
- };
10
- function finding(overrides) {
11
- return {
12
- category: 'scalability', severity: 'high', file: 'src/api/report.ts', line: 12,
13
- description: '`fetchAll` loads every row for the window into memory.', suggestion: 'Aggregate in the database.',
14
- confidence: 0.8, ...overrides,
15
- };
16
- }
17
- describe('verifyCodeFindings', () => {
18
- it('counts every dropped finding by reason, so a drop is never silent', () => {
19
- const rejected = {};
20
- verifyCodeFindings([
21
- finding({}), finding({ file: 'src/other.ts' }), finding({ line: 80 }), finding({ line: undefined }),
22
- finding({ confidence: 0.1 }), finding({ description: '`fetchEverything` has no limit.' }),
23
- ], [context], rejected);
24
- expect(rejected).toEqual({ no_context: 1, out_of_range: 1, no_line: 1, low_confidence: 1, ungrounded_identifier: 1 });
25
- });
26
- it('keeps a finding grounded in the sent source', () => {
27
- const [kept] = verifyCodeFindings([finding({})], [context]);
28
- expect(kept).toMatchObject({ file: 'src/api/report.ts', verified: true });
29
- });
30
- it('resolves a basename-only file for a single-file review', () => {
31
- expect(verifyCodeFindings([finding({ file: 'report.ts' })], [context])).toHaveLength(1);
32
- });
33
- it('drops findings for files that were not sent', () => {
34
- expect(verifyCodeFindings([finding({ file: 'src/other.ts' })], [context])).toHaveLength(0);
35
- });
36
- it('drops findings whose line is outside the sent ranges', () => {
37
- expect(verifyCodeFindings([finding({ line: 80 })], [context])).toHaveLength(0);
38
- });
39
- it('drops findings that cite an identifier the model was never shown', () => {
40
- expect(verifyCodeFindings([finding({ description: '`fetchEverything` has no limit.' })], [context])).toHaveLength(0);
41
- });
42
- it('drops findings that cite no line, however confident', () => {
43
- expect(verifyCodeFindings([finding({ line: undefined, confidence: 0.9 })], [context])).toHaveLength(0);
44
- expect(verifyCodeFindings([finding({ line: null, description: 'The code is well-structured.' })], [context])).toHaveLength(0);
45
- });
46
- it('drops very low confidence findings', () => {
47
- expect(verifyCodeFindings([finding({ confidence: 0.2 })], [context])).toHaveLength(0);
48
- });
49
- });
50
- describe('quotedIdentifiers', () => {
51
- it('extracts identifier-like backticked tokens only', () => {
52
- expect(quotedIdentifiers('`fetchAll()` and `rows.length` but not `a + b` or `"quoted"`')).toEqual(['fetchAll', 'rows.length']);
53
- });
54
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,33 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { safeCalls, testFileSource } from './calls.js';
3
- describe('safeCalls', () => {
4
- it('keeps calls of the function with literal arguments only', () => {
5
- expect(safeCalls('encodePath', [
6
- "encodePath('/a b')",
7
- "encodePath('/x?y', { keepQuery: true, depth: -1 }, [1, null, undefined]);",
8
- 'encodePath(`/%`)',
9
- ])).toEqual(["encodePath('/a b')", "encodePath('/x?y', { keepQuery: true, depth: -1 }, [1, null, undefined])", 'encodePath(`/%`)']);
10
- });
11
- it('drops anything that could do more than call the function on data', () => {
12
- expect(safeCalls('encodePath', [
13
- "require('fs').rmSync('/')",
14
- "encodePath(process.env.HOME)",
15
- "encodePath((() => { fetch('x') })())",
16
- 'encodePath(`${secret}`)',
17
- "other('/a')",
18
- "encodePath('/a'); encodePath('/b')",
19
- "encodePath({ [key]: 1 })",
20
- ])).toEqual([]);
21
- });
22
- });
23
- describe('testFileSource', () => {
24
- it('imports the function, runs each call and records return values or errors without asserting', () => {
25
- const source = testFileSource('vitest', 'encodePath', '../src/path', ["encodePath('/a b')"]);
26
- expect(source).toContain("import { test } from 'vitest';");
27
- expect(source).toContain('import { encodePath } from "../src/path";');
28
- expect(source).toContain(`["encodePath('/a b')", () => encodePath('/a b')],`);
29
- expect(source).toContain('writeFileSync(process.env.RIGOUR_DIFF_OUT as string, JSON.stringify(out));');
30
- expect(source).not.toContain('expect(');
31
- expect(testFileSource('jest', 'f', './f', ['f(1)'])).not.toContain("from 'vitest'");
32
- });
33
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,58 +0,0 @@
1
- import { execFileSync } from 'child_process';
2
- import fs from 'fs';
3
- import os from 'os';
4
- import path from 'path';
5
- import { afterEach, describe, expect, it } from 'vitest';
6
- import { runDiffTests } from './run.js';
7
- // The workspace root's node_modules, where the vitest binary is installed.
8
- const WORKSPACE_MODULES = path.resolve(__dirname, '../../../../../node_modules');
9
- let repo;
10
- afterEach(() => { if (repo)
11
- fs.rmSync(repo, { recursive: true, force: true }); repo = undefined; });
12
- const BEFORE = "export function encodePath(p: string): string {\n return p.replace(/ /g, '%20').replace(/#/g, '%23');\n}\n";
13
- const AFTER = "export function encodePath(p: string): string {\n return p.replace(/#/g, '%23');\n}\n";
14
- function repoWithChange() {
15
- repo = fs.mkdtempSync(path.join(os.tmpdir(), 'diff-tests-'));
16
- const git = (...args) => execFileSync('git', args, { cwd: repo });
17
- git('init', '-q');
18
- git('config', 'user.email', 't@example.com');
19
- git('config', 'user.name', 't');
20
- git('config', 'commit.gpgsign', 'false');
21
- fs.writeFileSync(path.join(repo, 'package.json'), JSON.stringify({ name: 'paths', type: 'module', devDependencies: { vitest: '*' } }));
22
- fs.writeFileSync(path.join(repo, '.gitignore'), 'node_modules\n');
23
- fs.mkdirSync(path.join(repo, 'src'));
24
- fs.writeFileSync(path.join(repo, 'src/encode.ts'), BEFORE);
25
- git('add', '-A');
26
- git('commit', '-qm', 'init');
27
- fs.symlinkSync(WORKSPACE_MODULES, path.join(repo, 'node_modules'), process.platform === 'win32' ? 'junction' : 'dir');
28
- fs.writeFileSync(path.join(repo, 'src/encode.ts'), AFTER);
29
- return repo;
30
- }
31
- function provider(calls, intended = false) {
32
- return {
33
- name: 'fake', isAvailable: async () => true, setup: async () => { }, dispose: () => { },
34
- analyze: async (prompt) => prompt.includes('"intended"') ? JSON.stringify({ intended }) : JSON.stringify({ calls }),
35
- };
36
- }
37
- describe('runDiffTests', () => {
38
- it('reports a call whose outcome changed between the base and the change', async () => {
39
- const cwd = repoWithChange();
40
- const changes = await runDiffTests({
41
- cwd, baseRef: 'HEAD', focusLines: { 'src/encode.ts': [2] }, inference: {},
42
- provider: provider(["encodePath('/a b')", "encodePath('/x#y')", "process.exit(1)"]),
43
- });
44
- expect(changes).toEqual([{
45
- file: 'src/encode.ts', line: 2, name: 'encodePath', call: "encodePath('/a b')",
46
- before: 'returns "/a%20b"', after: 'returns "/a b"',
47
- }]);
48
- expect(fs.readdirSync(path.join(cwd, 'src'))).toEqual(['encode.ts']);
49
- }, 60_000);
50
- it('drops a change the PR description says is intended', async () => {
51
- const cwd = repoWithChange();
52
- const changes = await runDiffTests({
53
- cwd, baseRef: 'HEAD', focusLines: { 'src/encode.ts': [2] }, inference: {}, prBody: 'Stop encoding spaces.',
54
- provider: provider(["encodePath('/a b')"], true),
55
- });
56
- expect(changes).toEqual([]);
57
- }, 60_000);
58
- });
@@ -1 +0,0 @@
1
- export {};