181 lines
5.6 KiB
Python
181 lines
5.6 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from livekit.plugins.hamming._plugin import (
|
|
RECORDING_MODE_NONE,
|
|
RECORDING_MODE_SESSION_AUDIO,
|
|
HammingRuntime,
|
|
RecordingResolutionContext,
|
|
_build_recording_config,
|
|
_extract_test_case_run_id,
|
|
_SessionMonitor,
|
|
build_config,
|
|
)
|
|
|
|
|
|
class _FakeSession:
|
|
def __init__(self) -> None:
|
|
self._events: dict[str, object] = {}
|
|
|
|
async def start(self, *args: object, **kwargs: object) -> object:
|
|
return {"args": args, "kwargs": kwargs}
|
|
|
|
def on(self, event_name: str, handler: object) -> None:
|
|
self._events[event_name] = handler
|
|
|
|
|
|
class _FakeReport:
|
|
room = "room-123"
|
|
started_at = 100.0
|
|
timestamp = 125.0
|
|
events: list[object] = []
|
|
audio_recording_path = None
|
|
|
|
def to_dict(self) -> dict[str, object]:
|
|
return {"room": self.room, "events": []}
|
|
|
|
|
|
class RecordingConfigTests(unittest.TestCase):
|
|
def test_default_configuration_disables_recording(self) -> None:
|
|
config = build_config(api_key="test-key", external_agent_id="agent-123")
|
|
|
|
self.assertEqual(config.recording.mode, RECORDING_MODE_NONE)
|
|
|
|
def test_auto_record_audio_maps_to_session_audio_mode(self) -> None:
|
|
config = _build_recording_config(
|
|
recording=None,
|
|
auto_record_audio=True,
|
|
recording_mode=None,
|
|
)
|
|
|
|
self.assertEqual(config.mode, RECORDING_MODE_SESSION_AUDIO)
|
|
|
|
def test_unsupported_modes_fail_fast(self) -> None:
|
|
with self.assertRaises(ValueError):
|
|
_build_recording_config(
|
|
recording={"mode": "unsupported_mode"},
|
|
auto_record_audio=False,
|
|
recording_mode=None,
|
|
)
|
|
|
|
def test_participant_egress_requires_recording_resolver(self) -> None:
|
|
with self.assertRaises(ValueError):
|
|
build_config(
|
|
api_key="test-key",
|
|
external_agent_id="agent-123",
|
|
recording={"mode": "participant_egress"},
|
|
)
|
|
|
|
def test_extract_test_case_run_id_falls_back_to_recording_context(self) -> None:
|
|
value = _extract_test_case_run_id(
|
|
participant_metadata_raw=None,
|
|
recording_context={"customer_conversation_id": "conv-123"},
|
|
)
|
|
|
|
self.assertEqual(value, "conv-123")
|
|
|
|
|
|
class AttachSessionRecordingTests(unittest.IsolatedAsyncioTestCase):
|
|
async def test_session_audio_wraps_session_start_with_default_recording_options(
|
|
self,
|
|
) -> None:
|
|
runtime = HammingRuntime(
|
|
build_config(
|
|
api_key="test-key",
|
|
external_agent_id="agent-123",
|
|
recording={"mode": "session_audio"},
|
|
)
|
|
)
|
|
|
|
session = _FakeSession()
|
|
runtime.attach_session(session)
|
|
|
|
result = await session.start(foo="bar")
|
|
|
|
self.assertEqual(
|
|
result,
|
|
{
|
|
"args": (),
|
|
"kwargs": {
|
|
"foo": "bar",
|
|
"record": {
|
|
"audio": True,
|
|
"traces": False,
|
|
"logs": False,
|
|
"transcript": False,
|
|
},
|
|
},
|
|
},
|
|
)
|
|
self.assertIn("close", session._events)
|
|
|
|
async def test_session_audio_does_not_override_explicit_record_argument(self) -> None:
|
|
runtime = HammingRuntime(
|
|
build_config(
|
|
api_key="test-key",
|
|
external_agent_id="agent-123",
|
|
recording={"mode": "session_audio"},
|
|
)
|
|
)
|
|
|
|
session = _FakeSession()
|
|
runtime.attach_session(session)
|
|
|
|
result = await session.start(record={"audio": False})
|
|
|
|
self.assertEqual(
|
|
result,
|
|
{
|
|
"args": (),
|
|
"kwargs": {
|
|
"record": {
|
|
"audio": False,
|
|
},
|
|
},
|
|
},
|
|
)
|
|
|
|
async def test_room_composite_lookup_retries_after_transient_fetch_failure(self) -> None:
|
|
runtime = HammingRuntime(build_config(api_key="test-key", external_agent_id="agent-123"))
|
|
runtime.transport.fetch_test_case_run_recording_url = AsyncMock(
|
|
side_effect=[RuntimeError("transient"), "https://recordings.example.com/room.ogg"]
|
|
)
|
|
|
|
monitor = _SessionMonitor(
|
|
runtime=runtime,
|
|
session=_FakeSession(),
|
|
participant_identity=None,
|
|
participant_metadata='{"conversation_id":"conv-123"}',
|
|
external_agent_id="agent-123",
|
|
job_ctx=None,
|
|
session_key=1,
|
|
recording_context=None,
|
|
)
|
|
resolution_context = RecordingResolutionContext(
|
|
session=monitor._session,
|
|
report=_FakeReport(),
|
|
job_ctx=None,
|
|
close_event=None,
|
|
call_id="call-123",
|
|
room_name="room-123",
|
|
participant_identity=None,
|
|
participant_metadata_raw='{"conversation_id":"conv-123"}',
|
|
external_agent_id="agent-123",
|
|
recording_context=None,
|
|
)
|
|
|
|
with patch("livekit.plugins.hamming._plugin.asyncio.sleep", new=AsyncMock()) as sleep_mock:
|
|
recording_url = await monitor._resolve_hamming_room_composite_recording_url(
|
|
resolution_context=resolution_context
|
|
)
|
|
|
|
self.assertEqual(recording_url, "https://recordings.example.com/room.ogg")
|
|
self.assertEqual(runtime.transport.fetch_test_case_run_recording_url.await_count, 2)
|
|
sleep_mock.assert_awaited_once()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|