Files
wehub-resource-sync 5296d0e97c
CI / Ban suppressions and legacy annotations (push) Has been cancelled
CI / pytest (push) Has been cancelled
CI / ruff-check (push) Has been cancelled
CI / ruff-format (push) Has been cancelled
CI / ty (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:44 +08:00

157 lines
4.6 KiB
Python

"""Tests for Mistral Codestral provider."""
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from free_claude_code.config.provider_catalog import CODESTRAL_DEFAULT_BASE
from free_claude_code.providers.base import ProviderConfig
from tests.providers.request_factory import make_messages_request
from tests.providers.support import passthrough_rate_limiter, profiled_provider
def make_request(**overrides):
return make_messages_request("devstral-small-latest", **overrides)
@pytest.fixture
def codestral_config():
return ProviderConfig(
api_key="test_codestral_key",
base_url=CODESTRAL_DEFAULT_BASE,
rate_limit=10,
rate_window=60,
enable_thinking=True,
)
@pytest.fixture
def codestral_provider(codestral_config):
return profiled_provider(
"mistral_codestral", codestral_config, rate_limiter=passthrough_rate_limiter()
)
def test_init(codestral_config):
"""Test provider initialization."""
with patch(
"free_claude_code.providers.openai_chat.provider.AsyncOpenAI"
) as mock_openai:
provider = profiled_provider(
"mistral_codestral",
codestral_config,
rate_limiter=passthrough_rate_limiter(),
)
assert provider._api_key == "test_codestral_key"
assert provider._base_url == CODESTRAL_DEFAULT_BASE
mock_openai.assert_called_once()
def test_default_base_url():
assert CODESTRAL_DEFAULT_BASE == "https://codestral.mistral.ai/v1"
def test_build_request_body_basic(codestral_provider):
"""Basic request body conversion works for Codestral."""
req = make_request()
body = codestral_provider._build_request_body(req)
assert body["model"] == "devstral-small-latest"
assert body["messages"][0]["role"] == "system"
def test_build_request_body_global_disable_blocks_reasoning_mapping():
"""Global disable disables reasoning replay in the converter."""
provider = profiled_provider(
"mistral_codestral",
ProviderConfig(
api_key="test_codestral_key",
base_url=CODESTRAL_DEFAULT_BASE,
rate_limit=10,
rate_window=60,
enable_thinking=False,
),
rate_limiter=passthrough_rate_limiter(),
)
req = make_request()
body = provider._build_request_body(req)
roles = [m.get("role") for m in body.get("messages", [])]
assert "assistant_reasoning_content" not in roles
@pytest.mark.asyncio
async def test_stream_response_text(codestral_provider):
"""Text content deltas are emitted as text blocks."""
req = make_request()
mock_chunk = MagicMock()
mock_chunk.choices = [
MagicMock(
delta=MagicMock(
content="Hello back!",
reasoning_content=None,
tool_calls=None,
),
finish_reason="stop",
)
]
mock_chunk.usage = MagicMock(completion_tokens=5, prompt_tokens=10)
async def mock_stream():
yield mock_chunk
with patch.object(
codestral_provider._client.chat.completions, "create", new_callable=AsyncMock
) as mock_create:
mock_create.return_value = mock_stream()
events = [event async for event in codestral_provider.stream_response(req)]
assert any(
'"text_delta"' in event and "Hello back!" in event for event in events
)
@pytest.mark.asyncio
async def test_stream_response_reasoning_content(codestral_provider):
"""reasoning_content deltas are emitted as thinking blocks."""
req = make_request()
mock_chunk = MagicMock()
mock_chunk.choices = [
MagicMock(
delta=MagicMock(
content=None,
reasoning_content="Thinking...",
tool_calls=None,
),
finish_reason="stop",
)
]
mock_chunk.usage = MagicMock(completion_tokens=2, prompt_tokens=10)
async def mock_stream():
yield mock_chunk
with patch.object(
codestral_provider._client.chat.completions, "create", new_callable=AsyncMock
) as mock_create:
mock_create.return_value = mock_stream()
events = [event async for event in codestral_provider.stream_response(req)]
assert any(
'"thinking_delta"' in event and "Thinking..." in event for event in events
)
@pytest.mark.asyncio
async def test_cleanup(codestral_provider):
"""cleanup closes the OpenAI client."""
codestral_provider._client = AsyncMock()
await codestral_provider.cleanup()
codestral_provider._client.close.assert_called_once()