126 lines
4.6 KiB
Python
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"]
|