@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.
- package/bin/cli.js +1435 -1373
- package/package.json +1 -1
- package/templates/qoder/commands/optional/wl-insight.md +6 -4
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +5 -5
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +3 -3
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +10 -10
- package/templates/qoder/config.yaml +35 -13
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +82 -1
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +70 -8
- package/templates/qoder/scripts/README.md +78 -115
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/{report → deployment/setup}/__init__.py +11 -11
- package/templates/qoder/scripts/{setup → deployment/setup}/carriers.py +33 -26
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{setup → deployment/setup}/init_doctor.py +40 -16
- package/templates/qoder/scripts/{setup → deployment/setup}/install_qoderwork.py +20 -12
- package/templates/qoder/scripts/{setup → deployment/setup}/platform_doctor.py +8 -2
- package/templates/qoder/scripts/{setup → deployment/setup}/repo_root.py +113 -112
- package/templates/qoder/scripts/{setup → deployment/setup}/setup.py +31 -11
- package/templates/qoder/scripts/{setup → deployment/setup}/setup_lanhu.py +13 -7
- package/templates/qoder/scripts/domain/__init__.py +0 -0
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/design}/fill_prototype.py +12 -6
- package/templates/qoder/scripts/{report → domain/design}/gen_design_doc.py +8 -2
- package/templates/qoder/scripts/{setup → domain/kg}/__init__.py +11 -11
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_entity_registry.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_relations.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_style_index.py +13 -7
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_workflows.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build.py +76 -9
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build_db.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_incremental.py +11 -6
- package/templates/qoder/scripts/{report → domain/kg/build}/learn_aggregate.py +9 -4
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/{common → domain/kg/extract}/test_extract.py +115 -115
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/graph}/kg_link_db.py +7 -2
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg → domain/kg}/kg.py +263 -30
- package/templates/qoder/scripts/{common → domain/kg}/kg_capabilities.py +182 -182
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/search}/context_pack.py +30 -9
- package/templates/qoder/scripts/{kg → domain/kg/search}/enrich_prompt.py +10 -4
- package/templates/qoder/scripts/{kg → domain/kg/search}/prefetch.py +33 -8
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{kg → domain/kg/search}/search_index.py +143 -77
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/storage}/kg_duckdb.py +38 -14
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/learning}/learn.py +8 -3
- package/templates/qoder/scripts/{mcp → domain/report}/__init__.py +11 -11
- package/templates/qoder/scripts/{report → domain/report}/add_session.py +9 -3
- package/templates/qoder/scripts/{report → domain/report}/export.py +10 -5
- package/templates/qoder/scripts/{report → domain/report}/report.py +9 -4
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/{report → domain/report}/role.py +10 -4
- package/templates/qoder/scripts/{report → domain/report}/status.py +112 -9
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/requirement}/archive_prd.py +8 -2
- package/templates/qoder/scripts/{report → domain/requirement}/req.py +228 -222
- package/templates/qoder/scripts/{kg → domain/task}/__init__.py +11 -11
- package/templates/qoder/scripts/{task → domain/task}/git_sync.py +40 -21
- package/templates/qoder/scripts/{task → domain/task}/syncgate.py +13 -8
- package/templates/qoder/scripts/{task → domain/task}/task.py +13 -5
- package/templates/qoder/scripts/{task → domain/task}/task_lifecycle.py +606 -596
- package/templates/qoder/scripts/{task → domain/task}/task_query.py +162 -161
- package/templates/qoder/scripts/{task → domain/task}/task_relations.py +425 -424
- package/templates/qoder/scripts/{task → domain/task}/team_sync.py +9 -4
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation}/bootstrap.py +10 -10
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/core}/cmd_registry.py +1 -1
- package/templates/qoder/scripts/{common → foundation/core}/config.py +1 -1
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -586
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/data}/contract.py +2 -2
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/{common → foundation/data}/result.py +1 -1
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -404
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/identity}/check_publish.py +103 -98
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -239
- package/templates/qoder/scripts/{common → foundation/identity}/guard.py +2 -2
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +14 -10
- package/templates/qoder/scripts/{common → foundation/identity}/roles.py +1 -1
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +2 -2
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/{common → foundation/protocol}/mcp_base.py +1 -1
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{mcp → protocol/mcp}/lanhu_stdio_wrapper.py +11 -5
- package/templates/qoder/scripts/{mcp → protocol/mcp}/mcp_doctor.py +47 -14
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/{mcp → protocol/mcp}/mysql_mcp_server.py +181 -67
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{report → validation/metrics}/eval_prd.py +17 -10
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{test → validation/test}/autotest.py +64 -4
- package/templates/qoder/scripts/{common → validation/test}/autotest_auth.py +1 -1
- package/templates/qoder/scripts/{test → validation/test}/autotest_batch.py +16 -3
- package/templates/qoder/scripts/{test → validation/test}/autotest_data.py +17 -12
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{test → validation/test}/autotest_run.py +17 -3
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{test → validation/test}/benchmark.py +12 -6
- package/templates/qoder/scripts/{test → validation/test}/kg_auto_login.py +8 -2
- package/templates/qoder/scripts/{test → validation/test}/kg_test_runner.py +7 -2
- package/templates/qoder/scripts/{test → validation/test}/page_probe.py +8 -3
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +30 -0
- package/templates/qoder/skills/design-import/SKILL.md +22 -13
- package/templates/qoder/skills/design-review/SKILL.md +19 -10
- package/templates/qoder/skills/prd-generator/SKILL.md +22 -18
- package/templates/qoder/skills/prd-review/SKILL.md +6 -4
- package/templates/qoder/skills/prototype-generator/SKILL.md +63 -14
- package/templates/qoder/skills/spec-coder/SKILL.md +22 -13
- package/templates/qoder/skills/spec-generator/SKILL.md +22 -10
- package/templates/qoder/skills/test-generator/SKILL.md +7 -5
- package/templates/qoder/skills/wl-code/SKILL.md +23 -11
- package/templates/qoder/skills/wl-commit/SKILL.md +9 -8
- package/templates/qoder/skills/wl-design/SKILL.md +5 -3
- package/templates/qoder/skills/wl-init/SKILL.md +6 -6
- package/templates/qoder/skills/wl-insight/SKILL.md +23 -10
- package/templates/qoder/skills/wl-prd-full/SKILL.md +57 -6
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +3 -3
- package/templates/qoder/skills/wl-prd-review/SKILL.md +16 -4
- package/templates/qoder/skills/wl-report/SKILL.md +25 -23
- package/templates/qoder/skills/wl-search/SKILL.md +149 -78
- package/templates/qoder/skills/wl-spec/SKILL.md +7 -5
- package/templates/qoder/skills/wl-status/SKILL.md +48 -16
- package/templates/qoder/skills/wl-task/SKILL.md +92 -10
- package/templates/qoder/skills/wl-test/SKILL.md +75 -29
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -236
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/.qoder/.runtime/ctx-cache-5660152f1d6dd819.md +0 -23
- package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +0 -23
- package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +0 -1
- package/templates/.qoder/learning/eval-history.jsonl +0 -28
- package/templates/data/index/wiki-index.json +0 -8
- package/templates/qoder/nul +0 -4
- package/templates/qoder/scripts/kg/extract.py +0 -398
- package/templates/qoder/scripts/kg/kg_semantic.py +0 -156
- package/templates/qoder/scripts/mcp/kg_mcp_server.py +0 -606
- package/templates/qoder/scripts/mcp/mcp_launcher.py +0 -442
- package/templates/qoder/scripts/mcp/zentao_mcp_server.py +0 -384
- package/templates/qoder/scripts/task/__init__.py +0 -11
- package/templates/qoder/scripts/test/__init__.py +0 -11
- package/templates/qoder/scripts/wlkj.py +0 -116
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /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
|