fix real cell RTP bridge readiness
This commit is contained in:
+164
-2
@@ -6,7 +6,7 @@ import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
from typing import Any, cast
|
||||
|
||||
from agent_call.real_cell import (
|
||||
CellCallConfig,
|
||||
@@ -75,11 +75,173 @@ class RealCellTests(unittest.TestCase):
|
||||
self.assertEqual(RTPMedia._payload(packet), payload)
|
||||
self.assertIsNone(RTPMedia._payload(b"bad"))
|
||||
|
||||
def test_cell_config_rejects_non_pcma_and_bad_port(self) -> None:
|
||||
def test_nonblocking_receive_returns_no_packet(self) -> None:
|
||||
media = RTPMedia("127.0.0.1", 0)
|
||||
try:
|
||||
self.assertIsNone(media.receive(0.0))
|
||||
finally:
|
||||
media.close()
|
||||
|
||||
def test_rtp_receive_filters_payload_peer_and_ssrc(self) -> None:
|
||||
media = RTPMedia("127.0.0.1", 0)
|
||||
sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sender.bind(("127.0.0.1", 0))
|
||||
media.peer = ("127.0.0.1", sender.getsockname()[1])
|
||||
|
||||
def packet(payload_type: int, ssrc: int, payload: bytes) -> bytes:
|
||||
return struct.pack("!BBHII", 0x80, payload_type, 1, 0, ssrc) + payload
|
||||
|
||||
try:
|
||||
sender.sendto(packet(0, 1, b"wrong-pt"), media.address)
|
||||
self.assertIsNone(media.receive(0.2))
|
||||
sender.sendto(packet(8, 1, b"voice"), media.address)
|
||||
self.assertEqual(media.receive(0.2), b"voice")
|
||||
sender.sendto(packet(8, 2, b"wrong-ssrc"), media.address)
|
||||
self.assertIsNone(media.receive(0.2))
|
||||
finally:
|
||||
sender.close()
|
||||
media.close()
|
||||
|
||||
def test_external_media_peer_comes_from_ari(self) -> None:
|
||||
config = CellCallConfig("http://127.0.0.1:8088", "u", "p")
|
||||
call = cast(
|
||||
Any,
|
||||
RealCellCall(
|
||||
config,
|
||||
cast(Any, SimpleNamespace(config={})),
|
||||
cast(Any, SimpleNamespace()),
|
||||
),
|
||||
)
|
||||
media = RTPMedia("127.0.0.1", 0)
|
||||
calls: list[tuple[str, str, dict | None]] = []
|
||||
|
||||
class FakeARI:
|
||||
def request(self, method: str, resource: str, params=None, **_kwargs):
|
||||
calls.append((method, resource, params))
|
||||
if params and params.get("variable") == "UNICASTRTP_LOCAL_ADDRESS":
|
||||
return {"value": "127.0.0.1"}
|
||||
return {"value": "12345"}
|
||||
|
||||
call.media = media
|
||||
call.ari = FakeARI()
|
||||
try:
|
||||
call._set_external_media_peer("external-1")
|
||||
self.assertEqual(media.peer, ("127.0.0.1", 12345))
|
||||
self.assertEqual(len(calls), 2)
|
||||
finally:
|
||||
media.close()
|
||||
|
||||
def test_add_when_ready_marks_only_after_success(self) -> None:
|
||||
config = CellCallConfig("http://127.0.0.1:8088", "u", "p")
|
||||
call = cast(
|
||||
Any,
|
||||
RealCellCall(
|
||||
config,
|
||||
cast(Any, SimpleNamespace(config={})),
|
||||
cast(Any, SimpleNamespace()),
|
||||
),
|
||||
)
|
||||
|
||||
class FakeARI:
|
||||
def __init__(self) -> None:
|
||||
self.attempts = 0
|
||||
|
||||
def request(self, method: str, _resource: str, _params=None, **_kwargs):
|
||||
if method == "POST":
|
||||
self.attempts += 1
|
||||
if self.attempts == 1:
|
||||
raise CellCallError("ARI_HTTP_422", "not in Stasis", True)
|
||||
return {}
|
||||
|
||||
fake_ari = FakeARI()
|
||||
call.ari = fake_ari
|
||||
self.assertTrue(call._add_when_ready("target"))
|
||||
self.assertEqual(fake_ari.attempts, 2)
|
||||
self.assertIn("target", call._known_channels)
|
||||
|
||||
def test_wait_answer_requires_bridge_membership(self) -> None:
|
||||
config = CellCallConfig("http://127.0.0.1:8088", "u", "p", event_timeout_s=1.0)
|
||||
call = cast(
|
||||
Any,
|
||||
RealCellCall(
|
||||
config,
|
||||
cast(Any, SimpleNamespace(config={})),
|
||||
cast(Any, SimpleNamespace()),
|
||||
),
|
||||
)
|
||||
call.target_channel_id = "target"
|
||||
call.external_channel_id = "external"
|
||||
call.media = RTPMedia("127.0.0.1", 0)
|
||||
call.media.peer = ("127.0.0.1", 12345)
|
||||
bridge_members = [{"external"}, {"external", "target"}]
|
||||
|
||||
class FakeARI:
|
||||
def request(self, method: str, resource: str, _params=None, **_kwargs):
|
||||
if method == "GET" and resource == f"bridges/{call.bridge_id}":
|
||||
return {"channels": list(bridge_members.pop(0))}
|
||||
return {}
|
||||
|
||||
call.ari = FakeARI()
|
||||
call.events.put(
|
||||
{"type": "ChannelStateChange", "channel": {"id": "target", "state": "Up"}}
|
||||
)
|
||||
call.events.put({"type": "StasisStart", "channel": {"id": "target"}})
|
||||
try:
|
||||
call._wait_answer(SimpleNamespace())
|
||||
self.assertEqual(bridge_members, [])
|
||||
finally:
|
||||
call.media.close()
|
||||
|
||||
def test_finish_recording_stops_then_reads_stored_file(self) -> None:
|
||||
class FakeARI:
|
||||
def __init__(self) -> None:
|
||||
self.calls: list[tuple[str, str]] = []
|
||||
|
||||
def request(self, method: str, resource: str, **_kwargs):
|
||||
self.calls.append((method, resource))
|
||||
return {} if method == "POST" else b"RIFFtest"
|
||||
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
config = CellCallConfig(
|
||||
"http://127.0.0.1:8088", "u", "p", recording_dir=directory
|
||||
)
|
||||
call = cast(
|
||||
Any,
|
||||
RealCellCall(
|
||||
config,
|
||||
cast(Any, SimpleNamespace(config={})),
|
||||
cast(Any, SimpleNamespace()),
|
||||
),
|
||||
)
|
||||
fake_ari = FakeARI()
|
||||
call.ari = fake_ari
|
||||
path = call._finish_recording()
|
||||
self.assertIsNotNone(path)
|
||||
self.assertEqual(fake_ari.calls[0][0], "POST")
|
||||
self.assertIn("/stop", fake_ari.calls[0][1])
|
||||
self.assertNotIn(
|
||||
("DELETE", fake_ari.calls[0][1].rsplit("/stop", 1)[0]), fake_ari.calls
|
||||
)
|
||||
|
||||
def test_cell_config_rejects_non_pcma_bad_port_and_prefix(self) -> None:
|
||||
with self.assertRaises(CellCallError):
|
||||
CellCallConfig("http://127.0.0.1:8088", "u", "p", rtp_format="ulaw")
|
||||
with self.assertRaises(CellCallError):
|
||||
CellCallConfig("http://127.0.0.1:8088", "u", "p", rtp_bind_port=65536)
|
||||
with self.assertRaises(CellCallError):
|
||||
CellCallConfig("http://127.0.0.1:8088", "u", "p", dial_prefix="7089+")
|
||||
self.assertEqual(
|
||||
CellCallConfig(
|
||||
"http://127.0.0.1:8088", "u", "p", dial_prefix=""
|
||||
).dial_prefix,
|
||||
"",
|
||||
)
|
||||
self.assertEqual(
|
||||
CellCallConfig(
|
||||
"http://127.0.0.1:8088", "u", "p", dial_prefix="mka755"
|
||||
).dial_prefix,
|
||||
"mka755",
|
||||
)
|
||||
|
||||
def test_ledger_marks_in_progress_as_in_doubt(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
|
||||
Reference in New Issue
Block a user