test_growth_category_tree.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. from collections.abc import Generator
  2. from contextlib import contextmanager
  3. from types import SimpleNamespace
  4. from sqlalchemy import create_engine
  5. from sqlalchemy.orm import Session, sessionmaker
  6. from api.auth_middleware import normal_user_can_access
  7. from api.services import growth_category_tree as service
  8. from api.services.growth_category_tree import _build_growth_tree
  9. from supply_infra.db.base import Base
  10. from supply_infra.db.models.global_category_content_weight import (
  11. GlobalCategoryContentWeight,
  12. )
  13. from supply_infra.db.models.global_v2 import GlobalCategoryV2
  14. def test_growth_tree_api_is_available_to_normal_users() -> None:
  15. assert normal_user_can_access("GET", "/api/growth-category-tree")
  16. def test_build_growth_tree_uses_stable_ids_and_daily_scores() -> None:
  17. categories = [
  18. SimpleNamespace(
  19. stable_id=10,
  20. parent_stable_id=None,
  21. level=1,
  22. name="根分类",
  23. description="根描述",
  24. ),
  25. SimpleNamespace(
  26. stable_id=11,
  27. parent_stable_id=10,
  28. level=2,
  29. name="子分类",
  30. description=None,
  31. ),
  32. SimpleNamespace(
  33. stable_id=12,
  34. parent_stable_id=999,
  35. level=2,
  36. name="孤立分类",
  37. description=None,
  38. ),
  39. ]
  40. weights = [
  41. SimpleNamespace(
  42. stable_id=10,
  43. source_element_count=8,
  44. read_rate_score=0.3,
  45. avg_read_rate_score=1.2,
  46. like_rate_score=0.04,
  47. ),
  48. SimpleNamespace(
  49. stable_id=11,
  50. source_element_count=0,
  51. read_rate_score=0.0,
  52. avg_read_rate_score=0.0,
  53. like_rate_score=0.0,
  54. ),
  55. ]
  56. nodes = _build_growth_tree(categories, weights)
  57. assert [node["id"] for node in nodes] == [10, 12]
  58. root = nodes[0]
  59. assert root["weights"] == {
  60. "read_rate": 0.3,
  61. "avg_read_rate": 1.2,
  62. "like_rate": 0.04,
  63. }
  64. assert root["counts"] == {
  65. "read_rate": 8,
  66. "avg_read_rate": 8,
  67. "like_rate": 8,
  68. }
  69. assert root["children"][0]["id"] == 11
  70. assert root["children"][0]["counts"]["read_rate"] == 0
  71. assert "hung_word_count" not in root
  72. def test_build_growth_category_tree_serializes_before_session_closes(monkeypatch) -> None:
  73. engine = create_engine("sqlite+pysqlite:///:memory:")
  74. Base.metadata.create_all(
  75. engine,
  76. tables=[
  77. GlobalCategoryV2.__table__,
  78. GlobalCategoryContentWeight.__table__,
  79. ],
  80. )
  81. factory = sessionmaker(bind=engine, expire_on_commit=True)
  82. with factory() as session:
  83. session.add(
  84. GlobalCategoryV2(
  85. id=1,
  86. stable_id=10,
  87. name="根分类",
  88. description=None,
  89. source_type="实质",
  90. level=1,
  91. parent_stable_id=None,
  92. )
  93. )
  94. session.add(
  95. GlobalCategoryContentWeight(
  96. id=1,
  97. stable_id=10,
  98. parent_stable_id=None,
  99. level=1,
  100. biz_dt="20260819",
  101. status="completed",
  102. source_element_count=5,
  103. read_rate_score=0.2,
  104. avg_read_rate_score=1.5,
  105. like_rate_score=0.03,
  106. )
  107. )
  108. session.commit()
  109. @contextmanager
  110. def get_test_session() -> Generator[Session, None, None]:
  111. session = factory()
  112. try:
  113. yield session
  114. session.commit()
  115. finally:
  116. session.close()
  117. monkeypatch.setattr(service, "get_session", get_test_session)
  118. payload = service.build_growth_category_tree()
  119. assert payload["biz_dt"] == "20260819"
  120. assert payload["nodes"][0]["weights"]["avg_read_rate"] == 1.5