12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
14
15
- import time
16
- from unittest .mock import AsyncMock , MagicMock , patch
15
+ from unittest .mock import AsyncMock , MagicMock , patch , PropertyMock
17
16
18
17
import pytest
19
18
23
22
MOCK_GOOGLE_ID_TOKEN = "test_id_token_123"
24
23
MOCK_PROJECT_ID = "test-project"
25
24
# A realistic expiry timestamp (e.g., 1 hour from now)
26
- MOCK_EXPIRY_TIMESTAMP = time .time () + 3600
25
+ MOCK_EXPIRY_DATETIME = auth_methods .datetime .now (auth_methods .timezone .utc ) + auth_methods .timedelta (hours = 1 )
26
+
27
27
28
28
# Expected exception messages from auth_methods.py
29
29
FETCH_TOKEN_FAILURE_MSG = "Failed to fetch Google ID token."
@@ -48,21 +48,19 @@ class TestAsyncAuthMethods:
48
48
"""Tests for asynchronous Google ID token fetching."""
49
49
50
50
@pytest .mark .asyncio
51
- @patch ("toolbox_core.auth_methods._decode_jwt_and_get_expiry" )
52
51
@patch ("toolbox_core.auth_methods._aiohttp_requests.Request" )
53
52
@patch ("toolbox_core.auth_methods.default_async" , new_callable = MagicMock )
54
53
async def test_aget_google_id_token_success_first_call (
55
- self , mock_default_async , mock_async_req_class , mock_decode_expiry
54
+ self , mock_default_async , mock_async_req_class
56
55
):
57
56
"""Tests successful fetching of an async token on the first call."""
58
57
mock_creds_instance = AsyncMock ()
59
58
mock_creds_instance .id_token = MOCK_GOOGLE_ID_TOKEN
59
+ type(mock_creds_instance ).expiry = PropertyMock (return_value = MOCK_EXPIRY_DATETIME )
60
60
mock_default_async .return_value = (mock_creds_instance , MOCK_PROJECT_ID )
61
- mock_decode_expiry .return_value = MOCK_EXPIRY_TIMESTAMP
62
61
63
62
mock_async_req_instance = MagicMock ()
64
63
mock_async_req_class .return_value = mock_async_req_instance
65
-
66
64
token = await auth_methods .aget_google_id_token ()
67
65
68
66
mock_default_async .assert_called_once_with ()
@@ -76,49 +74,46 @@ async def test_aget_google_id_token_success_first_call(
76
74
assert token == f"{ auth_methods .BEARER_TOKEN_PREFIX } { MOCK_GOOGLE_ID_TOKEN } "
77
75
assert auth_methods ._cached_google_id_token ["token" ] == MOCK_GOOGLE_ID_TOKEN
78
76
assert (
79
- auth_methods ._cached_google_id_token ["expires_at" ] == MOCK_EXPIRY_TIMESTAMP
77
+ auth_methods ._cached_google_id_token ["expires_at" ] == MOCK_EXPIRY_DATETIME
80
78
)
81
- mock_decode_expiry .assert_called_once_with (MOCK_GOOGLE_ID_TOKEN )
82
79
83
80
@pytest .mark .asyncio
84
- @patch ("toolbox_core.auth_methods._decode_jwt_and_get_expiry" )
85
81
@patch ("toolbox_core.auth_methods._aiohttp_requests.Request" )
86
82
@patch ("toolbox_core.auth_methods.default_async" , new_callable = MagicMock )
87
83
async def test_aget_google_id_token_success_uses_cache (
88
- self , mock_default_async , mock_async_req_class , mock_decode_expiry
84
+ self , mock_default_async , mock_async_req_class
89
85
):
90
86
"""Tests that subsequent calls use the cached token if valid."""
91
87
auth_methods ._cached_google_id_token ["token" ] = MOCK_GOOGLE_ID_TOKEN
92
88
auth_methods ._cached_google_id_token ["expires_at" ] = (
93
- time .time () + auth_methods .CACHE_REFRESH_MARGIN_SECONDS + 100
89
+ auth_methods .datetime .now (auth_methods .timezone .utc ) +
90
+ auth_methods .timedelta (seconds = auth_methods .CACHE_REFRESH_MARGIN_SECONDS + 100 )
94
91
) # Ensure it's valid
95
92
96
93
token = await auth_methods .aget_google_id_token ()
97
94
98
95
mock_default_async .assert_not_called ()
99
96
mock_async_req_class .assert_not_called ()
100
- mock_decode_expiry .assert_not_called ()
101
97
102
98
assert token == f"{ auth_methods .BEARER_TOKEN_PREFIX } { MOCK_GOOGLE_ID_TOKEN } "
103
99
assert auth_methods ._cached_google_id_token ["token" ] == MOCK_GOOGLE_ID_TOKEN
104
100
105
101
@pytest .mark .asyncio
106
- @patch ("toolbox_core.auth_methods._decode_jwt_and_get_expiry" )
107
102
@patch ("toolbox_core.auth_methods._aiohttp_requests.Request" )
108
103
@patch ("toolbox_core.auth_methods.default_async" , new_callable = MagicMock )
109
104
async def test_aget_google_id_token_refreshes_expired_cache (
110
- self , mock_default_async , mock_async_req_class , mock_decode_expiry
105
+ self , mock_default_async , mock_async_req_class
111
106
):
112
107
"""Tests that an expired cached token triggers a refresh."""
113
108
auth_methods ._cached_google_id_token ["token" ] = "expired_token"
114
109
auth_methods ._cached_google_id_token ["expires_at" ] = (
115
- time . time ( ) - 100
110
+ auth_methods . datetime . now ( auth_methods . timezone . utc ) - auth_methods . timedelta ( seconds = 100 )
116
111
) # Expired
117
112
118
113
mock_creds_instance = AsyncMock ()
119
114
mock_creds_instance .id_token = MOCK_GOOGLE_ID_TOKEN # New token after refresh
115
+ type(mock_creds_instance ).expiry = PropertyMock (return_value = MOCK_EXPIRY_DATETIME )
120
116
mock_default_async .return_value = (mock_creds_instance , MOCK_PROJECT_ID )
121
- mock_decode_expiry .return_value = MOCK_EXPIRY_TIMESTAMP
122
117
123
118
mock_async_req_instance = MagicMock ()
124
119
mock_async_req_class .return_value = mock_async_req_instance
@@ -130,10 +125,8 @@ async def test_aget_google_id_token_refreshes_expired_cache(
130
125
mock_creds_instance .refresh .assert_called_once_with (mock_async_req_instance )
131
126
assert token == f"{ auth_methods .BEARER_TOKEN_PREFIX } { MOCK_GOOGLE_ID_TOKEN } "
132
127
assert auth_methods ._cached_google_id_token ["token" ] == MOCK_GOOGLE_ID_TOKEN
133
- assert (
134
- auth_methods ._cached_google_id_token ["expires_at" ] == MOCK_EXPIRY_TIMESTAMP
135
- )
136
- mock_decode_expiry .assert_called_once_with (MOCK_GOOGLE_ID_TOKEN )
128
+ assert auth_methods ._cached_google_id_token ["expires_at" ] == MOCK_EXPIRY_DATETIME
129
+
137
130
138
131
@pytest .mark .asyncio
139
132
@patch ("toolbox_core.auth_methods._aiohttp_requests.Request" )
@@ -144,6 +137,7 @@ async def test_aget_google_id_token_fetch_failure(
144
137
"""Tests error handling when fetching the token fails (no id_token returned)."""
145
138
mock_creds_instance = AsyncMock ()
146
139
mock_creds_instance .id_token = None # Simulate no ID token after refresh
140
+ type(mock_creds_instance ).expiry = PropertyMock (return_value = MOCK_EXPIRY_DATETIME ) # Still need expiry for update_cache
147
141
mock_default_async .return_value = (mock_creds_instance , MOCK_PROJECT_ID )
148
142
mock_async_req_class .return_value = MagicMock ()
149
143
@@ -176,20 +170,19 @@ async def test_aget_google_id_token_refresh_raises_exception(
176
170
mock_creds_instance .refresh .assert_called_once ()
177
171
178
172
@pytest .mark .asyncio
179
- @patch ("toolbox_core.auth_methods._decode_jwt_and_get_expiry" )
180
173
@patch ("toolbox_core.auth_methods._aiohttp_requests.Request" )
181
174
@patch ("toolbox_core.auth_methods.default_async" , new_callable = MagicMock )
182
175
async def test_aget_google_id_token_no_expiry_info (
183
- self , mock_default_async , mock_async_req_class , mock_decode_expiry
176
+ self , mock_default_async , mock_async_req_class
184
177
):
185
178
"""Tests that a token without expiry info is still cached but effectively expired."""
186
179
mock_creds_instance = AsyncMock ()
187
180
mock_creds_instance .id_token = MOCK_GOOGLE_ID_TOKEN
181
+ type(mock_creds_instance ).expiry = PropertyMock (return_value = None ) # Simulate no expiry info
188
182
mock_default_async .return_value = (mock_creds_instance , MOCK_PROJECT_ID )
189
- mock_decode_expiry .return_value = None # Simulate no expiry info
190
183
191
184
mock_async_req_class .return_value = MagicMock ()
192
-
185
+
193
186
token = await auth_methods .aget_google_id_token ()
194
187
195
188
assert token == f"{ auth_methods .BEARER_TOKEN_PREFIX } { MOCK_GOOGLE_ID_TOKEN } "
@@ -198,13 +191,11 @@ async def test_aget_google_id_token_no_expiry_info(
198
191
auth_methods ._cached_google_id_token ["expires_at" ] == 0
199
192
) # Should be 0 if no expiry
200
193
mock_async_req_class .assert_called_once_with ()
201
- mock_decode_expiry .assert_called_once_with (MOCK_GOOGLE_ID_TOKEN )
202
194
203
195
204
196
class TestSyncAuthMethods :
205
197
"""Tests for synchronous Google ID token fetching."""
206
198
207
- @patch ("toolbox_core.auth_methods._decode_jwt_and_get_expiry" )
208
199
@patch ("toolbox_core.auth_methods.Request" )
209
200
@patch ("toolbox_core.auth_methods.AuthorizedSession" )
210
201
@patch ("toolbox_core.auth_methods.google.auth.default" )
@@ -213,13 +204,12 @@ def test_get_google_id_token_success_first_call(
213
204
mock_sync_default ,
214
205
mock_auth_session_class ,
215
206
mock_sync_req_class ,
216
- mock_decode_expiry ,
217
207
):
218
208
"""Tests successful fetching of a sync token on the first call."""
219
209
mock_creds_instance = MagicMock ()
220
210
mock_creds_instance .id_token = MOCK_GOOGLE_ID_TOKEN
211
+ type(mock_creds_instance ).expiry = PropertyMock (return_value = MOCK_EXPIRY_DATETIME )
221
212
mock_sync_default .return_value = (mock_creds_instance , MOCK_PROJECT_ID )
222
- mock_decode_expiry .return_value = MOCK_EXPIRY_TIMESTAMP
223
213
224
214
mock_session_instance = MagicMock ()
225
215
mock_auth_session_class .return_value = mock_session_instance
@@ -237,11 +227,9 @@ def test_get_google_id_token_success_first_call(
237
227
assert token == f"{ auth_methods .BEARER_TOKEN_PREFIX } { MOCK_GOOGLE_ID_TOKEN } "
238
228
assert auth_methods ._cached_google_id_token ["token" ] == MOCK_GOOGLE_ID_TOKEN
239
229
assert (
240
- auth_methods ._cached_google_id_token ["expires_at" ] == MOCK_EXPIRY_TIMESTAMP
230
+ auth_methods ._cached_google_id_token ["expires_at" ] == MOCK_EXPIRY_DATETIME
241
231
)
242
- mock_decode_expiry .assert_called_once_with (MOCK_GOOGLE_ID_TOKEN )
243
232
244
- @patch ("toolbox_core.auth_methods._decode_jwt_and_get_expiry" )
245
233
@patch ("toolbox_core.auth_methods.Request" )
246
234
@patch ("toolbox_core.auth_methods.AuthorizedSession" )
247
235
@patch ("toolbox_core.auth_methods.google.auth.default" )
@@ -250,25 +238,23 @@ def test_get_google_id_token_success_uses_cache(
250
238
mock_sync_default ,
251
239
mock_auth_session_class ,
252
240
mock_sync_req_class ,
253
- mock_decode_expiry ,
254
241
):
255
242
"""Tests that subsequent calls use the cached token if valid."""
256
243
auth_methods ._cached_google_id_token ["token" ] = MOCK_GOOGLE_ID_TOKEN
257
244
auth_methods ._cached_google_id_token ["expires_at" ] = (
258
- time .time () + auth_methods .CACHE_REFRESH_MARGIN_SECONDS + 100
245
+ auth_methods .datetime .now (auth_methods .timezone .utc ) +
246
+ auth_methods .timedelta (seconds = auth_methods .CACHE_REFRESH_MARGIN_SECONDS + 100 )
259
247
) # Ensure it's valid
260
248
261
249
token = auth_methods .get_google_id_token ()
262
250
263
251
mock_sync_default .assert_not_called ()
264
252
mock_auth_session_class .assert_not_called ()
265
253
mock_sync_req_class .assert_not_called ()
266
- mock_decode_expiry .assert_not_called ()
267
254
268
255
assert token == f"{ auth_methods .BEARER_TOKEN_PREFIX } { MOCK_GOOGLE_ID_TOKEN } "
269
256
assert auth_methods ._cached_google_id_token ["token" ] == MOCK_GOOGLE_ID_TOKEN
270
257
271
- @patch ("toolbox_core.auth_methods._decode_jwt_and_get_expiry" )
272
258
@patch ("toolbox_core.auth_methods.Request" )
273
259
@patch ("toolbox_core.auth_methods.AuthorizedSession" )
274
260
@patch ("toolbox_core.auth_methods.google.auth.default" )
@@ -277,19 +263,18 @@ def test_get_google_id_token_refreshes_expired_cache(
277
263
mock_sync_default ,
278
264
mock_auth_session_class ,
279
265
mock_sync_req_class ,
280
- mock_decode_expiry ,
281
266
):
282
267
"""Tests that an expired cached token triggers a refresh."""
283
268
# Prime the cache with an expired token
284
269
auth_methods ._cached_google_id_token ["token" ] = "expired_token_sync"
285
270
auth_methods ._cached_google_id_token ["expires_at" ] = (
286
- time . time ( ) - 100
271
+ auth_methods . datetime . now ( auth_methods . timezone . utc ) - auth_methods . timedelta ( seconds = 100 )
287
272
) # Expired
288
273
289
274
mock_creds_instance = MagicMock ()
290
275
mock_creds_instance .id_token = MOCK_GOOGLE_ID_TOKEN # New token after refresh
276
+ type(mock_creds_instance ).expiry = PropertyMock (return_value = MOCK_EXPIRY_DATETIME )
291
277
mock_sync_default .return_value = (mock_creds_instance , MOCK_PROJECT_ID )
292
- mock_decode_expiry .return_value = MOCK_EXPIRY_TIMESTAMP
293
278
294
279
mock_session_instance = MagicMock ()
295
280
mock_auth_session_class .return_value = mock_session_instance
@@ -305,10 +290,7 @@ def test_get_google_id_token_refreshes_expired_cache(
305
290
mock_creds_instance .refresh .assert_called_once_with (mock_sync_request_instance )
306
291
assert token == f"{ auth_methods .BEARER_TOKEN_PREFIX } { MOCK_GOOGLE_ID_TOKEN } "
307
292
assert auth_methods ._cached_google_id_token ["token" ] == MOCK_GOOGLE_ID_TOKEN
308
- assert (
309
- auth_methods ._cached_google_id_token ["expires_at" ] == MOCK_EXPIRY_TIMESTAMP
310
- )
311
- mock_decode_expiry .assert_called_once_with (MOCK_GOOGLE_ID_TOKEN )
293
+ assert auth_methods ._cached_google_id_token ["expires_at" ] == MOCK_EXPIRY_DATETIME
312
294
313
295
@patch ("toolbox_core.auth_methods.Request" )
314
296
@patch ("toolbox_core.auth_methods.AuthorizedSession" )
@@ -319,6 +301,7 @@ def test_get_google_id_token_fetch_failure(
319
301
"""Tests error handling when fetching the token fails (no id_token returned)."""
320
302
mock_creds_instance = MagicMock ()
321
303
mock_creds_instance .id_token = None # Simulate no ID token after refresh
304
+ type(mock_creds_instance ).expiry = PropertyMock (return_value = MOCK_EXPIRY_DATETIME ) # Still need expiry for update_cache
322
305
mock_sync_default .return_value = (mock_creds_instance , MOCK_PROJECT_ID )
323
306
324
307
mock_session_instance = MagicMock ()
@@ -362,7 +345,6 @@ def test_get_google_id_token_refresh_raises_exception(
362
345
mock_sync_req_class .assert_called_once_with (mock_session_instance )
363
346
mock_creds_instance .refresh .assert_called_once ()
364
347
365
- @patch ("toolbox_core.auth_methods._decode_jwt_and_get_expiry" )
366
348
@patch ("toolbox_core.auth_methods.Request" )
367
349
@patch ("toolbox_core.auth_methods.AuthorizedSession" )
368
350
@patch ("toolbox_core.auth_methods.google.auth.default" )
@@ -371,13 +353,12 @@ def test_get_google_id_token_no_expiry_info(
371
353
mock_sync_default ,
372
354
mock_auth_session_class ,
373
355
mock_sync_req_class ,
374
- mock_decode_expiry ,
375
356
):
376
357
"""Tests that a token without expiry info is still cached but effectively expired."""
377
358
mock_creds_instance = MagicMock ()
378
359
mock_creds_instance .id_token = MOCK_GOOGLE_ID_TOKEN
360
+ type(mock_creds_instance ).expiry = PropertyMock (return_value = None ) # Simulate no expiry info
379
361
mock_sync_default .return_value = (mock_creds_instance , MOCK_PROJECT_ID )
380
- mock_decode_expiry .return_value = None # Simulate no expiry info
381
362
382
363
mock_session_instance = MagicMock ()
383
364
mock_auth_session_class .return_value = mock_session_instance
@@ -395,4 +376,3 @@ def test_get_google_id_token_no_expiry_info(
395
376
mock_sync_default .assert_called_once_with ()
396
377
mock_auth_session_class .assert_called_once_with (mock_creds_instance )
397
378
mock_sync_req_class .assert_called_once_with (mock_session_instance )
398
- mock_decode_expiry .assert_called_once_with (MOCK_GOOGLE_ID_TOKEN )
0 commit comments