legco_ai_assistant/backend/app/test/test_phase5_integration.py

126 lines
4.6 KiB
Python

"""Phase 5 tests: Integration — full video → transcribe with provider switching.
Covers:
- Full transcript with dashscope provider (mocked OpenAI)
- Full transcript with openrouter provider (mocked httpx)
- API key validation per provider
"""
from unittest.mock import AsyncMock, MagicMock, patch
import httpx
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from app.routers.video import router
@pytest.fixture
def video_client(tmp_path, monkeypatch):
upload_dir = tmp_path / "test_uploads"
upload_dir.mkdir()
monkeypatch.setenv("VIDEO_UPLOAD_DIR", str(upload_dir))
monkeypatch.setenv("MAX_VIDEO_SIZE_MB", "50")
from app.core.config import get_settings
get_settings.cache_clear()
app = FastAPI()
app.include_router(router, prefix="/api/v1")
return TestClient(app), upload_dir
def _upload_video(client, filename="test.mp4", content=b"\x00" * 1024):
resp = client.post(
"/api/v1/video/upload",
files={"file": (filename, content, "video/mp4")},
)
assert resp.status_code == 200
return resp.json()["video_id"]
class TestDashScopeIntegration:
@patch("app.routers.video.VideoService.extract_audio")
@patch("app.services.asr_providers.OpenAI")
def test_transcribe_with_dashscope(self, mock_openai_cls, mock_extract, video_client, monkeypatch):
monkeypatch.setenv("ASR_PROVIDER", "dashscope")
monkeypatch.setenv("DASHSCOPE_API_KEY", "sk-dashscope-test")
from app.core.config import get_settings
get_settings.cache_clear()
client, upload_dir = video_client
video_id = _upload_video(client)
fake_wav = upload_dir / "extracted.wav"
fake_wav.write_bytes(b"RIFF" + b"\x00" * 100)
mock_extract.return_value = fake_wav
mock_resp = MagicMock()
mock_resp.choices = [MagicMock()]
mock_resp.choices[0].message.content = "测试转录結果"
mock_openai_instance = MagicMock()
mock_openai_instance.chat.completions.create.return_value = mock_resp
mock_openai_cls.return_value = mock_openai_instance
resp = client.post(f"/api/v1/video/{video_id}/transcribe")
assert resp.status_code == 200
assert "text" in resp.json()
assert "" in resp.json()["text"] or "" in resp.json()["text"]
class TestOpenRouterIntegration:
@patch("app.routers.video.VideoService.extract_audio")
@patch("app.services.asr_providers.httpx.AsyncClient")
def test_transcribe_with_openrouter(self, mock_httpx_cls, mock_extract, video_client, monkeypatch):
monkeypatch.setenv("ASR_PROVIDER", "openrouter")
monkeypatch.setenv("OPENROUTER_API_KEY", "sk-or-test")
from app.core.config import get_settings
get_settings.cache_clear()
client, upload_dir = video_client
video_id = _upload_video(client)
fake_wav = upload_dir / "extracted.wav"
fake_wav.write_bytes(b"RIFF" + b"\x00" * 100)
mock_extract.return_value = fake_wav
mock_response = MagicMock(spec=httpx.Response)
mock_response.json.return_value = {"text": "測試轉錄結果", "usage": {}}
mock_response.raise_for_status = MagicMock()
mock_http_client = AsyncMock()
mock_http_client.post.return_value = mock_response
mock_httpx_cls.return_value = mock_http_client
resp = client.post(f"/api/v1/video/{video_id}/transcribe")
assert resp.status_code == 200
assert "text" in resp.json()
assert "" in resp.json()["text"] or "" in resp.json()["text"]
class TestApiKeyValidation:
def test_missing_dashscope_key_returns_500(self, video_client, monkeypatch):
monkeypatch.setenv("ASR_PROVIDER", "dashscope")
monkeypatch.setenv("DASHSCOPE_API_KEY", "")
from app.core.config import get_settings
get_settings.cache_clear()
client, upload_dir = video_client
video_id = _upload_video(client)
resp = client.post(f"/api/v1/video/{video_id}/transcribe")
assert resp.status_code == 500
assert "DASHSCOPE_API_KEY" in resp.json()["detail"]
def test_missing_openrouter_key_returns_500(self, video_client, monkeypatch):
monkeypatch.setenv("ASR_PROVIDER", "openrouter")
monkeypatch.setenv("OPENROUTER_API_KEY", "")
from app.core.config import get_settings
get_settings.cache_clear()
client, upload_dir = video_client
video_id = _upload_video(client)
resp = client.post(f"/api/v1/video/{video_id}/transcribe")
assert resp.status_code == 500
assert "OPENROUTER_API_KEY" in resp.json()["detail"]