136 lines
5.7 KiB
Python
136 lines
5.7 KiB
Python
r"""DB2 schema 限定表名(SCHEMA.TABLE)在数据生成层的归一化测试。
|
||
|
||
根因:`FROM SALARYDB.EMP-MASTER` 中 `SALARYDB.` 限定符使 core.py 表名正则
|
||
`(\w[\w-]*)` 遇 `.` 即停,把种子表解析为 `SALARYDB`;build_db_input 把 24 条
|
||
EMP 种子行挂到不存在的 `SALARYDB` 表键下,插入时被丢弃 → EMP_MASTER 未播种,
|
||
运行时 EMP 正常路径(SEARCH/EVALUATE)无法覆盖。
|
||
|
||
修复(分层,通用):
|
||
- core.py `_parse_sql` 各表名正则捕获完整限定名(SCHEMA.TABLE 不丢失)。
|
||
- to_sql.py `collect_sql_meta` 用 `_norm_table` 剥掉限定符取末段(EMP-MASTER),
|
||
与 YAML schema 表名一致;种子键归一,`_populate_database` 正常插入。
|
||
"""
|
||
|
||
import sys, os
|
||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||
from cobol_testgen.core import extract_sql_assignments
|
||
from cobol_testgen.to_sql import collect_sql_meta, build_db_input
|
||
|
||
_SRC_DECLARE = (
|
||
" PROCEDURE DIVISION.\n"
|
||
" EXEC SQL\n"
|
||
" DECLARE C2 CURSOR FOR\n"
|
||
" SELECT EMP-ID, EMP-NAME, DEPT-CODE, BASE-SALARY\n"
|
||
" FROM SALARYDB.EMP-MASTER\n"
|
||
" ORDER BY EMP-ID\n"
|
||
" END-EXEC.\n"
|
||
)
|
||
|
||
|
||
def test_extract_sql_assignments_keeps_full_qualified_table():
|
||
"""core.py 表名正则须捕获完整限定名(不丢失 .EMP-MASTER)。"""
|
||
assignments = extract_sql_assignments(_SRC_DECLARE)
|
||
tables = [a.get("table") for a in assignments.values() for a in a]
|
||
assert "SALARYDB.EMP-MASTER" in tables, f"qualified table lost: {tables}"
|
||
|
||
|
||
def test_collect_sql_meta_normalizes_schema_qualified_table():
|
||
"""collect_sql_meta 把 SCHEMA.TABLE 归一化为末段表名。"""
|
||
assignments = {
|
||
"__SQL_CURSOR_C2": [{
|
||
"type": "exec_sql_select",
|
||
"table": "SALARYDB.EMP-MASTER",
|
||
"select_list": "EMP-ID, EMP-NAME, DEPT-CODE, BASE-SALARY",
|
||
"into_vars": ["DBV-EMP-ID", "DBV-EMP-NAME", "DBV-DEPT-CODE", "DBV-BASE-SALARY"],
|
||
"where": "",
|
||
"sql_text": "DECLARE C2 CURSOR FOR SELECT EMP-ID FROM SALARYDB.EMP-MASTER",
|
||
}],
|
||
}
|
||
declared = {"EMP-MASTER": [
|
||
{"name": "EMP-ID", "db_type": "CHAR", "size": 8},
|
||
{"name": "EMP-NAME", "db_type": "CHAR", "size": 40},
|
||
{"name": "DEPT-CODE", "db_type": "CHAR", "size": 2},
|
||
{"name": "BASE-SALARY", "db_type": "CHAR", "size": 9},
|
||
]}
|
||
sql_meta = collect_sql_meta(assignments, declared)
|
||
assert len(sql_meta) == 1
|
||
assert sql_meta[0]["table"] == "EMP-MASTER", sql_meta[0]["table"]
|
||
|
||
|
||
def test_build_db_input_seeds_emp_master_not_salarydb():
|
||
"""种子行键必须是 EMP-MASTER(而非 SALARYDB),否则插入时被丢弃。"""
|
||
branch_paths = [
|
||
([('SQLCODE', '=', '0', True)], {}),
|
||
]
|
||
records = [{
|
||
"DBV-EMP-ID": "A0000001",
|
||
"DBV-EMP-NAME": "TANAKA",
|
||
"DBV-DEPT-CODE": "01",
|
||
"DBV-BASE-SALARY": "429010",
|
||
}]
|
||
declared = {"EMP-MASTER": [
|
||
{"name": "EMP-ID", "db_type": "CHAR", "size": 8},
|
||
{"name": "EMP-NAME", "db_type": "CHAR", "size": 40},
|
||
{"name": "DEPT-CODE", "db_type": "CHAR", "size": 2},
|
||
{"name": "BASE-SALARY", "db_type": "CHAR", "size": 9},
|
||
]}
|
||
assignments = {
|
||
"__SQL_CURSOR_C2": [{
|
||
"type": "exec_sql_select",
|
||
"table": "SALARYDB.EMP-MASTER",
|
||
"select_list": "EMP-ID, EMP-NAME, DEPT-CODE, BASE-SALARY",
|
||
"into_vars": ["DBV-EMP-ID", "DBV-EMP-NAME", "DBV-DEPT-CODE", "DBV-BASE-SALARY"],
|
||
"where": "",
|
||
"sql_text": "DECLARE C2 CURSOR FOR SELECT EMP-ID FROM SALARYDB.EMP-MASTER",
|
||
}],
|
||
}
|
||
db_input = build_db_input(
|
||
branch_paths, [], assignments,
|
||
collect_sql_meta(assignments, declared),
|
||
declared,
|
||
records=records,
|
||
)
|
||
assert "SALARYDB" not in db_input, f"wrong table key: {list(db_input.keys())}"
|
||
assert "EMP-MASTER" in db_input, f"missing EMP-MASTER key: {list(db_input.keys())}"
|
||
|
||
|
||
def test_select_where_accept_fed_hostvar_uses_runtime_default():
|
||
"""WHERE 宿主变量由 ACCEPT FROM COMMAND-LINE 馈入时,种子必须用运行时
|
||
缺省值(如 SHA02MNC RATES 的 WRK-YEAR-MONTH='202605'),而非记录里的
|
||
合成值('I00001')。否则 EFFECTIVE-FROM/TO 不满足 WHERE,RATES 不装载,
|
||
SEARCH/EVALUATE 无法覆盖。"""
|
||
branch_paths = [
|
||
([('SQLCODE', '=', '0', True)], {}),
|
||
]
|
||
records = [{"WRK-YEAR-MONTH": "I00001"}]
|
||
assignments = {
|
||
"WRK-YEAR-MONTH": [
|
||
{"type": "accept", "from": "COMMAND-LINE"},
|
||
{"type": "move_literal", "literal": "202605"},
|
||
],
|
||
}
|
||
sql_meta = [{
|
||
"type": "exec_sql_select",
|
||
"table": "INSURANCE-RATES",
|
||
"select_list": "EFFECTIVE-FROM, EFFECTIVE-TO",
|
||
"into_vars": [],
|
||
"where": "EFFECTIVE-FROM <= :WRK-YEAR-MONTH AND EFFECTIVE-TO >= :WRK-YEAR-MONTH",
|
||
"where_constraints": [
|
||
{"col": "EFFECTIVE-FROM", "type": "host_var", "host_var": "WRK-YEAR-MONTH", "op": "<="},
|
||
{"col": "EFFECTIVE-TO", "type": "host_var", "host_var": "WRK-YEAR-MONTH", "op": ">="},
|
||
],
|
||
}]
|
||
declared = {"INSURANCE-RATES": [
|
||
{"name": "EFFECTIVE-FROM", "db_type": "CHAR", "size": 6},
|
||
{"name": "EFFECTIVE-TO", "db_type": "CHAR", "size": 6},
|
||
]}
|
||
db_input = build_db_input(
|
||
branch_paths, [], assignments, sql_meta, declared, records=records,
|
||
)
|
||
rows = db_input.get("INSURANCE-RATES", [])
|
||
assert rows, "no INSURANCE-RATES seed rows"
|
||
assert rows[0]["EFFECTIVE-FROM"].strip() == "202605", (
|
||
f"seed EFFECTIVE-FROM={rows[0]['EFFECTIVE-FROM']!r} not runtime default"
|
||
)
|
||
assert rows[0]["EFFECTIVE-TO"].strip() == "202605"
|