mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-19 11:06:18 +00:00
0023_run_change_seq was chained ahead of the already-shipped 0023_user_preferences revision, so databases stamped at that revision or later treat it as an applied ancestor and never execute it: the run_change_clock table and runs.change_seq column are permanently missing and the first thread deletion fails with 'no such table: run_change_clock' (#5516). 0025_repair_run_change_seq re-applies the same guarded DDL on upgrade and no-ops on healthy shapes. RunChangeClockRow and UserPreferenceRow are also registered in the ORM model registry. Fixes #5516 Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
113 lines
3.3 KiB
Python
113 lines
3.3 KiB
Python
"""Migration tests for 0024_project_documents (Phase-2 spec §6.1).
|
|
|
|
Pins the table shape and the four indexes on upgrade, and the clean downgrade
|
|
(the chain-head pin moved on to 0025_repair_run_change_seq with that
|
|
revision).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
|
|
import pytest
|
|
import sqlalchemy as sa
|
|
from alembic import command
|
|
|
|
from deerflow.persistence.bootstrap import _get_alembic_config
|
|
from deerflow.persistence.engine import close_engine, init_engine
|
|
|
|
pytestmark = pytest.mark.asyncio
|
|
|
|
REVISION = "0024_project_documents"
|
|
PREVIOUS = "0023_user_preferences"
|
|
TABLE = "project_documents"
|
|
INDEXES = {
|
|
"ix_project_documents_project_id",
|
|
"ix_project_documents_user_id",
|
|
"ix_project_documents_sha256",
|
|
"ix_project_documents_trashed_at",
|
|
}
|
|
COLUMNS = {
|
|
"id",
|
|
"project_id",
|
|
"user_id",
|
|
"name",
|
|
"stored_relpath",
|
|
"sha256",
|
|
"size_bytes",
|
|
"source_thread_id",
|
|
"source_kind",
|
|
"source_name",
|
|
"trashed_at",
|
|
"trash_origin",
|
|
"created_at",
|
|
"updated_at",
|
|
}
|
|
|
|
|
|
async def _engine(tmp_path, name: str = "test.db"):
|
|
url = f"sqlite+aiosqlite:///{tmp_path / name}"
|
|
await init_engine("sqlite", url=url, sqlite_dir=str(tmp_path))
|
|
from deerflow.persistence.engine import get_engine
|
|
|
|
return get_engine()
|
|
|
|
|
|
async def _inspect(engine):
|
|
async with engine.connect() as conn:
|
|
|
|
def _read(sync_conn):
|
|
inspector = sa.inspect(sync_conn)
|
|
return (
|
|
inspector.has_table(TABLE),
|
|
{c["name"] for c in inspector.get_columns(TABLE)} if inspector.has_table(TABLE) else set(),
|
|
{i["name"] for i in inspector.get_indexes(TABLE)} if inspector.has_table(TABLE) else set(),
|
|
)
|
|
|
|
return await conn.run_sync(_read)
|
|
|
|
|
|
async def test_0024_upgrade_creates_table_and_indexes(tmp_path):
|
|
engine = await _engine(tmp_path)
|
|
try:
|
|
exists, columns, indexes = await _inspect(engine)
|
|
assert exists
|
|
assert columns == COLUMNS
|
|
assert INDEXES <= indexes
|
|
finally:
|
|
await close_engine()
|
|
|
|
|
|
async def test_0024_downgrade_drops_table_and_reupgrade_recreates(tmp_path):
|
|
engine = await _engine(tmp_path)
|
|
try:
|
|
cfg = _get_alembic_config(engine)
|
|
await asyncio.to_thread(command.downgrade, cfg, PREVIOUS)
|
|
exists, _, _ = await _inspect(engine)
|
|
assert not exists
|
|
|
|
# Downgrade is guarded and idempotent: repeating it is a no-op.
|
|
await asyncio.to_thread(command.downgrade, cfg, PREVIOUS)
|
|
|
|
await asyncio.to_thread(command.upgrade, cfg, REVISION)
|
|
exists, columns, indexes = await _inspect(engine)
|
|
assert exists
|
|
assert columns == COLUMNS
|
|
assert INDEXES <= indexes
|
|
finally:
|
|
await close_engine()
|
|
|
|
|
|
async def test_0024_upgrade_is_guarded_against_an_existing_table(tmp_path):
|
|
"""The inspector.has_table idiom keeps re-running the revision a no-op."""
|
|
engine = await _engine(tmp_path)
|
|
try:
|
|
cfg = _get_alembic_config(engine)
|
|
# Simulate a database that already carries the table (create_all
|
|
# bootstrap path): upgrading again must not fail.
|
|
await asyncio.to_thread(command.upgrade, cfg, REVISION)
|
|
exists, columns, _ = await _inspect(engine)
|
|
assert exists and columns == COLUMNS
|
|
finally:
|
|
await close_engine()
|