@dommaker/harness 0.18.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (366) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/bin/harness.js +136 -552
  3. package/dist/cli/commands/check.d.ts.map +1 -1
  4. package/dist/cli/commands/check.js +2 -13
  5. package/dist/cli/commands/check.js.map +1 -1
  6. package/dist/cli/commands/definitions.d.ts +90 -0
  7. package/dist/cli/commands/definitions.d.ts.map +1 -0
  8. package/dist/cli/commands/definitions.js +519 -0
  9. package/dist/cli/commands/definitions.js.map +1 -0
  10. package/dist/cli/commands/knowledge.js.map +1 -1
  11. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +3 -25
  12. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
  13. package/dist/cli/commands/sync-docs/capabilities-syncer.js +3 -93
  14. package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
  15. package/dist/cli/commands/sync-docs/index.d.ts +1 -1
  16. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  17. package/dist/cli/commands/sync-docs/index.js +4 -4
  18. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  19. package/dist/cli/commands/update-user-model.d.ts +1 -0
  20. package/dist/cli/commands/update-user-model.d.ts.map +1 -1
  21. package/dist/cli/commands/update-user-model.js +12 -5
  22. package/dist/cli/commands/update-user-model.js.map +1 -1
  23. package/dist/context/index.d.ts +1 -5
  24. package/dist/context/index.d.ts.map +1 -1
  25. package/dist/context/index.js +1 -5
  26. package/dist/context/index.js.map +1 -1
  27. package/dist/context/knowledge-injector.d.ts +3 -3
  28. package/dist/context/knowledge-injector.d.ts.map +1 -1
  29. package/dist/context/knowledge-injector.js +1 -1
  30. package/dist/context/session-manager.d.ts +1 -12
  31. package/dist/context/session-manager.d.ts.map +1 -1
  32. package/dist/context/session-manager.js +0 -63
  33. package/dist/context/session-manager.js.map +1 -1
  34. package/dist/context/types.d.ts +1 -39
  35. package/dist/context/types.d.ts.map +1 -1
  36. package/dist/context/types.js +2 -15
  37. package/dist/context/types.js.map +1 -1
  38. package/dist/core/constraints/agent-prompt-renderer.d.ts +32 -0
  39. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -0
  40. package/dist/core/constraints/agent-prompt-renderer.js +58 -0
  41. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -0
  42. package/dist/core/constraints/capabilities-parser.d.ts +17 -2
  43. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  44. package/dist/core/constraints/capabilities-parser.js +84 -2
  45. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  46. package/dist/core/constraints/check-cache.d.ts +48 -3
  47. package/dist/core/constraints/check-cache.d.ts.map +1 -1
  48. package/dist/core/constraints/check-cache.js +53 -5
  49. package/dist/core/constraints/check-cache.js.map +1 -1
  50. package/dist/core/constraints/checker.d.ts +3 -17
  51. package/dist/core/constraints/checker.d.ts.map +1 -1
  52. package/dist/core/constraints/checker.js +7 -47
  53. package/dist/core/constraints/checker.js.map +1 -1
  54. package/dist/core/constraints/definitions.d.ts +0 -7
  55. package/dist/core/constraints/definitions.d.ts.map +1 -1
  56. package/dist/core/constraints/definitions.js +1 -8
  57. package/dist/core/constraints/definitions.js.map +1 -1
  58. package/dist/core/constraints/doc-freshness/runner.d.ts +0 -2
  59. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  60. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  61. package/dist/core/constraints/index.d.ts +5 -1
  62. package/dist/core/constraints/index.d.ts.map +1 -1
  63. package/dist/core/constraints/index.js +8 -3
  64. package/dist/core/constraints/index.js.map +1 -1
  65. package/dist/core/constraints/interceptor.d.ts.map +1 -1
  66. package/dist/core/constraints/interceptor.js +1 -5
  67. package/dist/core/constraints/interceptor.js.map +1 -1
  68. package/dist/core/effective-constraints.d.ts.map +1 -1
  69. package/dist/core/effective-constraints.js +0 -1
  70. package/dist/core/effective-constraints.js.map +1 -1
  71. package/dist/core/project-config-loader.d.ts.map +1 -1
  72. package/dist/core/project-config-loader.js +1 -8
  73. package/dist/core/project-config-loader.js.map +1 -1
  74. package/dist/dashboard/stats.d.ts.map +1 -1
  75. package/dist/dashboard/stats.js.map +1 -1
  76. package/dist/dashboard/types.d.ts +2 -2
  77. package/dist/dashboard/types.d.ts.map +1 -1
  78. package/dist/failure/constraint-handler.d.ts.map +1 -1
  79. package/dist/failure/constraint-handler.js +0 -2
  80. package/dist/failure/constraint-handler.js.map +1 -1
  81. package/dist/gates/acceptance.d.ts +9 -1
  82. package/dist/gates/acceptance.d.ts.map +1 -1
  83. package/dist/gates/acceptance.js +21 -0
  84. package/dist/gates/acceptance.js.map +1 -1
  85. package/dist/gates/checker-gate.d.ts +23 -0
  86. package/dist/gates/checker-gate.d.ts.map +1 -0
  87. package/dist/gates/checker-gate.js +53 -0
  88. package/dist/gates/checker-gate.js.map +1 -0
  89. package/dist/gates/command.d.ts +9 -2
  90. package/dist/gates/command.d.ts.map +1 -1
  91. package/dist/gates/command.js +10 -0
  92. package/dist/gates/command.js.map +1 -1
  93. package/dist/gates/contract.d.ts +8 -2
  94. package/dist/gates/contract.d.ts.map +1 -1
  95. package/dist/gates/contract.js +9 -0
  96. package/dist/gates/contract.js.map +1 -1
  97. package/dist/gates/decision.d.ts +17 -0
  98. package/dist/gates/decision.d.ts.map +1 -0
  99. package/dist/gates/decision.js +25 -0
  100. package/dist/gates/decision.js.map +1 -0
  101. package/dist/gates/definitions.d.ts +63 -0
  102. package/dist/gates/definitions.d.ts.map +1 -0
  103. package/dist/gates/definitions.js +160 -0
  104. package/dist/gates/definitions.js.map +1 -0
  105. package/dist/gates/effective-gates.d.ts +30 -0
  106. package/dist/gates/effective-gates.d.ts.map +1 -0
  107. package/dist/gates/effective-gates.js +85 -0
  108. package/dist/gates/effective-gates.js.map +1 -0
  109. package/dist/gates/index.d.ts +25 -1
  110. package/dist/gates/index.d.ts.map +1 -1
  111. package/dist/gates/index.js +37 -1
  112. package/dist/gates/index.js.map +1 -1
  113. package/dist/gates/performance.d.ts +8 -2
  114. package/dist/gates/performance.d.ts.map +1 -1
  115. package/dist/gates/performance.js +9 -0
  116. package/dist/gates/performance.js.map +1 -1
  117. package/dist/gates/registry.d.ts +32 -0
  118. package/dist/gates/registry.d.ts.map +1 -0
  119. package/dist/gates/registry.js +90 -0
  120. package/dist/gates/registry.js.map +1 -0
  121. package/dist/gates/review.d.ts +8 -2
  122. package/dist/gates/review.d.ts.map +1 -1
  123. package/dist/gates/review.js +9 -0
  124. package/dist/gates/review.js.map +1 -1
  125. package/dist/gates/runner.d.ts +30 -0
  126. package/dist/gates/runner.d.ts.map +1 -0
  127. package/dist/gates/runner.js +38 -0
  128. package/dist/gates/runner.js.map +1 -0
  129. package/dist/gates/security.d.ts +8 -2
  130. package/dist/gates/security.d.ts.map +1 -1
  131. package/dist/gates/security.js +9 -0
  132. package/dist/gates/security.js.map +1 -1
  133. package/dist/gates/types.d.ts +41 -1
  134. package/dist/gates/types.d.ts.map +1 -1
  135. package/dist/hooks/bootstrap.d.ts.map +1 -1
  136. package/dist/hooks/bootstrap.js +0 -2
  137. package/dist/hooks/bootstrap.js.map +1 -1
  138. package/dist/hooks/config.d.ts +26 -0
  139. package/dist/hooks/config.d.ts.map +1 -0
  140. package/dist/hooks/config.js +30 -0
  141. package/dist/hooks/config.js.map +1 -0
  142. package/dist/hooks/index.d.ts +2 -1
  143. package/dist/hooks/index.d.ts.map +1 -1
  144. package/dist/hooks/index.js +4 -1
  145. package/dist/hooks/index.js.map +1 -1
  146. package/dist/hooks/registry.d.ts +18 -1
  147. package/dist/hooks/registry.d.ts.map +1 -1
  148. package/dist/hooks/registry.js +45 -0
  149. package/dist/hooks/registry.js.map +1 -1
  150. package/dist/hooks/types.d.ts +20 -0
  151. package/dist/hooks/types.d.ts.map +1 -1
  152. package/dist/index.d.ts +8 -8
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.js +13 -21
  155. package/dist/index.js.map +1 -1
  156. package/dist/knowledge/import.d.ts +2 -2
  157. package/dist/knowledge/import.d.ts.map +1 -1
  158. package/dist/knowledge/ingest.js.map +1 -1
  159. package/dist/knowledge/lifecycle-hooks.d.ts +2 -2
  160. package/dist/knowledge/lifecycle-hooks.d.ts.map +1 -1
  161. package/dist/knowledge/types.d.ts +5 -7
  162. package/dist/knowledge/types.d.ts.map +1 -1
  163. package/dist/knowledge/types.js +1 -1
  164. package/dist/knowledge/types.js.map +1 -1
  165. package/dist/monitoring/constraint-doctor.d.ts +4 -22
  166. package/dist/monitoring/constraint-doctor.d.ts.map +1 -1
  167. package/dist/monitoring/constraint-doctor.js +7 -94
  168. package/dist/monitoring/constraint-doctor.js.map +1 -1
  169. package/dist/presets/standard.d.ts +1 -1
  170. package/dist/presets/standard.d.ts.map +1 -1
  171. package/dist/presets/standard.js +1 -2
  172. package/dist/presets/standard.js.map +1 -1
  173. package/dist/spec/annotation-checker.d.ts.map +1 -1
  174. package/dist/spec/annotation-checker.js +6 -26
  175. package/dist/spec/annotation-checker.js.map +1 -1
  176. package/dist/tools/index.d.ts +0 -4
  177. package/dist/tools/index.d.ts.map +1 -1
  178. package/dist/tools/index.js +0 -4
  179. package/dist/tools/index.js.map +1 -1
  180. package/dist/types/constraint.d.ts +1 -12
  181. package/dist/types/constraint.d.ts.map +1 -1
  182. package/dist/types/constraint.js +0 -3
  183. package/dist/types/constraint.js.map +1 -1
  184. package/dist/types/project-config.d.ts +0 -2
  185. package/dist/types/project-config.d.ts.map +1 -1
  186. package/dist/utils/file-walk.d.ts +2 -2
  187. package/dist/utils/file-walk.js +2 -2
  188. package/package.json +9 -4
  189. package/src/CONTEXT.md +14 -0
  190. package/src/__tests__/checker-extra.test.ts +2 -59
  191. package/src/__tests__/constraint-doctor.test.ts +1 -140
  192. package/src/__tests__/constraint-handler.test.ts +0 -4
  193. package/src/__tests__/project-config-loader-extra.test.ts +0 -20
  194. package/src/__tests__/project-config-loader.test.ts +0 -1
  195. package/src/cli/commands/CONTEXT.md +15 -7
  196. package/src/cli/commands/__tests__/analyze-sessions.test.ts +178 -0
  197. package/src/cli/commands/__tests__/check-drift.test.ts +0 -3
  198. package/src/cli/commands/__tests__/check-skip-output.test.ts +0 -5
  199. package/src/cli/commands/__tests__/check.test.ts +7 -75
  200. package/src/cli/commands/__tests__/registry.test.ts +201 -0
  201. package/src/cli/commands/__tests__/release.test.ts +162 -0
  202. package/src/cli/commands/__tests__/update-user-model.test.ts +205 -0
  203. package/src/cli/commands/check.ts +2 -14
  204. package/src/cli/commands/definitions.ts +593 -0
  205. package/src/cli/commands/knowledge.ts +3 -3
  206. package/src/cli/commands/sync-docs/capabilities-syncer.ts +4 -110
  207. package/src/cli/commands/sync-docs/index.ts +8 -5
  208. package/src/cli/commands/update-user-model.ts +15 -5
  209. package/src/context/CONTEXT.md +2 -7
  210. package/src/context/__tests__/session-manager.test.ts +0 -91
  211. package/src/context/index.ts +1 -5
  212. package/src/context/knowledge-injector.ts +3 -3
  213. package/src/context/session-manager.ts +0 -74
  214. package/src/context/types.ts +2 -63
  215. package/src/core/CONTEXT.md +1 -1
  216. package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +129 -0
  217. package/src/core/constraints/__tests__/check-cache.test.ts +227 -0
  218. package/src/core/constraints/__tests__/interceptor.test.ts +0 -2
  219. package/src/core/constraints/agent-prompt-renderer.ts +71 -0
  220. package/src/core/constraints/capabilities-parser.ts +95 -2
  221. package/src/core/constraints/check-cache.ts +75 -3
  222. package/src/core/constraints/checker.ts +8 -53
  223. package/src/core/constraints/definitions.ts +0 -8
  224. package/src/core/constraints/doc-freshness/runner.ts +1 -2
  225. package/src/core/constraints/index.ts +11 -2
  226. package/src/core/constraints/interceptor.ts +1 -6
  227. package/src/core/effective-constraints.ts +0 -1
  228. package/src/core/project-config-loader.ts +1 -7
  229. package/src/dashboard/stats.ts +2 -2
  230. package/src/dashboard/types.ts +2 -2
  231. package/src/failure/constraint-handler.ts +0 -2
  232. package/src/gates/CONTEXT.md +16 -13
  233. package/src/gates/__tests__/checker-gate.test.ts +45 -0
  234. package/src/gates/__tests__/contract.test.ts +73 -0
  235. package/src/gates/__tests__/effective-gates.test.ts +82 -0
  236. package/src/gates/__tests__/gate-interface.test.ts +202 -0
  237. package/src/gates/__tests__/registry.test.ts +87 -0
  238. package/src/gates/__tests__/runner.test.ts +81 -0
  239. package/src/gates/acceptance.ts +24 -1
  240. package/src/gates/checker-gate.ts +54 -0
  241. package/src/gates/command.ts +13 -2
  242. package/src/gates/contract.ts +12 -2
  243. package/src/gates/decision.ts +27 -0
  244. package/src/gates/definitions.ts +207 -0
  245. package/src/gates/effective-gates.ts +106 -0
  246. package/src/gates/index.ts +38 -0
  247. package/src/gates/performance.ts +12 -2
  248. package/src/gates/registry.ts +107 -0
  249. package/src/gates/review.ts +12 -2
  250. package/src/gates/runner.ts +57 -0
  251. package/src/gates/security.ts +12 -2
  252. package/src/gates/types.ts +48 -1
  253. package/src/hooks/CONTEXT.md +12 -2
  254. package/src/hooks/__tests__/bootstrap.test.ts +0 -1
  255. package/src/hooks/__tests__/config.test.ts +19 -0
  256. package/src/hooks/__tests__/pipeline.test.ts +79 -0
  257. package/src/hooks/__tests__/registry.test.ts +67 -0
  258. package/src/hooks/bootstrap.ts +0 -2
  259. package/src/hooks/config.ts +29 -0
  260. package/src/hooks/index.ts +2 -1
  261. package/src/hooks/registry.ts +60 -1
  262. package/src/hooks/types.ts +21 -0
  263. package/src/index.ts +13 -22
  264. package/src/knowledge/import.ts +3 -3
  265. package/src/knowledge/ingest.ts +4 -4
  266. package/src/knowledge/lifecycle-hooks.ts +3 -3
  267. package/src/knowledge/types.ts +5 -8
  268. package/src/monitoring/constraint-doctor.ts +7 -106
  269. package/src/presets/__tests__/standard.test.ts +0 -2
  270. package/src/presets/standard.ts +1 -2
  271. package/src/spec/__tests__/annotation-checker.test.ts +9 -3
  272. package/src/spec/annotation-checker.ts +7 -29
  273. package/src/tools/CONTEXT.md +7 -14
  274. package/src/tools/index.ts +0 -4
  275. package/src/types/constraint.ts +1 -14
  276. package/src/types/project-config.ts +0 -3
  277. package/src/utils/file-walk.ts +2 -2
  278. package/dist/architecture/constraint-engine.d.ts +0 -67
  279. package/dist/architecture/constraint-engine.d.ts.map +0 -1
  280. package/dist/architecture/constraint-engine.js +0 -295
  281. package/dist/architecture/constraint-engine.js.map +0 -1
  282. package/dist/architecture/cross-project-checker.d.ts +0 -54
  283. package/dist/architecture/cross-project-checker.d.ts.map +0 -1
  284. package/dist/architecture/cross-project-checker.js +0 -467
  285. package/dist/architecture/cross-project-checker.js.map +0 -1
  286. package/dist/cli/commands/index.d.ts +0 -30
  287. package/dist/cli/commands/index.d.ts.map +0 -1
  288. package/dist/cli/commands/index.js +0 -91
  289. package/dist/cli/commands/index.js.map +0 -1
  290. package/dist/context/file-budget.d.ts +0 -40
  291. package/dist/context/file-budget.d.ts.map +0 -1
  292. package/dist/context/file-budget.js +0 -147
  293. package/dist/context/file-budget.js.map +0 -1
  294. package/dist/context/progressive-loader.d.ts +0 -109
  295. package/dist/context/progressive-loader.d.ts.map +0 -1
  296. package/dist/context/progressive-loader.js +0 -166
  297. package/dist/context/progressive-loader.js.map +0 -1
  298. package/dist/context/token-pipeline.d.ts +0 -55
  299. package/dist/context/token-pipeline.d.ts.map +0 -1
  300. package/dist/context/token-pipeline.js +0 -220
  301. package/dist/context/token-pipeline.js.map +0 -1
  302. package/dist/context/tool-output-budget.d.ts +0 -59
  303. package/dist/context/tool-output-budget.d.ts.map +0 -1
  304. package/dist/context/tool-output-budget.js +0 -177
  305. package/dist/context/tool-output-budget.js.map +0 -1
  306. package/dist/core/constraints/default-executors.d.ts +0 -10
  307. package/dist/core/constraints/default-executors.d.ts.map +0 -1
  308. package/dist/core/constraints/default-executors.js +0 -120
  309. package/dist/core/constraints/default-executors.js.map +0 -1
  310. package/dist/llm/adapter.d.ts +0 -36
  311. package/dist/llm/adapter.d.ts.map +0 -1
  312. package/dist/llm/adapter.js +0 -118
  313. package/dist/llm/adapter.js.map +0 -1
  314. package/dist/llm/index.d.ts +0 -3
  315. package/dist/llm/index.d.ts.map +0 -1
  316. package/dist/llm/index.js +0 -19
  317. package/dist/llm/index.js.map +0 -1
  318. package/dist/llm/types.d.ts +0 -62
  319. package/dist/llm/types.d.ts.map +0 -1
  320. package/dist/llm/types.js +0 -8
  321. package/dist/llm/types.js.map +0 -1
  322. package/dist/tools/core/index.d.ts +0 -13
  323. package/dist/tools/core/index.d.ts.map +0 -1
  324. package/dist/tools/core/index.js +0 -198
  325. package/dist/tools/core/index.js.map +0 -1
  326. package/dist/tools/loader.d.ts +0 -21
  327. package/dist/tools/loader.d.ts.map +0 -1
  328. package/dist/tools/loader.js +0 -199
  329. package/dist/tools/loader.js.map +0 -1
  330. package/dist/tools/registry.d.ts +0 -59
  331. package/dist/tools/registry.d.ts.map +0 -1
  332. package/dist/tools/registry.js +0 -115
  333. package/dist/tools/registry.js.map +0 -1
  334. package/dist/tools/types.d.ts +0 -38
  335. package/dist/tools/types.d.ts.map +0 -1
  336. package/dist/tools/types.js +0 -6
  337. package/dist/tools/types.js.map +0 -1
  338. package/src/__tests__/architecture-engine.test.ts +0 -619
  339. package/src/__tests__/context/progressive-loader.test.ts +0 -131
  340. package/src/__tests__/cross-project-checker.test.ts +0 -318
  341. package/src/architecture/CONTEXT.md +0 -22
  342. package/src/architecture/constraint-engine.ts +0 -351
  343. package/src/architecture/cross-project-checker.ts +0 -545
  344. package/src/cli/commands/index.ts +0 -30
  345. package/src/context/__tests__/file-budget.test.ts +0 -142
  346. package/src/context/__tests__/progressive-loader.test.ts +0 -264
  347. package/src/context/__tests__/token-pipeline.test.ts +0 -178
  348. package/src/context/__tests__/tool-output-budget.test.ts +0 -95
  349. package/src/context/file-budget.ts +0 -142
  350. package/src/context/progressive-loader.ts +0 -260
  351. package/src/context/token-pipeline.ts +0 -265
  352. package/src/context/tool-output-budget.ts +0 -178
  353. package/src/core/constraints/__tests__/default-executors.test.ts +0 -57
  354. package/src/core/constraints/default-executors.ts +0 -92
  355. package/src/llm/CONTEXT.md +0 -21
  356. package/src/llm/__tests__/adapter.test.ts +0 -172
  357. package/src/llm/adapter.ts +0 -149
  358. package/src/llm/index.ts +0 -2
  359. package/src/llm/types.ts +0 -81
  360. package/src/tools/__tests__/loader.test.ts +0 -252
  361. package/src/tools/__tests__/registry.test.ts +0 -157
  362. package/src/tools/core/__tests__/core.test.ts +0 -122
  363. package/src/tools/core/index.ts +0 -201
  364. package/src/tools/loader.ts +0 -216
  365. package/src/tools/registry.ts +0 -128
  366. package/src/tools/types.ts +0 -46
@@ -1,619 +0,0 @@
1
- /**
2
- * ArchitectureConstraintEngine 测试
3
- */
4
-
5
- import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
6
- import { ArchitectureConstraintEngine, loadArchitectureRules, runArchitectureCheck } from '../architecture/constraint-engine';
7
- import type { ArchitectureRule, ArchitectureContext } from '../architecture/constraint-engine';
8
- import * as fs from 'fs';
9
- import * as path from 'path';
10
-
11
- describe('ArchitectureConstraintEngine', () => {
12
- const tempDir = path.join(process.cwd(), 'temp-test-arch');
13
-
14
- beforeEach(() => {
15
- fs.mkdirSync(tempDir, { recursive: true });
16
- });
17
-
18
- afterEach(() => {
19
- try {
20
- fs.rmSync(tempDir, { recursive: true, force: true });
21
- } catch {
22
- // ignore
23
- }
24
- });
25
-
26
- describe('forbidden-pattern 规则', () => {
27
- it('应该检测禁止模式', async () => {
28
- const rules: ArchitectureRule[] = [
29
- {
30
- id: 'no-test-in-src',
31
- type: 'forbidden-pattern',
32
- patterns: ['test'],
33
- severity: 'error',
34
- message: '源码目录不应包含 test',
35
- },
36
- ];
37
-
38
- const engine = new ArchitectureConstraintEngine(rules);
39
- const context: ArchitectureContext = {
40
- files: ['src/utils/test-helper.ts', 'src/utils/helper.ts'],
41
- diff: '',
42
- };
43
-
44
- const result = await engine.check(context);
45
-
46
- expect(result.passed).toBe(false);
47
- expect(result.violations.length).toBe(1);
48
- expect(result.violations[0].ruleId).toBe('no-test-in-src');
49
- });
50
-
51
- it('应该支持 scope 过滤', async () => {
52
- const rules: ArchitectureRule[] = [
53
- {
54
- id: 'no-test-in-core',
55
- type: 'forbidden-pattern',
56
- patterns: ['test'],
57
- scope: 'src/core/*',
58
- severity: 'error',
59
- },
60
- ];
61
-
62
- const engine = new ArchitectureConstraintEngine(rules);
63
- const context: ArchitectureContext = {
64
- files: ['src/utils/test-helper.ts', 'src/core/test.ts'],
65
- diff: '',
66
- };
67
-
68
- const result = await engine.check(context);
69
-
70
- expect(result.violations.length).toBe(1);
71
- expect(result.violations[0].files?.[0]).toBe('src/core/test.ts');
72
- });
73
-
74
- it('警告级别不应该影响 passed', async () => {
75
- const rules: ArchitectureRule[] = [
76
- {
77
- id: 'warning-rule',
78
- type: 'forbidden-pattern',
79
- patterns: ['warn'],
80
- severity: 'warning',
81
- },
82
- ];
83
-
84
- const engine = new ArchitectureConstraintEngine(rules);
85
- const context: ArchitectureContext = {
86
- files: ['src/warning.ts'],
87
- diff: '',
88
- };
89
-
90
- const result = await engine.check(context);
91
-
92
- expect(result.passed).toBe(true);
93
- expect(result.violations.length).toBe(1);
94
- });
95
-
96
- it('多个 pattern 应该全部检测', async () => {
97
- const rules: ArchitectureRule[] = [
98
- {
99
- id: 'no-forbidden',
100
- type: 'forbidden-pattern',
101
- patterns: ['test', 'spec', 'mock'],
102
- severity: 'error',
103
- },
104
- ];
105
-
106
- const engine = new ArchitectureConstraintEngine(rules);
107
- const context: ArchitectureContext = {
108
- files: ['src/test.ts', 'src/spec.ts', 'src/mock.ts', 'src/ok.ts'],
109
- diff: '',
110
- };
111
-
112
- const result = await engine.check(context);
113
-
114
- expect(result.violations.length).toBe(3);
115
- });
116
-
117
- it('大小写不敏感匹配', async () => {
118
- const rules: ArchitectureRule[] = [
119
- {
120
- id: 'no-test',
121
- type: 'forbidden-pattern',
122
- patterns: ['TEST'],
123
- severity: 'error',
124
- },
125
- ];
126
-
127
- const engine = new ArchitectureConstraintEngine(rules);
128
- const context: ArchitectureContext = {
129
- files: ['src/TestFile.ts', 'src/testfile.ts'],
130
- diff: '',
131
- };
132
-
133
- const result = await engine.check(context);
134
-
135
- expect(result.violations.length).toBe(2);
136
- });
137
-
138
- it('glob 模式 **/ 匹配任意前缀', async () => {
139
- const rules: ArchitectureRule[] = [
140
- {
141
- id: 'no-test-anywhere',
142
- type: 'forbidden-pattern',
143
- patterns: ['test'],
144
- scope: '**/core/**',
145
- severity: 'error',
146
- },
147
- ];
148
-
149
- const engine = new ArchitectureConstraintEngine(rules);
150
- const context: ArchitectureContext = {
151
- files: ['packages/lib/core/test.ts', 'src/core/test.ts', 'src/test.ts'],
152
- diff: '',
153
- };
154
-
155
- const result = await engine.check(context);
156
-
157
- // **/core/** 应该匹配 packages/lib/core/ 和 src/core/
158
- expect(result.violations.length).toBeGreaterThanOrEqual(2);
159
- });
160
-
161
- it('空 patterns 不应该报错', async () => {
162
- const rules: ArchitectureRule[] = [
163
- {
164
- id: 'empty-patterns',
165
- type: 'forbidden-pattern',
166
- patterns: [],
167
- severity: 'error',
168
- },
169
- ];
170
-
171
- const engine = new ArchitectureConstraintEngine(rules);
172
- const context: ArchitectureContext = {
173
- files: ['src/any.ts'],
174
- diff: '',
175
- };
176
-
177
- const result = await engine.check(context);
178
-
179
- expect(result.violations.length).toBe(0);
180
- });
181
-
182
- it('自定义 message 应该出现在 violation', async () => {
183
- const rules: ArchitectureRule[] = [
184
- {
185
- id: 'custom-msg',
186
- type: 'forbidden-pattern',
187
- patterns: ['test'],
188
- message: '自定义错误消息',
189
- severity: 'error',
190
- },
191
- ];
192
-
193
- const engine = new ArchitectureConstraintEngine(rules);
194
- const context: ArchitectureContext = {
195
- files: ['src/test.ts'],
196
- diff: '',
197
- };
198
-
199
- const result = await engine.check(context);
200
-
201
- expect(result.violations[0].message).toBe('自定义错误消息');
202
- });
203
- });
204
-
205
- describe('file-count 规则', () => {
206
- it('应该检测文件数超限', async () => {
207
- const rules: ArchitectureRule[] = [
208
- {
209
- id: 'max-10-files',
210
- type: 'file-count',
211
- threshold: 10,
212
- severity: 'error',
213
- message: '单次提交文件数过多',
214
- },
215
- ];
216
-
217
- const engine = new ArchitectureConstraintEngine(rules);
218
- const context: ArchitectureContext = {
219
- files: Array(15).fill('file.ts'),
220
- diff: '',
221
- };
222
-
223
- const result = await engine.check(context);
224
-
225
- expect(result.passed).toBe(false);
226
- expect(result.violations[0].message).toContain('文件数');
227
- });
228
-
229
- it('文件数未超限应该通过', async () => {
230
- const rules: ArchitectureRule[] = [
231
- {
232
- id: 'max-10-files',
233
- type: 'file-count',
234
- threshold: 10,
235
- severity: 'error',
236
- },
237
- ];
238
-
239
- const engine = new ArchitectureConstraintEngine(rules);
240
- const context: ArchitectureContext = {
241
- files: Array(5).fill('file.ts'),
242
- diff: '',
243
- };
244
-
245
- const result = await engine.check(context);
246
-
247
- expect(result.violations.length).toBe(0);
248
- });
249
-
250
- it('正好等于阈值应该通过', async () => {
251
- const rules: ArchitectureRule[] = [
252
- {
253
- id: 'max-10-files',
254
- type: 'file-count',
255
- threshold: 10,
256
- severity: 'error',
257
- },
258
- ];
259
-
260
- const engine = new ArchitectureConstraintEngine(rules);
261
- const context: ArchitectureContext = {
262
- files: Array(10).fill('file.ts'),
263
- diff: '',
264
- };
265
-
266
- const result = await engine.check(context);
267
-
268
- expect(result.violations.length).toBe(0);
269
- });
270
-
271
- it('默认阈值应该是 3', async () => {
272
- const rules: ArchitectureRule[] = [
273
- {
274
- id: 'default-threshold',
275
- type: 'file-count',
276
- severity: 'warning',
277
- },
278
- ];
279
-
280
- const engine = new ArchitectureConstraintEngine(rules);
281
- const context: ArchitectureContext = {
282
- files: Array(5).fill('file.ts'),
283
- diff: '',
284
- };
285
-
286
- const result = await engine.check(context);
287
-
288
- // 默认阈值 3,5 个文件应该触发
289
- expect(result.violations.length).toBe(1);
290
- });
291
- });
292
-
293
- describe('module-boundary 规则', () => {
294
- it('无 boundaries 配置应该通过', async () => {
295
- const rules: ArchitectureRule[] = [
296
- {
297
- id: 'no-cross-module',
298
- type: 'module-boundary',
299
- severity: 'error',
300
- },
301
- ];
302
-
303
- const engine = new ArchitectureConstraintEngine(rules);
304
- const context: ArchitectureContext = {
305
- files: ['src/core/index.ts'],
306
- diff: '+++ b/src/core/index.ts\n+import { foo } from "../cli/bar"',
307
- };
308
-
309
- const result = await engine.check(context);
310
- expect(result.passed).toBe(true);
311
- });
312
-
313
- it('应该检测跨模块导入', async () => {
314
- const rules: ArchitectureRule[] = [
315
- {
316
- id: 'no-core-to-cli',
317
- type: 'module-boundary',
318
- severity: 'error',
319
- boundaries: [
320
- { from: 'src/core/**', denyImport: 'src/cli/**' },
321
- ],
322
- },
323
- ];
324
-
325
- const engine = new ArchitectureConstraintEngine(rules);
326
- const context: ArchitectureContext = {
327
- files: ['src/core/index.ts'],
328
- diff: '+++ b/src/core/index.ts\n+import { bar } from "../cli/bar"',
329
- };
330
-
331
- const result = await engine.check(context);
332
- expect(result.passed).toBe(false);
333
- expect(result.violations.length).toBe(1);
334
- expect(result.violations[0].ruleId).toBe('no-core-to-cli');
335
- });
336
-
337
- it('合规导入不应该报错', async () => {
338
- const rules: ArchitectureRule[] = [
339
- {
340
- id: 'no-core-to-cli',
341
- type: 'module-boundary',
342
- severity: 'error',
343
- boundaries: [
344
- { from: 'src/core/**', denyImport: 'src/cli/**' },
345
- ],
346
- },
347
- ];
348
-
349
- const engine = new ArchitectureConstraintEngine(rules);
350
- const context: ArchitectureContext = {
351
- files: ['src/core/index.ts'],
352
- diff: '+++ b/src/core/index.ts\n+import { util } from "./util"',
353
- };
354
-
355
- const result = await engine.check(context);
356
- expect(result.passed).toBe(true);
357
- });
358
-
359
- it('外部依赖导入应该跳过', async () => {
360
- const rules: ArchitectureRule[] = [
361
- {
362
- id: 'no-core-to-cli',
363
- type: 'module-boundary',
364
- severity: 'error',
365
- boundaries: [
366
- { from: 'src/core/**', denyImport: 'src/cli/**' },
367
- ],
368
- },
369
- ];
370
-
371
- const engine = new ArchitectureConstraintEngine(rules);
372
- const context: ArchitectureContext = {
373
- files: ['src/core/index.ts'],
374
- diff: '+++ b/src/core/index.ts\n+import chalk from "chalk"',
375
- };
376
-
377
- const result = await engine.check(context);
378
- expect(result.passed).toBe(true);
379
- });
380
-
381
- it('多条边界规则应该全部检测', async () => {
382
- const rules: ArchitectureRule[] = [
383
- {
384
- id: 'boundary-rules',
385
- type: 'module-boundary',
386
- severity: 'error',
387
- boundaries: [
388
- { from: 'src/core/**', denyImport: 'src/cli/**' },
389
- { from: 'src/gates/**', denyImport: 'src/core/session/**' },
390
- ],
391
- },
392
- ];
393
-
394
- const engine = new ArchitectureConstraintEngine(rules);
395
- const context: ArchitectureContext = {
396
- files: ['src/core/index.ts', 'src/gates/review.ts'],
397
- diff: [
398
- '+++ b/src/core/index.ts',
399
- '+import { cmd } from "../cli/command"',
400
- '+++ b/src/gates/review.ts',
401
- '+import { session } from "../core/session/startup"',
402
- ].join('\n'),
403
- };
404
-
405
- const result = await engine.check(context);
406
- expect(result.violations.length).toBe(2);
407
- });
408
-
409
- it('只检查变更文件中的 import', async () => {
410
- const rules: ArchitectureRule[] = [
411
- {
412
- id: 'no-core-to-cli',
413
- type: 'module-boundary',
414
- severity: 'error',
415
- boundaries: [
416
- { from: 'src/core/**', denyImport: 'src/cli/**' },
417
- ],
418
- },
419
- ];
420
-
421
- const engine = new ArchitectureConstraintEngine(rules);
422
- // 变更文件是 src/other.ts(不在 from 范围内),diff 中有 src/core 的 import
423
- const context: ArchitectureContext = {
424
- files: ['src/other.ts'],
425
- diff: '+++ b/src/other.ts\n+import { cmd } from "../cli/command"',
426
- };
427
-
428
- const result = await engine.check(context);
429
- expect(result.passed).toBe(true);
430
- });
431
- });
432
-
433
- describe('多规则', () => {
434
- it('应该执行所有规则', async () => {
435
- const rules: ArchitectureRule[] = [
436
- {
437
- id: 'no-test',
438
- type: 'forbidden-pattern',
439
- patterns: ['test'],
440
- severity: 'error',
441
- },
442
- {
443
- id: 'max-files',
444
- type: 'file-count',
445
- threshold: 5,
446
- severity: 'warning',
447
- },
448
- ];
449
-
450
- const engine = new ArchitectureConstraintEngine(rules);
451
- const context: ArchitectureContext = {
452
- files: ['test.ts', 'test2.ts', 'file1.ts', 'file2.ts', 'file3.ts', 'file4.ts'],
453
- diff: '',
454
- };
455
-
456
- const result = await engine.check(context);
457
-
458
- expect(result.violations.length).toBeGreaterThanOrEqual(2);
459
- });
460
- });
461
-
462
- describe('空规则', () => {
463
- it('空规则数组应该返回 passed', async () => {
464
- const engine = new ArchitectureConstraintEngine([]);
465
- const context: ArchitectureContext = {
466
- files: ['any.ts'],
467
- diff: '',
468
- };
469
-
470
- const result = await engine.check(context);
471
-
472
- expect(result.passed).toBe(true);
473
- expect(result.violations).toEqual([]);
474
- });
475
- });
476
-
477
- describe('unknown 规则类型', () => {
478
- it('应该忽略未知类型', async () => {
479
- const rules: ArchitectureRule[] = [
480
- {
481
- id: 'unknown-type',
482
- type: 'custom' as any,
483
- severity: 'error',
484
- },
485
- ];
486
-
487
- const engine = new ArchitectureConstraintEngine(rules);
488
- const context: ArchitectureContext = {
489
- files: ['any.ts'],
490
- diff: '',
491
- };
492
-
493
- const result = await engine.check(context);
494
-
495
- expect(result.violations.length).toBe(0);
496
- });
497
- });
498
-
499
- describe('PR context', () => {
500
- it('应该支持 PR 上下文', async () => {
501
- const engine = new ArchitectureConstraintEngine([]);
502
- const context: ArchitectureContext = {
503
- files: ['file.ts'],
504
- diff: 'diff content',
505
- pr: {
506
- number: 123,
507
- changedFiles: 5,
508
- additions: 100,
509
- deletions: 50,
510
- },
511
- };
512
-
513
- const result = await engine.check(context);
514
-
515
- expect(result).toBeDefined();
516
- });
517
- });
518
-
519
- describe('loadArchitectureRules', () => {
520
- it('应该加载 JSON 配置', async () => {
521
- const configPath = path.join(tempDir, 'rules.json');
522
- fs.writeFileSync(configPath, JSON.stringify({
523
- rules: [
524
- { id: 'rule1', type: 'file-count', threshold: 10 },
525
- ],
526
- }));
527
-
528
- const rules = await loadArchitectureRules(configPath);
529
-
530
- expect(rules.length).toBe(1);
531
- expect(rules[0].id).toBe('rule1');
532
- });
533
-
534
- it('应该加载 YAML 配置', async () => {
535
- const configPath = path.join(tempDir, 'rules.yml');
536
- fs.writeFileSync(configPath, `
537
- rules:
538
- - id: yaml-rule
539
- type: forbidden-pattern
540
- patterns:
541
- - test
542
- - spec
543
- severity: error
544
- message: YAML 规则测试
545
- `);
546
-
547
- const rules = await loadArchitectureRules(configPath);
548
-
549
- expect(rules.length).toBe(1);
550
- expect(rules[0].id).toBe('yaml-rule');
551
- expect(rules[0].patterns?.length).toBe(2);
552
- });
553
-
554
- it('YAML threshold 应该解析为数字', async () => {
555
- const configPath = path.join(tempDir, 'rules.yml');
556
- fs.writeFileSync(configPath, `
557
- rules:
558
- - id: threshold-rule
559
- type: file-count
560
- threshold: 15
561
- `);
562
-
563
- const rules = await loadArchitectureRules(configPath);
564
-
565
- expect(rules[0].threshold).toBe(15);
566
- });
567
-
568
- it('YAML severity 应该解析为字符串', async () => {
569
- const configPath = path.join(tempDir, 'rules.yml');
570
- fs.writeFileSync(configPath, `
571
- rules:
572
- - id: severity-rule
573
- type: file-count
574
- severity: warning
575
- `);
576
-
577
- const rules = await loadArchitectureRules(configPath);
578
-
579
- expect(rules[0].severity).toBe('warning');
580
- });
581
-
582
- it('空文件应该返回空数组', async () => {
583
- const configPath = path.join(tempDir, 'empty.yml');
584
- fs.writeFileSync(configPath, '');
585
-
586
- const rules = await loadArchitectureRules(configPath);
587
-
588
- expect(rules).toEqual([]);
589
- });
590
-
591
- it('无 rules 字段应该返回空数组', async () => {
592
- const configPath = path.join(tempDir, 'no-rules.yml');
593
- fs.writeFileSync(configPath, 'other: value');
594
-
595
- const rules = await loadArchitectureRules(configPath);
596
-
597
- expect(rules).toEqual([]);
598
- });
599
- });
600
-
601
- describe('runArchitectureCheck', () => {
602
- it('应该运行完整检查', async () => {
603
- const configPath = path.join(tempDir, 'rules.json');
604
- fs.writeFileSync(configPath, JSON.stringify({
605
- rules: [
606
- { id: 'rule1', type: 'file-count', threshold: 10 },
607
- ],
608
- }));
609
-
610
- const result = await runArchitectureCheck(configPath, {
611
- files: Array(5).fill('file.ts'),
612
- diff: '',
613
- });
614
-
615
- expect(result).toBeDefined();
616
- expect(result.passed).toBe(true);
617
- });
618
- });
619
- });