test_sql_state_manager.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. SQLStateManager tests
  5. """
  6. import json
  7. import sys
  8. import pytest
  9. import data_modules.sql_state_manager as sql_state_manager_module
  10. from data_modules.sql_state_manager import SQLStateManager, EntityData
  11. from data_modules.index_manager import EntityMeta
  12. @pytest.fixture
  13. def temp_project(tmp_path):
  14. from data_modules.config import DataModulesConfig
  15. cfg = DataModulesConfig.from_project_root(tmp_path)
  16. cfg.ensure_dirs()
  17. return cfg
  18. def test_sql_state_manager_entity_and_alias(temp_project):
  19. manager = SQLStateManager(temp_project)
  20. entity = EntityData(
  21. id="xiaoyan",
  22. type="角色",
  23. name="萧炎",
  24. tier="核心",
  25. current={"realm": "斗师"},
  26. aliases=["炎帝", "小炎子"],
  27. is_protagonist=True,
  28. )
  29. assert manager.upsert_entity(entity) is True
  30. assert manager.upsert_entity(entity) is False
  31. fetched = manager.get_entity("xiaoyan")
  32. assert "炎帝" in fetched["aliases"]
  33. by_type = manager.get_entities_by_type("角色")
  34. assert any(e["id"] == "xiaoyan" for e in by_type)
  35. core = manager.get_core_entities()
  36. assert any(e["id"] == "xiaoyan" for e in core)
  37. protagonist = manager.get_protagonist()
  38. assert protagonist["id"] == "xiaoyan"
  39. resolved = manager.resolve_alias("炎帝")
  40. assert any(r["id"] == "xiaoyan" for r in resolved)
  41. assert manager.update_entity_current("xiaoyan", {"realm": "斗王"}) is True
  42. updated = manager.get_entity("xiaoyan")
  43. assert updated["current_json"]["realm"] == "斗王"
  44. def test_sql_state_manager_state_changes_and_relationships(temp_project):
  45. manager = SQLStateManager(temp_project)
  46. manager.upsert_entity(
  47. EntityData(id="xiaoyan", type="角色", name="萧炎", current={})
  48. )
  49. change_id = manager.record_state_change(
  50. entity_id="xiaoyan",
  51. field="realm",
  52. old_value="斗者",
  53. new_value="斗师",
  54. reason="突破",
  55. chapter=2,
  56. )
  57. assert change_id > 0
  58. assert len(manager.get_entity_state_changes("xiaoyan")) == 1
  59. assert len(manager.get_recent_state_changes(limit=5)) == 1
  60. assert len(manager.get_chapter_state_changes(2)) == 1
  61. assert manager.upsert_relationship(
  62. from_entity="xiaoyan",
  63. to_entity="yaolao",
  64. type="师徒",
  65. description="收徒",
  66. chapter=1,
  67. )
  68. rels = manager.get_entity_relationships("xiaoyan", direction="from")
  69. assert len(rels) == 1
  70. between = manager.get_relationship_between("xiaoyan", "yaolao")
  71. assert len(between) == 1
  72. assert len(manager.get_recent_relationships(limit=5)) >= 1
  73. def test_sql_state_manager_process_chapter_entities_and_exports(temp_project):
  74. manager = SQLStateManager(temp_project)
  75. stats = manager.process_chapter_entities(
  76. chapter=10,
  77. entities_appeared=[{"id": "xiaoyan", "mentions": ["萧炎"], "confidence": 0.9}],
  78. entities_new=[
  79. {"suggested_id": "yaolao", "name": "药老", "type": "角色", "tier": "重要"}
  80. ],
  81. state_changes=[
  82. {"entity_id": "yaolao", "field": "status", "old": "", "new": "出场", "reason": "登场"}
  83. ],
  84. relationships_new=[
  85. {"from": "xiaoyan", "to": "yaolao", "type": "师徒", "description": "收徒"}
  86. ],
  87. )
  88. assert stats["entities_created"] >= 1
  89. assert stats["relationships"] == 1
  90. rel_events = manager._index_manager.get_relationship_events("xiaoyan", direction="both")
  91. assert len(rel_events) >= 1
  92. entities_v3 = manager.export_to_entities_v3_format()
  93. assert "角色" in entities_v3
  94. alias_index = manager.export_to_alias_index_format()
  95. assert isinstance(alias_index, dict)
  96. def test_sql_state_manager_existing_entity_updates_and_stats(temp_project):
  97. manager = SQLStateManager(temp_project)
  98. manager.upsert_entity(
  99. EntityData(id="xiaoyan", type="角色", name="萧炎", current={"hp": 5})
  100. )
  101. stats = manager.process_chapter_entities(
  102. chapter=3,
  103. entities_appeared=[{"id": "xiaoyan", "mentions": ["萧炎"], "confidence": 0.9}],
  104. entities_new=[],
  105. state_changes=[
  106. {"entity_id": "xiaoyan", "field": "hp", "old": 5, "new": 0, "reason": "受伤"}
  107. ],
  108. relationships_new=[
  109. {"from_entity": "xiaoyan", "to_entity": "yaolao", "type": "师徒", "description": "收徒"}
  110. ],
  111. )
  112. assert stats["entities_updated"] >= 1
  113. assert stats["state_changes"] == 1
  114. updated = manager.get_entity("xiaoyan")
  115. assert updated["current_json"]["hp"] == 0
  116. rels = manager.get_entity_relationships("yaolao", direction="to")
  117. assert rels
  118. stats_summary = manager.get_stats()
  119. assert "entities" in stats_summary
  120. exported = manager.export_to_entities_v3_format()
  121. assert exported["角色"]["xiaoyan"]["canonical_name"] == "萧炎"
  122. def test_sql_state_manager_process_chapter_skips_and_existing(temp_project):
  123. manager = SQLStateManager(temp_project)
  124. manager.upsert_entity(EntityData(id="xiaoyan", type="角色", name="萧炎"))
  125. stats = manager.process_chapter_entities(
  126. chapter=1,
  127. entities_appeared=[{"mentions": ["无ID"]}, {"id": "xiaoyan", "mentions": ["萧炎"]}],
  128. entities_new=[{"name": "无ID"}, {"suggested_id": "xiaoyan", "name": "萧炎"}],
  129. state_changes=[{"field": "realm"}, {"entity_id": "xiaoyan", "field": "hp", "old": 1, "new": 1}],
  130. relationships_new=[{"from": "xiaoyan", "to": ""}],
  131. )
  132. assert stats["entities_updated"] >= 1
  133. assert stats["relationships"] == 0
  134. def test_sql_state_manager_export_protagonist_and_cli(temp_project, monkeypatch, capsys):
  135. manager = SQLStateManager(temp_project)
  136. def run_cli(args):
  137. monkeypatch.setattr(sys, "argv", args)
  138. sql_state_manager_module.main()
  139. return json.loads(capsys.readouterr().out or "{}")
  140. out = run_cli(["sql_state_manager", "--project-root", str(temp_project.project_root), "get-protagonist"])
  141. assert out.get("status") == "error"
  142. manager.upsert_entity(
  143. EntityData(id="xiaoyan", type="角色", name="萧炎", is_protagonist=True)
  144. )
  145. exported = manager.export_to_entities_v3_format()
  146. assert exported["角色"]["xiaoyan"]["is_protagonist"] is True
  147. out = run_cli(["sql_state_manager", "--project-root", str(temp_project.project_root), "get-protagonist"])
  148. assert out["status"] == "success"
  149. assert out["data"].get("canonical_name") == "萧炎"
  150. out = run_cli(["sql_state_manager", "--project-root", str(temp_project.project_root), "stats"])
  151. assert out["status"] == "success"
  152. assert "entities" in out.get("data", {})
  153. out = run_cli(["sql_state_manager", "--project-root", str(temp_project.project_root), "get-core-entities"])
  154. assert out["status"] == "success"
  155. out = run_cli(["sql_state_manager", "--project-root", str(temp_project.project_root), "export-entities-v3"])
  156. assert out["status"] == "success"
  157. assert "角色" in out.get("data", {})
  158. out = run_cli(["sql_state_manager", "--project-root", str(temp_project.project_root), "export-alias-index"])
  159. assert out["status"] == "success"
  160. assert isinstance(out.get("data", {}), dict)
  161. payload = json.dumps({"entities_appeared": [], "entities_new": [], "state_changes": [], "relationships_new": []})
  162. out = run_cli([
  163. "sql_state_manager",
  164. "--project-root",
  165. str(temp_project.project_root),
  166. "process-chapter",
  167. "--chapter",
  168. "2",
  169. "--data",
  170. payload,
  171. ])
  172. assert out["status"] == "success"