@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,1206 +0,0 @@
1
- /**
2
- * Hallucinated Imports Gate — Comprehensive Regression Tests
3
- *
4
- * Coverage: Go, Python, JS/TS, Ruby, C#, Rust, Java, Kotlin
5
- *
6
- * Tests the fix for Go stdlib false positives (PicoClaw regression)
7
- * and validates all 8 language checkers for false positive/negative accuracy.
8
- *
9
- */
10
- import { describe, it, expect, vi, beforeEach } from 'vitest';
11
- import { HallucinatedImportsGate } from './hallucinated-imports/index.js';
12
- import path from 'path';
13
- // Mock fs-extra — vi.hoisted ensures these are available when vi.mock runs (hoisted)
14
- const { mockPathExists, mockPathExistsSync, mockReadFile, mockReadFileSync, mockReadJson, mockReaddirSync, mockStat } = vi.hoisted(() => ({
15
- mockPathExists: vi.fn(),
16
- mockPathExistsSync: vi.fn(),
17
- mockReadFile: vi.fn(),
18
- mockReadFileSync: vi.fn(),
19
- mockReadJson: vi.fn(),
20
- mockReaddirSync: vi.fn().mockReturnValue([]),
21
- mockStat: vi.fn(),
22
- }));
23
- vi.mock('fs-extra', () => {
24
- const mock = {
25
- pathExists: mockPathExists,
26
- pathExistsSync: mockPathExistsSync,
27
- readFile: mockReadFile,
28
- readFileSync: mockReadFileSync,
29
- readJson: mockReadJson,
30
- readdirSync: mockReaddirSync,
31
- stat: mockStat,
32
- };
33
- return {
34
- ...mock,
35
- default: mock,
36
- };
37
- });
38
- // Mock FileScanner
39
- vi.mock('../utils/scanner.js', () => ({
40
- FileScanner: {
41
- findFiles: vi.fn().mockResolvedValue([]),
42
- },
43
- }));
44
- import { FileScanner } from '../utils/scanner.js';
45
- const normalizePath = (input) => input.replace(/\\/g, '/');
46
- // ═══════════════════════════════════════════════════════════════
47
- // GO
48
- // ═══════════════════════════════════════════════════════════════
49
- describe('HallucinatedImportsGate — Go stdlib false positives', () => {
50
- let gate;
51
- const testCwd = '/tmp/test-go-project';
52
- const context = { cwd: testCwd, ignore: [] };
53
- beforeEach(() => {
54
- vi.clearAllMocks();
55
- mockReaddirSync.mockReturnValue([]);
56
- gate = new HallucinatedImportsGate({ enabled: true });
57
- });
58
- it('should NOT flag Go standard library packages as hallucinated (PicoClaw regression)', async () => {
59
- const goFileContent = `package main
60
-
61
- import (
62
- "encoding/json"
63
- "path/filepath"
64
- "net/http"
65
- "crypto/rand"
66
- "crypto/sha256"
67
- "encoding/base64"
68
- "os/exec"
69
- "os/signal"
70
- "net/url"
71
- "fmt"
72
- "io"
73
- "os"
74
- "strings"
75
- "context"
76
- "sync"
77
- "time"
78
- "log"
79
- "errors"
80
- "io/ioutil"
81
- "io/fs"
82
- "math/rand"
83
- "regexp"
84
- "strconv"
85
- "bytes"
86
- "bufio"
87
- "sort"
88
- "testing"
89
- "net/http/httptest"
90
- "database/sql"
91
- "html/template"
92
- "text/template"
93
- "archive/zip"
94
- "compress/gzip"
95
- "runtime/debug"
96
- )
97
-
98
- func main() {}
99
- `;
100
- FileScanner.findFiles.mockResolvedValue(['main.go']);
101
- mockReadFile.mockResolvedValue(goFileContent);
102
- mockPathExists.mockResolvedValue(false);
103
- mockPathExistsSync.mockReturnValue(false);
104
- const failures = await gate.run(context);
105
- expect(failures).toHaveLength(0);
106
- });
107
- it('should NOT flag external module imports (github.com, etc.)', async () => {
108
- const goFileContent = `package main
109
-
110
- import (
111
- "github.com/gin-gonic/gin"
112
- "github.com/stretchr/testify/assert"
113
- "google.golang.org/grpc"
114
- "go.uber.org/zap"
115
- "golang.org/x/crypto/bcrypt"
116
- )
117
-
118
- func main() {}
119
- `;
120
- FileScanner.findFiles.mockResolvedValue(['main.go']);
121
- mockReadFile.mockResolvedValue(goFileContent);
122
- mockPathExists.mockResolvedValue(false);
123
- mockPathExistsSync.mockReturnValue(false);
124
- const failures = await gate.run(context);
125
- expect(failures).toHaveLength(0);
126
- });
127
- it('should flag project-relative imports that do not resolve (with go.mod)', async () => {
128
- const goMod = `module github.com/myorg/myproject
129
-
130
- go 1.22
131
- `;
132
- const goFileContent = `package main
133
-
134
- import (
135
- "fmt"
136
- "github.com/myorg/myproject/pkg/realmodule"
137
- "github.com/myorg/myproject/pkg/doesnotexist"
138
- )
139
-
140
- func main() {}
141
- `;
142
- FileScanner.findFiles.mockResolvedValue(['cmd/main.go', 'pkg/realmodule/handler.go']);
143
- mockReadFile.mockImplementation(async (filePath) => {
144
- if (filePath.includes('handler.go'))
145
- return 'package realmodule\n\nimport "fmt"\n\nfunc Handler() {}\n';
146
- return goFileContent;
147
- });
148
- mockPathExists.mockResolvedValue(false);
149
- mockPathExistsSync.mockReturnValue(true);
150
- mockReadFileSync.mockReturnValue(goMod);
151
- const failures = await gate.run(context);
152
- expect(failures).toHaveLength(1);
153
- expect(failures[0].details).toContain('doesnotexist');
154
- expect(failures[0].details).not.toContain('realmodule');
155
- });
156
- it('should NOT flag anything when no go.mod exists and imports have no dots', async () => {
157
- const goFileContent = `package main
158
-
159
- import (
160
- "fmt"
161
- "net/http"
162
- "internal/custom"
163
- )
164
-
165
- func main() {}
166
- `;
167
- FileScanner.findFiles.mockResolvedValue(['main.go']);
168
- mockReadFile.mockResolvedValue(goFileContent);
169
- mockPathExists.mockResolvedValue(false);
170
- mockPathExistsSync.mockReturnValue(false);
171
- const failures = await gate.run(context);
172
- expect(failures).toHaveLength(0);
173
- });
174
- it('should handle single-line imports', async () => {
175
- const goFileContent = `package main
176
-
177
- import "fmt"
178
- import "encoding/json"
179
- import "net/http"
180
-
181
- func main() {}
182
- `;
183
- FileScanner.findFiles.mockResolvedValue(['main.go']);
184
- mockReadFile.mockResolvedValue(goFileContent);
185
- mockPathExists.mockResolvedValue(false);
186
- mockPathExistsSync.mockReturnValue(false);
187
- const failures = await gate.run(context);
188
- expect(failures).toHaveLength(0);
189
- });
190
- it('should handle aliased imports', async () => {
191
- const goFileContent = `package main
192
-
193
- import (
194
- "fmt"
195
- mrand "math/rand"
196
- _ "net/http/pprof"
197
- . "os"
198
- )
199
-
200
- func main() {}
201
- `;
202
- FileScanner.findFiles.mockResolvedValue(['main.go']);
203
- mockReadFile.mockResolvedValue(goFileContent);
204
- mockPathExists.mockResolvedValue(false);
205
- mockPathExistsSync.mockReturnValue(false);
206
- const failures = await gate.run(context);
207
- expect(failures).toHaveLength(0);
208
- });
209
- });
210
- // ═══════════════════════════════════════════════════════════════
211
- // PYTHON
212
- // ═══════════════════════════════════════════════════════════════
213
- describe('HallucinatedImportsGate — Python stdlib coverage', () => {
214
- let gate;
215
- const testCwd = '/tmp/test-py-project';
216
- const context = { cwd: testCwd, ignore: [] };
217
- beforeEach(() => {
218
- vi.clearAllMocks();
219
- mockReaddirSync.mockReturnValue([]);
220
- gate = new HallucinatedImportsGate({ enabled: true });
221
- });
222
- it('should NOT flag Python standard library imports', async () => {
223
- const pyContent = `
224
- import os
225
- import sys
226
- import json
227
- import hashlib
228
- import pathlib
229
- import subprocess
230
- import argparse
231
- import typing
232
- import dataclasses
233
- import functools
234
- import itertools
235
- import collections
236
- import datetime
237
- import re
238
- import math
239
- import random
240
- import threading
241
- import asyncio
242
- from os.path import join, exists
243
- from collections import defaultdict
244
- from typing import List, Optional
245
- from urllib.parse import urlparse
246
- `;
247
- FileScanner.findFiles.mockResolvedValue(['main.py']);
248
- mockReadFile.mockResolvedValue(pyContent);
249
- mockPathExists.mockResolvedValue(false);
250
- const failures = await gate.run(context);
251
- expect(failures).toHaveLength(0);
252
- });
253
- });
254
- // ═══════════════════════════════════════════════════════════════
255
- // JS/TS (Node.js)
256
- // ═══════════════════════════════════════════════════════════════
257
- describe('HallucinatedImportsGate — JS/TS Node builtins', () => {
258
- let gate;
259
- const testCwd = path.resolve('/tmp/test-node-project');
260
- const testCwdNormalized = normalizePath(testCwd);
261
- const context = { cwd: testCwd, ignore: [] };
262
- beforeEach(() => {
263
- vi.clearAllMocks();
264
- mockReaddirSync.mockReturnValue([]);
265
- gate = new HallucinatedImportsGate({ enabled: true });
266
- });
267
- it('resolves changed-file imports against unchanged project files', async () => {
268
- FileScanner.findFiles
269
- .mockResolvedValueOnce(['src/feature.ts'])
270
- .mockResolvedValueOnce(['src/feature.ts', 'src/types.ts']);
271
- mockReadFile.mockResolvedValue("import type { Value } from './types.js';\n");
272
- mockPathExists.mockResolvedValue(false);
273
- const failures = await gate.run({ ...context, patterns: ['src/feature.ts'] });
274
- expect(failures).toEqual([]);
275
- expect(FileScanner.findFiles).toHaveBeenCalledTimes(2);
276
- });
277
- it('should NOT flag Node.js built-in modules', async () => {
278
- const jsContent = `
279
- import fs from 'fs';
280
- import path from 'path';
281
- import crypto from 'crypto';
282
- import http from 'http';
283
- import https from 'https';
284
- import url from 'url';
285
- import os from 'os';
286
- import stream from 'stream';
287
- import util from 'util';
288
- import { readFile } from 'node:fs';
289
- import { join } from 'node:path';
290
- `;
291
- FileScanner.findFiles.mockResolvedValue(['index.ts']);
292
- mockReadFile.mockResolvedValue(jsContent);
293
- mockPathExists.mockResolvedValue(false);
294
- const failures = await gate.run(context);
295
- expect(failures).toHaveLength(0);
296
- });
297
- it('should NOT flag Node 22.x built-in modules (async_hooks, diagnostics_channel, etc.)', async () => {
298
- const jsContent = `
299
- import { AsyncLocalStorage } from 'async_hooks';
300
- import dc from 'diagnostics_channel';
301
- import { readFile } from 'fs/promises';
302
- import test from 'test';
303
- import wt from 'worker_threads';
304
- import timers from 'timers/promises';
305
- import { ReadableStream } from 'stream/web';
306
- `;
307
- FileScanner.findFiles.mockResolvedValue(['server.ts']);
308
- mockReadFile.mockResolvedValue(jsContent);
309
- mockPathExists.mockResolvedValue(false);
310
- const failures = await gate.run(context);
311
- expect(failures).toHaveLength(0);
312
- });
313
- it('should resolve dependencies from nearest package.json in monorepos', async () => {
314
- const jsContent = `
315
- import { app } from 'electron';
316
- import React from 'react';
317
- `;
318
- FileScanner.findFiles.mockResolvedValue(['apps/desktop/src/main.ts']);
319
- mockReadFile.mockResolvedValue(jsContent);
320
- mockPathExists.mockImplementation(async (p) => {
321
- const normalized = normalizePath(p);
322
- return normalized === `${testCwdNormalized}/apps/desktop/package.json`
323
- || normalized === `${testCwdNormalized}/package.json`
324
- || normalized.includes('/node_modules/electron')
325
- || normalized.includes('/node_modules/react');
326
- });
327
- mockReadJson.mockImplementation(async (p) => {
328
- const normalized = normalizePath(p);
329
- if (normalized.endsWith('/apps/desktop/package.json')) {
330
- return {
331
- dependencies: { electron: '^31.0.0', react: '^18.0.0' },
332
- devDependencies: {},
333
- peerDependencies: {},
334
- optionalDependencies: {},
335
- };
336
- }
337
- // Root package.json should not incorrectly block desktop deps
338
- return {
339
- dependencies: {},
340
- devDependencies: {},
341
- peerDependencies: {},
342
- optionalDependencies: {},
343
- };
344
- });
345
- const failures = await gate.run(context);
346
- expect(failures).toHaveLength(0);
347
- });
348
- it('should NOT flag tsconfig path aliases that resolve in monorepos', async () => {
349
- const jsContent = `
350
- import { logger } from '@/utils/logger';
351
- import { cfg } from '~shared/config';
352
- `;
353
- const tsconfigContent = `{
354
- "compilerOptions": {
355
- "baseUrl": ".",
356
- "paths": {
357
- "@/*": ["src/*"],
358
- "~shared/*": ["../shared/src/*"]
359
- }
360
- }
361
- }`;
362
- FileScanner.findFiles.mockResolvedValue([
363
- 'apps/desktop/src/main.ts',
364
- 'apps/desktop/src/utils/logger.ts',
365
- 'apps/shared/src/config.ts',
366
- ]);
367
- mockReadFile.mockImplementation(async (p) => {
368
- const normalized = normalizePath(p);
369
- if (normalized.endsWith('/apps/desktop/tsconfig.json'))
370
- return tsconfigContent;
371
- if (normalized.endsWith('/apps/desktop/src/main.ts'))
372
- return jsContent;
373
- return 'export const ok = true;';
374
- });
375
- mockPathExists.mockImplementation(async (p) => {
376
- const normalized = normalizePath(p);
377
- return normalized === `${testCwdNormalized}/apps/desktop/tsconfig.json`
378
- || normalized === `${testCwdNormalized}/package.json`;
379
- });
380
- mockStat.mockResolvedValue({ isFile: () => true });
381
- mockReadJson.mockResolvedValue({
382
- dependencies: {},
383
- devDependencies: {},
384
- peerDependencies: {},
385
- optionalDependencies: {},
386
- });
387
- const failures = await gate.run(context);
388
- expect(failures).toHaveLength(0);
389
- });
390
- it('should flag tsconfig path aliases when target does not resolve', async () => {
391
- const jsContent = `import { logger } from '@/utils/missing';`;
392
- const tsconfigContent = `{
393
- "compilerOptions": {
394
- "baseUrl": ".",
395
- "paths": {
396
- "@/*": ["src/*"]
397
- }
398
- }
399
- }`;
400
- FileScanner.findFiles.mockResolvedValue(['apps/desktop/src/main.ts']);
401
- mockReadFile.mockImplementation(async (p) => {
402
- const normalized = normalizePath(p);
403
- if (normalized.endsWith('/apps/desktop/tsconfig.json'))
404
- return tsconfigContent;
405
- return jsContent;
406
- });
407
- mockPathExists.mockImplementation(async (p) => {
408
- const normalized = normalizePath(p);
409
- return normalized === `${testCwdNormalized}/apps/desktop/tsconfig.json`
410
- || normalized === `${testCwdNormalized}/package.json`;
411
- });
412
- mockReadJson.mockResolvedValue({
413
- dependencies: {},
414
- devDependencies: {},
415
- peerDependencies: {},
416
- optionalDependencies: {},
417
- });
418
- const failures = await gate.run(context);
419
- expect(failures).toHaveLength(1);
420
- // Depending on tsconfig resolution context, this may surface as
421
- // a direct alias resolution failure OR a missing package fallback.
422
- const details = failures[0].details;
423
- expect(details.includes("Path alias '@/utils/missing' does not resolve to a project file")
424
- || details.includes("Package '@/utils' not in package.json dependencies")).toBe(true);
425
- });
426
- it('should NOT flag a package importing itself by its own name (type tests, examples), and still flag an undeclared one', async () => {
427
- FileScanner.findFiles.mockResolvedValue(['test-d/types.ts']);
428
- mockReadFile.mockResolvedValue(`import ky from 'ky';\nimport left from 'left-pad';\n`);
429
- mockPathExists.mockImplementation(async (p) => normalizePath(p) === `${testCwdNormalized}/package.json`);
430
- mockReadJson.mockResolvedValue({ name: 'ky', dependencies: {}, devDependencies: {} });
431
- const failures = await gate.run(context);
432
- const details = failures.map(f => f.details).join('\n');
433
- expect(details).not.toContain("Package 'ky'");
434
- expect(details).toContain("Package 'left-pad' not in package.json dependencies");
435
- });
436
- it('should resolve configured aliases to hidden generated files', async () => {
437
- const jsContent = `import { docs } from '@/.source';`;
438
- const tsconfigContent = `{
439
- "compilerOptions": {
440
- "paths": {
441
- "@/*": ["./*"],
442
- "@/.source": ["./.source/index.ts"]
443
- }
444
- }
445
- }`;
446
- FileScanner.findFiles.mockResolvedValue(['docs-site/lib/source.ts']);
447
- mockReadFile.mockImplementation(async (p) => {
448
- const normalized = normalizePath(p);
449
- if (normalized.endsWith('/docs-site/tsconfig.json'))
450
- return tsconfigContent;
451
- return jsContent;
452
- });
453
- mockPathExists.mockImplementation(async (p) => {
454
- const normalized = normalizePath(p);
455
- return normalized === `${testCwdNormalized}/docs-site/tsconfig.json`
456
- || normalized === `${testCwdNormalized}/docs-site/.source/index.ts`
457
- || normalized === `${testCwdNormalized}/package.json`;
458
- });
459
- mockStat.mockResolvedValue({ isFile: () => true });
460
- mockReadJson.mockResolvedValue({
461
- dependencies: {},
462
- devDependencies: {},
463
- peerDependencies: {},
464
- optionalDependencies: {},
465
- });
466
- const failures = await gate.run(context);
467
- expect(failures).toHaveLength(0);
468
- });
469
- it('should NOT flag ESM .js specifiers that resolve to .ts source files', async () => {
470
- const jsContent = `
471
- import { helper } from './utils.js';
472
- `;
473
- FileScanner.findFiles.mockResolvedValue(['src/main.ts', 'src/utils.ts']);
474
- mockReadFile.mockImplementation(async (p) => {
475
- const normalized = p.replace(/\\/g, '/');
476
- if (normalized.endsWith('/src/main.ts'))
477
- return jsContent;
478
- return 'export const helper = () => 42;';
479
- });
480
- mockPathExists.mockImplementation(async (p) => {
481
- const normalized = p.replace(/\\/g, '/');
482
- return normalized === '/tmp/test-node-project/package.json';
483
- });
484
- mockReadJson.mockResolvedValue({
485
- dependencies: {},
486
- devDependencies: {},
487
- peerDependencies: {},
488
- optionalDependencies: {},
489
- });
490
- const failures = await gate.run(context);
491
- expect(failures).toHaveLength(0);
492
- });
493
- it('resolves dotted TypeScript basenames without hiding missing imports', async () => {
494
- const source = `
495
- import './adapter.cli';
496
- import './database.types';
497
- import './missing.cli';
498
- `;
499
- FileScanner.findFiles.mockResolvedValue([
500
- 'src/main.ts', 'src/adapter.cli.ts', 'src/database.types.ts',
501
- ]);
502
- mockReadFile.mockImplementation(async (p) => p.replace(/\\/g, '/').endsWith('/src/main.ts') ? source : 'export const value = 1;');
503
- mockPathExists.mockImplementation(async (p) => p.replace(/\\/g, '/') === '/tmp/test-node-project/package.json');
504
- mockReadJson.mockResolvedValue({ dependencies: {}, devDependencies: {} });
505
- const failures = await gate.run(context);
506
- expect(failures).toHaveLength(1);
507
- expect(failures[0].details).toContain('./missing.cli');
508
- expect(failures[0].details).not.toContain('./adapter.cli');
509
- expect(failures[0].details).not.toContain('./database.types');
510
- });
511
- it('ignores the generated Next.js environment declaration', async () => {
512
- FileScanner.findFiles.mockResolvedValue(['next-env.d.ts']);
513
- mockReadFile.mockResolvedValue("import './.next/dev/types/routes.d.ts';");
514
- const failures = await gate.run(context);
515
- expect(failures).toHaveLength(0);
516
- });
517
- });
518
- describe('HallucinatedImportsGate — ignore generated/test artifacts', () => {
519
- let gate;
520
- const testCwd = '/tmp/test-ignore-project';
521
- const context = { cwd: testCwd, ignore: [] };
522
- beforeEach(() => {
523
- vi.clearAllMocks();
524
- mockReaddirSync.mockReturnValue([]);
525
- gate = new HallucinatedImportsGate({ enabled: true });
526
- });
527
- it('skips studio-dist files by default', async () => {
528
- FileScanner.findFiles.mockResolvedValue(['packages/rigour-cli/studio-dist/assets/index.js']);
529
- mockReadFile.mockResolvedValue(`import 'definitely-not-a-real-package';`);
530
- mockPathExists.mockResolvedValue(false);
531
- const failures = await gate.run(context);
532
- expect(failures).toHaveLength(0);
533
- });
534
- it('skips test files by default', async () => {
535
- FileScanner.findFiles.mockResolvedValue(['src/example.test.ts']);
536
- mockReadFile.mockResolvedValue(`import 'totally-not-installed';`);
537
- mockPathExists.mockResolvedValue(false);
538
- const failures = await gate.run(context);
539
- expect(failures).toHaveLength(0);
540
- });
541
- });
542
- // ═══════════════════════════════════════════════════════════════
543
- // RUBY
544
- // ═══════════════════════════════════════════════════════════════
545
- describe('HallucinatedImportsGate — Ruby imports', () => {
546
- let gate;
547
- const testCwd = '/tmp/test-ruby-project';
548
- const context = { cwd: testCwd, ignore: [] };
549
- beforeEach(() => {
550
- vi.clearAllMocks();
551
- mockReaddirSync.mockReturnValue([]);
552
- gate = new HallucinatedImportsGate({ enabled: true });
553
- });
554
- it('should NOT flag Ruby standard library requires', async () => {
555
- const rbContent = `
556
- require 'json'
557
- require 'yaml'
558
- require 'net/http'
559
- require 'uri'
560
- require 'fileutils'
561
- require 'pathname'
562
- require 'open3'
563
- require 'digest'
564
- require 'openssl'
565
- require 'csv'
566
- require 'set'
567
- require 'date'
568
- require 'time'
569
- require 'tempfile'
570
- require 'securerandom'
571
- require 'logger'
572
- require 'socket'
573
- require 'erb'
574
- require 'optparse'
575
- require 'stringio'
576
- require 'zlib'
577
- require 'base64'
578
- require 'benchmark'
579
- require 'singleton'
580
- require 'forwardable'
581
- require 'shellwords'
582
- require 'bigdecimal'
583
- `;
584
- FileScanner.findFiles.mockResolvedValue(['app.rb']);
585
- mockReadFile.mockResolvedValue(rbContent);
586
- mockPathExists.mockResolvedValue(false);
587
- mockPathExistsSync.mockReturnValue(false);
588
- const failures = await gate.run(context);
589
- expect(failures).toHaveLength(0);
590
- });
591
- it('should NOT flag gems listed in Gemfile', async () => {
592
- const rbContent = `
593
- require 'rails'
594
- require 'pg'
595
- require 'puma'
596
- require 'sidekiq'
597
- require 'devise'
598
- `;
599
- const gemfile = `
600
- source 'https://rubygems.org'
601
-
602
- gem 'rails', '~> 7.0'
603
- gem 'pg'
604
- gem 'puma', '~> 6.0'
605
- gem 'sidekiq'
606
- gem 'devise'
607
- `;
608
- FileScanner.findFiles.mockResolvedValue(['app.rb']);
609
- mockReadFile.mockResolvedValue(rbContent);
610
- mockPathExistsSync.mockImplementation((p) => p.includes('Gemfile'));
611
- mockReadFileSync.mockReturnValue(gemfile);
612
- mockPathExists.mockResolvedValue(false);
613
- const failures = await gate.run(context);
614
- expect(failures).toHaveLength(0);
615
- });
616
- it('should flag unknown requires when Gemfile exists', async () => {
617
- const rbContent = `
618
- require 'json'
619
- require 'nonexistent_gem_abcxyz'
620
- `;
621
- const gemfile = `
622
- source 'https://rubygems.org'
623
- gem 'rails'
624
- `;
625
- FileScanner.findFiles.mockResolvedValue(['app.rb']);
626
- mockReadFile.mockResolvedValue(rbContent);
627
- mockPathExistsSync.mockImplementation((p) => p.includes('Gemfile'));
628
- mockReadFileSync.mockReturnValue(gemfile);
629
- mockPathExists.mockResolvedValue(false);
630
- const failures = await gate.run(context);
631
- expect(failures).toHaveLength(1);
632
- expect(failures[0].details).toContain('nonexistent_gem_abcxyz');
633
- });
634
- it('should flag broken require_relative paths', async () => {
635
- const rbContent = `
636
- require_relative 'lib/helpers'
637
- require_relative 'models/user'
638
- `;
639
- FileScanner.findFiles.mockResolvedValue(['app.rb']);
640
- mockReadFile.mockResolvedValue(rbContent);
641
- mockPathExists.mockResolvedValue(false);
642
- mockPathExistsSync.mockReturnValue(false);
643
- const failures = await gate.run(context);
644
- // Both require_relative should fail since no matching .rb files exist
645
- expect(failures).toHaveLength(1); // grouped into 1 failure by file
646
- expect(failures[0].details).toContain('lib/helpers');
647
- expect(failures[0].details).toContain('models/user');
648
- });
649
- it('should NOT flag require_relative that resolves to project files', async () => {
650
- const rbContent = `
651
- require_relative 'lib/helpers'
652
- `;
653
- FileScanner.findFiles.mockResolvedValue(['app.rb', 'lib/helpers.rb']);
654
- mockReadFile.mockImplementation(async (filePath) => {
655
- if (filePath.includes('helpers.rb'))
656
- return 'module Helpers; end';
657
- return rbContent;
658
- });
659
- mockPathExists.mockResolvedValue(false);
660
- mockPathExistsSync.mockReturnValue(false);
661
- const failures = await gate.run(context);
662
- expect(failures).toHaveLength(0);
663
- });
664
- it('should NOT flag gems from .gemspec add_dependency', async () => {
665
- const rbContent = `
666
- require 'thor'
667
- require 'httparty'
668
- `;
669
- const gemspec = `
670
- Gem::Specification.new do |spec|
671
- spec.name = "mygem"
672
- spec.add_dependency "thor", "~> 1.0"
673
- spec.add_runtime_dependency "httparty"
674
- end
675
- `;
676
- FileScanner.findFiles.mockResolvedValue(['lib/mygem.rb']);
677
- mockReadFile.mockResolvedValue(rbContent);
678
- mockPathExistsSync.mockImplementation((p) => !p.includes('Gemfile'));
679
- mockReaddirSync.mockReturnValue(['mygem.gemspec']);
680
- mockReadFileSync.mockReturnValue(gemspec);
681
- mockPathExists.mockResolvedValue(false);
682
- const failures = await gate.run(context);
683
- expect(failures).toHaveLength(0);
684
- });
685
- it('should skip flagging requires when no Gemfile context exists', async () => {
686
- // Without Gemfile or gemspec, we can't distinguish installed gems from hallucinated ones
687
- const rbContent = `
688
- require 'some_unknown_gem'
689
- `;
690
- FileScanner.findFiles.mockResolvedValue(['script.rb']);
691
- mockReadFile.mockResolvedValue(rbContent);
692
- mockPathExists.mockResolvedValue(false);
693
- mockPathExistsSync.mockReturnValue(false);
694
- const failures = await gate.run(context);
695
- // No Gemfile = gemDeps.size === 0 → skip flagging to avoid false positives
696
- expect(failures).toHaveLength(0);
697
- });
698
- });
699
- // ═══════════════════════════════════════════════════════════════
700
- // C# (.NET)
701
- // ═══════════════════════════════════════════════════════════════
702
- describe('HallucinatedImportsGate — C# imports', () => {
703
- let gate;
704
- const testCwd = '/tmp/test-csharp-project';
705
- const context = { cwd: testCwd, ignore: [] };
706
- beforeEach(() => {
707
- vi.clearAllMocks();
708
- mockReaddirSync.mockReturnValue([]);
709
- gate = new HallucinatedImportsGate({ enabled: true });
710
- });
711
- it('should NOT flag .NET framework namespaces', async () => {
712
- const csContent = `
713
- using System;
714
- using System.Collections.Generic;
715
- using System.Linq;
716
- using System.Threading.Tasks;
717
- using System.IO;
718
- using System.Net.Http;
719
- using System.Text;
720
- using System.Text.Json;
721
- using Microsoft.AspNetCore.Mvc;
722
- using Microsoft.Extensions.DependencyInjection;
723
- using Microsoft.Extensions.Logging;
724
- using Microsoft.EntityFrameworkCore;
725
- `;
726
- FileScanner.findFiles.mockResolvedValue(['Controllers/HomeController.cs']);
727
- mockReadFile.mockResolvedValue(csContent);
728
- mockPathExists.mockResolvedValue(false);
729
- mockPathExistsSync.mockReturnValue(false);
730
- const failures = await gate.run(context);
731
- expect(failures).toHaveLength(0);
732
- });
733
- it('should NOT flag NuGet packages from .csproj', async () => {
734
- const csContent = `
735
- using Newtonsoft.Json;
736
- using Serilog;
737
- using AutoMapper;
738
- using FluentValidation;
739
- using MediatR;
740
- `;
741
- const csproj = `
742
- <Project Sdk="Microsoft.NET.Sdk">
743
- <ItemGroup>
744
- <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
745
- <PackageReference Include="Serilog" Version="3.1.1" />
746
- <PackageReference Include="AutoMapper" Version="12.0.1" />
747
- <PackageReference Include="FluentValidation" Version="11.8.0" />
748
- <PackageReference Include="MediatR" Version="12.2.0" />
749
- </ItemGroup>
750
- </Project>
751
- `;
752
- FileScanner.findFiles.mockResolvedValue(['Program.cs']);
753
- mockReadFile.mockResolvedValue(csContent);
754
- mockPathExists.mockResolvedValue(false);
755
- mockPathExistsSync.mockReturnValue(false);
756
- mockReaddirSync.mockReturnValue(['MyProject.csproj']);
757
- mockReadFileSync.mockReturnValue(csproj);
758
- const failures = await gate.run(context);
759
- expect(failures).toHaveLength(0);
760
- });
761
- it('should NOT flag using static directives', async () => {
762
- const csContent = `
763
- using System;
764
- using static System.Math;
765
- using static System.Console;
766
- `;
767
- FileScanner.findFiles.mockResolvedValue(['Helper.cs']);
768
- mockReadFile.mockResolvedValue(csContent);
769
- mockPathExists.mockResolvedValue(false);
770
- mockPathExistsSync.mockReturnValue(false);
771
- const failures = await gate.run(context);
772
- expect(failures).toHaveLength(0);
773
- });
774
- it('should NOT flag using disposable pattern (using var = ...)', async () => {
775
- const csContent = `
776
- using System;
777
- using (var stream = new FileStream("test.txt", FileMode.Open))
778
- {
779
- // Should not be parsed as a namespace import
780
- }
781
- `;
782
- FileScanner.findFiles.mockResolvedValue(['Program.cs']);
783
- mockReadFile.mockResolvedValue(csContent);
784
- mockPathExists.mockResolvedValue(false);
785
- mockPathExistsSync.mockReturnValue(false);
786
- const failures = await gate.run(context);
787
- expect(failures).toHaveLength(0);
788
- });
789
- it('should flag project-relative namespaces that do not resolve (with .csproj)', async () => {
790
- const csContent = `
791
- using System;
792
- using MyProject.Services.UserService;
793
- using MyProject.Models.DoesNotExist;
794
- `;
795
- const csContent2 = `
796
- namespace MyProject.Services.UserService
797
- {
798
- public class UserService { }
799
- }
800
- `;
801
- const csproj = `<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup></Project>`;
802
- FileScanner.findFiles.mockResolvedValue([
803
- 'Controllers/HomeController.cs',
804
- 'Services/UserService/UserService.cs',
805
- ]);
806
- mockReadFile.mockImplementation(async (filePath) => {
807
- if (filePath.includes('UserService'))
808
- return csContent2;
809
- return csContent;
810
- });
811
- mockPathExists.mockResolvedValue(false);
812
- mockPathExistsSync.mockReturnValue(false);
813
- mockReaddirSync.mockReturnValue(['MyProject.csproj']);
814
- mockReadFileSync.mockReturnValue(csproj);
815
- const failures = await gate.run(context);
816
- // Should flag DoesNotExist but not UserService
817
- expect(failures).toHaveLength(1);
818
- expect(failures[0].details).toContain('DoesNotExist');
819
- expect(failures[0].details).not.toContain('UserService');
820
- });
821
- it('should NOT flag common ecosystem NuGet packages', async () => {
822
- const csContent = `
823
- using Xunit;
824
- using Moq;
825
- using FluentAssertions;
826
- using NUnit.Framework;
827
- using Dapper;
828
- using Polly;
829
- using StackExchange.Redis;
830
- using Npgsql;
831
- using Grpc.Core;
832
- `;
833
- FileScanner.findFiles.mockResolvedValue(['Tests.cs']);
834
- mockReadFile.mockResolvedValue(csContent);
835
- mockPathExists.mockResolvedValue(false);
836
- mockPathExistsSync.mockReturnValue(false);
837
- const failures = await gate.run(context);
838
- expect(failures).toHaveLength(0);
839
- });
840
- });
841
- // ═══════════════════════════════════════════════════════════════
842
- // RUST
843
- // ═══════════════════════════════════════════════════════════════
844
- describe('HallucinatedImportsGate — Rust imports', () => {
845
- let gate;
846
- const testCwd = '/tmp/test-rust-project';
847
- const context = { cwd: testCwd, ignore: [] };
848
- beforeEach(() => {
849
- vi.clearAllMocks();
850
- mockReaddirSync.mockReturnValue([]);
851
- gate = new HallucinatedImportsGate({ enabled: true });
852
- });
853
- it('should NOT flag Rust std library crates', async () => {
854
- const rsContent = `
855
- use std::collections::HashMap;
856
- use std::io::{self, Read, Write};
857
- use std::fs;
858
- use std::path::PathBuf;
859
- use std::sync::Arc;
860
- use core::fmt;
861
- use alloc::vec::Vec;
862
- `;
863
- FileScanner.findFiles.mockResolvedValue(['src/main.rs']);
864
- mockReadFile.mockResolvedValue(rsContent);
865
- mockPathExists.mockResolvedValue(false);
866
- mockPathExistsSync.mockReturnValue(false);
867
- const failures = await gate.run(context);
868
- expect(failures).toHaveLength(0);
869
- });
870
- it('should NOT flag crates listed in Cargo.toml', async () => {
871
- const rsContent = `
872
- use serde::{Serialize, Deserialize};
873
- use tokio::runtime::Runtime;
874
- use reqwest::Client;
875
- use clap::Parser;
876
- `;
877
- const cargoToml = `
878
- [package]
879
- name = "my-project"
880
- version = "0.1.0"
881
-
882
- [dependencies]
883
- serde = { version = "1.0", features = ["derive"] }
884
- tokio = { version = "1.0", features = ["full"] }
885
- reqwest = "0.11"
886
- clap = { version = "4.0", features = ["derive"] }
887
- `;
888
- FileScanner.findFiles.mockResolvedValue(['src/main.rs']);
889
- mockReadFile.mockResolvedValue(rsContent);
890
- mockPathExists.mockResolvedValue(false);
891
- mockPathExistsSync.mockImplementation((p) => p.includes('Cargo.toml'));
892
- mockReadFileSync.mockReturnValue(cargoToml);
893
- const failures = await gate.run(context);
894
- expect(failures).toHaveLength(0);
895
- });
896
- it('should handle Cargo.toml dash-to-underscore conversion', async () => {
897
- const rsContent = `
898
- use my_crate::something;
899
- use another_lib::util;
900
- `;
901
- const cargoToml = `
902
- [dependencies]
903
- my-crate = "1.0"
904
- another-lib = "2.0"
905
- `;
906
- FileScanner.findFiles.mockResolvedValue(['src/main.rs']);
907
- mockReadFile.mockResolvedValue(rsContent);
908
- mockPathExists.mockResolvedValue(false);
909
- mockPathExistsSync.mockImplementation((p) => p.includes('Cargo.toml'));
910
- mockReadFileSync.mockReturnValue(cargoToml);
911
- const failures = await gate.run(context);
912
- expect(failures).toHaveLength(0);
913
- });
914
- it('should NOT flag crate/self/super keywords', async () => {
915
- const rsContent = `
916
- use crate::config::Settings;
917
- use self::helpers::format;
918
- use super::parent_module;
919
- `;
920
- FileScanner.findFiles.mockResolvedValue(['src/lib.rs']);
921
- mockReadFile.mockResolvedValue(rsContent);
922
- mockPathExists.mockResolvedValue(false);
923
- mockPathExistsSync.mockReturnValue(false);
924
- const failures = await gate.run(context);
925
- expect(failures).toHaveLength(0);
926
- });
927
- it('should flag unknown extern crate not in Cargo.toml', async () => {
928
- const rsContent = `
929
- extern crate serde;
930
- extern crate nonexistent_crate;
931
- `;
932
- const cargoToml = `
933
- [dependencies]
934
- serde = "1.0"
935
- `;
936
- FileScanner.findFiles.mockResolvedValue(['src/main.rs']);
937
- mockReadFile.mockResolvedValue(rsContent);
938
- mockPathExists.mockResolvedValue(false);
939
- mockPathExistsSync.mockImplementation((p) => p.includes('Cargo.toml'));
940
- mockReadFileSync.mockReturnValue(cargoToml);
941
- const failures = await gate.run(context);
942
- expect(failures).toHaveLength(1);
943
- expect(failures[0].details).toContain('nonexistent_crate');
944
- expect(failures[0].details).not.toContain('serde');
945
- });
946
- it('should flag unknown use crate not in Cargo.toml', async () => {
947
- const rsContent = `
948
- use serde::Serialize;
949
- use fake_crate::FakeStruct;
950
- `;
951
- const cargoToml = `
952
- [dependencies]
953
- serde = "1.0"
954
- `;
955
- FileScanner.findFiles.mockResolvedValue(['src/main.rs']);
956
- mockReadFile.mockResolvedValue(rsContent);
957
- mockPathExists.mockResolvedValue(false);
958
- mockPathExistsSync.mockImplementation((p) => p.includes('Cargo.toml'));
959
- mockReadFileSync.mockReturnValue(cargoToml);
960
- const failures = await gate.run(context);
961
- expect(failures).toHaveLength(1);
962
- expect(failures[0].details).toContain('fake_crate');
963
- });
964
- it('should NOT flag pub use re-exports of known crates', async () => {
965
- const rsContent = `
966
- pub use serde::Serialize;
967
- pub use std::collections::HashMap;
968
- `;
969
- const cargoToml = `
970
- [dependencies]
971
- serde = "1.0"
972
- `;
973
- FileScanner.findFiles.mockResolvedValue(['src/lib.rs']);
974
- mockReadFile.mockResolvedValue(rsContent);
975
- mockPathExists.mockResolvedValue(false);
976
- mockPathExistsSync.mockImplementation((p) => p.includes('Cargo.toml'));
977
- mockReadFileSync.mockReturnValue(cargoToml);
978
- const failures = await gate.run(context);
979
- expect(failures).toHaveLength(0);
980
- });
981
- it('should handle [dev-dependencies] and [build-dependencies]', async () => {
982
- const rsContent = `
983
- use criterion::Criterion;
984
- use cc::Build;
985
- `;
986
- const cargoToml = `
987
- [dependencies]
988
- serde = "1.0"
989
-
990
- [dev-dependencies]
991
- criterion = "0.5"
992
-
993
- [build-dependencies]
994
- cc = "1.0"
995
- `;
996
- FileScanner.findFiles.mockResolvedValue(['benches/bench.rs']);
997
- mockReadFile.mockResolvedValue(rsContent);
998
- mockPathExists.mockResolvedValue(false);
999
- mockPathExistsSync.mockImplementation((p) => p.includes('Cargo.toml'));
1000
- mockReadFileSync.mockReturnValue(cargoToml);
1001
- const failures = await gate.run(context);
1002
- expect(failures).toHaveLength(0);
1003
- });
1004
- });
1005
- // ═══════════════════════════════════════════════════════════════
1006
- // JAVA
1007
- // ═══════════════════════════════════════════════════════════════
1008
- describe('HallucinatedImportsGate — Java imports', () => {
1009
- let gate;
1010
- const testCwd = '/tmp/test-java-project';
1011
- const context = { cwd: testCwd, ignore: [] };
1012
- beforeEach(() => {
1013
- vi.clearAllMocks();
1014
- mockReaddirSync.mockReturnValue([]);
1015
- gate = new HallucinatedImportsGate({ enabled: true });
1016
- });
1017
- it('should NOT flag Java standard library imports', async () => {
1018
- const javaContent = `
1019
- import java.util.List;
1020
- import java.util.Map;
1021
- import java.util.HashMap;
1022
- import java.io.File;
1023
- import java.io.IOException;
1024
- import java.net.URL;
1025
- import java.time.LocalDateTime;
1026
- import java.util.stream.Collectors;
1027
- import javax.net.ssl.SSLContext;
1028
- import jakarta.servlet.http.HttpServletRequest;
1029
- `;
1030
- FileScanner.findFiles.mockResolvedValue(['src/main/java/App.java']);
1031
- mockReadFile.mockResolvedValue(javaContent);
1032
- mockPathExists.mockResolvedValue(false);
1033
- mockPathExistsSync.mockReturnValue(false);
1034
- const failures = await gate.run(context);
1035
- expect(failures).toHaveLength(0);
1036
- });
1037
- it('should NOT flag import static for Java stdlib', async () => {
1038
- const javaContent = `
1039
- import static java.lang.Math.max;
1040
- import static java.util.Collections.emptyList;
1041
- `;
1042
- FileScanner.findFiles.mockResolvedValue(['Helper.java']);
1043
- mockReadFile.mockResolvedValue(javaContent);
1044
- mockPathExists.mockResolvedValue(false);
1045
- mockPathExistsSync.mockReturnValue(false);
1046
- const failures = await gate.run(context);
1047
- expect(failures).toHaveLength(0);
1048
- });
1049
- it('should NOT flag build.gradle dependencies', async () => {
1050
- const javaContent = `
1051
- import com.google.guava.collect.ImmutableList;
1052
- import org.springframework.boot.SpringApplication;
1053
- import io.netty.channel.Channel;
1054
- `;
1055
- const buildGradle = `
1056
- plugins {
1057
- id 'java'
1058
- }
1059
-
1060
- dependencies {
1061
- implementation 'com.google.guava:guava:32.0.0-jre'
1062
- implementation 'org.springframework.boot:spring-boot-starter:3.1.0'
1063
- implementation 'io.netty:netty-all:4.1.100'
1064
- }
1065
- `;
1066
- FileScanner.findFiles.mockResolvedValue(['src/main/java/App.java']);
1067
- mockReadFile.mockResolvedValue(javaContent);
1068
- mockPathExists.mockResolvedValue(false);
1069
- mockPathExistsSync.mockImplementation((p) => p.includes('build.gradle'));
1070
- mockReadFileSync.mockReturnValue(buildGradle);
1071
- const failures = await gate.run(context);
1072
- expect(failures).toHaveLength(0);
1073
- });
1074
- it('should NOT flag pom.xml dependencies', async () => {
1075
- const javaContent = `
1076
- import com.fasterxml.jackson.databind.ObjectMapper;
1077
- import org.apache.commons.lang3.StringUtils;
1078
- `;
1079
- const pomXml = `
1080
- <project>
1081
- <dependencies>
1082
- <dependency>
1083
- <groupId>com.fasterxml.jackson</groupId>
1084
- <artifactId>jackson-databind</artifactId>
1085
- </dependency>
1086
- <dependency>
1087
- <groupId>org.apache.commons</groupId>
1088
- <artifactId>commons-lang3</artifactId>
1089
- </dependency>
1090
- </dependencies>
1091
- </project>
1092
- `;
1093
- FileScanner.findFiles.mockResolvedValue(['src/main/java/App.java']);
1094
- mockReadFile.mockResolvedValue(javaContent);
1095
- mockPathExistsSync.mockImplementation((p) => p.includes('pom.xml'));
1096
- mockReadFileSync.mockReturnValue(pomXml);
1097
- mockPathExists.mockResolvedValue(false);
1098
- const failures = await gate.run(context);
1099
- expect(failures).toHaveLength(0);
1100
- });
1101
- it('should flag unknown imports when build deps context exists', async () => {
1102
- const javaContent = `
1103
- import java.util.List;
1104
- import com.nonexistent.hallucinated.FakeClass;
1105
- `;
1106
- const buildGradle = `
1107
- dependencies {
1108
- implementation 'org.springframework:spring-core:6.0.0'
1109
- }
1110
- `;
1111
- FileScanner.findFiles.mockResolvedValue(['src/main/java/App.java']);
1112
- mockReadFile.mockResolvedValue(javaContent);
1113
- mockPathExists.mockResolvedValue(false);
1114
- mockPathExistsSync.mockImplementation((p) => p.includes('build.gradle'));
1115
- mockReadFileSync.mockReturnValue(buildGradle);
1116
- const failures = await gate.run(context);
1117
- expect(failures).toHaveLength(1);
1118
- expect(failures[0].details).toContain('com.nonexistent.hallucinated.FakeClass');
1119
- });
1120
- it('should NOT flag when no build context exists (avoid false positives)', async () => {
1121
- const javaContent = `
1122
- import com.example.whatever.SomeClass;
1123
- `;
1124
- FileScanner.findFiles.mockResolvedValue(['App.java']);
1125
- mockReadFile.mockResolvedValue(javaContent);
1126
- mockPathExists.mockResolvedValue(false);
1127
- mockPathExistsSync.mockReturnValue(false);
1128
- const failures = await gate.run(context);
1129
- expect(failures).toHaveLength(0);
1130
- });
1131
- it('should NOT flag common test framework imports', async () => {
1132
- const javaContent = `
1133
- import org.junit.jupiter.api.Test;
1134
- import org.junit.jupiter.api.Assertions;
1135
- import org.slf4j.Logger;
1136
- import org.slf4j.LoggerFactory;
1137
- `;
1138
- FileScanner.findFiles.mockResolvedValue(['Test.java']);
1139
- mockReadFile.mockResolvedValue(javaContent);
1140
- mockPathExists.mockResolvedValue(false);
1141
- mockPathExistsSync.mockReturnValue(false);
1142
- const failures = await gate.run(context);
1143
- expect(failures).toHaveLength(0);
1144
- });
1145
- });
1146
- // ═══════════════════════════════════════════════════════════════
1147
- // KOTLIN
1148
- // ═══════════════════════════════════════════════════════════════
1149
- describe('HallucinatedImportsGate — Kotlin imports', () => {
1150
- let gate;
1151
- const testCwd = '/tmp/test-kotlin-project';
1152
- const context = { cwd: testCwd, ignore: [] };
1153
- beforeEach(() => {
1154
- vi.clearAllMocks();
1155
- mockReaddirSync.mockReturnValue([]);
1156
- gate = new HallucinatedImportsGate({ enabled: true });
1157
- });
1158
- it('should NOT flag Kotlin standard library imports', async () => {
1159
- const ktContent = `
1160
- import kotlin.collections.mutableListOf
1161
- import kotlin.coroutines.coroutineContext
1162
- import kotlinx.coroutines.launch
1163
- import kotlinx.coroutines.flow.Flow
1164
- import kotlinx.serialization.Serializable
1165
- `;
1166
- FileScanner.findFiles.mockResolvedValue(['src/main/kotlin/App.kt']);
1167
- mockReadFile.mockResolvedValue(ktContent);
1168
- mockPathExists.mockResolvedValue(false);
1169
- mockPathExistsSync.mockReturnValue(false);
1170
- const failures = await gate.run(context);
1171
- expect(failures).toHaveLength(0);
1172
- });
1173
- it('should NOT flag Java stdlib imports from Kotlin (interop)', async () => {
1174
- const ktContent = `
1175
- import java.util.UUID
1176
- import java.io.File
1177
- import java.time.Instant
1178
- import javax.crypto.Cipher
1179
- `;
1180
- FileScanner.findFiles.mockResolvedValue(['src/main/kotlin/App.kt']);
1181
- mockReadFile.mockResolvedValue(ktContent);
1182
- mockPathExists.mockResolvedValue(false);
1183
- mockPathExistsSync.mockReturnValue(false);
1184
- const failures = await gate.run(context);
1185
- expect(failures).toHaveLength(0);
1186
- });
1187
- it('should flag unknown Kotlin imports when Gradle context exists', async () => {
1188
- const ktContent = `
1189
- import kotlin.collections.mutableListOf
1190
- import com.hallucinated.fake.Module
1191
- `;
1192
- const buildGradle = `
1193
- dependencies {
1194
- implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.9.0'
1195
- }
1196
- `;
1197
- FileScanner.findFiles.mockResolvedValue(['src/main/kotlin/App.kt']);
1198
- mockReadFile.mockResolvedValue(ktContent);
1199
- mockPathExists.mockResolvedValue(false);
1200
- mockPathExistsSync.mockImplementation((p) => p.includes('build.gradle') || p.includes('build.gradle.kts'));
1201
- mockReadFileSync.mockReturnValue(buildGradle);
1202
- const failures = await gate.run(context);
1203
- expect(failures).toHaveLength(1);
1204
- expect(failures[0].details).toContain('com.hallucinated.fake.Module');
1205
- });
1206
- });