| """Smoke tests for critical startup and import paths.""" |
|
|
| from __future__ import annotations |
|
|
| import subprocess |
| import sys |
| from pathlib import Path |
|
|
| from astrbot.core.pipeline.bootstrap import ensure_builtin_stages_registered |
| from astrbot.core.pipeline.process_stage.method.agent_sub_stages.internal import ( |
| InternalAgentSubStage, |
| ) |
| from astrbot.core.pipeline.process_stage.method.agent_sub_stages.third_party import ( |
| ThirdPartyAgentSubStage, |
| ) |
| from astrbot.core.pipeline.stage import Stage, registered_stages |
| from astrbot.core.pipeline.stage_order import STAGES_ORDER |
|
|
| REPO_ROOT = Path(__file__).resolve().parents[1] |
|
|
|
|
| def _run_code_in_fresh_interpreter(code: str, failure_message: str) -> None: |
| proc = subprocess.run( |
| [sys.executable, "-c", code], |
| cwd=REPO_ROOT, |
| capture_output=True, |
| text=True, |
| check=False, |
| ) |
| assert proc.returncode == 0, ( |
| f"{failure_message}\nstdout:\n{proc.stdout}\nstderr:\n{proc.stderr}\n" |
| ) |
|
|
|
|
| def test_smoke_critical_imports_in_fresh_interpreter() -> None: |
| code = ( |
| "import importlib;" |
| "mods=[" |
| "'astrbot.core.core_lifecycle'," |
| "'astrbot.core.astr_main_agent'," |
| "'astrbot.core.pipeline.scheduler'," |
| "'astrbot.core.pipeline.process_stage.method.agent_sub_stages.internal'," |
| "'astrbot.core.pipeline.process_stage.method.agent_sub_stages.third_party'" |
| "];" |
| "[importlib.import_module(m) for m in mods]" |
| ) |
| _run_code_in_fresh_interpreter(code, "Smoke import check failed.") |
|
|
|
|
| def test_smoke_pipeline_stage_registration_matches_order() -> None: |
| ensure_builtin_stages_registered() |
| stage_names = {cls.__name__ for cls in registered_stages} |
|
|
| assert set(STAGES_ORDER).issubset(stage_names) |
| assert len(stage_names) == len(registered_stages) |
|
|
|
|
| def test_smoke_agent_sub_stages_are_stage_subclasses() -> None: |
| assert issubclass(InternalAgentSubStage, Stage) |
| assert issubclass(ThirdPartyAgentSubStage, Stage) |
|
|
|
|
| def test_pipeline_package_exports_remain_compatible() -> None: |
| import astrbot.core.pipeline as pipeline |
|
|
| assert pipeline.ProcessStage is not None |
| assert pipeline.RespondStage is not None |
| assert isinstance(pipeline.STAGES_ORDER, list) |
| assert "ProcessStage" in pipeline.STAGES_ORDER |
|
|
|
|
| def test_builtin_stage_bootstrap_is_idempotent() -> None: |
| ensure_builtin_stages_registered() |
| before_count = len(registered_stages) |
| stage_names = {cls.__name__ for cls in registered_stages} |
|
|
| expected_stage_names = { |
| "WakingCheckStage", |
| "WhitelistCheckStage", |
| "SessionStatusCheckStage", |
| "RateLimitStage", |
| "ContentSafetyCheckStage", |
| "PreProcessStage", |
| "ProcessStage", |
| "ResultDecorateStage", |
| "RespondStage", |
| } |
|
|
| assert expected_stage_names.issubset(stage_names) |
|
|
| ensure_builtin_stages_registered() |
| assert len(registered_stages) == before_count |
|
|
|
|
| def test_pipeline_import_is_stable_with_mocked_apscheduler() -> None: |
| """Regression: importing pipeline should not require cron/apscheduler modules.""" |
| code = ( |
| "import sys;" |
| "from unittest.mock import MagicMock;" |
| "mock_apscheduler = MagicMock();" |
| "mock_apscheduler.schedulers = MagicMock();" |
| "mock_apscheduler.schedulers.asyncio = MagicMock();" |
| "mock_apscheduler.schedulers.background = MagicMock();" |
| "mock_apscheduler.triggers = MagicMock();" |
| "mock_apscheduler.triggers.cron = MagicMock();" |
| "mock_apscheduler.triggers.date = MagicMock();" |
| "sys.modules['apscheduler'] = mock_apscheduler;" |
| "sys.modules['apscheduler.schedulers'] = mock_apscheduler.schedulers;" |
| "sys.modules['apscheduler.schedulers.asyncio'] = mock_apscheduler.schedulers.asyncio;" |
| "sys.modules['apscheduler.schedulers.background'] = mock_apscheduler.schedulers.background;" |
| "sys.modules['apscheduler.triggers'] = mock_apscheduler.triggers;" |
| "sys.modules['apscheduler.triggers.cron'] = mock_apscheduler.triggers.cron;" |
| "sys.modules['apscheduler.triggers.date'] = mock_apscheduler.triggers.date;" |
| "import astrbot.core.pipeline as pipeline;" |
| "assert pipeline.ProcessStage is not None;" |
| "assert pipeline.RespondStage is not None" |
| ) |
| _run_code_in_fresh_interpreter( |
| code, |
| "Pipeline import should not depend on real apscheduler package.", |
| ) |
|
|