test: cover between/hostvar/gcov-merge, class conditions, schema drop-tables, gixsql fixes

This commit is contained in:
hangshuo652
2026-08-09 17:43:16 +08:00
parent 273a3f8211
commit ff51bda962
22 changed files with 2559 additions and 0 deletions
@@ -0,0 +1,148 @@
"""JIN02SKL 型"明细键─主表键"后缀配对测试。
复现根因:跨文件键协调 _find_key_pairs 只做同名精确匹配 / WORK- 变体 /
EMP 族回退。当明细键 base='SKILL-CODE' 与主表键 base='MST-SKILL-CODE'
(多 'MST-' 限定词)时匹配不到,导致 SEARCH ALL 永未命中、
TBL-EMP-COUNT 恒 0#7/#8> ZEROTRUE 分支不可达。
修复:增加"后缀匹配"层级——一方 base 是另一方真后缀且 key 类别一致
(同为 -CODE/-ID/-NO/DATE/EMP-ID)时配对。通用实现、无程序名硬编码。
"""
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.__init__ import (
_coordinate_multi_file_keys,
_find_key_pairs_inner,
)
def _group(name):
return {'name': name, 'level': 1, 'pic': None,
'pic_info': {'type': 'group', 'digits': 0, 'decimal': 0, 'length': 200, 'signed': False},
'section': 'FILE', 'is_filler': False, 'redefines': None, 'usage': 'DISPLAY',
'occurs': 0, 'occurs_depending': None, 'value': None, 'values': None}
def _field(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': 'FILE', 'is_filler': False, 'redefines': None, 'usage': 'DISPLAY',
'occurs': 0, 'occurs_depending': None, 'value': None, 'values': None}
def _skill_fields():
# 字段顺序与源文件一致:01 记录紧跟其子字段(COPY REPLACING R01/R02
return [
_group('R01INNREC'),
_field('R01SKILL-CODE', 3, 'X(004)', 'alphanumeric', 4),
_field('R01EMP-ID', 3, 'X(008)', 'alphanumeric', 8),
_field('R01EMP-NAME', 3, 'X(040)', 'alphanumeric', 40),
_field('R01EVAL-DATE', 3, 'X(008)', 'alphanumeric', 8),
_field('R01SCORE', 3, '9(003)', 'numeric', 3),
_group('R02INNREC'),
_field('R02MST-SKILL-CODE', 3, 'X(004)', 'alphanumeric', 4),
_field('R02MST-LEVEL', 3, '9(002)', 'numeric', 2),
_field('R02MST-SCORE-FROM', 3, '9(003)', 'numeric', 3),
_field('R02MST-SCORE-TO', 3, '9(003)', 'numeric', 3),
_field('R02MST-LEVEL-NAME', 3, 'X(020)', 'alphanumeric', 20),
]
def _skill_records(n=6):
recs = []
for i in range(n):
recs.append({
'R01SKILL-CODE': f'A{i + 1:03d}',
'R01EMP-ID': f'B{i + 1:07d}',
'R01EMP-NAME': 'N' * 40,
'R01EVAL-DATE': f'D{i + 1:08d}',
'R01SCORE': f'{100 + i:03d}',
'R02MST-SKILL-CODE': f'F{i + 1:03d}',
'R02MST-LEVEL': f'{20 + i:02d}',
'R02MST-SCORE-FROM': f'{300 + i:03d}',
'R02MST-SCORE-TO': f'{400 + i:03d}',
'R02MST-LEVEL-NAME': 'G' * 20,
})
return recs
FILE_SEC = {
'R01INNFIL': ['R01INNREC'],
'R02INNFIL': ['R02INNREC'],
'W01OUTFIL': ['W01OUTREC'],
'W02OUTFIL': ['W02OUTREC'],
}
OPEN_DIR = {
'R01INNFIL': 'INPUT',
'R02INNFIL': 'INPUT',
'W01OUTFIL': 'OUTPUT',
'W02OUTFIL': 'OUTPUT',
}
def test_find_key_pairs_suffix_skill_code():
"""明细键 SKILL-CODE 应与主表键 MST-SKILL-CODE 后缀配对。"""
fields = _skill_fields()
pairs = _find_key_pairs_inner('R01', 'R02', fields)
assert ('R01SKILL-CODE', 'R02MST-SKILL-CODE') in pairs, f'SKILL-CODE↔MST-SKILL-CODE 应配对, 实际 {pairs}'
def test_find_key_pairs_suffix_no_cross_class():
"""类别不一致的后缀不应误配:DATE 类键不与 CODE 类键配对。"""
fields = _skill_fields()
pairs = _find_key_pairs_inner('R01', 'R02', fields)
date_keys = [fa for fa, _ in pairs if 'DATE' in fa.upper()]
code_keys = [fb for _, fb in pairs if '-CODE' in fb.upper()]
# R01EVAL-DATE 不应与 R02MST-SKILL-CODE 配对
assert not any('R01EVAL-DATE' == fa and fb == 'R02MST-SKILL-CODE' for fa, fb in pairs), f'DATE 不应与 CODE 后缀配对, 实际 {pairs}'
def test_coordinate_multi_file_keys_skill_match():
"""协调后应使部分记录 R01SKILL-CODE == R02MST-SKILL-CODE,运行时 SEARCH 可达。"""
fields = _skill_fields()
records = _skill_records(6)
_coordinate_multi_file_keys(
records, [], fields, {}, FILE_SEC,
term_types=['normal'] * len(records), open_dir=OPEN_DIR,
)
matched = [r for r in records
if str(r['R01SKILL-CODE']).strip() == str(r['R02MST-SKILL-CODE']).strip()]
assert matched, '至少一条记录明细键应与主表键一致'
def test_coordinate_exact_match_still_pairs():
"""既有同名精确匹配(EMP-ID↔EMP-ID)不受后缀层级影响。"""
fields = [
_group('R01INNREC'),
_field('R01EMP-ID', 3, 'X(008)', 'alphanumeric', 8),
_group('R02INNREC'),
_field('R02EMP-ID', 3, 'X(008)', 'alphanumeric', 8),
]
pairs = _find_key_pairs_inner('R01', 'R02', fields)
assert ('R01EMP-ID', 'R02EMP-ID') in pairs, f'同名 EMP-ID 应仍精确配对, 实际 {pairs}'
def test_coordinate_last_record_matches_table_key():
"""末条 normal 记录明细键应对齐表内键(首条 R02 键)。
社员工键中断集约(JIN09RPT 型):最终社员工组在 EOF 後の终了处理で出力される
`IF WRK-EMP-EVAL-CNT > ZERO PERFORM 3000OUTPUSOR`)。協調が先頭のみ整列だと
最終组に照合データがなく最终组输出分支不可達。末条记录も表内键に揃えることで
該分支が到達可能になる。
"""
fields = _skill_fields()
records = _skill_records(12)
_coordinate_multi_file_keys(
records, [], fields, {}, FILE_SEC,
term_types=['normal'] * len(records), open_dir=OPEN_DIR,
)
table_key = str(records[0]['R02MST-SKILL-CODE']).strip()
last = records[-1]
assert str(last['R01SKILL-CODE']).strip() == table_key, \
f'末条记录明细键应对齐表内键 {table_key}, 实际 {last["R01SKILL-CODE"]!r}'