v1: executing-plans 模式生成,54 文件 1320 行 Python
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||||
from comparator.aligner import align_records
|
||||
|
||||
|
||||
def test_align_by_key():
|
||||
cobol = [{"CUST-ID": "C001", "AMT": 100}, {"CUST-ID": "C002", "AMT": 200}]
|
||||
spark = [{"CUST-ID": "C002", "AMT": 200}, {"CUST-ID": "C001", "AMT": 100}]
|
||||
result = align_records(cobol, spark, key_field="CUST-ID")
|
||||
assert len(result) == 2
|
||||
assert all(s == "MATCHED" for _, _, s in result)
|
||||
|
||||
|
||||
def test_missing_in_spark():
|
||||
cobol = [{"CUST-ID": "C001"}, {"CUST-ID": "C002"}]
|
||||
spark = [{"CUST-ID": "C001"}]
|
||||
result = align_records(cobol, spark, key_field="CUST-ID")
|
||||
statuses = [s for _, _, s in result]
|
||||
assert "MISSING_IN_SPARK" in statuses
|
||||
|
||||
|
||||
def test_extra_in_spark():
|
||||
cobol = [{"CUST-ID": "C001"}]
|
||||
spark = [{"CUST-ID": "C001"}, {"CUST-ID": "C002"}]
|
||||
result = align_records(cobol, spark, key_field="CUST-ID")
|
||||
statuses = [s for _, _, s in result]
|
||||
assert "EXTRA_IN_SPARK" in statuses
|
||||
|
||||
|
||||
def test_empty_inputs():
|
||||
assert align_records([], [], "key") == []
|
||||
|
||||
|
||||
def test_duplicate_keys():
|
||||
cobol = [{"ID": "K1", "V": 1}, {"ID": "K1", "V": 2}]
|
||||
java = [{"ID": "K1", "V": 1}, {"ID": "K1", "V": 2}]
|
||||
result = align_records(cobol, java, key_field="ID")
|
||||
assert len(result) == 2
|
||||
|
||||
|
||||
def test_align_none_key():
|
||||
cobol = [{"ID": None, "V": 1}]
|
||||
java = [{"ID": None, "V": 1}]
|
||||
result = align_records(cobol, java, key_field="ID")
|
||||
assert len(result) == 1
|
||||
@@ -0,0 +1,18 @@
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
from comparator.aligner import align_records
|
||||
|
||||
|
||||
def test_align_empty_key_value():
|
||||
cobol = [{"ID": "", "V": 1}]
|
||||
java = [{"ID": "", "V": 1}]
|
||||
result = align_records(cobol, java, key_field="ID")
|
||||
assert len(result) == 1
|
||||
|
||||
|
||||
def test_align_very_large_key_set():
|
||||
cobol = [{"ID": f"K{i:04d}", "V": i} for i in range(100)]
|
||||
java = [{"ID": f"K{i:04d}", "V": i} for i in range(100)]
|
||||
result = align_records(cobol, java, key_field="ID")
|
||||
assert len(result) == 100
|
||||
assert all(s == "MATCHED" for _, _, s in result)
|
||||
@@ -0,0 +1,23 @@
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||||
from comparator.field_compare import compare_field
|
||||
|
||||
|
||||
def test_negative_numbers():
|
||||
r = compare_field("AMT", "-1500", "-1500", "decimal")
|
||||
assert r.status == "PASS"
|
||||
|
||||
|
||||
def test_mixed_precision():
|
||||
r = compare_field("AMT", "1500.00", "1500", "decimal", tolerance=0.01)
|
||||
assert r.status == "PASS"
|
||||
|
||||
|
||||
def test_non_numeric_in_numeric_field():
|
||||
r = compare_field("AMT", "ABC", "1500", "decimal")
|
||||
assert r.status in ("MISMATCH", "NOT_SET")
|
||||
|
||||
|
||||
def test_very_large_number():
|
||||
r = compare_field("AMT", "9999999999", "9999999999", "decimal")
|
||||
assert r.status == "PASS"
|
||||
@@ -0,0 +1,49 @@
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||||
from comparator.field_compare import compare_field, DEFAULT_TOLERANCE
|
||||
|
||||
|
||||
def test_exact_match():
|
||||
r = compare_field("BR-AMT", "1500000", "1500000", "decimal")
|
||||
assert r.status == "PASS"
|
||||
|
||||
|
||||
def test_within_tolerance():
|
||||
r = compare_field("BR-AMT", "1500000", "1499999.99", "decimal", tolerance=DEFAULT_TOLERANCE)
|
||||
assert r.status == "TOLERATED"
|
||||
|
||||
|
||||
def test_beyond_tolerance():
|
||||
r = compare_field("BR-AMT", "1500000", "1000000", "decimal", tolerance=DEFAULT_TOLERANCE)
|
||||
assert r.status == "MISMATCH"
|
||||
|
||||
|
||||
def test_string_trim():
|
||||
r = compare_field("BR-STATUS", "A ", "A", "string")
|
||||
assert r.status == "PASS"
|
||||
|
||||
|
||||
def test_date_normalization():
|
||||
r = compare_field("BR-DATE", "20260522", "2026-05-22", "date")
|
||||
assert r.status == "PASS"
|
||||
|
||||
|
||||
def test_cobol_default():
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
r = compare_field("BR-AMT", "\x00\x00\x00\x00\x00", "0", "decimal")
|
||||
assert r.status in ("PASS", "TOLERATED")
|
||||
|
||||
|
||||
def test_java_null_vs_value():
|
||||
r = compare_field("BR-AMT", "1500000", "None", "decimal")
|
||||
assert r.status in ("MISMATCH", "NOT_SET")
|
||||
|
||||
|
||||
def test_negative_numbers():
|
||||
r = compare_field("AMT", "-1500", "-1500", "decimal")
|
||||
assert r.status == "PASS"
|
||||
|
||||
|
||||
def test_mixed_precision():
|
||||
r = compare_field("AMT", "1500.00", "1500", "decimal", tolerance=DEFAULT_TOLERANCE)
|
||||
assert r.status == "PASS"
|
||||
@@ -0,0 +1,47 @@
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||||
from comparator.normalizer import Normalizer
|
||||
|
||||
|
||||
def test_ebcdic_to_ascii():
|
||||
n = Normalizer()
|
||||
assert n.normalize_encoding(b'\xc1\xc2', "EBCDIC") == "AB"
|
||||
|
||||
|
||||
def test_ascii_passthrough():
|
||||
n = Normalizer()
|
||||
assert n.normalize_encoding(b"hello", "ASCII") == "hello"
|
||||
|
||||
|
||||
def test_comp3_to_decimal():
|
||||
n = Normalizer()
|
||||
assert n.normalize_comp3(b'\x15\x00\x0C') == "1500"
|
||||
|
||||
|
||||
def test_comp3_negative():
|
||||
n = Normalizer()
|
||||
assert n.normalize_comp3(b'\x15\x00\x1D') == "-1500"
|
||||
|
||||
|
||||
def test_ir_record_creation():
|
||||
n = Normalizer()
|
||||
ir = n.to_ir_record(
|
||||
field_name="BR-AMT", raw_hex="15000C",
|
||||
decoded_value="1500", encoding="EBCDIC",
|
||||
field_type="COMP3", length=5, scale=2, signed=True)
|
||||
assert ir.field_name == "BR-AMT"
|
||||
assert ir.cobol.decoded_value == "1500"
|
||||
assert ir.cobol.encoding == "EBCDIC"
|
||||
|
||||
|
||||
def test_date_iso_normalization():
|
||||
n = Normalizer()
|
||||
assert n.normalize_date("20260522") == "2026-05-22"
|
||||
assert n.normalize_date("2026-05-22") == "2026-05-22"
|
||||
|
||||
|
||||
def test_null_ir_record():
|
||||
n = Normalizer()
|
||||
ir = n.to_null_ir("BR-DATE", side="java")
|
||||
assert ir.field_name == "BR-DATE"
|
||||
assert ir.java is None
|
||||
@@ -0,0 +1,24 @@
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||||
from comparator.rounding_detect import detect_rounding, RoundingResult
|
||||
|
||||
|
||||
def test_truncation_detected():
|
||||
r = detect_rounding("1500000", "1499999")
|
||||
assert r.mode in ("TRUNCATE", "HALF_UP")
|
||||
|
||||
|
||||
def test_exact_match():
|
||||
r = detect_rounding("1500000", "1500000")
|
||||
assert r.mode == "EXACT"
|
||||
assert r.confidence == 1.0
|
||||
|
||||
|
||||
def test_low_confidence_small_diff():
|
||||
r = detect_rounding("1500", "1498")
|
||||
assert r.confidence < 1.0
|
||||
|
||||
|
||||
def test_suggestion_generated():
|
||||
r = detect_rounding("1500000", "1499999")
|
||||
assert len(r.suggestion) > 0
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
IDENTIFICATION DIVISION.
|
||||
PROGRAM-ID. SIMPLE.
|
||||
ENVIRONMENT DIVISION.
|
||||
INPUT-OUTPUT SECTION.
|
||||
FILE-CONTROL.
|
||||
SELECT INFILE ASSIGN TO "input.bin"
|
||||
ORGANIZATION IS SEQUENTIAL.
|
||||
DATA DIVISION.
|
||||
FILE SECTION.
|
||||
FD INFILE.
|
||||
01 BILL-RECORD.
|
||||
05 BR-AMT PIC S9(7)V99 COMP-3.
|
||||
05 BR-STATUS PIC X.
|
||||
05 BR-DATE PIC 9(8).
|
||||
WORKING-STORAGE SECTION.
|
||||
01 WS-EOF PIC X VALUE 'N'.
|
||||
PROCEDURE DIVISION.
|
||||
OPEN INPUT INFILE.
|
||||
PERFORM UNTIL WS-EOF = 'Y'
|
||||
READ INFILE INTO BILL-RECORD
|
||||
AT END MOVE 'Y' TO WS-EOF
|
||||
NOT AT END
|
||||
DISPLAY BR-AMT
|
||||
DISPLAY BR-STATUS
|
||||
DISPLAY BR-DATE
|
||||
END-READ
|
||||
END-PERFORM.
|
||||
CLOSE INFILE.
|
||||
STOP RUN.
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
01 BILL-RECORD.
|
||||
05 BR-AMT PIC S9(7)V99 COMP-3.
|
||||
05 BR-STATUS PIC X.
|
||||
05 BR-DATE PIC 9(8).
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
program: "SIMPLE"
|
||||
field_mapping:
|
||||
- cobol_field: "BR-AMT"
|
||||
java_field: "billAmount"
|
||||
type: "decimal"
|
||||
precision: 2
|
||||
- cobol_field: "BR-STATUS"
|
||||
java_field: "statusCode"
|
||||
type: "string"
|
||||
- cobol_field: "BR-DATE"
|
||||
java_field: "billDate"
|
||||
type: "date"
|
||||
format: "YYYYMMDD"
|
||||
@@ -0,0 +1,44 @@
|
||||
import sys, os, json
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||||
from pathlib import Path
|
||||
from report.generator import ReportGenerator
|
||||
from data.diff_result import VerificationRun, FieldResult
|
||||
|
||||
|
||||
def test_json_output(tmp_path):
|
||||
vr = VerificationRun(program="BILL-CALC", status="PASS", exit_code=0,
|
||||
field_results=[FieldResult(field_name="BR-AMT", status="PASS")])
|
||||
gen = ReportGenerator()
|
||||
path = gen.generate_json(vr, tmp_path / "result.json")
|
||||
data = json.loads(path.read_text())
|
||||
assert data["program"] == "BILL-CALC"
|
||||
assert data["status"] == "PASS"
|
||||
|
||||
|
||||
def test_html_output(tmp_path):
|
||||
vr = VerificationRun(program="TEST", status="MISMATCH",
|
||||
field_results=[FieldResult(field_name="F1", status="MISMATCH")])
|
||||
gen = ReportGenerator()
|
||||
path = gen.generate_html(vr, tmp_path / "report.html")
|
||||
assert path.exists()
|
||||
html = path.read_text()
|
||||
assert "MISMATCH" in html
|
||||
assert "F1" in html
|
||||
|
||||
|
||||
def test_machine_json(tmp_path):
|
||||
vr = VerificationRun(program="TEST", status="PASS", exit_code=0)
|
||||
gen = ReportGenerator()
|
||||
path = gen.generate_machine_json(vr, tmp_path / "machine.json")
|
||||
data = json.loads(path.read_text())
|
||||
assert data["exit_code"] == 0
|
||||
|
||||
|
||||
def test_suggestion_in_report(tmp_path):
|
||||
fr = FieldResult(field_name="BR-AMT", status="MISMATCH",
|
||||
suggestion="Check rounding_mode: TRUNCATE vs HALF_UP")
|
||||
vr = VerificationRun(program="TEST", status="MISMATCH", field_results=[fr])
|
||||
gen = ReportGenerator()
|
||||
path = gen.generate_json(vr, tmp_path / "result.json")
|
||||
data = json.loads(path.read_text())
|
||||
assert "suggestion" in data["field_results"][0]
|
||||
@@ -0,0 +1,33 @@
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
from config import Config
|
||||
from orchestrator import run_pipeline
|
||||
|
||||
|
||||
def test_e2e_pipeline_imports():
|
||||
"""Verify all modules import correctly without runtime tools."""
|
||||
from data.field_tree import Field, FieldTree
|
||||
from data.test_case import TestCase, TestSuite, SparkConfig
|
||||
from data.diff_result import FieldResult, VerificationRun
|
||||
from runners.runner import Runner, BuildResult, RunResult, CoverageReport
|
||||
from runners.native_java_runner import NativeJavaRunner
|
||||
from runners.spark_java_runner import SparkJavaRunner
|
||||
from runners.cobol_runner import CobolRunner
|
||||
from runners.data_writer import DataWriter
|
||||
from agents.llm import LLMClient
|
||||
from agents.agent1_parser import Agent1Parser
|
||||
from agents.agent2_data import Agent2Data
|
||||
from agents.agent3_diagnostic import Agent3Diagnostic
|
||||
from comparator.aligner import align_records
|
||||
from comparator.field_compare import compare_field
|
||||
from comparator.normalizer import Normalizer
|
||||
from comparator.rounding_detect import detect_rounding
|
||||
from comparator.cobol_binary_reader import CobolBinaryReader
|
||||
from report.generator import ReportGenerator
|
||||
from storage.bundle import TestDataBundle
|
||||
from storage.store import ReportStore, DiskCache
|
||||
from preprocessor import CopybookPreprocessor
|
||||
from config.mapping import MappingConfig, FieldMapping
|
||||
from quality.l1_offset_validate import L1OffsetValidator
|
||||
from quality.l2_value_roundtrip import L2RoundtripValidator
|
||||
assert True
|
||||
Reference in New Issue
Block a user