test_system_child_diff.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. """关联集合差量保存,仅使用独立内存 SQLite。"""
  2. from datetime import datetime
  3. import pytest
  4. from sqlalchemy import create_engine, text
  5. from sqlalchemy.orm import Session
  6. from app.crud import user, role
  7. from app.models.models import SysUserRole, SysUserPost, SysRole, SysRoleMenu, SysRoleDataScopeDetail, SysUser, SysPost, SysDept, SysMenu
  8. @pytest.fixture
  9. def db():
  10. engine = create_engine('sqlite:///:memory:')
  11. for model in (SysUserRole, SysUserPost, SysRole, SysRoleMenu, SysRoleDataScopeDetail, SysUser, SysPost, SysDept, SysMenu):
  12. model.__table__.create(engine)
  13. with engine.begin() as conn:
  14. conn.execute(text('CREATE UNIQUE INDEX uk_role_menu ON sys_role_menu(role_id, menu_id, del_flag)'))
  15. with Session(engine) as session:
  16. add(session, SysUser, 'u', user_no='u', nick_name='U')
  17. add(session, SysPost, 'p', post_name='post', post_code='P')
  18. add(session, SysMenu, 'm', menu_name='menu')
  19. add(session, SysDept, 'd', dept_name='dept')
  20. session.commit()
  21. yield session
  22. engine.dispose()
  23. def add(db, model, id, **fields):
  24. row = model(id=id, tenant_no='t', del_flag=0, create_time=datetime(2020, 1, 1), **fields)
  25. db.add(row)
  26. db.flush()
  27. return row
  28. def test_role_only_update_retains_posts_and_existing_identity(db):
  29. add(db, SysRole, 'r', role_name='role')
  30. add(db, SysRole, 'new', role_name='new')
  31. old = add(db, SysUserRole, 'ur', user_id='u', role_id='r')
  32. post = add(db, SysUserPost, 'up', user_id='u', post_id='p')
  33. user._replace_associations(db, 'u', 't', 'actor', role_ids=['r', 'new'])
  34. db.flush()
  35. assert old.del_flag == 0
  36. assert post.del_flag == 0
  37. assert old.create_time == datetime(2020, 1, 1)
  38. assert db.query(SysUserRole).filter_by(del_flag=0).count() == 2
  39. assert db.query(SysUserRole).count() == 2
  40. def test_post_diff_clear_and_restore_retains_ids_and_tenant(db):
  41. old = add(db, SysUserPost, 'up', user_id='u', post_id='p')
  42. foreign = add(db, SysUserPost, 'foreign', user_id='u', post_id='foreign')
  43. foreign.tenant_no = 'other'
  44. db.flush()
  45. user.set_user_post_ids_only(db, 'u', 't', ['p'], 'actor')
  46. db.flush()
  47. assert old.del_flag == 0
  48. assert foreign.del_flag == 0
  49. user.set_user_post_ids_only(db, 'u', 't', [], 'actor')
  50. db.flush()
  51. assert old.del_flag == 1
  52. user.set_user_post_ids_only(db, 'u', 't', ['p', 'p'], 'actor')
  53. db.flush()
  54. assert old.del_flag == 0
  55. assert db.query(SysUserPost).count() == 2
  56. def test_role_menus_noop_and_repeated_toggle_reuses_history(db):
  57. add(db, SysRole, 'r', role_name='role')
  58. old = add(db, SysRoleMenu, 'rm', role_id='r', menu_id='m')
  59. role.update_role_menus(db, 'r', ['m'], 't', 'actor')
  60. assert db.get(SysRoleMenu, 'rm') is not None
  61. assert old.del_flag == 0
  62. for _ in range(3):
  63. role.update_role_menus(db, 'r', [], 't', 'actor')
  64. assert old.del_flag == 1
  65. role.update_role_menus(db, 'r', ['m'], 't', 'actor')
  66. assert old.del_flag == 0
  67. assert db.query(SysRoleMenu).count() == 1
  68. def test_role_data_scope_complete_save_omitted_clears_and_diff_reuses_id(db):
  69. add(db, SysRole, 'r', role_name='role', data_scope='5')
  70. old = add(db, SysRoleDataScopeDetail, 'scope', role_id='r', scope_type='dept', scope_value='d')
  71. role.update_role_data_scope(db, 'r', '5', None, 't', 'actor')
  72. assert old.del_flag == 1
  73. role.update_role_data_scope(db, 'r', '5', [{'scopeType': 'dept', 'scopeValue': 'd'}], 't', 'actor')
  74. assert old.del_flag == 0
  75. assert db.query(SysRoleDataScopeDetail).count() == 1
  76. role.update_role_data_scope(db, 'r', '5', [], 't', 'actor')
  77. assert old.del_flag == 1
  78. def test_role_update_rejects_foreign_owner(db):
  79. owner = add(db, SysRole, 'r', role_name='role')
  80. owner.tenant_no = 'other'
  81. db.flush()
  82. with pytest.raises(ValueError):
  83. role.update_role_menus(db, 'r', ['m'], 't', 'actor')
  84. with pytest.raises(ValueError):
  85. role.update_role_data_scope(db, 'r', '5', [], 't', 'actor')
  86. @pytest.mark.parametrize('target_model, fields, kwargs', [
  87. (SysRole, {'role_name': 'foreign'}, {'role_ids': ['foreign']}),
  88. (SysPost, {'post_name': 'foreign', 'post_code': 'F'}, {'post_ids': ['foreign']}),
  89. ])
  90. def test_foreign_relation_target_rejected_before_changes(db, target_model, fields, kwargs):
  91. target = add(db, target_model, 'foreign', **fields)
  92. target.tenant_no = 'other'
  93. db.flush()
  94. with pytest.raises(ValueError):
  95. user._replace_associations(db, 'u', 't', 'actor', **kwargs)
  96. assert db.query(SysUserRole).count() == 0
  97. assert db.query(SysUserPost).count() == 0
  98. def test_foreign_scope_target_rejected(db):
  99. add(db, SysRole, 'r', role_name='role')
  100. target = db.get(SysDept, 'd')
  101. target.tenant_no = 'other'
  102. db.flush()
  103. with pytest.raises(ValueError):
  104. role.update_role_data_scope(db, 'r', '5', [{'scopeType':'dept','scopeValue':'d'}], 't', 'actor')
  105. def test_create_user_does_not_commit_parent_when_associations_fail(db, monkeypatch):
  106. monkeypatch.setattr(user, '_hash_password', lambda value: 'hashed')
  107. def fail(*args, **kwargs):
  108. raise ValueError('invalid association')
  109. monkeypatch.setattr(user, '_replace_associations', fail)
  110. with pytest.raises(ValueError):
  111. user.create_user(db, user_no='new-u', nick_name='new', password='password', tenant_no='t')
  112. db.rollback()
  113. assert db.query(SysUser).filter_by(user_no='new-u').first() is None
  114. def test_user_update_rejects_foreign_owner_before_header_change(db):
  115. with pytest.raises(ValueError):
  116. user.update_user(db, 'u', tenant_no='other', nick_name='hijacked', role_ids=[])
  117. assert db.get(SysUser, 'u').nick_name == 'U'
  118. def test_role_menu_rejects_foreign_target(db):
  119. add(db, SysRole, 'r', role_name='role')
  120. db.get(SysMenu, 'm').tenant_no = 'other'
  121. db.flush()
  122. with pytest.raises(ValueError):
  123. role.update_role_menus(db, 'r', ['m'], 't', 'actor')
  124. assert db.query(SysRoleMenu).count() == 0