test_db_upgrade.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  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~0027。"""
  15. script = _script()
  16. assert script.get_current_head() == "0027_ui_theme"
  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. ]
  40. def test_run_upgrade_lists_pending_steps_excluding_current(monkeypatch, capsys):
  41. """0016 升级到 head:待执行清单含 0017~0027,当前版本不重复出现。"""
  42. monkeypatch.setenv("DMS_DATABASE_URL", "mysql+pymysql://u:secret@127.0.0.1:3306/dms")
  43. versions = iter(["0016_portal_user_provision", "0027_ui_theme"])
  44. monkeypatch.setattr(
  45. "dms.db_upgrade._read_current_version", lambda url: next(versions)
  46. )
  47. monkeypatch.setattr("alembic.command.upgrade", lambda cfg, rev: None)
  48. assert run_upgrade(assume_yes=True) == 0
  49. out = capsys.readouterr().out
  50. assert " - 0017_category_type_display_name" in out
  51. assert " - 0018_sub_plan_binding" in out
  52. assert " - 0019_content_extract_error" in out
  53. assert " - 0020_sub_plan_to_attachment" in out
  54. assert " - 0021_remove_assigned_scope" in out
  55. assert " - 0022_login_lockout" in out
  56. assert " - 0023_admin_permissions" in out
  57. assert " - 0024_manage_permission_matrix" in out
  58. assert " - 0025_move_audit_actions" in out
  59. assert " - 0026_borrow_permission" in out
  60. assert " - 0027_ui_theme" in out
  61. assert " - 0016" not in out
  62. assert "secret" not in out # 输出中的地址必须已脱敏
  63. def test_mask_password():
  64. url = "mysql+pymysql://dms_app:secret-pwd@203.0.113.10:3306/dms?charset=utf8mb4"
  65. assert (
  66. _mask_password(url)
  67. == "mysql+pymysql://dms_app:***@203.0.113.10:3306/dms?charset=utf8mb4"
  68. )
  69. assert _mask_password("sqlite:///foo.db") == "sqlite:///foo.db"
  70. def test_run_upgrade_requires_database_url(monkeypatch, capsys):
  71. """未配置 DMS_DATABASE_URL 时拒绝执行(fail-fast,绝不 fallback 到 ini 地址)。"""
  72. monkeypatch.delenv("DMS_DATABASE_URL", raising=False)
  73. assert run_upgrade(assume_yes=True) == 2
  74. assert "DMS_DATABASE_URL" in capsys.readouterr().out