Files
cobol-java-v3/tests/cobol_testgen/test_to_sql_dispatch_seed.py
T

127 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""build_db_input 事务调度感知种子测试(JIN05UPD 根因)。
复现根因:UPDATE/DELETE 种子对每个 sql_ok 路径都用该路径记录的主键建行,
不区分记录运行时是否真的执行 UPDATE。JIN05UPD 的 4 条 INSERT 记录主键因此
全部被预置进 EMPLOYEE → 运行时 INSERT 全部 -803INSERT 成功分支不可达。
另外:非 sql_ok INSERT 路径的记录可能不是 INSERT 类(运行时走 WHEN OTHER),
碰撞行必须回退到真实 INSERT 记录的主键。
修复:按 88 级事务类型(INSERT/UPDATE/DELETE 语义标记)沿 MOVE 链追溯调度
字段,分类每条记录;UPDATE/DELETE 种子仅对 UPDATE/DELETE 记录;INSERT 碰撞
回退到首条 INSERT 类记录。
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
import pytest
from cobol_testgen.to_sql import build_db_input
def _fields():
def f(name, level, pic, ftype, length):
return {'name': name, 'level': level, 'pic': pic,
'pic_info': {'type': ftype, 'digits': 0, 'decimal': 0,
'length': length, 'signed': False},
'section': 'WS', 'is_filler': False, 'redefines': None,
'usage': 'DISPLAY', 'occurs': 0, 'occurs_depending': None,
'value': None, 'values': None}
base = [
f('WRK-TRAN-TYPE', 3, 'X(001)', 'alphanumeric', 1),
f('R01TRAN-TYPE', 3, 'X(001)', 'alphanumeric', 1),
f('R01TRAN-EMP-ID', 3, 'X(008)', 'alphanumeric', 8),
f('HV-EMP-ID', 3, 'X(008)', 'alphanumeric', 8),
]
for name, parent, vals in [('WRK-TRAN-INSERT', 'WRK-TRAN-TYPE', ['A', 'B', 'C']),
('WRK-TRAN-UPDATE', 'WRK-TRAN-TYPE', ['D', 'E', 'F', 'G']),
('WRK-TRAN-DELETE', 'WRK-TRAN-TYPE', ['Z'])]:
e = f(name, 88, None, 'alphanumeric', 1)
e['is_88'] = True
e['parent'] = parent
e['values'] = vals
e['value'] = vals[0]
base.append(e)
return base
_ASSIGN = {
'WRK-TRAN-TYPE': [{'type': 'move', 'source_vars': ['R01TRAN-TYPE']}],
'HV-EMP-ID': [{'type': 'move', 'source_vars': ['R01TRAN-EMP-ID']}],
}
def _declared():
return {'EMPLOYEE': [{'name': 'EMP_ID', 'db_type': 'CHAR', 'size': 8}]}
def _sql_meta():
return [
{'type': 'exec_sql_insert', 'table': 'EMPLOYEE',
'columns': ['EMP_ID'], 'host_vars': ['HV-EMP-ID'],
'sql_text': 'INSERT INTO EMPLOYEE (EMP_ID) VALUES (:HV-EMP-ID)'},
{'type': 'exec_sql_update', 'table': 'EMPLOYEE',
'set_clause': 'STATUS = :HV-STATUS',
'where': 'EMP_ID = :HV-EMP-ID', 'host_vars': ['HV-EMP-ID', 'HV-STATUS'],
'where_constraints': [{'col': 'EMP_ID', 'type': 'host_var', 'host_var': 'HV-EMP-ID'}],
'sql_text': 'UPDATE EMPLOYEE SET STATUS = :HV-STATUS WHERE EMP_ID = :HV-EMP-ID'},
]
def _records():
return [
{'R01TRAN-TYPE': 'A', 'R01TRAN-EMP-ID': 'B0000001', 'HV-EMP-ID': 'B0000001'}, # INSERT
{'R01TRAN-TYPE': 'D', 'R01TRAN-EMP-ID': 'B0000005', 'HV-EMP-ID': 'B0000005'}, # UPDATE
{'R01TRAN-TYPE': 'H', 'R01TRAN-EMP-ID': 'B0000008', 'HV-EMP-ID': 'B0000008'}, # OTHER
{'R01TRAN-TYPE': 'I', 'R01TRAN-EMP-ID': 'B0000009', 'HV-EMP-ID': 'B0000009'}, # OTHER (非sql_ok INSERT 路径)
]
def _paths():
return [
([], {}), # path0 sql_ok → rec0 (INSERT)
([], {}), # path1 sql_ok → rec1 (UPDATE)
([], {}), # path2 sql_ok → rec2 (OTHER)
([('SQLCODE', '<>', '0', True)], {}), # path3 INSERT 错误 → rec3 (OTHER)
]
_INSERT_PK = {'EMPLOYEE': ['EMP_ID']}
def test_update_seed_only_for_update_records():
"""INSERT/OTHER 记录不应被 UPDATE 种子注入;OTHER 记录主键不得入表。
B0000001 允许作为 INSERT 碰撞行出现一次(供 -803 分支),但不得因
UPDATE 种子而额外注入;B0000008/B0000009OTHER 记录)绝不能入表。
"""
di = build_db_input(_paths(), _fields(), _ASSIGN, _sql_meta(), _declared(),
records=_records(), insert_pk=_INSERT_PK)
keys = [r.get('EMP_ID', '').strip() for r in di.get('EMPLOYEE', [])]
assert keys.count('B0000001') <= 1, 'INSERT 记录主键仅允许作为碰撞行出现一次'
assert 'B0000005' in keys, 'UPDATE 记录主键应被种子注入'
assert 'B0000008' not in keys, 'OTHER 记录主键不应被种子注入'
assert 'B0000009' not in keys, '非 sql_ok OTHER 记录主键不应被 UPDATE 种子注入'
def test_insert_collision_falls_back_to_insert_record():
"""非 sql_ok INSERT 路径的记录若非 INSERT 类,碰撞行应回退到首条 INSERT 类记录主键。"""
di = build_db_input(_paths(), _fields(), _ASSIGN, _sql_meta(), _declared(),
records=_records(), insert_pk=_INSERT_PK)
keys = [r.get('EMP_ID', '').strip() for r in di.get('EMPLOYEE', [])]
assert 'B0000001' in keys, f'碰撞行应回退到首条 INSERT 记录主键, 实际 {keys}'
def test_insert_success_path_skips_seed():
"""sql_ok INSERT 路径不应生成任何种子行(INSERT 成功需主键空闲)。"""
paths = [
([('SQLCODE', '=', '0', True)], {}), # INSERT 成功
]
recs = [{'R01TRAN-TYPE': 'A', 'R01TRAN-EMP-ID': 'B0000001', 'HV-EMP-ID': 'B0000001'}]
di = build_db_input(paths, _fields(), _ASSIGN, _sql_meta(), _declared(),
records=recs, insert_pk=_INSERT_PK)
keys = [r.get('EMP_ID', '').strip() for r in di.get('EMPLOYEE', [])]
assert 'B0000001' not in keys, f'INSERT 成功路径不应生成种子, 实际 {keys}'