diff --git a/.env.example b/.env.example index 23256db..3b8cad7 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,14 @@ DEEPSEEK_API_KEY= # DEEPSEEK_MAX_RETRIES=3 # DEEPSEEK_MAX_TOKENS=32768 +# ── 通用 LLM 客户端(agents/llm.py)───────────────────────── +# LLM_MODEL=deepseek-v4-flash +# LLM_API_BASE=https://api.deepseek.com/v1 +# LLM_API_KEY= + +# ── Web E2E 测试服务器端口(tests/test_*_e2e.py)──────────── +# TEST_PORT=8000 + # ── 外部测试数据目录(test-data/ 回归脚本、诊断脚本)────────── # 未设置时自动推导为 %USERPROFILE%\Desktop\2026技术大赛\ 下同名目录 # COBOL_TNA_ROOT=D:\cobol-tna-system diff --git a/SETUP.md b/SETUP.md index 1868242..0fd6847 100644 --- a/SETUP.md +++ b/SETUP.md @@ -76,7 +76,7 @@ cobc --version # COB_LIBRARY_PATH — 运行时 DLL 搜索路径 # 非 DB 管道:子程序 DLL 目录(如 cobol-tna-system\bin) # DB 管道:必须包含 gixsql\lib\(libgixsql.dll, libgcc_s_dw2-1.dll 等) -# 如: set COB_LIBRARY_PATH=D:\cobol-java\cobol-java-v3\gixsql\lib +# 如: set COB_LIBRARY_PATH=\gixsql\lib # GIXSQL_DB_PATH — DB 管道 SQLite 数据库路径(默认 C:\Temp\gix\) # 如: set GIXSQL_DB_PATH=C:\Temp\gix @@ -114,14 +114,14 @@ cobc --version cd D:\ git clone https://gittea.dev/hangshuo652/cobol-java-v3.git # 或从已有仓库拉取 -cd D:\cobol-java\cobol-java-v3 +cd git pull ``` ### 3.4 验证安装 ```bash -cd D:\cobol-java\cobol-java-v3 +cd python -c "from cobol_testgen import extract_structure; print('OK')" # 输出: OK ``` @@ -188,7 +188,7 @@ cobol-java-v3/ ### 5.1 快速验证(10 秒) ```bash -cd D:\cobol-java\cobol-java-v3 +cd python test-data/s15_coverage_verification.py ``` @@ -400,7 +400,7 @@ dps = cov["decision_points"] # 各决策点明细 ### 电信计费系统 (37 程序) ``` -路径: D:\cobol-java\cobol-test-programs/ +路径: 由环境变量 COBOL_BENCH_ROOT 指定(默认推导 %USERPROFILE%\Desktop\2026技术大赛\cobol-test-programs) COPYBOOK: common/copybooks/ 类型: Matching / KeyBreak / Division / CSV / Sort 等 ``` @@ -408,7 +408,7 @@ COPYBOOK: common/copybooks/ ### 勤怠管理系统 (22 程序) ``` -路径: D:\cobol-java\cobol-tna-system/ +路径: 由环境变量 COBOL_TNA_ROOT 指定(默认推导 %USERPROFILE%\Desktop\2026技术大赛\cobol-tna-system) COPYBOOK: cpy/ 子程序: sub/*.cbl → bin/*.dll 类型: 日企勤怠管理 (打工统计) @@ -424,7 +424,7 @@ DB 程序 (EXEC SQL): KIN02UPD, KIN03EXP, KIN06CLD, KIN08DBU, KIN09CSV, ZAN06UPD ```batch @echo off -cd /d D:\cobol-java\cobol-java-v3 +cd /d echo === COBOL Test Data Generator === echo [1/3] Checking dependencies... python -c "import lark" 2>nul || pip install lark diff --git a/SETUP_QUICK.md b/SETUP_QUICK.md index 7aa9f48..e28f581 100644 --- a/SETUP_QUICK.md +++ b/SETUP_QUICK.md @@ -23,7 +23,7 @@ cobc --version ```powershell cd D:\ git clone https://gittea.dev/hangshuo652/cobol-java-v3.git -cd D:\cobol-java\cobol-java-v3 +cd ``` ## 4. 跑验证 diff --git a/_AI_USAGE_LOG.md b/_AI_USAGE_LOG.md index 43b6e54..ea3984c 100644 --- a/_AI_USAGE_LOG.md +++ b/_AI_USAGE_LOG.md @@ -4,6 +4,12 @@ --- +### 2026-08-24 05:00:00 - 修复 +- **范式步骤:** 硬编码收尾清除(P2/P3/文档) +- **修改摘要:** agents/llm.py 错误默认模型gpt-4o-mini改为LLM_MODEL环境变量解析(design_data.py同步去除显式实参,aurak.toml修正为deepseek-v4-flash);3个E2E测试文件端口号TEST_PORT环境变量化(JS内URL用__BASE_URL__占位符替换规避f-string花括号转义);8个md文档机器路径/过时签名占位符化;.env.example补充TEST_PORT与通用LLM条目。全库gpt-4o-mini、127.0.0.1:8000、marye、D盘cobol-java归零 +- **涉及文件:** `agents/llm.py`, `agents/design_data.py`, `aurak.toml`, `tests/test_web_e2e.py`, `tests/test_biz_e2e.py`, `tests/e2e/test_pipeline.py`, `test-data/test_deep_validation.py`, `.env.example`, `docs/plans/*`, `SETUP.md`, `SETUP_QUICK.md`, `docs/*.md`(4个) +- **使用模型:** deepseek + ### 2026-08-24 04:00:00 - 修复 - **范式步骤:** 硬编码清除(P0+P1) - **修改摘要:** 清除2个硬编码API Key及文档明文(config.json取消跟踪改gitignore管理);orchestrator_db.py DB路径抽GIXSQL_DB_DIR常量;hina/retry.py移除浏览器下载路径回退;新增test-data/_paths.py集中路径解析(环境变量→目录推导兜底);13个test-data脚本、tests下5文件、3个diagnose脚本全部去机器特定路径;创建.env.example。回归:260通过/6既有失败,零新增失败 diff --git a/agents/design_data.py b/agents/design_data.py index dc1f1a0..9be6708 100644 --- a/agents/design_data.py +++ b/agents/design_data.py @@ -166,7 +166,7 @@ class DesignDataGenerator: """式样书驱动测试数据生成器。 使用例: - llm = LLMClient(model="deepseek-v4-flash") + llm = LLMClient() # 模型经 LLM_MODEL 环境变量配置 gen = DesignDataGenerator(llm, cpy_dirs=["cpy"]) records = gen.generate( design_md_text=design_text, diff --git a/agents/llm.py b/agents/llm.py index 4e57abc..478897d 100644 --- a/agents/llm.py +++ b/agents/llm.py @@ -4,8 +4,9 @@ import httpx class LLMClient: - def __init__(self, model="gpt-4o-mini", timeout=15, cache_dir=".cache/llm"): - self.model = model + def __init__(self, model=None, timeout=15, cache_dir=".cache/llm"): + # 模型解析链: 显式实参 → LLM_MODEL 环境变量 → 项目默认(与 config 层一致) + self.model = model or os.environ.get("LLM_MODEL", "deepseek-v4-flash") self.timeout = timeout self.dir = Path(cache_dir) self.dir.mkdir(parents=True, exist_ok=True) diff --git a/aurak.toml b/aurak.toml index 5ea6d9c..cc5c100 100644 --- a/aurak.toml +++ b/aurak.toml @@ -4,7 +4,7 @@ copybook_paths = ["./copybooks", "/usr/share/copybooks"] dialect = "ibm" [llm] -model = "gpt-4o-mini" +model = "deepseek-v4-flash" timeout = 15 cache_dir = ".cache/llm" diff --git a/docs/cobol-statement-benchmark-plan.md b/docs/cobol-statement-benchmark-plan.md index 9497779..508b7cd 100644 --- a/docs/cobol-statement-benchmark-plan.md +++ b/docs/cobol-statement-benchmark-plan.md @@ -1,6 +1,6 @@ # COBOL 语句测试基准 — 详细测试计划 v1.0 -> 日期: 2026-06-21 | 对象: D:\cobol-java\cobol-java-v3 +> 日期: 2026-06-21 | 对象: > 范围: COBOL 85/2002 语句类型全覆盖 × 解析/数据生成/分类 三维度 --- diff --git a/docs/cobol-statement-benchmark-report.md b/docs/cobol-statement-benchmark-report.md index 87fdbf9..02ed7d1 100644 --- a/docs/cobol-statement-benchmark-report.md +++ b/docs/cobol-statement-benchmark-report.md @@ -1,6 +1,6 @@ # COBOL 语句测试基准 — 完整测试报告 -> 生成日期: 2026-06-21 | 工程: D:\cobol-java\cobol-java-v3 +> 生成日期: 2026-06-21 | 工程: > 分支: feat/phase2-review-fixes | 基于: featt/phase2-complete --- diff --git a/docs/detailed-design/05-agents-llm.md b/docs/detailed-design/05-agents-llm.md index a5928c2..6605427 100644 --- a/docs/detailed-design/05-agents-llm.md +++ b/docs/detailed-design/05-agents-llm.md @@ -362,7 +362,7 @@ class DesignDataGenerator: ```python class LLMClient: - def __init__(self, model="gpt-4o-mini", timeout=15, cache_dir=".cache/llm") + def __init__(self, model=None, timeout=15, cache_dir=".cache/llm") # model 经 LLM_MODEL 环境变量解析 def call(self, messages: list[dict], retries=1) -> str def _key(self, msgs: list[dict]) -> str # SHA256 哈希键 def _get(self, k: str) -> str | None # 缓存读取 diff --git a/docs/enhanced-test-implementation-plan.md b/docs/enhanced-test-implementation-plan.md index 61a24c0..80a62c9 100644 --- a/docs/enhanced-test-implementation-plan.md +++ b/docs/enhanced-test-implementation-plan.md @@ -332,7 +332,7 @@ def check_coverage(structure: dict, test_records: list[dict]) -> dict: - [ ] **Step 5: 运行 import 测试确认封装正确** -Run: `cd D:/cobol-java/v3-gstack-code-gen && python -c "from cobol_testgen import extract_structure, generate_data, incremental_supplement; print('API OK')"` +Run: `cd && python -c "from cobol_testgen import extract_structure, generate_data, incremental_supplement; print('API OK')"` Expected: `API OK` - [ ] **Step 6: Commit** @@ -384,7 +384,7 @@ class VerificationRun: - [ ] **Step 2: 运行测试确认不破坏现有代码** -Run: `cd D:/cobol-java/v3-gstack-code-gen && python -c "from data.diff_result import VerificationRun; vr = VerificationRun(); print(vr.paragraph_rate, vr.quality_score)"` +Run: `cd && python -c "from data.diff_result import VerificationRun; vr = VerificationRun(); print(vr.paragraph_rate, vr.quality_score)"` Expected: `0.0 0.0` - [ ] **Step 3: Commit** @@ -435,7 +435,7 @@ enabled = false - [ ] **Step 3: 确认 Config 向后兼容** -Run: `cd D:/cobol-java/v3-gstack-code-gen && python -c "from config import Config; c = Config(); print(c.quality_gate_mode, c.quality_gate_decision_threshold)"` +Run: `cd && python -c "from config import Config; c = Config(); print(c.quality_gate_mode, c.quality_gate_decision_threshold)"` Expected: `warn 0.9` - [ ] **Step 4: Commit** @@ -532,7 +532,7 @@ def run_pipeline(cfg: Config, cpath: str, cbl: str, java: str, map_path: str) -> - [ ] **Step 2: 运行测试确认 import 正确** -Run: `cd D:/cobol-java/v3-gstack-code-gen && python -c "from orchestrator import run_pipeline; print('import OK')"` +Run: `cd && python -c "from orchestrator import run_pipeline; print('import OK')"` Expected: `import OK` - [ ] **Step 3: Commit** @@ -651,7 +651,7 @@ class RetryHandler: - [ ] **Step 3: 测试 retry 模块** -Run: `cd D:/cobol-java/v3-gstack-code-gen && python -c "from hina.retry import RetryHandler; print('OK')"` +Run: `cd && python -c "from hina.retry import RetryHandler; print('OK')"` Expected: `OK` - [ ] **Step 4: Commit** @@ -851,7 +851,7 @@ def test_detect_keyword_no_match(): - [ ] **Step 3: 运行测试** -Run: `cd D:/cobol-java/v3-gstack-code-gen && python -c "from hina.classifier import detect_keyword; print('OK')"` +Run: `cd && python -c "from hina.classifier import detect_keyword; print('OK')"` Expected: `OK` - [ ] **Step 4: Commit** @@ -992,7 +992,7 @@ def supplement_only(base_tests: list[dict], hina_gaps: list[str]) -> list[dict]: - [ ] **Step 2: 测试策略模板** -Run: `cd D:/cobol-java/v3-gstack-code-gen && python -c "from hina.strategy import get_strategy; s = get_strategy('マッチング'); print(len(s['required']))"` +Run: `cd && python -c "from hina.strategy import get_strategy; s = get_strategy('マッチング'); print(len(s['required']))"` Expected: `9` - [ ] **Step 3: Commit** @@ -1088,7 +1088,7 @@ def _compute_score(coverage: dict, hina_result: dict) -> float: - [ ] **Step 2: 测试质量门禁** -Run: `cd D:/cobol-java/v3-gstack-code-gen && python -c "from hina.gate import check; r = check([], {}, {'branch_rate':0.8,'paragraph_rate':0.9,'uncovered_decision_ids':[1]}); print(r['passed'], r['score'])"` +Run: `cd && python -c "from hina.gate import check; r = check([], {}, {'branch_rate':0.8,'paragraph_rate':0.9,'uncovered_decision_ids':[1]}); print(r['passed'], r['score'])"` Expected: `False 0.87` - [ ] **Step 3: Commit** @@ -1437,7 +1437,7 @@ table{{border-collapse:collapse}} td,th{{padding:6px 12px}} - [ ] **Step 2: 运行测试确认 HTML 生成正确** -Run: `cd D:/cobol-java/v3-gstack-code-gen && python -m pytest tests/report/test_generator.py -v` +Run: `cd && python -m pytest tests/report/test_generator.py -v` Expected: `3 passed` - [ ] **Step 3: Commit** @@ -1583,7 +1583,7 @@ def test_check_coverage(): - [ ] **Step 2: 运行集成测试** -Run: `cd D:/cobol-java/v3-gstack-code-gen && python -m pytest tests/test_quality/test_integration.py -v` +Run: `cd && python -m pytest tests/test_quality/test_integration.py -v` Expected: `6 passed` - [ ] **Step 3: 最终 Commit** diff --git a/docs/module-interfaces.md b/docs/module-interfaces.md index f61840f..d1b7e90 100644 --- a/docs/module-interfaces.md +++ b/docs/module-interfaces.md @@ -217,7 +217,7 @@ class VerificationRun: │ project_name: str = "" │ │ copybook_paths: list = ["./copybooks"] │ │ dialect: str = "ibm" # cobc -std 参数 │ -│ llm_model: str = "gpt-4o-mini" # LLM 模型 │ +│ llm_model: str = "deepseek-v4-flash" # LLM 模型 │ │ llm_timeout: int = 15 │ │ llm_cache_dir: str = ".cache/llm" │ │ coverage_default: str = "boundary" │ @@ -545,7 +545,7 @@ class VerificationRun: ``` ┌─────────────────────────────────────────────────────────────────────────┐ -│ LLMClient(model="gpt-4o-mini", timeout=15, cache_dir=".cache/llm") │ +│ LLMClient(timeout=15, cache_dir=".cache/llm") # 模型经 LLM_MODEL 环境变量解析 │ │ call(messages: list[dict], retries=1) → str │ │ │ │ 通信契约: POST {base}/chat/completions │ diff --git a/docs/plans/db-pipeline-agent2data-integration.md b/docs/plans/db-pipeline-agent2data-integration.md index c4caf9d..8c522b5 100644 --- a/docs/plans/db-pipeline-agent2data-integration.md +++ b/docs/plans/db-pipeline-agent2data-integration.md @@ -122,7 +122,7 @@ except ImportError: ### 验证方法 ```bash -cd C:\Users\marye\Desktop\2026技术大赛\cobol-java-v3 +cd set COB_LIBRARY_PATH=\bin;\gixsql\lib set GIXSQL_DB_PATH=C:\Temp\gix python -m cobol_testgen --gcov \src\ZAN06UPD.cbl runtime\ diff --git a/docs/test-plan.md b/docs/test-plan.md index b48cdeb..d5f270d 100644 --- a/docs/test-plan.md +++ b/docs/test-plan.md @@ -358,7 +358,7 @@ L6: E2E UI 测试 ─ Playwright 浏览器测试 ← 新增层级 | # | 测试名 | 内容 | 期待 | |:-:|:-------|:-----|:------| -| CF-01 | Config: 默认值 | 无参数 | runner_mode=native, llm_model=gpt-4o-mini | +| CF-01 | Config: 默认值 | 无参数 | runner_mode=native, llm_model=deepseek-v4-flash | | CF-02 | Config: from_toml | 有效 TOML 文件 | 正确解析 | | CF-03 | Config: from_toml 文件不存在 | 不存在路径 | 默认值 | | CF-04 | Config: from_toml 非法 TOML | 格式错误 | 妥善处理/不崩溃 | @@ -881,4 +881,4 @@ L1 类型测试 (Ph7-8): 80 (matching/division/CSV/CALL/SORT/parametrized) 3. **Docker 不可用**: 当前环境无 Docker Desktop, Spark runner 不可测试 4. **外部依赖**: 実 COBOL 测试需要 WSL + GnuCOBOL + Java (GnuCOBOL 3.1.2.0, OpenJDK 17) 5. **LLM API 成本**: Agent 测试依赖 LLM 调用, 缓存命中可降低成本 -6. **沙盒权限**: settings.json 需包含 `D:/cobol-java/**` 读写权限以便子 Agent 并行开发 +6. **沙盒权限**: settings.json 需包含 `/**` 读写权限以便子 Agent 并行开发 diff --git a/docs/v3-理解文档.md b/docs/v3-理解文档.md index 5830680..9cf8646 100644 --- a/docs/v3-理解文档.md +++ b/docs/v3-理解文档.md @@ -400,7 +400,7 @@ FieldTree + Field (data/field_tree.py) ← Agent1Parser LLM 解析 COPYBOOK 的 ``` `Agent2Data.design()` 的 LLM 调用结果被 `complete_tests` 完全替换,该 LLM 调用除了产生 `spark_config` 外没有实际用途。LLM 费用被浪费。 -8. **硬编码 LLM 成本** — `orchestrator.py:30,111`:`vr.llm_cost += 0.002`(固定 $0.002/次),与实际模型(Config 中 `gpt-4o-mini`)的 token 计费无关。 +8. **硬编码 LLM 成本** — `orchestrator.py:30,111`:`vr.llm_cost += 0.002`(固定 $0.002/次),与实际模型(Config 中配置的 LLM)的 token 计费无关。 9. **`cobol_testgen/generate_data()` 中的条件值强制相等** — `cobol_testgen/__init__.py:1069-1077`: ```python diff --git a/test-data/test_deep_validation.py b/test-data/test_deep_validation.py index 70e41f5..b00f10e 100644 --- a/test-data/test_deep_validation.py +++ b/test-data/test_deep_validation.py @@ -10,7 +10,7 @@ 4. 验证输出文件存在且内容正确 前提: FastAPI + Worker 已经在运行 - Windows: start uvicorn web.api:app --port 8000 & python web/worker.py + Windows: start uvicorn web.api:app --port $TEST_PORT & python web/worker.py WSL: python3 web/worker.py ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ """ diff --git a/tests/e2e/test_pipeline.py b/tests/e2e/test_pipeline.py index 9f9464d..f3b5af4 100644 --- a/tests/e2e/test_pipeline.py +++ b/tests/e2e/test_pipeline.py @@ -1,7 +1,7 @@ """E2E Tests for COBOL->Java Verification Platform Run: cd && python -m pytest tests/e2e/ -v -Requires: web server on http://127.0.0.1:8000, WSL available, +Requires: web server on http://127.0.0.1:$TEST_PORT, WSL available, DEEPSEEK_API_KEY set in the environment. """ import json, os, sys, time, uuid, shutil @@ -11,7 +11,8 @@ from pathlib import Path import pytest PROJECT = Path(__file__).parent.parent.parent.resolve() -BASE_URL = "http://127.0.0.1:8000" +TEST_PORT = int(os.environ.get("TEST_PORT", "8000")) +BASE_URL = f"http://127.0.0.1:{TEST_PORT}" TASKS_DIR = PROJECT / "tasks" UPLOADS_DIR = PROJECT / "uploads" FIXTURES = PROJECT / "tests" / "fixtures" diff --git a/tests/test_biz_e2e.py b/tests/test_biz_e2e.py index b673c80..23d55bc 100644 --- a/tests/test_biz_e2e.py +++ b/tests/test_biz_e2e.py @@ -7,11 +7,17 @@ import pytest, os, time, json, shutil from pathlib import Path from playwright.sync_api import Page, expect, sync_playwright -BASE_URL = "http://127.0.0.1:8000" +TEST_PORT = int(os.environ.get("TEST_PORT", "8000")) +BASE_URL = f"http://127.0.0.1:{TEST_PORT}" COBOL_GIT = Path(os.environ.get("COBOL_GIT_ROOT", Path(__file__).resolve().parent.parent.parent / "jcl-cobol-git")) FIXTURES = Path(__file__).parent / "fixtures" TESTS_DIR = Path(__file__).parent + +def _js(js: str) -> str: + """JS 模板中的 __BASE_URL__ 占位符替换(避免 f-string 花括号转义)""" + return js.replace("__BASE_URL__", BASE_URL) + # Check if worker can process tasks def _worker_available(): return os.name == "nt" # Always try on Windows (files go to tasks/) @@ -72,7 +78,7 @@ def test_full_upload_flow(page: Page, test_files: dict): def test_submit_with_js_fetch(page: Page, test_files: dict): """TC-BIZ-01: Submit via Blob → returns 202 + task_id. (Worker not needed)""" page.goto(BASE_URL) - result = page.evaluate(""" + result = page.evaluate(_js(""" (async () => { const fd = new FormData(); fd.append("runner", "native"); @@ -80,10 +86,10 @@ def test_submit_with_js_fetch(page: Page, test_files: dict): fd.append("cobol_src", new Blob(["STOP RUN."], {type:"text/plain"}), "test.cbl"); fd.append("java_src", new Blob(["test"], {type:"text/plain"}), "test.java"); fd.append("mapping", new Blob(["program: TEST"], {type:"text/plain"}), "test.yaml"); - const r = await fetch("http://127.0.0.1:8000/verify", {method:"POST", body:fd}); + const r = await fetch("__BASE_URL__/verify", {method:"POST", body:fd}); return await r.json(); })() - """) + """)) assert result.get("task_id"), f"No task_id: {result}" assert result.get("status") == "queued" @@ -91,7 +97,7 @@ def test_submit_with_js_fetch(page: Page, test_files: dict): # Quick status check (don't wait for Worker) status = page.evaluate(f""" (async () => {{ - const r = await fetch("http://127.0.0.1:8000/status/{result["task_id"]}"); + const r = await fetch("{BASE_URL}/status/{result["task_id"]}"); return await r.json(); }})() """) @@ -102,16 +108,16 @@ def test_result_page_has_fields_table(page: Page): """TC-BIZ-03: Result page renders field comparison table.""" page.goto(BASE_URL) # Submit a task first - result = page.evaluate(""" + result = page.evaluate(_js(""" (async () => { const fd = new FormData(); fd.append("runner", "native"); ["copybook","cobol_src","mapping"].forEach(k => fd.append(k, new Blob(["test"], {type:"text/plain"}), k+".txt")); - const r = await fetch("http://127.0.0.1:8000/verify", {method:"POST", body:fd}); + const r = await fetch("__BASE_URL__/verify", {method:"POST", body:fd}); return await r.json(); })() - """) + """)) task_id = result.get("task_id","") if task_id: page.goto(f"{BASE_URL}/result/{task_id}") @@ -122,7 +128,7 @@ def test_result_page_has_fields_table(page: Page): def test_debug_section_api(page: Page): """TC-BIZ-04: /fields/{id} returns debug data.""" page.goto(BASE_URL) - result = page.evaluate(""" + result = page.evaluate(_js(""" (async () => { const fd = new FormData(); fd.append("runner", "native"); @@ -130,17 +136,17 @@ def test_debug_section_api(page: Page): fd.append("cobol_src", new Blob(["STOP RUN."], {type:"text/plain"}), "test.cbl"); fd.append("java_src", new Blob(["test"], {type:"text/plain"}), "test.java"); fd.append("mapping", new Blob(["program: TEST"], {type:"text/plain"}), "test.yaml"); - const r = await fetch("http://127.0.0.1:8000/verify", {method:"POST", body:fd}); + const r = await fetch("__BASE_URL__/verify", {method:"POST", body:fd}); return await r.json(); })() - """) + """)) task_id = result.get("task_id", "") assert task_id, "No task_id returned" fields_result = page.evaluate(f""" (async () => {{ - const r = await fetch("http://127.0.0.1:8000/fields/{task_id}"); + const r = await fetch("{BASE_URL}/fields/{task_id}"); return await r.json(); }})() """) @@ -152,7 +158,7 @@ def test_debug_section_api(page: Page): def test_file_size_limit(page: Page): """TC-BIZ-05: Upload >10MB file returns 413.""" page.goto(BASE_URL) - result = page.evaluate(""" + result = page.evaluate(_js(""" (async () => { const fd = new FormData(); const big = new Blob([new Uint8Array(11*1024*1024)], {type:"text/plain"}); @@ -161,10 +167,10 @@ def test_file_size_limit(page: Page): fd.append("java_src", new Blob(["test"]), "test.java"); fd.append("mapping", new Blob(["test"]), "test.yaml"); fd.append("runner", "native"); - const r = await fetch("http://127.0.0.1:8000/verify", {method:"POST", body:fd}); + const r = await fetch("__BASE_URL__/verify", {method:"POST", body:fd}); return r.status; })() - """) + """)) assert result == 413, f"Expected 413, got {result}" diff --git a/tests/test_web_e2e.py b/tests/test_web_e2e.py index d8b2349..df7e51d 100644 --- a/tests/test_web_e2e.py +++ b/tests/test_web_e2e.py @@ -1,11 +1,15 @@ """ Playwright E2E tests for COBOL-Java Migration Platform Web UI. -Server must be running: python -m uvicorn web.api:app --host 127.0.0.1 --port 8000 +Server must be running: python -m uvicorn web.api:app --host 127.0.0.1 --port $TEST_PORT +(端口经 TEST_PORT 环境变量配置, 默认 8000) """ +import os + import pytest from playwright.sync_api import Page, expect, sync_playwright -BASE_URL = "http://127.0.0.1:8000" +TEST_PORT = int(os.environ.get("TEST_PORT", "8000")) +BASE_URL = f"http://127.0.0.1:{TEST_PORT}" @pytest.fixture(scope="module") @@ -53,13 +57,14 @@ def test_form_elements_present(page: Page): def test_submit_empty_form(page: Page): """验证空表单提交返回 422 (缺少必填字段)""" page.goto(BASE_URL) - result = page.evaluate(""" + js = """ (async () => { const fd = new FormData(); - const r = await fetch('http://127.0.0.1:8000/verify', { method: 'POST', body: fd }); + const r = await fetch('__BASE_URL__/verify', { method: 'POST', body: fd }); return r.status; })() - """) + """.replace("__BASE_URL__", BASE_URL) + result = page.evaluate(js) assert result == 422 @@ -73,7 +78,7 @@ def test_submit_with_files(page: Page): page.set_input_files("input[name=mapping]", "tests/fixtures/simple.yaml") # 用 evaluate 直接调 API 绕过 webkitdirectory 限制 - result = page.evaluate(""" + js = """ (async () => { const fd = new FormData(); fd.append('copybook', new Blob(['test'], {type:'text/plain'}), 'test.cpy'); @@ -81,10 +86,11 @@ def test_submit_with_files(page: Page): fd.append('java_src', new Blob(['test'], {type:'text/plain'}), 'test.java'); fd.append('mapping', new Blob(['test'], {type:'text/plain'}), 'test.yaml'); fd.append('runner', 'native'); - const r = await fetch('http://127.0.0.1:8000/verify', { method: 'POST', body: fd }); + const r = await fetch('__BASE_URL__/verify', { method: 'POST', body: fd }); return { status: r.status, body: await r.json() }; })() - """) + """.replace("__BASE_URL__", BASE_URL) + result = page.evaluate(js) assert result["status"] == 202 assert "task_id" in result["body"]