test_walk_graph_config.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. """V3-M4A: WalkGraphStore 读三个游走配置 JSON + 校验 + 拍板值解包。"""
  2. from __future__ import annotations
  3. import pytest
  4. from content_agent.integrations.walk_graph_json import (
  5. WalkGraphStore,
  6. _validate_graph,
  7. edge_permission,
  8. edge_supported,
  9. low_budget,
  10. )
  11. def test_graph_has_8_nodes_and_9_edges():
  12. # M8:删 query_next_page 边后 10→9;search_page 节点保留(孤儿不报错)。
  13. graph = WalkGraphStore().load_graph()
  14. assert len(graph["nodes"]) == 8
  15. assert len(graph["edges"]) == 9
  16. assert "query_next_page" not in {edge["edge_id"] for edge in graph["edges"]}
  17. assert {edge["gate"] for edge in graph["edges"]} == {"none", "decision_gated", "keep_only"}
  18. def test_policy_unwraps_pinned_values():
  19. policy = WalkGraphStore().load_policy()
  20. # M8 拍板:总闸 60→10、删 max_reseed_rounds;deny / halve_min_1(max(1, budget//2))。
  21. assert policy["global"]["max_total_actions_per_run"] == 30
  22. assert policy["global"]["max_depth"] == 5
  23. assert "max_reseed_rounds" not in policy["global"]
  24. assert policy["edge_permissions"]["KEEP_CONTENT_FOR_REVIEW"]["video_to_hashtag"] == "deny"
  25. assert policy["budget_tiers"]["low_budget"] == "halve_min_1"
  26. assert low_budget(3) == 1
  27. assert low_budget(10) == 5
  28. def test_policy_pins_gemini_workers_without_call_cap():
  29. # Gemini 数量上限已取消;仅保留判定并发度。M8 删 max_reseed_rounds。
  30. policy_global = WalkGraphStore().load_policy()["global"]
  31. assert set(policy_global) == {
  32. "max_total_actions_per_run",
  33. "max_depth",
  34. "gemini_max_workers",
  35. }
  36. assert policy_global["gemini_max_workers"] == 2
  37. def test_policy_allows_smoke_env_overrides(monkeypatch):
  38. monkeypatch.setenv("CONTENT_AGENT_WALK_MAX_TOTAL_ACTIONS_PER_RUN", "4")
  39. monkeypatch.setenv("CONTENT_AGENT_WALK_MAX_DEPTH", "2")
  40. monkeypatch.setenv("CONTENT_AGENT_GEMINI_MAX_WORKERS", "3")
  41. policy_global = WalkGraphStore().load_policy()["global"]
  42. assert policy_global["max_total_actions_per_run"] == 4
  43. assert policy_global["max_depth"] == 2
  44. assert policy_global["gemini_max_workers"] == 3
  45. assert set(policy_global) == {
  46. "max_total_actions_per_run",
  47. "max_depth",
  48. "gemini_max_workers",
  49. }
  50. def test_policy_edge_budgets_match_decided_values():
  51. # M8 拍板 2026-06-17:标签 5、作者 5、每作者作品 5;query_next_page 已删。
  52. budgets = WalkGraphStore().load_policy()["edge_budgets_by_id"]
  53. assert "query_next_page" not in budgets
  54. assert budgets["author_to_works"]["max_total_actions"] == 20
  55. assert budgets["author_to_works"]["max_works_per_author"] == 5
  56. assert budgets["hashtag_to_query"]["max_total_actions"] == 20
  57. def test_shipinhao_author_edges_blocked_and_terminal_edges_supported():
  58. store = WalkGraphStore()
  59. profile = store.load_profile("shipinhao")
  60. assert not edge_supported(profile, "author_to_works")
  61. assert not edge_supported(profile, "author_work_to_content")
  62. assert edge_supported(profile, "hashtag_to_query")
  63. # 终端边不在 profile(平台无关控制边)→ 恒 supported。
  64. assert edge_supported(profile, "decision_to_asset")
  65. assert all(
  66. edge_supported(store.load_profile("douyin"), edge_id)
  67. for edge_id in ["author_to_works", "video_to_hashtag"]
  68. )
  69. def test_edge_permission_defaults_to_deny():
  70. policy = WalkGraphStore().load_policy()
  71. assert edge_permission(policy, "ADD_TO_CONTENT_POOL", "author_to_works") == "allow"
  72. assert edge_permission(policy, "KEEP_CONTENT_FOR_REVIEW", "author_to_works") == "allow_low_budget"
  73. assert edge_permission(policy, "REJECT_CONTENT", "author_to_works") == "deny"
  74. assert edge_permission(policy, None, "author_to_works") == "deny"
  75. assert edge_permission(policy, "ADD_TO_CONTENT_POOL", "unknown_edge") == "deny"
  76. def test_unknown_graph_edge_fails_validation():
  77. graph = WalkGraphStore().load_graph()
  78. tampered = {**graph, "edges": [*graph["edges"], {**graph["edges"][0], "edge_id": "made_up_edge"}]}
  79. findings = _validate_graph(tampered, {edge["edge_id"] for edge in graph["edges"]})
  80. assert any(finding["check_id"] == "edge_not_in_catalog" for finding in findings)
  81. with pytest.raises(ValueError):
  82. store = WalkGraphStore()
  83. bad_policy = {**store.load_policy(), "edge_budgets": [{"edge_id": "made_up_edge"}]}
  84. from content_agent.integrations.walk_graph_json import _raise_on_fail, _validate_policy
  85. _raise_on_fail(_validate_policy(bad_policy, {e["edge_id"] for e in graph["edges"]}), "walk policy")