test_api_provider.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. from http import HTTPStatus
  2. from unittest import mock
  3. import pytest
  4. from aiohttp import ClientSession
  5. from clean_python import ctx
  6. from clean_python import Tenant
  7. from clean_python.api_client import ApiException
  8. from clean_python.api_client import ApiProvider
  9. MODULE = "clean_python.api_client.api_provider"
  10. @pytest.fixture
  11. def tenant() -> Tenant:
  12. ctx.tenant = Tenant(id=2, name="")
  13. yield ctx.tenant
  14. ctx.tenant = None
  15. @pytest.fixture
  16. def response():
  17. # this mocks the aiohttp.ClientResponse:
  18. response = mock.Mock()
  19. response.status = int(HTTPStatus.OK)
  20. response.headers = {"Content-Type": "application/json"}
  21. response.json = mock.AsyncMock(return_value={"foo": 2})
  22. response.read = mock.AsyncMock()
  23. return response
  24. @pytest.fixture
  25. def api_provider(tenant, response) -> ApiProvider:
  26. request = mock.AsyncMock()
  27. with mock.patch.object(ClientSession, "request", new=request):
  28. api_provider = ApiProvider(
  29. url="http://testserver/foo/",
  30. fetch_token=lambda a, b: f"tenant-{b}",
  31. )
  32. api_provider._session.request.return_value = response
  33. yield api_provider
  34. async def test_get(api_provider: ApiProvider, response):
  35. actual = await api_provider.request("GET", "")
  36. assert api_provider._session.request.call_count == 1
  37. assert api_provider._session.request.call_args[1] == dict(
  38. method="GET",
  39. url="http://testserver/foo",
  40. headers={"Authorization": "Bearer tenant-2"},
  41. timeout=5.0,
  42. data=None,
  43. json=None,
  44. )
  45. assert actual == {"foo": 2}
  46. async def test_post_json(api_provider: ApiProvider, response):
  47. response.status == int(HTTPStatus.CREATED)
  48. api_provider._session.request.return_value = response
  49. actual = await api_provider.request("POST", "bar", json={"foo": 2})
  50. assert api_provider._session.request.call_count == 1
  51. assert api_provider._session.request.call_args[1] == dict(
  52. method="POST",
  53. url="http://testserver/foo/bar",
  54. data=None,
  55. json={"foo": 2},
  56. headers={
  57. "Authorization": "Bearer tenant-2",
  58. },
  59. timeout=5.0,
  60. )
  61. assert actual == {"foo": 2}
  62. @pytest.mark.parametrize(
  63. "path,params,expected_url",
  64. [
  65. ("", None, "http://testserver/foo"),
  66. ("bar", None, "http://testserver/foo/bar"),
  67. ("bar/", None, "http://testserver/foo/bar"),
  68. ("", {"a": 2}, "http://testserver/foo?a=2"),
  69. ("bar", {"a": 2}, "http://testserver/foo/bar?a=2"),
  70. ("bar/", {"a": 2}, "http://testserver/foo/bar?a=2"),
  71. ("", {"a": [1, 2]}, "http://testserver/foo?a=1&a=2"),
  72. ("", {"a": 1, "b": "foo"}, "http://testserver/foo?a=1&b=foo"),
  73. ],
  74. )
  75. async def test_url(api_provider: ApiProvider, path, params, expected_url):
  76. await api_provider.request("GET", path, params=params)
  77. assert api_provider._session.request.call_args[1]["url"] == expected_url
  78. async def test_timeout(api_provider: ApiProvider):
  79. await api_provider.request("POST", "bar", timeout=2.1)
  80. assert api_provider._session.request.call_args[1]["timeout"] == 2.1
  81. @pytest.mark.parametrize(
  82. "status", [HTTPStatus.OK, HTTPStatus.NOT_FOUND, HTTPStatus.INTERNAL_SERVER_ERROR]
  83. )
  84. async def test_unexpected_content_type(api_provider: ApiProvider, response, status):
  85. response.status = int(status)
  86. response.headers["Content-Type"] = "text/plain"
  87. with pytest.raises(ApiException) as e:
  88. await api_provider.request("GET", "bar")
  89. assert e.value.status is status
  90. assert str(e.value) == f"{status}: Unexpected content type 'text/plain'"
  91. async def test_json_variant_content_type(api_provider: ApiProvider, response):
  92. response.headers["Content-Type"] = "application/something+json"
  93. actual = await api_provider.request("GET", "bar")
  94. assert actual == {"foo": 2}
  95. async def test_no_content(api_provider: ApiProvider, response):
  96. response.status = int(HTTPStatus.NO_CONTENT)
  97. response.headers = {}
  98. actual = await api_provider.request("DELETE", "bar/2")
  99. assert actual is None
  100. @pytest.mark.parametrize("status", [HTTPStatus.BAD_REQUEST, HTTPStatus.NOT_FOUND])
  101. async def test_error_response(api_provider: ApiProvider, response, status):
  102. response.status = int(status)
  103. with pytest.raises(ApiException) as e:
  104. await api_provider.request("GET", "bar")
  105. assert e.value.status is status
  106. assert str(e.value) == str(int(status)) + ": {'foo': 2}"
  107. async def test_no_token(api_provider: ApiProvider):
  108. api_provider._fetch_token = lambda a, b: None
  109. await api_provider.request("GET", "")
  110. assert api_provider._session.request.call_args[1]["headers"] == {}