""" Tests for API module exports and re-exports. """ from __future__ import annotations import pytest class TestApiStarModule: """Tests for api/star module exports.""" def test_star_module_exports_context(self): """api.star should export Context.""" from astrbot_sdk.api.star import Context assert Context is not None def test_star_context_is_legacy_context(self): """api.star.Context should be LegacyContext.""" from astrbot_sdk._legacy_api import LegacyContext from astrbot_sdk.api.star import Context assert Context is LegacyContext def test_star_module_exports_metadata(self): """api.star should export StarMetadata.""" from astrbot_sdk.api.star import StarMetadata metadata = StarMetadata(name="demo", version="1.0.0") assert metadata.name == "demo" assert metadata.version == "1.0.0" def test_star_module_exports_legacy_star_and_register(self): """api.star should expose legacy Star/register imports.""" from astrbot_sdk._legacy_api import LegacyStar from astrbot_sdk.api.star import Star, StarTools, register @register(name="demo", author="tester") class DemoStar(Star): pass assert Star is LegacyStar assert callable(StarTools.get_data_dir) assert callable(register) assert DemoStar.__astrbot_plugin_metadata__ == { "name": "demo", "author": "tester", } class TestApiComponentsModule: """Tests for api/components module exports.""" def test_components_module_exports_command_component(self): """api.components should export CommandComponent.""" from astrbot_sdk.api.components import CommandComponent assert CommandComponent is not None def test_command_component_is_from_legacy_api(self): """api.components.CommandComponent should be from _legacy_api.""" from astrbot_sdk._legacy_api import CommandComponent as LegacyCommandComponent from astrbot_sdk.api.components import CommandComponent assert CommandComponent is LegacyCommandComponent class TestApiEventModule: """Tests for api/event module exports.""" def test_event_module_exports(self): """api.event should export expected names.""" from astrbot_sdk.api.event import ADMIN, AstrMessageEvent, MessageChain, filter assert ADMIN == "admin" assert filter is not None assert AstrMessageEvent is not None assert MessageChain is not None def test_astr_message_event_is_message_event_subclass(self): """AstrMessageEvent should be a MessageEvent-compatible subclass.""" from astrbot_sdk.api.event import AstrMessageEvent from astrbot_sdk.events import MessageEvent assert issubclass(AstrMessageEvent, MessageEvent) def test_all_exports(self): """api.event should export all expected names.""" from astrbot_sdk.api.event import __all__ assert "ADMIN" in __all__ assert "AstrMessageEvent" in __all__ assert "filter" in __all__ def test_event_module_exports_legacy_types(self): """api.event should expose common legacy helper types.""" from astrbot_sdk.api.event import ( MessageEventResult, MessageSession, MessageType, ) assert MessageEventResult is not None assert MessageSession is not None assert MessageType is not None def test_astr_message_event_preserves_legacy_message_str_and_private_group_none( self, ): """AstrMessageEvent should expose message_str and return None for missing group.""" from astrbot_sdk.api.event import AstrMessageEvent event = AstrMessageEvent(text="hello", user_id="user-1") assert event.message_str == "hello" assert event.get_group_id() is None def test_message_chain_serializes_components(self): """MessageChain.to_payload() should preserve compat component fields.""" from astrbot_sdk.api.message import Comp, MessageChain chain = MessageChain( [ Comp.Plain(text="hello"), Comp.Image(file="https://example.com/image.png"), ] ) assert chain.to_payload() == [ {"type": "Plain", "text": "hello"}, {"type": "Image", "file": "https://example.com/image.png"}, ] @pytest.mark.asyncio async def test_astr_message_event_send_uses_send_chain_when_context_bound(self): """AstrMessageEvent.send() should use platform.send_chain for rich messages.""" from unittest.mock import AsyncMock, MagicMock from astrbot_sdk.api.event import AstrMessageEvent from astrbot_sdk.api.message import Comp, MessageChain from astrbot_sdk.protocol.descriptors import SessionRef runtime_context = MagicMock() runtime_context.platform = AsyncMock() event = AstrMessageEvent(session_id="session-1", context=runtime_context) chain = MessageChain( [ Comp.Plain(text="hello"), Comp.Image(file="https://example.com/image.png"), ] ) await event.send(chain) runtime_context.platform.send_chain.assert_called_once_with( SessionRef(conversation_id="session-1"), [ {"type": "Plain", "text": "hello"}, {"type": "Image", "file": "https://example.com/image.png"}, ], ) class TestApiModule: """Tests for top-level api module.""" def test_api_module_exists(self): """api module should be importable.""" import astrbot_sdk.api assert astrbot_sdk.api is not None def test_api_subpackages_exist(self): """New compat subpackages should be importable.""" from loguru import logger from astrbot_sdk.api import ( AstrBotConfig, basic, message, message_components, platform, provider, ) assert AstrBotConfig is not None assert basic is not None assert logger is not None assert message is not None assert message_components is not None assert platform is not None assert provider is not None class TestAstrbotImportAlias: """Tests for the legacy ``astrbot`` package-name alias.""" def test_legacy_astrbot_root_exports_logger(self): """astrbot root package should expose logger like the old package did.""" from astrbot import logger assert logger is not None def test_legacy_astrbot_api_exports(self): """astrbot.api should expose the old logger/config entrance.""" from astrbot.api import AstrBotConfig, llm_tool, logger, sp assert AstrBotConfig is not None assert logger is not None assert sp is not None assert callable(llm_tool) def test_legacy_astrbot_event_exports(self): """astrbot.api.event should expose MessageChain from the legacy location.""" from astrbot.api.event import AstrMessageEvent, MessageChain, filter assert AstrMessageEvent is not None assert MessageChain is not None assert filter is not None def test_legacy_astrbot_star_exports(self): """astrbot.api.star should expose Context/Star/register/StarTools.""" from astrbot.api.star import Context, Star, StarTools, register assert Context is not None assert Star is not None assert callable(StarTools.get_data_dir) assert callable(register) def test_legacy_astrbot_core_session_waiter_exports(self): """astrbot.core.utils.session_waiter should expose the compat waiter helpers.""" from astrbot.core.utils.session_waiter import ( SessionController, SessionWaiter, session_waiter, ) assert SessionController is not None assert callable(SessionWaiter.trigger) assert callable(session_waiter) def test_legacy_astrbot_core_root_exports(self): """astrbot.core root should expose common legacy root names.""" from astrbot.core import AstrBotConfig, logger, sp from astrbot.core.message.components import At, Image, Node, Nodes, Plain assert AstrBotConfig is not None assert logger is not None assert sp is not None assert Plain(text="ok").text == "ok" assert Image(file="https://example.com/test.png").file assert At(qq="1").user_id == "1" assert Node(uin="1", content=[]).sender_id == "1" assert Nodes(nodes=[]).nodes == [] def test_legacy_astrbot_core_config_imports(self): """astrbot.core.config old import paths should remain available.""" from astrbot.core.config import AstrBotConfig from astrbot.core.config.astrbot_config import AstrBotConfig as ConfigModel assert AstrBotConfig is ConfigModel def test_legacy_astrbot_core_platform_imports(self): """astrbot.core.platform old import paths should remain available.""" from astrbot.core.platform import ( AstrBotMessage, AstrMessageEvent, MessageType, Platform, PlatformMetadata, ) from astrbot.core.platform.astr_message_event import MessageSession from astrbot.core.platform.message_type import MessageType as MessageTypeModule from astrbot.core.platform.platform_metadata import ( PlatformMetadata as MetaModule, ) from astrbot.core.platform.register import register_platform_adapter from astrbot.core.platform.sources.aiocqhttp import AiocqhttpMessageEvent assert AstrBotMessage is not None assert AstrMessageEvent is AiocqhttpMessageEvent assert MessageSession is not None assert MessageType is MessageTypeModule assert PlatformMetadata is MetaModule assert Platform is not None with pytest.raises(NotImplementedError, match="register_platform_adapter"): register_platform_adapter() def test_legacy_astrbot_core_utils_astrbot_path_imports(self): """astrbot.core.utils.astrbot_path should expose legacy path helpers.""" from astrbot.core.utils.astrbot_path import ( get_astrbot_data_path, get_astrbot_temp_path, ) assert callable(get_astrbot_data_path) assert callable(get_astrbot_temp_path) def test_legacy_astrbot_core_provider_imports(self): """astrbot.core.provider old import paths should remain available.""" from astrbot.core.provider.entities import ProviderType, RerankResult from astrbot.core.provider.provider import ( EmbeddingProvider, Provider, RerankProvider, ) assert Provider is not None assert EmbeddingProvider is not None assert RerankProvider is not None assert ProviderType.CHAT_COMPLETION.value == "chat_completion" assert RerankResult(index=1, relevance_score=0.5).index == 1 def test_legacy_astrbot_core_agent_and_db_imports(self): """astrbot.core.agent/db old import paths should remain available.""" from astrbot.core.agent.message import TextPart from astrbot.core.db.po import Personality assert TextPart(text="hi").text == "hi" assert Personality is not None def test_legacy_astrbot_event_filter_module_exports(self): """astrbot.api.event.filter should be importable from the old module path.""" from astrbot.api.event.filter import EventMessageType, command, llm_tool assert EventMessageType is not None assert callable(command) assert callable(llm_tool) def test_legacy_astrbot_platform_exports(self): """astrbot.api.platform should expose the common legacy platform types.""" from astrbot.api.platform import ( AstrBotMessage, AstrMessageEvent, MessageType, Platform, PlatformMetadata, register_platform_adapter, ) assert AstrBotMessage is not None assert AstrMessageEvent is not None assert MessageType is not None assert Platform is not None assert PlatformMetadata is not None with pytest.raises(NotImplementedError, match="register_platform_adapter"): register_platform_adapter() def test_legacy_astrbot_provider_exports(self): """astrbot.api.provider should expose the common legacy provider types.""" from astrbot.api.provider import ( LLMResponse, Provider, ProviderMetaData, ProviderRequest, ProviderType, STTProvider, ) meta = ProviderMetaData(id="demo") req = ProviderRequest(prompt="hello") assert LLMResponse is not None assert Provider is not None assert STTProvider is not None assert meta.id == "demo" assert req.prompt == "hello" assert ProviderType.CHAT_COMPLETION.value == "chat_completion" def test_legacy_astrbot_api_all_exports(self): """astrbot.api.all should remain importable from the old umbrella module.""" from astrbot.api.all import ( AstrMessageEvent, Context, LLMResponse, MessageChain, command, llm_tool, register, sp, ) assert AstrMessageEvent is not None assert Context is not None assert LLMResponse is not None assert MessageChain is not None assert callable(command) assert callable(llm_tool) assert callable(register) assert sp is not None def test_legacy_astrbot_util_exports(self): """astrbot.api.util should expose the waiter helpers from the old path.""" from astrbot.api.util import SessionController, SessionWaiter, session_waiter assert SessionController is not None assert callable(SessionWaiter.trigger) assert callable(session_waiter) @pytest.mark.asyncio async def test_legacy_astrbot_sp_roundtrip(self): """astrbot.api.sp should provide a usable in-memory compat store.""" from astrbot.api import sp await sp.global_put("feature_flag", True) assert await sp.global_get("feature_flag", False) is True await sp.session_put("umo:test", "counter", 3) assert await sp.session_get("umo:test", "counter", 0) == 3 await sp.session_remove("umo:test", "counter") assert await sp.session_get("umo:test", "counter", 0) == 0