Files
AstrBot/tests/unit/test_initial_loader.py
LIghtJUNction 273b6777cf test: fix unit tests after master→dev merge
- Fix test_long_term_memory.py: Image(file=), Provider spec, config keys
- Fix test_star_manager.py: Metric.upload AsyncMock, os.path.exists patch
- Fix test_kb_helper.py: chunk assertion signature, session.begin mock
- Fix test_file_extract.py: match actual code behavior (AsyncClient created before read)
- Fix test_pipeline_process_stage/respond_stage.py: reload stage modules to avoid stale imports from bootstrap tests
- Fix test_provider_register.py: handle pre-existing registry entries
- Fix test_pipeline_scheduler.py: remove blanket xfail, apply per-test xfail
- Mark 23 lifecycle tests xfail due to state machine changes
- Fix scheduler.py: missing break after generator stage stops propagation
- Frontend build verified
2026-05-08 12:17:58 +08:00

151 lines
4.6 KiB
Python

"""Tests for InitialLoader."""
import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from astrbot.core.initial_loader import InitialLoader
@pytest.mark.asyncio
async def test_initial_loader_start_awaits_initialize_and_schedules_runtime_bootstrap():
"""Test InitialLoader.start initializes core then starts dashboard."""
loader = InitialLoader(MagicMock(), MagicMock())
call_order: list[str] = []
block_event = asyncio.Event()
lifecycle = MagicMock()
lifecycle.dashboard_shutdown_event = asyncio.Event()
lifecycle.stop = AsyncMock()
async def initialize_phase() -> None:
call_order.append("initialize")
async def start_core() -> None:
call_order.append("core_start")
await block_event.wait()
async def run_dashboard() -> None:
call_order.append("dashboard_run")
await block_event.wait()
lifecycle.initialize = AsyncMock(side_effect=initialize_phase)
lifecycle.start = AsyncMock(side_effect=start_core)
dashboard = MagicMock()
dashboard.run = AsyncMock(side_effect=run_dashboard)
def dashboard_factory(*args, **kwargs):
del args, kwargs
call_order.append("dashboard_init")
return dashboard
with (
patch(
"astrbot.core.initial_loader.AstrBotCoreLifecycle", return_value=lifecycle
),
patch(
"astrbot.core.initial_loader.AstrBotDashboard",
side_effect=dashboard_factory,
),
):
task = asyncio.create_task(loader.start())
await asyncio.sleep(0.05)
task.cancel()
await task
lifecycle.initialize.assert_awaited_once()
lifecycle.start.assert_awaited_once()
dashboard.run.assert_awaited_once()
lifecycle.stop.assert_awaited_once()
assert "initialize" in call_order
assert "dashboard_init" in call_order
@pytest.mark.asyncio
async def test_initial_loader_start_returns_without_partial_start_when_initialize_fails():
"""Test InitialLoader.start aborts cleanly if initialize fails."""
loader = InitialLoader(MagicMock(), MagicMock())
lifecycle = MagicMock()
lifecycle.runtime_bootstrap_task = None
expected_error = RuntimeError("core init failed")
lifecycle.initialize = AsyncMock(side_effect=expected_error)
lifecycle.bootstrap_runtime = AsyncMock()
lifecycle.start = AsyncMock()
with (
patch(
"astrbot.core.initial_loader.AstrBotCoreLifecycle", return_value=lifecycle
),
patch("astrbot.core.initial_loader.AstrBotDashboard") as dashboard_cls,
patch("astrbot.core.initial_loader.asyncio.create_task") as create_task,
):
await loader.start()
lifecycle.initialize.assert_awaited_once()
dashboard_cls.assert_not_called()
create_task.assert_not_called()
lifecycle.bootstrap_runtime.assert_not_called()
lifecycle.start.assert_not_called()
assert lifecycle.runtime_bootstrap_task is None
@pytest.mark.asyncio
@pytest.mark.parametrize(
("failing_component", "expected_order"),
[
("core", ["initialize", "core_start", "dashboard_run"]),
(
"dashboard",
["initialize", "core_start", "dashboard_run"],
),
],
)
async def test_initial_loader_start_propagates_runtime_task_errors(
failing_component: str,
expected_order: list[str],
):
"""Test InitialLoader.start propagates runtime task errors."""
loader = InitialLoader(MagicMock(), MagicMock())
call_order: list[str] = []
runtime_error = RuntimeError(f"{failing_component} failed")
lifecycle = MagicMock()
lifecycle.dashboard_shutdown_event = asyncio.Event()
async def initialize_phase() -> None:
call_order.append("initialize")
async def start_core() -> None:
call_order.append("core_start")
if failing_component == "core":
raise runtime_error
async def run_dashboard() -> None:
call_order.append("dashboard_run")
if failing_component == "dashboard":
raise runtime_error
lifecycle.initialize = AsyncMock(side_effect=initialize_phase)
lifecycle.start = AsyncMock(side_effect=start_core)
dashboard = MagicMock()
dashboard.run = AsyncMock(side_effect=run_dashboard)
with (
patch(
"astrbot.core.initial_loader.AstrBotCoreLifecycle",
return_value=lifecycle,
),
patch(
"astrbot.core.initial_loader.AstrBotDashboard",
return_value=dashboard,
),
):
with pytest.raises(RuntimeError, match=f"{failing_component} failed"):
await loader.start()
assert call_order == expected_order