Files
awoooi/apps/api/tests/test_nvidia_chat.py
OG T 04bfff9d19 refactor(ai): 模組化重構 - NVIDIA chat 移至 NvidiaProvider
符合 feedback_lewooogo_modular_enforcement.md 規範:
- 移除 openclaw.py 中的 _call_nvidia() (重複邏輯)
- 新增 NvidiaProvider.chat() 方法
- 更新 INvidiaProvider Protocol
- openclaw.py 改用 get_nvidia_provider().chat()
- 測試移至 test_nvidia_chat.py

架構層次:
- Router → Service → Provider (正確)
- 禁止 Service 層重複實作已存在的 Provider 功能

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-29 20:49:23 +08:00

160 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
test_nvidia_chat.py - NvidiaProvider.chat() 測試
2026-03-29 ogt: 模組化重構 - 測試移至 NvidiaProvider
符合 feedback_lewooogo_modular_enforcement.md 規範
測試策略 (遵循 feedback_no_mock_testing.md):
- 使用真實 NVIDIA API (需 NVIDIA_API_KEY)
- 跳過條件: 無 API Key 時跳過
"""
import json
import os
import pytest
from src.core.config import get_settings
from src.services.nvidia_provider import NvidiaProvider, get_nvidia_provider
settings = get_settings()
@pytest.fixture
def nvidia_provider():
"""建立 NvidiaProvider 實例"""
return NvidiaProvider()
@pytest.mark.asyncio
@pytest.mark.skipif(
not os.getenv("NVIDIA_API_KEY") and not settings.NVIDIA_API_KEY,
reason="NVIDIA_API_KEY not configured",
)
async def test_chat_success(nvidia_provider):
"""
測試 chat() 成功回應
驗證:
- 回應格式正確 (4-tuple)
- success = True
- total_tokens > 0
- cost_usd = 0 (免費 tier)
"""
prompt = """你是一個 JSON 產生器。請回傳以下格式:
{"status": "ok", "message": "test"}
只回傳 JSON不要其他內容。"""
response, success, total_tokens, cost_usd = await nvidia_provider.chat(prompt)
assert success is True, f"Expected success, got error: {response}"
assert isinstance(response, str)
assert len(response) > 0
assert total_tokens > 0, "Expected token count > 0"
assert cost_usd == 0.0, "Expected $0 for free tier"
@pytest.mark.asyncio
async def test_chat_no_api_key():
"""
測試無 API Key 時的處理
驗證:
- success = False
- 回傳適當錯誤訊息
"""
# 建立沒有 API Key 的 provider
provider = NvidiaProvider(api_key=None)
response, success, total_tokens, cost_usd = await provider.chat("test")
assert success is False
assert "not configured" in response.lower()
assert total_tokens == 0
assert cost_usd == 0.0
@pytest.mark.asyncio
@pytest.mark.skipif(
not os.getenv("NVIDIA_API_KEY") and not settings.NVIDIA_API_KEY,
reason="NVIDIA_API_KEY not configured",
)
async def test_chat_json_response(nvidia_provider):
"""
測試 JSON 格式回應
驗證:
- 回應是有效 JSON
"""
prompt = """回傳一個 JSON 物件,包含:
- action: "NO_ACTION"
- reason: "測試"
只回傳 JSON。"""
response, success, _, _ = await nvidia_provider.chat(prompt)
assert success is True
# 驗證是有效 JSON
try:
data = json.loads(response)
assert isinstance(data, dict)
except json.JSONDecodeError:
pytest.fail(f"Response is not valid JSON: {response[:200]}")
@pytest.mark.asyncio
@pytest.mark.skipif(
not os.getenv("NVIDIA_API_KEY") and not settings.NVIDIA_API_KEY,
reason="NVIDIA_API_KEY not configured",
)
async def test_chat_uses_model_registry(nvidia_provider):
"""
測試使用 ModelRegistry 取得模型
驗證:
- 使用 models.json 中定義的 rca 模型
"""
from src.services.model_registry import get_model_registry
registry = get_model_registry()
expected_model = registry.get_model("nvidia", "rca")
# 模型應該是 llama-3.1-nemotron-70b-instruct
assert "nemotron" in expected_model.lower()
assert "70b" in expected_model or "mini" in expected_model
@pytest.mark.asyncio
def test_get_nvidia_provider_singleton():
"""
測試單例模式
驗證:
- get_nvidia_provider() 返回同一實例
"""
provider1 = get_nvidia_provider()
provider2 = get_nvidia_provider()
assert provider1 is provider2
@pytest.mark.asyncio
@pytest.mark.skipif(
not os.getenv("NVIDIA_API_KEY") and not settings.NVIDIA_API_KEY,
reason="NVIDIA_API_KEY not configured",
)
async def test_chat_includes_otel_tracing(nvidia_provider):
"""
測試 OTEL 追蹤整合
驗證:
- chat() 執行時有 OTEL span
"""
# 這個測試主要驗證代碼不會拋出異常
prompt = '{"test": true}'
response, success, _, _ = await nvidia_provider.chat(prompt)
# 只要沒有拋出異常就算通過
assert isinstance(response, str)