"""V29 能识别先于迁移版本登记的兼容字段。""" import importlib.util from pathlib import Path from types import SimpleNamespace import pytest from sqlalchemy.dialects.mysql import DATETIME, TINYINT, VARCHAR def migration(monkeypatch, columns): path = Path(__file__).resolve().parents[2] / "alembic/versions/29b0c1d2e3f4_receipt_manual_confirmation.py" spec = importlib.util.spec_from_file_location("receipt_confirmation_migration", path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) statements = [] bind = SimpleNamespace(execute=lambda sql: statements.append(str(sql))) monkeypatch.setattr(module, "op", SimpleNamespace(get_bind=lambda: bind)) monkeypatch.setattr(module, "inspect", lambda _: SimpleNamespace(get_columns=lambda table: columns)) return module, statements def existing_columns(): return [ {"name": "confirmation_status", "type": TINYINT(), "nullable": False, "default": "'0'"}, {"name": "confirmed_by", "type": VARCHAR(64, collation="utf8mb4_general_ci"), "nullable": True}, {"name": "confirmed_at", "type": DATETIME(), "nullable": True}, {"name": "confirmed_revision", "type": VARCHAR(64), "nullable": True}, ] def test_兼容已有字段且仍执行权限和参数初始化(monkeypatch): module, statements = migration(monkeypatch, existing_columns()) module.upgrade() assert len(statements) == 4 assert all(not statement.startswith("ALTER TABLE") for statement in statements) assert any("receipt.confirmation.enabled" in statement for statement in statements) def test_已有字段不完整时拒绝迁移(monkeypatch): module, statements = migration(monkeypatch, existing_columns()[:1]) with pytest.raises(RuntimeError, match="字段不完整"): module.upgrade() assert not statements