@hupan56/wlkj 3.0.0 → 3.1.2

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 (237) hide show
  1. package/bin/cli.js +1435 -1373
  2. package/package.json +1 -1
  3. package/templates/qoder/commands/optional/wl-insight.md +6 -4
  4. package/templates/qoder/commands/optional/wl-report.md +20 -39
  5. package/templates/qoder/commands/optional/wl-status.md +4 -4
  6. package/templates/qoder/commands/wl-code.md +2 -2
  7. package/templates/qoder/commands/wl-design.md +5 -5
  8. package/templates/qoder/commands/wl-init.md +3 -3
  9. package/templates/qoder/commands/wl-prd.md +26 -13
  10. package/templates/qoder/commands/wl-req.md +3 -3
  11. package/templates/qoder/commands/wl-search.md +78 -47
  12. package/templates/qoder/commands/wl-task.md +343 -40
  13. package/templates/qoder/commands/wl-test.md +10 -10
  14. package/templates/qoder/config.yaml +35 -13
  15. package/templates/qoder/hooks/post-tool-use.py +181 -0
  16. package/templates/qoder/hooks/session-start.py +82 -1
  17. package/templates/qoder/hooks/stop-eval.py +207 -0
  18. package/templates/qoder/hooks/user-prompt-submit.py +139 -0
  19. package/templates/qoder/rules/wl-pipeline.md +70 -8
  20. package/templates/qoder/scripts/README.md +78 -115
  21. package/templates/qoder/scripts/capability/__init__.py +26 -0
  22. package/templates/qoder/scripts/capability/__main__.py +72 -0
  23. package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
  24. package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
  25. package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
  26. package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
  27. package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
  28. package/templates/qoder/scripts/capability/caps/context.py +36 -0
  29. package/templates/qoder/scripts/capability/caps/cron.py +50 -0
  30. package/templates/qoder/scripts/capability/caps/identity.py +43 -0
  31. package/templates/qoder/scripts/capability/caps/memory.py +71 -0
  32. package/templates/qoder/scripts/capability/caps/notify.py +41 -0
  33. package/templates/qoder/scripts/capability/caps/present.py +48 -0
  34. package/templates/qoder/scripts/capability/caps/repo.py +31 -0
  35. package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
  36. package/templates/qoder/scripts/capability/chain.py +92 -0
  37. package/templates/qoder/scripts/capability/memory_chain.py +41 -0
  38. package/templates/qoder/scripts/capability/registry.py +246 -0
  39. package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
  40. package/templates/qoder/scripts/capability/smoke_test.py +211 -0
  41. package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
  42. package/templates/qoder/scripts/{report → deployment/setup}/__init__.py +11 -11
  43. package/templates/qoder/scripts/{setup → deployment/setup}/carriers.py +33 -26
  44. package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
  45. package/templates/qoder/scripts/{setup → deployment/setup}/init_doctor.py +40 -16
  46. package/templates/qoder/scripts/{setup → deployment/setup}/install_qoderwork.py +20 -12
  47. package/templates/qoder/scripts/{setup → deployment/setup}/platform_doctor.py +8 -2
  48. package/templates/qoder/scripts/{setup → deployment/setup}/repo_root.py +113 -112
  49. package/templates/qoder/scripts/{setup → deployment/setup}/setup.py +31 -11
  50. package/templates/qoder/scripts/{setup → deployment/setup}/setup_lanhu.py +13 -7
  51. package/templates/qoder/scripts/domain/__init__.py +0 -0
  52. package/templates/qoder/scripts/domain/design/__init__.py +0 -0
  53. package/templates/qoder/scripts/{report → domain/design}/fill_prototype.py +12 -6
  54. package/templates/qoder/scripts/{report → domain/design}/gen_design_doc.py +8 -2
  55. package/templates/qoder/scripts/{setup → domain/kg}/__init__.py +11 -11
  56. package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
  57. package/templates/qoder/scripts/{kg → domain/kg/build}/build_entity_registry.py +7 -2
  58. package/templates/qoder/scripts/{kg → domain/kg/build}/build_relations.py +7 -2
  59. package/templates/qoder/scripts/{kg → domain/kg/build}/build_style_index.py +13 -7
  60. package/templates/qoder/scripts/{kg → domain/kg/build}/build_workflows.py +7 -2
  61. package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build.py +76 -9
  62. package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build_db.py +7 -2
  63. package/templates/qoder/scripts/{kg → domain/kg/build}/kg_incremental.py +11 -6
  64. package/templates/qoder/scripts/{report → domain/kg/build}/learn_aggregate.py +9 -4
  65. package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
  66. package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
  67. package/templates/qoder/scripts/{common → domain/kg/extract}/test_extract.py +115 -115
  68. package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
  69. package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
  70. package/templates/qoder/scripts/{kg → domain/kg/graph}/kg_link_db.py +7 -2
  71. package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
  72. package/templates/qoder/scripts/{kg → domain/kg}/kg.py +263 -30
  73. package/templates/qoder/scripts/{common → domain/kg}/kg_capabilities.py +182 -182
  74. package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
  75. package/templates/qoder/scripts/{kg → domain/kg/search}/context_pack.py +30 -9
  76. package/templates/qoder/scripts/{kg → domain/kg/search}/enrich_prompt.py +10 -4
  77. package/templates/qoder/scripts/{kg → domain/kg/search}/prefetch.py +33 -8
  78. package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
  79. package/templates/qoder/scripts/{kg → domain/kg/search}/search_index.py +143 -77
  80. package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
  81. package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
  82. package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
  83. package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
  84. package/templates/qoder/scripts/{kg → domain/kg/storage}/kg_duckdb.py +38 -14
  85. package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
  86. package/templates/qoder/scripts/{report → domain/learning}/learn.py +8 -3
  87. package/templates/qoder/scripts/{mcp → domain/report}/__init__.py +11 -11
  88. package/templates/qoder/scripts/{report → domain/report}/add_session.py +9 -3
  89. package/templates/qoder/scripts/{report → domain/report}/export.py +10 -5
  90. package/templates/qoder/scripts/{report → domain/report}/report.py +9 -4
  91. package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
  92. package/templates/qoder/scripts/{report → domain/report}/role.py +10 -4
  93. package/templates/qoder/scripts/{report → domain/report}/status.py +112 -9
  94. package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
  95. package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
  96. package/templates/qoder/scripts/{report → domain/requirement}/archive_prd.py +8 -2
  97. package/templates/qoder/scripts/{report → domain/requirement}/req.py +228 -222
  98. package/templates/qoder/scripts/{kg → domain/task}/__init__.py +11 -11
  99. package/templates/qoder/scripts/{task → domain/task}/git_sync.py +40 -21
  100. package/templates/qoder/scripts/{task → domain/task}/syncgate.py +13 -8
  101. package/templates/qoder/scripts/{task → domain/task}/task.py +13 -5
  102. package/templates/qoder/scripts/{task → domain/task}/task_lifecycle.py +606 -596
  103. package/templates/qoder/scripts/{task → domain/task}/task_query.py +162 -161
  104. package/templates/qoder/scripts/{task → domain/task}/task_relations.py +425 -424
  105. package/templates/qoder/scripts/{task → domain/task}/team_sync.py +9 -4
  106. package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
  107. package/templates/qoder/scripts/foundation/__init__.py +0 -0
  108. package/templates/qoder/scripts/{common → foundation}/bootstrap.py +10 -10
  109. package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
  110. package/templates/qoder/scripts/{common → foundation/core}/cmd_registry.py +1 -1
  111. package/templates/qoder/scripts/{common → foundation/core}/config.py +1 -1
  112. package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -586
  113. package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
  114. package/templates/qoder/scripts/{common → foundation/data}/contract.py +2 -2
  115. package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
  116. package/templates/qoder/scripts/{common → foundation/data}/result.py +1 -1
  117. package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -404
  118. package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
  119. package/templates/qoder/scripts/{common → foundation/identity}/check_publish.py +103 -98
  120. package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -239
  121. package/templates/qoder/scripts/{common → foundation/identity}/guard.py +2 -2
  122. package/templates/qoder/scripts/{common → foundation/identity}/identity.py +14 -10
  123. package/templates/qoder/scripts/{common → foundation/identity}/roles.py +1 -1
  124. package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
  125. package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
  126. package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +2 -2
  127. package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
  128. package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
  129. package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
  130. package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
  131. package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
  132. package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
  133. package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
  134. package/templates/qoder/scripts/{common → foundation/protocol}/mcp_base.py +1 -1
  135. package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
  136. package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
  137. package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
  138. package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
  139. package/templates/qoder/scripts/orchestration/__init__.py +0 -0
  140. package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
  141. package/templates/qoder/scripts/protocol/__init__.py +0 -0
  142. package/templates/qoder/scripts/protocol/browser/README.md +207 -0
  143. package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
  144. package/templates/qoder/scripts/protocol/browser/config.env +9 -0
  145. package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
  146. package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
  147. package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
  148. package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
  149. package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
  150. package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
  151. package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
  152. package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
  153. package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
  154. package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
  155. package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
  156. package/templates/qoder/scripts/{mcp → protocol/mcp}/lanhu_stdio_wrapper.py +11 -5
  157. package/templates/qoder/scripts/{mcp → protocol/mcp}/mcp_doctor.py +47 -14
  158. package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
  159. package/templates/qoder/scripts/{mcp → protocol/mcp}/mysql_mcp_server.py +181 -67
  160. package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
  161. package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
  162. package/templates/qoder/scripts/protocol/transports/base.py +87 -0
  163. package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
  164. package/templates/qoder/scripts/protocol/transports/http.py +141 -0
  165. package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
  166. package/templates/qoder/scripts/run_weekly_update.bat +27 -18
  167. package/templates/qoder/scripts/run_weekly_update.sh +22 -13
  168. package/templates/qoder/scripts/validation/__init__.py +0 -0
  169. package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
  170. package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
  171. package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
  172. package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
  173. package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
  174. package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
  175. package/templates/qoder/scripts/{report → validation/metrics}/eval_prd.py +17 -10
  176. package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
  177. package/templates/qoder/scripts/validation/test/__init__.py +11 -0
  178. package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
  179. package/templates/qoder/scripts/{test → validation/test}/autotest.py +64 -4
  180. package/templates/qoder/scripts/{common → validation/test}/autotest_auth.py +1 -1
  181. package/templates/qoder/scripts/{test → validation/test}/autotest_batch.py +16 -3
  182. package/templates/qoder/scripts/{test → validation/test}/autotest_data.py +17 -12
  183. package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
  184. package/templates/qoder/scripts/{test → validation/test}/autotest_run.py +17 -3
  185. package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
  186. package/templates/qoder/scripts/{test → validation/test}/benchmark.py +12 -6
  187. package/templates/qoder/scripts/{test → validation/test}/kg_auto_login.py +8 -2
  188. package/templates/qoder/scripts/{test → validation/test}/kg_test_runner.py +7 -2
  189. package/templates/qoder/scripts/{test → validation/test}/page_probe.py +8 -3
  190. package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
  191. package/templates/qoder/settings.json +30 -0
  192. package/templates/qoder/skills/design-import/SKILL.md +22 -13
  193. package/templates/qoder/skills/design-review/SKILL.md +19 -10
  194. package/templates/qoder/skills/prd-generator/SKILL.md +22 -18
  195. package/templates/qoder/skills/prd-review/SKILL.md +6 -4
  196. package/templates/qoder/skills/prototype-generator/SKILL.md +63 -14
  197. package/templates/qoder/skills/spec-coder/SKILL.md +22 -13
  198. package/templates/qoder/skills/spec-generator/SKILL.md +22 -10
  199. package/templates/qoder/skills/test-generator/SKILL.md +7 -5
  200. package/templates/qoder/skills/wl-code/SKILL.md +23 -11
  201. package/templates/qoder/skills/wl-commit/SKILL.md +9 -8
  202. package/templates/qoder/skills/wl-design/SKILL.md +5 -3
  203. package/templates/qoder/skills/wl-init/SKILL.md +6 -6
  204. package/templates/qoder/skills/wl-insight/SKILL.md +23 -10
  205. package/templates/qoder/skills/wl-prd-full/SKILL.md +57 -6
  206. package/templates/qoder/skills/wl-prd-quick/SKILL.md +3 -3
  207. package/templates/qoder/skills/wl-prd-review/SKILL.md +16 -4
  208. package/templates/qoder/skills/wl-report/SKILL.md +25 -23
  209. package/templates/qoder/skills/wl-search/SKILL.md +149 -78
  210. package/templates/qoder/skills/wl-spec/SKILL.md +7 -5
  211. package/templates/qoder/skills/wl-status/SKILL.md +48 -16
  212. package/templates/qoder/skills/wl-task/SKILL.md +92 -10
  213. package/templates/qoder/skills/wl-test/SKILL.md +75 -29
  214. package/templates/qoder/templates/prd-full-template.md +7 -0
  215. package/templates/root/AGENTS.md +275 -236
  216. package/templates/root/requirements.txt +3 -0
  217. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
  218. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
  219. package/templates/.qoder/.runtime/ctx-cache-5660152f1d6dd819.md +0 -23
  220. package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +0 -23
  221. package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +0 -1
  222. package/templates/.qoder/learning/eval-history.jsonl +0 -28
  223. package/templates/data/index/wiki-index.json +0 -8
  224. package/templates/qoder/nul +0 -4
  225. package/templates/qoder/scripts/kg/extract.py +0 -398
  226. package/templates/qoder/scripts/kg/kg_semantic.py +0 -156
  227. package/templates/qoder/scripts/mcp/kg_mcp_server.py +0 -606
  228. package/templates/qoder/scripts/mcp/mcp_launcher.py +0 -442
  229. package/templates/qoder/scripts/mcp/zentao_mcp_server.py +0 -384
  230. package/templates/qoder/scripts/task/__init__.py +0 -11
  231. package/templates/qoder/scripts/test/__init__.py +0 -11
  232. package/templates/qoder/scripts/wlkj.py +0 -116
  233. /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
  234. /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
  235. /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
  236. /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
  237. /package/templates/qoder/scripts/{test → validation/test}/secure-ls.js +0 -0
@@ -1,115 +1,115 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- Test code extractor — E1: extract structured assertions from Test.java files.
4
-
5
- These are the "second layer" of the knowledge graph: structured business
6
- knowledge (Given-When-Then, assertion patterns, service dependencies) that
7
- production code extraction can never reach. 39+ Test.java files in fywl-ics
8
- contain real integration tests with @Autowired services and assertion logic.
9
-
10
- JOIN keys to other layers:
11
- - injected service class → code-callgraph (who this test exercises)
12
- - package path → ui-modules / prd-index (which business module)
13
- - @Test method name → code-keyword (searchable)
14
- """
15
- import os
16
- import re
17
-
18
- # @Test annotation (Junit 5)
19
- RE_TEST = re.compile(r'@Test\b', re.M)
20
- # @DisplayName("...")
21
- RE_DISPLAY = re.compile(r'@DisplayName\s*\(\s*["\']([^"\']+)["\']\s*\)')
22
- # method signature: void methodName() or public void methodName()
23
- RE_METHOD = re.compile(r'(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:void|\w+(?:<[^>]*>)?)\s+(\w+)\s*\([^)]*\)\s*(?:throws\s+[\w.,\s]+)?\s*\{', re.M)
24
- # @Autowired / @Resource field: @Autowired private SomeService svc;
25
- RE_INJECT = re.compile(r'@(?:Autowired|Resource)\s+(?:private\s+|protected\s+)?(\w+(?:<[^>]*>)?)\s+(\w+)\s*;', re.M)
26
- # @SpringBootTest, @RunWith, @ExtendWith — marks integration tests
27
- RE_INTEGRATION = re.compile(r'@(?:SpringBootTest|RunWith|ExtendWith|WebMvcTest|DataJpaTest)', re.M)
28
- # Assertions: assertThat(x).isEqualTo(y), Assertions.assertEquals(x,y), Assert.X
29
- RE_ASSERT = re.compile(r'(?:assertThat|Assertions?|Assert)\.(\w+)\s*\(', re.M)
30
- # package declaration
31
- RE_PACKAGE = re.compile(r'package\s+([\w.]+)\s*;', re.M)
32
-
33
-
34
- def extract_test_assertions(proj_path):
35
- """Scan a project for *Test.java files and extract structured test info.
36
-
37
- Returns a list of dicts:
38
- {file, class, package, method, display_name, injected_deps[],
39
- assertions[], is_integration, comment}
40
- """
41
- results = []
42
- if not proj_path or not os.path.isdir(proj_path):
43
- return results
44
-
45
- for root, dirs, files in os.walk(proj_path):
46
- dirs[:] = [d for d in dirs if d not in ('node_modules', '.git', 'dist', '__pycache__')]
47
- for f in files:
48
- if not f.endswith('Test.java'):
49
- continue
50
- fpath = os.path.join(root, f)
51
- try:
52
- content = open(fpath, encoding='utf-8', errors='ignore').read()
53
- except Exception:
54
- continue
55
- if '@Test' not in content:
56
- continue
57
-
58
- # Class-level info
59
- pkg_m = RE_PACKAGE.search(content)
60
- package = pkg_m.group(1) if pkg_m else ''
61
- is_integration = bool(RE_INTEGRATION.search(content))
62
-
63
- # Injected dependencies (services/mappers under test)
64
- injected = []
65
- for im in RE_INJECT.finditer(content):
66
- dep_type = im.group(1).split('<')[0] # strip generics
67
- injected.append({'type': dep_type, 'name': im.group(2)})
68
-
69
- # Class name (from filename, strip .java)
70
- class_name = f[:-5]
71
-
72
- # Extract each @Test method
73
- test_positions = [m.start() for m in RE_TEST.finditer(content)]
74
- display_map = {}
75
- for dm in RE_DISPLAY.finditer(content):
76
- # Associate @DisplayName with the nearest @Test after it
77
- display_map[dm.end()] = dm.group(1)
78
-
79
- for tp in test_positions:
80
- # Find display name (nearest before this @Test, within 100 chars)
81
- display = ''
82
- for pos, name in display_map.items():
83
- if pos <= tp and tp - pos < 200:
84
- display = name
85
-
86
- # Find method name after @Test
87
- search_region = content[tp:tp + 300]
88
- method_m = RE_METHOD.search(search_region)
89
- if not method_m:
90
- continue
91
- method_name = method_m.group(1)
92
-
93
- # Extract method body for assertions
94
- body_start = tp + method_m.end()
95
- body = content[body_start:body_start + 600]
96
- assertions = list(set(m.group(1) for m in RE_ASSERT.finditer(body)))
97
-
98
- # Extract comment before @Test (/** ... */ or // ...)
99
- before = content[max(0, tp - 200):tp]
100
- comment_m = re.search(r'(?:/\*\*\s*\n\s*\*\s*([^*]+?)\s*\n)|(?://\s*(.+)$)', before, re.M)
101
- comment = (comment_m.group(1) or comment_m.group(2)).strip() if comment_m else ''
102
-
103
- results.append({
104
- 'file': fpath.replace('\\', '/'),
105
- 'class': class_name,
106
- 'package': package,
107
- 'method': method_name,
108
- 'display_name': display,
109
- 'comment': comment[:80],
110
- 'injected_deps': [d['type'] for d in injected[:5]],
111
- 'assertions': assertions[:5],
112
- 'is_integration': is_integration,
113
- })
114
-
115
- return results
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Test code extractor — E1: extract structured assertions from Test.java files.
4
+
5
+ These are the "second layer" of the knowledge graph: structured business
6
+ knowledge (Given-When-Then, assertion patterns, service dependencies) that
7
+ production code extraction can never reach. 39+ Test.java files in fywl-ics
8
+ contain real integration tests with @Autowired services and assertion logic.
9
+
10
+ JOIN keys to other layers:
11
+ - injected service class → code-callgraph (who this test exercises)
12
+ - package path → ui-modules / prd-index (which business module)
13
+ - @Test method name → code-keyword (searchable)
14
+ """
15
+ import os
16
+ import re
17
+
18
+ # @Test annotation (Junit 5)
19
+ RE_TEST = re.compile(r'@Test\b', re.M)
20
+ # @DisplayName("...")
21
+ RE_DISPLAY = re.compile(r'@DisplayName\s*\(\s*["\']([^"\']+)["\']\s*\)')
22
+ # method signature: void methodName() or public void methodName()
23
+ RE_METHOD = re.compile(r'(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:void|\w+(?:<[^>]*>)?)\s+(\w+)\s*\([^)]*\)\s*(?:throws\s+[\w.,\s]+)?\s*\{', re.M)
24
+ # @Autowired / @Resource field: @Autowired private SomeService svc;
25
+ RE_INJECT = re.compile(r'@(?:Autowired|Resource)\s+(?:private\s+|protected\s+)?(\w+(?:<[^>]*>)?)\s+(\w+)\s*;', re.M)
26
+ # @SpringBootTest, @RunWith, @ExtendWith — marks integration tests
27
+ RE_INTEGRATION = re.compile(r'@(?:SpringBootTest|RunWith|ExtendWith|WebMvcTest|DataJpaTest)', re.M)
28
+ # Assertions: assertThat(x).isEqualTo(y), Assertions.assertEquals(x,y), Assert.X
29
+ RE_ASSERT = re.compile(r'(?:assertThat|Assertions?|Assert)\.(\w+)\s*\(', re.M)
30
+ # package declaration
31
+ RE_PACKAGE = re.compile(r'package\s+([\w.]+)\s*;', re.M)
32
+
33
+
34
+ def extract_test_assertions(proj_path):
35
+ """Scan a project for *Test.java files and extract structured test info.
36
+
37
+ Returns a list of dicts:
38
+ {file, class, package, method, display_name, injected_deps[],
39
+ assertions[], is_integration, comment}
40
+ """
41
+ results = []
42
+ if not proj_path or not os.path.isdir(proj_path):
43
+ return results
44
+
45
+ for root, dirs, files in os.walk(proj_path):
46
+ dirs[:] = [d for d in dirs if d not in ('node_modules', '.git', 'dist', '__pycache__')]
47
+ for f in files:
48
+ if not f.endswith('Test.java'):
49
+ continue
50
+ fpath = os.path.join(root, f)
51
+ try:
52
+ content = open(fpath, encoding='utf-8', errors='ignore').read()
53
+ except Exception:
54
+ continue
55
+ if '@Test' not in content:
56
+ continue
57
+
58
+ # Class-level info
59
+ pkg_m = RE_PACKAGE.search(content)
60
+ package = pkg_m.group(1) if pkg_m else ''
61
+ is_integration = bool(RE_INTEGRATION.search(content))
62
+
63
+ # Injected dependencies (services/mappers under test)
64
+ injected = []
65
+ for im in RE_INJECT.finditer(content):
66
+ dep_type = im.group(1).split('<')[0] # strip generics
67
+ injected.append({'type': dep_type, 'name': im.group(2)})
68
+
69
+ # Class name (from filename, strip .java)
70
+ class_name = f[:-5]
71
+
72
+ # Extract each @Test method
73
+ test_positions = [m.start() for m in RE_TEST.finditer(content)]
74
+ display_map = {}
75
+ for dm in RE_DISPLAY.finditer(content):
76
+ # Associate @DisplayName with the nearest @Test after it
77
+ display_map[dm.end()] = dm.group(1)
78
+
79
+ for tp in test_positions:
80
+ # Find display name (nearest before this @Test, within 100 chars)
81
+ display = ''
82
+ for pos, name in display_map.items():
83
+ if pos <= tp and tp - pos < 200:
84
+ display = name
85
+
86
+ # Find method name after @Test
87
+ search_region = content[tp:tp + 300]
88
+ method_m = RE_METHOD.search(search_region)
89
+ if not method_m:
90
+ continue
91
+ method_name = method_m.group(1)
92
+
93
+ # Extract method body for assertions
94
+ body_start = tp + method_m.end()
95
+ body = content[body_start:body_start + 600]
96
+ assertions = list(set(m.group(1) for m in RE_ASSERT.finditer(body)))
97
+
98
+ # Extract comment before @Test (/** ... */ or // ...)
99
+ before = content[max(0, tp - 200):tp]
100
+ comment_m = re.search(r'(?:/\*\*\s*\n\s*\*\s*([^*]+?)\s*\n)|(?://\s*(.+)$)', before, re.M)
101
+ comment = (comment_m.group(1) or comment_m.group(2)).strip() if comment_m else ''
102
+
103
+ results.append({
104
+ 'file': fpath.replace('\\', '/'),
105
+ 'class': class_name,
106
+ 'package': package,
107
+ 'method': method_name,
108
+ 'display_name': display,
109
+ 'comment': comment[:80],
110
+ 'injected_deps': [d['type'] for d in injected[:5]],
111
+ 'assertions': assertions[:5],
112
+ 'is_integration': is_integration,
113
+ })
114
+
115
+ return results