test_db_upgrade.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. """db_upgrade 纯逻辑测试(不连接任何数据库)。"""
  2. from alembic.config import Config
  3. from alembic.script import ScriptDirectory
  4. from dms.db_upgrade import _mask_password, resolve_migrations_dir, run_upgrade
  5. def _script() -> ScriptDirectory:
  6. cfg = Config()
  7. cfg.set_main_option("script_location", str(resolve_migrations_dir()))
  8. return ScriptDirectory.from_config(cfg)
  9. def test_resolve_migrations_dir_contains_env_and_versions():
  10. migrations = resolve_migrations_dir()
  11. assert (migrations / "env.py").exists()
  12. assert (migrations / "versions").is_dir()
  13. def test_migration_chain_head_and_steps_from_0013():
  14. """远端生产库 0013 → head 恰好经过 0014~0038。"""
  15. script = _script()
  16. assert script.get_current_head() == "0038_session_jti"
  17. steps = [
  18. r.revision
  19. for r in reversed(
  20. list(script.walk_revisions("0013_document_password_lock", "head"))
  21. )
  22. ]
  23. assert steps == [
  24. "0013_document_password_lock",
  25. "0014_document_relations",
  26. "0015_login_ip_restriction",
  27. "0016_portal_user_provision",
  28. "0017_category_type_display_name",
  29. "0018_sub_plan_binding",
  30. "0019_content_extract_error",
  31. "0020_sub_plan_to_attachment",
  32. "0021_remove_assigned_scope",
  33. "0022_login_lockout",
  34. "0023_admin_permissions",
  35. "0024_manage_permission_matrix",
  36. "0025_move_audit_actions",
  37. "0026_borrow_permission",
  38. "0027_ui_theme",
  39. "0028_backup_audit_actions",
  40. "0029_system_management",
  41. "0030_role_edit_permission",
  42. "0031_user_sort_no",
  43. "0032_user_managed_org",
  44. "0033_role_upload_permission",
  45. "0034_role_anchor_self",
  46. "0035_drop_audit_action_check",
  47. "0036_notification",
  48. "0037_drop_audit_target_check",
  49. "0038_session_jti",
  50. ]
  51. def test_run_upgrade_lists_pending_steps_excluding_current(monkeypatch, capsys):
  52. """0016 升级到 head:待执行清单含 0017~0038,当前版本不重复出现。"""
  53. monkeypatch.setenv("DMS_DATABASE_URL", "mysql+pymysql://u:secret@127.0.0.1:3306/dms")
  54. versions = iter(["0016_portal_user_provision", "0038_session_jti"])
  55. monkeypatch.setattr(
  56. "dms.db_upgrade._read_current_version", lambda url: next(versions)
  57. )
  58. monkeypatch.setattr("alembic.command.upgrade", lambda cfg, rev: None)
  59. assert run_upgrade(assume_yes=True) == 0
  60. out = capsys.readouterr().out
  61. assert " - 0017_category_type_display_name" in out
  62. assert " - 0018_sub_plan_binding" in out
  63. assert " - 0019_content_extract_error" in out
  64. assert " - 0020_sub_plan_to_attachment" in out
  65. assert " - 0021_remove_assigned_scope" in out
  66. assert " - 0022_login_lockout" in out
  67. assert " - 0023_admin_permissions" in out
  68. assert " - 0024_manage_permission_matrix" in out
  69. assert " - 0025_move_audit_actions" in out
  70. assert " - 0026_borrow_permission" in out
  71. assert " - 0027_ui_theme" in out
  72. assert " - 0028_backup_audit_actions" in out
  73. assert " - 0029_system_management" in out
  74. assert " - 0030_role_edit_permission" in out
  75. assert " - 0031_user_sort_no" in out
  76. assert " - 0032_user_managed_org" in out
  77. assert " - 0033_role_upload_permission" in out
  78. assert " - 0034_role_anchor_self" in out
  79. assert " - 0035_drop_audit_action_check" in out
  80. assert " - 0036_notification" in out
  81. assert " - 0037_drop_audit_target_check" in out
  82. assert " - 0038_session_jti" in out
  83. assert " - 0016" not in out
  84. assert "secret" not in out # 输出中的地址必须已脱敏
  85. def test_mask_password():
  86. url = "mysql+pymysql://dms_app:secret-pwd@203.0.113.10:3306/dms?charset=utf8mb4"
  87. assert (
  88. _mask_password(url)
  89. == "mysql+pymysql://dms_app:***@203.0.113.10:3306/dms?charset=utf8mb4"
  90. )
  91. assert _mask_password("sqlite:///foo.db") == "sqlite:///foo.db"
  92. def test_run_upgrade_requires_database_url(monkeypatch, capsys):
  93. """未配置 DMS_DATABASE_URL 时拒绝执行(fail-fast,绝不 fallback 到 ini 地址)。"""
  94. monkeypatch.delenv("DMS_DATABASE_URL", raising=False)
  95. assert run_upgrade(assume_yes=True) == 2
  96. assert "DMS_DATABASE_URL" in capsys.readouterr().out