refactor: isolate database migrations and model defaults

This commit is contained in:
kandrusyak
2026-07-27 17:51:16 +03:00
parent ab029a4c67
commit c20d893255
7 changed files with 238 additions and 311 deletions

View File

@@ -12,10 +12,21 @@ from assistant_bot.agent import (
parse_agent_decision,
)
from assistant_bot.agent_tools import AGENT_TOOLS
from assistant_bot.migrations import LATEST_SCHEMA_VERSION
from assistant_bot.reminders import parse_reminder, unit_to_timedelta
from assistant_bot.storage import AssistantStorage
DEFAULT_MODELS = {
"local": "qwen3.5:9b",
"yandex": "gpt://folder/yandexgpt/latest",
}
def create_storage(path: Path) -> AssistantStorage:
return AssistantStorage(path, default_models=DEFAULT_MODELS)
class ReminderParserTests(unittest.TestCase):
def test_supported_relative_unit(self) -> None:
delta = unit_to_timedelta(2, "часа")
@@ -89,7 +100,7 @@ class AgentToolContractTests(unittest.TestCase):
def test_crud_tool_result_shapes_remain_stable(self) -> None:
with tempfile.TemporaryDirectory() as directory:
storage = AssistantStorage(Path(directory) / "assistant.sqlite3")
storage = create_storage(Path(directory) / "assistant.sqlite3")
common = {
"storage": storage,
"user_id": 42,
@@ -187,7 +198,7 @@ class AgentToolContractTests(unittest.TestCase):
result = execute_agent_tool(
"missing",
{},
AssistantStorage(Path(directory) / "assistant.sqlite3"),
create_storage(Path(directory) / "assistant.sqlite3"),
user_id=42,
chat_id=100,
tz=ZoneInfo("UTC"),
@@ -200,15 +211,27 @@ class AgentToolContractTests(unittest.TestCase):
class StorageTests(unittest.TestCase):
def test_new_database_uses_latest_schema_version(self) -> None:
with tempfile.TemporaryDirectory() as directory:
database_path = Path(directory) / "assistant.sqlite3"
create_storage(database_path)
with closing(sqlite3.connect(database_path)) as connection:
version = connection.execute(
"PRAGMA user_version"
).fetchone()[0]
self.assertEqual(version, LATEST_SCHEMA_VERSION)
def test_user_authorization_is_persisted(self) -> None:
with tempfile.TemporaryDirectory() as directory:
database_path = Path(directory) / "assistant.sqlite3"
storage = AssistantStorage(database_path)
storage = create_storage(database_path)
self.assertFalse(storage.is_user_authorized(42))
storage.authorize_user(42)
reopened_storage = AssistantStorage(database_path)
reopened_storage = create_storage(database_path)
self.assertTrue(reopened_storage.is_user_authorized(42))
self.assertFalse(reopened_storage.is_user_authorized(43))
@@ -227,14 +250,32 @@ class StorageTests(unittest.TestCase):
)
connection.commit()
storage = AssistantStorage(database_path)
storage = create_storage(database_path)
storage.set_user_model(42, "yandexgpt", "yandex")
self.assertEqual(storage.get_user_model(42, "yandex"), "yandexgpt")
with closing(sqlite3.connect(database_path)) as connection:
version = connection.execute(
"PRAGMA user_version"
).fetchone()[0]
self.assertEqual(version, LATEST_SCHEMA_VERSION)
def test_default_models_are_injected(self) -> None:
with tempfile.TemporaryDirectory() as directory:
storage = create_storage(Path(directory) / "assistant.sqlite3")
self.assertEqual(
storage.get_user_model(42, "local"),
DEFAULT_MODELS["local"],
)
self.assertEqual(
storage.get_user_model(42, "yandex"),
DEFAULT_MODELS["yandex"],
)
def test_provider_models_are_stored_separately(self) -> None:
with tempfile.TemporaryDirectory() as directory:
storage = AssistantStorage(Path(directory) / "assistant.sqlite3")
storage = create_storage(Path(directory) / "assistant.sqlite3")
storage.set_user_model(42, "qwen3.5:9b", "local")
storage.set_user_model(42, "yandexgpt", "yandex")
@@ -244,7 +285,7 @@ class StorageTests(unittest.TestCase):
def test_memory_crud(self) -> None:
with tempfile.TemporaryDirectory() as directory:
storage = AssistantStorage(Path(directory) / "assistant.sqlite3")
storage = create_storage(Path(directory) / "assistant.sqlite3")
memory_id = storage.add_memory(42, "короткие ответы")
self.assertEqual(storage.list_memories(42)[0]["id"], memory_id)
@@ -253,7 +294,7 @@ class StorageTests(unittest.TestCase):
def test_new_context_preserves_searchable_archive(self) -> None:
with tempfile.TemporaryDirectory() as directory:
storage = AssistantStorage(Path(directory) / "assistant.sqlite3")
storage = create_storage(Path(directory) / "assistant.sqlite3")
storage.add_conversation_exchange(
42,
100,