From 227373914fa59a3a72eba051662d7f502991e5c2 Mon Sep 17 00:00:00 2001 From: worldmozara Date: Tue, 14 Jul 2026 17:25:22 +0800 Subject: [PATCH] test/memory+runtime: cover store and client factory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable pytest-asyncio auto mode; assert default user, message round-trip, and provider→client mapping including errors. --- pyproject.toml | 5 ++ tests/test_memory/test_store.py | 61 +++++++++++++++++++++++ tests/test_runtime/test_client_factory.py | 60 ++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 tests/test_memory/test_store.py create mode 100644 tests/test_runtime/test_client_factory.py diff --git a/pyproject.toml b/pyproject.toml index 01340b3..f959ee9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,11 @@ reportExplicitAny = "hint" reportImplicitStringConcatenation = "hint" reportImportCycles = "hint" +[tool.pytest.ini_options] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" +testpaths = ["tests"] + [tool.pdm] distribution = true diff --git a/tests/test_memory/test_store.py b/tests/test_memory/test_store.py new file mode 100644 index 0000000..9d5e0bc --- /dev/null +++ b/tests/test_memory/test_store.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from plyngent.config.models import DatabaseConfig +from plyngent.lmproto.openai_compatible.model import AssistantChatMessage, UserChatMessage +from plyngent.memory import DEFAULT_USER_NAME, MemoryStore + +if TYPE_CHECKING: + from collections.abc import AsyncIterator + + +@pytest.fixture +async def store() -> AsyncIterator[MemoryStore]: + memory = await MemoryStore.open(DatabaseConfig()) + yield memory + await memory.close() + + +async def test_open_creates_default_user(store: MemoryStore) -> None: + user = await store.get_user_by_name(DEFAULT_USER_NAME) + assert user is not None + assert user.name == DEFAULT_USER_NAME + + +async def test_create_session_uses_default_user(store: MemoryStore) -> None: + session = await store.create_session(name="chat-1") + assert session.sid is not None + assert session.name == "chat-1" + user = await store.get_user_by_name(DEFAULT_USER_NAME) + assert user is not None + assert session.uid == user.uid + + +async def test_append_and_list_messages(store: MemoryStore) -> None: + session = await store.create_session() + user_msg = UserChatMessage(content="hello") + assistant_msg = AssistantChatMessage(content="hi") + + row0 = await store.append_message(session.sid, user_msg) + row1 = await store.append_message(session.sid, assistant_msg) + assert row0.seq == 0 + assert row1.seq == 1 + + messages = await store.list_messages(session.sid) + assert len(messages) == 2 # noqa: PLR2004 + assert isinstance(messages[0], UserChatMessage) + assert messages[0].content == "hello" + assert isinstance(messages[1], AssistantChatMessage) + assert messages[1].content == "hi" + + +async def test_list_sessions(store: MemoryStore) -> None: + s1 = await store.create_session(name="a") + s2 = await store.create_session(name="b") + sessions = await store.list_sessions() + ids = {s.sid for s in sessions} + assert s1.sid in ids + assert s2.sid in ids diff --git a/tests/test_runtime/test_client_factory.py b/tests/test_runtime/test_client_factory.py new file mode 100644 index 0000000..2e02cc2 --- /dev/null +++ b/tests/test_runtime/test_client_factory.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +import pytest + +from plyngent.config.models import ( + AnthropicProvider, + DeepseekProvider, + OpenAICompatibleProvider, + OpenAIProvider, +) +from plyngent.lmproto.deepseek import DeepseekOpenAIClient +from plyngent.lmproto.openai_compatible import OpenAIClient +from plyngent.runtime import ProviderNotSupportedError, create_client, provider_to_openai_config + + +def test_openai_provider_defaults_base_url() -> None: + provider = OpenAIProvider(access_key_or_token="sk-test") + config = provider_to_openai_config(provider) + assert config.access_key_or_token == "sk-test" + assert config.base_url == "https://api.openai.com/v1" + client = create_client(provider) + assert isinstance(client, OpenAIClient) + + +def test_openai_compatible_requires_url() -> None: + provider = OpenAICompatibleProvider(access_key_or_token="sk-test") + with pytest.raises(ProviderNotSupportedError, match="url"): + _ = create_client(provider) + + +def test_openai_compatible_client() -> None: + provider = OpenAICompatibleProvider( + access_key_or_token="sk-test", + url="https://example.com/v1", + ) + client = create_client(provider) + assert isinstance(client, OpenAIClient) + assert provider_to_openai_config(provider).base_url == "https://example.com/v1" + + +def test_deepseek_openai_convention() -> None: + provider = DeepseekProvider(access_key_or_token="sk-test") + client = create_client(provider) + assert isinstance(client, DeepseekOpenAIClient) + assert provider_to_openai_config(provider).base_url == "https://api.deepseek.com/v1" + + +def test_deepseek_anthropic_convention_not_implemented() -> None: + provider = DeepseekProvider( + access_key_or_token="sk-test", + extras={"convention": "anthropic"}, + ) + with pytest.raises(ProviderNotSupportedError, match="anthropic"): + _ = create_client(provider) + + +def test_anthropic_not_implemented() -> None: + provider = AnthropicProvider(access_key_or_token="sk-test") + with pytest.raises(ProviderNotSupportedError, match="anthropic"): + _ = create_client(provider)