""" cobol-java-v3 平台用户故事测试 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 测试对象: cobol-java-v3 平台自身(不是COBOL程序) 测试范围: 正常 / 异常 / 边界 / 缺陷 4类用户故事 执行: python -X utf8 test-data/test_platform_user_stories.py """ import sys, os, json, time, tempfile, shutil, traceback from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) from data.diff_result import VerificationRun, FieldResult from data.test_case import TestCase, TestSuite, SparkConfig from data.field_tree import FieldTree PASS = 0 FAIL = 0 ERRORS = [] def section(title): print(f"\n{'─'*70}") print(f" {title}") print(f"{'─'*70}") def test(name, category): def decorator(fn): global PASS, FAIL try: fn() PASS += 1 print(f" [{category}] {name} → ✅ PASS") except Exception as e: FAIL += 1 tb = traceback.format_exc()[-300:] ERRORS.append(f"{name}: {e}") print(f" [{category}] {name} → ❌ FAIL: {e}") print(f" {tb.split(chr(10))[-3]}") return fn return decorator # ════════════════════════════════════════════ # 正常系 — Normal # ════════════════════════════════════════════ section("N: 正常系ユーザーストーリー") @test("VerificationRun 作成と全フィールド設定", "NORMAL") def _(): vr = VerificationRun(program="TESTPGM", runner="native") assert vr.program == "TESTPGM" assert vr.runner == "native" assert vr.timestamp != "" vr.branch_rate = 0.95 vr.paragraph_rate = 1.0 vr.hina_type = "マッチング" vr.quality_score = 0.85 vr.heal_retry = 1 assert vr.branch_rate == 0.95 assert vr.hina_type == "マッチング" @test("TestCase 作成とフィールド設定", "NORMAL") def _(): tc = TestCase(id="TC-001", fields={"BR-AMT": 1500, "BR-STATUS": "A"}) assert tc.id == "TC-001" assert tc.fields["BR-AMT"] == 1500 assert tc.fields["BR-STATUS"] == "A" assert tc.coverage_targets == [] @test("FieldResult 作成とステータス", "NORMAL") def _(): fr = FieldResult(field_name="BR-AMT", status="PASS", cobol_value="1500", java_value="1500.00") assert fr.field_name == "BR-AMT" assert fr.status == "PASS" fr.status = "MISMATCH" assert fr.status == "MISMATCH" @test("Config デフォルト値", "NORMAL") def _(): from config import Config c = Config() assert c.quality_gate_mode == "warn" assert c.runner_mode == "native" assert c.dialect == "ibm" assert c.gcov_enabled == False assert c.max_quality_retries == 4 @test("Config from_toml 正常", "NORMAL") def _(): from config import Config c = Config.from_toml(path=Path(__file__).parent.parent / "aurak.toml") assert c.project_name != "" or c.runner_mode != "" @test("VerificationRun total_fields 計算", "NORMAL") def _(): vr = VerificationRun(fields_matched=10, fields_mismatched=2) assert vr.total_fields == 12 @test("HINA classifier L1: DB操作", "NORMAL") def _(): from hina.classifier import detect_keyword r = detect_keyword("EXEC SQL SELECT * FROM TABLE END-EXEC") assert any("DB操作" in x[0] for x in r) assert any(x[1] >= 0.95 for x in r) @test("HINA classifier L1: CALL", "NORMAL") def _(): from hina.classifier import detect_keyword r = detect_keyword("CALL 'SUBPGM' USING A.\nLINKAGE SECTION.") assert any("子程序调用" in x[0] for x in r) @test("HINA strategy マッチングテンプレート", "NORMAL") def _(): from hina.strategy import get_strategy s = get_strategy("マッチング") assert len(s["required"]) == 9 @test("Quality gate: 合格", "NORMAL") def _(): from hina.gate import check r = check([{"a": 1}], {}, {"branch_rate": 0.95, "paragraph_rate": 1.0, "uncovered_decision_ids": []}) assert r["passed"] == True @test("RetryHandler: 即PASS", "NORMAL") def _(): from hina.retry import RetryHandler h = RetryHandler() vr = h.run(lambda: VerificationRun(status="PASS")) assert vr.status == "PASS" assert vr.heal_retry == 0 @test("ReportGenerator: HTML生成", "NORMAL") def _(): from report.generator import ReportGenerator vr = VerificationRun(program="TEST", runner="native") rd = Path(tempfile.mkdtemp()) try: g = ReportGenerator() p = g.generate_html(vr, rd / "test.html") assert p.exists() html = p.read_text(encoding="utf-8") assert "TEST" in html finally: shutil.rmtree(rd) @test("ReportGenerator: HTML カバレッジ表示", "NORMAL") def _(): from report.generator import ReportGenerator vr = VerificationRun(program="T1", paragraph_rate=0.9, branch_rate=0.85) rd = Path(tempfile.mkdtemp()) try: p = ReportGenerator().generate_html(vr, rd / "t.html") html = p.read_text(encoding="utf-8") assert "段落覆盖率" in html assert "分支覆盖率" in html finally: shutil.rmtree(rd) @test("ReportGenerator: HTML HINA表示", "NORMAL") def _(): from report.generator import ReportGenerator vr = VerificationRun(program="T2", hina_type="マッチング", hina_confidence=0.95) rd = Path(tempfile.mkdtemp()) try: p = ReportGenerator().generate_html(vr, rd / "t.html") assert "HINA" in p.read_text(encoding="utf-8") finally: shutil.rmtree(rd) @test("ReportGenerator: JSON 新フィールド", "NORMAL") def _(): from report.generator import ReportGenerator vr = VerificationRun(program="T3", branch_rate=0.9, quality_score=0.85) rd = Path(tempfile.mkdtemp()) try: p = ReportGenerator().generate_json(vr, rd / "t.json") d = json.loads(p.read_text()) assert d["branch_rate"] == 0.9 assert d["quality_score"] == 0.85 finally: shutil.rmtree(rd) @test("cobol_testgen extract_structure: IF", "NORMAL") def _(): from cobol_testgen import extract_structure s = extract_structure("PROCEDURE DIVISION.\nIF A>B MOVE 1 TO C ELSE MOVE 2 TO C.\nGOBACK.") assert "paragraphs" in s assert "decision_points" in s # ════════════════════════════════════════════ # 異常系 — Abnormal # ════════════════════════════════════════════ section("A: 異常系ユーザーストーリー") @test("空COBOLソース→extract_structure", "ABNORMAL") def _(): from cobol_testgen import extract_structure s = extract_structure("") assert s is not None assert s.get("total_branches", 0) == 0 @test("PROCEDURE DIVISIONなし→extract_structure", "ABNORMAL") def _(): from cobol_testgen import extract_structure s = extract_structure("IDENTIFICATION DIVISION.\nPROGRAM-ID. X.\nDATA DIVISION.\nWORKING-STORAGE SECTION.\n01 A PIC X(10).") assert s is not None assert "paragraphs" in s @test("Quality gate: 空データ", "ABNORMAL") def _(): from hina.gate import check r = check([], {}, {"branch_rate": 0.0, "paragraph_rate": 0.0, "uncovered_decision_ids": []}) assert r["passed"] == False assert "no_data" in r.get("issues", {}) @test("Quality gate: 分岐不足", "ABNORMAL") def _(): from hina.gate import check r = check([{"x": 1}], {}, {"branch_rate": 0.5, "paragraph_rate": 1.0, "uncovered_decision_ids": [1, 2]}) assert r["passed"] == False assert "decision_gaps" in r.get("issues", {}) @test("RetryHandler: 全FAIL→FATAL", "ABNORMAL") def _(): from hina.retry import RetryHandler from data.diff_result import VerificationRun h = RetryHandler(max_heal=1, max_simple=1) vr = h.run(lambda: VerificationRun(status="ERROR", exit_code=3)) assert vr.status == "FATAL" assert vr.exit_code == 4 @test("Config: 必須fieldなし", "ABNORMAL") def _(): from config import Config c = Config.from_toml(path="nonexistent.toml") assert c.runner_mode == "native" assert c.quality_gate_mode == "warn" @test("extract_structure: 不正COBOL構文", "ABNORMAL") def _(): from cobol_testgen import extract_structure s = extract_structure("THIS IS NOT VALID COBOL @@@ @@@") assert s is not None @test("generate_data: 分岐なしプログラム", "ABNORMAL") def _(): from cobol_testgen import generate_data s = "PROCEDURE DIVISION.\nGOBACK." r = generate_data(s) assert isinstance(r, list) assert len(r) == 0 @test("incremental_supplement: 存在しないID", "ABNORMAL") def _(): from cobol_testgen import incremental_supplement r = incremental_supplement(None, [-1]) assert isinstance(r, list) @test("VerificationRun: 空フィールド", "ABNORMAL") def _(): vr = VerificationRun() assert vr.total_fields == 0 assert vr.status == "PASS" @test("HINA classifier: キーワードなし", "ABNORMAL") def _(): from hina.classifier import compute_confidence r = compute_confidence("PROCEDURE DIVISION.\nDISPLAY 'HELLO'.") assert r["category"] == "unknown" assert r["confidence"] == 0.0 @test("HINA strategy: 未知のタイプ", "ABNORMAL") def _(): from hina.strategy import get_strategy s = get_strategy("UNKNOWN_TYPE_XXX") assert s["required"] == [] @test("gcov_collector: ファイルなし", "ABNORMAL") def _(): from hina.gcov_collector import collect_gcov r = collect_gcov(Path("nonexistent.cbl"), Path("/dev/null")) assert r["available"] == False assert "reason" in r # ════════════════════════════════════════════ # 境界系 — Boundary # ════════════════════════════════════════════ section("B: 境界系ユーザーストーリー") @test("超巨大プログラム: 1000個IF", "BOUNDARY") def _(): from cobol_testgen import extract_structure lines = ["PROCEDURE DIVISION."] for i in range(1000): lines.append(f"IF A > {i} THEN MOVE {i} TO X ELSE MOVE {i} TO Y END-IF.") lines.append("GOBACK.") src = "\n".join(lines) t0 = time.time() s = extract_structure(src) elapsed = time.time() - t0 print(f" → 1000 IF: {elapsed:.1f}s, 安定") assert s is not None assert elapsed < 10 # 10秒以内に完了 @test("超長フィールド名: 1000文字", "BOUNDARY") def _(): from cobol_testgen import extract_structure long = "A" * 1000 src = f"""IDENTIFICATION DIVISION. PROGRAM-ID. X. DATA DIVISION. WORKING-STORAGE SECTION. 01 {long} PIC X(10). PROCEDURE DIVISION. GOBACK.""" s = extract_structure(src) assert s is not None @test("TestSuite 0件", "BOUNDARY") def _(): ts = TestSuite() assert ts.has_spark == False assert len(ts.test_cases) == 0 @test("SparkConfig 大量レコード", "BOUNDARY") def _(): from data.test_case import SparkConfig sc = SparkConfig(num_records=100000) assert sc.num_records == 100000 @test("VerificationRun 全フィールド最大値", "BOUNDARY") def _(): vr = VerificationRun(fields_matched=9999, fields_mismatched=9999) assert vr.total_fields == 19998 vr.branch_rate = 1.0 vr.quality_score = 1.0 assert vr.branch_rate == 1.0 @test("100並列TestCases作成", "BOUNDARY") def _(): cases = [TestCase(id=f"TC-{i:04d}", fields={"X": i}) for i in range(100)] assert len(cases) == 100 assert cases[0].id == "TC-0000" assert cases[99].id == "TC-0099" # ════════════════════════════════════════════ # 欠陥系 — Defect (過去修正したバグの回帰) # ════════════════════════════════════════════ section("D: 欠陥系ユーザーストーリー (回帰テスト)") @test("DEFECT-001:complete_tests→DataWriter", "DEFECT") def _(): """P1修复: complete_tests 必须传递给 DataWriter""" from data.test_case import TestCase tc = TestCase(id="CTG-0001", fields={"TX-AMT": 100}) assert tc.id == "CTG-0001" assert tc.fields["TX-AMT"] == 100 # DataWriter 接受 TestCase[] from data.test_case import TestSuite ts = TestSuite(test_cases=[tc]) assert len(ts.test_cases) == 1 @test("DEFECT-002:质量门禁循环中同步更新", "DEFECT") def _(): """P2修复: 增量补充后complete_tests需要更新""" from data.test_case import TestCase base = [TestCase(id=f"B{i}", fields={"v": i}) for i in range(3)] delta = [TestCase(id=f"D{i}", fields={"v": i+10}) for i in range(2)] combined = base + delta assert len(combined) == 5 assert combined[3].id == "D0" @test("DEFECT-003:分层重试 heal恢复", "DEFECT") def _(): """分层重试: heal修复后应成功""" from hina.retry import RetryHandler from data.diff_result import VerificationRun called = [0] def fn(): called[0] += 1 if called[0] <= 2: return VerificationRun(status="BLOCKED", exit_code=2, debug={"cobol_build": {"log": "not found"}}) return VerificationRun(status="PASS") h = RetryHandler(max_heal=3, max_simple=1) vr = h.run(fn) assert vr.status == "PASS" assert vr.heal_retry > 0 @test("DEFECT-004:COPYBOOKファイル名不一致", "DEFECT") def _(): """修复: COPY BBBBBFC (5B+FC) の解決""" from cobol_testgen.read import resolve_copybooks src = " COPY BBBBBFC REPLACING ==(A)== BY ==R01==." # copybookファイルがなくてもクラッシュしない result = resolve_copybooks(src, "/nonexistent") assert result is not None @test("DEFECT-005:Lark VALUE句解析", "DEFECT") def _(): """修复: VALUE '文字' のLark解析""" from cobol_testgen import extract_structure src = "IDENTIFICATION DIVISION.\nPROGRAM-ID. X.\nDATA DIVISION.\nWORKING-STORAGE SECTION.\n01 A PIC X(10) VALUE 'TEST'.\nPROCEDURE DIVISION.\nGOBACK." s = extract_structure(src) assert s is not None @test("DEFECT-006:OPEN方向OUTPUT誤認識", "DEFECT") def _(): """修复: OPEN方向キーワードがファイル名に含まれない""" from cobol_testgen.read import scan_open_statements src = "OPEN INPUT TRANS-FILE.\nOPEN OUTPUT OUTPUT-FILE." dirs = scan_open_statements(src) # 'OUTPUT'は方向キーワードとして除外され、ファイル名にはならない assert 'OUTPUT' not in dirs # キーワードはフィルタされる assert 'OUTPUT-FILE' in dirs assert dirs['OUTPUT-FILE'] == 'OUTPUT' @test("DEFECT-007:Enum値一致判定", "DEFECT") def _(): """HINA分類のmethodキー存在確認""" from hina.classifier import compute_confidence r = compute_confidence("EXEC SQL SELECT\nEND-EXEC.") assert "method" in r assert r["method"] == "keyword" r2 = compute_confidence("DISPLAY 'X'.") assert r2["method"] == "none" @test("DEFECT-008:machine_json全フィールド", "DEFECT") def _(): """P5修复: machine_jsonに全フィールド含む""" from report.generator import ReportGenerator vr = VerificationRun(program="TEST", branch_rate=0.9, paragraph_rate=0.8, quality_score=0.85, hina_type="M", hina_confidence=0.95) rd = Path(tempfile.mkdtemp()) try: p = ReportGenerator().generate_machine_json(vr, rd / "m.json") d = json.loads(p.read_text()) assert "branch_rate" in d assert "paragraph_rate" in d assert "quality_score" in d assert "hina_type" in d finally: shutil.rmtree(rd) # ════════════════════════════════════════════ # 集計 # ════════════════════════════════════════════ section("テスト結果集計") total = PASS + FAIL print(f"\n 総テスト数: {total}") print(f" 合格: {PASS}") print(f" 不合格: {FAIL}") print(f" 合格率: {PASS/max(total,1)*100:.1f}%") print(f"\n RESULT: {'ALL PASSED' if FAIL==0 else 'SOME FAILED'}") if ERRORS: print(f"\n 失敗詳細:") for e in ERRORS: print(f" ❌ {e}") sys.exit(0 if FAIL == 0 else 1)