Files
MediaCrawler/tests/test_monitor_column_migration.py
butubb e608b51210
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
test: 修正列渲染断言——SQLAlchemy 会对保留字加反引号
monitor_run.trigger 是 MySQL 保留字,渲染出来是 `trigger`。这比之前手写的列清单更正确:
清单里的裸 trigger 会直接语法错误。断言改为剥掉引号后比对列名。
2026-10-07 15:37:08 +08:00

84 lines
3.0 KiB
Python

# -*- coding: utf-8 -*-
"""Guards for the in-place column migration.
``create_all`` creates missing tables but never adds columns to a table that
already exists, so new model columns are applied by ``_ensure_columns``. That step
was originally driven by a hand-kept list, and forgetting to update it did not
fail loudly -- the app still started, connected, and then failed on every query
and every scheduler tick. These tests pin down its replacement, which derives the
work from the model metadata.
"""
import pytest
from sqlalchemy import Boolean, Column, Integer, MetaData, String, Table
from api.monitor import db
from api.monitor.models import MonitorBase
def _migratable_columns():
for table in MonitorBase.metadata.sorted_tables:
for column in table.columns:
if column.primary_key:
continue
yield pytest.param(column, id=f"{table.name}.{column.name}")
def _ddl(column: Column) -> str:
"""Render a detached column, so the tests never mutate the real metadata."""
scratch = Table("scratch", MetaData(), column)
return db._column_ddl(scratch.columns[0])
@pytest.mark.parametrize("column", _migratable_columns())
def test_column_renders_as_ddl(column):
ddl = db._column_ddl(column)
# SQLAlchemy back-quotes an identifier only when it has to, so the rendered
# name matches the column's with the quoting stripped -- which is the case for
# reserved words like monitor_run.trigger. That quoting is a feature: the
# hand-kept list this replaced would have emitted bare `trigger` and died on a
# syntax error.
assert ddl.split(" ", 1)[0].strip("`") == column.name
# MySQL refuses AUTO_INCREMENT together with the DEFAULT this helper appends
# to NOT NULL columns.
assert "AUTO_INCREMENT" not in ddl
@pytest.mark.parametrize("column", _migratable_columns())
def test_not_null_columns_carry_a_default(column):
"""So ADD COLUMN cannot fail on a table that already holds rows.
Without a DEFAULT, whether the ALTER succeeds depends on the server's
sql_mode -- not something a deployment should hinge on.
"""
if column.nullable:
pytest.skip("nullable column needs no seed value")
assert "DEFAULT" in db._column_ddl(column)
def test_boolean_default_becomes_a_mysql_literal():
"""Python's True is not a SQL keyword; it has to become 1."""
assert "DEFAULT 1" in _ddl(Column("flag", Boolean, nullable=False, default=True))
def test_string_defaults_are_quoted():
assert "DEFAULT 'interval'" in _ddl(
Column("mode", String(16), nullable=False, default="interval")
)
def test_integer_defaults_are_not_quoted():
ddl = _ddl(Column("n", Integer, nullable=False, default=0))
assert "DEFAULT 0" in ddl
assert "DEFAULT '0'" not in ddl
def test_a_not_null_column_without_a_model_default_falls_back_to_zero():
"""Belt and braces: even a column the model gives no default still migrates."""
ddl = _ddl(Column("n", Integer, nullable=False))
assert "DEFAULT 0" in ddl