From 7d3d877055d2079974d362d0c59580905ddfb58e Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 08:51:37 -0800 Subject: [PATCH 01/22] import sequence from typing --- google/auth/_default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/auth/_default.py b/google/auth/_default.py index d854163c4..b734cc75b 100644 --- a/google/auth/_default.py +++ b/google/auth/_default.py @@ -17,7 +17,7 @@ Implements application default credentials and project ID detection. """ -from collections.abc import Sequence +from typing import Sequence import io import json import logging From 1e98bc1788ef796ce87512cb21d1b3818c7abff3 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 08:52:34 -0800 Subject: [PATCH 02/22] add 3.7 and 3.8 to unit tests --- noxfile.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/noxfile.py b/noxfile.py index 8a97f74cd..5a019a979 100644 --- a/noxfile.py +++ b/noxfile.py @@ -32,9 +32,7 @@ ] DEFAULT_PYTHON_VERSION = "3.14" -# TODO(https://github.com/googleapis/google-auth-library-python/issues/1787): -# Remove or restore testing for Python 3.7/3.8 -UNIT_TEST_PYTHON_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] +UNIT_TEST_PYTHON_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] # Error if a python version is missing nox.options.error_on_missing_interpreters = True From 0d4111537892da28fad879314a3089a822216b6b Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 09:00:05 -0800 Subject: [PATCH 03/22] added nox session --- noxfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index 5a019a979..4890d43c6 100644 --- a/noxfile.py +++ b/noxfile.py @@ -42,8 +42,8 @@ "lint", "blacken", "mypy", - # TODO(https://github.com/googleapis/google-auth-library-python/issues/1787): - # Remove or restore testing for Python 3.7/3.8 + "unit-3.7", + "unit-3.8", "unit-3.9", "unit-3.10", "unit-3.11", From 4f4db7be5a6c9e0773f23f79fd0bcaff8ab666ae Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 09:40:49 -0800 Subject: [PATCH 04/22] added github actions --- .github/workflows/unittest.yml | 58 ++++++++++++++++++++++++++++++++++ noxfile.py | 2 -- 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/unittest.yml diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml new file mode 100644 index 000000000..3de1fa75e --- /dev/null +++ b/.github/workflows/unittest.yml @@ -0,0 +1,58 @@ +on: + pull_request: + branches: + - main +name: unittest +jobs: + unit: + runs-on: ubuntu-22.04 + strategy: + matrix: + python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - name: Install nox + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install nox + - name: Run unit tests + env: + COVERAGE_FILE: .coverage-${{ matrix.python }} + run: | + nox -s unit-${{ matrix.python }} + - name: Upload coverage results + uses: actions/upload-artifact@v4 + with: + name: coverage-artifact-${{ matrix.python }} + path: .coverage-${{ matrix.python }} + include-hidden-files: true + + cover: + runs-on: ubuntu-latest + needs: + - unit + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install coverage + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install coverage + - name: Download coverage results + uses: actions/download-artifact@v4 + with: + path: .coverage-results/ + - name: Report coverage results + run: | + find .coverage-results -type f -name '*.zip' -exec unzip {} \; + coverage combine .coverage-results/**/.coverage* + coverage report --show-missing --fail-under=99 diff --git a/noxfile.py b/noxfile.py index 4890d43c6..4a851967b 100644 --- a/noxfile.py +++ b/noxfile.py @@ -42,8 +42,6 @@ "lint", "blacken", "mypy", - "unit-3.7", - "unit-3.8", "unit-3.9", "unit-3.10", "unit-3.11", From 31ba7efd842b7378eec40bdb65396dfe0b187119 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 09:43:22 -0800 Subject: [PATCH 05/22] added annotations --- .github/workflows/unittest.yml | 2 +- google/auth/_default.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 3de1fa75e..6b6ee237b 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] + python: ['3.7', '3.8'] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/google/auth/_default.py b/google/auth/_default.py index b734cc75b..4877d7b1f 100644 --- a/google/auth/_default.py +++ b/google/auth/_default.py @@ -16,6 +16,7 @@ Implements application default credentials and project ID detection. """ +from __future__ import annotations from typing import Sequence import io From 31c434602ccaa59ba97265df4a7f631bd9da0a12 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 09:46:06 -0800 Subject: [PATCH 06/22] added extra annotations import --- google/oauth2/id_token.py | 1 + 1 file changed, 1 insertion(+) diff --git a/google/oauth2/id_token.py b/google/oauth2/id_token.py index fe4bebd78..d21be1a06 100644 --- a/google/oauth2/id_token.py +++ b/google/oauth2/id_token.py @@ -54,6 +54,7 @@ http://openid.net/specs/openid-connect-core-1_0.html#IDToken .. _CacheControl: https://cachecontrol.readthedocs.io """ +from __future__ import annotations import http.client as http_client import json From 1aa5bb7fe37fd5a66c9c979de0d9a42e091630c9 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 09:49:54 -0800 Subject: [PATCH 07/22] support AsyncMock on 3.7 --- tests/transport/aio/test_aiohttp.py | 7 ++++++- tests_async/oauth2/test_reauth_async.py | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/transport/aio/test_aiohttp.py b/tests/transport/aio/test_aiohttp.py index 13f86ba34..07791fc8f 100644 --- a/tests/transport/aio/test_aiohttp.py +++ b/tests/transport/aio/test_aiohttp.py @@ -13,7 +13,12 @@ # limitations under the License. import asyncio -from unittest.mock import AsyncMock, Mock, patch +from unittest.mock import Mock, patch +try: + from unittest.mock import AsyncMock +except ImportError: + # Fallback for Python < 3.8 + from mock import AsyncMock from aioresponses import aioresponses # type: ignore import pytest # type: ignore diff --git a/tests_async/oauth2/test_reauth_async.py b/tests_async/oauth2/test_reauth_async.py index 4874a3728..a2c80471f 100644 --- a/tests_async/oauth2/test_reauth_async.py +++ b/tests_async/oauth2/test_reauth_async.py @@ -14,6 +14,11 @@ import copy from unittest import mock +try: + from unittest.mock import AsyncMock +except ImportError: + # Fallback for Python < 3.8 + from mock import AsyncMock import pytest # type: ignore @@ -22,7 +27,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", From 8a1444a9b01e34c9d393a8dd563ff1fbc13eab3f Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 09:52:30 -0800 Subject: [PATCH 08/22] adding mock to constraints file --- testing/constraints-3.7.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt index 52ad3af91..b07e00fc3 100644 --- a/testing/constraints-3.7.txt +++ b/testing/constraints-3.7.txt @@ -10,4 +10,5 @@ setuptools==40.3.0 rsa==3.1.4 aiohttp==3.6.2 requests==2.20.0 -pyjwt==2.0 \ No newline at end of file +pyjwt==2.0 +mock==5.2.0 From 649855ab2c7e8e3dc21b5ce387d035e81ce17758 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 09:55:46 -0800 Subject: [PATCH 09/22] add mock to testing extras --- setup.py | 1 + testing/constraints-3.7.txt | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3db2d8cf9..4ee234fc2 100644 --- a/setup.py +++ b/setup.py @@ -76,6 +76,7 @@ # 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; python_version < '3.8'", ] extras = { diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt index b07e00fc3..c5c53dbbe 100644 --- a/testing/constraints-3.7.txt +++ b/testing/constraints-3.7.txt @@ -11,4 +11,3 @@ rsa==3.1.4 aiohttp==3.6.2 requests==2.20.0 pyjwt==2.0 -mock==5.2.0 From 8a082c9e68d25d6228d6da90d175358f25e3af62 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 10:00:42 -0800 Subject: [PATCH 10/22] remove 3.7 from tests --- .github/workflows/unittest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 6b6ee237b..ab21829d1 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - python: ['3.7', '3.8'] + python: ['3.8'] steps: - name: Checkout uses: actions/checkout@v4 From 3322dfea0909fbd08ee2d6e467d0f9cd12ff5640 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 10:06:21 -0800 Subject: [PATCH 11/22] revert 3.7 changes --- setup.py | 1 - testing/constraints-3.7.txt | 2 +- tests/transport/aio/test_aiohttp.py | 7 +------ tests_async/oauth2/test_reauth_async.py | 7 +------ 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/setup.py b/setup.py index 4ee234fc2..3db2d8cf9 100644 --- a/setup.py +++ b/setup.py @@ -76,7 +76,6 @@ # 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; python_version < '3.8'", ] extras = { diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt index c5c53dbbe..52ad3af91 100644 --- a/testing/constraints-3.7.txt +++ b/testing/constraints-3.7.txt @@ -10,4 +10,4 @@ setuptools==40.3.0 rsa==3.1.4 aiohttp==3.6.2 requests==2.20.0 -pyjwt==2.0 +pyjwt==2.0 \ No newline at end of file diff --git a/tests/transport/aio/test_aiohttp.py b/tests/transport/aio/test_aiohttp.py index 07791fc8f..13f86ba34 100644 --- a/tests/transport/aio/test_aiohttp.py +++ b/tests/transport/aio/test_aiohttp.py @@ -13,12 +13,7 @@ # limitations under the License. import asyncio -from unittest.mock import Mock, patch -try: - from unittest.mock import AsyncMock -except ImportError: - # Fallback for Python < 3.8 - from mock import AsyncMock +from unittest.mock import AsyncMock, Mock, patch from aioresponses import aioresponses # type: ignore import pytest # type: ignore diff --git a/tests_async/oauth2/test_reauth_async.py b/tests_async/oauth2/test_reauth_async.py index a2c80471f..4874a3728 100644 --- a/tests_async/oauth2/test_reauth_async.py +++ b/tests_async/oauth2/test_reauth_async.py @@ -14,11 +14,6 @@ import copy from unittest import mock -try: - from unittest.mock import AsyncMock -except ImportError: - # Fallback for Python < 3.8 - from mock import AsyncMock import pytest # type: ignore @@ -27,7 +22,7 @@ from google.oauth2 import reauth -MOCK_REQUEST = AsyncMock(spec=["transport.Request"]) +MOCK_REQUEST = mock.AsyncMock(spec=["transport.Request"]) CHALLENGES_RESPONSE_TEMPLATE = { "status": "CHALLENGE_REQUIRED", "sessionId": "123", From 1211004ea82b4059598748fcb343c0bef1677d06 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 10:42:36 -0800 Subject: [PATCH 12/22] fixed lint --- noxfile.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 4a851967b..70173e55c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -32,7 +32,16 @@ ] DEFAULT_PYTHON_VERSION = "3.14" -UNIT_TEST_PYTHON_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] +UNIT_TEST_PYTHON_VERSIONS = [ + "3.7", + "3.8", + "3.9", + "3.10", + "3.11", + "3.12", + "3.13", + "3.14", +] # Error if a python version is missing nox.options.error_on_missing_interpreters = True From 5d9b9554bb5d40eb6267fa77fcd17b86588c4e8a Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 10:47:50 -0800 Subject: [PATCH 13/22] add 3.7 unit tests --- .github/workflows/unittest.yml | 2 +- setup.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index ab21829d1..6b6ee237b 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -8,7 +8,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..4ee234fc2 100644 --- a/setup.py +++ b/setup.py @@ -76,6 +76,7 @@ # 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; python_version < '3.8'", ] extras = { From 1e1ed0a9123be559f7417edda026d0f4e84dc381 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 10:48:25 -0800 Subject: [PATCH 14/22] support AsyncMock for 3.7 --- tests/transport/aio/test_aiohttp.py | 8 +- tests_async/oauth2/test__client_async.py | 81 ++++++++++--------- tests_async/oauth2/test_credentials_async.py | 15 ++-- tests_async/oauth2/test_id_token.py | 27 ++++--- tests_async/oauth2/test_reauth_async.py | 7 +- .../oauth2/test_service_account_async.py | 9 ++- 6 files changed, 88 insertions(+), 59 deletions(-) diff --git a/tests/transport/aio/test_aiohttp.py b/tests/transport/aio/test_aiohttp.py index 13f86ba34..92ce2ccc0 100644 --- a/tests/transport/aio/test_aiohttp.py +++ b/tests/transport/aio/test_aiohttp.py @@ -13,8 +13,12 @@ # limitations under the License. import asyncio -from unittest.mock import AsyncMock, Mock, patch - +from unittest.mock import Mock, patch +try: + from unittest.mock import AsyncMock +except ImportError: + # Fallback for Python < 3.8 + from mock import AsyncMock from aioresponses import aioresponses # type: ignore import pytest # type: ignore import pytest_asyncio # type: ignore diff --git a/tests_async/oauth2/test__client_async.py b/tests_async/oauth2/test__client_async.py index 5ad9596cf..49936d0de 100644 --- a/tests_async/oauth2/test__client_async.py +++ b/tests_async/oauth2/test__client_async.py @@ -16,6 +16,11 @@ import http.client as http_client import json from unittest import mock +try: + from unittest.mock import AsyncMock +except ImportError: + # Fallback for Python < 3.8 + from mock import AsyncMock import urllib import pytest # type: ignore @@ -29,13 +34,13 @@ 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"]) + response.data = AsyncMock(spec=["__call__", "read"]) + response.data.read = AsyncMock(spec=["__call__"], return_value=data) + response.content = AsyncMock(spec=["__call__"], return_value=data) + request = AsyncMock(spec=["transport.Request"]) request.return_value = response return request @@ -142,21 +147,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 +175,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( @@ -399,7 +404,7 @@ 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 + AsyncMock(), 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 @@ -426,7 +431,7 @@ 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 + AsyncMock(), AsyncMock(), AsyncMock(), can_retry=can_retry ) mock_token_endpoint_request.assert_called_with( mock.ANY, mock.ANY, mock.ANY, can_retry=can_retry @@ -440,11 +445,11 @@ 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(), + AsyncMock(), + AsyncMock(), + AsyncMock(), + AsyncMock(), + AsyncMock(), ) mock_token_endpoint_request.assert_called_with( mock.ANY, mock.ANY, mock.ANY, can_retry=True @@ -459,11 +464,11 @@ 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(), + AsyncMock(), + AsyncMock(), + AsyncMock(), + AsyncMock(), + AsyncMock(), can_retry=can_retry, ) mock_token_endpoint_request.assert_called_with( @@ -481,10 +486,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..41da15a39 100644 --- a/tests_async/oauth2/test_credentials_async.py +++ b/tests_async/oauth2/test_credentials_async.py @@ -18,6 +18,11 @@ import pickle import sys from unittest import mock +try: + from unittest.mock import AsyncMock +except ImportError: + # Fallback for Python < 3.8 + from mock import AsyncMock import pytest # type: ignore @@ -82,7 +87,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 @@ -112,7 +117,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"): @@ -147,7 +152,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, @@ -211,7 +216,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, @@ -278,7 +283,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, diff --git a/tests_async/oauth2/test_id_token.py b/tests_async/oauth2/test_id_token.py index 51d85daf2..138fadd2c 100644 --- a/tests_async/oauth2/test_id_token.py +++ b/tests_async/oauth2/test_id_token.py @@ -15,6 +15,11 @@ import json import os from unittest import mock +try: + from unittest.mock import AsyncMock +except ImportError: + # Fallback for Python < 3.8 + from mock import AsyncMock import pytest # type: ignore @@ -28,16 +33,16 @@ 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( + response.data = AsyncMock(spec=["__call__", "read"]) + response.content = AsyncMock( spec=["__call__"], return_value=json.dumps(data).encode("utf-8") ) - request = mock.AsyncMock(spec=["transport.Request"]) + request = AsyncMock(spec=["transport.Request"]) request.return_value = response return request @@ -223,7 +228,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 +245,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 +259,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 +267,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 +282,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 +298,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 +313,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..a2c80471f 100644 --- a/tests_async/oauth2/test_reauth_async.py +++ b/tests_async/oauth2/test_reauth_async.py @@ -14,6 +14,11 @@ import copy from unittest import mock +try: + from unittest.mock import AsyncMock +except ImportError: + # Fallback for Python < 3.8 + from mock import AsyncMock import pytest # type: ignore @@ -22,7 +27,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", diff --git a/tests_async/oauth2/test_service_account_async.py b/tests_async/oauth2/test_service_account_async.py index 5a9a89fca..8e9b394aa 100644 --- a/tests_async/oauth2/test_service_account_async.py +++ b/tests_async/oauth2/test_service_account_async.py @@ -14,6 +14,11 @@ import datetime from unittest import mock +try: + from unittest.mock import AsyncMock +except ImportError: + # Fallback for Python < 3.8 + from mock import AsyncMock import pytest # type: ignore @@ -331,7 +336,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 +368,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 From 47f0753695ff8011664f24f8711117563fef2b68 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 10:58:16 -0800 Subject: [PATCH 15/22] fixed 3.7 test errors --- tests/compute_engine/test_credentials.py | 2 +- tests/oauth2/test_webauthn_handler.py | 2 +- tests/test__exponential_backoff.py | 6 +++++- tests/test_impersonated_credentials.py | 12 +++++++----- tests/transport/aio/test_sessions.py | 5 ++++- tests_async/oauth2/test__client_async.py | 8 ++++++-- tests_async/oauth2/test_id_token.py | 9 ++++++--- 7 files changed, 30 insertions(+), 14 deletions(-) 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(" Date: Tue, 6 Jan 2026 11:11:57 -0800 Subject: [PATCH 16/22] update tests --- setup.py | 1 + tests/test__exponential_backoff.py | 11 +- tests_async/oauth2/test__client_async.py | 149 ++++++++++--------- tests_async/oauth2/test_credentials_async.py | 8 +- 4 files changed, 90 insertions(+), 79 deletions(-) diff --git a/setup.py b/setup.py index 4ee234fc2..dadf78bd0 100644 --- a/setup.py +++ b/setup.py @@ -77,6 +77,7 @@ # aiohttp < 3.10.0 which is a bug. Investigate and remove the pinned aiohttp version. "aiohttp < 3.10.0", "mock; python_version < '3.8'", + "freezegun", ] extras = { diff --git a/tests/test__exponential_backoff.py b/tests/test__exponential_backoff.py index 6411fa561..b793576e8 100644 --- a/tests/test__exponential_backoff.py +++ b/tests/test__exponential_backoff.py @@ -13,6 +13,11 @@ # limitations under the License. from unittest import mock +try: + from unittest.mock import AsyncMock +except ImportError: + # Fallback for Python < 3.8 + from mock import AsyncMock import pytest # type: ignore @@ -57,12 +62,8 @@ def test_minimum_total_attempts(): _exponential_backoff.ExponentialBackoff(total_attempts=1) -async def async_sleep(delay): - pass - - @pytest.mark.asyncio -@mock.patch("asyncio.sleep", side_effect=async_sleep) +@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_async/oauth2/test__client_async.py b/tests_async/oauth2/test__client_async.py index 28f6b6fd0..a6e784100 100644 --- a/tests_async/oauth2/test__client_async.py +++ b/tests_async/oauth2/test__client_async.py @@ -23,6 +23,7 @@ from mock import AsyncMock import urllib +import freezegun import pytest # type: ignore from google.auth import _helpers @@ -212,26 +213,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 @@ -292,8 +293,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", @@ -303,37 +303,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", @@ -344,32 +344,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 @@ -390,11 +391,19 @@ async def test_refresh_grant_no_access_token(): assert not excinfo.value.retryable +def create_async_mock(): + m = mock.Mock() + async def async_response(*args, **kwargs): + return mock.Mock() + m.side_effect = async_response + return m + + @pytest.mark.asyncio @mock.patch("google.oauth2._client._parse_expiry", return_value=None) @mock.patch.object(_client, "_token_endpoint_request", autospec=True) async def test_jwt_grant_retry_default(mock_token_endpoint_request, mock_expiry): - _ = await _client.jwt_grant(mock.Mock(), mock.Mock(), mock.Mock()) + _ = await _client.jwt_grant(create_async_mock(), mock.Mock(), mock.Mock()) mock_token_endpoint_request.assert_called_with( mock.ANY, mock.ANY, mock.ANY, can_retry=True ) @@ -408,7 +417,7 @@ async def test_jwt_grant_retry_with_retry( mock_token_endpoint_request, mock_expiry, can_retry ): _ = await _client.jwt_grant( - AsyncMock(), mock.Mock(), mock.Mock(), can_retry=can_retry + create_async_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 @@ -421,7 +430,7 @@ async def test_jwt_grant_retry_with_retry( async def test_id_token_jwt_grant_retry_default( mock_token_endpoint_request, mock_jwt_decode ): - _ = await _client.id_token_jwt_grant(mock.Mock(), mock.Mock(), mock.Mock()) + _ = await _client.id_token_jwt_grant(create_async_mock(), mock.Mock(), mock.Mock()) mock_token_endpoint_request.assert_called_with( mock.ANY, mock.ANY, mock.ANY, can_retry=True ) @@ -435,7 +444,7 @@ 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( - AsyncMock(), AsyncMock(), AsyncMock(), can_retry=can_retry + create_async_mock(), AsyncMock(), AsyncMock(), can_retry=can_retry ) mock_token_endpoint_request.assert_called_with( mock.ANY, mock.ANY, mock.ANY, can_retry=can_retry diff --git a/tests_async/oauth2/test_credentials_async.py b/tests_async/oauth2/test_credentials_async.py index 41da15a39..f9db211c7 100644 --- a/tests_async/oauth2/test_credentials_async.py +++ b/tests_async/oauth2/test_credentials_async.py @@ -63,7 +63,7 @@ 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") @mock.patch( "google.auth._helpers.utcnow", return_value=datetime.datetime.min + _helpers.REFRESH_THRESHOLD, @@ -125,7 +125,7 @@ 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") @mock.patch( "google.auth._helpers.utcnow", return_value=datetime.datetime.min + _helpers.REFRESH_THRESHOLD, @@ -189,7 +189,7 @@ 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") @mock.patch( "google.auth._helpers.utcnow", return_value=datetime.datetime.min + _helpers.REFRESH_THRESHOLD, @@ -252,7 +252,7 @@ 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") @mock.patch( "google.auth._helpers.utcnow", return_value=datetime.datetime.min + _helpers.REFRESH_THRESHOLD, From 38dc583cf9d1970d510602ee49f1cdd393b2e1c9 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 11:22:34 -0800 Subject: [PATCH 17/22] updated tests --- tests_async/oauth2/test_credentials_async.py | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests_async/oauth2/test_credentials_async.py b/tests_async/oauth2/test_credentials_async.py index f9db211c7..bca1360f2 100644 --- a/tests_async/oauth2/test_credentials_async.py +++ b/tests_async/oauth2/test_credentials_async.py @@ -100,9 +100,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 @@ -173,9 +173,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 @@ -236,9 +236,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 @@ -307,9 +307,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 From b4fb17bc408cb129a9d410f72b2cb44447139312 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 11:35:35 -0800 Subject: [PATCH 18/22] fixed tests --- tests_async/oauth2/test__client_async.py | 48 ++++++++----------- tests_async/oauth2/test_credentials_async.py | 10 ++-- tests_async/oauth2/test_id_token.py | 16 +++---- tests_async/oauth2/test_reauth_async.py | 26 ++++++---- .../transport/test_aiohttp_requests.py | 4 ++ 5 files changed, 53 insertions(+), 51 deletions(-) diff --git a/tests_async/oauth2/test__client_async.py b/tests_async/oauth2/test__client_async.py index a6e784100..130141bd2 100644 --- a/tests_async/oauth2/test__client_async.py +++ b/tests_async/oauth2/test__client_async.py @@ -391,19 +391,11 @@ async def test_refresh_grant_no_access_token(): assert not excinfo.value.retryable -def create_async_mock(): - m = mock.Mock() - async def async_response(*args, **kwargs): - return mock.Mock() - m.side_effect = async_response - return m - - @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(create_async_mock(), mock.Mock(), mock.Mock()) + _ = await _client.jwt_grant(mock.Mock(), mock.Mock(), mock.Mock()) mock_token_endpoint_request.assert_called_with( mock.ANY, mock.ANY, mock.ANY, can_retry=True ) @@ -412,12 +404,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( - create_async_mock(), 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 @@ -426,11 +418,11 @@ 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 ): - _ = await _client.id_token_jwt_grant(create_async_mock(), mock.Mock(), mock.Mock()) + _ = await _client.id_token_jwt_grant(mock.Mock(), mock.Mock(), mock.Mock()) mock_token_endpoint_request.assert_called_with( mock.ANY, mock.ANY, mock.ANY, can_retry=True ) @@ -439,12 +431,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( - create_async_mock(), AsyncMock(), 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 @@ -453,16 +445,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( - AsyncMock(), - AsyncMock(), - AsyncMock(), - AsyncMock(), - 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 @@ -472,16 +464,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( - AsyncMock(), - AsyncMock(), - AsyncMock(), - AsyncMock(), - AsyncMock(), + mock.Mock(), + mock.Mock(), + mock.Mock(), + mock.Mock(), + mock.Mock(), can_retry=can_retry, ) mock_token_endpoint_request.assert_called_with( diff --git a/tests_async/oauth2/test_credentials_async.py b/tests_async/oauth2/test_credentials_async.py index bca1360f2..a1a9820e0 100644 --- a/tests_async/oauth2/test_credentials_async.py +++ b/tests_async/oauth2/test_credentials_async.py @@ -63,7 +63,7 @@ def test_default_state(self): assert credentials.client_id == self.CLIENT_ID assert credentials.client_secret == self.CLIENT_SECRET - @mock.patch("google.oauth2._credentials_async.reauth.refresh_grant") + @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, @@ -125,7 +125,7 @@ async def test_refresh_no_refresh_token(self): request.assert_not_called() - @mock.patch("google.oauth2._credentials_async.reauth.refresh_grant") + @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, @@ -189,7 +189,7 @@ async def test_credentials_with_scopes_requested_refresh_success( # expired.) assert creds.valid - @mock.patch("google.oauth2._credentials_async.reauth.refresh_grant") + @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, @@ -252,7 +252,7 @@ async def test_credentials_with_scopes_returned_refresh_success( # expired.) assert creds.valid - @mock.patch("google.oauth2._credentials_async.reauth.refresh_grant") + @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, @@ -471,7 +471,7 @@ 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 4c147880b..e0aba8583 100644 --- a/tests_async/oauth2/test_id_token.py +++ b/tests_async/oauth2/test_id_token.py @@ -72,7 +72,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) @@ -90,7 +90,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( @@ -110,7 +110,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( @@ -130,7 +130,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"} @@ -148,7 +148,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"} @@ -169,7 +169,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"} @@ -180,7 +180,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( @@ -197,7 +197,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( diff --git a/tests_async/oauth2/test_reauth_async.py b/tests_async/oauth2/test_reauth_async.py index a2c80471f..f10c372d9 100644 --- a/tests_async/oauth2/test_reauth_async.py +++ b/tests_async/oauth2/test_reauth_async.py @@ -60,7 +60,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( @@ -75,7 +75,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"] @@ -95,7 +95,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" @@ -171,7 +171,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" @@ -185,6 +185,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) @@ -195,10 +196,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, @@ -216,7 +219,7 @@ 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) @@ -227,6 +230,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): @@ -239,6 +243,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): @@ -251,10 +256,11 @@ 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( @@ -282,7 +288,7 @@ 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: @@ -314,14 +320,14 @@ 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, @@ -342,7 +348,7 @@ 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/transport/test_aiohttp_requests.py b/tests_async/transport/test_aiohttp_requests.py index d6a24da2e..3b9d3b15d 100644 --- a/tests_async/transport/test_aiohttp_requests.py +++ b/tests_async/transport/test_aiohttp_requests.py @@ -13,6 +13,10 @@ # limitations under the License. from unittest import mock +try: + from unittest.mock import AsyncMock +except ImportError: + from mock import AsyncMock import aiohttp # type: ignore from aioresponses import aioresponses, core # type: ignore From eaef0fb9d5e5a58ed6dec892dda2a21802ff1fc7 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 11:58:14 -0800 Subject: [PATCH 19/22] fixing tests --- setup.py | 1 + tests/test__exponential_backoff.py | 17 ++++++++++------ tests_async/oauth2/test__client_async.py | 18 +++++++++++------ tests_async/oauth2/test_credentials_async.py | 13 +++++++----- tests_async/oauth2/test_id_token.py | 9 +++++---- .../oauth2/test_service_account_async.py | 14 ++++++++----- .../transport/test_aiohttp_requests.py | 20 ++++++++++++------- 7 files changed, 59 insertions(+), 33 deletions(-) diff --git a/setup.py b/setup.py index dadf78bd0..3e83e859f 100644 --- a/setup.py +++ b/setup.py @@ -77,6 +77,7 @@ # aiohttp < 3.10.0 which is a bug. Investigate and remove the pinned aiohttp version. "aiohttp < 3.10.0", "mock; python_version < '3.8'", + "asyncmock; python_version < '3.8'", "freezegun", ] diff --git a/tests/test__exponential_backoff.py b/tests/test__exponential_backoff.py index b793576e8..c7f214211 100644 --- a/tests/test__exponential_backoff.py +++ b/tests/test__exponential_backoff.py @@ -12,12 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -from unittest import mock -try: +import sys +if sys.version_info >= (3, 8): + from unittest import mock from unittest.mock import AsyncMock -except ImportError: - # Fallback for Python < 3.8 - from mock import AsyncMock +else: + import mock + + try: + from mock import AsyncMock + except ImportError: + from asyncmock import AsyncMock import pytest # type: ignore @@ -100,4 +105,4 @@ def test_minimum_total_attempts_async(): _exponential_backoff.AsyncExponentialBackoff(total_attempts=0) with pytest.raises(exceptions.InvalidValue): _exponential_backoff.AsyncExponentialBackoff(total_attempts=-1) - _exponential_backoff.AsyncExponentialBackoff(total_attempts=1) + _exponential_backoff.AsyncExponentialBackoff(total_attempts=1) \ No newline at end of file diff --git a/tests_async/oauth2/test__client_async.py b/tests_async/oauth2/test__client_async.py index 130141bd2..e24a4c4f9 100644 --- a/tests_async/oauth2/test__client_async.py +++ b/tests_async/oauth2/test__client_async.py @@ -15,14 +15,20 @@ import datetime import http.client as http_client import json -from unittest import mock -try: - from unittest.mock import AsyncMock -except ImportError: - # Fallback for Python < 3.8 - from mock import AsyncMock +import sys import urllib +if sys.version_info >= (3, 8): + from unittest import mock + from unittest.mock import AsyncMock +else: + import mock + + try: + from mock import AsyncMock + except ImportError: + from asyncmock import AsyncMock + import freezegun import pytest # type: ignore diff --git a/tests_async/oauth2/test_credentials_async.py b/tests_async/oauth2/test_credentials_async.py index a1a9820e0..e1a2d5324 100644 --- a/tests_async/oauth2/test_credentials_async.py +++ b/tests_async/oauth2/test_credentials_async.py @@ -17,12 +17,15 @@ import os import pickle import sys -from unittest import mock -try: +if sys.version_info >= (3, 8): + from unittest import mock from unittest.mock import AsyncMock -except ImportError: - # Fallback for Python < 3.8 - from mock import AsyncMock +else: + import mock + try: + from mock import AsyncMock + except ImportError: + from asyncmock import AsyncMock import pytest # type: ignore diff --git a/tests_async/oauth2/test_id_token.py b/tests_async/oauth2/test_id_token.py index e0aba8583..3da644c0e 100644 --- a/tests_async/oauth2/test_id_token.py +++ b/tests_async/oauth2/test_id_token.py @@ -14,11 +14,12 @@ import json import os -from unittest import mock -try: +import sys +if sys.version_info >= (3, 8): + from unittest import mock from unittest.mock import AsyncMock -except ImportError: - # Fallback for Python < 3.8 +else: + import mock from mock import AsyncMock import pytest # type: ignore diff --git a/tests_async/oauth2/test_service_account_async.py b/tests_async/oauth2/test_service_account_async.py index 8e9b394aa..94e25d47c 100644 --- a/tests_async/oauth2/test_service_account_async.py +++ b/tests_async/oauth2/test_service_account_async.py @@ -13,12 +13,16 @@ # limitations under the License. import datetime -from unittest import mock -try: +import sys +if sys.version_info >= (3, 8): + from unittest import mock from unittest.mock import AsyncMock -except ImportError: - # Fallback for Python < 3.8 - from mock import AsyncMock +else: + import mock + try: + from mock import AsyncMock + except ImportError: + from asyncmock import AsyncMock import pytest # type: ignore diff --git a/tests_async/transport/test_aiohttp_requests.py b/tests_async/transport/test_aiohttp_requests.py index 3b9d3b15d..765993f1a 100644 --- a/tests_async/transport/test_aiohttp_requests.py +++ b/tests_async/transport/test_aiohttp_requests.py @@ -12,11 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -from unittest import mock -try: +import sys +if sys.version_info >= (3, 8): + from unittest import mock from unittest.mock import AsyncMock -except ImportError: - from mock import AsyncMock +else: + import mock + + try: + from mock import AsyncMock + except ImportError: + from asyncmock import AsyncMock import aiohttp # type: ignore from aioresponses import aioresponses, core # type: ignore @@ -44,7 +50,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() @@ -57,7 +63,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() @@ -104,7 +110,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() From 42d331da585d0a8041ad33d38ccd01593564b4c0 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 12:06:21 -0800 Subject: [PATCH 20/22] simplified imports --- setup.py | 3 +-- tests/test__exponential_backoff.py | 6 +----- tests/transport/aio/test_aiohttp.py | 11 ++++++----- tests_async/oauth2/test__client_async.py | 6 +----- tests_async/oauth2/test_credentials_async.py | 5 +---- tests_async/oauth2/test_reauth_async.py | 9 +++++---- tests_async/oauth2/test_service_account_async.py | 5 +---- tests_async/transport/test_aiohttp_requests.py | 6 +----- 8 files changed, 17 insertions(+), 34 deletions(-) diff --git a/setup.py b/setup.py index 3e83e859f..68bb92a8a 100644 --- a/setup.py +++ b/setup.py @@ -76,8 +76,7 @@ # 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; python_version < '3.8'", - "asyncmock; python_version < '3.8'", + "mock >= 4.0.0; python_version < '3.8'", "freezegun", ] diff --git a/tests/test__exponential_backoff.py b/tests/test__exponential_backoff.py index c7f214211..6716a02e4 100644 --- a/tests/test__exponential_backoff.py +++ b/tests/test__exponential_backoff.py @@ -18,11 +18,7 @@ from unittest.mock import AsyncMock else: import mock - - try: - from mock import AsyncMock - except ImportError: - from asyncmock import AsyncMock + from mock import AsyncMock import pytest # type: ignore diff --git a/tests/transport/aio/test_aiohttp.py b/tests/transport/aio/test_aiohttp.py index 92ce2ccc0..3bfc5c5b2 100644 --- a/tests/transport/aio/test_aiohttp.py +++ b/tests/transport/aio/test_aiohttp.py @@ -13,12 +13,13 @@ # limitations under the License. import asyncio -from unittest.mock import Mock, patch -try: - from unittest.mock import AsyncMock -except ImportError: - # Fallback for Python < 3.8 +import sys +if sys.version_info >= (3, 8): + from unittest.mock import Mock, patch, AsyncMock +else: + from unittest.mock import Mock, patch from mock import AsyncMock + from aioresponses import aioresponses # type: ignore import pytest # type: ignore import pytest_asyncio # type: ignore diff --git a/tests_async/oauth2/test__client_async.py b/tests_async/oauth2/test__client_async.py index e24a4c4f9..ebc6f0a71 100644 --- a/tests_async/oauth2/test__client_async.py +++ b/tests_async/oauth2/test__client_async.py @@ -23,11 +23,7 @@ from unittest.mock import AsyncMock else: import mock - - try: - from mock import AsyncMock - except ImportError: - from asyncmock import AsyncMock + from mock import AsyncMock import freezegun import pytest # type: ignore diff --git a/tests_async/oauth2/test_credentials_async.py b/tests_async/oauth2/test_credentials_async.py index e1a2d5324..bfba92d5c 100644 --- a/tests_async/oauth2/test_credentials_async.py +++ b/tests_async/oauth2/test_credentials_async.py @@ -22,10 +22,7 @@ from unittest.mock import AsyncMock else: import mock - try: - from mock import AsyncMock - except ImportError: - from asyncmock import AsyncMock + from mock import AsyncMock import pytest # type: ignore diff --git a/tests_async/oauth2/test_reauth_async.py b/tests_async/oauth2/test_reauth_async.py index f10c372d9..9b38f3b84 100644 --- a/tests_async/oauth2/test_reauth_async.py +++ b/tests_async/oauth2/test_reauth_async.py @@ -13,11 +13,12 @@ # limitations under the License. import copy -from unittest import mock -try: +import sys +if sys.version_info >= (3, 8): + from unittest import mock from unittest.mock import AsyncMock -except ImportError: - # Fallback for Python < 3.8 +else: + import mock from mock import AsyncMock import pytest # type: ignore diff --git a/tests_async/oauth2/test_service_account_async.py b/tests_async/oauth2/test_service_account_async.py index 94e25d47c..150100a67 100644 --- a/tests_async/oauth2/test_service_account_async.py +++ b/tests_async/oauth2/test_service_account_async.py @@ -19,10 +19,7 @@ from unittest.mock import AsyncMock else: import mock - try: - from mock import AsyncMock - except ImportError: - from asyncmock import AsyncMock + from mock import AsyncMock import pytest # type: ignore diff --git a/tests_async/transport/test_aiohttp_requests.py b/tests_async/transport/test_aiohttp_requests.py index 765993f1a..e4f57ec06 100644 --- a/tests_async/transport/test_aiohttp_requests.py +++ b/tests_async/transport/test_aiohttp_requests.py @@ -18,11 +18,7 @@ from unittest.mock import AsyncMock else: import mock - - try: - from mock import AsyncMock - except ImportError: - from asyncmock import AsyncMock + from mock import AsyncMock import aiohttp # type: ignore from aioresponses import aioresponses, core # type: ignore From 81789cc6c815f2d2586a9d15805fd0d8c84abe27 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 12:18:33 -0800 Subject: [PATCH 21/22] fixed lint --- google/auth/_default.py | 1 + tests/test__exponential_backoff.py | 3 ++- tests/test_impersonated_credentials.py | 2 +- tests/transport/aio/test_aiohttp.py | 1 + tests_async/oauth2/test_credentials_async.py | 21 +++++++++++++----- tests_async/oauth2/test_id_token.py | 5 +++-- tests_async/oauth2/test_reauth_async.py | 22 ++++++++++++++----- .../oauth2/test_service_account_async.py | 1 + .../transport/test_aiohttp_requests.py | 1 + 9 files changed, 42 insertions(+), 15 deletions(-) diff --git a/google/auth/_default.py b/google/auth/_default.py index cbb5408e9..01bd02a21 100644 --- a/google/auth/_default.py +++ b/google/auth/_default.py @@ -17,6 +17,7 @@ Implements application default credentials and project ID detection. """ from __future__ import annotations + import io import json import logging diff --git a/tests/test__exponential_backoff.py b/tests/test__exponential_backoff.py index 6716a02e4..5c4515a57 100644 --- a/tests/test__exponential_backoff.py +++ b/tests/test__exponential_backoff.py @@ -13,6 +13,7 @@ # limitations under the License. import sys + if sys.version_info >= (3, 8): from unittest import mock from unittest.mock import AsyncMock @@ -101,4 +102,4 @@ def test_minimum_total_attempts_async(): _exponential_backoff.AsyncExponentialBackoff(total_attempts=0) with pytest.raises(exceptions.InvalidValue): _exponential_backoff.AsyncExponentialBackoff(total_attempts=-1) - _exponential_backoff.AsyncExponentialBackoff(total_attempts=1) \ No newline at end of file + _exponential_backoff.AsyncExponentialBackoff(total_attempts=1) diff --git a/tests/test_impersonated_credentials.py b/tests/test_impersonated_credentials.py index 1e2e7fcb2..2f1c22f8f 100644 --- a/tests/test_impersonated_credentials.py +++ b/tests/test_impersonated_credentials.py @@ -1147,7 +1147,7 @@ def _test_id_token_helper( # In Python 3.7, self might not be in call_args for autospecced methods args = mock_authorizedsession_idtoken.call_args[0] - + # Look for expected_url in args assert expected_url in args diff --git a/tests/transport/aio/test_aiohttp.py b/tests/transport/aio/test_aiohttp.py index 3bfc5c5b2..bf796bc48 100644 --- a/tests/transport/aio/test_aiohttp.py +++ b/tests/transport/aio/test_aiohttp.py @@ -14,6 +14,7 @@ import asyncio import sys + if sys.version_info >= (3, 8): from unittest.mock import Mock, patch, AsyncMock else: diff --git a/tests_async/oauth2/test_credentials_async.py b/tests_async/oauth2/test_credentials_async.py index bfba92d5c..67de9f59a 100644 --- a/tests_async/oauth2/test_credentials_async.py +++ b/tests_async/oauth2/test_credentials_async.py @@ -17,6 +17,7 @@ import os import pickle import sys + if sys.version_info >= (3, 8): from unittest import mock from unittest.mock import AsyncMock @@ -63,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._credentials_async.reauth.refresh_grant", new_callable=AsyncMock) + @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, @@ -125,7 +128,9 @@ async def test_refresh_no_refresh_token(self): request.assert_not_called() - @mock.patch("google.oauth2._credentials_async.reauth.refresh_grant", new_callable=AsyncMock) + @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, @@ -189,7 +194,9 @@ async def test_credentials_with_scopes_requested_refresh_success( # expired.) assert creds.valid - @mock.patch("google.oauth2._credentials_async.reauth.refresh_grant", new_callable=AsyncMock) + @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, @@ -252,7 +259,9 @@ async def test_credentials_with_scopes_returned_refresh_success( # expired.) assert creds.valid - @mock.patch("google.oauth2._credentials_async.reauth.refresh_grant", new_callable=AsyncMock) + @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, @@ -471,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", new_callable=AsyncMock) + @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 3da644c0e..6cb609aac 100644 --- a/tests_async/oauth2/test_id_token.py +++ b/tests_async/oauth2/test_id_token.py @@ -15,6 +15,7 @@ import json import os import sys + if sys.version_info >= (3, 8): from unittest import mock from unittest.mock import AsyncMock @@ -39,9 +40,9 @@ def make_request(status, data=None): if data is not None: response.data = AsyncMock(spec=["__call__", "read"]) - + async def get_content(*args, **kwargs): - return json.dumps(data).encode("utf-8") + 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) diff --git a/tests_async/oauth2/test_reauth_async.py b/tests_async/oauth2/test_reauth_async.py index 9b38f3b84..e91cba92b 100644 --- a/tests_async/oauth2/test_reauth_async.py +++ b/tests_async/oauth2/test_reauth_async.py @@ -14,6 +14,7 @@ import copy import sys + if sys.version_info >= (3, 8): from unittest import mock from unittest.mock import AsyncMock @@ -220,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", new_callable=AsyncMock, 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) @@ -261,7 +264,9 @@ async def test_get_rapt_token(): return_value=("token", None, None, None), ) as mock_refresh_grant: with mock.patch( - "google.oauth2._reauth_async._obtain_rapt", new_callable=AsyncMock, 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( @@ -289,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", new_callable=AsyncMock + "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: @@ -321,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", new_callable=AsyncMock + "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", new_callable=AsyncMock, 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, @@ -349,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", new_callable=AsyncMock + "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 150100a67..de5b1da39 100644 --- a/tests_async/oauth2/test_service_account_async.py +++ b/tests_async/oauth2/test_service_account_async.py @@ -14,6 +14,7 @@ import datetime import sys + if sys.version_info >= (3, 8): from unittest import mock from unittest.mock import AsyncMock diff --git a/tests_async/transport/test_aiohttp_requests.py b/tests_async/transport/test_aiohttp_requests.py index e4f57ec06..b607d0635 100644 --- a/tests_async/transport/test_aiohttp_requests.py +++ b/tests_async/transport/test_aiohttp_requests.py @@ -13,6 +13,7 @@ # limitations under the License. import sys + if sys.version_info >= (3, 8): from unittest import mock from unittest.mock import AsyncMock From d605096014057cea22e3481a653ea7277028876e Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 12:50:35 -0800 Subject: [PATCH 22/22] added no cover blocks --- tests/test__exponential_backoff.py | 2 +- tests/transport/aio/test_aiohttp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test__exponential_backoff.py b/tests/test__exponential_backoff.py index 5c4515a57..3a4eb2004 100644 --- a/tests/test__exponential_backoff.py +++ b/tests/test__exponential_backoff.py @@ -17,7 +17,7 @@ if sys.version_info >= (3, 8): from unittest import mock from unittest.mock import AsyncMock -else: +else: # pragma: NO COVER import mock from mock import AsyncMock diff --git a/tests/transport/aio/test_aiohttp.py b/tests/transport/aio/test_aiohttp.py index bf796bc48..a32175892 100644 --- a/tests/transport/aio/test_aiohttp.py +++ b/tests/transport/aio/test_aiohttp.py @@ -17,7 +17,7 @@ if sys.version_info >= (3, 8): from unittest.mock import Mock, patch, AsyncMock -else: +else: # pragma: NO COVER from unittest.mock import Mock, patch from mock import AsyncMock