googleapis / python-firestore

Apache License 2.0
212 stars 70 forks source link

JWT Token issue when datetimes are mocked (used to work earlier) #924

Open amks1 opened 3 weeks ago

amks1 commented 3 weeks ago

I have 2 Firebase projects: one production and the other for development. I use the development one for my unit tests, which has been working perfectly until recently. However now I'm facing issues at the places in my unit tests where I mock the date and time.

Code

# patch.py
import datetime, time
from unittest.mock import patch

class MockDateTime(datetime.datetime):
    """
    Inherits from datetime.datetime class and modifies the now() and today() methods
    to give the desired date and time.
    """

    @classmethod
    def now(cls, *args, **kwargs):
        actual_datetime = datetime.datetime.fromtimestamp(time.time())
        if isinstance(cls.new_date, datetime.date):
            return datetime.datetime.combine(cls.new_date, actual_datetime.time())
        return cls.new_date

    @classmethod
    def today(cls, *args, **kwargs):
        new_date = cls.now()
        return new_date.date()

class PatchDateTime():

    """
    Use as context manager -
        with PatchDateTime(new_date):
            # code to be executed
    """

    def __init__(self, new_date) -> None:
        self.new_date = new_date

    def __enter__(self):
        MockDateTime_sub = type('MockDateTime_sub', (MockDateTime,),
            {
                'new_date': self.new_date,
            }
        )
        self.patch = patch.object(datetime,'datetime', MockDateTime_sub)
        self.patch.start()

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.patch.stop()
# test.py
from patch import PatchDateTime
import unittest

class Test1(unittest.TestCase):

    def test_1(self):
        new_date = datetime.date.today() + datetime.timedelta(days=20)
        with PatchDateTime(new_date):
            ##### Any firestore code goes here
            pass

if __name__ == '__main__':
    unittest.main()

Stack trace

Running test.py gives the error:

Traceback (most recent call last):
  File "c:\dev\.venv3119\Lib\site-packages\google\api_core\grpc_helpers.py", line 170, in error_remapped_callable
    return _StreamingResponseIterator(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\dev\.venv3119\Lib\site-packages\google\api_core\grpc_helpers.py", line 92, in __init__
    self._stored_first_result = next(self._wrapped)
                                ^^^^^^^^^^^^^^^^^^^
  File "c:\dev\.venv3119\Lib\site-packages\grpc\_channel.py", line 542, in __next__
    return self._next()
           ^^^^^^^^^^^^
  File "c:\dev\.venv3119\Lib\site-packages\grpc\_channel.py", line 968, in _next
    raise self
grpc._channel._MultiThreadedRendezvous: <_MultiThreadedRendezvous of RPC that terminated with:
        status = StatusCode.UNAVAILABLE
        details = "Getting metadata from plugin failed with error: ('invalid_grant: Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.', {'error': 'invalid_grant', 'error_description': 'Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.'})"
        debug_error_string = "UNKNOWN:Error received from peer  {grpc_message:"Getting metadata from plugin failed with error: (\'invalid_grant: Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.\', {\'error\': \'invalid_grant\', \'error_description\': \'Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.\'})", grpc_status:14, created_time:"2024-06-16T11:00:59.5087004+00:00"}"
>

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "c:\dev\.venv3119\Lib\site-packages\google\api_core\retry\retry_unary.py", line 144, in retry_target
    result = target()
             ^^^^^^^^
  File "c:\dev\.venv3119\Lib\site-packages\google\api_core\timeout.py", line 120, in func_with_timeout
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "c:\dev\.venv3119\Lib\site-packages\google\api_core\grpc_helpers.py", line 174, in error_remapped_callable
    raise exceptions.from_grpc_error(exc) from exc
google.api_core.exceptions.ServiceUnavailable: 503 Getting metadata from plugin failed with error: ('invalid_grant: Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.', {'error': 'invalid_grant', 'error_description': 'Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.'})

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "c:\dev\unit_tests2\test_subs\test_1.py", line 146, in test_2
    self.subs_obj.update(new_obj)
  File "C:\dev\project1\models\firestoredb\subs\subs.py", line 1134, in update_subs
    user_objects = models.User.get(id=user_ids)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\dev\project1\models\firestoredb\firestoredb_parent.py", line 387, in get
    for doc in docs:
  File "c:\dev\.venv3119\Lib\site-packages\google\cloud\firestore_v1\query.py", line 351, in stream
    response_iterator, expected_prefix = self._get_stream_iterator(
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\dev\.venv3119\Lib\site-packages\google\cloud\firestore_v1\query.py", line 226, in _get_stream_iterator
    response_iterator = self._client._firestore_api.run_query(
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\dev\.venv3119\Lib\site-packages\google\cloud\firestore_v1\services\firestore\client.py", line 1562, in run_query
    response = rpc(
               ^^^^
  File "c:\dev\.venv3119\Lib\site-packages\google\api_core\gapic_v1\method.py", line 131, in __call__
    return wrapped_func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\dev\.venv3119\Lib\site-packages\google\api_core\retry\retry_unary.py", line 293, in retry_wrapped_func
    return retry_target(
           ^^^^^^^^^^^^^
  File "c:\dev\.venv3119\Lib\site-packages\google\api_core\retry\retry_unary.py", line 153, in retry_target
    _retry_error_helper(
  File "c:\dev\.venv3119\Lib\site-packages\google\api_core\retry\retry_base.py", line 221, in _retry_error_helper
    raise final_exc from source_exc
google.api_core.exceptions.RetryError: Timeout of 300.0s exceeded, last exception: 503 Getting metadata from plugin failed with error: ('invalid_grant: Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.', {'error': 'invalid_grant', 'error_description': 'Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.'})

Environment details