项目文件夹

文件
2026-07-13 13:12:00 +08:00

95 行
3.3 KiB
Python

"""Tests for the hosts.name VARCHAR(64) migration (t1a2b3c4d5e6).
Verifies that after upgrade the column is VARCHAR(64) and NOT NULL, and
that downgrade restores it to VARCHAR(256).
"""
from __future__ import annotations
from collections.abc import Iterator
from pathlib import Path
import pytest
import sqlalchemy as sa
from alembic import command
from sqlalchemy.engine import Engine
from omnigent.db.utils import (
_build_alembic_config,
clear_engine_cache,
get_or_create_engine,
)
@pytest.fixture
def db_engine(tmp_path: Path) -> Iterator[Engine]:
"""Fresh SQLite database with the full migration chain applied (including t1a2b3c4d5e6)."""
db_path = tmp_path / "test.db"
uri = f"sqlite:///{db_path}"
engine = get_or_create_engine(uri)
try:
yield engine
finally:
clear_engine_cache()
def test_hosts_name_column_is_varchar64(db_engine: Engine) -> None:
"""After upgrade, hosts.name must be VARCHAR(64) and NOT NULL."""
cols = sa.inspect(db_engine).get_columns("hosts")
name_cols = [c for c in cols if c["name"] == "name"]
assert len(name_cols) == 1, "Expected exactly one 'name' column on hosts"
col = name_cols[0]
assert not col["nullable"], "hosts.name must be NOT NULL after the migration"
col_type = col["type"]
assert isinstance(col_type, sa.String), (
f"hosts.name must be a String type; got {type(col_type)}"
)
assert col_type.length == 64, f"hosts.name must be VARCHAR(64); got VARCHAR({col_type.length})"
def test_downgrade_restores_varchar256(tmp_path: Path) -> None:
"""Downgrade to s1a2b3c4d5e6 widens hosts.name back to VARCHAR(256)."""
db_path = tmp_path / "downgrade.db"
uri = f"sqlite:///{db_path}"
engine = get_or_create_engine(uri)
# Insert a host row at head (t1a2b3c4d5e6).
with engine.connect() as conn:
conn.execute(
sa.text(
"INSERT INTO hosts "
"(workspace_id, owner, name, host_id, status, created_at, updated_at) "
"VALUES (0, 'user@example.com', 'my-laptop', 'host_abc123', 1, "
"1700000000, 1700000001)"
)
)
conn.commit()
# Downgrade to s1a2b3c4d5e6 (below the enums→SMALLINT migration that now
# sits above t1, which converts hosts.status back to a string on the way).
config = _build_alembic_config(uri)
with engine.begin() as conn:
config.attributes["connection"] = conn
command.downgrade(config, "s1a2b3c4d5e6")
inspector = sa.inspect(engine)
name_col = next(c for c in inspector.get_columns("hosts") if c["name"] == "name")
assert not name_col["nullable"], "hosts.name must still be NOT NULL after downgrade"
col_type = name_col["type"]
assert isinstance(col_type, sa.String), (
f"hosts.name must be a String type after downgrade; got {type(col_type)}"
)
assert col_type.length == 256, (
f"hosts.name must be VARCHAR(256) after downgrade; got VARCHAR({col_type.length})"
)
# Existing data should survive the round-trip.
with engine.connect() as conn:
row = conn.execute(
sa.text("SELECT name FROM hosts WHERE owner = 'user@example.com'")
).scalar_one_or_none()
assert row == "my-laptop", f"Existing host name should survive downgrade; got {row!r}"
engine.dispose()
clear_engine_cache()