@dommaker/harness 0.19.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 (348) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/bin/harness.js +136 -557
  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/context/index.d.ts +1 -5
  20. package/dist/context/index.d.ts.map +1 -1
  21. package/dist/context/index.js +1 -5
  22. package/dist/context/index.js.map +1 -1
  23. package/dist/context/knowledge-injector.d.ts +3 -3
  24. package/dist/context/knowledge-injector.d.ts.map +1 -1
  25. package/dist/context/knowledge-injector.js +1 -1
  26. package/dist/context/session-manager.d.ts +1 -12
  27. package/dist/context/session-manager.d.ts.map +1 -1
  28. package/dist/context/session-manager.js +0 -63
  29. package/dist/context/session-manager.js.map +1 -1
  30. package/dist/context/types.d.ts +1 -39
  31. package/dist/context/types.d.ts.map +1 -1
  32. package/dist/context/types.js +2 -15
  33. package/dist/context/types.js.map +1 -1
  34. package/dist/core/constraints/agent-prompt-renderer.d.ts +1 -1
  35. package/dist/core/constraints/agent-prompt-renderer.js +1 -1
  36. package/dist/core/constraints/capabilities-parser.d.ts +17 -2
  37. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  38. package/dist/core/constraints/capabilities-parser.js +84 -2
  39. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  40. package/dist/core/constraints/checker.d.ts +3 -17
  41. package/dist/core/constraints/checker.d.ts.map +1 -1
  42. package/dist/core/constraints/checker.js +7 -47
  43. package/dist/core/constraints/checker.js.map +1 -1
  44. package/dist/core/constraints/definitions.d.ts +0 -7
  45. package/dist/core/constraints/definitions.d.ts.map +1 -1
  46. package/dist/core/constraints/definitions.js +1 -8
  47. package/dist/core/constraints/definitions.js.map +1 -1
  48. package/dist/core/constraints/doc-freshness/runner.d.ts +0 -2
  49. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  50. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  51. package/dist/core/constraints/index.d.ts +1 -1
  52. package/dist/core/constraints/index.d.ts.map +1 -1
  53. package/dist/core/constraints/index.js +2 -3
  54. package/dist/core/constraints/index.js.map +1 -1
  55. package/dist/core/constraints/interceptor.d.ts.map +1 -1
  56. package/dist/core/constraints/interceptor.js +1 -5
  57. package/dist/core/constraints/interceptor.js.map +1 -1
  58. package/dist/core/effective-constraints.d.ts.map +1 -1
  59. package/dist/core/effective-constraints.js +0 -1
  60. package/dist/core/effective-constraints.js.map +1 -1
  61. package/dist/core/project-config-loader.d.ts.map +1 -1
  62. package/dist/core/project-config-loader.js +1 -8
  63. package/dist/core/project-config-loader.js.map +1 -1
  64. package/dist/dashboard/stats.d.ts.map +1 -1
  65. package/dist/dashboard/stats.js.map +1 -1
  66. package/dist/dashboard/types.d.ts +2 -2
  67. package/dist/dashboard/types.d.ts.map +1 -1
  68. package/dist/failure/constraint-handler.d.ts.map +1 -1
  69. package/dist/failure/constraint-handler.js +0 -2
  70. package/dist/failure/constraint-handler.js.map +1 -1
  71. package/dist/gates/acceptance.d.ts +9 -1
  72. package/dist/gates/acceptance.d.ts.map +1 -1
  73. package/dist/gates/acceptance.js +21 -0
  74. package/dist/gates/acceptance.js.map +1 -1
  75. package/dist/gates/checker-gate.d.ts +23 -0
  76. package/dist/gates/checker-gate.d.ts.map +1 -0
  77. package/dist/gates/checker-gate.js +53 -0
  78. package/dist/gates/checker-gate.js.map +1 -0
  79. package/dist/gates/command.d.ts +9 -2
  80. package/dist/gates/command.d.ts.map +1 -1
  81. package/dist/gates/command.js +10 -0
  82. package/dist/gates/command.js.map +1 -1
  83. package/dist/gates/contract.d.ts +8 -2
  84. package/dist/gates/contract.d.ts.map +1 -1
  85. package/dist/gates/contract.js +9 -0
  86. package/dist/gates/contract.js.map +1 -1
  87. package/dist/gates/decision.d.ts +17 -0
  88. package/dist/gates/decision.d.ts.map +1 -0
  89. package/dist/gates/decision.js +25 -0
  90. package/dist/gates/decision.js.map +1 -0
  91. package/dist/gates/definitions.d.ts +63 -0
  92. package/dist/gates/definitions.d.ts.map +1 -0
  93. package/dist/gates/definitions.js +160 -0
  94. package/dist/gates/definitions.js.map +1 -0
  95. package/dist/gates/effective-gates.d.ts +30 -0
  96. package/dist/gates/effective-gates.d.ts.map +1 -0
  97. package/dist/gates/effective-gates.js +85 -0
  98. package/dist/gates/effective-gates.js.map +1 -0
  99. package/dist/gates/index.d.ts +25 -1
  100. package/dist/gates/index.d.ts.map +1 -1
  101. package/dist/gates/index.js +37 -1
  102. package/dist/gates/index.js.map +1 -1
  103. package/dist/gates/performance.d.ts +8 -2
  104. package/dist/gates/performance.d.ts.map +1 -1
  105. package/dist/gates/performance.js +9 -0
  106. package/dist/gates/performance.js.map +1 -1
  107. package/dist/gates/registry.d.ts +32 -0
  108. package/dist/gates/registry.d.ts.map +1 -0
  109. package/dist/gates/registry.js +90 -0
  110. package/dist/gates/registry.js.map +1 -0
  111. package/dist/gates/review.d.ts +8 -2
  112. package/dist/gates/review.d.ts.map +1 -1
  113. package/dist/gates/review.js +9 -0
  114. package/dist/gates/review.js.map +1 -1
  115. package/dist/gates/runner.d.ts +30 -0
  116. package/dist/gates/runner.d.ts.map +1 -0
  117. package/dist/gates/runner.js +38 -0
  118. package/dist/gates/runner.js.map +1 -0
  119. package/dist/gates/security.d.ts +8 -2
  120. package/dist/gates/security.d.ts.map +1 -1
  121. package/dist/gates/security.js +9 -0
  122. package/dist/gates/security.js.map +1 -1
  123. package/dist/gates/types.d.ts +41 -1
  124. package/dist/gates/types.d.ts.map +1 -1
  125. package/dist/hooks/bootstrap.d.ts.map +1 -1
  126. package/dist/hooks/bootstrap.js +0 -2
  127. package/dist/hooks/bootstrap.js.map +1 -1
  128. package/dist/hooks/config.d.ts +26 -0
  129. package/dist/hooks/config.d.ts.map +1 -0
  130. package/dist/hooks/config.js +30 -0
  131. package/dist/hooks/config.js.map +1 -0
  132. package/dist/hooks/index.d.ts +2 -1
  133. package/dist/hooks/index.d.ts.map +1 -1
  134. package/dist/hooks/index.js +4 -1
  135. package/dist/hooks/index.js.map +1 -1
  136. package/dist/hooks/registry.d.ts +18 -1
  137. package/dist/hooks/registry.d.ts.map +1 -1
  138. package/dist/hooks/registry.js +45 -0
  139. package/dist/hooks/registry.js.map +1 -1
  140. package/dist/hooks/types.d.ts +20 -0
  141. package/dist/hooks/types.d.ts.map +1 -1
  142. package/dist/index.d.ts +4 -8
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +5 -20
  145. package/dist/index.js.map +1 -1
  146. package/dist/knowledge/import.d.ts +2 -2
  147. package/dist/knowledge/import.d.ts.map +1 -1
  148. package/dist/knowledge/ingest.js.map +1 -1
  149. package/dist/knowledge/lifecycle-hooks.d.ts +2 -2
  150. package/dist/knowledge/lifecycle-hooks.d.ts.map +1 -1
  151. package/dist/knowledge/types.d.ts +5 -7
  152. package/dist/knowledge/types.d.ts.map +1 -1
  153. package/dist/knowledge/types.js +1 -1
  154. package/dist/knowledge/types.js.map +1 -1
  155. package/dist/monitoring/constraint-doctor.d.ts +4 -22
  156. package/dist/monitoring/constraint-doctor.d.ts.map +1 -1
  157. package/dist/monitoring/constraint-doctor.js +7 -94
  158. package/dist/monitoring/constraint-doctor.js.map +1 -1
  159. package/dist/presets/standard.d.ts +1 -1
  160. package/dist/presets/standard.d.ts.map +1 -1
  161. package/dist/presets/standard.js +1 -2
  162. package/dist/presets/standard.js.map +1 -1
  163. package/dist/spec/annotation-checker.d.ts.map +1 -1
  164. package/dist/spec/annotation-checker.js +6 -26
  165. package/dist/spec/annotation-checker.js.map +1 -1
  166. package/dist/tools/index.d.ts +0 -4
  167. package/dist/tools/index.d.ts.map +1 -1
  168. package/dist/tools/index.js +0 -4
  169. package/dist/tools/index.js.map +1 -1
  170. package/dist/types/constraint.d.ts +1 -12
  171. package/dist/types/constraint.d.ts.map +1 -1
  172. package/dist/types/constraint.js +0 -3
  173. package/dist/types/constraint.js.map +1 -1
  174. package/dist/types/project-config.d.ts +0 -2
  175. package/dist/types/project-config.d.ts.map +1 -1
  176. package/dist/utils/file-walk.d.ts +2 -2
  177. package/dist/utils/file-walk.js +2 -2
  178. package/package.json +6 -1
  179. package/src/CONTEXT.md +14 -0
  180. package/src/__tests__/checker-extra.test.ts +2 -59
  181. package/src/__tests__/constraint-doctor.test.ts +1 -140
  182. package/src/__tests__/constraint-handler.test.ts +0 -4
  183. package/src/__tests__/project-config-loader-extra.test.ts +0 -20
  184. package/src/__tests__/project-config-loader.test.ts +0 -1
  185. package/src/cli/commands/CONTEXT.md +15 -7
  186. package/src/cli/commands/__tests__/check-drift.test.ts +0 -3
  187. package/src/cli/commands/__tests__/check-skip-output.test.ts +0 -5
  188. package/src/cli/commands/__tests__/check.test.ts +7 -75
  189. package/src/cli/commands/__tests__/registry.test.ts +201 -0
  190. package/src/cli/commands/check.ts +2 -14
  191. package/src/cli/commands/definitions.ts +593 -0
  192. package/src/cli/commands/knowledge.ts +3 -3
  193. package/src/cli/commands/sync-docs/capabilities-syncer.ts +4 -110
  194. package/src/cli/commands/sync-docs/index.ts +8 -5
  195. package/src/context/CONTEXT.md +2 -7
  196. package/src/context/__tests__/session-manager.test.ts +0 -91
  197. package/src/context/index.ts +1 -5
  198. package/src/context/knowledge-injector.ts +3 -3
  199. package/src/context/session-manager.ts +0 -74
  200. package/src/context/types.ts +2 -63
  201. package/src/core/constraints/__tests__/interceptor.test.ts +0 -2
  202. package/src/core/constraints/agent-prompt-renderer.ts +1 -1
  203. package/src/core/constraints/capabilities-parser.ts +95 -2
  204. package/src/core/constraints/checker.ts +8 -53
  205. package/src/core/constraints/definitions.ts +0 -8
  206. package/src/core/constraints/doc-freshness/runner.ts +1 -2
  207. package/src/core/constraints/index.ts +1 -2
  208. package/src/core/constraints/interceptor.ts +1 -6
  209. package/src/core/effective-constraints.ts +0 -1
  210. package/src/core/project-config-loader.ts +1 -7
  211. package/src/dashboard/stats.ts +2 -2
  212. package/src/dashboard/types.ts +2 -2
  213. package/src/failure/constraint-handler.ts +0 -2
  214. package/src/gates/CONTEXT.md +16 -13
  215. package/src/gates/__tests__/checker-gate.test.ts +45 -0
  216. package/src/gates/__tests__/contract.test.ts +73 -0
  217. package/src/gates/__tests__/effective-gates.test.ts +82 -0
  218. package/src/gates/__tests__/gate-interface.test.ts +202 -0
  219. package/src/gates/__tests__/registry.test.ts +87 -0
  220. package/src/gates/__tests__/runner.test.ts +81 -0
  221. package/src/gates/acceptance.ts +24 -1
  222. package/src/gates/checker-gate.ts +54 -0
  223. package/src/gates/command.ts +13 -2
  224. package/src/gates/contract.ts +12 -2
  225. package/src/gates/decision.ts +27 -0
  226. package/src/gates/definitions.ts +207 -0
  227. package/src/gates/effective-gates.ts +106 -0
  228. package/src/gates/index.ts +38 -0
  229. package/src/gates/performance.ts +12 -2
  230. package/src/gates/registry.ts +107 -0
  231. package/src/gates/review.ts +12 -2
  232. package/src/gates/runner.ts +57 -0
  233. package/src/gates/security.ts +12 -2
  234. package/src/gates/types.ts +48 -1
  235. package/src/hooks/CONTEXT.md +12 -2
  236. package/src/hooks/__tests__/bootstrap.test.ts +0 -1
  237. package/src/hooks/__tests__/config.test.ts +19 -0
  238. package/src/hooks/__tests__/pipeline.test.ts +79 -0
  239. package/src/hooks/__tests__/registry.test.ts +67 -0
  240. package/src/hooks/bootstrap.ts +0 -2
  241. package/src/hooks/config.ts +29 -0
  242. package/src/hooks/index.ts +2 -1
  243. package/src/hooks/registry.ts +60 -1
  244. package/src/hooks/types.ts +21 -0
  245. package/src/index.ts +5 -22
  246. package/src/knowledge/import.ts +3 -3
  247. package/src/knowledge/ingest.ts +4 -4
  248. package/src/knowledge/lifecycle-hooks.ts +3 -3
  249. package/src/knowledge/types.ts +5 -8
  250. package/src/monitoring/constraint-doctor.ts +7 -106
  251. package/src/presets/__tests__/standard.test.ts +0 -2
  252. package/src/presets/standard.ts +1 -2
  253. package/src/spec/__tests__/annotation-checker.test.ts +9 -3
  254. package/src/spec/annotation-checker.ts +7 -29
  255. package/src/tools/CONTEXT.md +7 -14
  256. package/src/tools/index.ts +0 -4
  257. package/src/types/constraint.ts +1 -14
  258. package/src/types/project-config.ts +0 -3
  259. package/src/utils/file-walk.ts +2 -2
  260. package/dist/architecture/constraint-engine.d.ts +0 -67
  261. package/dist/architecture/constraint-engine.d.ts.map +0 -1
  262. package/dist/architecture/constraint-engine.js +0 -295
  263. package/dist/architecture/constraint-engine.js.map +0 -1
  264. package/dist/architecture/cross-project-checker.d.ts +0 -54
  265. package/dist/architecture/cross-project-checker.d.ts.map +0 -1
  266. package/dist/architecture/cross-project-checker.js +0 -467
  267. package/dist/architecture/cross-project-checker.js.map +0 -1
  268. package/dist/cli/commands/index.d.ts +0 -30
  269. package/dist/cli/commands/index.d.ts.map +0 -1
  270. package/dist/cli/commands/index.js +0 -91
  271. package/dist/cli/commands/index.js.map +0 -1
  272. package/dist/context/file-budget.d.ts +0 -40
  273. package/dist/context/file-budget.d.ts.map +0 -1
  274. package/dist/context/file-budget.js +0 -147
  275. package/dist/context/file-budget.js.map +0 -1
  276. package/dist/context/progressive-loader.d.ts +0 -109
  277. package/dist/context/progressive-loader.d.ts.map +0 -1
  278. package/dist/context/progressive-loader.js +0 -166
  279. package/dist/context/progressive-loader.js.map +0 -1
  280. package/dist/context/token-pipeline.d.ts +0 -55
  281. package/dist/context/token-pipeline.d.ts.map +0 -1
  282. package/dist/context/token-pipeline.js +0 -220
  283. package/dist/context/token-pipeline.js.map +0 -1
  284. package/dist/context/tool-output-budget.d.ts +0 -59
  285. package/dist/context/tool-output-budget.d.ts.map +0 -1
  286. package/dist/context/tool-output-budget.js +0 -177
  287. package/dist/context/tool-output-budget.js.map +0 -1
  288. package/dist/core/constraints/default-executors.d.ts +0 -10
  289. package/dist/core/constraints/default-executors.d.ts.map +0 -1
  290. package/dist/core/constraints/default-executors.js +0 -120
  291. package/dist/core/constraints/default-executors.js.map +0 -1
  292. package/dist/llm/adapter.d.ts +0 -36
  293. package/dist/llm/adapter.d.ts.map +0 -1
  294. package/dist/llm/adapter.js +0 -118
  295. package/dist/llm/adapter.js.map +0 -1
  296. package/dist/llm/index.d.ts +0 -3
  297. package/dist/llm/index.d.ts.map +0 -1
  298. package/dist/llm/index.js +0 -19
  299. package/dist/llm/index.js.map +0 -1
  300. package/dist/llm/types.d.ts +0 -62
  301. package/dist/llm/types.d.ts.map +0 -1
  302. package/dist/llm/types.js +0 -8
  303. package/dist/llm/types.js.map +0 -1
  304. package/dist/tools/core/index.d.ts +0 -13
  305. package/dist/tools/core/index.d.ts.map +0 -1
  306. package/dist/tools/core/index.js +0 -198
  307. package/dist/tools/core/index.js.map +0 -1
  308. package/dist/tools/loader.d.ts +0 -21
  309. package/dist/tools/loader.d.ts.map +0 -1
  310. package/dist/tools/loader.js +0 -199
  311. package/dist/tools/loader.js.map +0 -1
  312. package/dist/tools/registry.d.ts +0 -59
  313. package/dist/tools/registry.d.ts.map +0 -1
  314. package/dist/tools/registry.js +0 -115
  315. package/dist/tools/registry.js.map +0 -1
  316. package/dist/tools/types.d.ts +0 -38
  317. package/dist/tools/types.d.ts.map +0 -1
  318. package/dist/tools/types.js +0 -6
  319. package/dist/tools/types.js.map +0 -1
  320. package/src/__tests__/architecture-engine.test.ts +0 -619
  321. package/src/__tests__/context/progressive-loader.test.ts +0 -131
  322. package/src/__tests__/cross-project-checker.test.ts +0 -318
  323. package/src/architecture/CONTEXT.md +0 -22
  324. package/src/architecture/constraint-engine.ts +0 -351
  325. package/src/architecture/cross-project-checker.ts +0 -545
  326. package/src/cli/commands/index.ts +0 -30
  327. package/src/context/__tests__/file-budget.test.ts +0 -142
  328. package/src/context/__tests__/progressive-loader.test.ts +0 -264
  329. package/src/context/__tests__/token-pipeline.test.ts +0 -178
  330. package/src/context/__tests__/tool-output-budget.test.ts +0 -95
  331. package/src/context/file-budget.ts +0 -142
  332. package/src/context/progressive-loader.ts +0 -260
  333. package/src/context/token-pipeline.ts +0 -265
  334. package/src/context/tool-output-budget.ts +0 -178
  335. package/src/core/constraints/__tests__/default-executors.test.ts +0 -57
  336. package/src/core/constraints/default-executors.ts +0 -92
  337. package/src/llm/CONTEXT.md +0 -21
  338. package/src/llm/__tests__/adapter.test.ts +0 -172
  339. package/src/llm/adapter.ts +0 -149
  340. package/src/llm/index.ts +0 -2
  341. package/src/llm/types.ts +0 -81
  342. package/src/tools/__tests__/loader.test.ts +0 -252
  343. package/src/tools/__tests__/registry.test.ts +0 -157
  344. package/src/tools/core/__tests__/core.test.ts +0 -122
  345. package/src/tools/core/index.ts +0 -201
  346. package/src/tools/loader.ts +0 -216
  347. package/src/tools/registry.ts +0 -128
  348. 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
- });