"""0065 高价值三态迁移的 upgrade/downgrade 往返验证(真实 MySQL)。""" import secrets from urllib.parse import quote_plus from alembic import command from alembic.config import Config from sqlalchemy import create_engine, inspect, text import web.backend.database as database_module from tests.support.db import get_mysql_credentials def test_0065_upgrade_downgrade_roundtrip(): creds = get_mysql_credentials() mig_db = f"douyu_login_mig_rt_{secrets.token_hex(4)}" mig_url = ( f"mysql+pymysql://{quote_plus(creds['user'])}:{quote_plus(creds['password'])}" f"@{creds['host']}:{creds['port']}/{mig_db}?charset=utf8mb4" ) admin_engine = create_engine( f"mysql+pymysql://{quote_plus(creds['user'])}:{quote_plus(creds['password'])}" f"@{creds['host']}:{creds['port']}/?charset=utf8mb4" ) with admin_engine.begin() as conn: conn.execute(text(f"CREATE DATABASE `{mig_db}` CHARACTER SET utf8mb4")) original = database_module.DATABASE_URL try: database_module.DATABASE_URL = mig_url cfg = Config("alembic.ini") cfg.set_main_option("script_location", "web/backend/migrations") cfg.set_main_option("sqlalchemy.url", mig_url) command.upgrade(cfg, "head") insp = inspect(create_engine(mig_url)) cols = {c["name"] for c in insp.get_columns("accounts")} assert "high_value_bairimeng_status" in cols assert "high_value_mengtu_status" in cols assert "high_value_checked_at" in cols assert "high_value_owned" not in cols assert "ix_accounts_high_value_checked_at" in { i["name"] for i in insp.get_indexes("accounts") } # 塞两行数据:owned/owned 与 owned/unknown,验证降级数组干净 engine = create_engine(mig_url) with engine.begin() as conn: for i, (b, m) in enumerate([("owned", "owned"), ("owned", "unknown")]): conn.execute( text( "INSERT INTO accounts (username, password, email, email_password," " cookie, high_value_bairimeng_status, high_value_mengtu_status)" " VALUES (:u, 'p', 'e@x.com', 'p', '', :b, :m)" ), {"u": f"rt-{i}", "b": b, "m": m}, ) command.downgrade(cfg, "20261021_0064") insp = inspect(create_engine(mig_url)) cols = {c["name"] for c in insp.get_columns("accounts")} assert "high_value_owned" in cols assert "high_value_bairimeng_status" not in cols assert "ix_accounts_high_value_checked_at" not in { i["name"] for i in insp.get_indexes("accounts") } with engine.begin() as conn: rows = conn.execute( text( "SELECT username, high_value_owned FROM accounts ORDER BY username" ) ).fetchall() # JSON 列读出来是字符串;重点是里面**不能有 null**(旧实现会写出 [null,...]) assert rows[0][1] == '["套装-兔兔白日梦", "套装-狂战萌兔"]', rows[0] assert rows[1][1] == '["套装-兔兔白日梦"]', rows[1] assert "null" not in rows[0][1] and "null" not in rows[1][1] command.upgrade(cfg, "head") insp = inspect(create_engine(mig_url)) cols = {c["name"] for c in insp.get_columns("accounts")} assert "high_value_owned" not in cols assert "high_value_checked_at" in cols finally: database_module.DATABASE_URL = original with admin_engine.begin() as conn: conn.execute(text(f"DROP DATABASE IF EXISTS `{mig_db}`"))