From ca80540f7e32dab3fc1e866b65ea6e05f1acb3c2 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Thu, 30 Jul 2026 19:46:30 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=9A(backend)=20rename=20TelephonyServi?= =?UTF-8?q?ce=20to=20SIPManagement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- CHANGELOG.md | 1 + src/backend/core/roomkit/viewsets.py | 8 +- src/backend/core/services/livekit_events.py | 16 ++-- .../{telephony.py => sip_management.py} | 20 ++-- .../tests/roomkit/test_api_roomkit_join.py | 68 ++++++------- .../tests/services/test_livekit_events.py | 28 +++--- ...hony_service.py => test_sip_management.py} | 96 +++++++++---------- 7 files changed, 119 insertions(+), 118 deletions(-) rename src/backend/core/services/{telephony.py => sip_management.py} (86%) rename src/backend/core/tests/services/{test_telephony_service.py => test_sip_management.py} (83%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1396f3ca..4ac7e7f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/backend/core/roomkit/viewsets.py b/src/backend/core/roomkit/viewsets.py index 7792b5d8..9ffcfb67 100644 --- a/src/backend/core/roomkit/viewsets.py +++ b/src/backend/core/roomkit/viewsets.py @@ -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( diff --git a/src/backend/core/services/livekit_events.py b/src/backend/core/services/livekit_events.py index c5f44c17..32e7be98 100644 --- a/src/backend/core/services/livekit_events.py +++ b/src/backend/core/services/livekit_events.py @@ -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: diff --git a/src/backend/core/services/telephony.py b/src/backend/core/services/sip_management.py similarity index 86% rename from src/backend/core/services/telephony.py rename to src/backend/core/services/sip_management.py index ef570ff8..0c934b8c 100644 --- a/src/backend/core/services/telephony.py +++ b/src/backend/core/services/sip_management.py @@ -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() diff --git a/src/backend/core/tests/roomkit/test_api_roomkit_join.py b/src/backend/core/tests/roomkit/test_api_roomkit_join.py index cce2c8b5..894d3fb0 100644 --- a/src/backend/core/tests/roomkit/test_api_roomkit_join.py +++ b/src/backend/core/tests/roomkit/test_api_roomkit_join.py @@ -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() diff --git a/src/backend/core/tests/services/test_livekit_events.py b/src/backend/core/tests/services/test_livekit_events.py index 127b8880..e0a1054f 100644 --- a/src/backend/core/tests/services/test_livekit_events.py +++ b/src/backend/core/tests/services/test_livekit_events.py @@ -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() diff --git a/src/backend/core/tests/services/test_telephony_service.py b/src/backend/core/tests/services/test_sip_management.py similarity index 83% rename from src/backend/core/tests/services/test_telephony_service.py rename to src/backend/core/tests/services/test_sip_management.py index 3cdc8ab6..75a14139 100644 --- a/src/backend/core/tests/services/test_telephony_service.py +++ b/src/backend/core/tests/services/test_sip_management.py @@ -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)