soloforge 1.1.45 → 1.1.46

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 (269) hide show
  1. package/README.md +3 -3
  2. package/dist/context/contracts/lifecycle_knowledge_contract.d.ts.map +1 -1
  3. package/dist/context/contracts/lifecycle_knowledge_contract.js +2 -2
  4. package/dist/context/contracts/lifecycle_knowledge_contract.js.map +1 -1
  5. package/dist/core/task_context/manager.d.ts +0 -8
  6. package/dist/core/task_context/manager.d.ts.map +1 -1
  7. package/dist/core/task_context/manager.js +1 -19
  8. package/dist/core/task_context/manager.js.map +1 -1
  9. package/dist/core/task_context/state_fact_classifier.d.ts +0 -8
  10. package/dist/core/task_context/state_fact_classifier.d.ts.map +1 -1
  11. package/dist/core/task_context/state_fact_classifier.js +0 -36
  12. package/dist/core/task_context/state_fact_classifier.js.map +1 -1
  13. package/dist/domain/contracts/design_artifact_pack.d.ts +0 -1
  14. package/dist/domain/contracts/design_artifact_pack.d.ts.map +1 -1
  15. package/dist/domain/contracts/design_artifact_pack.js +1 -4
  16. package/dist/domain/contracts/design_artifact_pack.js.map +1 -1
  17. package/dist/domain/index.d.ts +1 -1
  18. package/dist/domain/index.d.ts.map +1 -1
  19. package/dist/domain/registry.d.ts +2 -2
  20. package/dist/domain/registry.d.ts.map +1 -1
  21. package/dist/domain/types.d.ts +1 -12
  22. package/dist/domain/types.d.ts.map +1 -1
  23. package/dist/domain/types.js +3 -0
  24. package/dist/domain/types.js.map +1 -1
  25. package/dist/gate/contracts/capability_registry.d.ts.map +1 -1
  26. package/dist/gate/contracts/capability_registry.js +0 -26
  27. package/dist/gate/contracts/capability_registry.js.map +1 -1
  28. package/dist/gate/contracts/control_plane_contract.d.ts +1 -1
  29. package/dist/gate/contracts/control_plane_contract.d.ts.map +1 -1
  30. package/dist/gate/contracts/control_plane_contract.js +1 -10
  31. package/dist/gate/contracts/control_plane_contract.js.map +1 -1
  32. package/dist/gate/contracts/tool_invocation_contract_registry.d.ts +1 -0
  33. package/dist/gate/contracts/tool_invocation_contract_registry.d.ts.map +1 -1
  34. package/dist/gate/contracts/tool_invocation_contract_registry.js +1 -1
  35. package/dist/gate/contracts/tool_invocation_contract_registry.js.map +1 -1
  36. package/dist/gate/index.d.ts +0 -2
  37. package/dist/gate/index.d.ts.map +1 -1
  38. package/dist/gate/index.js +0 -1
  39. package/dist/gate/index.js.map +1 -1
  40. package/dist/server/tools/index.d.ts.map +1 -1
  41. package/dist/server/tools/index.js +1 -3
  42. package/dist/server/tools/index.js.map +1 -1
  43. package/dist/server/tools/lazy_loaders.d.ts +0 -23
  44. package/dist/server/tools/lazy_loaders.d.ts.map +1 -1
  45. package/dist/server/tools/lazy_loaders.js +2 -33
  46. package/dist/server/tools/lazy_loaders.js.map +1 -1
  47. package/dist/server/tools/middleware.d.ts +2 -5
  48. package/dist/server/tools/middleware.d.ts.map +1 -1
  49. package/dist/server/tools/middleware.js +3 -33
  50. package/dist/server/tools/middleware.js.map +1 -1
  51. package/dist/server/tools/sf_task.js +2 -7
  52. package/dist/server/tools/sf_task.js.map +1 -1
  53. package/dist/server/tools/types.d.ts +0 -2
  54. package/dist/server/tools/types.d.ts.map +1 -1
  55. package/dist/types/pipeline_types.d.ts +1 -9
  56. package/dist/types/pipeline_types.d.ts.map +1 -1
  57. package/dist/verify/audit/verifier.d.ts.map +1 -1
  58. package/dist/verify/audit/verifier.js +0 -33
  59. package/dist/verify/audit/verifier.js.map +1 -1
  60. package/dist/verify/index.d.ts +1 -4
  61. package/dist/verify/index.d.ts.map +1 -1
  62. package/dist/verify/index.js +0 -3
  63. package/dist/verify/index.js.map +1 -1
  64. package/dist/verify/types.d.ts +0 -21
  65. package/dist/verify/types.d.ts.map +1 -1
  66. package/package.json +1 -1
  67. package/templates/shared//345/206/263/347/255/226/347/275/221/345/205/263.md +7 -21
  68. package/dist/context/engine/dependency_scanner.d.ts +0 -23
  69. package/dist/context/engine/dependency_scanner.d.ts.map +0 -1
  70. package/dist/context/engine/dependency_scanner.js +0 -309
  71. package/dist/context/engine/dependency_scanner.js.map +0 -1
  72. package/dist/context/engine/exploration.d.ts +0 -194
  73. package/dist/context/engine/exploration.d.ts.map +0 -1
  74. package/dist/context/engine/exploration.js +0 -712
  75. package/dist/context/engine/exploration.js.map +0 -1
  76. package/dist/context/knowledge/knowledge_acceptance_registry.d.ts +0 -42
  77. package/dist/context/knowledge/knowledge_acceptance_registry.d.ts.map +0 -1
  78. package/dist/context/knowledge/knowledge_acceptance_registry.js +0 -249
  79. package/dist/context/knowledge/knowledge_acceptance_registry.js.map +0 -1
  80. package/dist/context/knowledge/knowledge_scenario_registry.d.ts +0 -26
  81. package/dist/context/knowledge/knowledge_scenario_registry.d.ts.map +0 -1
  82. package/dist/context/knowledge/knowledge_scenario_registry.js +0 -267
  83. package/dist/context/knowledge/knowledge_scenario_registry.js.map +0 -1
  84. package/dist/context/knowledge/knowledge_sovereignty.d.ts +0 -65
  85. package/dist/context/knowledge/knowledge_sovereignty.d.ts.map +0 -1
  86. package/dist/context/knowledge/knowledge_sovereignty.js +0 -199
  87. package/dist/context/knowledge/knowledge_sovereignty.js.map +0 -1
  88. package/dist/context/team_awareness.d.ts +0 -42
  89. package/dist/context/team_awareness.d.ts.map +0 -1
  90. package/dist/context/team_awareness.js +0 -154
  91. package/dist/context/team_awareness.js.map +0 -1
  92. package/dist/context/templates/template_asset_visibility.d.ts +0 -116
  93. package/dist/context/templates/template_asset_visibility.d.ts.map +0 -1
  94. package/dist/context/templates/template_asset_visibility.js +0 -310
  95. package/dist/context/templates/template_asset_visibility.js.map +0 -1
  96. package/dist/gate/conflict_gate.d.ts +0 -53
  97. package/dist/gate/conflict_gate.d.ts.map +0 -1
  98. package/dist/gate/conflict_gate.js +0 -96
  99. package/dist/gate/conflict_gate.js.map +0 -1
  100. package/dist/gate/contracts/capability_action_advisor.d.ts +0 -34
  101. package/dist/gate/contracts/capability_action_advisor.d.ts.map +0 -1
  102. package/dist/gate/contracts/capability_action_advisor.js +0 -165
  103. package/dist/gate/contracts/capability_action_advisor.js.map +0 -1
  104. package/dist/gate/contracts/capability_state_store.d.ts +0 -121
  105. package/dist/gate/contracts/capability_state_store.d.ts.map +0 -1
  106. package/dist/gate/contracts/capability_state_store.js +0 -188
  107. package/dist/gate/contracts/capability_state_store.js.map +0 -1
  108. package/dist/gate/contracts/contract_guard.d.ts +0 -50
  109. package/dist/gate/contracts/contract_guard.d.ts.map +0 -1
  110. package/dist/gate/contracts/contract_guard.js +0 -611
  111. package/dist/gate/contracts/contract_guard.js.map +0 -1
  112. package/dist/gate/contracts/escape_report.d.ts +0 -103
  113. package/dist/gate/contracts/escape_report.d.ts.map +0 -1
  114. package/dist/gate/contracts/escape_report.js +0 -145
  115. package/dist/gate/contracts/escape_report.js.map +0 -1
  116. package/dist/gate/feasibility_checker.d.ts +0 -27
  117. package/dist/gate/feasibility_checker.d.ts.map +0 -1
  118. package/dist/gate/feasibility_checker.js +0 -114
  119. package/dist/gate/feasibility_checker.js.map +0 -1
  120. package/dist/gate/migration_guard.d.ts +0 -22
  121. package/dist/gate/migration_guard.d.ts.map +0 -1
  122. package/dist/gate/migration_guard.js +0 -277
  123. package/dist/gate/migration_guard.js.map +0 -1
  124. package/dist/gate/scope_lease.d.ts +0 -94
  125. package/dist/gate/scope_lease.d.ts.map +0 -1
  126. package/dist/gate/scope_lease.js +0 -180
  127. package/dist/gate/scope_lease.js.map +0 -1
  128. package/dist/git/git_deps.d.ts +0 -85
  129. package/dist/git/git_deps.d.ts.map +0 -1
  130. package/dist/git/git_deps.js +0 -22
  131. package/dist/git/git_deps.js.map +0 -1
  132. package/dist/git/operations.d.ts +0 -173
  133. package/dist/git/operations.d.ts.map +0 -1
  134. package/dist/git/operations.js +0 -350
  135. package/dist/git/operations.js.map +0 -1
  136. package/dist/server/tools/gate_engine.d.ts +0 -17
  137. package/dist/server/tools/gate_engine.d.ts.map +0 -1
  138. package/dist/server/tools/gate_engine.js +0 -17
  139. package/dist/server/tools/gate_engine.js.map +0 -1
  140. package/dist/server/tools/middleware/budget_check.d.ts +0 -35
  141. package/dist/server/tools/middleware/budget_check.d.ts.map +0 -1
  142. package/dist/server/tools/middleware/budget_check.js +0 -69
  143. package/dist/server/tools/middleware/budget_check.js.map +0 -1
  144. package/dist/shared/contracts/contract_registry/builtin_contracts_core.d.ts +0 -16
  145. package/dist/shared/contracts/contract_registry/builtin_contracts_core.d.ts.map +0 -1
  146. package/dist/shared/contracts/contract_registry/builtin_contracts_core.js +0 -306
  147. package/dist/shared/contracts/contract_registry/builtin_contracts_core.js.map +0 -1
  148. package/dist/shared/contracts/contract_registry/builtin_contracts_core_2.d.ts +0 -16
  149. package/dist/shared/contracts/contract_registry/builtin_contracts_core_2.d.ts.map +0 -1
  150. package/dist/shared/contracts/contract_registry/builtin_contracts_core_2.js +0 -304
  151. package/dist/shared/contracts/contract_registry/builtin_contracts_core_2.js.map +0 -1
  152. package/dist/shared/contracts/contract_registry/builtin_contracts_extended.d.ts +0 -16
  153. package/dist/shared/contracts/contract_registry/builtin_contracts_extended.d.ts.map +0 -1
  154. package/dist/shared/contracts/contract_registry/builtin_contracts_extended.js +0 -434
  155. package/dist/shared/contracts/contract_registry/builtin_contracts_extended.js.map +0 -1
  156. package/dist/shared/contracts/contract_registry/index.d.ts +0 -8
  157. package/dist/shared/contracts/contract_registry/index.d.ts.map +0 -1
  158. package/dist/shared/contracts/contract_registry/index.js +0 -7
  159. package/dist/shared/contracts/contract_registry/index.js.map +0 -1
  160. package/dist/shared/contracts/contract_registry/registry.d.ts +0 -62
  161. package/dist/shared/contracts/contract_registry/registry.d.ts.map +0 -1
  162. package/dist/shared/contracts/contract_registry/registry.js +0 -191
  163. package/dist/shared/contracts/contract_registry/registry.js.map +0 -1
  164. package/dist/shared/contracts/contract_registry/registry_internal.d.ts +0 -19
  165. package/dist/shared/contracts/contract_registry/registry_internal.d.ts.map +0 -1
  166. package/dist/shared/contracts/contract_registry/registry_internal.js +0 -54
  167. package/dist/shared/contracts/contract_registry/registry_internal.js.map +0 -1
  168. package/dist/shared/contracts/contract_registry/types.d.ts +0 -47
  169. package/dist/shared/contracts/contract_registry/types.d.ts.map +0 -1
  170. package/dist/shared/contracts/contract_registry/types.js +0 -5
  171. package/dist/shared/contracts/contract_registry/types.js.map +0 -1
  172. package/dist/shared/contracts/contract_registry/validation.d.ts +0 -25
  173. package/dist/shared/contracts/contract_registry/validation.d.ts.map +0 -1
  174. package/dist/shared/contracts/contract_registry/validation.js +0 -363
  175. package/dist/shared/contracts/contract_registry/validation.js.map +0 -1
  176. package/dist/shared/llm_gateway.d.ts +0 -186
  177. package/dist/shared/llm_gateway.d.ts.map +0 -1
  178. package/dist/shared/llm_gateway.js +0 -393
  179. package/dist/shared/llm_gateway.js.map +0 -1
  180. package/dist/verify/audit/audit_sampler.d.ts +0 -29
  181. package/dist/verify/audit/audit_sampler.d.ts.map +0 -1
  182. package/dist/verify/audit/audit_sampler.js +0 -42
  183. package/dist/verify/audit/audit_sampler.js.map +0 -1
  184. package/dist/verify/audit/debt_reporter.d.ts +0 -22
  185. package/dist/verify/audit/debt_reporter.d.ts.map +0 -1
  186. package/dist/verify/audit/debt_reporter.js +0 -107
  187. package/dist/verify/audit/debt_reporter.js.map +0 -1
  188. package/dist/verify/audit/debugger.d.ts +0 -25
  189. package/dist/verify/audit/debugger.d.ts.map +0 -1
  190. package/dist/verify/audit/debugger.js +0 -429
  191. package/dist/verify/audit/debugger.js.map +0 -1
  192. package/dist/verify/audit/degradation.d.ts +0 -62
  193. package/dist/verify/audit/degradation.d.ts.map +0 -1
  194. package/dist/verify/audit/degradation.js +0 -78
  195. package/dist/verify/audit/degradation.js.map +0 -1
  196. package/dist/verify/audit/developer_sovereignty.d.ts +0 -29
  197. package/dist/verify/audit/developer_sovereignty.d.ts.map +0 -1
  198. package/dist/verify/audit/developer_sovereignty.js +0 -137
  199. package/dist/verify/audit/developer_sovereignty.js.map +0 -1
  200. package/dist/verify/audit/failure_report.d.ts +0 -76
  201. package/dist/verify/audit/failure_report.d.ts.map +0 -1
  202. package/dist/verify/audit/failure_report.js +0 -145
  203. package/dist/verify/audit/failure_report.js.map +0 -1
  204. package/dist/verify/audit/governance_report.d.ts +0 -110
  205. package/dist/verify/audit/governance_report.d.ts.map +0 -1
  206. package/dist/verify/audit/governance_report.js +0 -194
  207. package/dist/verify/audit/governance_report.js.map +0 -1
  208. package/dist/verify/audit/mutation_audit.d.ts +0 -40
  209. package/dist/verify/audit/mutation_audit.d.ts.map +0 -1
  210. package/dist/verify/audit/mutation_audit.js +0 -154
  211. package/dist/verify/audit/mutation_audit.js.map +0 -1
  212. package/dist/verify/audit/risk_sampler.d.ts +0 -44
  213. package/dist/verify/audit/risk_sampler.d.ts.map +0 -1
  214. package/dist/verify/audit/risk_sampler.js +0 -80
  215. package/dist/verify/audit/risk_sampler.js.map +0 -1
  216. package/dist/verify/audit/runtime_safety.d.ts +0 -89
  217. package/dist/verify/audit/runtime_safety.d.ts.map +0 -1
  218. package/dist/verify/audit/runtime_safety.js +0 -209
  219. package/dist/verify/audit/runtime_safety.js.map +0 -1
  220. package/dist/verify/audit/semantic_evidence.d.ts +0 -36
  221. package/dist/verify/audit/semantic_evidence.d.ts.map +0 -1
  222. package/dist/verify/audit/semantic_evidence.js +0 -90
  223. package/dist/verify/audit/semantic_evidence.js.map +0 -1
  224. package/dist/verify/audit/test_generator.d.ts +0 -23
  225. package/dist/verify/audit/test_generator.d.ts.map +0 -1
  226. package/dist/verify/audit/test_generator.js +0 -278
  227. package/dist/verify/audit/test_generator.js.map +0 -1
  228. package/dist/verify/audit/test_quality.d.ts +0 -49
  229. package/dist/verify/audit/test_quality.d.ts.map +0 -1
  230. package/dist/verify/audit/test_quality.js +0 -684
  231. package/dist/verify/audit/test_quality.js.map +0 -1
  232. package/dist/verify/change_coordinator.d.ts +0 -45
  233. package/dist/verify/change_coordinator.d.ts.map +0 -1
  234. package/dist/verify/change_coordinator.js +0 -168
  235. package/dist/verify/change_coordinator.js.map +0 -1
  236. package/dist/verify/contracts/decision_contract.d.ts +0 -51
  237. package/dist/verify/contracts/decision_contract.d.ts.map +0 -1
  238. package/dist/verify/contracts/decision_contract.js +0 -70
  239. package/dist/verify/contracts/decision_contract.js.map +0 -1
  240. package/dist/verify/contracts/mechanism_contract_registry/contracts-audit.d.ts +0 -17
  241. package/dist/verify/contracts/mechanism_contract_registry/contracts-audit.d.ts.map +0 -1
  242. package/dist/verify/contracts/mechanism_contract_registry/contracts-audit.js +0 -428
  243. package/dist/verify/contracts/mechanism_contract_registry/contracts-audit.js.map +0 -1
  244. package/dist/verify/contracts/mechanism_contract_registry/contracts-governance.d.ts +0 -17
  245. package/dist/verify/contracts/mechanism_contract_registry/contracts-governance.d.ts.map +0 -1
  246. package/dist/verify/contracts/mechanism_contract_registry/contracts-governance.js +0 -429
  247. package/dist/verify/contracts/mechanism_contract_registry/contracts-governance.js.map +0 -1
  248. package/dist/verify/contracts/mechanism_contract_registry/contracts-pipeline.d.ts +0 -17
  249. package/dist/verify/contracts/mechanism_contract_registry/contracts-pipeline.d.ts.map +0 -1
  250. package/dist/verify/contracts/mechanism_contract_registry/contracts-pipeline.js +0 -194
  251. package/dist/verify/contracts/mechanism_contract_registry/contracts-pipeline.js.map +0 -1
  252. package/dist/verify/contracts/mechanism_contract_registry/contracts-platform.d.ts +0 -17
  253. package/dist/verify/contracts/mechanism_contract_registry/contracts-platform.d.ts.map +0 -1
  254. package/dist/verify/contracts/mechanism_contract_registry/contracts-platform.js +0 -365
  255. package/dist/verify/contracts/mechanism_contract_registry/contracts-platform.js.map +0 -1
  256. package/dist/verify/contracts/mechanism_contract_registry/index.d.ts +0 -19
  257. package/dist/verify/contracts/mechanism_contract_registry/index.d.ts.map +0 -1
  258. package/dist/verify/contracts/mechanism_contract_registry/index.js +0 -21
  259. package/dist/verify/contracts/mechanism_contract_registry/index.js.map +0 -1
  260. package/dist/verify/contracts/mechanism_contract_registry/types.d.ts +0 -33
  261. package/dist/verify/contracts/mechanism_contract_registry/types.d.ts.map +0 -1
  262. package/dist/verify/contracts/mechanism_contract_registry/types.js +0 -5
  263. package/dist/verify/contracts/mechanism_contract_registry/types.js.map +0 -1
  264. package/dist/verify/contracts/mechanism_contract_registry/validation.d.ts +0 -38
  265. package/dist/verify/contracts/mechanism_contract_registry/validation.d.ts.map +0 -1
  266. package/dist/verify/contracts/mechanism_contract_registry/validation.js +0 -136
  267. package/dist/verify/contracts/mechanism_contract_registry/validation.js.map +0 -1
  268. package/templates/shared/LLM/351/242/204/347/256/227/347/275/221/345/205/263.md +0 -54
  269. package/templates/shared//346/265/201/345/274/217/345/277/203/350/267/263.md +0 -64
@@ -1,684 +0,0 @@
1
- /**
2
- * Test Quality — 审计层模块。
3
- *
4
- * 职责边界:
5
- * - 负责:analyzeTestQuality 等 审计层职责
6
- * - 不负责:不属于本模块的职责由对应模块承担
7
- *
8
- * 被谁调用:发布门禁、质量检查
9
- * 调用谁:index、knowledge_config_loader、logger
10
- *
11
- * 数据流:审计输入(代码/配置) → 检查 → 评分/报告
12
- * 持久化:无持久化(纯计算/内存态)
13
- */
14
- import { loadKnowledgeConfig, extractNumberRule, extractListRule } from "../../shared/knowledge_config_loader.js";
15
- import { debug } from "../../shared/logger.js";
16
- /**
17
- * 测试质量分析器 — 对测试文件进行纯规则维度的质量评估,包含断言密度、边界覆盖、
18
- * 命名规范性、断言重复和场景覆盖五个检查维度。零 AI 依赖。
19
- *
20
- * 机制 4: 变异测试防造假 — 通过逻辑算符反转和边界值偏移模拟代码变异,
21
- * 检测现有测试是否能捕捉到变更。若变异后测试依然全绿,判定测试无效。
22
- *
23
- * 分析测试文件质量,返回结构化报告,包含各项检查的评分和改进建议。
24
- * @param testContent - 测试文件内容
25
- * @param filename - 测试文件名(用于结果标识)
26
- * @param sourceContent - 可选的被测源码内容(用于变异测试)
27
- * @returns 测试质量报告,包含总分(0-100)、各项检查结果和改进建议列表
28
- */
29
- /**
30
- * 分析测试文件质量,返回评分和检查结果。
31
- * @param filename - 测试文件名
32
- * @param content - 文件内容
33
- * @returns 质量评分和检查项列表
34
- */
35
- export function analyzeTestQuality(testContent, filename, sourceContent, coverageMap, sourceFilePath) {
36
- debug("测试质量分析", `开始分析 ${filename}`);
37
- const checks = [];
38
- const suggestions = [];
39
- // ── 1. assertion_density ──
40
- const assertionDensityCheck = checkAssertionDensity(testContent);
41
- checks.push(assertionDensityCheck);
42
- if (!assertionDensityCheck.passed) {
43
- suggestions.push("建议为每个测试函数添加至少一个断言 (expect/assert)");
44
- }
45
- // ── 2. boundary_coverage ──
46
- const boundaryCheck = checkBoundaryCoverage(testContent);
47
- checks.push(boundaryCheck);
48
- if (!boundaryCheck.passed) {
49
- suggestions.push("建议增加边界值测试 (null, undefined, 空值, 极端值等)");
50
- }
51
- // ── 3. naming ──
52
- const namingCheck = checkNaming(testContent);
53
- checks.push(namingCheck);
54
- if (!namingCheck.passed) {
55
- suggestions.push("建议使用更具描述性的测试命名 (should/when/returns/handles/throws)");
56
- }
57
- // ── 4. duplication ──
58
- const duplicationCheck = checkDuplication(testContent);
59
- checks.push(duplicationCheck);
60
- if (!duplicationCheck.passed) {
61
- suggestions.push("建议提取公共断言为辅助函数,减少重复断言");
62
- }
63
- // ── 5. scenario_coverage ──
64
- const scenarioCheck = checkScenarioCoverage();
65
- checks.push(scenarioCheck);
66
- // ── 6. mutation_audit (机制 4: 变异测试防造假 + 覆盖率联动) ──
67
- if (sourceContent) {
68
- debug("测试质量分析", `启动变异测试防造假检测`);
69
- const mutationCheck = checkMutationResistanceWithCoverage(testContent, sourceContent, coverageMap, sourceFilePath);
70
- checks.push(mutationCheck);
71
- if (!mutationCheck.passed) {
72
- suggestions.push("测试可能存在'自己证明自己'问题:变异后断言仍能通过。建议增加反向用例和精确断言");
73
- }
74
- }
75
- else {
76
- // 无源码时跳过变异测试,不扣分
77
- checks.push({
78
- dimension: "scenario_coverage",
79
- passed: true,
80
- detail: "变异测试跳过(未提供被测源码)",
81
- });
82
- }
83
- // 评分计算: 满分 100,每项未通过的检查扣 16.7 分(6 项),最低 0 分
84
- // ── 评分计算 ──
85
- let score = 100;
86
- const deduction = Math.floor(100 / checks.length);
87
- for (const check of checks) {
88
- if (!check.passed) {
89
- score -= deduction;
90
- }
91
- }
92
- score = Math.max(0, score);
93
- debug("测试质量分析", `${filename} 评分 ${score}/100,${checks.filter(c => c.passed).length}/${checks.length} 项通过`);
94
- // ── 门禁硬化:为未通过的检查标记 severity ──
95
- let warningCount = 0;
96
- for (const check of checks) {
97
- if (!check.passed) {
98
- check.severity = "warning";
99
- warningCount++;
100
- }
101
- }
102
- return {
103
- file: filename,
104
- score,
105
- checks,
106
- suggestions,
107
- warning_count: warningCount,
108
- };
109
- }
110
- // ────────────────────────────────────────────
111
- // 检查实现
112
- // ────────────────────────────────────────────
113
- /** 断言密度检查 — 统计测试函数数与断言数的比率,阈值 >= 1.0(每个测试至少一个断言) */
114
- function checkAssertionDensity(content) {
115
- // 匹配 it/test/@Test 函数调用,统计测试函数总数
116
- const testFnMatches = content.match(/\b(it|test|@Test)\s*\(/g) ?? [];
117
- const testCount = testFnMatches.length;
118
- if (testCount === 0) {
119
- return {
120
- dimension: "assertion_density",
121
- passed: false,
122
- detail: "未检测到测试函数",
123
- };
124
- }
125
- // 匹配 expect/assert/assertEquals/assertEquals/assertThat/assertStrictEquals 调用
126
- const assertMatches = content.match(/\b(expect|assert|assertEquals|assertThat|assertStrictEquals)\s*\(/g) ?? [];
127
- // 过滤非断言的 expect 匹配器 (expect.any(), expect.arrayContaining() 等)
128
- const matcherSuffixes = ["any(", "arrayContaining(", "objectContaining(", "stringContaining(", "stringMatching(", "extend(", "anything(", "closeTo(", "assertions("];
129
- const assertCount = assertMatches.filter((m) => {
130
- // 对于 expect( 调用,检查括号后面跟随的内容
131
- const expectMatch = m.match(/^expect\s*\($/);
132
- if (expectMatch) {
133
- // 在内容中定位匹配位置并检查后续内容
134
- const idx = content.indexOf(m);
135
- const afterOpen = content.slice(idx + m.length).trimStart();
136
- return !matcherSuffixes.some((suffix) => afterOpen.startsWith(suffix));
137
- }
138
- return true;
139
- }).length;
140
- const density = assertCount / testCount;
141
- if (density >= 1.0) {
142
- return {
143
- dimension: "assertion_density",
144
- passed: true,
145
- detail: `断言密度: ${density.toFixed(1)} (${assertCount} 断言 / ${testCount} 测试)`,
146
- };
147
- }
148
- return {
149
- dimension: "assertion_density",
150
- passed: false,
151
- detail: `断言密度不足: ${density.toFixed(1)} (${assertCount} 断言 / ${testCount} 测试, 阈值 >= 1.0)`,
152
- };
153
- }
154
- /** 边界覆盖检查 — 在 expect() 和 it()/test() 代码块内搜索边界值关键字(null/undefined/空值/极值等) */
155
- function checkBoundaryCoverage(content) {
156
- // 仅在 expect() 上下文和 it()/test() 代码块内搜索,避免误匹配 URL/端口/import 中的值
157
- // 提取所有 expect(...) 代码块内容(按括号深度匹配到闭合)
158
- const expectContexts = [];
159
- const expectRegex = /\bexpect\s*\(/g;
160
- let expectMatch;
161
- while ((expectMatch = expectRegex.exec(content)) !== null) {
162
- // 跳过非断言匹配器 (expect.any(), expect.arrayContaining() 等)
163
- const afterOpen = content.slice(expectMatch.index + expectMatch[0].length).trimStart();
164
- if (/^(any|arrayContaining|objectContaining|stringContaining|stringMatching|extend|anything|closeTo|assertions)\s*\(/.test(afterOpen)) {
165
- continue;
166
- }
167
- // 提取 expect 块内容(按括号深度匹配到对应的闭合括号)
168
- let depth = 1;
169
- let pos = expectMatch.index + expectMatch[0].length;
170
- while (pos < content.length && depth > 0) {
171
- if (content[pos] === "(")
172
- depth++;
173
- else if (content[pos] === ")")
174
- depth--;
175
- pos++;
176
- }
177
- expectContexts.push(content.slice(expectMatch.index, pos));
178
- }
179
- // 同时搜索 it()/test() 代码块内部
180
- const testBlockRegex = /\b(?:it|test)\s*\(\s*["'`][^"'`]*["'`]\s*,\s*(?:async\s+)?/g;
181
- let testMatch;
182
- while ((testMatch = testBlockRegex.exec(content)) !== null) {
183
- // 提取回调函数开始后的代码块内容
184
- let depth = 0;
185
- let pos = testMatch.index + testMatch[0].length;
186
- while (pos < content.length && depth >= 0) {
187
- if (content[pos] === "{")
188
- depth++;
189
- else if (content[pos] === "}") {
190
- depth--;
191
- if (depth === 0) {
192
- pos++;
193
- break;
194
- }
195
- }
196
- pos++;
197
- }
198
- expectContexts.push(content.slice(testMatch.index, pos));
199
- }
200
- const searchableText = expectContexts.join("\n");
201
- // 跳过注释行和 import 语句
202
- const filteredLines = searchableText
203
- .split("\n")
204
- .filter((line) => !line.trim().startsWith("//") && !line.trim().startsWith("*") && !line.trimStart().startsWith("import "));
205
- const filteredText = filteredLines.join("\n");
206
- // 边界值关键字列表: null、undefined、空字符串、数字边界、中文描述
207
- const boundaryKeywords = [
208
- "null", "undefined", '""', "''", "0",
209
- "MAX_VALUE", "MIN_VALUE", "MAX_SAFE_INTEGER", "MIN_SAFE_INTEGER",
210
- "empty", "blank", "边界", "极端",
211
- ];
212
- const found = [];
213
- for (const keyword of boundaryKeywords) {
214
- if (filteredText.includes(keyword)) {
215
- found.push(keyword);
216
- }
217
- }
218
- if (found.length > 0) {
219
- return {
220
- dimension: "boundary_coverage",
221
- passed: true,
222
- detail: `检测到边界值测试: ${found.join(", ")}`,
223
- };
224
- }
225
- return {
226
- dimension: "boundary_coverage",
227
- passed: false,
228
- detail: "未检测到边界值测试",
229
- };
230
- }
231
- /**
232
- * 命名规范性检查 — 提取 it()/test() 中的测试名称,检查描述性关键词占比是否 >= 50%。
233
- * 支持英文关键词(should/when/returns/handles/throws)和中文关键词(应该/当/返回/处理/抛出)。
234
- */
235
- function checkNaming(content) {
236
- // 从 it("...", test("...", 模式中提取测试名称
237
- const namePattern = /\b(?:it|test)\s*\(\s*["'`]([^"'`]+)["'`]/g;
238
- const names = [];
239
- let match;
240
- while ((match = namePattern.exec(content)) !== null) {
241
- names.push(match[1]);
242
- }
243
- if (names.length === 0) {
244
- // 无法提取测试名称(如仅使用 describe)— 中性通过(无法评估)
245
- return {
246
- dimension: "naming",
247
- passed: true,
248
- detail: "未提取到测试名称 (可能使用 describe-only 模式)",
249
- };
250
- }
251
- // 描述性关键词: 英文 BDD 命名约定和中文命名约定
252
- const descriptiveKeywords = [
253
- "should", "when", "returns", "can", "handles", "throws",
254
- "shouldn't", "must", "will",
255
- "应该", "当", "返回", "处理", "抛出", "正确", "失败",
256
- ];
257
- let descriptiveCount = 0;
258
- for (const name of names) {
259
- const lowerName = name.toLowerCase();
260
- for (const keyword of descriptiveKeywords) {
261
- if (lowerName.includes(keyword.toLowerCase())) {
262
- descriptiveCount++;
263
- break;
264
- }
265
- }
266
- }
267
- const ratio = descriptiveCount / names.length;
268
- if (ratio >= 0.5) {
269
- return {
270
- dimension: "naming",
271
- passed: true,
272
- detail: `描述性命名比例: ${(ratio * 100).toFixed(0)}% (${descriptiveCount}/${names.length})`,
273
- };
274
- }
275
- return {
276
- dimension: "naming",
277
- passed: false,
278
- detail: `测试命名不够描述性: ${(ratio * 100).toFixed(0)}% (${descriptiveCount}/${names.length})`,
279
- };
280
- }
281
- /** 断言重复检查 — 提取所有 expect() 语句,计算重复比率,阈值 > 30% 则不通过 */
282
- function checkDuplication(content) {
283
- // 提取 expect(...) 语句
284
- const expectPattern = /\bexpect\s*\([^)]*\)/g;
285
- const expectCalls = [];
286
- let match;
287
- while ((match = expectPattern.exec(content)) !== null) {
288
- expectCalls.push(match[0].trim());
289
- }
290
- if (expectCalls.length === 0) {
291
- // 无可衡量的内容 — 中性通过
292
- return {
293
- dimension: "duplication",
294
- passed: true,
295
- detail: "无断言语句,跳过重复检查",
296
- };
297
- }
298
- // 排序后统计唯一断言数,重复率 = 1 - (唯一数/总数)
299
- const sorted = [...expectCalls].sort();
300
- const unique = new Set(sorted);
301
- const duplicateRatio = 1 - unique.size / sorted.length;
302
- if (duplicateRatio > 0.3) {
303
- const duplicateCount = sorted.length - unique.size;
304
- return {
305
- dimension: "duplication",
306
- passed: false,
307
- detail: `存在重复断言: ${duplicateCount}/${sorted.length} 条重复 (${(duplicateRatio * 100).toFixed(0)}%)`,
308
- };
309
- }
310
- return {
311
- dimension: "duplication",
312
- passed: true,
313
- detail: `断言重复率: ${(duplicateRatio * 100).toFixed(0)}%`,
314
- };
315
- }
316
- /** 场景覆盖检查 — 占位检查,实际场景覆盖需结合知识模板验收项进行交叉评估 */
317
- function checkScenarioCoverage() {
318
- return {
319
- dimension: "scenario_coverage",
320
- passed: true,
321
- detail: "需结合知识模板验收项进行交叉检查",
322
- };
323
- }
324
- // ── 机制 4: 变异测试防造假(知识库驱动) ──
325
- /** 从知识库 变异审计.md 读取变异审计配置 */
326
- function loadMutationConfig() {
327
- const config = loadKnowledgeConfig("shared/变异审计.md");
328
- const body = config?.body ?? "";
329
- return {
330
- precision_threshold: extractNumberRule(body, "Precision_Threshold") ?? 0.5,
331
- max_sample_lines: 8,
332
- whitelist_keywords: extractListRule(body, "变异白名单") ?? ["if", "else", "switch", "===", "!=="],
333
- blacklist_keywords: extractListRule(body, "变异黑名单") ?? ["console.log", "import", "export", "debugger"],
334
- };
335
- }
336
- const MUTATION_CONFIG = loadMutationConfig();
337
- /** 预定义变异操作集 */
338
- const MUTATION_OPERATORS = [
339
- {
340
- description: "逻辑算符反转 (=== → !==, !== → ===, > → <=, < → >=)",
341
- mutate: (line) => {
342
- if (/===/.test(line) && !/!==/.test(line))
343
- return line.replace(/===/g, "!==");
344
- if (/!==/.test(line))
345
- return line.replace(/!==/g, "===");
346
- if (/>=/.test(line))
347
- return line.replace(/>=/g, "<");
348
- if (/<=/.test(line))
349
- return line.replace(/<=/g, ">");
350
- if (/>/.test(line) && !/>=/.test(line))
351
- return line.replace(/>/g, "<=");
352
- if (/</.test(line) && !/<=/.test(line))
353
- return line.replace(/</g, ">=");
354
- return line;
355
- },
356
- },
357
- {
358
- description: "边界值偏移 (+1/-1 常量数字) — 只变异边界值,跳过 ID/时间戳/端口等非逻辑数字",
359
- mutate: (line) => {
360
- return line.replace(/\b(\d+)\b/g, (match, num, offset) => {
361
- const n = parseInt(num, 10);
362
- // 跳过大数字(可能是时间戳、端口、ID 等)
363
- if (n > 1000)
364
- return match;
365
- // FLAW-AUD-05 修复:跳过常见端口、HTTP 状态码、超时值等不应变异的常量
366
- const COMMON_PORTS = new Set([80, 443, 3000, 3001, 4000, 5000, 8000, 8080, 8443, 9090]);
367
- const HTTP_STATUS = new Set([100, 200, 201, 204, 301, 302, 304, 307, 400, 401, 403, 404, 405, 408, 409, 410, 413, 415, 422, 429, 500, 502, 503, 504]);
368
- if (COMMON_PORTS.has(n) || HTTP_STATUS.has(n))
369
- return match;
370
- // 跳过看起来像 ID 的数字(前后有下划线或位于特定模式中)
371
- const before = offset > 0 ? line[offset - 1] : "";
372
- if (before === "_" || before === "#")
373
- return match;
374
- // 跳过版本号模式(如 v1, v2, "1.0", "2.0" 中的数字)
375
- if (before === "v" || before === "V")
376
- return match;
377
- // 跳过超时值(_000 结尾的数字,如 5000, 30000, 120000)
378
- if (n >= 100 && n % 1000 === 0 && n <= 300000)
379
- return match;
380
- // 只变异典型的边界值: 0, 1, -1, 小于数组长度相关的数字
381
- if (n === 0)
382
- return "1";
383
- if (n === 1)
384
- return "0";
385
- if (n === -1)
386
- return "0";
387
- // 2-10 范围内的数字可能是数组长度/循环上界,偏移为有意义变异
388
- if (n >= 2 && n <= 10)
389
- return (n - 1).toString();
390
- // 其他数字跳过
391
- return match;
392
- });
393
- },
394
- },
395
- {
396
- description: "布尔值反转 (true → false, false → true)",
397
- mutate: (line) => {
398
- if (/\btrue\b/.test(line))
399
- return line.replace(/\btrue\b/g, "false");
400
- if (/\bfalse\b/.test(line))
401
- return line.replace(/\bfalse\b/g, "true");
402
- return line;
403
- },
404
- },
405
- {
406
- description: "返回值篡改 (return → return null)",
407
- mutate: (line) => {
408
- if (/return\s+/.test(line) && !/return\s+null/.test(line)) {
409
- return line.replace(/return\s+/, "return null; // ");
410
- }
411
- return line;
412
- },
413
- },
414
- ];
415
- // ── 变异白名单: AST 级别节点过滤 ──
416
- /**
417
- * 判断一行代码是否属于可变异的白名单节点。
418
- * 白名单: 条件分支(if/else/switch)、比较/逻辑操作符、return 语句、算术表达式。
419
- * 黑名单: console.log、注释、import/export、纯字符串/变量名赋值、类型声明。
420
- */
421
- function isMutationCandidate(line) {
422
- const trimmed = line.trim();
423
- // 黑名单: 跳过注释
424
- if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*"))
425
- return false;
426
- // 黑名单: 跳过 import/export 声明
427
- if (/^(import |export |from )/.test(trimmed))
428
- return false;
429
- // 黑名单: 跳过 console.log / console.warn / console.error 等无副作用日志
430
- if (/console\.(log|warn|error|info|debug)\s*\(/.test(trimmed))
431
- return false;
432
- // 黑名单: 跳过纯类型声明(TypeScript interface/type/enum)
433
- if (/^(interface |type |enum |declare )/.test(trimmed))
434
- return false;
435
- // 黑名单: 跳过纯字符串字面量行(无逻辑)
436
- if (/^['"`]/.test(trimmed) && trimmed.endsWith(';'))
437
- return false;
438
- // 黑名单: 跳过 debugger / breakpoint
439
- if (/^debugger/.test(trimmed))
440
- return false;
441
- // 白名单: 条件分支 (if / else if / switch / case / ternary)
442
- if (/\b(if|else|switch|case)\b/.test(trimmed) && /[(){}?]/.test(trimmed))
443
- return true;
444
- // 白名单: 比较操作符
445
- if (/(===|!==|==|!=|>=|<=|>|<)/.test(trimmed))
446
- return true;
447
- // 白名单: 逻辑操作符 (&& ||)
448
- if (/(&&|\|\|)/.test(trimmed))
449
- return true;
450
- // 白名单: return 语句(非 return void)
451
- if (/\breturn\b/.test(trimmed) && !/return\s*;\s*$/.test(trimmed))
452
- return true;
453
- // 白名单: 布尔字面量在表达式中
454
- if (/\b(true|false)\b/.test(trimmed) && /[=(){}?]/.test(trimmed))
455
- return true;
456
- // 白名单: 算术表达式在赋值/返回中
457
- if (/[+\-*/%]/.test(trimmed) && /[=()]/.test(trimmed))
458
- return true;
459
- return false;
460
- }
461
- /**
462
- * 变异测试防造假 — 通过对源码模拟变异,检查测试的断言是否精确到能检测变异。
463
- * 策略: 从源码中提取白名单关键逻辑行(AST 级别过滤),执行变异,
464
- * 检查测试中是否有断言针对这些逻辑的精确值。
465
- * 若测试只含模糊断言(如 toBeTruthy),判定为可能存在"自己证明自己"问题。
466
- */
467
- /** 伪随机数生成器 (基于种子,确保可复现且不由 AI 控制) */
468
- function seededRandom(seed) {
469
- let s = seed;
470
- return () => {
471
- s = (s * 1103515245 + 12345) & 0x7fffffff;
472
- return s / 0x7fffffff;
473
- };
474
- }
475
- /** 基于 RNG 的随机抽样(Fisher-Yates 部分洗牌)*/
476
- function seededSample(arr, count, rng) {
477
- const result = [...arr];
478
- const n = Math.min(count, result.length);
479
- for (let i = 0; i < n; i++) {
480
- const j = i + Math.floor(rng() * (result.length - i));
481
- [result[i], result[j]] = [result[j], result[i]];
482
- }
483
- return result.slice(0, n);
484
- }
485
- /**
486
- * 将旧版扁平 src/engine/X.ts 路径重映射到重组后的子目录路径。
487
- * engine/ 目录重组后,很多模块被移入 core/、pipeline/、templates/ 等子目录。
488
- * lcov 报告可能仍使用旧路径,需要规范化。
489
- */
490
- function remapEnginePath(sfPath) {
491
- // src/engine/ 下已知的子目录归属映射(扁平文件名 → 子目录)
492
- // 迁移后目标路径:core/ → src/shared/;其他子目录维持 src/engine/<subdir>/
493
- const engineSubdirMap = {
494
- // core/ → 已迁移至 src/shared/
495
- llm_gateway: "shared",
496
- logger: "shared",
497
- debug_log: "shared",
498
- helpers: "shared",
499
- io_controller: "shared",
500
- git_deps: "shared",
501
- log_governance: "shared",
502
- path_scope_utils: "shared",
503
- // pipeline/
504
- classifier: "pipeline",
505
- intent_expander: "pipeline",
506
- task_context: "pipeline",
507
- scope_controller: "pipeline",
508
- job_manager: "pipeline",
509
- task_planner: "pipeline",
510
- // templates/
511
- scaffolder: "templates",
512
- template_sync: "templates",
513
- consumable_asset_registry: "templates",
514
- asset_manifest: "templates",
515
- // audit/
516
- code_reviewer: "audit",
517
- test_quality: "audit",
518
- audit_verifier: "audit",
519
- mutation_audit: "audit",
520
- privacy_secret_contract: "audit",
521
- // contracts/
522
- contract_registry: "contracts",
523
- mechanism_contract_registry: "contracts",
524
- dual_layer_mechanism_registry: "contracts",
525
- tool_invocation_contract_registry: "contracts",
526
- // knowledge/
527
- knowledge_config_loader: "knowledge",
528
- knowledge_manager: "knowledge",
529
- // release/
530
- verifier: "release",
531
- release_readiness_gate: "release",
532
- };
533
- const prefix = "src/engine/";
534
- if (!sfPath.startsWith(prefix))
535
- return sfPath;
536
- const rest = sfPath.slice(prefix.length);
537
- // 已经在子目录中的路径直接返回
538
- if (rest.includes("/"))
539
- return sfPath;
540
- const baseName = rest.replace(/\.ts$/, "");
541
- const subdir = engineSubdirMap[baseName];
542
- if (!subdir)
543
- return sfPath;
544
- // shared/ 子目录已迁出 engine/,目标路径为 src/shared/X.ts
545
- if (subdir === "shared")
546
- return `src/shared/${rest}`;
547
- // 其他子目录仍在 src/engine/<subdir>/
548
- return `${prefix}${subdir}/${rest}`;
549
- }
550
- /**
551
- * 解析 lcov.info 报告,提取每个文件的已覆盖行号。
552
- * lcov 格式:
553
- * SF:path/to/file.ts
554
- * DA:10,1
555
- * DA:25,0
556
- * end_of_record
557
- * DA 行中第二个字段 > 0 表示该行被执行过(已覆盖)。
558
- * @param lcovContent - lcov.info 文件内容
559
- * @returns 覆盖率映射(文件路径 → 已覆盖行号集合)
560
- */
561
- export function parseLcov(lcovContent) {
562
- const map = new Map();
563
- debug("覆盖率解析", `开始解析 lcov 报告`);
564
- let currentFile = "";
565
- for (const line of lcovContent.split("\n")) {
566
- const sfMatch = line.match(/^SF:(.+)$/);
567
- if (sfMatch) {
568
- currentFile = remapEnginePath(sfMatch[1]);
569
- continue;
570
- }
571
- const daMatch = line.match(/^DA:(\d+),(\d+)/);
572
- if (daMatch && currentFile) {
573
- const lineNum = parseInt(daMatch[1], 10);
574
- const hitCount = parseInt(daMatch[2], 10);
575
- if (hitCount > 0) {
576
- if (!map.has(currentFile))
577
- map.set(currentFile, new Set());
578
- map.get(currentFile).add(lineNum);
579
- }
580
- }
581
- if (line === "end_of_record") {
582
- currentFile = "";
583
- }
584
- }
585
- return map;
586
- }
587
- /**
588
- * 带覆盖率联动的变异测试 — 只变异被测试覆盖的代码行。
589
- * 若变异了未覆盖行,该变异无法被测试检测到,审计结果无效。
590
- * @param testContent - 测试文件内容
591
- * @param sourceContent - 被测源码内容
592
- * @param coverageMap - 可选的覆盖率映射(文件 → 已覆盖行号),无则退化为全行变异
593
- * @param sourceFilePath - 源码文件路径,用于在 coverageMap 中查找覆盖行
594
- */
595
- function checkMutationResistanceWithCoverage(testContent, sourceContent, coverageMap, sourceFilePath) {
596
- const sourceLines = sourceContent.split("\n");
597
- const coveredLineNumbers = (sourceFilePath && coverageMap) ? coverageMap.get(sourceFilePath) : undefined;
598
- // 提取白名单关键逻辑行,并记录行号
599
- const candidates = [];
600
- for (let i = 0; i < sourceLines.length; i++) {
601
- if (isMutationCandidate(sourceLines[i])) {
602
- candidates.push({ line: sourceLines[i], lineNum: i + 1 });
603
- }
604
- }
605
- if (candidates.length === 0) {
606
- return {
607
- dimension: "scenario_coverage",
608
- passed: true,
609
- detail: "无可变异的关键逻辑行,跳过变异测试",
610
- };
611
- }
612
- // 覆盖率过滤: 只保留被测试覆盖的行
613
- let filteredCandidates = candidates;
614
- const hasCoverageData = coverageMap !== undefined && sourceFilePath !== undefined;
615
- if (hasCoverageData) {
616
- const covered = coveredLineNumbers ?? new Set();
617
- const beforeCount = candidates.length;
618
- if (covered.size === 0) {
619
- return {
620
- dimension: "scenario_coverage",
621
- passed: false,
622
- detail: `变异审计无效: ${beforeCount} 个可变异行均未被测试覆盖(覆盖率联动)`,
623
- };
624
- }
625
- filteredCandidates = candidates.filter((c) => covered.has(c.lineNum));
626
- if (filteredCandidates.length === 0) {
627
- return {
628
- dimension: "scenario_coverage",
629
- passed: false,
630
- detail: `变异审计无效: ${beforeCount} 个可变异行均未被测试覆盖(覆盖率联动)`,
631
- };
632
- }
633
- }
634
- // 随机抽样
635
- const seed = sourceContent.split("").reduce((acc, ch) => acc + ch.charCodeAt(0), 0);
636
- const rng = seededRandom(seed);
637
- const sampleSize = Math.min(filteredCandidates.length, 8);
638
- const keyLines = seededSample(filteredCandidates, sampleSize, rng);
639
- // 断言精确度检查 — 使用宽松正则以支持嵌套括号如 expect(fn(a, b))
640
- const preciseAssertions = testContent.match(/\bexpect\b.*?\.(toBe|toEqual|toStrictEqual|toBeLessThan|toBeGreaterThan|toThrow|toThrowError)\s*\(/g) ?? [];
641
- const looseAssertions = testContent.match(/\bexpect\b.*?\.(toBeTruthy|toBeFalsy|toBeDefined|toBeUndefined)\s*\(/g) ?? [];
642
- const totalAssertions = preciseAssertions.length + looseAssertions.length;
643
- if (totalAssertions === 0) {
644
- return {
645
- dimension: "scenario_coverage",
646
- passed: false,
647
- detail: "无断言,无法检测源码变异(变异测试防造假失败)",
648
- };
649
- }
650
- const precisionRatio = preciseAssertions.length / totalAssertions;
651
- let mutationsApplied = 0;
652
- let mutationsUndetected = 0;
653
- for (const item of keyLines) {
654
- const shuffledOps = seededSample([...MUTATION_OPERATORS], MUTATION_OPERATORS.length, rng);
655
- for (const mut of shuffledOps) {
656
- const mutated = mut.mutate(item.line);
657
- if (mutated !== item.line) {
658
- mutationsApplied++;
659
- const mutatedValues = mutated.match(/(!==|===|<=|>=|>|<|\b\d+\b|true|false)/g) ?? [];
660
- const hasDetection = mutatedValues.some((v) => testContent.includes(v));
661
- if (!hasDetection)
662
- mutationsUndetected++;
663
- break;
664
- }
665
- }
666
- }
667
- const passThreshold = MUTATION_CONFIG.precision_threshold;
668
- const precisionOk = precisionRatio >= passThreshold;
669
- const detectionOk = mutationsApplied === 0 || (mutationsUndetected / mutationsApplied) < passThreshold;
670
- const coverageNote = hasCoverageData ? `(覆盖率联动: ${filteredCandidates.length}/${candidates.length} 可变异行已被覆盖)` : "";
671
- if (precisionOk && detectionOk) {
672
- return {
673
- dimension: "scenario_coverage",
674
- passed: true,
675
- detail: `变异测试通过(随机抽样 ${keyLines.length} 行${coverageNote})— 精确断言率 ${(precisionRatio * 100).toFixed(0)}%,${mutationsApplied} 次变异中 ${mutationsApplied - mutationsUndetected} 次可检测`,
676
- };
677
- }
678
- return {
679
- dimension: "scenario_coverage",
680
- passed: false,
681
- detail: `变异测试未通过(随机抽样 ${keyLines.length} 行${coverageNote})— 精确断言率 ${(precisionRatio * 100).toFixed(0)}%(阈值 50%),${mutationsApplied} 次变异中 ${mutationsUndetected} 次不可检测。测试可能存在"自己证明自己"问题`,
682
- };
683
- }
684
- //# sourceMappingURL=test_quality.js.map