Files
cobol-java-v3/black-box-data-create/tests/test_rule_loader.py
T
2026-08-23 10:46:56 +08:00

279 lines
10 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.
import os
import pytest
from agent.rule_loader import RuleLoader, PGM_PATTERN_MAP
from agent.models import ProgramMeta
RULES_DIR = os.path.join(os.path.dirname(__file__), '..', 'rules')
ALL_35_TYPES = [
'マッチング(11',
'マッチング(1N',
'マッチング(N1',
'レイアウト編集のみ(GETPUT',
'振り分け(IF文)',
'振り分け(EVALUATE文)',
'キーブレイク(集計)',
'キーブレイク(集約)',
'DB更新',
'50分割',
'25分割',
'100分割',
'項目チェック(重複含まず)',
'オンラインPGM',
'CSV→FB変換(改行なし)',
'2段階マッチング(11⇒11)',
'2段階マッチング(N1⇒N1)',
'マッチングMN⇒出力M件',
'マッチングMN⇒出力N件',
'マッチングMN⇒出力M×N件',
'CSV→FB変換(改行あり)',
'2段階マッチング(MN⇒MN)',
'SELECT条件',
'内部テーブル検索',
'サブプログラム使用',
'DB検索',
'項目チェック(半角20桁/4桁)',
'SYSIN読込',
'ASCII→EBCDIC変換',
'キーブレイク(集計集約以外)',
'項目チェック(重複含む)',
'1:N+キーブレイク(同キー)',
'1:N+キーブレイク(異キー)',
'SORTINPUT/OUTPUT PROCEDURE',
'MERGE(複数ファイル結合)',
]
DETAILED_DESIGN_PATTERNS = [
'29ASCII→EBCDIC変換)',
'18M:N→M件マッチング)',
'20M:N→M×N件直積出力)',
'マッチング(1:1→1:1 2段階)',
'マッチング(N:1→N:1 2段階)',
'マッチング(M:N→M:N 2段階)',
'331:N+異キーキーブレイク)',
'34SORT INPUT/OUTPUT PROCEDURE',
'1125分割)',
'12100分割)',
'27(半角20桁/4桁チェック)',
'19M:N→N件マッチング+集計)',
'DB更新 + SYSIN読込(P28)',
'編集出力(ランキング生成)',
'321:N+同キーキーブレイク)',
'編集出力(印刷制御)',
]
def test_pgm_pattern_map_has_known_patterns():
assert 'マッチング(1:1)' in PGM_PATTERN_MAP
assert 'マッチング(1:N)' in PGM_PATTERN_MAP
assert 'DB更新' in PGM_PATTERN_MAP
@pytest.mark.parametrize('pattern', ALL_35_TYPES)
def test_all_classification_types_loadable(pattern):
"""程序分类说明.md の35类型すべてが規則に解決できること。"""
loader = RuleLoader(RULES_DIR)
assert loader._load_pgm_pattern_rule(pattern) is not None, f'未実装: {pattern}'
@pytest.mark.parametrize('pattern', DETAILED_DESIGN_PATTERNS)
def test_detailed_design_pattern_names_loadable(pattern):
"""詳細設計書の PGMパターン/プログラムタイプ 表記が規則に解決できること。"""
loader = RuleLoader(RULES_DIR)
assert loader._load_pgm_pattern_rule(pattern) is not None, f'マッピング不足: {pattern}'
def test_non_aggregate_keybreak_uses_dedicated_rule():
loader = RuleLoader(RULES_DIR)
rule = loader._load_pgm_pattern_rule('キーブレイク(集計集約以外)')
assert rule is not None
assert '非集計' in rule
def test_two_stage_matching_split_by_form():
loader = RuleLoader(RULES_DIR)
r1 = loader._load_pgm_pattern_rule('2段階マッチング(11⇒11)')
r2 = loader._load_pgm_pattern_rule('2段階マッチング(N1⇒N1)')
r3 = loader._load_pgm_pattern_rule('マッチング(M:N→M:N 2段階)')
assert r1 is not None and r2 is not None and r3 is not None
assert '11⇒11' in r1
assert 'N1⇒N1' in r2
assert 'MN⇒MN' in r3
assert r1 != r2 and r2 != r3 and r1 != r3
def test_load_matching_1_1_rule():
loader = RuleLoader(RULES_DIR)
meta = ProgramMeta(
program_id='TEST', program_name='', system_name='',
pgm_type='メイン', pgm_pattern='マッチング(1:1)',
summary_lines=[], prerequisites=[], files=[], keys=[], modules=[],
process_detail='', output_records='',
input_type='file', copy_fields={}, db_tables={}
)
combined, descriptions, count = loader.load(meta)
assert count > 0
assert len(combined) > 0
assert 'マッチング(1:1)' in combined
assert '数据生成' in combined
def test_load_matching_1_n_rule():
loader = RuleLoader(RULES_DIR)
meta = ProgramMeta(
program_id='TEST', program_name='', system_name='',
pgm_type='メイン', pgm_pattern='マッチング(1:N)',
summary_lines=[], prerequisites=[], files=[], keys=[], modules=[],
process_detail='', output_records='',
input_type='file', copy_fields={}, db_tables={}
)
combined, descriptions, count = loader.load(meta)
assert count > 0
assert len(combined) > 0
assert 'マッチング(1:N)' in combined
def test_matching_n_1_has_only_n_1_imbalance():
loader = RuleLoader(RULES_DIR)
rule = loader._load_pgm_pattern_rule('マッチング(N:1)')
assert rule is not None
_, count = loader._parse_group_info(rule)
assert count == 9
assert 'N:1匹配(MT-N003' in rule or 'N:1匹配(MT-N003)' in rule
assert '极端不平衡 N:1' in rule
assert '极端不平衡 1:N' not in rule
def test_if_branching_rule_covers_if_benchmark():
loader = RuleLoader(RULES_DIR)
rule = loader._load_pgm_pattern_rule('振り分け(IF文)')
assert rule is not None
_, count = loader._parse_group_info(rule)
assert count == 5
for marker in ('B-N001', 'B-N005', 'IF 2路分支'):
assert marker in rule
assert 'B-N006' not in rule
assert 'B-N010' not in rule
assert 'B-R001' not in rule
def test_evaluate_branching_rule_covers_evaluate_benchmark():
loader = RuleLoader(RULES_DIR)
rule = loader._load_pgm_pattern_rule('振り分け(EVALUATE文)')
assert rule is not None
_, count = loader._parse_group_info(rule)
assert count == 5
for marker in ('B-N006', 'B-N009', 'B-A002', 'EVALUATE WHEN 多值'):
assert marker in rule
assert 'B-N001' not in rule
assert 'B-N010' not in rule
assert 'B-R001' not in rule
def test_keybreak_summary_rule_covers_benchmark():
loader = RuleLoader(RULES_DIR)
rule = loader._load_pgm_pattern_rule('キーブレイク(集計)')
assert rule is not None
_, count = loader._parse_group_info(rule)
assert count == 7
for marker in ('KB-N001', 'KB-N004', 'KB-N005', 'KB-N006', 'KB-A001', 'KB-A002', 'KB-R001'):
assert marker in rule
assert 'KB-N002' not in rule
def test_keybreak_aggregation_rule_covers_benchmark():
loader = RuleLoader(RULES_DIR)
rule = loader._load_pgm_pattern_rule('キーブレイク(集約)')
assert rule is not None
_, count = loader._parse_group_info(rule)
assert count == 7
for marker in ('KB-N002', 'KB-N004', 'KB-N005', 'KB-N006', 'KB-A001', 'KB-A002', 'KB-R001'):
assert marker in rule
assert 'KB-N001' not in rule
def test_combined_keybreak_rule_removed_and_remapped():
combined = os.path.join(RULES_DIR, 'pgm_pattern', 'キーブレイク(集計、集約).md')
assert not os.path.exists(combined)
loader = RuleLoader(RULES_DIR)
summary = loader._load_pgm_pattern_rule('キーブレイク(集計)')
for pattern in ('キーブレイク(集計、集約)', '1:N+キーブレイク(同キー)',
'1:Nキーブレイク(同キー集約)', '321:N+同キーキーブレイク)'):
rule = loader._load_pgm_pattern_rule(pattern)
assert rule is not None
assert rule == summary
def test_item_check_no_dup_rule_covers_benchmark():
loader = RuleLoader(RULES_DIR)
rule = loader._load_pgm_pattern_rule('項目チェック(重複含まず)')
assert rule is not None
_, count = loader._parse_group_info(rule)
assert count == 5
for marker in ('VF-N001', 'VF-N002', 'VF-N003', 'VF-N004', 'VF-R001'):
assert marker in rule
assert 'VF-N005' not in rule
assert not os.path.exists(os.path.join(RULES_DIR, 'pgm_pattern', '項目チェック.md'))
generic = loader._load_pgm_pattern_rule('項目チェック')
assert generic == rule
def test_item_check_halfwidth_rule_covers_benchmark():
loader = RuleLoader(RULES_DIR)
rule = loader._load_pgm_pattern_rule('項目チェック(半角20桁/4桁)')
assert rule is not None
_, count = loader._parse_group_info(rule)
assert count == 4
for marker in ('VF-N005', 'VF-N006', 'VF-A001', 'VF-A002'):
assert marker in rule
assert 'VF-N001' not in rule
def test_load_nonexistent_pattern_raises():
loader = RuleLoader(RULES_DIR)
meta = ProgramMeta(
program_id='TEST', program_name='', system_name='',
pgm_type='メイン', pgm_pattern='存在しないパターン',
summary_lines=[], prerequisites=[], files=[], keys=[], modules=[],
process_detail='', output_records='',
input_type='file', copy_fields={}, db_tables={}
)
try:
loader.load(meta)
assert False, 'Should have raised FileNotFoundError'
except FileNotFoundError:
pass
def test_detect_conditional_branch():
process = """
2-1-2.ロジック分岐判定(EVALUATE)
2-1-2-1.STATUS='1'の場合
INSERT処理
"""
assert RuleLoader._detect_feature(process, ['場合', 'EVALUATE', 'IF']) is True
def test_no_keyword_match():
process = "1.初期処理\n2.主処理\n3.終了処理"
assert RuleLoader._detect_feature(process, ['EVALUATE']) is False
def test_rule_with_conditional_feature():
loader = RuleLoader(RULES_DIR)
meta = ProgramMeta(
program_id='TEST', program_name='', system_name='',
pgm_type='メイン', pgm_pattern='マッチング(1:1)',
summary_lines=[], prerequisites=[], files=[], keys=[], modules=[],
process_detail='2-1-2.EVALUATEで条件分岐する。',
output_records='',
input_type='file', copy_fields={}, db_tables={}
)
combined, descriptions, count = loader.load(meta)
assert count > 0
assert '条件分支' in combined