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"