test_aiohttp.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. # Copyright 2024 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import asyncio
  15. from aioresponses import aioresponses # type: ignore
  16. from mock import AsyncMock, Mock, patch
  17. import pytest # type: ignore
  18. import pytest_asyncio # type: ignore
  19. from google.auth import exceptions
  20. import google.auth.aio.transport.aiohttp as auth_aiohttp
  21. try:
  22. import aiohttp # type: ignore
  23. except ImportError as caught_exc: # pragma: NO COVER
  24. raise ImportError(
  25. "The aiohttp library is not installed from please install the aiohttp package to use the aiohttp transport."
  26. ) from caught_exc
  27. @pytest.fixture
  28. def mock_response():
  29. response = Mock()
  30. response.status = 200
  31. response.headers = {"Content-Type": "application/json", "Content-Length": "100"}
  32. mock_iterator = AsyncMock()
  33. mock_iterator.__aiter__.return_value = iter(
  34. [b"Cavefish ", b"have ", b"no ", b"sight."]
  35. )
  36. response.content.iter_chunked = lambda chunk_size: mock_iterator
  37. response.read = AsyncMock(return_value=b"Cavefish have no sight.")
  38. response.close = AsyncMock()
  39. return auth_aiohttp.Response(response)
  40. class TestResponse(object):
  41. @pytest.mark.asyncio
  42. async def test_response_status_code(self, mock_response):
  43. assert mock_response.status_code == 200
  44. @pytest.mark.asyncio
  45. async def test_response_headers(self, mock_response):
  46. assert mock_response.headers["Content-Type"] == "application/json"
  47. assert mock_response.headers["Content-Length"] == "100"
  48. @pytest.mark.asyncio
  49. async def test_response_content(self, mock_response):
  50. content = b"".join([chunk async for chunk in mock_response.content()])
  51. assert content == b"Cavefish have no sight."
  52. @pytest.mark.asyncio
  53. async def test_response_content_raises_error(self, mock_response):
  54. with patch.object(
  55. mock_response._response.content,
  56. "iter_chunked",
  57. side_effect=aiohttp.ClientPayloadError,
  58. ):
  59. with pytest.raises(exceptions.ResponseError) as exc:
  60. [chunk async for chunk in mock_response.content()]
  61. exc.match("Failed to read from the payload stream")
  62. @pytest.mark.asyncio
  63. async def test_response_read(self, mock_response):
  64. content = await mock_response.read()
  65. assert content == b"Cavefish have no sight."
  66. @pytest.mark.asyncio
  67. async def test_response_read_raises_error(self, mock_response):
  68. with patch.object(
  69. mock_response._response,
  70. "read",
  71. side_effect=aiohttp.ClientResponseError(None, None),
  72. ):
  73. with pytest.raises(exceptions.ResponseError) as exc:
  74. await mock_response.read()
  75. exc.match("Failed to read the response body.")
  76. @pytest.mark.asyncio
  77. async def test_response_close(self, mock_response):
  78. await mock_response.close()
  79. mock_response._response.close.assert_called_once()
  80. @pytest.mark.asyncio
  81. async def test_response_content_stream(self, mock_response):
  82. itr = mock_response.content().__aiter__()
  83. content = []
  84. try:
  85. while True:
  86. chunk = await itr.__anext__()
  87. content.append(chunk)
  88. except StopAsyncIteration:
  89. pass
  90. assert b"".join(content) == b"Cavefish have no sight."
  91. @pytest.mark.asyncio
  92. class TestRequest:
  93. @pytest_asyncio.fixture
  94. async def aiohttp_request(self):
  95. request = auth_aiohttp.Request()
  96. yield request
  97. await request.close()
  98. async def test_request_call_success(self, aiohttp_request):
  99. with aioresponses() as m:
  100. mocked_chunks = [b"Cavefish ", b"have ", b"no ", b"sight."]
  101. mocked_response = b"".join(mocked_chunks)
  102. m.get("http://example.com", status=200, body=mocked_response)
  103. response = await aiohttp_request("http://example.com")
  104. assert response.status_code == 200
  105. assert response.headers == {"Content-Type": "application/json"}
  106. content = b"".join([chunk async for chunk in response.content()])
  107. assert content == b"Cavefish have no sight."
  108. async def test_request_call_success_with_provided_session(self):
  109. mock_session = aiohttp.ClientSession()
  110. request = auth_aiohttp.Request(mock_session)
  111. with aioresponses() as m:
  112. mocked_chunks = [b"Cavefish ", b"have ", b"no ", b"sight."]
  113. mocked_response = b"".join(mocked_chunks)
  114. m.get("http://example.com", status=200, body=mocked_response)
  115. response = await request("http://example.com")
  116. assert response.status_code == 200
  117. assert response.headers == {"Content-Type": "application/json"}
  118. content = b"".join([chunk async for chunk in response.content()])
  119. assert content == b"Cavefish have no sight."
  120. async def test_request_call_raises_client_error(self, aiohttp_request):
  121. with aioresponses() as m:
  122. m.get("http://example.com", exception=aiohttp.ClientError)
  123. with pytest.raises(exceptions.TransportError) as exc:
  124. await aiohttp_request("http://example.com/api")
  125. exc.match("Failed to send request to http://example.com/api.")
  126. async def test_request_call_raises_timeout_error(self, aiohttp_request):
  127. with aioresponses() as m:
  128. m.get("http://example.com", exception=asyncio.TimeoutError)
  129. with pytest.raises(exceptions.TimeoutError) as exc:
  130. await aiohttp_request("http://example.com")
  131. exc.match("Request timed out after 180 seconds.")
  132. async def test_request_call_raises_transport_error_for_closed_session(
  133. self, aiohttp_request
  134. ):
  135. with aioresponses() as m:
  136. m.get("http://example.com", exception=asyncio.TimeoutError)
  137. aiohttp_request._closed = True
  138. with pytest.raises(exceptions.TransportError) as exc:
  139. await aiohttp_request("http://example.com")
  140. exc.match("session is closed.")
  141. aiohttp_request._closed = False