From 060f471cb9980234621e8ccdc3fe362e854c0529 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 11 Jun 2026 00:53:10 -0400 Subject: [PATCH] test(qwen): red phase for Qwen via DashScope (5 failing tests) 5 failing tests in tests/test_qwen_provider.py that establish the core behaviors of the new Qwen (DashScope) provider: 1. test_send_qwen_routes_to_dashscope: _send_qwen calls _ensure_qwen_client and _dashscope_call, returns the text from the DashScope response 2. test_qwen_vision_vl_model_accepts_image: when file_items contains an image, the messages passed to _dashscope_call include the image ref 3. test_qwen_tool_format_translation: build_dashscope_tools converts OpenAI-shaped tool dicts to DashScope shape (name/description/parameters flat structure, not wrapped in function:) 4. test_qwen_error_classification: classify_dashscope_error maps dashscope.common.error.InvalidApiKey -> ProviderError(kind='auth', provider='qwen') 5. test_list_qwen_models_returns_hardcoded_registry: _list_qwen_models returns the 7 Qwen models registered in src/vendor_capabilities.py The autouse _reset_qwen_state fixture uses hasattr() so it is a no-op when _qwen_client / _qwen_history do not exist (yet); this keeps the fixture working in the Red phase. All 5 tests fail: - Tests 1, 2: AttributeError: src.ai_client has no _ensure_qwen_client / _send_qwen / _dashscope_call - Tests 3, 4: ModuleNotFoundError: No module named src.qwen_adapter - Test 5: ImportError: cannot import name _list_qwen_models Test signature adapted to match the real _send_minimax signature at src/ai_client.py:2143-2148 (10 params, no enable_tools / rag_engine) rather than the plan's 12-param signature. Next: Green phase - implement src/qwen_adapter.py + src/ai_client.py state + _ensure_qwen_client + _send_qwen + _list_qwen_models. --- tests/test_qwen_provider.py | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/test_qwen_provider.py diff --git a/tests/test_qwen_provider.py b/tests/test_qwen_provider.py new file mode 100644 index 00000000..75345a02 --- /dev/null +++ b/tests/test_qwen_provider.py @@ -0,0 +1,55 @@ +from unittest.mock import MagicMock, patch +import pytest +from src import ai_client + +@pytest.fixture(autouse=True) +def _reset_qwen_state(): + if hasattr(ai_client, '_qwen_client'): + ai_client._qwen_client = None + if hasattr(ai_client, '_qwen_history'): + ai_client._qwen_history = [] + yield + +def test_send_qwen_routes_to_dashscope(monkeypatch: pytest.MonkeyPatch) -> None: + ai_client.set_provider("qwen", "qwen-max") + with patch("src.ai_client._ensure_qwen_client") as ensure, \ + patch("src.ai_client._dashscope_call", return_value={"text": "hi from qwen", "tool_calls": [], "usage": {"input_tokens": 10, "output_tokens": 5}}) as call: + result = ai_client._send_qwen("system", "user", ".", None, "", False, None, None, None) + assert result == "hi from qwen" + call.assert_called_once() + ensure.assert_called_once() + +def test_qwen_vision_vl_model_accepts_image(monkeypatch: pytest.MonkeyPatch) -> None: + ai_client.set_provider("qwen", "qwen-vl-max") + with patch("src.ai_client._ensure_qwen_client"), \ + patch("src.ai_client._dashscope_call", return_value={"text": "I see a cat", "tool_calls": [], "usage": {"input_tokens": 10, "output_tokens": 5}}) as call: + file_items = [{"path": "/tmp/cat.png", "is_image": True, "base64_data": "iVBOR..."}] + result = ai_client._send_qwen("system", "describe this image", ".", file_items, "", False, None, None, None) + assert "cat" in result.lower() + kwargs = call.call_args.kwargs + msgs_str = str(kwargs.get("messages", [])).lower() + assert "image" in msgs_str or "cat.png" in msgs_str + +def test_qwen_tool_format_translation() -> None: + from src.qwen_adapter import build_dashscope_tools + openai_tools = [{"type": "function", "function": {"name": "read_file", "description": "Read a file", "parameters": {"type": "object", "properties": {"path": {"type": "string"}}}}}] + ds_tools = build_dashscope_tools(openai_tools) + assert len(ds_tools) == 1 + assert ds_tools[0]["name"] == "read_file" + assert "parameters" in ds_tools[0] + +def test_qwen_error_classification() -> None: + from src.ai_client import ProviderError + from src.qwen_adapter import classify_dashscope_error + import dashscope + err = classify_dashscope_error(dashscope.common.error.InvalidApiKey("bad key")) + assert err.kind == "auth" + assert err.provider == "qwen" + +def test_list_qwen_models_returns_hardcoded_registry() -> None: + from src.ai_client import _list_qwen_models + models = _list_qwen_models() + assert "qwen-max" in models + assert "qwen-vl-max" in models + assert "qwen-turbo" in models + assert "qwen-audio" in models