diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index a5ddd035d..15143d0bb 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - python: ['3.8'] + python: ['3.7', '3.8'] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/setup.py b/setup.py index 3db2d8cf9..68bb92a8a 100644 --- a/setup.py +++ b/setup.py @@ -76,6 +76,8 @@ # TODO(https://github.com/googleapis/google-auth-library-python/issues/1722): `test_aiohttp_requests` depend on # aiohttp < 3.10.0 which is a bug. Investigate and remove the pinned aiohttp version. "aiohttp < 3.10.0", + "mock >= 4.0.0; python_version < '3.8'", + "freezegun", ] extras = { diff --git a/tests/compute_engine/test_credentials.py b/tests/compute_engine/test_credentials.py index 2dec4814e..45bbe05b1 100644 --- a/tests/compute_engine/test_credentials.py +++ b/tests/compute_engine/test_credentials.py @@ -1307,7 +1307,7 @@ def test_get_id_token_from_metadata( ) cred.refresh(request=mock.Mock()) - assert get.call_args.kwargs["headers"] == { + assert get.call_args[1]["headers"] == { "x-goog-api-client": ID_TOKEN_REQUEST_METRICS_HEADER_VALUE } diff --git a/tests/oauth2/test_webauthn_handler.py b/tests/oauth2/test_webauthn_handler.py index 1dd75d6e1..f0ccaabfd 100644 --- a/tests/oauth2/test_webauthn_handler.py +++ b/tests/oauth2/test_webauthn_handler.py @@ -107,7 +107,7 @@ def test_success_get_assertion(os_get_stub, subprocess_run_stub): os_get_stub.assert_called_once() subprocess_run_stub.assert_called_once() - stdin_input = subprocess_run_stub.call_args.kwargs["input"] + stdin_input = subprocess_run_stub.call_args[1]["input"] input_json_len_le = stdin_input[:4] input_json_len = struct.unpack("= (3, 8): + from unittest import mock + from unittest.mock import AsyncMock +else: # pragma: NO COVER + import mock + from mock import AsyncMock import pytest # type: ignore @@ -58,7 +65,7 @@ def test_minimum_total_attempts(): @pytest.mark.asyncio -@mock.patch("asyncio.sleep", return_value=None) +@mock.patch("asyncio.sleep", new_callable=AsyncMock) async def test_exponential_backoff_async(mock_time_async): eb = _exponential_backoff.AsyncExponentialBackoff() curr_wait = eb._current_wait_in_seconds diff --git a/tests/test_impersonated_credentials.py b/tests/test_impersonated_credentials.py index a271ad1b9..2f1c22f8f 100644 --- a/tests/test_impersonated_credentials.py +++ b/tests/test_impersonated_credentials.py @@ -344,7 +344,7 @@ def test_refresh_success( assert credentials.valid assert not credentials.expired assert ( - request.call_args.kwargs["headers"]["x-goog-api-client"] + request.call_args[1]["headers"]["x-goog-api-client"] == ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE ) @@ -1075,7 +1075,7 @@ def test_id_token_metrics(self, mock_donor_credentials): ID_TOKEN_EXPIRY ) assert ( - mock_post.call_args.kwargs["headers"]["x-goog-api-client"] + mock_post.call_args[1]["headers"]["x-goog-api-client"] == ID_TOKEN_REQUEST_METRICS_HEADER_VALUE ) @@ -1145,9 +1145,11 @@ def _test_id_token_helper( id_creds = id_creds.from_credentials(target_credentials=target_credentials) id_creds.refresh(request) - args = mock_authorizedsession_idtoken.call_args.args + # In Python 3.7, self might not be in call_args for autospecced methods + args = mock_authorizedsession_idtoken.call_args[0] - assert args[2] == expected_url + # Look for expected_url in args + assert expected_url in args assert id_creds.token == ID_TOKEN_DATA assert id_creds._include_email is True diff --git a/tests/transport/aio/test_aiohttp.py b/tests/transport/aio/test_aiohttp.py index 13f86ba34..a32175892 100644 --- a/tests/transport/aio/test_aiohttp.py +++ b/tests/transport/aio/test_aiohttp.py @@ -13,7 +13,13 @@ # limitations under the License. import asyncio -from unittest.mock import AsyncMock, Mock, patch +import sys + +if sys.version_info >= (3, 8): + from unittest.mock import Mock, patch, AsyncMock +else: # pragma: NO COVER + from unittest.mock import Mock, patch + from mock import AsyncMock from aioresponses import aioresponses # type: ignore import pytest # type: ignore diff --git a/tests/transport/aio/test_sessions.py b/tests/transport/aio/test_sessions.py index d0cb21dda..c0e576d34 100644 --- a/tests/transport/aio/test_sessions.py +++ b/tests/transport/aio/test_sessions.py @@ -261,9 +261,12 @@ async def test_request_max_allowed_time_exceeded_error(self): @pytest.mark.parametrize("retry_status", DEFAULT_RETRYABLE_STATUS_CODES) @pytest.mark.asyncio async def test_request_max_retries(self, retry_status): + async def async_sleep(delay): + pass + mocked_response = MockResponse(status_code=retry_status) auth_request = MockRequest(mocked_response) - with patch("asyncio.sleep", return_value=None): + with patch("asyncio.sleep", side_effect=async_sleep): authed_session = sessions.AsyncAuthorizedSession( self.credentials, auth_request ) diff --git a/tests_async/oauth2/test__client_async.py b/tests_async/oauth2/test__client_async.py index 5ad9596cf..ebc6f0a71 100644 --- a/tests_async/oauth2/test__client_async.py +++ b/tests_async/oauth2/test__client_async.py @@ -15,9 +15,17 @@ import datetime import http.client as http_client import json -from unittest import mock +import sys import urllib +if sys.version_info >= (3, 8): + from unittest import mock + from unittest.mock import AsyncMock +else: + import mock + from mock import AsyncMock + +import freezegun import pytest # type: ignore from google.auth import _helpers @@ -29,13 +37,17 @@ def make_request(response_data, status=http_client.OK, text=False): - response = mock.AsyncMock(spec=["transport.Response"]) + response = AsyncMock(spec=["transport.Response"]) response.status = status data = response_data if text else json.dumps(response_data).encode("utf-8") - response.data = mock.AsyncMock(spec=["__call__", "read"]) - response.data.read = mock.AsyncMock(spec=["__call__"], return_value=data) - response.content = mock.AsyncMock(spec=["__call__"], return_value=data) - request = mock.AsyncMock(spec=["transport.Request"]) + + async def get_content(*args, **kwargs): + return data + + response.data = AsyncMock(spec=["__call__", "read"]) + response.data.read = AsyncMock(spec=["__call__"], side_effect=get_content) + response.content = AsyncMock(spec=["__call__"], side_effect=get_content) + request = AsyncMock(spec=["transport.Request"]) request.return_value = response return request @@ -142,21 +154,21 @@ async def test__token_endpoint_request_internal_failure_error(): @pytest.mark.asyncio async def test__token_endpoint_request_internal_failure_and_retry_failure_error(): - retryable_error = mock.AsyncMock(spec=["transport.Response"]) + retryable_error = AsyncMock(spec=["transport.Response"]) retryable_error.status = http_client.BAD_REQUEST data = json.dumps({"error_description": "internal_failure"}).encode("utf-8") - retryable_error.data = mock.AsyncMock(spec=["__call__", "read"]) - retryable_error.data.read = mock.AsyncMock(spec=["__call__"], return_value=data) - retryable_error.content = mock.AsyncMock(spec=["__call__"], return_value=data) + retryable_error.data = AsyncMock(spec=["__call__", "read"]) + retryable_error.data.read = AsyncMock(spec=["__call__"], return_value=data) + retryable_error.content = AsyncMock(spec=["__call__"], return_value=data) - unretryable_error = mock.AsyncMock(spec=["transport.Response"]) + unretryable_error = AsyncMock(spec=["transport.Response"]) unretryable_error.status = http_client.BAD_REQUEST data = json.dumps({"error_description": "invalid_scope"}).encode("utf-8") - unretryable_error.data = mock.AsyncMock(spec=["__call__", "read"]) - unretryable_error.data.read = mock.AsyncMock(spec=["__call__"], return_value=data) - unretryable_error.content = mock.AsyncMock(spec=["__call__"], return_value=data) + unretryable_error.data = AsyncMock(spec=["__call__", "read"]) + unretryable_error.data.read = AsyncMock(spec=["__call__"], return_value=data) + unretryable_error.content = AsyncMock(spec=["__call__"], return_value=data) - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) request.side_effect = [retryable_error, retryable_error, unretryable_error] with pytest.raises(exceptions.RefreshError): @@ -170,21 +182,21 @@ async def test__token_endpoint_request_internal_failure_and_retry_failure_error( @pytest.mark.asyncio async def test__token_endpoint_request_internal_failure_and_retry_succeeds(): - retryable_error = mock.AsyncMock(spec=["transport.Response"]) + retryable_error = AsyncMock(spec=["transport.Response"]) retryable_error.status = http_client.BAD_REQUEST data = json.dumps({"error_description": "internal_failure"}).encode("utf-8") - retryable_error.data = mock.AsyncMock(spec=["__call__", "read"]) - retryable_error.data.read = mock.AsyncMock(spec=["__call__"], return_value=data) - retryable_error.content = mock.AsyncMock(spec=["__call__"], return_value=data) + retryable_error.data = AsyncMock(spec=["__call__", "read"]) + retryable_error.data.read = AsyncMock(spec=["__call__"], return_value=data) + retryable_error.content = AsyncMock(spec=["__call__"], return_value=data) - response = mock.AsyncMock(spec=["transport.Response"]) + response = AsyncMock(spec=["transport.Response"]) response.status = http_client.OK data = json.dumps({"hello": "world"}).encode("utf-8") - response.data = mock.AsyncMock(spec=["__call__", "read"]) - response.data.read = mock.AsyncMock(spec=["__call__"], return_value=data) - response.content = mock.AsyncMock(spec=["__call__"], return_value=data) + response.data = AsyncMock(spec=["__call__", "read"]) + response.data.read = AsyncMock(spec=["__call__"], return_value=data) + response.content = AsyncMock(spec=["__call__"], return_value=data) - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) request.side_effect = [retryable_error, response] _ = await _client._token_endpoint_request( @@ -203,26 +215,26 @@ def verify_request_params(request, params): @pytest.mark.asyncio -@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) -async def test_jwt_grant(utcnow): +async def test_jwt_grant(): request = make_request( {"access_token": "token", "expires_in": 500, "extra": "data"} ) - token, expiry, extra_data = await _client.jwt_grant( - request, "http://example.com", "assertion_value" - ) + with freezegun.freeze_time("2020-01-01 00:00:00"): + token, expiry, extra_data = await _client.jwt_grant( + request, "http://example.com", "assertion_value" + ) - # Check request call - verify_request_params( - request, - {"grant_type": sync_client._JWT_GRANT_TYPE, "assertion": "assertion_value"}, - ) + # Check request call + verify_request_params( + request, + {"grant_type": sync_client._JWT_GRANT_TYPE, "assertion": "assertion_value"}, + ) - # Check result - assert token == "token" - assert expiry == utcnow() + datetime.timedelta(seconds=500) - assert extra_data["extra"] == "data" + # Check result + assert token == "token" + assert expiry == datetime.datetime(2020, 1, 1) + datetime.timedelta(seconds=500) + assert extra_data["extra"] == "data" @pytest.mark.asyncio @@ -283,8 +295,7 @@ async def test_id_token_jwt_grant_no_access_token(): @pytest.mark.asyncio -@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) -async def test_refresh_grant(unused_utcnow): +async def test_refresh_grant(): request = make_request( { "access_token": "token", @@ -294,37 +305,37 @@ async def test_refresh_grant(unused_utcnow): } ) - token, refresh_token, expiry, extra_data = await _client.refresh_grant( - request, - "http://example.com", - "refresh_token", - "client_id", - "client_secret", - rapt_token="rapt_token", - ) + with freezegun.freeze_time("2020-01-01 00:00:00"): + token, refresh_token, expiry, extra_data = await _client.refresh_grant( + request, + "http://example.com", + "refresh_token", + "client_id", + "client_secret", + rapt_token="rapt_token", + ) - # Check request call - verify_request_params( - request, - { - "grant_type": sync_client._REFRESH_GRANT_TYPE, - "refresh_token": "refresh_token", - "client_id": "client_id", - "client_secret": "client_secret", - "rapt": "rapt_token", - }, - ) + # Check request call + verify_request_params( + request, + { + "grant_type": sync_client._REFRESH_GRANT_TYPE, + "refresh_token": "refresh_token", + "client_id": "client_id", + "client_secret": "client_secret", + "rapt": "rapt_token", + }, + ) - # Check result - assert token == "token" - assert refresh_token == "new_refresh_token" - assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500) - assert extra_data["extra"] == "data" + # Check result + assert token == "token" + assert refresh_token == "new_refresh_token" + assert expiry == datetime.datetime(2020, 1, 1) + datetime.timedelta(seconds=500) + assert extra_data["extra"] == "data" @pytest.mark.asyncio -@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) -async def test_refresh_grant_with_scopes(unused_utcnow): +async def test_refresh_grant_with_scopes(): request = make_request( { "access_token": "token", @@ -335,32 +346,33 @@ async def test_refresh_grant_with_scopes(unused_utcnow): } ) - token, refresh_token, expiry, extra_data = await _client.refresh_grant( - request, - "http://example.com", - "refresh_token", - "client_id", - "client_secret", - test_client.SCOPES_AS_LIST, - ) + with freezegun.freeze_time("2020-01-01 00:00:00"): + token, refresh_token, expiry, extra_data = await _client.refresh_grant( + request, + "http://example.com", + "refresh_token", + "client_id", + "client_secret", + test_client.SCOPES_AS_LIST, + ) - # Check request call. - verify_request_params( - request, - { - "grant_type": sync_client._REFRESH_GRANT_TYPE, - "refresh_token": "refresh_token", - "client_id": "client_id", - "client_secret": "client_secret", - "scope": test_client.SCOPES_AS_STRING, - }, - ) + # Check request call. + verify_request_params( + request, + { + "grant_type": sync_client._REFRESH_GRANT_TYPE, + "refresh_token": "refresh_token", + "client_id": "client_id", + "client_secret": "client_secret", + "scope": test_client.SCOPES_AS_STRING, + }, + ) - # Check result. - assert token == "token" - assert refresh_token == "new_refresh_token" - assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500) - assert extra_data["extra"] == "data" + # Check result. + assert token == "token" + assert refresh_token == "new_refresh_token" + assert expiry == datetime.datetime(2020, 1, 1) + datetime.timedelta(seconds=500) + assert extra_data["extra"] == "data" @pytest.mark.asyncio @@ -383,7 +395,7 @@ async def test_refresh_grant_no_access_token(): @pytest.mark.asyncio @mock.patch("google.oauth2._client._parse_expiry", return_value=None) -@mock.patch.object(_client, "_token_endpoint_request", autospec=True) +@mock.patch.object(_client, "_token_endpoint_request", new_callable=AsyncMock) async def test_jwt_grant_retry_default(mock_token_endpoint_request, mock_expiry): _ = await _client.jwt_grant(mock.Mock(), mock.Mock(), mock.Mock()) mock_token_endpoint_request.assert_called_with( @@ -394,12 +406,12 @@ async def test_jwt_grant_retry_default(mock_token_endpoint_request, mock_expiry) @pytest.mark.asyncio @pytest.mark.parametrize("can_retry", [True, False]) @mock.patch("google.oauth2._client._parse_expiry", return_value=None) -@mock.patch.object(_client, "_token_endpoint_request", autospec=True) +@mock.patch.object(_client, "_token_endpoint_request", new_callable=AsyncMock) async def test_jwt_grant_retry_with_retry( mock_token_endpoint_request, mock_expiry, can_retry ): _ = await _client.jwt_grant( - mock.AsyncMock(), mock.Mock(), mock.Mock(), can_retry=can_retry + mock.Mock(), mock.Mock(), mock.Mock(), can_retry=can_retry ) mock_token_endpoint_request.assert_called_with( mock.ANY, mock.ANY, mock.ANY, can_retry=can_retry @@ -408,7 +420,7 @@ async def test_jwt_grant_retry_with_retry( @pytest.mark.asyncio @mock.patch("google.auth.jwt.decode", return_value={"exp": 0}) -@mock.patch.object(_client, "_token_endpoint_request", autospec=True) +@mock.patch.object(_client, "_token_endpoint_request", new_callable=AsyncMock) async def test_id_token_jwt_grant_retry_default( mock_token_endpoint_request, mock_jwt_decode ): @@ -421,12 +433,12 @@ async def test_id_token_jwt_grant_retry_default( @pytest.mark.asyncio @pytest.mark.parametrize("can_retry", [True, False]) @mock.patch("google.auth.jwt.decode", return_value={"exp": 0}) -@mock.patch.object(_client, "_token_endpoint_request", autospec=True) +@mock.patch.object(_client, "_token_endpoint_request", new_callable=AsyncMock) async def test_id_token_jwt_grant_retry_with_retry( mock_token_endpoint_request, mock_jwt_decode, can_retry ): _ = await _client.id_token_jwt_grant( - mock.AsyncMock(), mock.AsyncMock(), mock.AsyncMock(), can_retry=can_retry + mock.Mock(), mock.Mock(), mock.Mock(), can_retry=can_retry ) mock_token_endpoint_request.assert_called_with( mock.ANY, mock.ANY, mock.ANY, can_retry=can_retry @@ -435,16 +447,16 @@ async def test_id_token_jwt_grant_retry_with_retry( @pytest.mark.asyncio @mock.patch("google.oauth2._client._parse_expiry", return_value=None) -@mock.patch.object(_client, "_token_endpoint_request", autospec=True) +@mock.patch.object(_client, "_token_endpoint_request", new_callable=AsyncMock) async def test_refresh_grant_retry_default( mock_token_endpoint_request, mock_parse_expiry ): _ = await _client.refresh_grant( - mock.AsyncMock(), - mock.AsyncMock(), - mock.AsyncMock(), - mock.AsyncMock(), - mock.AsyncMock(), + mock.Mock(), + mock.Mock(), + mock.Mock(), + mock.Mock(), + mock.Mock(), ) mock_token_endpoint_request.assert_called_with( mock.ANY, mock.ANY, mock.ANY, can_retry=True @@ -454,16 +466,16 @@ async def test_refresh_grant_retry_default( @pytest.mark.asyncio @pytest.mark.parametrize("can_retry", [True, False]) @mock.patch("google.oauth2._client._parse_expiry", return_value=None) -@mock.patch.object(_client, "_token_endpoint_request", autospec=True) +@mock.patch.object(_client, "_token_endpoint_request", new_callable=AsyncMock) async def test_refresh_grant_retry_with_retry( mock_token_endpoint_request, mock_parse_expiry, can_retry ): _ = await _client.refresh_grant( - mock.AsyncMock(), - mock.AsyncMock(), - mock.AsyncMock(), - mock.AsyncMock(), - mock.AsyncMock(), + mock.Mock(), + mock.Mock(), + mock.Mock(), + mock.Mock(), + mock.Mock(), can_retry=can_retry, ) mock_token_endpoint_request.assert_called_with( @@ -481,10 +493,10 @@ async def test__token_endpoint_request_no_throw_with_retry(can_retry): _ = await _client._token_endpoint_request_no_throw( mock_request, - mock.AsyncMock(), + AsyncMock(), "body", - mock.AsyncMock(), - mock.AsyncMock(), + AsyncMock(), + AsyncMock(), can_retry=can_retry, ) diff --git a/tests_async/oauth2/test_credentials_async.py b/tests_async/oauth2/test_credentials_async.py index 0a5d8ab1a..67de9f59a 100644 --- a/tests_async/oauth2/test_credentials_async.py +++ b/tests_async/oauth2/test_credentials_async.py @@ -17,7 +17,13 @@ import os import pickle import sys -from unittest import mock + +if sys.version_info >= (3, 8): + from unittest import mock + from unittest.mock import AsyncMock +else: + import mock + from mock import AsyncMock import pytest # type: ignore @@ -58,7 +64,9 @@ def test_default_state(self): assert credentials.client_id == self.CLIENT_ID assert credentials.client_secret == self.CLIENT_SECRET - @mock.patch("google.oauth2._reauth_async.refresh_grant", autospec=True) + @mock.patch( + "google.oauth2._credentials_async.reauth.refresh_grant", new_callable=AsyncMock + ) @mock.patch( "google.auth._helpers.utcnow", return_value=datetime.datetime.min + _helpers.REFRESH_THRESHOLD, @@ -82,7 +90,7 @@ async def test_refresh_success(self, unused_utcnow, refresh_grant): rapt_token, ) - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) creds = self.make_credentials() # Refresh credentials @@ -95,9 +103,9 @@ async def test_refresh_success(self, unused_utcnow, refresh_grant): self.REFRESH_TOKEN, self.CLIENT_ID, self.CLIENT_SECRET, - None, - None, - True, + scopes=None, + rapt_token=None, + enable_reauth_refresh=True, ) # Check that the credentials have the token and expiry @@ -112,7 +120,7 @@ async def test_refresh_success(self, unused_utcnow, refresh_grant): @pytest.mark.asyncio async def test_refresh_no_refresh_token(self): - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) credentials_ = _credentials_async.Credentials(token=None, refresh_token=None) with pytest.raises(exceptions.RefreshError, match="necessary fields"): @@ -120,7 +128,9 @@ async def test_refresh_no_refresh_token(self): request.assert_not_called() - @mock.patch("google.oauth2._reauth_async.refresh_grant", autospec=True) + @mock.patch( + "google.oauth2._credentials_async.reauth.refresh_grant", new_callable=AsyncMock + ) @mock.patch( "google.auth._helpers.utcnow", return_value=datetime.datetime.min + _helpers.REFRESH_THRESHOLD, @@ -147,7 +157,7 @@ async def test_credentials_with_scopes_requested_refresh_success( rapt_token, ) - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) creds = _credentials_async.Credentials( token=None, refresh_token=self.REFRESH_TOKEN, @@ -168,9 +178,9 @@ async def test_credentials_with_scopes_requested_refresh_success( self.REFRESH_TOKEN, self.CLIENT_ID, self.CLIENT_SECRET, - scopes, - "old_rapt_token", - False, + scopes=scopes, + rapt_token="old_rapt_token", + enable_reauth_refresh=False, ) # Check that the credentials have the token and expiry @@ -184,7 +194,9 @@ async def test_credentials_with_scopes_requested_refresh_success( # expired.) assert creds.valid - @mock.patch("google.oauth2._reauth_async.refresh_grant", autospec=True) + @mock.patch( + "google.oauth2._credentials_async.reauth.refresh_grant", new_callable=AsyncMock + ) @mock.patch( "google.auth._helpers.utcnow", return_value=datetime.datetime.min + _helpers.REFRESH_THRESHOLD, @@ -211,7 +223,7 @@ async def test_credentials_with_scopes_returned_refresh_success( rapt_token, ) - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) creds = _credentials_async.Credentials( token=None, refresh_token=self.REFRESH_TOKEN, @@ -231,9 +243,9 @@ async def test_credentials_with_scopes_returned_refresh_success( self.REFRESH_TOKEN, self.CLIENT_ID, self.CLIENT_SECRET, - scopes, - None, - False, + scopes=scopes, + rapt_token=None, + enable_reauth_refresh=False, ) # Check that the credentials have the token and expiry @@ -247,7 +259,9 @@ async def test_credentials_with_scopes_returned_refresh_success( # expired.) assert creds.valid - @mock.patch("google.oauth2._reauth_async.refresh_grant", autospec=True) + @mock.patch( + "google.oauth2._credentials_async.reauth.refresh_grant", new_callable=AsyncMock + ) @mock.patch( "google.auth._helpers.utcnow", return_value=datetime.datetime.min + _helpers.REFRESH_THRESHOLD, @@ -278,7 +292,7 @@ async def test_credentials_with_scopes_refresh_failure_raises_refresh_error( rapt_token, ) - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) creds = _credentials_async.Credentials( token=None, refresh_token=self.REFRESH_TOKEN, @@ -302,9 +316,9 @@ async def test_credentials_with_scopes_refresh_failure_raises_refresh_error( self.REFRESH_TOKEN, self.CLIENT_ID, self.CLIENT_SECRET, - scopes, - None, - False, + scopes=scopes, + rapt_token=None, + enable_reauth_refresh=False, ) # Check that the credentials have the token and expiry @@ -466,7 +480,9 @@ def test_unpickle_old_credentials_pickle(self): assert credentials.quota_project_id is None @mock.patch("google.oauth2._credentials_async.Credentials.apply", autospec=True) - @mock.patch("google.oauth2._credentials_async.Credentials.refresh", autospec=True) + @mock.patch( + "google.oauth2._credentials_async.Credentials.refresh", new_callable=AsyncMock + ) @pytest.mark.asyncio async def test_before_request(self, refresh, apply): cred = self.make_credentials() diff --git a/tests_async/oauth2/test_id_token.py b/tests_async/oauth2/test_id_token.py index 51d85daf2..6cb609aac 100644 --- a/tests_async/oauth2/test_id_token.py +++ b/tests_async/oauth2/test_id_token.py @@ -14,7 +14,14 @@ import json import os -from unittest import mock +import sys + +if sys.version_info >= (3, 8): + from unittest import mock + from unittest.mock import AsyncMock +else: + import mock + from mock import AsyncMock import pytest # type: ignore @@ -28,16 +35,19 @@ def make_request(status, data=None): - response = mock.AsyncMock(spec=["transport.Response"]) + response = AsyncMock(spec=["transport.Response"]) response.status = status if data is not None: - response.data = mock.AsyncMock(spec=["__call__", "read"]) - response.content = mock.AsyncMock( - spec=["__call__"], return_value=json.dumps(data).encode("utf-8") - ) + response.data = AsyncMock(spec=["__call__", "read"]) + + async def get_content(*args, **kwargs): + return json.dumps(data).encode("utf-8") + + response.data.read = AsyncMock(spec=["__call__"], side_effect=get_content) + response.content = AsyncMock(spec=["__call__"], side_effect=get_content) - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) request.return_value = response return request @@ -64,7 +74,7 @@ async def test__fetch_certs_failure(): @mock.patch("google.auth.jwt.decode", autospec=True) -@mock.patch("google.oauth2._id_token_async._fetch_certs", autospec=True) +@mock.patch("google.oauth2._id_token_async._fetch_certs", new_callable=AsyncMock) @pytest.mark.asyncio async def test_verify_token(_fetch_certs, decode): result = await id_token.verify_token(mock.sentinel.token, mock.sentinel.request) @@ -82,7 +92,7 @@ async def test_verify_token(_fetch_certs, decode): @mock.patch("google.auth.jwt.decode", autospec=True) -@mock.patch("google.oauth2._id_token_async._fetch_certs", autospec=True) +@mock.patch("google.oauth2._id_token_async._fetch_certs", new_callable=AsyncMock) @pytest.mark.asyncio async def test_verify_token_clock_skew(_fetch_certs, decode): result = await id_token.verify_token( @@ -102,7 +112,7 @@ async def test_verify_token_clock_skew(_fetch_certs, decode): @mock.patch("google.auth.jwt.decode", autospec=True) -@mock.patch("google.oauth2._id_token_async._fetch_certs", autospec=True) +@mock.patch("google.oauth2._id_token_async._fetch_certs", new_callable=AsyncMock) @pytest.mark.asyncio async def test_verify_token_args(_fetch_certs, decode): result = await id_token.verify_token( @@ -122,7 +132,7 @@ async def test_verify_token_args(_fetch_certs, decode): ) -@mock.patch("google.oauth2._id_token_async.verify_token", autospec=True) +@mock.patch("google.oauth2._id_token_async.verify_token", new_callable=AsyncMock) @pytest.mark.asyncio async def test_verify_oauth2_token(verify_token): verify_token.return_value = {"iss": "accounts.google.com"} @@ -140,7 +150,7 @@ async def test_verify_oauth2_token(verify_token): ) -@mock.patch("google.oauth2._id_token_async.verify_token", autospec=True) +@mock.patch("google.oauth2._id_token_async.verify_token", new_callable=AsyncMock) @pytest.mark.asyncio async def test_verify_oauth2_token_clock_skew(verify_token): verify_token.return_value = {"iss": "accounts.google.com"} @@ -161,7 +171,7 @@ async def test_verify_oauth2_token_clock_skew(verify_token): ) -@mock.patch("google.oauth2._id_token_async.verify_token", autospec=True) +@mock.patch("google.oauth2._id_token_async.verify_token", new_callable=AsyncMock) @pytest.mark.asyncio async def test_verify_oauth2_token_invalid_iss(verify_token): verify_token.return_value = {"iss": "invalid_issuer"} @@ -172,7 +182,7 @@ async def test_verify_oauth2_token_invalid_iss(verify_token): ) -@mock.patch("google.oauth2._id_token_async.verify_token", autospec=True) +@mock.patch("google.oauth2._id_token_async.verify_token", new_callable=AsyncMock) @pytest.mark.asyncio async def test_verify_firebase_token(verify_token): result = await id_token.verify_firebase_token( @@ -189,7 +199,7 @@ async def test_verify_firebase_token(verify_token): ) -@mock.patch("google.oauth2._id_token_async.verify_token", autospec=True) +@mock.patch("google.oauth2._id_token_async.verify_token", new_callable=AsyncMock) @pytest.mark.asyncio async def test_verify_firebase_token_clock_skew(verify_token): result = await id_token.verify_firebase_token( @@ -223,7 +233,7 @@ def mock_init(self, request, audience, use_metadata_identity_endpoint): __init__=mock_init, refresh=mock.Mock(), ): - request = mock.AsyncMock() + request = AsyncMock() token = await id_token.fetch_id_token( request, "https://pubsub.googleapis.com" ) @@ -240,7 +250,7 @@ async def mock_refresh(self, request): with mock.patch.object( _service_account_async.IDTokenCredentials, "refresh", mock_refresh ): - request = mock.AsyncMock() + request = AsyncMock() token = await id_token.fetch_id_token(request, "https://pubsub.googleapis.com") assert token == "id_token" @@ -254,7 +264,7 @@ async def test_fetch_id_token_no_cred_exists(monkeypatch): side_effect=exceptions.TransportError(), ): with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: - request = mock.AsyncMock() + request = AsyncMock() await id_token.fetch_id_token(request, "https://pubsub.googleapis.com") assert excinfo.match( r"Neither metadata server or valid service account credentials are found." @@ -262,7 +272,7 @@ async def test_fetch_id_token_no_cred_exists(monkeypatch): with mock.patch("google.auth.compute_engine._metadata.ping", return_value=False): with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: - request = mock.AsyncMock() + request = AsyncMock() await id_token.fetch_id_token(request, "https://pubsub.googleapis.com") assert excinfo.match( r"Neither metadata server or valid service account credentials are found." @@ -277,7 +287,7 @@ async def test_fetch_id_token_invalid_cred_file(monkeypatch): monkeypatch.setenv(environment_vars.CREDENTIALS, not_json_file) with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: - request = mock.AsyncMock() + request = AsyncMock() await id_token.fetch_id_token(request, "https://pubsub.googleapis.com") assert excinfo.match( r"GOOGLE_APPLICATION_CREDENTIALS is not valid service account credentials." @@ -293,7 +303,7 @@ async def test_fetch_id_token_invalid_cred_type(monkeypatch): with mock.patch("google.auth.compute_engine._metadata.ping", return_value=False): with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: - request = mock.AsyncMock() + request = AsyncMock() await id_token.fetch_id_token(request, "https://pubsub.googleapis.com") assert excinfo.match( r"Neither metadata server or valid service account credentials are found." @@ -308,7 +318,7 @@ async def test_fetch_id_token_invalid_cred_path(monkeypatch): monkeypatch.setenv(environment_vars.CREDENTIALS, not_json_file) with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: - request = mock.AsyncMock() + request = AsyncMock() await id_token.fetch_id_token(request, "https://pubsub.googleapis.com") assert excinfo.match( r"GOOGLE_APPLICATION_CREDENTIALS path is either not found or invalid." diff --git a/tests_async/oauth2/test_reauth_async.py b/tests_async/oauth2/test_reauth_async.py index 4874a3728..e91cba92b 100644 --- a/tests_async/oauth2/test_reauth_async.py +++ b/tests_async/oauth2/test_reauth_async.py @@ -13,7 +13,14 @@ # limitations under the License. import copy -from unittest import mock +import sys + +if sys.version_info >= (3, 8): + from unittest import mock + from unittest.mock import AsyncMock +else: + import mock + from mock import AsyncMock import pytest # type: ignore @@ -22,7 +29,7 @@ from google.oauth2 import reauth -MOCK_REQUEST = mock.AsyncMock(spec=["transport.Request"]) +MOCK_REQUEST = AsyncMock(spec=["transport.Request"]) CHALLENGES_RESPONSE_TEMPLATE = { "status": "CHALLENGE_REQUIRED", "sessionId": "123", @@ -55,7 +62,7 @@ def obtain_challenge_input(self, metadata): @pytest.mark.asyncio async def test__get_challenges(): with mock.patch( - "google.oauth2._client_async._token_endpoint_request" + "google.oauth2._client_async._token_endpoint_request", new_callable=AsyncMock ) as mock_token_endpoint_request: await _reauth_async._get_challenges(MOCK_REQUEST, ["SAML"], "token") mock_token_endpoint_request.assert_called_with( @@ -70,7 +77,7 @@ async def test__get_challenges(): @pytest.mark.asyncio async def test__get_challenges_with_scopes(): with mock.patch( - "google.oauth2._client_async._token_endpoint_request" + "google.oauth2._client_async._token_endpoint_request", new_callable=AsyncMock ) as mock_token_endpoint_request: await _reauth_async._get_challenges( MOCK_REQUEST, ["SAML"], "token", requested_scopes=["scope"] @@ -90,7 +97,7 @@ async def test__get_challenges_with_scopes(): @pytest.mark.asyncio async def test__send_challenge_result(): with mock.patch( - "google.oauth2._client_async._token_endpoint_request" + "google.oauth2._client_async._token_endpoint_request", new_callable=AsyncMock ) as mock_token_endpoint_request: await _reauth_async._send_challenge_result( MOCK_REQUEST, "123", "1", {"credential": "password"}, "token" @@ -166,7 +173,7 @@ async def test__run_next_challenge_success(): "google.oauth2.challenges.AVAILABLE_CHALLENGES", {"PASSWORD": mock_challenge} ): with mock.patch( - "google.oauth2._reauth_async._send_challenge_result" + "google.oauth2._reauth_async._send_challenge_result", new_callable=AsyncMock ) as mock_send_challenge_result: await _reauth_async._run_next_challenge( CHALLENGES_RESPONSE_TEMPLATE, MOCK_REQUEST, "token" @@ -180,6 +187,7 @@ async def test__run_next_challenge_success(): async def test__obtain_rapt_authenticated(): with mock.patch( "google.oauth2._reauth_async._get_challenges", + new_callable=AsyncMock, return_value=CHALLENGES_RESPONSE_AUTHENTICATED, ): new_rapt_token = await _reauth_async._obtain_rapt(MOCK_REQUEST, "token", None) @@ -190,10 +198,12 @@ async def test__obtain_rapt_authenticated(): async def test__obtain_rapt_authenticated_after_run_next_challenge(): with mock.patch( "google.oauth2._reauth_async._get_challenges", + new_callable=AsyncMock, return_value=CHALLENGES_RESPONSE_TEMPLATE, ): with mock.patch( "google.oauth2._reauth_async._run_next_challenge", + new_callable=AsyncMock, side_effect=[ CHALLENGES_RESPONSE_TEMPLATE, CHALLENGES_RESPONSE_AUTHENTICATED, @@ -211,7 +221,9 @@ async def test__obtain_rapt_unsupported_status(): challenges_response = copy.deepcopy(CHALLENGES_RESPONSE_TEMPLATE) challenges_response["status"] = "STATUS_UNSPECIFIED" with mock.patch( - "google.oauth2._reauth_async._get_challenges", return_value=challenges_response + "google.oauth2._reauth_async._get_challenges", + new_callable=AsyncMock, + return_value=challenges_response, ): with pytest.raises(exceptions.ReauthFailError) as excinfo: await _reauth_async._obtain_rapt(MOCK_REQUEST, "token", None) @@ -222,6 +234,7 @@ async def test__obtain_rapt_unsupported_status(): async def test__obtain_rapt_not_interactive(): with mock.patch( "google.oauth2._reauth_async._get_challenges", + new_callable=AsyncMock, return_value=CHALLENGES_RESPONSE_TEMPLATE, ): with mock.patch("google.oauth2.reauth.is_interactive", return_value=False): @@ -234,6 +247,7 @@ async def test__obtain_rapt_not_interactive(): async def test__obtain_rapt_not_authenticated(): with mock.patch( "google.oauth2._reauth_async._get_challenges", + new_callable=AsyncMock, return_value=CHALLENGES_RESPONSE_TEMPLATE, ): with mock.patch("google.oauth2.reauth.RUN_CHALLENGE_RETRY_LIMIT", 0): @@ -246,10 +260,13 @@ async def test__obtain_rapt_not_authenticated(): async def test_get_rapt_token(): with mock.patch( "google.oauth2._client_async.refresh_grant", + new_callable=AsyncMock, return_value=("token", None, None, None), ) as mock_refresh_grant: with mock.patch( - "google.oauth2._reauth_async._obtain_rapt", return_value="new_rapt_token" + "google.oauth2._reauth_async._obtain_rapt", + new_callable=AsyncMock, + return_value="new_rapt_token", ) as mock_obtain_rapt: assert ( await _reauth_async.get_rapt_token( @@ -277,7 +294,8 @@ async def test_get_rapt_token(): @pytest.mark.asyncio async def test_refresh_grant_failed(): with mock.patch( - "google.oauth2._client_async._token_endpoint_request_no_throw" + "google.oauth2._client_async._token_endpoint_request_no_throw", + new_callable=AsyncMock, ) as mock_token_request: mock_token_request.return_value = (False, {"error": "Bad request"}, True) with pytest.raises(exceptions.RefreshError) as excinfo: @@ -309,14 +327,17 @@ async def test_refresh_grant_failed(): @pytest.mark.asyncio async def test_refresh_grant_success(): with mock.patch( - "google.oauth2._client_async._token_endpoint_request_no_throw" + "google.oauth2._client_async._token_endpoint_request_no_throw", + new_callable=AsyncMock, ) as mock_token_request: mock_token_request.side_effect = [ (False, {"error": "invalid_grant", "error_subtype": "rapt_required"}, True), (True, {"access_token": "access_token"}, None), ] with mock.patch( - "google.oauth2._reauth_async.get_rapt_token", return_value="new_rapt_token" + "google.oauth2._reauth_async.get_rapt_token", + new_callable=AsyncMock, + return_value="new_rapt_token", ): assert await _reauth_async.refresh_grant( MOCK_REQUEST, @@ -337,7 +358,8 @@ async def test_refresh_grant_success(): @pytest.mark.asyncio async def test_refresh_grant_reauth_refresh_disabled(): with mock.patch( - "google.oauth2._client_async._token_endpoint_request_no_throw" + "google.oauth2._client_async._token_endpoint_request_no_throw", + new_callable=AsyncMock, ) as mock_token_request: mock_token_request.side_effect = [ ( diff --git a/tests_async/oauth2/test_service_account_async.py b/tests_async/oauth2/test_service_account_async.py index 5a9a89fca..de5b1da39 100644 --- a/tests_async/oauth2/test_service_account_async.py +++ b/tests_async/oauth2/test_service_account_async.py @@ -13,7 +13,14 @@ # limitations under the License. import datetime -from unittest import mock +import sys + +if sys.version_info >= (3, 8): + from unittest import mock + from unittest.mock import AsyncMock +else: + import mock + from mock import AsyncMock import pytest # type: ignore @@ -331,7 +338,7 @@ async def test_refresh_success(self, id_token_jwt_grant): {}, ) - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) # Refresh credentials await credentials.refresh(request) @@ -363,7 +370,7 @@ async def test_before_request_refreshes(self, id_token_jwt_grant): _helpers.utcnow() + datetime.timedelta(seconds=500), None, ) - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) # Credentials should start as invalid assert not credentials.valid diff --git a/tests_async/transport/test_aiohttp_requests.py b/tests_async/transport/test_aiohttp_requests.py index d6a24da2e..b607d0635 100644 --- a/tests_async/transport/test_aiohttp_requests.py +++ b/tests_async/transport/test_aiohttp_requests.py @@ -12,7 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from unittest import mock +import sys + +if sys.version_info >= (3, 8): + from unittest import mock + from unittest.mock import AsyncMock +else: + import mock + from mock import AsyncMock import aiohttp # type: ignore from aioresponses import aioresponses, core # type: ignore @@ -40,7 +47,7 @@ def test__is_compressed_not(self): @pytest.mark.asyncio async def test_raw_content(self): - mock_response = mock.AsyncMock() + mock_response = AsyncMock() mock_response.content.read.return_value = mock.sentinel.read combined_response = aiohttp_requests._CombinedResponse(response=mock_response) raw_content = await combined_response.raw_content() @@ -53,7 +60,7 @@ async def test_raw_content(self): @pytest.mark.asyncio async def test_content(self): - mock_response = mock.AsyncMock() + mock_response = AsyncMock() mock_response.content.read.return_value = mock.sentinel.read combined_response = aiohttp_requests._CombinedResponse(response=mock_response) content = await combined_response.content() @@ -100,7 +107,7 @@ async def test_status_prop(self): @pytest.mark.asyncio async def test_data_prop(self): - mock_response = mock.AsyncMock() + mock_response = AsyncMock() mock_response.content.read.return_value = mock.sentinel.read response = aiohttp_requests._Response(mock_response) data = await response.data.read()