Skip to content

Commit 1d053dc

Browse files
Fix(tests): Use mock time for consistent token generation and verification tests
Patches time.time and google.auth.jwt._helpers.utcnow to use a fixed timestamp (MOCK_CURRENT_TIME) throughout tests/test_token_gen.py. This addresses test flakiness and inconsistencies by ensuring that: 1. Tokens and cookies are generated with predictable `iat` and `exp` claims based on MOCK_CURRENT_TIME. 2. The verification logic within the Firebase Admin SDK and the underlying google-auth library also uses MOCK_CURRENT_TIME. Helper functions _get_id_token and _get_session_cookie were updated to default to using MOCK_CURRENT_TIME for their internal time calculations, simplifying test code. Relevant fixtures and token definitions were updated to rely on these new defaults and the fixed timestamp. The setup_method in TestVerifyIdToken, TestVerifySessionCookie, TestCertificateCaching, and TestCertificateFetchTimeout now mock time.time and google.auth.jwt._helpers.utcnow to ensure that all time-sensitive operations during testing use the MOCK_CURRENT_TIME.
1 parent 70013c8 commit 1d053dc

File tree

1 file changed

+62
-25
lines changed

1 file changed

+62
-25
lines changed

tests/test_token_gen.py

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
import json
2020
import os
2121
import time
22+
import unittest.mock
23+
import datetime
2224

2325
from google.auth import crypt
2426
from google.auth import jwt
2527
import google.auth.exceptions
2628
import google.oauth2.id_token
29+
from google.auth.jwt import _helpers as jwt_helpers
2730
import pytest
2831
from pytest_localserver import plugin
2932

@@ -36,6 +39,7 @@
3639
from tests import testutils
3740

3841

42+
MOCK_CURRENT_TIME = 1500000000
3943
MOCK_UID = 'user1'
4044
MOCK_CREDENTIAL = credentials.Certificate(
4145
testutils.resource_filename('service_account.json'))
@@ -105,16 +109,17 @@ def verify_custom_token(custom_token, expected_claims, tenant_id=None):
105109
for key, value in expected_claims.items():
106110
assert value == token['claims'][key]
107111

108-
def _get_id_token(payload_overrides=None, header_overrides=None):
112+
def _get_id_token(payload_overrides=None, header_overrides=None, current_time=MOCK_CURRENT_TIME):
109113
signer = crypt.RSASigner.from_string(MOCK_PRIVATE_KEY)
110114
headers = {
111115
'kid': 'mock-key-id-1'
112116
}
117+
now = int(current_time if current_time is not None else time.time())
113118
payload = {
114119
'aud': MOCK_CREDENTIAL.project_id,
115120
'iss': 'https://securetoken.google.com/' + MOCK_CREDENTIAL.project_id,
116-
'iat': int(time.time()) - 100,
117-
'exp': int(time.time()) + 3600,
121+
'iat': now - 100,
122+
'exp': now + 3600,
118123
'sub': '1234567890',
119124
'admin': True,
120125
'firebase': {
@@ -127,12 +132,12 @@ def _get_id_token(payload_overrides=None, header_overrides=None):
127132
payload = _merge_jwt_claims(payload, payload_overrides)
128133
return jwt.encode(signer, payload, header=headers)
129134

130-
def _get_session_cookie(payload_overrides=None, header_overrides=None):
135+
def _get_session_cookie(payload_overrides=None, header_overrides=None, current_time=MOCK_CURRENT_TIME):
131136
payload_overrides = payload_overrides or {}
132137
if 'iss' not in payload_overrides:
133138
payload_overrides['iss'] = 'https://session.firebase.google.com/{0}'.format(
134139
MOCK_CREDENTIAL.project_id)
135-
return _get_id_token(payload_overrides, header_overrides)
140+
return _get_id_token(payload_overrides, header_overrides, current_time=current_time)
136141

137142
def _instrument_user_manager(app, status, payload):
138143
client = auth._get_client(app)
@@ -205,7 +210,7 @@ def env_var_app(request):
205210
@pytest.fixture(scope='module')
206211
def revoked_tokens():
207212
mock_user = json.loads(testutils.resource('get_user.json'))
208-
mock_user['users'][0]['validSince'] = str(int(time.time())+100)
213+
mock_user['users'][0]['validSince'] = str(MOCK_CURRENT_TIME + 100)
209214
return json.dumps(mock_user)
210215

211216
@pytest.fixture(scope='module')
@@ -218,7 +223,7 @@ def user_disabled():
218223
def user_disabled_and_revoked():
219224
mock_user = json.loads(testutils.resource('get_user.json'))
220225
mock_user['users'][0]['disabled'] = True
221-
mock_user['users'][0]['validSince'] = str(int(time.time())+100)
226+
mock_user['users'][0]['validSince'] = str(MOCK_CURRENT_TIME + 100)
222227
return json.dumps(mock_user)
223228

224229

@@ -420,6 +425,17 @@ def test_unexpected_response(self, user_mgt_app):
420425

421426
class TestVerifyIdToken:
422427

428+
def setup_method(self, method):
429+
self.time_patch = unittest.mock.patch('time.time', return_value=MOCK_CURRENT_TIME)
430+
self.time_patch.start()
431+
self.utcnow_patch = unittest.mock.patch.object(
432+
jwt_helpers, 'utcnow', return_value=datetime.datetime.utcfromtimestamp(MOCK_CURRENT_TIME))
433+
self.utcnow_patch.start()
434+
435+
def teardown_method(self, method):
436+
self.time_patch.stop()
437+
self.utcnow_patch.stop()
438+
423439
valid_tokens = {
424440
'BinaryToken': TEST_ID_TOKEN,
425441
'TextToken': TEST_ID_TOKEN.decode('utf-8'),
@@ -435,15 +451,9 @@ class TestVerifyIdToken:
435451
'EmptySubject': _get_id_token({'sub': ''}),
436452
'IntSubject': _get_id_token({'sub': 10}),
437453
'LongStrSubject': _get_id_token({'sub': 'a' * 129}),
438-
'FutureToken': _get_id_token({'iat': int(time.time()) + 1000}),
439-
'ExpiredToken': _get_id_token({
440-
'iat': int(time.time()) - 10000,
441-
'exp': int(time.time()) - 3600
442-
}),
443-
'ExpiredTokenShort': _get_id_token({
444-
'iat': int(time.time()) - 10000,
445-
'exp': int(time.time()) - 30
446-
}),
454+
'FutureToken': _get_id_token({'iat': MOCK_CURRENT_TIME + 1000}),
455+
'ExpiredToken': _get_id_token({'iat': MOCK_CURRENT_TIME - 10000, 'exp': MOCK_CURRENT_TIME - 3600}),
456+
'ExpiredTokenShort': _get_id_token({'iat': MOCK_CURRENT_TIME - 10000, 'exp': MOCK_CURRENT_TIME - 30}),
447457
'BadFormatToken': 'foobar'
448458
}
449459

@@ -618,6 +628,17 @@ def test_certificate_request_failure(self, user_mgt_app):
618628

619629
class TestVerifySessionCookie:
620630

631+
def setup_method(self, method):
632+
self.time_patch = unittest.mock.patch('time.time', return_value=MOCK_CURRENT_TIME)
633+
self.time_patch.start()
634+
self.utcnow_patch = unittest.mock.patch.object(
635+
jwt_helpers, 'utcnow', return_value=datetime.datetime.utcfromtimestamp(MOCK_CURRENT_TIME))
636+
self.utcnow_patch.start()
637+
638+
def teardown_method(self, method):
639+
self.time_patch.stop()
640+
self.utcnow_patch.stop()
641+
621642
valid_cookies = {
622643
'BinaryCookie': TEST_SESSION_COOKIE,
623644
'TextCookie': TEST_SESSION_COOKIE.decode('utf-8'),
@@ -633,15 +654,9 @@ class TestVerifySessionCookie:
633654
'EmptySubject': _get_session_cookie({'sub': ''}),
634655
'IntSubject': _get_session_cookie({'sub': 10}),
635656
'LongStrSubject': _get_session_cookie({'sub': 'a' * 129}),
636-
'FutureCookie': _get_session_cookie({'iat': int(time.time()) + 1000}),
637-
'ExpiredCookie': _get_session_cookie({
638-
'iat': int(time.time()) - 10000,
639-
'exp': int(time.time()) - 3600
640-
}),
641-
'ExpiredCookieShort': _get_session_cookie({
642-
'iat': int(time.time()) - 10000,
643-
'exp': int(time.time()) - 30
644-
}),
657+
'FutureCookie': _get_session_cookie({'iat': MOCK_CURRENT_TIME + 1000}),
658+
'ExpiredCookie': _get_session_cookie({'iat': MOCK_CURRENT_TIME - 10000, 'exp': MOCK_CURRENT_TIME - 3600}),
659+
'ExpiredCookieShort': _get_session_cookie({'iat': MOCK_CURRENT_TIME - 10000, 'exp': MOCK_CURRENT_TIME - 30}),
645660
'BadFormatCookie': 'foobar',
646661
'IDToken': TEST_ID_TOKEN,
647662
}
@@ -792,6 +807,17 @@ def test_certificate_request_failure(self, user_mgt_app):
792807

793808
class TestCertificateCaching:
794809

810+
def setup_method(self, method):
811+
self.time_patch = unittest.mock.patch('time.time', return_value=MOCK_CURRENT_TIME)
812+
self.time_patch.start()
813+
self.utcnow_patch = unittest.mock.patch.object(
814+
jwt_helpers, 'utcnow', return_value=datetime.datetime.utcfromtimestamp(MOCK_CURRENT_TIME))
815+
self.utcnow_patch.start()
816+
817+
def teardown_method(self, method):
818+
self.time_patch.stop()
819+
self.utcnow_patch.stop()
820+
795821
def test_certificate_caching(self, user_mgt_app, httpserver):
796822
httpserver.serve_content(MOCK_PUBLIC_CERTS, 200, headers={'Cache-Control': 'max-age=3600'})
797823
verifier = _token_gen.TokenVerifier(user_mgt_app)
@@ -810,6 +836,17 @@ def test_certificate_caching(self, user_mgt_app, httpserver):
810836

811837
class TestCertificateFetchTimeout:
812838

839+
def setup_method(self, method):
840+
self.time_patch = unittest.mock.patch('time.time', return_value=MOCK_CURRENT_TIME)
841+
self.time_patch.start()
842+
self.utcnow_patch = unittest.mock.patch.object(
843+
jwt_helpers, 'utcnow', return_value=datetime.datetime.utcfromtimestamp(MOCK_CURRENT_TIME))
844+
self.utcnow_patch.start()
845+
846+
def teardown_method(self, method):
847+
self.time_patch.stop()
848+
self.utcnow_patch.stop()
849+
813850
timeout_configs = [
814851
({'httpTimeout': 4}, 4),
815852
({'httpTimeout': None}, None),

0 commit comments

Comments
 (0)