feat: Phase 2 complete — 13 Phases of COBOL type classification and test benchmark
P0.6: gcov infrastructure P1: extract_structure output expansion (11 new feature fields) P2: Confusion group rule engine (8 pairs + contradiction + backtrack) P3: 4-factor confidence calculation + quality gate update P4: 33+2 COBOL program type test samples (22 files, 7 categories) P5: parametrized/ test data generation engine P6: japanese_data.py lookup tables P7-10: Type-specific test suites (~159 parametrized tests) P11: Full classification pipeline (classify_program) + orchestrator integration P12: Documentation (module-interfaces, test-plan v3.0, coverage-matrix) Architecture decisions: - classification_pipeline/ merged to hina/pipeline/ - parametrized/ as independent module - japanese_data.py as root-level file - hina/__all__ only exports classify_program() Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
"""WR-01~07: Worker 进程测试"""
|
||||
|
||||
import sys, os, json, tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch, MagicMock
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
|
||||
from web.worker import main as worker_main
|
||||
|
||||
|
||||
def _write_task(tasks_dir, task_id, status="queued", runner="native"):
|
||||
data = {
|
||||
"id": task_id, "status": status, "runner": runner,
|
||||
"copybook": f"/tmp/{task_id}/copybook.cpy",
|
||||
"cobol_src": f"/tmp/{task_id}/program.cbl",
|
||||
"java_src": f"/tmp/{task_id}/java",
|
||||
"mapping": f"/tmp/{task_id}/mapping.yaml",
|
||||
}
|
||||
(tasks_dir / f"{task_id}.json").write_text(json.dumps(data), encoding="utf-8")
|
||||
|
||||
|
||||
# ── WR-01: No tasks ──
|
||||
|
||||
def test_worker_no_tasks():
|
||||
"""WR-01: 空 tasks/ → 无操作"""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
with patch("web.worker.TASKS_DIR", Path(tmp)), \
|
||||
patch("web.worker.time") as mock_time:
|
||||
mock_time.sleep.side_effect = KeyboardInterrupt
|
||||
try:
|
||||
worker_main()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
assert True
|
||||
|
||||
|
||||
# ── WR-02: Normal task ──
|
||||
|
||||
def test_worker_normal_task():
|
||||
"""WR-02: queued 任务 → 处理"""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tasks_dir = Path(tmp)
|
||||
_write_task(tasks_dir, "t001")
|
||||
mock_vr = MagicMock(
|
||||
program="T", status="PASS", fields_matched=5, fields_mismatched=0,
|
||||
duration_s=0.5, runner="native", field_results=[], debug={},
|
||||
)
|
||||
with patch("web.worker.TASKS_DIR", tasks_dir), \
|
||||
patch("config.Config") as mock_cfg, \
|
||||
patch("orchestrator.run_pipeline", return_value=mock_vr), \
|
||||
patch("web.worker.time") as mock_time:
|
||||
mock_time.sleep.side_effect = KeyboardInterrupt
|
||||
mock_cfg.return_value = MagicMock()
|
||||
try:
|
||||
worker_main()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
assert (tasks_dir / "t001.json").exists()
|
||||
|
||||
|
||||
# ── WR-03: null JSON / empty file ──
|
||||
|
||||
def test_worker_null_json():
|
||||
"""WR-03: null JSON → error 状态写入"""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tasks_dir = Path(tmp)
|
||||
(tasks_dir / "n.json").write_text("null")
|
||||
with patch("web.worker.TASKS_DIR", tasks_dir), \
|
||||
patch("web.worker.time") as mock_time:
|
||||
mock_time.sleep.side_effect = KeyboardInterrupt
|
||||
try:
|
||||
worker_main()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
data = json.loads((tasks_dir / "n.json").read_text(encoding="utf-8"))
|
||||
assert data["status"] == "error"
|
||||
|
||||
|
||||
def test_worker_empty_json():
|
||||
"""WR-03b: 空文件 → error 状态写入"""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tasks_dir = Path(tmp)
|
||||
(tasks_dir / "e.json").write_text("")
|
||||
with patch("web.worker.TASKS_DIR", tasks_dir), \
|
||||
patch("web.worker.time") as mock_time:
|
||||
mock_time.sleep.side_effect = KeyboardInterrupt
|
||||
try:
|
||||
worker_main()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
data = json.loads((tasks_dir / "e.json").read_text(encoding="utf-8"))
|
||||
assert data["status"] == "error"
|
||||
|
||||
|
||||
# ── WR-04: Spark without spark-submit ──
|
||||
|
||||
def test_worker_spark_no_submit():
|
||||
"""WR-04: spark 无 spark-submit → worker 内部处理"""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tasks_dir = Path(tmp)
|
||||
_write_task(tasks_dir, "s001", runner="spark")
|
||||
with patch("web.worker.TASKS_DIR", tasks_dir), \
|
||||
patch("config.Config") as mock_cfg, \
|
||||
patch("orchestrator.run_pipeline") as mock_run, \
|
||||
patch("web.worker.time") as mock_time:
|
||||
mock_time.sleep.side_effect = KeyboardInterrupt
|
||||
mock_cfg.return_value = MagicMock()
|
||||
mock_run.return_value = MagicMock(
|
||||
program="S", status="PASS", fields_matched=3, fields_mismatched=0,
|
||||
duration_s=0.2, runner="spark", field_results=[], debug={},
|
||||
)
|
||||
try:
|
||||
worker_main()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
assert True
|
||||
|
||||
|
||||
# ── WR-05: Multiple tasks ──
|
||||
|
||||
def test_worker_multiple_tasks():
|
||||
"""WR-05: 2个 queued → 依次处理"""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tasks_dir = Path(tmp)
|
||||
_write_task(tasks_dir, "a1")
|
||||
_write_task(tasks_dir, "a2")
|
||||
mock_vr = MagicMock(
|
||||
program="M", status="PASS", fields_matched=4, fields_mismatched=0,
|
||||
duration_s=0.1, runner="native", field_results=[], debug={},
|
||||
)
|
||||
with patch("web.worker.TASKS_DIR", tasks_dir), \
|
||||
patch("config.Config") as mock_cfg, \
|
||||
patch("orchestrator.run_pipeline", return_value=mock_vr), \
|
||||
patch("web.worker.time") as mock_time:
|
||||
mock_time.sleep.side_effect = KeyboardInterrupt
|
||||
mock_cfg.return_value = MagicMock()
|
||||
try:
|
||||
worker_main()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
assert True
|
||||
|
||||
|
||||
# ── WR-07: Task state machine ──
|
||||
|
||||
def test_task_state_machine():
|
||||
"""WR-07: 只处理 queued 任务"""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tasks_dir = Path(tmp)
|
||||
_write_task(tasks_dir, "rt1", status="running")
|
||||
with patch("web.worker.TASKS_DIR", tasks_dir), \
|
||||
patch("web.worker.time") as mock_time:
|
||||
mock_time.sleep.side_effect = KeyboardInterrupt
|
||||
try:
|
||||
worker_main()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
data = json.loads((tasks_dir / "rt1.json").read_text(encoding="utf-8"))
|
||||
assert data["status"] == "running"
|
||||
Reference in New Issue
Block a user