Files
banban/talkingq-url/tests/test_parent_voice_mp3_normalization.py
2026-07-07 16:58:05 +08:00

216 lines
8.1 KiB
Python

from datetime import datetime, timezone
from types import SimpleNamespace
import pytest
from banban.dao.im import ConversationMessageCreateResult
from banban.schemas.im import ChildConversationMessageItem
from banban.service.im import ImService
from banban.service.message_audio_storage import StoredMessageAudio
from utils.audio_transcode import infer_audio_source_format, prepare_audio_as_mp3
def test_infer_audio_source_format_distinguishes_aac_from_mp3_frame_sync():
assert infer_audio_source_format(b"\xff\xf1P\x80\x00\x1f\xfc") == "aac"
assert infer_audio_source_format(b"\xff\xfb\x90\x64" + b"\x00" * 32) == "mp3"
@pytest.mark.asyncio
async def test_prepare_audio_as_mp3_keeps_mp3_without_transcoding(monkeypatch):
async def fail_transcode(*args, **kwargs):
raise AssertionError("mp3 upload should not be transcoded")
monkeypatch.setattr("utils.audio_transcode.transcode_audio_to_mp3", fail_transcode)
original = b"ID3" + b"\x00" * 32
prepared = await prepare_audio_as_mp3(
original,
filename="voice.aac",
content_type="audio/aac",
)
assert prepared.content == original
assert prepared.mime_type == "audio/mpeg"
assert prepared.extension == "mp3"
assert prepared.size_bytes == len(original)
assert prepared.source_format == "mp3"
assert prepared.transcoded is False
@pytest.mark.asyncio
async def test_prepare_audio_as_mp3_transcodes_aac_upload(monkeypatch):
calls = []
async def fake_transcode(audio_data, **kwargs):
calls.append({"audio_data": audio_data, **kwargs})
return b"ID3 converted"
monkeypatch.setattr("utils.audio_transcode.transcode_audio_to_mp3", fake_transcode)
original = b"\xff\xf1P\x80\x00\x1f\xfc"
prepared = await prepare_audio_as_mp3(
original,
filename="voice.aac",
content_type="audio/aac",
)
assert prepared.content == b"ID3 converted"
assert prepared.mime_type == "audio/mpeg"
assert prepared.extension == "mp3"
assert prepared.size_bytes == len(b"ID3 converted")
assert prepared.source_format == "aac"
assert prepared.original_mime_type == "audio/aac"
assert prepared.original_extension == "aac"
assert prepared.transcoded is True
assert calls == [
{
"audio_data": original,
"source_format": "aac",
"source_suffix": ".aac",
"session_device_id": "system",
"session_id": "audio_transcode",
}
]
@pytest.mark.asyncio
async def test_parent_voice_upload_stores_and_queues_mp3_for_device(monkeypatch):
service = ImService()
uploaded = []
pending_messages = []
duration_jobs = []
async def fake_assert_parent_child_access(**kwargs):
return {"child_id": kwargs["child_id"], "child_name": "child"}
async def fake_get_child_binding(child_id, parent_user_id):
return {"device_id": "TalkingQ_XQSN00001007"}
async def fake_upload_audio(**kwargs):
uploaded.append(kwargs)
return StoredMessageAudio(
file_key="messages/audio/parent-4/2026/06/11/converted.mp3",
public_url="https://cos.example/messages/audio/parent-4/converted.mp3",
)
async def fake_create_parent_child_message(*, parent_user_id, child_id, payload):
message = ChildConversationMessageItem(
id=181,
conversation_id=11,
seq=1,
sender_type="parent",
sender_id=str(parent_user_id),
receiver_type="child",
receiver_id=str(child_id),
content_type=payload.content_type,
content_text=payload.content_text,
content_json=payload.content_json,
media_file_key=payload.media_file_key,
media_duration_ms=payload.media_duration_ms,
media_mime_type=payload.media_mime_type,
media_size_bytes=payload.media_size_bytes,
media_transcript_text=payload.media_transcript_text,
client_msg_id=payload.client_msg_id,
sender_name_snapshot="parent",
sender_avatar_snapshot=None,
receiver_name_snapshot="child",
receiver_avatar_snapshot=None,
ext_json=payload.ext_json,
created_at=datetime.now(timezone.utc),
)
return ConversationMessageCreateResult(
idempotent=False,
conversation_id=11,
conversation_type=2,
message=message,
)
async def fake_get_raw_message_media_file_key(**kwargs):
return "messages/audio/parent-4/2026/06/11/converted.mp3"
async def fake_get_device_audio_url(file_key_or_url, *, device_id=None):
assert file_key_or_url.endswith(".mp3")
assert device_id == "TalkingQ_XQSN00001007"
return "http://127.0.0.1:8080/device-audio/converted.mp3"
async def fake_add_pending_message(**kwargs):
pending_messages.append(kwargs)
async def fake_schedule_message_media_duration_parse(**kwargs):
duration_jobs.append(kwargs)
async def fake_prepare_audio_as_mp3(content, **kwargs):
assert content == b"\xff\xf1 aac"
assert kwargs["filename"] == "voice.aac"
assert kwargs["content_type"] == "audio/aac"
return SimpleNamespace(
content=b"ID3 converted mp3",
mime_type="audio/mpeg",
extension="mp3",
size_bytes=len(b"ID3 converted mp3"),
source_format="aac",
original_mime_type="audio/aac",
original_extension="aac",
transcoded=True,
)
monkeypatch.setattr(service, "assert_parent_child_access", fake_assert_parent_child_access)
monkeypatch.setattr("banban.service.im.binding_service.get_child_binding", fake_get_child_binding)
monkeypatch.setattr(service.audio_storage, "upload_audio", fake_upload_audio)
monkeypatch.setattr(service, "create_parent_child_message", fake_create_parent_child_message)
monkeypatch.setattr(service, "_get_raw_message_media_file_key", fake_get_raw_message_media_file_key)
monkeypatch.setattr("banban.service.im.device_audio_cache_service.get_device_audio_url", fake_get_device_audio_url)
monkeypatch.setattr("banban.service.im.pending_voice_message_service.add_pending_message", fake_add_pending_message)
monkeypatch.setattr(service, "schedule_message_media_duration_parse", fake_schedule_message_media_duration_parse)
monkeypatch.setattr("banban.service.im.prepare_audio_as_mp3", fake_prepare_audio_as_mp3)
result = await service.create_parent_child_voice_message(
parent_user_id=4,
child_id=7,
filename="voice.aac",
content_type="audio/aac",
content=b"\xff\xf1 aac",
media_duration_ms=1234,
media_transcript_text=None,
client_msg_id="client-1",
ext_json={
"message_kind": "leave_message",
"source": "parent_weapp_voice",
},
)
assert result.message.media_file_key.endswith(".mp3")
assert result.message.media_mime_type == "audio/mpeg"
assert result.message.media_size_bytes == len(b"ID3 converted mp3")
assert result.message.ext_json["message_kind"] == "leave_message"
assert result.message.ext_json["normalized_audio_format"] == "mp3"
assert result.message.ext_json["source_audio_format"] == "aac"
assert result.message.ext_json["audio_transcoded"] is True
assert uploaded == [
{
"device_id": "parent-4",
"content": b"ID3 converted mp3",
"content_type": "audio/mpeg",
"extension": "mp3",
}
]
assert pending_messages == [
{
"target_device_id": "TalkingQ_XQSN00001007",
"sender_device_id": None,
"im_message_id": 181,
"media_file_key": "messages/audio/parent-4/2026/06/11/converted.mp3",
"audio_url": "http://127.0.0.1:8080/device-audio/converted.mp3",
"source": "parent_child_voice",
}
]
assert duration_jobs == [
{
"message_id": 181,
"audio_data": b"ID3 converted mp3",
"mime_type": "audio/mpeg",
"source": "parent_weapp_voice",
}
]