Complete DDS training workflow and delivery package
This commit is contained in:
parent
68dd83c7c2
commit
4c4b91064f
229 changed files with 11969 additions and 1024 deletions
69
backend/tests/test_recording.py
Normal file
69
backend/tests/test_recording.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
"""WAV-запись вызова: формат, микширование и защищённая выдача."""
|
||||
|
||||
import wave
|
||||
from datetime import datetime, timezone
|
||||
from types import SimpleNamespace
|
||||
from uuid import uuid4
|
||||
|
||||
import numpy as np
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.api.auth import Principal
|
||||
from app.api.http import sessions
|
||||
from app.domain.roles import Role
|
||||
from app.main import app
|
||||
from app.voice.recording import CallRecorder
|
||||
|
||||
|
||||
def test_recorder_mixes_16_and_24_khz_into_atomic_wav(tmp_path):
|
||||
ticks = iter((10.0, 10.0, 10.02))
|
||||
path = tmp_path / "call.wav"
|
||||
recorder = CallRecorder(path, clock=lambda: next(ticks))
|
||||
recorder.add_pcm(np.full(320, 1000, dtype="<i2").tobytes(), sample_rate=16_000)
|
||||
recorder.add_pcm(np.full(480, 2000, dtype="<i2").tobytes(), sample_rate=24_000)
|
||||
|
||||
assert recorder.finalize() == path
|
||||
assert recorder.finalize() == path
|
||||
assert not path.with_suffix(".wav.tmp").exists()
|
||||
with wave.open(str(path), "rb") as source:
|
||||
assert source.getnchannels() == 1
|
||||
assert source.getsampwidth() == 2
|
||||
assert source.getframerate() == 16_000
|
||||
assert source.getnframes() >= 640
|
||||
samples = np.frombuffer(source.readframes(source.getnframes()), dtype="<i2")
|
||||
assert samples.max() >= 2000
|
||||
|
||||
|
||||
def test_recording_download_is_authenticated_and_has_wav_type(tmp_path, monkeypatch):
|
||||
session_id = uuid4()
|
||||
path = tmp_path / f"{session_id}.wav"
|
||||
with wave.open(str(path), "wb") as target:
|
||||
target.setnchannels(1)
|
||||
target.setsampwidth(2)
|
||||
target.setframerate(16_000)
|
||||
target.writeframes(b"\x00\x00" * 320)
|
||||
|
||||
async def fake_session(db, requested):
|
||||
assert requested == session_id
|
||||
return SimpleNamespace(
|
||||
trainee_id=uuid4(), owner_login="dev", ended_at=datetime.now(timezone.utc),
|
||||
)
|
||||
|
||||
monkeypatch.setattr(sessions.repo, "get_session", fake_session)
|
||||
monkeypatch.setattr(sessions, "recording_path", lambda requested: path)
|
||||
with TestClient(app) as client:
|
||||
assert client.get(f"/api/sessions/{session_id}/recording.wav").status_code == 401
|
||||
client.post("/api/auth/dev-token")
|
||||
response = client.get(f"/api/sessions/{session_id}/recording.wav")
|
||||
assert response.status_code == 200
|
||||
assert response.headers["content-type"] == "audio/wav"
|
||||
assert response.content.startswith(b"RIFF")
|
||||
|
||||
monkeypatch.setattr(
|
||||
sessions,
|
||||
"require",
|
||||
lambda request, *roles: Principal(
|
||||
login="other", full_name="Другой", role=Role.TRAINEE, trainee_id=uuid4()
|
||||
),
|
||||
)
|
||||
assert client.get(f"/api/sessions/{session_id}/recording.wav").status_code == 403
|
||||
Loading…
Reference in a new issue