Files
omnigent-ai--omnigent/tests/db/test_migration_host_name_varchar64.py
2026-07-13 13:12:00 +08:00

95 lines
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()