139 lines
6.3 KiB
Python
139 lines
6.3 KiB
Python
"""RI-01~05: opaque FD + READ INTO — WS 子字段值序列化回 FD 记录字节 + 跨记录 key 差异保证"""
|
|
|
|
import sys, os, json, tempfile
|
|
from pathlib import Path
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
|
from cobol_testgen import _serialize_read_into_records
|
|
from cobol_testgen.core import build_branch_tree
|
|
|
|
|
|
def _mk_fields():
|
|
return [
|
|
{'name': 'R01INNREC', 'level': 1, 'pic': 'X(080)',
|
|
'pic_info': {'type': 'alphanumeric', 'digits': 0, 'decimal': 0, 'length': 80, 'signed': False},
|
|
'is_filler': False, 'redefines': None, 'is_88': False},
|
|
{'name': 'WRK-ABSENCE-REC', 'level': 1, 'pic': '',
|
|
'pic_info': {'type': 'unknown', 'digits': 0, 'decimal': 0, 'length': 0, 'signed': False},
|
|
'is_filler': False, 'redefines': None, 'is_88': False},
|
|
{'name': 'WRK-EMP-ID', 'level': 5, 'pic': 'X(008)',
|
|
'pic_info': {'type': 'alphanumeric', 'digits': 0, 'decimal': 0, 'length': 8, 'signed': False},
|
|
'is_filler': False, 'redefines': None, 'is_88': False},
|
|
{'name': 'WRK-YEAR-MONTH', 'level': 5, 'pic': 'X(006)',
|
|
'pic_info': {'type': 'alphanumeric', 'digits': 0, 'decimal': 0, 'length': 6, 'signed': False},
|
|
'is_filler': False, 'redefines': None, 'is_88': False},
|
|
{'name': 'WRK-ABSENT-HOURS', 'level': 5, 'pic': '9(004)V9(001)',
|
|
'pic_info': {'type': 'numeric', 'digits': 4, 'decimal': 1, 'length': 0, 'signed': False},
|
|
'is_filler': False, 'redefines': None, 'is_88': False},
|
|
{'name': 'FILLER', 'level': 5, 'pic': 'X(062)',
|
|
'pic_info': {'type': 'alphanumeric', 'digits': 0, 'decimal': 0, 'length': 62, 'signed': False},
|
|
'is_filler': True, 'redefines': None, 'is_88': False},
|
|
{'name': 'WRK-PREV-EMP-ID', 'level': 1, 'pic': 'X(008)',
|
|
'pic_info': {'type': 'alphanumeric', 'digits': 0, 'decimal': 0, 'length': 8, 'signed': False},
|
|
'is_filler': False, 'redefines': None, 'is_88': False},
|
|
]
|
|
|
|
|
|
def _mk_records():
|
|
return [
|
|
{'R01INNREC': 'A' + '0' * 79,
|
|
'WRK-EMP-ID': 'G0000001', 'WRK-YEAR-MONTH': 'H00001', 'WRK-ABSENT-HOURS': '01501'},
|
|
{'R01INNREC': 'A' + '0' * 79,
|
|
'WRK-EMP-ID': 'G0000007', 'WRK-YEAR-MONTH': 'H00007', 'WRK-ABSENT-HOURS': '01507'},
|
|
{'R01INNREC': 'A' + '0' * 79,
|
|
'WRK-EMP-ID': 'G0000007', 'WRK-YEAR-MONTH': 'H00008', 'WRK-ABSENT-HOURS': '01508'},
|
|
]
|
|
|
|
|
|
def test_serialize_into_fd_bytes():
|
|
"""RI-01: INTO 组子字段值按 PIC 布局 pack 回 R01INNREC 字节"""
|
|
records = _mk_records()
|
|
file_sec = {'R01INNFIL': ['R01INNREC']}
|
|
assignments = {'WRK-ABSENCE-REC': [{'type': 'read_into', 'file': 'R01INNFIL', 'source_vars': []}]}
|
|
_serialize_read_into_records(records, assignments, _mk_fields(), file_sec)
|
|
|
|
r0 = records[0]['R01INNREC']
|
|
assert r0[0:8] == 'G0000001', r0[0:8]
|
|
assert r0[8:14] == 'H00001', r0[8:14]
|
|
assert r0[14:19] == '01501', r0[14:19]
|
|
assert len(r0) == 80
|
|
assert r0[19:].strip(' ') == '', r0[19:]
|
|
|
|
|
|
def test_key_variation_both_pairs():
|
|
"""RI-02: 相邻记录 key 既有相同又有差异(同 key 累加 + 新 key flush 两路径均可达)"""
|
|
records = _mk_records()
|
|
file_sec = {'R01INNFIL': ['R01INNREC']}
|
|
assignments = {'WRK-ABSENCE-REC': [{'type': 'read_into', 'file': 'R01INNFIL', 'source_vars': []}]}
|
|
_serialize_read_into_records(records, assignments, _mk_fields(), file_sec)
|
|
|
|
keys = [r['R01INNREC'][0:8] for r in records]
|
|
has_same = any(keys[i] == keys[i + 1] for i in range(len(keys) - 1))
|
|
has_diff = any(keys[i] != keys[i + 1] for i in range(len(keys) - 1))
|
|
assert has_same, f"no same-key pair: {keys}"
|
|
assert has_diff, f"no diff-key pair: {keys}"
|
|
|
|
|
|
def test_all_same_key_gets_break():
|
|
"""RI-03: 全记录同 key 时自动注入差异,保证新 key 分支可达"""
|
|
records = [
|
|
{'R01INNREC': 'A' + '0' * 79,
|
|
'WRK-EMP-ID': 'G0000001', 'WRK-YEAR-MONTH': 'H00001', 'WRK-ABSENT-HOURS': '01501'},
|
|
{'R01INNREC': 'A' + '0' * 79,
|
|
'WRK-EMP-ID': 'G0000001', 'WRK-YEAR-MONTH': 'H00002', 'WRK-ABSENT-HOURS': '01502'},
|
|
{'R01INNREC': 'A' + '0' * 79,
|
|
'WRK-EMP-ID': 'G0000001', 'WRK-YEAR-MONTH': 'H00003', 'WRK-ABSENT-HOURS': '01503'},
|
|
]
|
|
file_sec = {'R01INNFIL': ['R01INNREC']}
|
|
assignments = {'WRK-ABSENCE-REC': [{'type': 'read_into', 'file': 'R01INNFIL', 'source_vars': []}]}
|
|
_serialize_read_into_records(records, assignments, _mk_fields(), file_sec)
|
|
|
|
keys = [r['R01INNREC'][0:8] for r in records]
|
|
has_diff = any(keys[i] != keys[i + 1] for i in range(len(keys) - 1))
|
|
assert has_diff, f"no diff-key pair: {keys}"
|
|
|
|
|
|
def test_noop_without_read_into():
|
|
"""RI-04: 无 READ INTO 或 FD 有子字段时不改动记录"""
|
|
records = _mk_records()
|
|
file_sec = {'R01INNFIL': ['R01INNREC']}
|
|
before = [dict(r) for r in records]
|
|
_serialize_read_into_records(records, {}, _mk_fields(), file_sec)
|
|
assert records == before
|
|
|
|
fields_with_children = [
|
|
{'name': 'R01INNREC', 'level': 1, 'pic': 'X(080)',
|
|
'pic_info': {'type': 'alphanumeric', 'digits': 0, 'decimal': 0, 'length': 80, 'signed': False},
|
|
'is_filler': False, 'redefines': None, 'is_88': False},
|
|
{'name': 'R01EMP-ID', 'level': 5, 'pic': 'X(008)',
|
|
'pic_info': {'type': 'alphanumeric', 'digits': 0, 'decimal': 0, 'length': 8, 'signed': False},
|
|
'is_filler': False, 'redefines': None, 'is_88': False},
|
|
]
|
|
records2 = _mk_records()
|
|
before2 = [dict(r) for r in records2]
|
|
assignments = {'WRK-ABSENCE-REC': [{'type': 'read_into', 'file': 'R01INNFIL', 'source_vars': []}]}
|
|
_serialize_read_into_records(records2, assignments, fields_with_children, file_sec)
|
|
assert records2 == before2
|
|
|
|
|
|
def test_multiline_read_into_parsed():
|
|
"""RI-05: READ <FD> / INTO <GRP> 分行书写仍能解析为 read_into 赋值"""
|
|
proc = """PROCEDURE DIVISION.
|
|
MAIN.
|
|
PERFORM UNTIL WRK-EOF = 'Y'
|
|
READ R01INNFIL
|
|
INTO WRK-ABSENCE-REC
|
|
AT END
|
|
MOVE 'Y' TO WRK-EOF
|
|
END-READ
|
|
IF WRK-EOF = 'Y'
|
|
EXIT PERFORM
|
|
END-IF
|
|
END-PERFORM.
|
|
GOBACK.
|
|
"""
|
|
tree, asgn = build_branch_tree(proc, [])
|
|
read_keys = [k for k in asgn if any(
|
|
isinstance(a, dict) and a.get('type') == 'read_into' for a in asgn[k])]
|
|
assert read_keys == ['WRK-ABSENCE-REC'], read_keys
|
|
assert asgn['WRK-ABSENCE-REC'][0]['file'] == 'R01INNFIL'
|