userver: en/testsuite/plugins/mocked_time.py Source File
Loading...
Searching...
No Matches
mocked_time.py
1import datetime
2
3import dateutil.parser
4import pytest
5
6from testsuite import utils
7from testsuite.utils import traceback
8
9MOCK_TIME_DISABLED_MESSAGE = (
10 'Mock time is disabled. Use @pytest.mark.now to enable mock '
11 'time for a particular test'
12)
13
14UTC = datetime.timezone.utc
15
16
17class BaseError(Exception):
18 """Base class for errors in this module"""
19
20
22 """Raised when attempting to use a disabled feature"""
23
24
25__tracebackhide__ = traceback.hide(BaseError)
26
27
29 def __init__(self, time: datetime.datetime, *, is_enabled: bool):
30 self._now: datetime.datetime = time
31 self._is_enabled = is_enabled
32
33 def sleep(self, delta: float) -> None:
34 """Increase mock time value
35
36 :param delta: increase value in seconds
37 """
38 if not self._is_enabled:
39 raise DisabledUsageError(MOCK_TIME_DISABLED_MESSAGE)
40 self._now += datetime.timedelta(seconds=delta)
41
42 def now(self, tz: datetime.tzinfo | None = None) -> datetime.datetime:
43 """:returns: current value of mock time"""
44 now: datetime.datetime
45 if self._is_enabled:
46 now = self._now
47 else:
48 now = utils.utcnow()
49
50 if tz:
51 now = now.replace(tzinfo=UTC).astimezone(tz=tz)
52
53 return now
54
55 def set(self, time: datetime.datetime):
56 """Set mock time value"""
57 if not self._is_enabled:
58 raise DisabledUsageError(MOCK_TIME_DISABLED_MESSAGE)
59 self._now = utils.to_utc(time)
60
61 @property
62 def is_enabled(self) -> bool:
63 return self._is_enabled
64
65
66def pytest_addoption(parser):
67 parser.addini(
68 name='mocked-time-enabled',
69 type='bool',
70 default=True,
71 help='Set false to disable mocked time by default',
72 )
73
74
75def pytest_configure(config):
76 config.addinivalue_line(
77 'markers',
78 'now: specify current time mocked value',
79 )
80
81
82def pytest_register_object_hooks():
83 return {
84 '$dateDiff': {'$fixture': '_date_diff_hook'},
85 '$timeDelta': _time_delta_hook,
86 }
87
88
89def pytest_servicetest_modifyitem(session, item):
90 item.add_marker(pytest.mark.now(enabled=False))
91
92
93@pytest.fixture
94def mocked_time(
95 _mocked_time_enabled: bool,
96 now: datetime.datetime,
97) -> MockedTime:
98 """@returns @ref MockedTime
99
100 @ingroup userver_testsuite_fixtures
101 Part of the [yandex-taxi-testsuite](https://github.com/yandex/yandex-taxi-testsuite/blob/develop/testsuite/plugins/mocked_time.py#L94)
102 """
103 return MockedTime(now, is_enabled=_mocked_time_enabled)
104
105
106@pytest.fixture
107def now(request) -> datetime.datetime:
108 marker = request.node.get_closest_marker('now')
109 if not marker or not marker.args:
110 return utils.utcnow()
111 stamp = marker.args[0]
112 if isinstance(stamp, int):
113 return utils.utcfromtimestamp(stamp)
114 return utils.to_utc(dateutil.parser.parse(stamp))
115
116
117@pytest.fixture
118def _mocked_time_enabled(request, pytestconfig) -> bool:
119 now_marker_exists = False
120 for marker in request.node.iter_markers(name='now'):
121 if 'enabled' in marker.kwargs:
122 return marker.kwargs['enabled']
123 now_marker_exists = True
124 if now_marker_exists:
125 return True
126 return pytestconfig.getini('mocked-time-enabled')
127
128
129@pytest.fixture
130def _date_diff_hook(now: datetime.datetime):
131 def wrapper(doc: dict):
132 seconds = float(doc['$dateDiff'])
133 return now + datetime.timedelta(seconds=seconds)
134
135 return wrapper
136
137
138def _time_delta_hook(doc: dict):
139 delta = float(doc['$timeDelta'])
140 return datetime.timedelta(seconds=delta)