mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-16 01:40:15 +08:00
- 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
151 lines
4.6 KiB
Python
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
|