黑盒数据做成合并
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
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 = [
|
||||
'マッチング(1:1)',
|
||||
'マッチング(1:N)',
|
||||
'マッチング(N:1)',
|
||||
'レイアウト編集のみ(GETPUT)',
|
||||
'振り分け(IF文)',
|
||||
'振り分け(EVALUATE文)',
|
||||
'キーブレイク(集計)',
|
||||
'キーブレイク(集約)',
|
||||
'DB更新',
|
||||
'50分割',
|
||||
'25分割',
|
||||
'100分割',
|
||||
'項目チェック(重複含まず)',
|
||||
'オンラインPGM',
|
||||
'CSV→FB変換(改行なし)',
|
||||
'2段階マッチング(1:1⇒1:1)',
|
||||
'2段階マッチング(N:1⇒N:1)',
|
||||
'マッチングM:N⇒出力M件',
|
||||
'マッチングM:N⇒出力N件',
|
||||
'マッチングM:N⇒出力M×N件',
|
||||
'CSV→FB変換(改行あり)',
|
||||
'2段階マッチング(M:N⇒M:N)',
|
||||
'SELECT条件',
|
||||
'内部テーブル検索',
|
||||
'サブプログラム使用',
|
||||
'DB検索',
|
||||
'項目チェック(半角20桁/4桁)',
|
||||
'SYSIN読込',
|
||||
'ASCII→EBCDIC変換',
|
||||
'キーブレイク(集計集約以外)',
|
||||
'項目チェック(重複含む)',
|
||||
'1:N+キーブレイク(同キー)',
|
||||
'1:N+キーブレイク(異キー)',
|
||||
'SORT(INPUT/OUTPUT PROCEDURE)',
|
||||
'MERGE(複数ファイル結合)',
|
||||
]
|
||||
|
||||
DETAILED_DESIGN_PATTERNS = [
|
||||
'29(ASCII→EBCDIC変換)',
|
||||
'18(M:N→M件マッチング)',
|
||||
'20(M:N→M×N件直積出力)',
|
||||
'マッチング(1:1→1:1 2段階)',
|
||||
'マッチング(N:1→N:1 2段階)',
|
||||
'マッチング(M:N→M:N 2段階)',
|
||||
'33(1:N+異キーキーブレイク)',
|
||||
'34(SORT INPUT/OUTPUT PROCEDURE)',
|
||||
'11(25分割)',
|
||||
'12(100分割)',
|
||||
'27(半角20桁/4桁チェック)',
|
||||
'19(M:N→N件マッチング+集計)',
|
||||
'DB更新 + SYSIN読込(P28)',
|
||||
'編集出力(ランキング生成)',
|
||||
'32(1: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段階マッチング(1:1⇒1:1)')
|
||||
r2 = loader._load_pgm_pattern_rule('2段階マッチング(N:1⇒N:1)')
|
||||
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 '1:1⇒1:1' in r1
|
||||
assert 'N:1⇒N:1' in r2
|
||||
assert 'M:N⇒M:N' 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キーブレイク(同キー集約)', '32(1: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
|
||||
Reference in New Issue
Block a user