🚚(backend) rename TelephonyService to SIPManagement

Rename the telephony service to a more descriptive name,
SIPManagementService, which clearly states what the service is used
for.

It is no longer used only by the telephony feature; the roomkit
feature also relies on it now.
This commit is contained in:
lebaudantoine
2026-07-30 19:46:30 +02:00
parent 80f3af0690
commit ca80540f7e
7 changed files with 119 additions and 118 deletions
+1
View File
@@ -27,6 +27,7 @@ and this project adheres to
- 📝(legal) update terms of service
- 💄(frontend) render Avatar initials in uppercase
- 💄(frontend) improve participant name rendering in the list
- 🚚(backend) rename TelephonyService to SIPManagement
## Fixed
+4 -4
View File
@@ -16,7 +16,7 @@ from rest_framework import (
from core import analytics, models
from core.api import permissions, throttling
from core.api.feature_flag import FeatureFlag
from core.services.telephony import TelephonyException, TelephonyService
from core.services.sip_management import SIPException, SIPManagement
from . import authentication, serializers
@@ -50,7 +50,7 @@ class RoomKitViewSet(viewsets.ViewSet):
without waiting for a WebRTC user.
The webhook-based creation path is kept: both converge on the same rule
through the shared TelephonyService.
through the shared SIPManagement.
"""
serializer = serializers.RoomKitJoinSerializer(data=request.data)
@@ -64,8 +64,8 @@ class RoomKitViewSet(viewsets.ViewSet):
raise drf_exceptions.NotFound("No room found for this PIN code.") from e
try:
created = TelephonyService().ensure_dispatch_rule(room)
except TelephonyException as e:
created = SIPManagement().ensure_dispatch_rule(room)
except SIPException as e:
raise drf_exceptions.APIException("Could not create dispatch rule.") from e
analytics.capture(
+8 -8
View File
@@ -28,7 +28,7 @@ from .room_management import (
RoomManagementException,
RoomNotFoundException,
)
from .telephony import TelephonyException, TelephonyService
from .sip_management import SIPException, SIPManagement
logger = getLogger(__name__)
@@ -107,7 +107,7 @@ class LiveKitEventsService:
)
self.webhook_receiver = api.WebhookReceiver(token_verifier)
self.lobby_service = LobbyService()
self.telephony_service = TelephonyService()
self.sip_management = SIPManagement()
self.recording_events = RecordingEventsService()
self._filter_regex = None
@@ -247,10 +247,10 @@ class LiveKitEventsService:
if settings.ROOM_TELEPHONY_ENABLED or settings.ROOMKIT_ENABLED:
try:
self.telephony_service.create_dispatch_rule(room)
except TelephonyException as e:
self.sip_management.create_dispatch_rule(room)
except SIPException as e:
raise ActionFailedError(
f"Failed to create telephony dispatch rule for room {room_id}"
f"Failed to create sip dispatch rule for room {room_id}"
) from e
def _handle_room_finished(self, data):
@@ -267,10 +267,10 @@ class LiveKitEventsService:
if settings.ROOM_TELEPHONY_ENABLED or settings.ROOMKIT_ENABLED:
try:
self.telephony_service.delete_dispatch_rule(room_id)
except TelephonyException as e:
self.sip_management.delete_dispatch_rule(room_id)
except SIPException as e:
raise ActionFailedError(
f"Failed to delete telephony dispatch rule for room {room_id}"
f"Failed to delete sip dispatch rule for room {room_id}"
) from e
try:
@@ -1,4 +1,4 @@
"""Telephony service for managing SIP dispatch rules for room access."""
"""SIP management service for managing SIP dispatch rules for room access."""
from logging import getLogger
@@ -17,16 +17,16 @@ from core import utils
logger = getLogger(__name__)
class TelephonyException(Exception):
"""Exception raised when telephony operations fail."""
class SIPException(Exception):
"""Exception raised when SIP operations fail."""
class DispatchRuleConflictError(TelephonyException):
class DispatchRuleConflictError(SIPException):
"""Raised when a dispatch rule already exists for the same routing criteria."""
class TelephonyService:
"""Service for managing participant access through the telephony system (SIP)."""
class SIPManagement:
"""Service for managing SIP access through the telephony or roomkit system (SIP)."""
def _rule_name(self, room_id):
"""Generate the rule name for a room based on its ID."""
@@ -36,7 +36,7 @@ class TelephonyService:
async def create_dispatch_rule(self, room):
"""Create a SIP inbound dispatch rule for direct room routing.
Configures telephony to route incoming SIP calls directly to the specified room
Configures livekit-sip to route incoming SIP calls directly to the specified room
using the room's ID and PIN code for authentication.
"""
@@ -60,7 +60,7 @@ class TelephonyService:
logger.exception(
"Unexpected error creating dispatch rule for room %s", room.id
)
raise TelephonyException("Could not create dispatch rule") from e
raise SIPException("Could not create dispatch rule") from e
finally:
await lkapi.aclose()
@@ -85,7 +85,7 @@ class TelephonyService:
)
except TwirpError as e:
logger.exception("Failed to list dispatch rules for room %s", room_id)
raise TelephonyException("Could not list dispatch rules") from e
raise SIPException("Could not list dispatch rules") from e
finally:
await lkapi.aclose()
@@ -146,7 +146,7 @@ class TelephonyService:
except TwirpError as e:
logger.exception("Failed to delete dispatch rules for room %s", room_id)
raise TelephonyException("Could not delete dispatch rules") from e
raise SIPException("Could not delete dispatch rules") from e
finally:
await lkapi.aclose()
@@ -9,19 +9,19 @@ from unittest import mock
import pytest
from ...factories import RoomFactory
from ...services.telephony import TelephonyException
from ...services.sip_management import SIPException
pytestmark = pytest.mark.django_db
@pytest.fixture
def mock_telephony_service():
"""Mock the TelephonyService used by the roomkit viewset."""
with mock.patch("core.roomkit.viewsets.TelephonyService") as mock_service_class:
def mock_sip_management():
"""Mock the SIPManagement used by the roomkit viewset."""
with mock.patch("core.roomkit.viewsets.SIPManagement") as mock_service_class:
yield mock_service_class.return_value
def test_join_anonymous(settings, mock_telephony_service, client):
def test_join_anonymous(settings, mock_sip_management, client):
"""Requests without an Authorization header should be rejected."""
settings.ROOMKIT_ENABLED = True
settings.ROOMKIT_SERVER_TO_SERVER_API_TOKEN = "testAuthToken"
@@ -32,10 +32,10 @@ def test_join_anonymous(settings, mock_telephony_service, client):
assert response.status_code == 401
assert response.json() == {"detail": "Authorization header is missing."}
mock_telephony_service.ensure_dispatch_rule.assert_not_called()
mock_sip_management.ensure_dispatch_rule.assert_not_called()
def test_join_malformed_authorization_header(settings, mock_telephony_service, client):
def test_join_malformed_authorization_header(settings, mock_sip_management, client):
"""Requests with a malformed Authorization header should be rejected."""
settings.ROOMKIT_ENABLED = True
settings.ROOMKIT_SERVER_TO_SERVER_API_TOKEN = "testAuthToken"
@@ -50,10 +50,10 @@ def test_join_malformed_authorization_header(settings, mock_telephony_service, c
assert response.status_code == 401
assert response.json() == {"detail": "Invalid authorization header."}
mock_telephony_service.ensure_dispatch_rule.assert_not_called()
mock_sip_management.ensure_dispatch_rule.assert_not_called()
def test_join_wrong_bearer(settings, mock_telephony_service, client):
def test_join_wrong_bearer(settings, mock_sip_management, client):
"""Requests with an incorrect bearer token should be rejected."""
settings.ROOMKIT_ENABLED = True
settings.ROOMKIT_SERVER_TO_SERVER_API_TOKEN = "testAuthToken"
@@ -68,10 +68,10 @@ def test_join_wrong_bearer(settings, mock_telephony_service, client):
assert response.status_code == 401
assert response.json() == {"detail": "Invalid server-to-server token."}
mock_telephony_service.ensure_dispatch_rule.assert_not_called()
mock_sip_management.ensure_dispatch_rule.assert_not_called()
def test_join_token_not_configured(settings, mock_telephony_service, client):
def test_join_token_not_configured(settings, mock_sip_management, client):
"""Requests should be rejected when no server-to-server token is configured."""
settings.ROOMKIT_ENABLED = True
@@ -86,10 +86,10 @@ def test_join_token_not_configured(settings, mock_telephony_service, client):
)
assert response.status_code == 401
mock_telephony_service.ensure_dispatch_rule.assert_not_called()
mock_sip_management.ensure_dispatch_rule.assert_not_called()
def test_join_roomkit_disabled(settings, mock_telephony_service, client):
def test_join_roomkit_disabled(settings, mock_sip_management, client):
"""The endpoint should not be exposed when the roomkit integration is disabled."""
settings.ROOMKIT_ENABLED = False
@@ -104,10 +104,10 @@ def test_join_roomkit_disabled(settings, mock_telephony_service, client):
)
assert response.status_code == 404
mock_telephony_service.ensure_dispatch_rule.assert_not_called()
mock_sip_management.ensure_dispatch_rule.assert_not_called()
def test_join_missing_pin(settings, mock_telephony_service, client):
def test_join_missing_pin(settings, mock_sip_management, client):
"""Requests without a PIN code should be rejected."""
settings.ROOMKIT_ENABLED = True
@@ -121,10 +121,10 @@ def test_join_missing_pin(settings, mock_telephony_service, client):
assert response.status_code == 400
assert response.json() == {"pin_code": ["This field is required."]}
mock_telephony_service.ensure_dispatch_rule.assert_not_called()
mock_sip_management.ensure_dispatch_rule.assert_not_called()
def test_join_blank_pin(settings, mock_telephony_service, client):
def test_join_blank_pin(settings, mock_sip_management, client):
"""Requests with a blank PIN code should be rejected."""
settings.ROOMKIT_ENABLED = True
@@ -138,10 +138,10 @@ def test_join_blank_pin(settings, mock_telephony_service, client):
assert response.status_code == 400
assert response.json() == {"pin_code": ["This field may not be blank."]}
mock_telephony_service.ensure_dispatch_rule.assert_not_called()
mock_sip_management.ensure_dispatch_rule.assert_not_called()
def test_join_wrong_pin_length(settings, mock_telephony_service, client):
def test_join_wrong_pin_length(settings, mock_sip_management, client):
"""Requests with a PIN code of unexpected length should be rejected."""
settings.ROOMKIT_ENABLED = True
@@ -156,10 +156,10 @@ def test_join_wrong_pin_length(settings, mock_telephony_service, client):
assert response.status_code == 400
assert response.json() == {"pin_code": ["PIN code length is invalid."]}
mock_telephony_service.ensure_dispatch_rule.assert_not_called()
mock_sip_management.ensure_dispatch_rule.assert_not_called()
def test_join_unknown_pin(settings, mock_telephony_service, client):
def test_join_unknown_pin(settings, mock_sip_management, client):
"""Requests with a PIN matching no room should return 404 and create no rule."""
settings.ROOMKIT_ENABLED = True
settings.ROOMKIT_SERVER_TO_SERVER_API_TOKEN = "testAuthToken"
@@ -174,16 +174,16 @@ def test_join_unknown_pin(settings, mock_telephony_service, client):
assert response.status_code == 404
assert response.json() == {"detail": "No room found for this PIN code."}
mock_telephony_service.ensure_dispatch_rule.assert_not_called()
mock_sip_management.ensure_dispatch_rule.assert_not_called()
def test_join_success(settings, mock_telephony_service, client):
def test_join_success(settings, mock_sip_management, client):
"""Requests with a valid PIN should create the dispatch rule."""
settings.ROOMKIT_ENABLED = True
settings.ROOMKIT_SERVER_TO_SERVER_API_TOKEN = "testAuthToken"
room = RoomFactory(pin_code="1234567890")
mock_telephony_service.ensure_dispatch_rule.return_value = True
mock_sip_management.ensure_dispatch_rule.return_value = True
response = client.post(
"/api/v1.0/roomkit/join/",
@@ -193,17 +193,17 @@ def test_join_success(settings, mock_telephony_service, client):
assert response.status_code == 200
assert response.json() == {"status": "success"}
mock_telephony_service.ensure_dispatch_rule.assert_called_once_with(room)
mock_sip_management.ensure_dispatch_rule.assert_called_once_with(room)
def test_join_dispatch_rule_already_exists(settings, mock_telephony_service, client):
def test_join_dispatch_rule_already_exists(settings, mock_sip_management, client):
"""Requests should succeed when the dispatch rule already exists (idempotency)."""
settings.ROOMKIT_ENABLED = True
settings.ROOMKIT_SERVER_TO_SERVER_API_TOKEN = "testAuthToken"
room = RoomFactory(pin_code="1234567890")
mock_telephony_service.ensure_dispatch_rule.return_value = False
mock_sip_management.ensure_dispatch_rule.return_value = False
response = client.post(
"/api/v1.0/roomkit/join/",
@@ -213,17 +213,17 @@ def test_join_dispatch_rule_already_exists(settings, mock_telephony_service, cli
assert response.status_code == 200
assert response.json() == {"status": "success"}
mock_telephony_service.ensure_dispatch_rule.assert_called_once_with(room)
mock_sip_management.ensure_dispatch_rule.assert_called_once_with(room)
def test_join_tracks_analytics_event(settings, mock_telephony_service, client):
def test_join_tracks_analytics_event(settings, mock_sip_management, client):
"""Successful joins should be tracked with an analytics event."""
settings.ROOMKIT_ENABLED = True
settings.ROOMKIT_SERVER_TO_SERVER_API_TOKEN = "testAuthToken"
room = RoomFactory(pin_code="1234567890")
mock_telephony_service.ensure_dispatch_rule.return_value = True
mock_sip_management.ensure_dispatch_rule.return_value = True
with mock.patch("core.roomkit.viewsets.analytics.capture") as mock_capture:
response = client.post(
@@ -242,14 +242,14 @@ def test_join_tracks_analytics_event(settings, mock_telephony_service, client):
}
def test_join_telephony_failure(settings, mock_telephony_service, client):
"""Requests should fail with a server error when the telephony service fails."""
def test_join_sip_failure(settings, mock_sip_management, client):
"""Requests should fail with a server error when the sip management service fails."""
settings.ROOMKIT_ENABLED = True
settings.ROOMKIT_SERVER_TO_SERVER_API_TOKEN = "testAuthToken"
room = RoomFactory(pin_code="1234567890")
mock_telephony_service.ensure_dispatch_rule.side_effect = TelephonyException(
mock_sip_management.ensure_dispatch_rule.side_effect = SIPException(
"Could not create dispatch rule"
)
@@ -262,5 +262,5 @@ def test_join_telephony_failure(settings, mock_telephony_service, client):
)
assert response.status_code == 500
mock_telephony_service.ensure_dispatch_rule.assert_called_once_with(room)
mock_sip_management.ensure_dispatch_rule.assert_called_once_with(room)
mock_capture.assert_not_called()
@@ -21,7 +21,7 @@ from core.services.livekit_events import (
)
from core.services.lobby import LobbyService
from core.services.room_management import RoomManagementException
from core.services.telephony import TelephonyException, TelephonyService
from core.services.sip_management import SIPException, SIPManagement
from core.utils import NotificationError
pytestmark = pytest.mark.django_db
@@ -59,7 +59,7 @@ def test_initialization(
mock_token_verifier.assert_called_once_with(api_key, api_secret)
mock_webhook_receiver.assert_called_once_with(mock_token_verifier.return_value)
assert isinstance(service.lobby_service, LobbyService)
assert isinstance(service.telephony_service, TelephonyService)
assert isinstance(service.sip_management, SIPManagement)
assert isinstance(service.recording_events, RecordingEventsService)
@@ -469,11 +469,11 @@ def test_handle_egress_ended_ignores_non_savable_recording(
@mock.patch.object(LobbyService, "clear_room_cache")
@mock.patch.object(TelephonyService, "delete_dispatch_rule")
@mock.patch.object(SIPManagement, "delete_dispatch_rule")
def test_handle_room_finished_clears_cache_and_deletes_dispatch_rule(
mock_delete_dispatch_rule, mock_clear_cache, service, settings
):
"""Should clear lobby cache and delete telephony dispatch rule when room finishes."""
"""Should clear lobby cache and delete SIP dispatch rule when room finishes."""
settings.ROOM_TELEPHONY_ENABLED = True
mock_room_name = uuid.uuid4()
mock_data = mock.MagicMock()
@@ -486,7 +486,7 @@ def test_handle_room_finished_clears_cache_and_deletes_dispatch_rule(
@mock.patch.object(LobbyService, "clear_room_cache")
@mock.patch.object(TelephonyService, "delete_dispatch_rule")
@mock.patch.object(SIPManagement, "delete_dispatch_rule")
def test_handle_room_finished_deletes_dispatch_rule_when_only_roomkit_enabled(
mock_delete_dispatch_rule, mock_clear_cache, service, settings
):
@@ -504,7 +504,7 @@ def test_handle_room_finished_deletes_dispatch_rule_when_only_roomkit_enabled(
@mock.patch.object(LobbyService, "clear_room_cache")
@mock.patch.object(TelephonyService, "delete_dispatch_rule")
@mock.patch.object(SIPManagement, "delete_dispatch_rule")
def test_handle_room_finished_skips_telephony_when_disabled(
mock_delete_dispatch_rule, mock_clear_cache, service, settings
):
@@ -524,7 +524,7 @@ def test_handle_room_finished_skips_telephony_when_disabled(
@mock.patch.object(
LobbyService, "clear_room_cache", side_effect=Exception("Test error")
)
@mock.patch.object(TelephonyService, "delete_dispatch_rule")
@mock.patch.object(SIPManagement, "delete_dispatch_rule")
def test_handle_room_finished_raises_error_when_cache_clearing_fails(
mock_delete_dispatch_rule, mock_clear_cache, service, settings
):
@@ -547,9 +547,9 @@ def test_handle_room_finished_raises_error_when_cache_clearing_fails(
@mock.patch.object(LobbyService, "clear_room_cache")
@mock.patch.object(
TelephonyService,
SIPManagement,
"delete_dispatch_rule",
side_effect=TelephonyException("Test error"),
side_effect=SIPException("Test error"),
)
def test_handle_room_finished_raises_error_when_telephony_deletion_fails(
mock_delete_dispatch_rule, mock_clear_cache, service, settings
@@ -581,11 +581,11 @@ def test_handle_room_finished_raises_error_for_invalid_room_name(service):
service._handle_room_finished(mock_data)
@mock.patch.object(TelephonyService, "create_dispatch_rule")
@mock.patch.object(SIPManagement, "create_dispatch_rule")
def test_handle_room_started_creates_dispatch_rule_successfully(
mock_create_dispatch_rule, service, settings
):
"""Should create telephony dispatch rule when room starts successfully."""
"""Should create SIP dispatch rule when room starts successfully."""
settings.ROOM_TELEPHONY_ENABLED = True
room = RoomFactory()
mock_data = mock.MagicMock()
@@ -596,7 +596,7 @@ def test_handle_room_started_creates_dispatch_rule_successfully(
mock_create_dispatch_rule.assert_called_once_with(room)
@mock.patch.object(TelephonyService, "create_dispatch_rule")
@mock.patch.object(SIPManagement, "create_dispatch_rule")
def test_handle_room_started_creates_dispatch_rule_when_only_roomkit_enabled(
mock_create_dispatch_rule, service, settings
):
@@ -612,11 +612,11 @@ def test_handle_room_started_creates_dispatch_rule_when_only_roomkit_enabled(
mock_create_dispatch_rule.assert_called_once_with(room)
@mock.patch.object(TelephonyService, "create_dispatch_rule")
@mock.patch.object(SIPManagement, "create_dispatch_rule")
def test_handle_room_started_skips_dispatch_rule_when_telephony_disabled(
mock_create_dispatch_rule, service, settings
):
"""Should skip creating telephony dispatch rule when telephony is disabled during room start."""
"""Should skip creating SIP dispatch rule when telephony is disabled during room start."""
settings.ROOM_TELEPHONY_ENABLED = False
settings.ROOMKIT_ENABLED = False
room = RoomFactory()
@@ -1,5 +1,5 @@
"""
Test telephony service.
Test SIP mamagement service.
"""
# pylint: disable=W0212
@@ -20,10 +20,10 @@ from livekit.protocol.sip import (
from core.factories import RoomFactory
from core.models import RoomAccessLevel
from core.services.telephony import (
from core.services.sip_management import (
DispatchRuleConflictError,
TelephonyException,
TelephonyService,
SIPException,
SIPManagement,
)
pytestmark = pytest.mark.django_db
@@ -39,9 +39,9 @@ def create_mock_livekit_client():
def test_rule_name():
"""Test rule name generation."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
rule_name = telephony_service._rule_name(room.id)
rule_name = sip_management._rule_name(room.id)
assert rule_name == f"SIP_{str(room.id)}"
@@ -49,14 +49,14 @@ def test_rule_name():
@mock.patch("core.utils.create_livekit_client")
def test_create_dispatch_rule_success(mock_client_factory):
"""Test successful dispatch rule creation."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_api = create_mock_livekit_client()
mock_api.sip.create_sip_dispatch_rule = mock.AsyncMock()
mock_client_factory.return_value = mock_api
telephony_service.create_dispatch_rule(room)
sip_management.create_dispatch_rule(room)
mock_api.sip.create_sip_dispatch_rule.assert_called_once()
create_request = mock_api.sip.create_sip_dispatch_rule.call_args[1]["create"]
@@ -71,7 +71,7 @@ def test_create_dispatch_rule_success(mock_client_factory):
@mock.patch("core.utils.create_livekit_client")
def test_create_dispatch_rule_api_failure(mock_client_factory):
"""Test dispatch rule creation when API fails."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_api = create_mock_livekit_client()
@@ -80,8 +80,8 @@ def test_create_dispatch_rule_api_failure(mock_client_factory):
)
mock_client_factory.return_value = mock_api
with pytest.raises(TelephonyException, match="Could not create dispatch rule"):
telephony_service.create_dispatch_rule(room)
with pytest.raises(SIPException, match="Could not create dispatch rule"):
sip_management.create_dispatch_rule(room)
mock_api.sip.create_sip_dispatch_rule.assert_called_once()
mock_api.aclose.assert_called_once()
@@ -90,7 +90,7 @@ def test_create_dispatch_rule_api_failure(mock_client_factory):
@mock.patch("core.utils.create_livekit_client")
def test_list_dispatch_rules_ids_success(mock_client_factory):
"""Test successful listing of dispatch rule IDs."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_rules = [
@@ -115,7 +115,7 @@ def test_list_dispatch_rules_ids_success(mock_client_factory):
)
mock_client_factory.return_value = mock_api
result = async_to_sync(telephony_service._list_dispatch_rules_ids)(room.id)
result = async_to_sync(sip_management._list_dispatch_rules_ids)(room.id)
assert len(result) == 2
assert "rule-1" in result
@@ -131,7 +131,7 @@ def test_list_dispatch_rules_ids_success(mock_client_factory):
@mock.patch("core.utils.create_livekit_client")
def test_list_dispatch_rules_ids_empty_response(mock_client_factory):
"""Test listing dispatch rule IDs when no rules exist."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_api = create_mock_livekit_client()
@@ -140,7 +140,7 @@ def test_list_dispatch_rules_ids_empty_response(mock_client_factory):
)
mock_client_factory.return_value = mock_api
result = async_to_sync(telephony_service._list_dispatch_rules_ids)(room.id)
result = async_to_sync(sip_management._list_dispatch_rules_ids)(room.id)
assert result == []
mock_api.aclose.assert_called_once()
@@ -149,7 +149,7 @@ def test_list_dispatch_rules_ids_empty_response(mock_client_factory):
@mock.patch("core.utils.create_livekit_client")
def test_list_dispatch_rules_ids_no_matching_rules(mock_client_factory):
"""Test listing dispatch rule IDs when no rules match the room."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_rules = [
@@ -167,7 +167,7 @@ def test_list_dispatch_rules_ids_no_matching_rules(mock_client_factory):
)
mock_client_factory.return_value = mock_api
result = async_to_sync(telephony_service._list_dispatch_rules_ids)(room.id)
result = async_to_sync(sip_management._list_dispatch_rules_ids)(room.id)
assert result == []
mock_api.aclose.assert_called_once()
@@ -176,7 +176,7 @@ def test_list_dispatch_rules_ids_no_matching_rules(mock_client_factory):
@mock.patch("core.utils.create_livekit_client")
def test_list_dispatch_rules_ids_api_failure(mock_client_factory):
"""Test listing dispatch rule IDs when API fails."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_api = create_mock_livekit_client()
@@ -185,34 +185,34 @@ def test_list_dispatch_rules_ids_api_failure(mock_client_factory):
)
mock_client_factory.return_value = mock_api
with pytest.raises(TelephonyException, match="Could not list dispatch rules"):
async_to_sync(telephony_service._list_dispatch_rules_ids)(room.id)
with pytest.raises(SIPException, match="Could not list dispatch rules"):
async_to_sync(sip_management._list_dispatch_rules_ids)(room.id)
mock_api.sip.list_sip_dispatch_rule.assert_called_once()
mock_api.aclose.assert_called_once()
@mock.patch("core.services.telephony.TelephonyService._list_dispatch_rules_ids")
@mock.patch("core.services.sip_management.SIPManagement._list_dispatch_rules_ids")
@mock.patch("core.utils.create_livekit_client")
def test_delete_dispatch_rule_no_rules(mock_client_factory, mock_list_rules):
"""Test deleting dispatch rules when no rules exist."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_list_rules.return_value = []
result = telephony_service.delete_dispatch_rule(room.id)
result = sip_management.delete_dispatch_rule(room.id)
assert result is False
mock_list_rules.assert_called_once_with(room.id)
mock_client_factory.assert_not_called()
@mock.patch("core.services.telephony.TelephonyService._list_dispatch_rules_ids")
@mock.patch("core.services.sip_management.SIPManagement._list_dispatch_rules_ids")
@mock.patch("core.utils.create_livekit_client")
def test_delete_dispatch_rule_single_rule(mock_client_factory, mock_list_rules):
"""Test deleting a single dispatch rule."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_list_rules.return_value = ["rule-1"]
@@ -220,7 +220,7 @@ def test_delete_dispatch_rule_single_rule(mock_client_factory, mock_list_rules):
mock_api.sip.delete_sip_dispatch_rule = mock.AsyncMock()
mock_client_factory.return_value = mock_api
result = telephony_service.delete_dispatch_rule(room.id)
result = sip_management.delete_dispatch_rule(room.id)
assert result is True
mock_api.sip.delete_sip_dispatch_rule.assert_called_once()
@@ -230,11 +230,11 @@ def test_delete_dispatch_rule_single_rule(mock_client_factory, mock_list_rules):
mock_api.aclose.assert_called_once()
@mock.patch("core.services.telephony.TelephonyService._list_dispatch_rules_ids")
@mock.patch("core.services.sip_management.SIPManagement._list_dispatch_rules_ids")
@mock.patch("core.utils.create_livekit_client")
def test_delete_dispatch_rule_multiple_rules(mock_client_factory, mock_list_rules):
"""Test deleting multiple dispatch rules."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_list_rules.return_value = ["rule-1", "rule-2", "rule-3"]
@@ -242,7 +242,7 @@ def test_delete_dispatch_rule_multiple_rules(mock_client_factory, mock_list_rule
mock_api.sip.delete_sip_dispatch_rule = mock.AsyncMock()
mock_client_factory.return_value = mock_api
result = telephony_service.delete_dispatch_rule(room.id)
result = sip_management.delete_dispatch_rule(room.id)
assert result is True
assert mock_api.sip.delete_sip_dispatch_rule.call_count == 3
@@ -257,11 +257,11 @@ def test_delete_dispatch_rule_multiple_rules(mock_client_factory, mock_list_rule
mock_api.aclose.assert_called_once()
@mock.patch("core.services.telephony.TelephonyService._list_dispatch_rules_ids")
@mock.patch("core.services.sip_management.SIPManagement._list_dispatch_rules_ids")
@mock.patch("core.utils.create_livekit_client")
def test_delete_dispatch_rule_partial_failure(mock_client_factory, mock_list_rules):
"""Test deleting multiple dispatch rules when one deletion fails."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_list_rules.return_value = ["rule-1", "rule-2", "rule-3"]
@@ -281,18 +281,18 @@ def test_delete_dispatch_rule_partial_failure(mock_client_factory, mock_list_rul
)
mock_client_factory.return_value = mock_api
with pytest.raises(TelephonyException, match="Could not delete dispatch rules"):
telephony_service.delete_dispatch_rule(room.id)
with pytest.raises(SIPException, match="Could not delete dispatch rules"):
sip_management.delete_dispatch_rule(room.id)
assert mock_api.sip.delete_sip_dispatch_rule.call_count == 2
mock_api.aclose.assert_called_once()
@mock.patch("core.services.telephony.TelephonyService._list_dispatch_rules_ids")
@mock.patch("core.services.sip_management.SIPManagement._list_dispatch_rules_ids")
@mock.patch("core.utils.create_livekit_client")
def test_delete_dispatch_rule_api_failure(mock_client_factory, mock_list_rules):
"""Test deleting dispatch rules when API fails immediately."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_list_rules.return_value = ["rule-1"]
@@ -302,8 +302,8 @@ def test_delete_dispatch_rule_api_failure(mock_client_factory, mock_list_rules):
)
mock_client_factory.return_value = mock_api
with pytest.raises(TelephonyException, match="Could not delete dispatch rules"):
telephony_service.delete_dispatch_rule(room.id)
with pytest.raises(SIPException, match="Could not delete dispatch rules"):
sip_management.delete_dispatch_rule(room.id)
mock_api.sip.delete_sip_dispatch_rule.assert_called_once()
mock_api.aclose.assert_called_once()
@@ -312,7 +312,7 @@ def test_delete_dispatch_rule_api_failure(mock_client_factory, mock_list_rules):
@mock.patch("core.utils.create_livekit_client")
def test_create_dispatch_rule_conflict_raises_dedicated_error(mock_client_factory):
"""Test that a LiveKit conflict error raises DispatchRuleConflictError."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_api = create_mock_livekit_client()
@@ -329,7 +329,7 @@ def test_create_dispatch_rule_conflict_raises_dedicated_error(mock_client_factor
mock_client_factory.return_value = mock_api
with pytest.raises(DispatchRuleConflictError):
telephony_service.create_dispatch_rule(room)
sip_management.create_dispatch_rule(room)
mock_api.aclose.assert_called_once()
@@ -337,7 +337,7 @@ def test_create_dispatch_rule_conflict_raises_dedicated_error(mock_client_factor
@mock.patch("core.utils.create_livekit_client")
def test_ensure_dispatch_rule_creates_when_missing(mock_client_factory):
"""Test that ensure_dispatch_rule creates the rule when none exists."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_api = create_mock_livekit_client()
@@ -347,7 +347,7 @@ def test_ensure_dispatch_rule_creates_when_missing(mock_client_factory):
mock_api.sip.create_sip_dispatch_rule = mock.AsyncMock()
mock_client_factory.return_value = mock_api
created = telephony_service.ensure_dispatch_rule(room)
created = sip_management.ensure_dispatch_rule(room)
assert created is True
mock_api.sip.create_sip_dispatch_rule.assert_called_once()
@@ -362,7 +362,7 @@ def test_ensure_dispatch_rule_creates_when_missing(mock_client_factory):
@mock.patch("core.utils.create_livekit_client")
def test_ensure_dispatch_rule_skips_when_existing(mock_client_factory):
"""Test that ensure_dispatch_rule is idempotent when the rule already exists."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
existing_rule = SIPDispatchRuleInfo(
@@ -375,7 +375,7 @@ def test_ensure_dispatch_rule_skips_when_existing(mock_client_factory):
mock_api.sip.create_sip_dispatch_rule = mock.AsyncMock()
mock_client_factory.return_value = mock_api
created = telephony_service.ensure_dispatch_rule(room)
created = sip_management.ensure_dispatch_rule(room)
assert created is False
mock_api.sip.create_sip_dispatch_rule.assert_not_called()
@@ -389,7 +389,7 @@ def test_ensure_dispatch_rule_returns_false_on_conflict(mock_client_factory):
between the existence check and the creation, LiveKit rejects the
duplicate and ensure_dispatch_rule reports the rule as already existing.
"""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_api = create_mock_livekit_client()
@@ -408,7 +408,7 @@ def test_ensure_dispatch_rule_returns_false_on_conflict(mock_client_factory):
)
mock_client_factory.return_value = mock_api
created = telephony_service.ensure_dispatch_rule(room)
created = sip_management.ensure_dispatch_rule(room)
assert created is False
@@ -416,7 +416,7 @@ def test_ensure_dispatch_rule_returns_false_on_conflict(mock_client_factory):
@mock.patch("core.utils.create_livekit_client")
def test_ensure_dispatch_rule_raises_on_other_failures(mock_client_factory):
"""Test that ensure_dispatch_rule propagates unexpected LiveKit failures."""
telephony_service = TelephonyService()
sip_management = SIPManagement()
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED, pin_code="1234")
mock_api = create_mock_livekit_client()
@@ -428,5 +428,5 @@ def test_ensure_dispatch_rule_raises_on_other_failures(mock_client_factory):
)
mock_client_factory.return_value = mock_api
with pytest.raises(TelephonyException, match="Could not create dispatch rule"):
telephony_service.ensure_dispatch_rule(room)
with pytest.raises(SIPException, match="Could not create dispatch rule"):
sip_management.ensure_dispatch_rule(room)