Skip to content

Commit 2d9b18c

Browse files
chore: Use mock time for consistent token generation and verification tests (#881)
* 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. * Fix(tests): Apply time mocking to test_tenant_mgt.py Extends the time mocking strategy (using a fixed MOCK_CURRENT_TIME) to tests in `tests/test_tenant_mgt.py` to ensure consistency with changes previously made in `tests/test_token_gen.py`. Specifically: - Imported `MOCK_CURRENT_TIME` from `tests.test_token_gen`. - Added `setup_method` (and `teardown_method`) to the `TestVerifyIdToken` and `TestCreateCustomToken` classes. - These setup methods patch `time.time` and `google.auth.jwt._helpers.utcnow` to return `MOCK_CURRENT_TIME` (or its datetime equivalent). This ensures that token generation (for custom tokens) and token verification within `test_tenant_mgt.py` align with the mocked timeline, preventing potential flakiness or failures due to time inconsistencies. All tests in `test_tenant_mgt.py` pass with these changes. * fix lint and refactor --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 70013c8 commit 2d9b18c

File tree

2 files changed

+92
-20
lines changed

2 files changed

+92
-20
lines changed

tests/test_tenant_mgt.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""Test cases for the firebase_admin.tenant_mgt module."""
1616

1717
import json
18+
import unittest.mock
1819
from urllib import parse
1920

2021
import pytest
@@ -29,6 +30,7 @@
2930
from firebase_admin import _utils
3031
from tests import testutils
3132
from tests import test_token_gen
33+
from tests.test_token_gen import MOCK_CURRENT_TIME, MOCK_CURRENT_TIME_UTC
3234

3335

3436
GET_TENANT_RESPONSE = """{
@@ -964,6 +966,17 @@ def _assert_saml_provider_config(self, provider_config, want_id='saml.provider')
964966

965967
class TestVerifyIdToken:
966968

969+
def setup_method(self):
970+
self.time_patch = unittest.mock.patch('time.time', return_value=MOCK_CURRENT_TIME)
971+
self.mock_time = self.time_patch.start()
972+
self.utcnow_patch = unittest.mock.patch(
973+
'google.auth.jwt._helpers.utcnow', return_value=MOCK_CURRENT_TIME_UTC)
974+
self.mock_utcnow = self.utcnow_patch.start()
975+
976+
def teardown_method(self):
977+
self.time_patch.stop()
978+
self.utcnow_patch.stop()
979+
967980
def test_valid_token(self, tenant_mgt_app):
968981
client = tenant_mgt.auth_for_tenant('test-tenant', app=tenant_mgt_app)
969982
client._token_verifier.request = test_token_gen.MOCK_REQUEST
@@ -997,6 +1010,17 @@ def tenant_aware_custom_token_app():
9971010

9981011
class TestCreateCustomToken:
9991012

1013+
def setup_method(self):
1014+
self.time_patch = unittest.mock.patch('time.time', return_value=MOCK_CURRENT_TIME)
1015+
self.mock_time = self.time_patch.start()
1016+
self.utcnow_patch = unittest.mock.patch(
1017+
'google.auth.jwt._helpers.utcnow', return_value=MOCK_CURRENT_TIME_UTC)
1018+
self.mock_utcnow = self.utcnow_patch.start()
1019+
1020+
def teardown_method(self):
1021+
self.time_patch.stop()
1022+
self.utcnow_patch.stop()
1023+
10001024
def test_custom_token(self, tenant_aware_custom_token_app):
10011025
client = tenant_mgt.auth_for_tenant('test-tenant', app=tenant_aware_custom_token_app)
10021026

tests/test_token_gen.py

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import json
2020
import os
2121
import time
22+
import unittest.mock
2223

2324
from google.auth import crypt
2425
from google.auth import jwt
@@ -36,6 +37,9 @@
3637
from tests import testutils
3738

3839

40+
MOCK_CURRENT_TIME = 1500000000
41+
MOCK_CURRENT_TIME_UTC = datetime.datetime.fromtimestamp(
42+
MOCK_CURRENT_TIME, tz=datetime.timezone.utc)
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,13 @@ 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(
136+
payload_overrides=None, header_overrides=None, current_time=MOCK_CURRENT_TIME):
131137
payload_overrides = payload_overrides or {}
132138
if 'iss' not in payload_overrides:
133139
payload_overrides['iss'] = 'https://session.firebase.google.com/{0}'.format(
134140
MOCK_CREDENTIAL.project_id)
135-
return _get_id_token(payload_overrides, header_overrides)
141+
return _get_id_token(payload_overrides, header_overrides, current_time=current_time)
136142

137143
def _instrument_user_manager(app, status, payload):
138144
client = auth._get_client(app)
@@ -205,7 +211,7 @@ def env_var_app(request):
205211
@pytest.fixture(scope='module')
206212
def revoked_tokens():
207213
mock_user = json.loads(testutils.resource('get_user.json'))
208-
mock_user['users'][0]['validSince'] = str(int(time.time())+100)
214+
mock_user['users'][0]['validSince'] = str(MOCK_CURRENT_TIME + 100)
209215
return json.dumps(mock_user)
210216

211217
@pytest.fixture(scope='module')
@@ -218,7 +224,7 @@ def user_disabled():
218224
def user_disabled_and_revoked():
219225
mock_user = json.loads(testutils.resource('get_user.json'))
220226
mock_user['users'][0]['disabled'] = True
221-
mock_user['users'][0]['validSince'] = str(int(time.time())+100)
227+
mock_user['users'][0]['validSince'] = str(MOCK_CURRENT_TIME + 100)
222228
return json.dumps(mock_user)
223229

224230

@@ -420,6 +426,17 @@ def test_unexpected_response(self, user_mgt_app):
420426

421427
class TestVerifyIdToken:
422428

429+
def setup_method(self):
430+
self.time_patch = unittest.mock.patch('time.time', return_value=MOCK_CURRENT_TIME)
431+
self.time_patch.start()
432+
self.utcnow_patch = unittest.mock.patch(
433+
'google.auth.jwt._helpers.utcnow', return_value=MOCK_CURRENT_TIME_UTC)
434+
self.utcnow_patch.start()
435+
436+
def teardown_method(self):
437+
self.time_patch.stop()
438+
self.utcnow_patch.stop()
439+
423440
valid_tokens = {
424441
'BinaryToken': TEST_ID_TOKEN,
425442
'TextToken': TEST_ID_TOKEN.decode('utf-8'),
@@ -435,14 +452,14 @@ class TestVerifyIdToken:
435452
'EmptySubject': _get_id_token({'sub': ''}),
436453
'IntSubject': _get_id_token({'sub': 10}),
437454
'LongStrSubject': _get_id_token({'sub': 'a' * 129}),
438-
'FutureToken': _get_id_token({'iat': int(time.time()) + 1000}),
455+
'FutureToken': _get_id_token({'iat': MOCK_CURRENT_TIME + 1000}),
439456
'ExpiredToken': _get_id_token({
440-
'iat': int(time.time()) - 10000,
441-
'exp': int(time.time()) - 3600
457+
'iat': MOCK_CURRENT_TIME - 10000,
458+
'exp': MOCK_CURRENT_TIME - 3600
442459
}),
443460
'ExpiredTokenShort': _get_id_token({
444-
'iat': int(time.time()) - 10000,
445-
'exp': int(time.time()) - 30
461+
'iat': MOCK_CURRENT_TIME - 10000,
462+
'exp': MOCK_CURRENT_TIME - 30
446463
}),
447464
'BadFormatToken': 'foobar'
448465
}
@@ -618,6 +635,17 @@ def test_certificate_request_failure(self, user_mgt_app):
618635

619636
class TestVerifySessionCookie:
620637

638+
def setup_method(self):
639+
self.time_patch = unittest.mock.patch('time.time', return_value=MOCK_CURRENT_TIME)
640+
self.time_patch.start()
641+
self.utcnow_patch = unittest.mock.patch(
642+
'google.auth.jwt._helpers.utcnow', return_value=MOCK_CURRENT_TIME_UTC)
643+
self.utcnow_patch.start()
644+
645+
def teardown_method(self):
646+
self.time_patch.stop()
647+
self.utcnow_patch.stop()
648+
621649
valid_cookies = {
622650
'BinaryCookie': TEST_SESSION_COOKIE,
623651
'TextCookie': TEST_SESSION_COOKIE.decode('utf-8'),
@@ -633,14 +661,14 @@ class TestVerifySessionCookie:
633661
'EmptySubject': _get_session_cookie({'sub': ''}),
634662
'IntSubject': _get_session_cookie({'sub': 10}),
635663
'LongStrSubject': _get_session_cookie({'sub': 'a' * 129}),
636-
'FutureCookie': _get_session_cookie({'iat': int(time.time()) + 1000}),
664+
'FutureCookie': _get_session_cookie({'iat': MOCK_CURRENT_TIME + 1000}),
637665
'ExpiredCookie': _get_session_cookie({
638-
'iat': int(time.time()) - 10000,
639-
'exp': int(time.time()) - 3600
666+
'iat': MOCK_CURRENT_TIME - 10000,
667+
'exp': MOCK_CURRENT_TIME - 3600
640668
}),
641669
'ExpiredCookieShort': _get_session_cookie({
642-
'iat': int(time.time()) - 10000,
643-
'exp': int(time.time()) - 30
670+
'iat': MOCK_CURRENT_TIME - 10000,
671+
'exp': MOCK_CURRENT_TIME - 30
644672
}),
645673
'BadFormatCookie': 'foobar',
646674
'IDToken': TEST_ID_TOKEN,
@@ -792,6 +820,17 @@ def test_certificate_request_failure(self, user_mgt_app):
792820

793821
class TestCertificateCaching:
794822

823+
def setup_method(self):
824+
self.time_patch = unittest.mock.patch('time.time', return_value=MOCK_CURRENT_TIME)
825+
self.time_patch.start()
826+
self.utcnow_patch = unittest.mock.patch(
827+
'google.auth.jwt._helpers.utcnow', return_value=MOCK_CURRENT_TIME_UTC)
828+
self.utcnow_patch.start()
829+
830+
def teardown_method(self):
831+
self.time_patch.stop()
832+
self.utcnow_patch.stop()
833+
795834
def test_certificate_caching(self, user_mgt_app, httpserver):
796835
httpserver.serve_content(MOCK_PUBLIC_CERTS, 200, headers={'Cache-Control': 'max-age=3600'})
797836
verifier = _token_gen.TokenVerifier(user_mgt_app)
@@ -810,6 +849,18 @@ def test_certificate_caching(self, user_mgt_app, httpserver):
810849

811850
class TestCertificateFetchTimeout:
812851

852+
def setup_method(self):
853+
self.time_patch = unittest.mock.patch('time.time', return_value=MOCK_CURRENT_TIME)
854+
self.time_patch.start()
855+
self.utcnow_patch = unittest.mock.patch(
856+
'google.auth.jwt._helpers.utcnow', return_value=MOCK_CURRENT_TIME_UTC)
857+
self.utcnow_patch.start()
858+
859+
def teardown_method(self):
860+
self.time_patch.stop()
861+
self.utcnow_patch.stop()
862+
testutils.cleanup_apps()
863+
813864
timeout_configs = [
814865
({'httpTimeout': 4}, 4),
815866
({'httpTimeout': None}, None),
@@ -852,6 +903,3 @@ def _instrument_session(self, app):
852903
recorder = []
853904
request.session.mount('https://', testutils.MockAdapter(MOCK_PUBLIC_CERTS, 200, recorder))
854905
return recorder
855-
856-
def teardown_method(self):
857-
testutils.cleanup_apps()

0 commit comments

Comments
 (0)