test_config_tooling.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. """V2-M1 config tooling safety net: config_store, validators, Excel↔JSON sync."""
  2. from __future__ import annotations
  3. import importlib.util
  4. from pathlib import Path
  5. import pytest
  6. from content_agent.integrations import config_store
  7. from content_agent.integrations.policy_json import JsonPolicyBundleStore
  8. ROOT = Path(__file__).resolve().parents[1]
  9. RULE_PACK_JSON = ROOT / "product_documents/规则包/douyin_rule_packs.v1.json"
  10. def _load_script(name: str):
  11. spec = importlib.util.spec_from_file_location(name, ROOT / "scripts" / f"{name}.py")
  12. mod = importlib.util.module_from_spec(spec)
  13. spec.loader.exec_module(mod)
  14. return mod
  15. def test_config_store_hash_matches_raw_file_bytes():
  16. parsed, raw = config_store.load_json(RULE_PACK_JSON)
  17. assert config_store.sha256_text(raw) == config_store.sha256_text(RULE_PACK_JSON.read_text("utf-8"))
  18. # policy_bundle_hash must still hash the raw rule-pack text (refactor parity).
  19. bundle = JsonPolicyBundleStore(".").load_policy_bundle("V1")
  20. assert bundle["policy_bundle_hash"] == config_store.sha256_text(raw)
  21. assert bundle["strategy_source_ref"]["content_sha256"] == bundle["policy_bundle_hash"]
  22. def test_rule_pack_fk_validator_has_no_failures():
  23. import json
  24. mod = _load_script("validate_rule_pack_config")
  25. findings = mod.validate_rule_pack_config(json.loads(RULE_PACK_JSON.read_text("utf-8")))
  26. assert [f for f in findings if f["level"] == "fail"] == []
  27. def test_rule_pack_validator_rejects_enabled_dispatch_conflict():
  28. import json
  29. from copy import deepcopy
  30. mod = _load_script("validate_rule_pack_config")
  31. pkg = deepcopy(json.loads(RULE_PACK_JSON.read_text("utf-8")))
  32. duplicate = dict(pkg["rule_pack_dispatch"][0])
  33. duplicate["dispatch_id"] = "dispatch_content_duplicate"
  34. pkg["rule_pack_dispatch"].append(duplicate)
  35. findings = mod.validate_rule_pack_config(pkg)
  36. conflicts = [f for f in findings if f["check_id"] == "dispatch_conflict"]
  37. assert len(conflicts) == 1
  38. assert conflicts[0]["level"] == "fail"
  39. assert "CONFIG_RULE_PACK_DISPATCH_CONFLICT" in conflicts[0]["message"]
  40. assert "douyin_content_discovery_rule_pack_v1" in conflicts[0]["message"]
  41. def test_excel_meta_strategy_id_matches_walk_strategy():
  42. import json
  43. openpyxl = pytest.importorskip("openpyxl")
  44. workbook = openpyxl.load_workbook(
  45. ROOT / "tech_documents/规则包映射/规则包映射配置表.xlsx", read_only=True
  46. )
  47. rows = list(workbook["rule_package_meta"].iter_rows(values_only=True))
  48. meta = dict(zip(rows[0], rows[2])) # row 2 is the data-dictionary row; row 3 is data
  49. walk_strategy = json.loads(
  50. (ROOT / "product_documents/抖音游走策略/douyin_walk_strategy.v1.json").read_text("utf-8")
  51. )
  52. assert meta["strategy_id"] == walk_strategy["strategy_id"]
  53. def test_query_prompts_validator_passes_after_m2():
  54. mod = _load_script("validate_query_prompts_config")
  55. assert mod.main() == 0
  56. def test_query_prompts_validator_rejects_invalid_profiles():
  57. mod = _load_script("validate_query_prompts_config")
  58. assert mod.validate_query_prompts_config({}) == [
  59. {"level": "fail", "check_id": "profiles", "message": "no profiles defined"}
  60. ]
  61. findings = mod.validate_query_prompts_config(
  62. {
  63. "profiles": {
  64. "douyin/V1": {
  65. "system": "s",
  66. "user": "u",
  67. "temperature": 3,
  68. "max_tokens": 0,
  69. "variants_per_seed": 0,
  70. }
  71. }
  72. }
  73. )
  74. assert {"level": "fail", "check_id": "missing_field", "message": "douyin/V1 missing evidence_fields"} in findings
  75. assert {"level": "fail", "check_id": "temperature", "message": "douyin/V1 temperature out of [0,2]: 3"} in findings
  76. assert {"level": "fail", "check_id": "max_tokens", "message": "douyin/V1 max_tokens must be > 0"} in findings
  77. assert {"level": "fail", "check_id": "variants_per_seed", "message": "douyin/V1 variants_per_seed must be >= 1"} in findings
  78. def test_excel_matches_json_byte_equal():
  79. pytest.importorskip("openpyxl")
  80. from scripts.build_config_from_excel import build
  81. from scripts.check_config_json_canonical import canonical_dumps
  82. for path_str, obj in build().items():
  83. assert canonical_dumps(obj) == Path(path_str).read_text("utf-8"), f"Excel drift in {path_str}"