diff --git a/backend/migrations/versions/20260803_reliability_monitoring.py b/backend/migrations/versions/20260803_reliability_monitoring.py index 21b3048..1cd3c74 100644 --- a/backend/migrations/versions/20260803_reliability_monitoring.py +++ b/backend/migrations/versions/20260803_reliability_monitoring.py @@ -105,9 +105,13 @@ def _create_missing_indexes(connection: Connection) -> None: existing_indexes = { index["name"] for index in inspector.get_indexes(table.name) } - for index in table.indexes: - if index.name not in existing_indexes: - index.create(bind=connection) + index = next( + item + for item in table.indexes + if item.name == "idx_ping_records_device_created_at" + ) + if index.name not in existing_indexes: + index.create(bind=connection) def _upgrade_postgresql_alert_type(connection: Connection) -> None: diff --git a/backend/tests/test_models_and_migration.py b/backend/tests/test_models_and_migration.py index fb6d7fa..9e83318 100644 --- a/backend/tests/test_models_and_migration.py +++ b/backend/tests/test_models_and_migration.py @@ -4,7 +4,7 @@ import importlib from pathlib import Path import pytest -from sqlalchemy import Column, Integer, Table, inspect, text +from sqlalchemy import Column, Index, Integer, Table, inspect, text from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine import app.models as models @@ -249,3 +249,44 @@ async def test_migration_preserves_legacy_rows_and_owns_only_its_schema( if future_table in Base.metadata.tables.values(): Base.metadata.remove(future_table) await engine.dispose() + + +async def test_migration_does_not_create_a_future_ping_record_index( + tmp_path: Path, +): + """Historical migration DDL stays limited to its named composite index.""" + migration = importlib.import_module( + "migrations.versions.20260803_reliability_monitoring" + ) + future_index = Index( + "idx_future_ping_record_round_num", + models.PingRecord.__table__.c.round_num, + ) + engine = create_async_engine( + f"sqlite+aiosqlite:///{tmp_path / 'future-index.db'}" + ) + try: + async with engine.begin() as connection: + await connection.execute( + text( + "CREATE TABLE ping_records (" + "id INTEGER PRIMARY KEY, device_id INTEGER NOT NULL, " + "is_alive BOOLEAN NOT NULL, response_time_ms FLOAT, " + "round_num INTEGER NOT NULL, created_at DATETIME)" + ) + ) + await connection.run_sync(migration.run_reliability_migration) + index_names = await connection.run_sync( + lambda sync_connection: { + index["name"] + for index in inspect(sync_connection).get_indexes( + "ping_records" + ) + } + ) + + assert "idx_ping_records_device_created_at" in index_names + assert "idx_future_ping_record_round_num" not in index_names + finally: + models.PingRecord.__table__.indexes.remove(future_index) + await engine.dispose()