test_pl_dispatch_calendar_migration.py 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. """用实际授权 INSERT 验证迁移:停用/隐藏入口不得派生维护权限。"""
  2. from pathlib import Path
  3. import re
  4. import sqlite3
  5. from uuid import uuid4
  6. import pytest
  7. MIGRATION = Path(__file__).resolve().parents[2] / 'docs/sql/migrations/2026-10-26-manual-dispatch-calendar.sql'
  8. def database(prefix, *, existing_target=True):
  9. db = sqlite3.connect(':memory:')
  10. db.create_function('UUID', 0, lambda: str(uuid4()))
  11. db.create_function('NOW', 0, lambda: '2026-10-26 12:00:00')
  12. common = "tenant_no TEXT DEFAULT 't1', order_num INT, remark TEXT, create_by TEXT, create_dept_id TEXT, create_time TEXT, update_time TEXT, del_flag INT DEFAULT 0"
  13. db.execute(f"CREATE TABLE sys_menu (id TEXT PRIMARY KEY, menu_name TEXT, parent_id TEXT DEFAULT '0', menu_type TEXT, path TEXT, component TEXT, perms TEXT, is_frame INT, visible TEXT DEFAULT '1', status TEXT DEFAULT '1', {common})")
  14. db.execute(f"CREATE TABLE sys_role (id TEXT PRIMARY KEY, status TEXT DEFAULT '1', {common})")
  15. db.execute(f"CREATE TABLE sys_role_menu (id TEXT PRIMARY KEY, role_id TEXT, menu_id TEXT, {common})")
  16. def menu(record_id, parent_id, path=None, perms=None):
  17. db.execute('INSERT INTO sys_menu (id,parent_id,path,perms) VALUES (?,?,?,?)', (record_id, parent_id, path, perms))
  18. is_web = prefix == 'pl'
  19. menu('root', '0', '/web' if is_web else '/mini')
  20. menu('parent', 'root', '/pl' if is_web else '/mini/pl')
  21. if is_web:
  22. menu('folder', 'parent', 'calendar-module')
  23. menu('leaf', 'folder' if is_web else 'parent', perms=prefix + ':dispatch-calendar:list')
  24. menu('source', 'parent', perms='pl:plan:list' if is_web else 'mini:pl:plan:manage')
  25. if existing_target:
  26. menu('target', 'leaf', perms=prefix + ':dispatch-calendar:manage')
  27. db.execute("INSERT INTO sys_role (id) VALUES ('r1')")
  28. db.execute("INSERT INTO sys_role_menu (id,role_id,menu_id) VALUES ('calendar-grant','r1','leaf')")
  29. db.execute("INSERT INTO sys_role_menu (id,role_id,menu_id) VALUES ('source-grant','r1','source')")
  30. return db
  31. def migrate(db):
  32. for sql in re.findall(r'INSERT INTO sys_(?:menu|role_menu)\s*\(.*?;', MIGRATION.read_text(), re.S):
  33. db.execute(sql)
  34. def grants(db):
  35. return db.execute("SELECT COUNT(*) FROM sys_role_menu rm JOIN sys_menu m ON m.id=rm.menu_id WHERE m.perms LIKE '%dispatch-calendar:manage' AND rm.del_flag=0").fetchone()[0]
  36. @pytest.mark.parametrize('prefix,part', [(prefix, part) for prefix in ['pl', 'mini:pl']
  37. for part in ['source', 'leaf', 'parent', 'root', 'target']]
  38. + [('pl', 'folder')])
  39. @pytest.mark.parametrize('field', ['status', 'visible'])
  40. def test_inactive_menu_cannot_grant_manage(prefix, part, field):
  41. with database(prefix) as db:
  42. db.execute(f'UPDATE sys_menu SET {field}=? WHERE id=?', ('0', part))
  43. migrate(db)
  44. assert grants(db) == 0
  45. @pytest.mark.parametrize('prefix', ['pl', 'mini:pl'])
  46. def test_active_permissions_are_granted_once_and_revocation_is_preserved(prefix):
  47. with database(prefix, existing_target=False) as db:
  48. migrate(db)
  49. migrate(db)
  50. assert grants(db) == 1
  51. db.execute("UPDATE sys_role_menu SET del_flag=1 WHERE menu_id IN (SELECT id FROM sys_menu WHERE perms LIKE '%dispatch-calendar:manage')")
  52. migrate(db)
  53. assert grants(db) == 0
  54. assert db.execute("SELECT COUNT(*) FROM sys_role_menu WHERE del_flag=1").fetchone()[0] == 1
  55. @pytest.mark.parametrize('prefix', ['pl', 'mini:pl'])
  56. @pytest.mark.parametrize('part', ['leaf', 'parent', 'root'])
  57. def test_inactive_calendar_ancestors_do_not_create_manage_menu(prefix, part):
  58. with database(prefix, existing_target=False) as db:
  59. db.execute('UPDATE sys_menu SET status=? WHERE id=?', ('0', part))
  60. migrate(db)
  61. assert db.execute("SELECT COUNT(*) FROM sys_menu WHERE perms LIKE '%dispatch-calendar:manage'").fetchone()[0] == 0