"""模型 ↔ SQL dump 建表结构一致性契约测试。 document/djangoadmin.fastapi.antdvue.sql 是建表事实来源 (scripts/init_db.py 以 SQL 为准、模型仅 backfill),因此 SQLAlchemy 模型 metadata 必须与 SQL 建表语句保持一致,否则 ORM 读写与建表脚本会产生隐蔽漂移。 对比维度:表集合、列名集合、每列 (类型族, 长度, 可空)、每表主键集合。 忽略:default/server_default、字符集、排序规则、显示宽度、索引、自增起始值。 """ import pytest from tests.contract.helpers import collect_model_schema, collect_sql_schema pytestmark = pytest.mark.contract def test_table_sets_match(): sql_schema, _ = collect_sql_schema() model_schema, _ = collect_model_schema() assert set(sql_schema) == set(model_schema), ( f"表集合不一致!\n" f"仅SQL存在: {sorted(set(sql_schema) - set(model_schema))}\n" f"仅模型存在: {sorted(set(model_schema) - set(sql_schema))}" ) def test_columns_and_types_match(): sql_schema, sql_pk = collect_sql_schema() model_schema, model_pk = collect_model_schema() assert set(sql_schema) == set(model_schema), "表集合不一致,先修复表集合再对比列" for table in sorted(sql_schema): sql_cols = set(sql_schema[table]) model_cols = set(model_schema[table]) assert sql_cols == model_cols, ( f"[{table}] 列名不一致!\n" f"仅SQL存在: {sorted(sql_cols - model_cols)}\n" f"仅模型存在: {sorted(model_cols - sql_cols)}" ) for col in sorted(sql_cols): assert sql_schema[table][col] == model_schema[table][col], ( f"[{table}.{col}] 定义不一致: SQL={sql_schema[table][col]} " f"模型={model_schema[table][col]}" ) assert sql_pk[table] == model_pk[table], ( f"[{table}] 主键不一致: SQL={sorted(sql_pk[table])} 模型={sorted(model_pk[table])}" )