test_requests.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. # Copyright 2016 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 datetime
  15. import functools
  16. import http.client as http_client
  17. import os
  18. import sys
  19. import freezegun
  20. import mock
  21. import OpenSSL
  22. import pytest # type: ignore
  23. import requests
  24. import requests.adapters
  25. from google.auth import environment_vars
  26. from google.auth import exceptions
  27. import google.auth.credentials
  28. import google.auth.transport._custom_tls_signer
  29. import google.auth.transport._mtls_helper
  30. import google.auth.transport.requests
  31. from google.oauth2 import service_account
  32. from tests.transport import compliance
  33. @pytest.fixture
  34. def frozen_time():
  35. with freezegun.freeze_time("1970-01-01 00:00:00", tick=False) as frozen:
  36. yield frozen
  37. class TestRequestResponse(compliance.RequestResponseTests):
  38. def make_request(self):
  39. return google.auth.transport.requests.Request()
  40. def test_timeout(self):
  41. http = mock.create_autospec(requests.Session, instance=True)
  42. request = google.auth.transport.requests.Request(http)
  43. request(url="http://example.com", method="GET", timeout=5)
  44. assert http.request.call_args[1]["timeout"] == 5
  45. def test_session_closed_on_del(self):
  46. http = mock.create_autospec(requests.Session, instance=True)
  47. request = google.auth.transport.requests.Request(http)
  48. request.__del__()
  49. http.close.assert_called_with()
  50. http = mock.create_autospec(requests.Session, instance=True)
  51. http.close.side_effect = TypeError("test injected TypeError")
  52. request = google.auth.transport.requests.Request(http)
  53. request.__del__()
  54. http.close.assert_called_with()
  55. class TestTimeoutGuard(object):
  56. def make_guard(self, *args, **kwargs):
  57. return google.auth.transport.requests.TimeoutGuard(*args, **kwargs)
  58. def test_tracks_elapsed_time_w_numeric_timeout(self, frozen_time):
  59. with self.make_guard(timeout=10) as guard:
  60. frozen_time.tick(delta=datetime.timedelta(seconds=3.8))
  61. assert guard.remaining_timeout == 6.2
  62. def test_tracks_elapsed_time_w_tuple_timeout(self, frozen_time):
  63. with self.make_guard(timeout=(16, 19)) as guard:
  64. frozen_time.tick(delta=datetime.timedelta(seconds=3.8))
  65. assert guard.remaining_timeout == (12.2, 15.2)
  66. def test_noop_if_no_timeout(self, frozen_time):
  67. with self.make_guard(timeout=None) as guard:
  68. frozen_time.tick(delta=datetime.timedelta(days=3650))
  69. # NOTE: no timeout error raised, despite years have passed
  70. assert guard.remaining_timeout is None
  71. def test_timeout_error_w_numeric_timeout(self, frozen_time):
  72. with pytest.raises(requests.exceptions.Timeout):
  73. with self.make_guard(timeout=10) as guard:
  74. frozen_time.tick(delta=datetime.timedelta(seconds=10.001))
  75. assert guard.remaining_timeout == pytest.approx(-0.001)
  76. def test_timeout_error_w_tuple_timeout(self, frozen_time):
  77. with pytest.raises(requests.exceptions.Timeout):
  78. with self.make_guard(timeout=(11, 10)) as guard:
  79. frozen_time.tick(delta=datetime.timedelta(seconds=10.001))
  80. assert guard.remaining_timeout == pytest.approx((0.999, -0.001))
  81. def test_custom_timeout_error_type(self, frozen_time):
  82. class FooError(Exception):
  83. pass
  84. with pytest.raises(FooError):
  85. with self.make_guard(timeout=1, timeout_error_type=FooError):
  86. frozen_time.tick(delta=datetime.timedelta(seconds=2))
  87. def test_lets_suite_errors_bubble_up(self, frozen_time):
  88. with pytest.raises(IndexError):
  89. with self.make_guard(timeout=1):
  90. [1, 2, 3][3]
  91. class CredentialsStub(google.auth.credentials.Credentials):
  92. def __init__(self, token="token"):
  93. super(CredentialsStub, self).__init__()
  94. self.token = token
  95. def apply(self, headers, token=None):
  96. headers["authorization"] = self.token
  97. def before_request(self, request, method, url, headers):
  98. self.apply(headers)
  99. def refresh(self, request):
  100. self.token += "1"
  101. def with_quota_project(self, quota_project_id):
  102. raise NotImplementedError()
  103. class TimeTickCredentialsStub(CredentialsStub):
  104. """Credentials that spend some (mocked) time when refreshing a token."""
  105. def __init__(self, time_tick, token="token"):
  106. self._time_tick = time_tick
  107. super(TimeTickCredentialsStub, self).__init__(token=token)
  108. def refresh(self, request):
  109. self._time_tick()
  110. super(TimeTickCredentialsStub, self).refresh(requests)
  111. class AdapterStub(requests.adapters.BaseAdapter):
  112. def __init__(self, responses, headers=None):
  113. super(AdapterStub, self).__init__()
  114. self.responses = responses
  115. self.requests = []
  116. self.headers = headers or {}
  117. def send(self, request, **kwargs):
  118. # pylint: disable=arguments-differ
  119. # request is the only required argument here and the only argument
  120. # we care about.
  121. self.requests.append(request)
  122. return self.responses.pop(0)
  123. def close(self): # pragma: NO COVER
  124. # pylint wants this to be here because it's abstract in the base
  125. # class, but requests never actually calls it.
  126. return
  127. class TimeTickAdapterStub(AdapterStub):
  128. """Adapter that spends some (mocked) time when making a request."""
  129. def __init__(self, time_tick, responses, headers=None):
  130. self._time_tick = time_tick
  131. super(TimeTickAdapterStub, self).__init__(responses, headers=headers)
  132. def send(self, request, **kwargs):
  133. self._time_tick()
  134. return super(TimeTickAdapterStub, self).send(request, **kwargs)
  135. class TestMutualTlsAdapter(object):
  136. @mock.patch.object(requests.adapters.HTTPAdapter, "init_poolmanager")
  137. @mock.patch.object(requests.adapters.HTTPAdapter, "proxy_manager_for")
  138. def test_success(self, mock_proxy_manager_for, mock_init_poolmanager):
  139. adapter = google.auth.transport.requests._MutualTlsAdapter(
  140. pytest.public_cert_bytes, pytest.private_key_bytes
  141. )
  142. adapter.init_poolmanager()
  143. mock_init_poolmanager.assert_called_with(ssl_context=adapter._ctx_poolmanager)
  144. adapter.proxy_manager_for()
  145. mock_proxy_manager_for.assert_called_with(ssl_context=adapter._ctx_proxymanager)
  146. def test_invalid_cert_or_key(self):
  147. with pytest.raises(OpenSSL.crypto.Error):
  148. google.auth.transport.requests._MutualTlsAdapter(
  149. b"invalid cert", b"invalid key"
  150. )
  151. @mock.patch.dict("sys.modules", {"OpenSSL.crypto": None})
  152. def test_import_error(self):
  153. with pytest.raises(ImportError):
  154. google.auth.transport.requests._MutualTlsAdapter(
  155. pytest.public_cert_bytes, pytest.private_key_bytes
  156. )
  157. def make_response(status=http_client.OK, data=None):
  158. response = requests.Response()
  159. response.status_code = status
  160. response._content = data
  161. return response
  162. class TestAuthorizedSession(object):
  163. TEST_URL = "http://example.com/"
  164. def test_constructor(self):
  165. authed_session = google.auth.transport.requests.AuthorizedSession(
  166. mock.sentinel.credentials
  167. )
  168. assert authed_session.credentials == mock.sentinel.credentials
  169. def test_constructor_with_auth_request(self):
  170. http = mock.create_autospec(requests.Session)
  171. auth_request = google.auth.transport.requests.Request(http)
  172. authed_session = google.auth.transport.requests.AuthorizedSession(
  173. mock.sentinel.credentials, auth_request=auth_request
  174. )
  175. assert authed_session._auth_request is auth_request
  176. def test_request_default_timeout(self):
  177. credentials = mock.Mock(wraps=CredentialsStub())
  178. response = make_response()
  179. adapter = AdapterStub([response])
  180. authed_session = google.auth.transport.requests.AuthorizedSession(credentials)
  181. authed_session.mount(self.TEST_URL, adapter)
  182. patcher = mock.patch("google.auth.transport.requests.requests.Session.request")
  183. with patcher as patched_request:
  184. authed_session.request("GET", self.TEST_URL)
  185. expected_timeout = google.auth.transport.requests._DEFAULT_TIMEOUT
  186. assert patched_request.call_args[1]["timeout"] == expected_timeout
  187. def test_request_no_refresh(self):
  188. credentials = mock.Mock(wraps=CredentialsStub())
  189. response = make_response()
  190. adapter = AdapterStub([response])
  191. authed_session = google.auth.transport.requests.AuthorizedSession(credentials)
  192. authed_session.mount(self.TEST_URL, adapter)
  193. result = authed_session.request("GET", self.TEST_URL)
  194. assert response == result
  195. assert credentials.before_request.called
  196. assert not credentials.refresh.called
  197. assert len(adapter.requests) == 1
  198. assert adapter.requests[0].url == self.TEST_URL
  199. assert adapter.requests[0].headers["authorization"] == "token"
  200. def test_request_refresh(self):
  201. credentials = mock.Mock(wraps=CredentialsStub())
  202. final_response = make_response(status=http_client.OK)
  203. # First request will 401, second request will succeed.
  204. adapter = AdapterStub(
  205. [make_response(status=http_client.UNAUTHORIZED), final_response]
  206. )
  207. authed_session = google.auth.transport.requests.AuthorizedSession(
  208. credentials, refresh_timeout=60
  209. )
  210. authed_session.mount(self.TEST_URL, adapter)
  211. result = authed_session.request("GET", self.TEST_URL)
  212. assert result == final_response
  213. assert credentials.before_request.call_count == 2
  214. assert credentials.refresh.called
  215. assert len(adapter.requests) == 2
  216. assert adapter.requests[0].url == self.TEST_URL
  217. assert adapter.requests[0].headers["authorization"] == "token"
  218. assert adapter.requests[1].url == self.TEST_URL
  219. assert adapter.requests[1].headers["authorization"] == "token1"
  220. def test_request_max_allowed_time_timeout_error(self, frozen_time):
  221. tick_one_second = functools.partial(
  222. frozen_time.tick, delta=datetime.timedelta(seconds=1.0)
  223. )
  224. credentials = mock.Mock(
  225. wraps=TimeTickCredentialsStub(time_tick=tick_one_second)
  226. )
  227. adapter = TimeTickAdapterStub(
  228. time_tick=tick_one_second, responses=[make_response(status=http_client.OK)]
  229. )
  230. authed_session = google.auth.transport.requests.AuthorizedSession(credentials)
  231. authed_session.mount(self.TEST_URL, adapter)
  232. # Because a request takes a full mocked second, max_allowed_time shorter
  233. # than that will cause a timeout error.
  234. with pytest.raises(requests.exceptions.Timeout):
  235. authed_session.request("GET", self.TEST_URL, max_allowed_time=0.9)
  236. def test_request_max_allowed_time_w_transport_timeout_no_error(self, frozen_time):
  237. tick_one_second = functools.partial(
  238. frozen_time.tick, delta=datetime.timedelta(seconds=1.0)
  239. )
  240. credentials = mock.Mock(
  241. wraps=TimeTickCredentialsStub(time_tick=tick_one_second)
  242. )
  243. adapter = TimeTickAdapterStub(
  244. time_tick=tick_one_second,
  245. responses=[
  246. make_response(status=http_client.UNAUTHORIZED),
  247. make_response(status=http_client.OK),
  248. ],
  249. )
  250. authed_session = google.auth.transport.requests.AuthorizedSession(credentials)
  251. authed_session.mount(self.TEST_URL, adapter)
  252. # A short configured transport timeout does not affect max_allowed_time.
  253. # The latter is not adjusted to it and is only concerned with the actual
  254. # execution time. The call below should thus not raise a timeout error.
  255. authed_session.request("GET", self.TEST_URL, timeout=0.5, max_allowed_time=3.1)
  256. def test_request_max_allowed_time_w_refresh_timeout_no_error(self, frozen_time):
  257. tick_one_second = functools.partial(
  258. frozen_time.tick, delta=datetime.timedelta(seconds=1.0)
  259. )
  260. credentials = mock.Mock(
  261. wraps=TimeTickCredentialsStub(time_tick=tick_one_second)
  262. )
  263. adapter = TimeTickAdapterStub(
  264. time_tick=tick_one_second,
  265. responses=[
  266. make_response(status=http_client.UNAUTHORIZED),
  267. make_response(status=http_client.OK),
  268. ],
  269. )
  270. authed_session = google.auth.transport.requests.AuthorizedSession(
  271. credentials, refresh_timeout=1.1
  272. )
  273. authed_session.mount(self.TEST_URL, adapter)
  274. # A short configured refresh timeout does not affect max_allowed_time.
  275. # The latter is not adjusted to it and is only concerned with the actual
  276. # execution time. The call below should thus not raise a timeout error
  277. # (and `timeout` does not come into play either, as it's very long).
  278. authed_session.request("GET", self.TEST_URL, timeout=60, max_allowed_time=3.1)
  279. def test_request_timeout_w_refresh_timeout_timeout_error(self, frozen_time):
  280. tick_one_second = functools.partial(
  281. frozen_time.tick, delta=datetime.timedelta(seconds=1.0)
  282. )
  283. credentials = mock.Mock(
  284. wraps=TimeTickCredentialsStub(time_tick=tick_one_second)
  285. )
  286. adapter = TimeTickAdapterStub(
  287. time_tick=tick_one_second,
  288. responses=[
  289. make_response(status=http_client.UNAUTHORIZED),
  290. make_response(status=http_client.OK),
  291. ],
  292. )
  293. authed_session = google.auth.transport.requests.AuthorizedSession(
  294. credentials, refresh_timeout=100
  295. )
  296. authed_session.mount(self.TEST_URL, adapter)
  297. # An UNAUTHORIZED response triggers a refresh (an extra request), thus
  298. # the final request that otherwise succeeds results in a timeout error
  299. # (all three requests together last 3 mocked seconds).
  300. with pytest.raises(requests.exceptions.Timeout):
  301. authed_session.request(
  302. "GET", self.TEST_URL, timeout=60, max_allowed_time=2.9
  303. )
  304. def test_authorized_session_without_default_host(self):
  305. credentials = mock.create_autospec(service_account.Credentials)
  306. authed_session = google.auth.transport.requests.AuthorizedSession(credentials)
  307. authed_session.credentials._create_self_signed_jwt.assert_called_once_with(None)
  308. def test_authorized_session_with_default_host(self):
  309. default_host = "pubsub.googleapis.com"
  310. credentials = mock.create_autospec(service_account.Credentials)
  311. authed_session = google.auth.transport.requests.AuthorizedSession(
  312. credentials, default_host=default_host
  313. )
  314. authed_session.credentials._create_self_signed_jwt.assert_called_once_with(
  315. "https://{}/".format(default_host)
  316. )
  317. def test_configure_mtls_channel_with_callback(self):
  318. mock_callback = mock.Mock()
  319. mock_callback.return_value = (
  320. pytest.public_cert_bytes,
  321. pytest.private_key_bytes,
  322. )
  323. auth_session = google.auth.transport.requests.AuthorizedSession(
  324. credentials=mock.Mock()
  325. )
  326. with mock.patch.dict(
  327. os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
  328. ):
  329. auth_session.configure_mtls_channel(mock_callback)
  330. assert auth_session.is_mtls
  331. assert isinstance(
  332. auth_session.adapters["https://"],
  333. google.auth.transport.requests._MutualTlsAdapter,
  334. )
  335. @mock.patch(
  336. "google.auth.transport._mtls_helper.get_client_cert_and_key", autospec=True
  337. )
  338. def test_configure_mtls_channel_with_metadata(self, mock_get_client_cert_and_key):
  339. mock_get_client_cert_and_key.return_value = (
  340. True,
  341. pytest.public_cert_bytes,
  342. pytest.private_key_bytes,
  343. )
  344. auth_session = google.auth.transport.requests.AuthorizedSession(
  345. credentials=mock.Mock()
  346. )
  347. with mock.patch.dict(
  348. os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
  349. ):
  350. auth_session.configure_mtls_channel()
  351. assert auth_session.is_mtls
  352. assert isinstance(
  353. auth_session.adapters["https://"],
  354. google.auth.transport.requests._MutualTlsAdapter,
  355. )
  356. @mock.patch.object(google.auth.transport.requests._MutualTlsAdapter, "__init__")
  357. @mock.patch(
  358. "google.auth.transport._mtls_helper.get_client_cert_and_key", autospec=True
  359. )
  360. def test_configure_mtls_channel_non_mtls(
  361. self, mock_get_client_cert_and_key, mock_adapter_ctor
  362. ):
  363. mock_get_client_cert_and_key.return_value = (False, None, None)
  364. auth_session = google.auth.transport.requests.AuthorizedSession(
  365. credentials=mock.Mock()
  366. )
  367. with mock.patch.dict(
  368. os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
  369. ):
  370. auth_session.configure_mtls_channel()
  371. assert not auth_session.is_mtls
  372. # Assert _MutualTlsAdapter constructor is not called.
  373. mock_adapter_ctor.assert_not_called()
  374. @mock.patch(
  375. "google.auth.transport._mtls_helper.get_client_cert_and_key", autospec=True
  376. )
  377. def test_configure_mtls_channel_exceptions(self, mock_get_client_cert_and_key):
  378. mock_get_client_cert_and_key.side_effect = exceptions.ClientCertError()
  379. auth_session = google.auth.transport.requests.AuthorizedSession(
  380. credentials=mock.Mock()
  381. )
  382. with pytest.raises(exceptions.MutualTLSChannelError):
  383. with mock.patch.dict(
  384. os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
  385. ):
  386. auth_session.configure_mtls_channel()
  387. mock_get_client_cert_and_key.return_value = (False, None, None)
  388. with mock.patch.dict("sys.modules"):
  389. sys.modules["OpenSSL"] = None
  390. with pytest.raises(exceptions.MutualTLSChannelError):
  391. with mock.patch.dict(
  392. os.environ,
  393. {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"},
  394. ):
  395. auth_session.configure_mtls_channel()
  396. @mock.patch(
  397. "google.auth.transport._mtls_helper.get_client_cert_and_key", autospec=True
  398. )
  399. def test_configure_mtls_channel_without_client_cert_env(
  400. self, get_client_cert_and_key
  401. ):
  402. # Test client cert won't be used if GOOGLE_API_USE_CLIENT_CERTIFICATE
  403. # environment variable is not set.
  404. auth_session = google.auth.transport.requests.AuthorizedSession(
  405. credentials=mock.Mock()
  406. )
  407. auth_session.configure_mtls_channel()
  408. assert not auth_session.is_mtls
  409. get_client_cert_and_key.assert_not_called()
  410. mock_callback = mock.Mock()
  411. auth_session.configure_mtls_channel(mock_callback)
  412. assert not auth_session.is_mtls
  413. mock_callback.assert_not_called()
  414. def test_close_wo_passed_in_auth_request(self):
  415. authed_session = google.auth.transport.requests.AuthorizedSession(
  416. mock.sentinel.credentials
  417. )
  418. authed_session._auth_request_session = mock.Mock(spec=["close"])
  419. authed_session.close()
  420. authed_session._auth_request_session.close.assert_called_once_with()
  421. def test_close_w_passed_in_auth_request(self):
  422. http = mock.create_autospec(requests.Session)
  423. auth_request = google.auth.transport.requests.Request(http)
  424. authed_session = google.auth.transport.requests.AuthorizedSession(
  425. mock.sentinel.credentials, auth_request=auth_request
  426. )
  427. authed_session.close() # no raise
  428. class TestMutualTlsOffloadAdapter(object):
  429. @mock.patch.object(requests.adapters.HTTPAdapter, "init_poolmanager")
  430. @mock.patch.object(requests.adapters.HTTPAdapter, "proxy_manager_for")
  431. @mock.patch.object(
  432. google.auth.transport._custom_tls_signer.CustomTlsSigner, "load_libraries"
  433. )
  434. @mock.patch.object(
  435. google.auth.transport._custom_tls_signer.CustomTlsSigner,
  436. "attach_to_ssl_context",
  437. )
  438. def test_success(
  439. self,
  440. mock_attach_to_ssl_context,
  441. mock_load_libraries,
  442. mock_proxy_manager_for,
  443. mock_init_poolmanager,
  444. ):
  445. enterprise_cert_file_path = "/path/to/enterprise/cert/json"
  446. adapter = google.auth.transport.requests._MutualTlsOffloadAdapter(
  447. enterprise_cert_file_path
  448. )
  449. mock_load_libraries.assert_called_once()
  450. assert mock_attach_to_ssl_context.call_count == 2
  451. adapter.init_poolmanager()
  452. mock_init_poolmanager.assert_called_with(ssl_context=adapter._ctx_poolmanager)
  453. adapter.proxy_manager_for()
  454. mock_proxy_manager_for.assert_called_with(ssl_context=adapter._ctx_proxymanager)
  455. @mock.patch.object(requests.adapters.HTTPAdapter, "init_poolmanager")
  456. @mock.patch.object(requests.adapters.HTTPAdapter, "proxy_manager_for")
  457. @mock.patch.object(
  458. google.auth.transport._custom_tls_signer.CustomTlsSigner, "should_use_provider"
  459. )
  460. @mock.patch.object(
  461. google.auth.transport._custom_tls_signer.CustomTlsSigner, "load_libraries"
  462. )
  463. @mock.patch.object(
  464. google.auth.transport._custom_tls_signer.CustomTlsSigner,
  465. "attach_to_ssl_context",
  466. )
  467. def test_success_should_use_provider(
  468. self,
  469. mock_attach_to_ssl_context,
  470. mock_load_libraries,
  471. mock_should_use_provider,
  472. mock_proxy_manager_for,
  473. mock_init_poolmanager,
  474. ):
  475. enterprise_cert_file_path = "/path/to/enterprise/cert/json"
  476. adapter = google.auth.transport.requests._MutualTlsOffloadAdapter(
  477. enterprise_cert_file_path
  478. )
  479. mock_should_use_provider.side_effect = True
  480. mock_load_libraries.assert_called_once()
  481. assert mock_attach_to_ssl_context.call_count == 2
  482. adapter.init_poolmanager()
  483. mock_init_poolmanager.assert_called_with(ssl_context=adapter._ctx_poolmanager)
  484. adapter.proxy_manager_for()
  485. mock_proxy_manager_for.assert_called_with(ssl_context=adapter._ctx_proxymanager)