""" tests/test_deepseek_service.py ───────────────────────────────────────────────────────────────── Operation Ollama-First v5.0 / Phase 15 — DeepSeek 直連 service 驗證 """ from unittest.mock import patch, MagicMock import pytest @pytest.fixture(autouse=True) def _reset_env(monkeypatch): """每 test 清 env""" monkeypatch.delenv('DEEPSEEK_DIRECT_ENABLED', raising=False) monkeypatch.delenv('DEEPSEEK_API_KEY', raising=False) yield def test_is_available_requires_key_and_flag(monkeypatch): from services.deepseek_service import DeepSeekService svc = DeepSeekService() # 無 key 無 flag → False assert svc.is_available() is False # 只有 flag → False monkeypatch.setenv('DEEPSEEK_DIRECT_ENABLED', 'true') assert svc.is_available() is False # flag + key → True(需 reload module 取新 env) monkeypatch.setenv('DEEPSEEK_API_KEY', 'sk-test') import importlib import services.deepseek_service as ds importlib.reload(ds) assert ds.deepseek_service.is_available() is True def test_generate_returns_failure_when_unavailable(monkeypatch): """flag OFF 時 generate 直接 return failure,不打 HTTP""" monkeypatch.setenv('DEEPSEEK_DIRECT_ENABLED', 'false') from services.deepseek_service import DeepSeekService svc = DeepSeekService() with patch('services.deepseek_service.requests.post') as mock_post: resp = svc.generate('test prompt') assert resp.success is False assert 'DEEPSEEK_DIRECT_ENABLED=false' in (resp.error or '') or \ 'API_KEY 未設' in (resp.error or '') mock_post.assert_not_called() def test_generate_success_parses_usage(monkeypatch): """正常 200 回應應解 usage tokens""" monkeypatch.setenv('DEEPSEEK_DIRECT_ENABLED', 'true') monkeypatch.setenv('DEEPSEEK_API_KEY', 'sk-test') import importlib import services.deepseek_service as ds importlib.reload(ds) fake_resp = MagicMock(status_code=200) fake_resp.json.return_value = { 'model': 'deepseek-chat', 'choices': [{'message': {'content': 'Hello from DeepSeek'}}], 'usage': {'prompt_tokens': 100, 'completion_tokens': 50}, } with patch('services.deepseek_service.requests.post', return_value=fake_resp): resp = ds.deepseek_service.generate('hi', system_prompt='you are helpful') assert resp.success is True assert resp.content == 'Hello from DeepSeek' assert resp.input_tokens == 100 assert resp.output_tokens == 50 assert resp.model == 'deepseek-chat' def test_generate_http_500_returns_failure(monkeypatch): monkeypatch.setenv('DEEPSEEK_DIRECT_ENABLED', 'true') monkeypatch.setenv('DEEPSEEK_API_KEY', 'sk-test') import importlib import services.deepseek_service as ds importlib.reload(ds) fake_resp = MagicMock(status_code=500) fake_resp.text = 'Internal Server Error' with patch('services.deepseek_service.requests.post', return_value=fake_resp): resp = ds.deepseek_service.generate('test') assert resp.success is False assert 'HTTP 500' in (resp.error or '') def test_generate_timeout_returns_failure(monkeypatch): monkeypatch.setenv('DEEPSEEK_DIRECT_ENABLED', 'true') monkeypatch.setenv('DEEPSEEK_API_KEY', 'sk-test') import importlib import services.deepseek_service as ds importlib.reload(ds) import requests with patch('services.deepseek_service.requests.post', side_effect=requests.Timeout('60s')): resp = ds.deepseek_service.generate('test') assert resp.success is False assert 'timeout' in (resp.error or '').lower() def test_check_connection_when_unavailable(): from services.deepseek_service import DeepSeekService svc = DeepSeekService() # 無 key 無 flag assert svc.check_connection() is False