Skip to content

Commit 06c538c

Browse files
committed
add tests
1 parent 3a2726c commit 06c538c

13 files changed

Lines changed: 2710 additions & 7 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- uses: actions/checkout@v4
1414
- uses: astral-sh/setup-uv@v3
1515
- run: uv sync --dev
16-
- run: uv run ruff check src/metorial/ examples/ tests/
16+
- run: uv run ruff check src/metorial/ tests/
1717

1818
format:
1919
runs-on: ubuntu-latest

.gitignore

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,9 @@ cython_debug/
185185
.abstra/
186186

187187
# Visual Studio Code
188-
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
188+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
189189
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
190-
# and can be added to the global gitignore or merged into this file. However, if you prefer,
190+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
191191
# you could uncomment the following to ignore the entire vscode folder
192192
# .vscode/
193193

@@ -211,5 +211,4 @@ __marimo__/
211211

212212
# Project-specific
213213
playground/
214-
tests/
215-
pytest.ini
214+
pytest.ini

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ extend-exclude = '''
8484
)/
8585
'''
8686

87-
# MyPy configuration (WorkOS pattern - strict mode)
87+
# MyPy configuration
8888
[tool.mypy]
8989
python_version = "3.10"
9090
strict = true

src/metorial/providers/google.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ async def call_google_tools(
8585
continue
8686

8787
try:
88-
result = await tool_mgr.execute_tool(str(call_name), args)
88+
result = await tool_mgr.execute_tool(str(call_name), cast(dict[str, Any], args))
8989
if hasattr(result, "model_dump"):
9090
result = result.model_dump()
9191
except Exception as e:

tests/conftest.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
"""
2+
Shared test fixtures and configuration for metorial tests.
3+
4+
Includes sync/async client parametrization pattern.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
from typing import TYPE_CHECKING, Literal
10+
from unittest.mock import AsyncMock, MagicMock
11+
12+
import pytest
13+
14+
if TYPE_CHECKING:
15+
from metorial import Metorial, MetorialSync
16+
17+
18+
# --------------------------------------------------------------------------
19+
# Pytest configuration
20+
# --------------------------------------------------------------------------
21+
22+
23+
def pytest_configure(config: pytest.Config) -> None:
24+
"""Register custom markers."""
25+
config.addinivalue_line(
26+
"markers",
27+
"sync_and_async: mark test to run with both sync and async clients",
28+
)
29+
30+
31+
# --------------------------------------------------------------------------
32+
# Sync/Async client parametrization
33+
# --------------------------------------------------------------------------
34+
35+
36+
@pytest.fixture(params=["sync", "async"])
37+
def client_type(request: pytest.FixtureRequest) -> Literal["sync", "async"]:
38+
"""Parametrize tests to run with both sync and async clients."""
39+
return request.param
40+
41+
42+
@pytest.fixture
43+
def metorial_client(
44+
client_type: Literal["sync", "async"],
45+
mock_metorial_config: dict[str, str],
46+
) -> Metorial | MetorialSync:
47+
"""Create a Metorial client based on client_type fixture.
48+
49+
This allows tests marked with @pytest.mark.sync_and_async to run
50+
automatically with both sync and async clients.
51+
"""
52+
if client_type == "sync":
53+
from metorial import MetorialSync
54+
55+
return MetorialSync(api_key=mock_metorial_config["apiKey"])
56+
else:
57+
from metorial import Metorial
58+
59+
return Metorial(api_key=mock_metorial_config["apiKey"])
60+
61+
62+
@pytest.fixture
63+
def async_metorial_client(mock_metorial_config: dict[str, str]) -> Metorial:
64+
"""Create an async-only Metorial client for async-specific tests."""
65+
from metorial import Metorial
66+
67+
return Metorial(api_key=mock_metorial_config["apiKey"])
68+
69+
70+
@pytest.fixture
71+
def sync_metorial_client(mock_metorial_config: dict[str, str]) -> MetorialSync:
72+
"""Create a sync-only Metorial client for sync-specific tests."""
73+
from metorial import MetorialSync
74+
75+
return MetorialSync(api_key=mock_metorial_config["apiKey"])
76+
77+
78+
# --------------------------------------------------------------------------
79+
# Mock fixtures
80+
# --------------------------------------------------------------------------
81+
82+
83+
@pytest.fixture
84+
def mock_tool_manager() -> MagicMock:
85+
"""Mock tool manager for testing."""
86+
manager = MagicMock()
87+
manager.get_tools.return_value = []
88+
manager.call_tool = AsyncMock(return_value={"content": "result"})
89+
manager.get_tool.return_value = None
90+
return manager
91+
92+
93+
@pytest.fixture
94+
def mock_metorial_config() -> dict[str, str]:
95+
"""Mock configuration for testing."""
96+
return {
97+
"apiKey": "test-api-key",
98+
"apiHost": "https://api.metorial.com",
99+
"mcpHost": "https://mcp.metorial.com",
100+
}
101+
102+
103+
@pytest.fixture
104+
def mock_mcp_tool() -> MagicMock:
105+
"""Mock MCP tool object."""
106+
tool = MagicMock()
107+
tool.name = "test_tool"
108+
tool.description = "A test tool"
109+
tool.parameters = {"type": "object", "properties": {"param1": {"type": "string"}}}
110+
return tool
111+
112+
113+
@pytest.fixture
114+
def mock_mcp_session() -> MagicMock:
115+
"""Mock MCP session for testing."""
116+
session = MagicMock()
117+
session.get_tool_manager = AsyncMock()
118+
session.close = AsyncMock()
119+
return session
120+
121+
122+
@pytest.fixture
123+
def mock_http_response() -> MagicMock:
124+
"""Mock HTTP response for testing RawResponse."""
125+
response = MagicMock()
126+
response.status_code = 200
127+
response.headers = {
128+
"X-Request-ID": "req-test-123",
129+
"Content-Type": "application/json",
130+
}
131+
return response

0 commit comments

Comments
 (0)