Standardize ASR on Tencent's dialect-capable engine across all environments, drop faster-whisper from dependencies and deployment images, and add an expo-sqlite iOS vendor sync plus pod install in prebuild to prevent native build failures after npm install. Co-authored-by: Cursor <cursoragent@cursor.com>
104 lines
3.2 KiB
Python
104 lines
3.2 KiB
Python
import asyncio
|
|
import sys
|
|
from types import ModuleType, SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from app.adapters.asr.tencent_asr import TencentASRProvider
|
|
from app.core import chapter_pipeline_lock
|
|
from app.features.story import post_commit
|
|
|
|
|
|
def test_chapter_pipeline_lock_delegates_to_token_lock(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
handle = object()
|
|
recorded: dict[str, object] = {}
|
|
|
|
def fake_acquire(key: str, *, ttl_seconds: int) -> object:
|
|
recorded["key"] = key
|
|
recorded["ttl_seconds"] = ttl_seconds
|
|
return handle
|
|
|
|
def fake_release(arg: object) -> None:
|
|
recorded["released"] = arg
|
|
|
|
monkeypatch.setattr(chapter_pipeline_lock, "acquire_redis_lock", fake_acquire)
|
|
monkeypatch.setattr(chapter_pipeline_lock, "release_redis_lock", fake_release)
|
|
|
|
acquired = chapter_pipeline_lock.acquire_chapter_pipeline_lock(
|
|
"user-1", "childhood", ttl_seconds=120
|
|
)
|
|
chapter_pipeline_lock.release_chapter_pipeline_lock(acquired)
|
|
|
|
assert acquired is handle
|
|
assert recorded == {
|
|
"key": "lock:chapter:user-1:childhood",
|
|
"ttl_seconds": 120,
|
|
"released": handle,
|
|
}
|
|
|
|
|
|
def test_post_commit_reuses_singleton_redis_client(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
client = object()
|
|
|
|
def fake_get_sync_redis(*, decode_responses: bool):
|
|
assert decode_responses is True
|
|
return client
|
|
|
|
monkeypatch.setattr(post_commit, "get_sync_redis", fake_get_sync_redis)
|
|
|
|
first = post_commit._get_redis()
|
|
second = post_commit._get_redis()
|
|
|
|
assert first is second
|
|
assert first is client
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_tencent_asr_transcribe_uses_to_thread(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
to_thread_calls: list[tuple[object, tuple[object, ...]]] = []
|
|
|
|
class FakeRequest:
|
|
EngSerViceType: str | None = None
|
|
SourceType: int | None = None
|
|
VoiceFormat: str | None = None
|
|
Data: str | None = None
|
|
DataLen: int | None = None
|
|
|
|
class FakeClient:
|
|
def SentenceRecognition(self, req: FakeRequest) -> SimpleNamespace:
|
|
return SimpleNamespace(Result=" 你好,世界 ")
|
|
|
|
async def fake_to_thread(fn, *args):
|
|
to_thread_calls.append((fn, args))
|
|
return fn(*args)
|
|
|
|
models_module = ModuleType("tencentcloud.asr.v20190614.models")
|
|
models_module.SentenceRecognitionRequest = FakeRequest
|
|
package_module = ModuleType("tencentcloud.asr.v20190614")
|
|
package_module.models = models_module
|
|
|
|
monkeypatch.setitem(sys.modules, "tencentcloud.asr.v20190614", package_module)
|
|
monkeypatch.setattr(asyncio, "to_thread", fake_to_thread)
|
|
|
|
provider = TencentASRProvider("sid", "skey", engine_type="16k_zh_large")
|
|
client = FakeClient()
|
|
monkeypatch.setattr(provider, "_get_client", lambda: client)
|
|
|
|
text = await provider.transcribe(b"fake-audio", format="m4a")
|
|
|
|
assert text == "你好,世界"
|
|
assert len(to_thread_calls) == 1
|
|
fn, args = to_thread_calls[0]
|
|
assert getattr(fn, "__self__", None) is client
|
|
assert getattr(fn, "__name__", "") == "SentenceRecognition"
|
|
request = args[0]
|
|
assert request.EngSerViceType == "16k_zh_large"
|
|
assert request.VoiceFormat == "m4a"
|
|
assert request.DataLen == len(b"fake-audio")
|