黑盒数据做成合并

This commit is contained in:
2026-08-23 10:46:56 +08:00
parent 0203ead96b
commit 4b8d214260
75 changed files with 9900 additions and 0 deletions
@@ -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 = [
'マッチング(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