test_user_list_relations_perf.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. """用户列表关联补全:须批量查询,避免按用户 N+1。"""
  2. import unittest
  3. from datetime import datetime
  4. from sqlalchemy import create_engine, event
  5. from sqlalchemy.orm import sessionmaker
  6. class TestEnrichUserRelationsPerf(unittest.TestCase):
  7. def setUp(self):
  8. from app.models.models import (
  9. SysPost,
  10. SysPostRole,
  11. SysRole,
  12. SysUser,
  13. SysUserPost,
  14. SysUserRole,
  15. )
  16. self.engine = create_engine("sqlite:///:memory:")
  17. for table in (
  18. SysUser.__table__,
  19. SysRole.__table__,
  20. SysUserRole.__table__,
  21. SysPost.__table__,
  22. SysUserPost.__table__,
  23. SysPostRole.__table__,
  24. ):
  25. table.create(self.engine)
  26. Session = sessionmaker(bind=self.engine, autoflush=False, autocommit=False)
  27. self.db = Session()
  28. self.now = datetime.now()
  29. self.query_count = 0
  30. @event.listens_for(self.engine, "before_cursor_execute")
  31. def _count(_conn, _cursor, _statement, _parameters, _context, _executemany):
  32. self.query_count += 1
  33. self._listener = _count
  34. self.SysUser = SysUser
  35. self.SysRole = SysRole
  36. self.SysUserRole = SysUserRole
  37. self.SysPost = SysPost
  38. self.SysUserPost = SysUserPost
  39. self.SysPostRole = SysPostRole
  40. def tearDown(self):
  41. event.remove(self.engine, "before_cursor_execute", self._listener)
  42. self.db.close()
  43. self.engine.dispose()
  44. def _seed(self, n: int = 20):
  45. self.db.add(
  46. self.SysRole(
  47. id="role-direct",
  48. role_name="运营管理员",
  49. status="1",
  50. tenant_no="zkxw",
  51. create_dept_id="",
  52. create_time=self.now,
  53. update_time=self.now,
  54. del_flag=0,
  55. )
  56. )
  57. self.db.add(
  58. self.SysRole(
  59. id="role-post",
  60. role_name="职位角色",
  61. status="1",
  62. tenant_no="zkxw",
  63. create_dept_id="",
  64. create_time=self.now,
  65. update_time=self.now,
  66. del_flag=0,
  67. )
  68. )
  69. self.db.add(
  70. self.SysPost(
  71. id="post-1",
  72. post_name="人事",
  73. post_code="hr",
  74. status="1",
  75. tenant_no="zkxw",
  76. create_dept_id="",
  77. create_time=self.now,
  78. update_time=self.now,
  79. del_flag=0,
  80. )
  81. )
  82. self.db.add(
  83. self.SysPostRole(
  84. id="pr-1",
  85. post_id="post-1",
  86. role_id="role-post",
  87. tenant_no="zkxw",
  88. create_dept_id="",
  89. create_time=self.now,
  90. update_time=self.now,
  91. del_flag=0,
  92. )
  93. )
  94. for i in range(n):
  95. uid = f"u{i}"
  96. self.db.add(
  97. self.SysUser(
  98. id=uid,
  99. user_no=f"user{i}",
  100. nick_name=f"用户{i}",
  101. password="x",
  102. status="1",
  103. tenant_no="zkxw",
  104. create_dept_id="",
  105. create_time=self.now,
  106. update_time=self.now,
  107. del_flag=0,
  108. )
  109. )
  110. self.db.add(
  111. self.SysUserRole(
  112. id=f"ur{i}",
  113. user_id=uid,
  114. role_id="role-direct",
  115. tenant_no="zkxw",
  116. create_dept_id="",
  117. create_time=self.now,
  118. update_time=self.now,
  119. del_flag=0,
  120. )
  121. )
  122. self.db.add(
  123. self.SysUserPost(
  124. id=f"up{i}",
  125. user_id=uid,
  126. post_id="post-1",
  127. tenant_no="zkxw",
  128. create_dept_id="",
  129. create_time=self.now,
  130. update_time=self.now,
  131. del_flag=0,
  132. )
  133. )
  134. self.db.commit()
  135. def test_enrich_query_count_bounded_and_fields_correct(self):
  136. from app.crud import user as user_crud
  137. n = 20
  138. self._seed(n)
  139. items = [{"id": f"u{i}"} for i in range(n)]
  140. self.query_count = 0
  141. out = user_crud.enrich_user_dicts_with_relations(self.db, items)
  142. self.assertIs(out, items)
  143. self.assertEqual(len(items), n)
  144. first = items[0]
  145. self.assertEqual(first["roleIds"], ["role-direct"])
  146. self.assertEqual(first["postIds"], ["post-1"])
  147. self.assertEqual(first["postInheritedRoleIds"], ["role-post"])
  148. self.assertEqual(first["effectiveRoleIds"], ["role-direct", "role-post"])
  149. self.assertEqual(first["effectiveRoleNames"], ["运营管理员", "职位角色"])
  150. # 批量查询:角色/职位关联 + 启用角色名,不应随用户数线性增长
  151. self.assertLessEqual(self.query_count, 8)
  152. self.assertLess(self.query_count, n)
  153. if __name__ == "__main__":
  154. unittest.main()