Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added db_utils tests #53

Merged
merged 5 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ jobs:
mypy src/ swarm_copy/
# Include src/ directory in Python path to prioritize local files in pytest
export PYTHONPATH=$(pwd)/src:$PYTHONPATH
pytest --color=yes
pytest --color=yes tests/ swarm_copy_tests/
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Tool implementations without langchain or langgraph dependencies
- CRUDs.
- BlueNaas CRUD tools
- Unit tests for database

### Fixed
- Migrate LLM Evaluation logic to scripts and add tests
Expand Down
10 changes: 6 additions & 4 deletions swarm_copy/tools/bluenaas_memodel_getall.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ class InputMEModelGetAll(BaseModel):
page_size: int = Field(
default=20, description="Number of results returned by the API."
)
model_type: Literal["single-neuron-simulation", "synaptome-simulation"] = Field(
kanesoban marked this conversation as resolved.
Show resolved Hide resolved
default="single-neuron-simulation",
description="Type of simulation to retrieve.",
simulation_type: Literal["single-neuron-simulation", "synaptome-simulation"] = (
Field(
default="single-neuron-simulation",
description="Type of simulation to retrieve.",
)
)


Expand All @@ -55,7 +57,7 @@ async def arun(self) -> PaginatedResponseUnionMEModelResponseSynaptomeModelRespo
response = await self.metadata.httpx_client.get(
url=f"{self.metadata.bluenaas_url}/neuron-model/{self.metadata.vlab_id}/{self.metadata.project_id}/me-models",
params={
"simulation_type": self.input_schema.model_type,
"simulation_type": self.input_schema.simulation_type,
"offset": self.input_schema.offset,
"page_size": self.input_schema.page_size,
},
Expand Down
4 changes: 2 additions & 2 deletions swarm_copy/tools/bluenaas_memodel_getone.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class MEModelGetOneMetadata(BaseMetadata):
class InputMEModelGetOne(BaseModel):
"""Inputs for the BlueNaaS single-neuron simulation."""

model_id: str = Field(
simulation_id: str = Field(
description="ID of the model to retrieve. Should be an https link."
)

Expand All @@ -45,7 +45,7 @@ async def arun(self) -> MEModelResponse:
)

response = await self.metadata.httpx_client.get(
url=f"{self.metadata.bluenaas_url}/neuron-model/{self.metadata.vlab_id}/{self.metadata.project_id}/{quote_plus(self.input_schema.model_id)}",
url=f"{self.metadata.bluenaas_url}/neuron-model/{self.metadata.vlab_id}/{self.metadata.project_id}/{quote_plus(self.input_schema.simulation_id)}",
headers={"Authorization": f"Bearer {self.metadata.token}"},
)

Expand Down
3 changes: 2 additions & 1 deletion swarm_copy/tools/traces_tool.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Traces tool."""

import logging
from pathlib import Path
from typing import Any, ClassVar

from pydantic import BaseModel, Field
Expand Down Expand Up @@ -46,7 +47,7 @@ class GetTracesMetadata(BaseMetadata):
knowledge_graph_url: str
token: str
trace_search_size: int
brainregion_path: str
brainregion_path: str | Path


class GetTracesTool(BaseTool):
Expand Down
1 change: 1 addition & 0 deletions swarm_copy_tests/app/database/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Unit tests for database."""
97 changes: 97 additions & 0 deletions swarm_copy_tests/app/database/test_db_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import json
from unittest.mock import AsyncMock, Mock, patch

import pytest
from fastapi import HTTPException

from swarm_copy.app.database.db_utils import get_thread, save_history, get_history
from swarm_copy.app.database.sql_schemas import Entity, Messages, Threads


@pytest.mark.asyncio
async def test_get_thread():
user_id = "0"
thread_id = "0"
mock_thread_result = Mock()
mock_scalars_return = Mock()
mock_thread = Mock()
mock_scalars_return.one_or_none.return_value = mock_thread
mock_thread_result.scalars.return_value = mock_scalars_return
mock_session = AsyncMock()
mock_session.execute.return_value = mock_thread_result
result = await get_thread(user_id=user_id, thread_id=thread_id, session=mock_session)
assert result == mock_thread
kanesoban marked this conversation as resolved.
Show resolved Hide resolved


@pytest.mark.asyncio
async def test_get_thread_exception():
user_id = "0"
thread_id = "0"
mock_thread_result = Mock()
mock_scalars_return = Mock()
mock_scalars_return.one_or_none.return_value = None
mock_thread_result.scalars.return_value = mock_scalars_return
mock_session = AsyncMock()
mock_session.execute.return_value = mock_thread_result
with pytest.raises(HTTPException):
await get_thread(user_id=user_id, thread_id=thread_id, session=mock_session)
kanesoban marked this conversation as resolved.
Show resolved Hide resolved


@pytest.mark.parametrize("message_role,expected_entity,content", [
('user', Entity.USER, False),
('tool', Entity.TOOL, False),
('assistant', Entity.AI_MESSAGE, True),
('assistant', Entity.AI_TOOL, False)
])
@pytest.mark.asyncio
async def test_save_history(message_role, expected_entity, content):
history = [{"role": message_role, "content": content}]
user_id, thread_id, offset = "test_user", "test_thread", 0

mock_session = AsyncMock()
mock_thread = AsyncMock()

async def mock_get_thread(**kwargs):
return mock_thread

with patch("swarm_copy.app.database.db_utils.get_thread", mock_get_thread):
await save_history(history, user_id, thread_id, offset, mock_session)

assert mock_session.add.called

called_with_param = mock_session.add.call_args[0][0]
assert isinstance(called_with_param, Messages)
assert called_with_param.order == 0
assert called_with_param.thread_id == thread_id
assert called_with_param.entity == expected_entity
assert called_with_param.content == json.dumps(history[0])

assert mock_session.commit.called


@pytest.mark.asyncio
async def test_save_history_exception():
history = [{"role": "bad role", "content": None}]
user_id, thread_id, offset = "test_user", "test_thread", 0

mock_session = AsyncMock()

with pytest.raises(HTTPException):
await save_history(history, user_id, thread_id, offset, mock_session)


@pytest.mark.asyncio
async def test_get_history():
msg1 = Mock()
msg1.content = json.dumps("message1")
msg2 = Mock()
msg2.content = json.dumps("message2")
mock_thread = AsyncMock()
messages = [msg1, msg2]

async def mock_messages():
return messages

mock_thread.awaitable_attrs.messages = mock_messages()
results = await get_history(mock_thread)
assert results == [json.loads(msg.content) for msg in messages]
Loading