"""作业 6:字段注册表测试(分组完整性、类型/敏感级别、外部表示)。""" import pytest from src.services.fields_registry import ( FIELD_REGISTRY, GROUP_KEYS, INTERNAL_FIELD_KEYS, FORM_TO_BUSINESS_KEY, BUSINESS_TO_FORM_KEY, FieldGroup, SensitiveLevel, business_patch_from_form, parse_field_value, to_external_string, ) def test_registry_covers_all_three_groups(): """注册表完整覆盖 acd/human1/human2 三个分组(作业 6-3)。""" assert set(GROUP_KEYS) == set(FieldGroup) assert len(FIELD_REGISTRY) == 28 assert len(GROUP_KEYS[FieldGroup.ACD]) == 8 assert len(GROUP_KEYS[FieldGroup.HUMAN1]) == 10 assert len(GROUP_KEYS[FieldGroup.HUMAN2]) == 10 def test_person_fields_include_full_set(): """人员字段必须包含 sfzmwh/sjwh/csbw,不能只迁姓名和号码。""" for index in (1, 2): group_keys = set(GROUP_KEYS[FieldGroup.HUMAN1 if index == 1 else FieldGroup.HUMAN2]) for suffix in ("xm", "hpzl", "hphm", "sfzmhm", "sfzmwh", "sjhm", "sjwh", "csbw"): assert f"{suffix}{index}" in group_keys def test_workflow_native_driver_keys_registered(): """workflow 20261022 state 原生人员键(对账发现)已登记, 与 API 契约键同义并存;driver1License 是 sandbox 输出别名而非 state 键,不登记。""" for index in (1, 2): assert f"driver{index}ID" in FIELD_REGISTRY assert f"driver{index}Phonenumber" in FIELD_REGISTRY assert FIELD_REGISTRY[f"driver{index}ID"].sensitive is SensitiveLevel.SENSITIVE assert f"driver{index}License" not in FIELD_REGISTRY def test_registry_metadata_recorded(): """每个字段都记录类型、可写性、敏感级别(作业 6-4)。""" for spec in FIELD_REGISTRY.values(): assert spec.value_type in (str, bool, int) assert spec.writable is True assert isinstance(spec.sensitive, SensitiveLevel) assert spec.description def test_sensitive_levels_assigned(): """证件号/手机号 sensitive,姓名 personal,其余 public。""" assert FIELD_REGISTRY["sfzmhm1"].sensitive is SensitiveLevel.SENSITIVE assert FIELD_REGISTRY["sjhm2"].sensitive is SensitiveLevel.SENSITIVE assert FIELD_REGISTRY["xm1"].sensitive is SensitiveLevel.PERSONAL assert FIELD_REGISTRY["hphm1"].sensitive is SensitiveLevel.PUBLIC @pytest.mark.parametrize("raw,expected", [ ("1", True), ("true", True), ("是", True), ("0", False), ("false", False), ("否", False), ]) def test_parse_bool_explicit(raw, expected): assert parse_field_value(FIELD_REGISTRY["ywrysw"], raw) is expected @pytest.mark.parametrize("bad", ["", "maybe", "2", "有吧"]) def test_parse_bool_rejects_ambiguous(bad): """模糊值必须拒绝——保留 空/False/0/未知 的区分能力。""" with pytest.raises(ValueError): parse_field_value(FIELD_REGISTRY["ywrysw"], bad) def test_parse_int_and_str(): assert parse_field_value(FIELD_REGISTRY["jdcsl"], "2") == 2 assert parse_field_value(FIELD_REGISTRY["jdcsl"], "0") == 0 with pytest.raises(ValueError): parse_field_value(FIELD_REGISTRY["jdcsl"], "两辆") assert parse_field_value(FIELD_REGISTRY["hphm1"], "苏B12345") == "苏B12345" def test_external_string_representation(): """对外序列化:True->1,False->0,None->空串;内部仍区分 False 与 0。""" assert to_external_string(True) == "1" assert to_external_string(False) == "0" assert to_external_string(0) == "0" assert to_external_string(None) == "" assert to_external_string("苏B12345") == "苏B12345" # 内部存储不混淆:False 是 bool、0 是 int assert parse_field_value(FIELD_REGISTRY["ywrysw"], "0") is False assert parse_field_value(FIELD_REGISTRY["jdcsl"], "0") == 0 assert isinstance(parse_field_value(FIELD_REGISTRY["jdcsl"], "0"), int) def test_internal_field_protection_listed(): """图内部键登记在保护清单中,set_info 白名单不会收录它们。""" for key in INTERNAL_FIELD_KEYS: assert key not in FIELD_REGISTRY def test_form_business_key_mapping_covers_form_fields(): """图表单六键全部映射到 acd 业务键(作业 6 建立的映射)。""" assert set(FORM_TO_BUSINESS_KEY) == { "has_injury", "has_non_motor_vehicle_hit", "is_on_scene", "vehicle_count", "accident_time", "accident_cause", } for business_key in FORM_TO_BUSINESS_KEY.values(): assert business_key in FIELD_REGISTRY assert FIELD_REGISTRY[business_key].group is FieldGroup.ACD def test_business_patch_from_form_maps_and_filters(): """表单键映射、人员键原样、未知键过滤。""" patch = business_patch_from_form({ "has_injury": False, # -> ywrysw "vehicle_count": 2, # -> jdcsl "xm2": "李四", # 人员键原样 "stage_code": "1002", # 内部键过滤 "no_such_form_key": "x", # 未知键过滤 }) assert patch == {"ywrysw": False, "jdcsl": 2, "xm2": "李四"} def test_business_keys_without_form_source_not_injected(): """bjrjs/ywmtc 无表单来源:不进入注入映射(只能 set_info 直写)。""" assert "bjrjs" not in BUSINESS_TO_FORM_KEY assert "ywmtc" not in BUSINESS_TO_FORM_KEY