Files
MoviePilot/tests/test_plugin_instance_log_level_migration.py
DDSRem 2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00

145 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
插件实例日志等级覆盖列 Alembic 迁移测试。
"""
from __future__ import annotations
import importlib
from datetime import datetime, timezone
import sqlalchemy as sa
from alembic.migration import MigrationContext
from alembic.operations import Operations
from app.db.models.plugininstance import PluginInstance
MIGRATION_MODULE = "database.versions.487f7e681955_3_0_36"
def _bind_migration(monkeypatch, connection):
"""
把迁移绑定到隔离数据库连接。
"""
migration = importlib.import_module(MIGRATION_MODULE)
context = MigrationContext.configure(connection)
monkeypatch.setattr(migration, "op", Operations(context))
return migration
def _create_legacy_table(connection: sa.engine.Connection) -> None:
"""
建出加列前的表结构(3.0.35 的落点),模拟迁移前的存量数据库。
"""
now = datetime.now(timezone.utc).isoformat()
table = sa.Table(
"plugininstance",
sa.MetaData(),
sa.Column("id", sa.Integer(), primary_key=True),
sa.Column("instance_id", sa.String(length=128), nullable=False),
sa.Column("source_plugin_id", sa.String(length=128), nullable=False),
sa.Column("plugin_name", sa.String(length=255)),
sa.Column("plugin_desc", sa.String(length=255)),
sa.Column("plugin_icon", sa.String(length=255)),
sa.Column("config_data", sa.JSON()),
sa.Column("created_at", sa.String(length=40), nullable=False),
sa.Column("updated_at", sa.String(length=40), nullable=False),
)
table.create(connection)
connection.execute(
table.insert().values(
instance_id="DemoPluginWork",
source_plugin_id="DemoPlugin",
created_at=now,
updated_at=now,
)
)
def test_plugin_instance_log_level_migration_adds_columns_and_keeps_existing_rows(
monkeypatch,
) -> None:
"""
新增列必须可空,且不得影响已有行;重复升级与完整回滚都要幂等。
"""
engine = sa.create_engine("sqlite://")
with engine.begin() as connection:
_create_legacy_table(connection)
migration = _bind_migration(monkeypatch, connection)
migration.upgrade()
migration.upgrade()
columns = {
column["name"]: column
for column in sa.inspect(connection).get_columns("plugininstance")
}
# 与该迁移自身的落点比对,而不是与持续演进的当前模型比对:后续迁移
# 还会继续给该表加列,本断言不应该随之跟着变红。
assert columns.keys() == {
"id",
"instance_id",
"source_plugin_id",
"plugin_name",
"plugin_desc",
"plugin_icon",
"config_data",
"log_level",
"log_expires_at",
"created_at",
"updated_at",
}
assert columns["log_level"]["nullable"] is True
assert columns["log_expires_at"]["nullable"] is True
table = sa.Table("plugininstance", sa.MetaData(), autoload_with=connection)
row = connection.execute(sa.select(table)).mappings().one()
assert row["instance_id"] == "DemoPluginWork"
assert row["log_level"] is None
assert row["log_expires_at"] is None
migration.downgrade()
remaining = {
column["name"] for column in sa.inspect(connection).get_columns("plugininstance")
}
assert "log_level" not in remaining
assert "log_expires_at" not in remaining
migration.upgrade()
restored = {
column["name"] for column in sa.inspect(connection).get_columns("plugininstance")
}
assert {"log_level", "log_expires_at"}.issubset(restored)
def test_plugin_instance_log_level_migration_accepts_fresh_current_schema(
monkeypatch,
) -> None:
"""
create_all 已建当前表时重复升级不得因列已存在而报错。
"""
engine = sa.create_engine("sqlite://")
with engine.begin() as connection:
PluginInstance.__table__.create(connection)
migration = _bind_migration(monkeypatch, connection)
migration.upgrade()
migration.upgrade()
assert {
column["name"]
for column in sa.inspect(connection).get_columns("plugininstance")
} == {column.name for column in PluginInstance.__table__.columns}
def test_plugin_instance_log_level_migration_skips_when_table_absent(monkeypatch) -> None:
"""
表尚未建出时升级要安静跳过,不得因反射不到表而炸掉整条迁移链。
"""
engine = sa.create_engine("sqlite://")
with engine.begin() as connection:
migration = _bind_migration(monkeypatch, connection)
migration.upgrade()
assert "plugininstance" not in set(sa.inspect(connection).get_table_names())