From e49ddc6143fa3acd8727bb29e00206ed5277b4d4 Mon Sep 17 00:00:00 2001 From: Kendall Goto Date: Thu, 17 Jul 2025 16:21:47 -0700 Subject: [PATCH 1/7] fix logger.warning() typo; suppress broadcast runtime errors --- app/socket_connection_manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/socket_connection_manager.py b/app/socket_connection_manager.py index 8fc54bd3..acf13644 100644 --- a/app/socket_connection_manager.py +++ b/app/socket_connection_manager.py @@ -99,10 +99,10 @@ async def broadcast(self, message: Union[str, dict, list]) -> None: ) except RuntimeError as e: logger.warning( - f'Failed to send: "{message}" to websocket: "{websocket}."', - 'Error:"{e}"', + f'Failed to send: "{message}" to websocket: "{websocket}."' + f' error: "{e}"', ) - raise e + # raise e async def received_message(self, websocket: WebSocket, message: str) -> None: try: From 30d9318d58df03adad86aa475cb2c75946c97276 Mon Sep 17 00:00:00 2001 From: Kendall Goto Date: Tue, 22 Jul 2025 19:23:03 -0700 Subject: [PATCH 2/7] mark todo for runtime error resolution --- app/socket_connection_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/socket_connection_manager.py b/app/socket_connection_manager.py index acf13644..b63ff365 100644 --- a/app/socket_connection_manager.py +++ b/app/socket_connection_manager.py @@ -98,11 +98,11 @@ async def broadcast(self, message: Union[str, dict, list]) -> None: f' to websocket: "{websocket}", connection closed."' ) except RuntimeError as e: + # TODO: Determine / resolve underlying cause of mismanaged connections resulting in RuntimeErrors logger.warning( f'Failed to send: "{message}" to websocket: "{websocket}."' f' error: "{e}"', ) - # raise e async def received_message(self, websocket: WebSocket, message: str) -> None: try: From 9ba4f25487ea341979834b14c39b65a27cb9aedd Mon Sep 17 00:00:00 2001 From: Kendall Goto Date: Tue, 22 Jul 2025 19:28:19 -0700 Subject: [PATCH 3/7] remove no longer valid RuntimeError test --- .../test_socket_connection_manager.py | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/app/tests/socket_connection_manager/test_socket_connection_manager.py b/app/tests/socket_connection_manager/test_socket_connection_manager.py index 3408129c..373631b4 100644 --- a/app/tests/socket_connection_manager/test_socket_connection_manager.py +++ b/app/tests/socket_connection_manager/test_socket_connection_manager.py @@ -203,39 +203,6 @@ async def test_broadcast_failed_for_ConnectionClosed() -> None: # Cleanup socket_connection_manager.active_connections.clear() - -@pytest.mark.asyncio -async def test_broadcast_failed_for_RuntimeError() -> None: - """ - Tests if broadcast() is able to handle run time errors. - - Expected results: - 1. RuntimeError is raised - 2. "Send" is called in an attempt to broadcast - """ - test_message = "Test" - expected_parameter = {"type": "websocket.send", "text": test_message} - - # Add a websocket object to the "active_connections" list to imitate - # an existing active connection - socket_connection_manager.active_connections.clear() - socket = mock.MagicMock(spec=WebSocket) - connection = WebSocketConnection(socket, WebSocketTypeEnum.MAIN) - socket_connection_manager.active_connections.append(connection) - assert len(socket_connection_manager.active_connections) == 1 - - socket.send_text.side_effect = RuntimeError( - 'Cannot call "receive" once a disconnect message has been received.' - ) - - with pytest.raises(RuntimeError): - await socket_connection_manager.broadcast(message=test_message) - socket.send_text.assert_called_with(expected_parameter) - - # Cleanup - socket_connection_manager.active_connections.clear() - - @pytest.mark.asyncio async def test_received_message_valid_json() -> None: """ From 6bc3a1a891e2099fb906317ce3405cb11f9e461f Mon Sep 17 00:00:00 2001 From: Kendall Goto Date: Tue, 22 Jul 2025 19:40:10 -0700 Subject: [PATCH 4/7] test broadcast behavior with one bad, one good socket --- .../test_socket_connection_manager.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/tests/socket_connection_manager/test_socket_connection_manager.py b/app/tests/socket_connection_manager/test_socket_connection_manager.py index 373631b4..4e575e0d 100644 --- a/app/tests/socket_connection_manager/test_socket_connection_manager.py +++ b/app/tests/socket_connection_manager/test_socket_connection_manager.py @@ -203,6 +203,42 @@ async def test_broadcast_failed_for_ConnectionClosed() -> None: # Cleanup socket_connection_manager.active_connections.clear() +@pytest.mark.asyncio +async def test_broadcast_with_failed_connection() -> None: + """ + Tests if broadcast() is able to output to multiple connections, where one is closed / failed. + + Expected results: + 1. Broadcast runs without an error, skipping over the bad connection + 2. Message is sent successfully on the working connection + """ + test_message = "Test" + expected_parameter = {"type": "websocket.send", "text": test_message} + socket_connection_manager.active_connections.clear() + + # Add a closed socket to the active connection list + socket_bad = mock.MagicMock(spec=WebSocket) + connection = WebSocketConnection(socket_bad, WebSocketTypeEnum.MAIN) + socket_connection_manager.active_connections.append(connection) + assert len(socket_connection_manager.active_connections) == 1 + # Force a connection closed exception + socket_bad.send_text.side_effect = RuntimeError( + 'Cannot call "receive" once a disconnect message has been received.' + ) + + # Add a working socket to the active connection list + socket_good = mock.MagicMock(spec=WebSocket) + connection = WebSocketConnection(socket_good, WebSocketTypeEnum.MAIN) + socket_connection_manager.active_connections.append(connection) + assert len(socket_connection_manager.active_connections) == 2 + + await socket_connection_manager.broadcast(message=test_message) + socket_bad.send_text.assert_called_with(expected_parameter) + socket_good.send_text.assert_called_with(expected_parameter) + + # Cleanup + socket_connection_manager.active_connections.clear() + @pytest.mark.asyncio async def test_received_message_valid_json() -> None: """ From 3dcf2e1f2e1121fe4582a2204bf6fd3cdd6fe637 Mon Sep 17 00:00:00 2001 From: Kendall Goto Date: Tue, 22 Jul 2025 19:43:34 -0700 Subject: [PATCH 5/7] lint test_socket_connection_manager.py --- .../test_socket_connection_manager.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/tests/socket_connection_manager/test_socket_connection_manager.py b/app/tests/socket_connection_manager/test_socket_connection_manager.py index 4e575e0d..04fe710e 100644 --- a/app/tests/socket_connection_manager/test_socket_connection_manager.py +++ b/app/tests/socket_connection_manager/test_socket_connection_manager.py @@ -203,10 +203,12 @@ async def test_broadcast_failed_for_ConnectionClosed() -> None: # Cleanup socket_connection_manager.active_connections.clear() + @pytest.mark.asyncio async def test_broadcast_with_failed_connection() -> None: """ - Tests if broadcast() is able to output to multiple connections, where one is closed / failed. + Tests if broadcast() is able to output to multiple connections, + where one is closed / failed. Expected results: 1. Broadcast runs without an error, skipping over the bad connection @@ -239,6 +241,7 @@ async def test_broadcast_with_failed_connection() -> None: # Cleanup socket_connection_manager.active_connections.clear() + @pytest.mark.asyncio async def test_received_message_valid_json() -> None: """ From ab1b4214864b84990320bd4e079ded2ccac6b4c4 Mon Sep 17 00:00:00 2001 From: Kendall Goto Date: Tue, 22 Jul 2025 19:44:39 -0700 Subject: [PATCH 6/7] lint socket_connection_manager.py --- app/socket_connection_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/socket_connection_manager.py b/app/socket_connection_manager.py index b63ff365..36f87590 100644 --- a/app/socket_connection_manager.py +++ b/app/socket_connection_manager.py @@ -98,7 +98,8 @@ async def broadcast(self, message: Union[str, dict, list]) -> None: f' to websocket: "{websocket}", connection closed."' ) except RuntimeError as e: - # TODO: Determine / resolve underlying cause of mismanaged connections resulting in RuntimeErrors + # TODO: Determine / resolve underlying cause of mismanaged + # connections resulting in RuntimeErrors logger.warning( f'Failed to send: "{message}" to websocket: "{websocket}."' f' error: "{e}"', From 19e43795e60857e0ee5d6f93e8214082e17d64cd Mon Sep 17 00:00:00 2001 From: Kendall Goto Date: Tue, 22 Jul 2025 19:48:05 -0700 Subject: [PATCH 7/7] correct test_broadcast_with_failed_connection expected val --- .../test_socket_connection_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/tests/socket_connection_manager/test_socket_connection_manager.py b/app/tests/socket_connection_manager/test_socket_connection_manager.py index 04fe710e..33fd42e7 100644 --- a/app/tests/socket_connection_manager/test_socket_connection_manager.py +++ b/app/tests/socket_connection_manager/test_socket_connection_manager.py @@ -215,7 +215,6 @@ async def test_broadcast_with_failed_connection() -> None: 2. Message is sent successfully on the working connection """ test_message = "Test" - expected_parameter = {"type": "websocket.send", "text": test_message} socket_connection_manager.active_connections.clear() # Add a closed socket to the active connection list @@ -235,8 +234,8 @@ async def test_broadcast_with_failed_connection() -> None: assert len(socket_connection_manager.active_connections) == 2 await socket_connection_manager.broadcast(message=test_message) - socket_bad.send_text.assert_called_with(expected_parameter) - socket_good.send_text.assert_called_with(expected_parameter) + socket_bad.send_text.assert_called_with(test_message) + socket_good.send_text.assert_called_with(test_message) # Cleanup socket_connection_manager.active_connections.clear()