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

178 lines
7.0 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.
"""INSERT 表主键碰撞预置行生成测试(T2)。
gixsql+SQLite 下,`IF SQLCODE = -803` 分支要求运行时 INSERT 失败。
让 INSERT 失败的唯一途径是 DB 中已存在同主键行。因此 build_db_input
必须为"要求 SQLCODE≠0 的路径"生成主键碰撞预置行(主键值取自该记录)。
"""
import sys, os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
from cobol_testgen.to_sql import build_db_input
def _sql_meta_insert(table="EMP-MASTER", columns=("EMP-ID", "EMP-NAME"),
host_vars=("DBV-EMP-ID", "DBV-EMP-NAME")):
return [{
"type": "exec_sql_insert",
"table": table,
"columns": list(columns),
"host_vars": list(host_vars),
"sql_text": f"INSERT INTO {table} ({', '.join(columns)}) VALUES ({', '.join(':'+h for h in host_vars)})",
}]
def test_insert_sql_ok_path_skips_collision_row():
"""要求 SQLCODE=0INSERT 成功)的路径 → 不生成碰撞预置行"""
branch_paths = [
([('SQLCODE', '=', '0', True)], {}), # SQL ok → skip
]
records = [{'DBV-EMP-ID': 'A0000001', 'DBV-EMP-NAME': 'NAME'}]
db_input = build_db_input(
branch_paths, [], {}, _sql_meta_insert(),
{'EMP-MASTER': [{'name': 'EMP-ID', 'db_type': 'CHAR', 'size': 8}]},
records=records,
insert_pk={'EMP-MASTER': ['EMP-ID']},
)
assert 'EMP-MASTER' not in db_input or db_input['EMP-MASTER'] == []
def test_insert_error_path_generates_pk_collision_row():
"""要求 SQLCODE<>0INSERT 失败)的路径 → 生成主键碰撞预置行"""
branch_paths = [
([('SQLCODE', '<>', '0', True)], {}), # SQL fails → collision row
]
records = [{'DBV-EMP-ID': 'A0000001', 'DBV-EMP-NAME': 'NAME'}]
db_input = build_db_input(
branch_paths, [], {}, _sql_meta_insert(),
{'EMP-MASTER': [{'name': 'EMP-ID', 'db_type': 'CHAR', 'size': 8},
{'name': 'EMP-NAME', 'db_type': 'CHAR', 'size': 40}]},
records=records,
insert_pk={'EMP-MASTER': ['EMP-ID']},
)
rows = db_input.get('EMP-MASTER', [])
assert len(rows) == 1
assert rows[0]['EMP-ID'] == 'A0000001'
def test_insert_error_path_uses_record_host_var_value():
"""碰撞行主键取自记录中对应 host_var 的值"""
branch_paths = [
([('SQLCODE', '=', '-803', True)], {}),
]
records = [{'DBV-EMP-ID': 'Z9999999', 'DBV-EMP-NAME': 'DUPE'}]
db_input = build_db_input(
branch_paths, [], {}, _sql_meta_insert(),
{'EMP-MASTER': [{'name': 'EMP-ID', 'db_type': 'CHAR', 'size': 8}]},
records=records,
insert_pk={'EMP-MASTER': ['EMP-ID']},
)
rows = db_input.get('EMP-MASTER', [])
assert rows and rows[0]['EMP-ID'] == 'Z9999999'
def test_insert_collision_uses_input_record_pk_not_hostvar():
"""碰撞行主键用输入记录主键(R02EMP-ID),而非 expected host_var 值。
generate_records 的 rec 中 DBV-EMP-ID 可能是 DB 预置值(W前缀)或
expected 值,而运行时实际 INSERT 的是输入记录主键(R02EMP-ID)。
碰撞行必须与输入主键一致,运行时 INSERT 才会失败。
"""
branch_paths = [
([('SQLCODE', '=', '-803', True)], {}),
]
records = [{'R02EMP-ID': 'A0000001', 'DBV-EMP-ID': 'W0000001'}]
db_input = build_db_input(
branch_paths, [], {}, _sql_meta_insert(),
{'EMP-MASTER': [{'name': 'EMP-ID', 'db_type': 'CHAR', 'size': 8}]},
records=records,
insert_pk={'EMP-MASTER': ['EMP-ID']},
)
rows = db_input.get('EMP-MASTER', [])
assert rows and rows[0]['EMP-ID'] == 'A0000001'
def _sql_meta_insert_salary_results():
"""KYU06UPD 的 INSERT 语句形态(复合主键 EMP-ID + YEAR-MONTH)。"""
return [{
"type": "exec_sql_insert",
"table": "SALARY-RESULTS",
"columns": ["EMP-ID", "YEAR-MONTH", "GROSS-PAYMENT"],
"host_vars": ["DBV-EMP-ID", "DBV-YEAR-MONTH", "DBV-GROSS-PAYMENT"],
"sql_text": ("INSERT INTO SALARY-RESULTS (EMP-ID, YEAR-MONTH, GROSS-PAYMENT) "
"VALUES (:DBV-EMP-ID, :DBV-YEAR-MONTH, :DBV-GROSS-PAYMENT)"),
}]
def _declared_salary_results():
return {
"SALARY-RESULTS": [
{"name": "EMP-ID", "db_type": "CHAR", "size": 8},
{"name": "YEAR-MONTH", "db_type": "CHAR", "size": 6},
{"name": "GROSS-PAYMENT", "db_type": "DECIMAL", "size": 9},
],
}
def _kyu06_assignments():
"""KYU06UPD 的主键 MOVE 链:
DBV-EMP-ID ← R02EMP-ID(输入键)
DBV-YEAR-MONTH ← CNS-YEAR-MONTH-PARM ← WRK-YEAR-MONTHACCEPT +
IF = SPACES 默认 '202605'"""
return {
'DBV-EMP-ID': [{'type': 'move', 'source_vars': ['R02EMP-ID']}],
'DBV-YEAR-MONTH': [{'type': 'move', 'source_vars': ['CNS-YEAR-MONTH-PARM']}],
'CNS-YEAR-MONTH-PARM': [{'type': 'move', 'source_vars': ['WRK-YEAR-MONTH']}],
'WRK-YEAR-MONTH': [{'type': 'accept', 'from': 'USER'},
{'type': 'move_literal', 'literal': '202605'}],
}
def test_insert_collision_composite_pk_matches_runtime_keys():
"""复合主键碰撞行必须等于运行时 INSERT 键:
EMP-ID 用输入记录键(R02EMP-ID);YEAR-MONTH 用 ACCEPT 后
IF = SPACES 的默认字面量('202605'),而非记录中的合成值('L00007')。
运行时 INSERT 键是 (A0000007, '202605'),碰撞行若用合成 YEAR-MONTH
则永不碰撞,-803 分支永远不可达。
"""
branch_paths = [
([('SQLCODE', '=', '-803', True)], {}),
]
records = [{
'R02EMP-ID': 'A0000007',
'DBV-EMP-ID': 'M0000007',
'DBV-YEAR-MONTH': 'N00007',
'WRK-YEAR-MONTH': 'L00007',
}]
db_input = build_db_input(
branch_paths, [], _kyu06_assignments(), _sql_meta_insert_salary_results(),
_declared_salary_results(),
records=records,
insert_pk={'SALARY-RESULTS': ['EMP_ID', 'YEAR_MONTH'],
'SALARY_RESULTS': ['EMP_ID', 'YEAR_MONTH']},
)
rows = db_input.get('SALARY-RESULTS', [])
assert len(rows) == 1
assert rows[0]['EMP-ID'] == 'A0000007'
assert rows[0]['YEAR-MONTH'] == '202605'
def test_insert_collision_year_month_not_falling_back_to_emp_id():
"""无 MOVE 链/ACCEPT 默认时,YEAR-MONTH 碰撞列不应回退到 EMP-ID 输入字段
(旧 bug 会取 R02EMP-ID 截断成 'A00000'),而应使用宿主变量自身记录值。"""
branch_paths = [
([('SQLCODE', '=', '-803', True)], {}),
]
records = [{'R02EMP-ID': 'A0000007', 'DBV-EMP-ID': 'M0000007',
'DBV-YEAR-MONTH': 'N00007'}]
db_input = build_db_input(
branch_paths, [], {}, _sql_meta_insert_salary_results(),
_declared_salary_results(),
records=records,
insert_pk={'SALARY-RESULTS': ['EMP_ID', 'YEAR_MONTH'],
'SALARY_RESULTS': ['EMP_ID', 'YEAR_MONTH']},
)
rows = db_input.get('SALARY-RESULTS', [])
assert len(rows) == 1
assert rows[0]['YEAR-MONTH'] == 'N00007'