diff --git a/app/main.py b/app/main.py index 4f38762..5a6576e 100644 --- a/app/main.py +++ b/app/main.py @@ -64,6 +64,9 @@ async def lifespan(app: FastAPI): MarketBase.metadata.create_all(bind=mysql_engine, tables=[MarketData.__table__, SymbolTimestamp.__table__]) logger.info("MySQL 表结构初始化完成") + from app.migration import migrate_sqlite_to_mysql + migrate_sqlite_to_mysql() + # 创建默认管理员账户 from app.database import SessionLocal from app import auth_service diff --git a/tests/test_main_lifespan.py b/tests/test_main_lifespan.py index ac54156..2fe3fe1 100644 --- a/tests/test_main_lifespan.py +++ b/tests/test_main_lifespan.py @@ -57,6 +57,36 @@ async def test_lifespan_calls_redis_and_mysql_init(patch_lifespan_deps, caplog): patch_lifespan_deps["init_mysql"].assert_called_once() +@pytest.mark.asyncio +async def test_lifespan_triggers_migration_when_mysql_ok(patch_lifespan_deps): + """ MySQL 可用时,lifespan 应触发 SQLite 到 MySQL 的迁移。 """ + from app.main import lifespan, app + + patch_lifespan_deps["init_redis"].return_value = None + patch_lifespan_deps["init_mysql"].return_value = MagicMock() + + with patch("app.migration.migrate_sqlite_to_mysql") as mock_migrate: + async with lifespan(app): + pass + + mock_migrate.assert_called_once() + + +@pytest.mark.asyncio +async def test_lifespan_skips_migration_when_mysql_unavailable(patch_lifespan_deps): + """ MySQL 不可用时,lifespan 不应调用迁移函数。 """ + from app.main import lifespan, app + + patch_lifespan_deps["init_redis"].return_value = None + patch_lifespan_deps["init_mysql"].return_value = None + + with patch("app.migration.migrate_sqlite_to_mysql") as mock_migrate: + async with lifespan(app): + pass + + mock_migrate.assert_not_called() + + @pytest.mark.asyncio async def test_lifespan_storage_mode_redis_and_mysql(patch_lifespan_deps, caplog): """ Redis 与 MySQL 均可用时,日志显示 Redis + MySQL 模式。 """