Esempio n. 1
0
 def setUp(self) -> None:
     super().setUp()
     aiohttp_client_session = aiohttp.ClientSession()
     self.ws_connection = WSConnection(aiohttp_client_session)
     self.ws_assistant = WSAssistant(self.ws_connection)
class WSConnectionTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        cls.ev_loop = asyncio.get_event_loop()
        cls.ws_url = "ws://some/url"

    def setUp(self) -> None:
        super().setUp()
        self.mocking_assistant = NetworkMockingAssistant()
        self.client_session = aiohttp.ClientSession()
        self.ws_connection = WSConnection(self.client_session)
        self.async_tasks: List[asyncio.Task] = []

    def tearDown(self) -> None:
        self.ws_connection.disconnect()
        self.client_session.close()
        for task in self.async_tasks:
            task.cancel()
        super().tearDown()

    def async_run_with_timeout(self, coroutine: Awaitable, timeout: int = 1):
        ret = self.ev_loop.run_until_complete(
            asyncio.wait_for(coroutine, timeout))
        return ret

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_connect_and_disconnect(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )

        self.assertFalse(self.ws_connection.connected)

        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))

        self.assertTrue(self.ws_connection.connected)

        self.async_run_with_timeout(self.ws_connection.disconnect())

        self.assertFalse(self.ws_connection.connected)

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_attempt_to_connect_second_time_raises(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))

        with self.assertRaises(RuntimeError) as e:
            self.async_run_with_timeout(self.ws_connection.connect(
                self.ws_url))

        self.assertEqual("WS is connected.", str(e.exception))

    def test_send_when_disconnected_raises(self):
        request = WSRequest(payload={"one": 1})

        with self.assertRaises(RuntimeError) as e:
            self.async_run_with_timeout(self.ws_connection.send(request))

        self.assertEqual("WS is not connected.", str(e.exception))

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_send(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))
        request = WSRequest(payload={"one": 1})

        self.async_run_with_timeout(self.ws_connection.send(request))

        json_msgs = self.mocking_assistant.json_messages_sent_through_websocket(
            ws_connect_mock.return_value)

        self.assertEqual(1, len(json_msgs))
        self.assertEqual(request.payload, json_msgs[0])

    def test_receive_when_disconnected_raises(self):
        with self.assertRaises(RuntimeError) as e:
            self.async_run_with_timeout(self.ws_connection.receive())

        self.assertEqual("WS is not connected.", str(e.exception))

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_receive_raises_on_timeout(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )

        def raise_timeout(*_, **__):
            raise asyncio.TimeoutError

        ws_connect_mock.return_value.receive.side_effect = raise_timeout
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))

        with self.assertRaises(asyncio.TimeoutError) as e:
            self.async_run_with_timeout(self.ws_connection.receive())

        self.assertEqual("Message receive timed out.", str(e.exception))

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_receive(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))
        data = {"one": 1}
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value, message=json.dumps(data))

        self.assertEqual(0, self.ws_connection.last_recv_time)

        response = self.async_run_with_timeout(self.ws_connection.receive())

        self.assertIsInstance(response, WSResponse)
        self.assertEqual(data, response.data)
        self.assertNotEqual(0, self.ws_connection.last_recv_time)

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_receive_disconnects_and_raises_on_aiohttp_closed(
            self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        ws_connect_mock.return_value.close_code = 1111
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value,
            message="",
            message_type=aiohttp.WSMsgType.CLOSED)

        with self.assertRaises(ConnectionError) as e:
            self.async_run_with_timeout(self.ws_connection.receive())

        self.assertEqual(
            "The WS connection was closed unexpectedly. Close code = 1111 msg data: ",
            str(e.exception))
        self.assertFalse(self.ws_connection.connected)

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_receive_disconnects_and_raises_on_aiohttp_close(
            self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        ws_connect_mock.return_value.close_code = 1111
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value,
            message="",
            message_type=aiohttp.WSMsgType.CLOSE)

        with self.assertRaises(ConnectionError) as e:
            self.async_run_with_timeout(self.ws_connection.receive())

        self.assertEqual(
            "The WS connection was closed unexpectedly. Close code = 1111 msg data: ",
            str(e.exception))
        self.assertFalse(self.ws_connection.connected)

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_receive_ignores_aiohttp_close_msg_if_disconnect_called(
            self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value,
            message="",
            message_type=aiohttp.WSMsgType.CLOSED)
        prev_side_effect = ws_connect_mock.return_value.receive.side_effect

        async def disconnect_on_side_effect(*args, **kwargs):
            await self.ws_connection.disconnect()
            return await prev_side_effect(*args, **kwargs)

        ws_connect_mock.return_value.receive.side_effect = disconnect_on_side_effect

        response = self.async_run_with_timeout(self.ws_connection.receive())

        self.assertFalse(self.ws_connection.connected)
        self.assertIsNone(response)

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_receive_ignores_ping(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value,
            message="",
            message_type=aiohttp.WSMsgType.PING)
        data = {"one": 1}
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value, message=json.dumps(data))

        response = self.async_run_with_timeout(self.ws_connection.receive())

        self.assertEqual(data, response.data)

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_receive_sends_pong_on_ping(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value,
            message="",
            message_type=aiohttp.WSMsgType.PING)
        receive_task = self.ev_loop.create_task(self.ws_connection.receive())
        self.async_tasks.append(receive_task)

        self.mocking_assistant.run_until_all_aiohttp_messages_delivered(
            ws_connect_mock.return_value)

        ws_connect_mock.return_value.pong.assert_called()

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_receive_ping_updates_last_recv_time(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value,
            message="",
            message_type=aiohttp.WSMsgType.PING)
        receive_task = self.ev_loop.create_task(self.ws_connection.receive())
        self.async_tasks.append(receive_task)

        self.assertEqual(0, self.ws_connection.last_recv_time)

        self.mocking_assistant.run_until_all_aiohttp_messages_delivered(
            ws_connect_mock.return_value)

        self.assertNotEqual(0, self.ws_connection.last_recv_time)

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_receive_ignores_pong(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value,
            message="",
            message_type=aiohttp.WSMsgType.PONG)
        data = {"one": 1}
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value, message=json.dumps(data))

        response = self.async_run_with_timeout(self.ws_connection.receive())

        self.assertEqual(data, response.data)

    @patch("aiohttp.client.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_receive_pong_updates_last_recv_time(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        self.async_run_with_timeout(self.ws_connection.connect(self.ws_url))
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value,
            message="",
            message_type=aiohttp.WSMsgType.PONG)
        receive_task = self.ev_loop.create_task(self.ws_connection.receive())
        self.async_tasks.append(receive_task)

        self.assertEqual(0, self.ws_connection.last_recv_time)

        self.mocking_assistant.run_until_all_aiohttp_messages_delivered(
            ws_connect_mock.return_value)

        self.assertNotEqual(0, self.ws_connection.last_recv_time)
 def setUp(self) -> None:
     super().setUp()
     self.mocking_assistant = NetworkMockingAssistant()
     self.client_session = aiohttp.ClientSession()
     self.ws_connection = WSConnection(self.client_session)
     self.async_tasks: List[asyncio.Task] = []
Esempio n. 4
0
 async def get_ws_connection(self) -> WSConnection:
     shared_client = await self._get_shared_client()
     connection = WSConnection(aiohttp_client_session=shared_client)
     return connection