devcycle-python-server-sdk 2.0.0__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- devcycle_python_sdk/VERSION.txt +1 -1
- devcycle_python_sdk/__init__.py +3 -2
- devcycle_python_sdk/api/backoff.py +11 -0
- devcycle_python_sdk/api/bucketing_client.py +17 -25
- devcycle_python_sdk/api/config_client.py +116 -0
- devcycle_python_sdk/api/event_client.py +95 -0
- devcycle_python_sdk/api/local_bucketing.py +436 -0
- devcycle_python_sdk/{dvc_cloud_client.py → cloud_client.py} +69 -41
- devcycle_python_sdk/exceptions.py +25 -3
- devcycle_python_sdk/local_client.py +220 -0
- devcycle_python_sdk/managers/__init__.py +0 -0
- devcycle_python_sdk/managers/config_manager.py +95 -0
- devcycle_python_sdk/managers/event_queue_manager.py +223 -0
- devcycle_python_sdk/models/bucketed_config.py +150 -0
- devcycle_python_sdk/models/event.py +111 -8
- devcycle_python_sdk/models/feature.py +12 -0
- devcycle_python_sdk/models/platform_data.py +33 -0
- devcycle_python_sdk/models/user.py +52 -7
- devcycle_python_sdk/models/variable.py +17 -5
- devcycle_python_sdk/options.py +106 -0
- devcycle_python_sdk/protobuf/__init__.py +0 -0
- devcycle_python_sdk/protobuf/utils.py +129 -0
- devcycle_python_sdk/protobuf/variableForUserParams_pb2.py +46 -0
- devcycle_python_sdk/py.typed +0 -0
- devcycle_python_sdk/util/version.py +1 -1
- {devcycle_python_server_sdk-2.0.0.dist-info → devcycle_python_server_sdk-3.0.0.dist-info}/METADATA +3 -2
- devcycle_python_server_sdk-3.0.0.dist-info/RECORD +55 -0
- example/{example.py → cloud_client_example.py} +24 -20
- example/local_bucketing_client_example.py +74 -0
- test/api/__init__.py +0 -0
- test/{test_bucketing_client.py → api/test_bucketing_client.py} +10 -5
- test/api/test_config_client.py +133 -0
- test/api/test_event_client.py +97 -0
- test/api/test_local_bucketing.py +392 -0
- test/fixture/__init__.py +0 -0
- test/fixture/data.py +57 -0
- test/managers/__init__.py +0 -0
- test/managers/test_config_manager.py +132 -0
- test/managers/test_event_queue_manager.py +271 -0
- test/models/__init__.py +0 -0
- test/models/test_bucketed_config.py +26 -0
- test/{test_dvc_cloud_client.py → test_cloud_client.py} +11 -17
- test/test_local_client.py +397 -0
- test/util/__init__.py +0 -0
- test/util/test_utils.py +183 -0
- test/{test_version.py → util/test_version.py} +2 -2
- devcycle_python_sdk/dvc_options.py +0 -18
- devcycle_python_server_sdk-2.0.0.dist-info/RECORD +0 -26
- {devcycle_python_server_sdk-2.0.0.dist-info → devcycle_python_server_sdk-3.0.0.dist-info}/LICENSE +0 -0
- {devcycle_python_server_sdk-2.0.0.dist-info → devcycle_python_server_sdk-3.0.0.dist-info}/WHEEL +0 -0
- {devcycle_python_server_sdk-2.0.0.dist-info → devcycle_python_server_sdk-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
import uuid
|
|
4
|
+
import unittest
|
|
5
|
+
from unittest.mock import MagicMock, patch
|
|
6
|
+
|
|
7
|
+
from devcycle_python_sdk import DevCycleLocalOptions
|
|
8
|
+
from devcycle_python_sdk.managers.event_queue_manager import (
|
|
9
|
+
EventQueueManager,
|
|
10
|
+
QueueFullError,
|
|
11
|
+
)
|
|
12
|
+
from devcycle_python_sdk.models.event import (
|
|
13
|
+
FlushPayload,
|
|
14
|
+
UserEventsBatchRecord,
|
|
15
|
+
RequestEvent,
|
|
16
|
+
DevCycleEvent,
|
|
17
|
+
EventType,
|
|
18
|
+
)
|
|
19
|
+
from devcycle_python_sdk.models.user import DevCycleUser
|
|
20
|
+
|
|
21
|
+
from devcycle_python_sdk.exceptions import APIClientError, APIClientUnauthorizedError
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class EventQueueManagerTest(unittest.TestCase):
|
|
27
|
+
def setUp(self) -> None:
|
|
28
|
+
self.sdk_key = "dvc_server_" + str(uuid.uuid4())
|
|
29
|
+
|
|
30
|
+
# mock out the local bucketing to test event queue logic directly
|
|
31
|
+
self.test_local_bucketing = MagicMock()
|
|
32
|
+
self.test_local_bucketing.flush_event_queue = MagicMock()
|
|
33
|
+
self.test_local_bucketing.flush_event_queue.return_value = []
|
|
34
|
+
self.test_local_bucketing.get_event_queue_size = MagicMock()
|
|
35
|
+
self.test_local_bucketing.get_event_queue_size.return_value = 0
|
|
36
|
+
self.test_local_bucketing.on_event_payload_success = MagicMock()
|
|
37
|
+
self.test_local_bucketing.on_event_payload_failure = MagicMock()
|
|
38
|
+
|
|
39
|
+
self.test_options = DevCycleLocalOptions(
|
|
40
|
+
disable_automatic_event_logging=False, disable_custom_event_logging=False
|
|
41
|
+
)
|
|
42
|
+
self.test_options_no_thread = DevCycleLocalOptions(
|
|
43
|
+
disable_automatic_event_logging=True, disable_custom_event_logging=True
|
|
44
|
+
)
|
|
45
|
+
self.test_payload = FlushPayload(
|
|
46
|
+
payloadId="123",
|
|
47
|
+
records=[
|
|
48
|
+
UserEventsBatchRecord(
|
|
49
|
+
user=DevCycleUser.from_json(
|
|
50
|
+
{
|
|
51
|
+
"user_id": "999-000-111",
|
|
52
|
+
"createdDate": "1970-01-20T12:50:19.871Z",
|
|
53
|
+
"lastSeenDate": "1970-01-20T12:50:19.871Z",
|
|
54
|
+
"platform": "Python",
|
|
55
|
+
"platformVersion": "3.11.4",
|
|
56
|
+
"sdkType": "local",
|
|
57
|
+
"sdkVersion": "2.0.1",
|
|
58
|
+
"hostname": "Chris-DevCycle-MacBook.local",
|
|
59
|
+
}
|
|
60
|
+
),
|
|
61
|
+
events=[
|
|
62
|
+
RequestEvent.from_json(
|
|
63
|
+
{
|
|
64
|
+
"type": "aggVariableEvaluated",
|
|
65
|
+
"target": "string-var",
|
|
66
|
+
"user_id": "999-000-111",
|
|
67
|
+
"date": "1970-01-20T12:50:19.871Z",
|
|
68
|
+
"clientDate": "1970-01-20T12:50:19.871Z",
|
|
69
|
+
"value": 1.0,
|
|
70
|
+
"featureVars": {},
|
|
71
|
+
"metaData": {
|
|
72
|
+
"_feature": "62fbf6566f1ba302829f9e32",
|
|
73
|
+
"_variation": "62fbf6566f1ba302829f9e39",
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
],
|
|
78
|
+
)
|
|
79
|
+
],
|
|
80
|
+
eventCount=1,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def test_init(self):
|
|
84
|
+
self.test_local_bucketing.flush_event_queue.return_value = []
|
|
85
|
+
self.test_options.event_flush_interval_ms = 100
|
|
86
|
+
manager = EventQueueManager(
|
|
87
|
+
self.sdk_key, self.test_options, self.test_local_bucketing
|
|
88
|
+
)
|
|
89
|
+
self.assertTrue(manager._should_run())
|
|
90
|
+
self.assertTrue(manager.is_alive())
|
|
91
|
+
self.assertTrue(manager.daemon)
|
|
92
|
+
|
|
93
|
+
def test_close(self):
|
|
94
|
+
self.test_local_bucketing.flush_event_queue.return_value = []
|
|
95
|
+
self.test_options.event_flush_interval_ms = 100
|
|
96
|
+
manager = EventQueueManager(
|
|
97
|
+
self.sdk_key, self.test_options, self.test_local_bucketing
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
manager.close()
|
|
101
|
+
# let the thread wake up from sleep and react to the close
|
|
102
|
+
time.sleep(0.5)
|
|
103
|
+
self.assertFalse(manager._should_run())
|
|
104
|
+
self.assertFalse(manager.is_alive())
|
|
105
|
+
|
|
106
|
+
@patch("devcycle_python_sdk.api.event_client.EventAPIClient.publish_events")
|
|
107
|
+
def test_publish_event_payload_retryable_api_error(self, mock_publish_events):
|
|
108
|
+
self.test_local_bucketing.on_event_payload_success = MagicMock()
|
|
109
|
+
self.test_local_bucketing.on_event_payload_failure = MagicMock()
|
|
110
|
+
|
|
111
|
+
mock_publish_events.side_effect = APIClientError("Some retryable error")
|
|
112
|
+
|
|
113
|
+
manager = EventQueueManager(
|
|
114
|
+
self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
|
|
115
|
+
)
|
|
116
|
+
manager._publish_event_payload(self.test_payload)
|
|
117
|
+
|
|
118
|
+
# should try and publish events, get the error and then report the failure to local bucketing
|
|
119
|
+
mock_publish_events.assert_called_once()
|
|
120
|
+
self.test_local_bucketing.on_event_payload_failure.assert_called_with(
|
|
121
|
+
self.test_payload.payloadId, True
|
|
122
|
+
)
|
|
123
|
+
self.test_local_bucketing.on_event_payload_success.assert_not_called()
|
|
124
|
+
|
|
125
|
+
@patch("devcycle_python_sdk.api.event_client.EventAPIClient.publish_events")
|
|
126
|
+
def test_publish_event_payload_non_retryable_api_error(self, mock_publish_events):
|
|
127
|
+
mock_publish_events.side_effect = APIClientUnauthorizedError(
|
|
128
|
+
"Some retryable error"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
manager = EventQueueManager(
|
|
132
|
+
self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
|
|
133
|
+
)
|
|
134
|
+
manager._publish_event_payload(self.test_payload)
|
|
135
|
+
|
|
136
|
+
# should try and publish events, get the error and then report the failure to local bucketing
|
|
137
|
+
mock_publish_events.assert_called_once()
|
|
138
|
+
self.test_local_bucketing.on_event_payload_failure.assert_called_with(
|
|
139
|
+
self.test_payload.payloadId, False
|
|
140
|
+
)
|
|
141
|
+
self.test_local_bucketing.on_event_payload_success.assert_not_called()
|
|
142
|
+
|
|
143
|
+
@patch("devcycle_python_sdk.api.event_client.EventAPIClient.publish_events")
|
|
144
|
+
def test_flush_events_no_events(self, mock_publish_events):
|
|
145
|
+
self.test_local_bucketing.flush_event_queue = MagicMock()
|
|
146
|
+
self.test_local_bucketing.flush_event_queue.return_value = []
|
|
147
|
+
|
|
148
|
+
manager = EventQueueManager(
|
|
149
|
+
self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
|
|
150
|
+
)
|
|
151
|
+
manager._flush_events()
|
|
152
|
+
|
|
153
|
+
self.test_local_bucketing.flush_event_queue.assert_called_once()
|
|
154
|
+
mock_publish_events.assert_not_called()
|
|
155
|
+
|
|
156
|
+
@patch("devcycle_python_sdk.api.event_client.EventAPIClient.publish_events")
|
|
157
|
+
def test_flush_events(self, mock_publish_events):
|
|
158
|
+
self.test_local_bucketing.flush_event_queue.return_value = [self.test_payload]
|
|
159
|
+
|
|
160
|
+
manager = EventQueueManager(
|
|
161
|
+
self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
|
|
162
|
+
)
|
|
163
|
+
result = manager._flush_events()
|
|
164
|
+
|
|
165
|
+
self.assertEqual(result, 1)
|
|
166
|
+
self.test_local_bucketing.flush_event_queue.assert_called_once()
|
|
167
|
+
mock_publish_events.assert_called_once()
|
|
168
|
+
self.test_local_bucketing.on_event_payload_success.assert_called_once()
|
|
169
|
+
|
|
170
|
+
def test_queue_event_bad_data(self):
|
|
171
|
+
manager = EventQueueManager(
|
|
172
|
+
self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
|
|
173
|
+
)
|
|
174
|
+
with self.assertRaises(ValueError):
|
|
175
|
+
manager.queue_event(user=None, event=None)
|
|
176
|
+
|
|
177
|
+
with self.assertRaises(ValueError):
|
|
178
|
+
manager.queue_event(user=DevCycleUser(user_id="test"), event=None)
|
|
179
|
+
|
|
180
|
+
# Events must have a type
|
|
181
|
+
with self.assertRaises(ValueError):
|
|
182
|
+
manager.queue_event(
|
|
183
|
+
user=DevCycleUser(user_id="test"), event=DevCycleEvent()
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
def test_queue_event(self):
|
|
187
|
+
self.test_local_bucketing.queue_event = MagicMock()
|
|
188
|
+
self.test_local_bucketing.get_event_queue_size.return_value = 1
|
|
189
|
+
|
|
190
|
+
manager = EventQueueManager(
|
|
191
|
+
self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
|
|
192
|
+
)
|
|
193
|
+
DevCycleUser(user_id="test_user_id")
|
|
194
|
+
event = DevCycleEvent(
|
|
195
|
+
type=EventType.CustomEvent,
|
|
196
|
+
target="string-var",
|
|
197
|
+
value=1,
|
|
198
|
+
metaData={"test": "test"},
|
|
199
|
+
)
|
|
200
|
+
manager.queue_event(DevCycleUser(user_id="test"), event)
|
|
201
|
+
|
|
202
|
+
self.test_local_bucketing.queue_event.assert_called_once()
|
|
203
|
+
self.assertEqual(self.test_local_bucketing.get_event_queue_size.call_count, 2)
|
|
204
|
+
|
|
205
|
+
def test_check_queue_status(self):
|
|
206
|
+
self.test_options_no_thread.flush_event_queue_size = 5
|
|
207
|
+
self.test_options_no_thread.max_event_queue_size = 100
|
|
208
|
+
|
|
209
|
+
manager = EventQueueManager(
|
|
210
|
+
self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# queue empty, no issues
|
|
214
|
+
self.test_local_bucketing.get_event_queue_size.return_value = 0
|
|
215
|
+
manager._check_queue_status()
|
|
216
|
+
self.test_local_bucketing.flush_event_queue.assert_not_called()
|
|
217
|
+
|
|
218
|
+
# queue needs flush
|
|
219
|
+
self.test_local_bucketing.flush_event_queue.reset_mock()
|
|
220
|
+
self.test_local_bucketing.get_event_queue_size.return_value = 6
|
|
221
|
+
manager._check_queue_status()
|
|
222
|
+
self.test_local_bucketing.flush_event_queue.assert_called_once()
|
|
223
|
+
|
|
224
|
+
# queue full - should flush and then throw error if queue is still full
|
|
225
|
+
self.test_local_bucketing.flush_event_queue.reset_mock()
|
|
226
|
+
|
|
227
|
+
self.test_local_bucketing.get_event_queue_size.return_value = 100
|
|
228
|
+
with self.assertRaises(QueueFullError):
|
|
229
|
+
manager._check_queue_status()
|
|
230
|
+
self.test_local_bucketing.flush_event_queue.assert_called_once()
|
|
231
|
+
|
|
232
|
+
def test_queue_aggregate_event_bad_data(self):
|
|
233
|
+
manager = EventQueueManager(
|
|
234
|
+
self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
|
|
235
|
+
)
|
|
236
|
+
with self.assertRaises(ValueError):
|
|
237
|
+
manager.queue_aggregate_event(event=None, bucketed_config=None)
|
|
238
|
+
|
|
239
|
+
# agg event must have a type
|
|
240
|
+
with self.assertRaises(ValueError):
|
|
241
|
+
manager.queue_aggregate_event(event=DevCycleEvent(), bucketed_config=None)
|
|
242
|
+
|
|
243
|
+
# agg event must have a target
|
|
244
|
+
with self.assertRaises(ValueError):
|
|
245
|
+
manager.queue_aggregate_event(
|
|
246
|
+
event=DevCycleEvent(type=EventType.AggVariableDefaulted),
|
|
247
|
+
bucketed_config=None,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def test_queue_aggregate_event(self):
|
|
251
|
+
self.test_local_bucketing.queue_aggregate_event = MagicMock()
|
|
252
|
+
self.test_local_bucketing.get_event_queue_size.return_value = 1
|
|
253
|
+
|
|
254
|
+
manager = EventQueueManager(
|
|
255
|
+
self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
|
|
256
|
+
)
|
|
257
|
+
event = DevCycleEvent(
|
|
258
|
+
type=EventType.AggVariableDefaulted,
|
|
259
|
+
target="string-var",
|
|
260
|
+
value=1,
|
|
261
|
+
metaData={"test": "test"},
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
manager.queue_aggregate_event(event, None)
|
|
265
|
+
|
|
266
|
+
self.test_local_bucketing.queue_aggregate_event.assert_called_once()
|
|
267
|
+
self.assertEqual(self.test_local_bucketing.get_event_queue_size.call_count, 2)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
if __name__ == "__main__":
|
|
271
|
+
unittest.main()
|
test/models/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from devcycle_python_sdk.models.bucketed_config import BucketedConfig
|
|
6
|
+
from test.fixture.data import bucketed_config, bucketed_config_minimal
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BucketedConfigTest(unittest.TestCase):
|
|
12
|
+
def test_bucketed_config_all_fields(self) -> None:
|
|
13
|
+
bucketed_config_parsed = json.loads(bucketed_config())
|
|
14
|
+
result = BucketedConfig.from_json(bucketed_config_parsed)
|
|
15
|
+
|
|
16
|
+
# Fields are already checked in the local bucketing tests, no need to repeat here
|
|
17
|
+
self.assertIsNotNone(result)
|
|
18
|
+
|
|
19
|
+
def test_bucketed_config_minimal(self) -> None:
|
|
20
|
+
bucketed_config_parsed = json.loads(bucketed_config_minimal())
|
|
21
|
+
result = BucketedConfig.from_json(bucketed_config_parsed)
|
|
22
|
+
self.assertIsNotNone(result)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
unittest.main()
|
|
@@ -6,9 +6,9 @@ from time import time
|
|
|
6
6
|
from unittest.mock import patch
|
|
7
7
|
|
|
8
8
|
from devcycle_python_sdk import DevCycleCloudClient, DevCycleCloudOptions
|
|
9
|
-
from devcycle_python_sdk.models.user import
|
|
9
|
+
from devcycle_python_sdk.models.user import DevCycleUser
|
|
10
10
|
from devcycle_python_sdk.models.variable import Variable, TypeEnum
|
|
11
|
-
from devcycle_python_sdk.models.event import
|
|
11
|
+
from devcycle_python_sdk.models.event import DevCycleEvent
|
|
12
12
|
from devcycle_python_sdk.exceptions import (
|
|
13
13
|
CloudClientUnauthorizedError,
|
|
14
14
|
NotFoundError,
|
|
@@ -17,14 +17,14 @@ from devcycle_python_sdk.exceptions import (
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class
|
|
20
|
+
class DevCycleCloudClientTest(unittest.TestCase):
|
|
21
21
|
def setUp(self) -> None:
|
|
22
22
|
sdk_key = "dvc_server_" + str(uuid.uuid4())
|
|
23
23
|
options = DevCycleCloudOptions()
|
|
24
24
|
self.test_client = DevCycleCloudClient(sdk_key, options)
|
|
25
|
-
self.test_user =
|
|
26
|
-
self.test_user_no_id =
|
|
27
|
-
self.test_user_empty_id =
|
|
25
|
+
self.test_user = DevCycleUser(user_id="test_user_id")
|
|
26
|
+
self.test_user_no_id = DevCycleUser(user_id=None) # type: ignore
|
|
27
|
+
self.test_user_empty_id = DevCycleUser(user_id="")
|
|
28
28
|
|
|
29
29
|
def tearDown(self) -> None:
|
|
30
30
|
pass
|
|
@@ -55,8 +55,6 @@ class DVCCloudClientTest(unittest.TestCase):
|
|
|
55
55
|
option_with_data = DevCycleCloudOptions(
|
|
56
56
|
enable_edge_db=False,
|
|
57
57
|
bucketing_api_uri="https://localhost:8080",
|
|
58
|
-
config_cdn_uri="https://localhost:8080",
|
|
59
|
-
events_api_uri="https://localhost:8080",
|
|
60
58
|
)
|
|
61
59
|
client = DevCycleCloudClient(sdk_key, option_with_data)
|
|
62
60
|
self.assertIsNotNone(client)
|
|
@@ -210,7 +208,7 @@ class DVCCloudClientTest(unittest.TestCase):
|
|
|
210
208
|
|
|
211
209
|
@patch("devcycle_python_sdk.api.bucketing_client.BucketingAPIClient.track")
|
|
212
210
|
def test_track_event_bad_user(self, mock_track_call):
|
|
213
|
-
event =
|
|
211
|
+
event = DevCycleEvent(
|
|
214
212
|
type="user",
|
|
215
213
|
target="test_target",
|
|
216
214
|
date=time(),
|
|
@@ -236,10 +234,9 @@ class DVCCloudClientTest(unittest.TestCase):
|
|
|
236
234
|
self.test_client.track(self.test_user, None)
|
|
237
235
|
mock_track_call.assert_not_called()
|
|
238
236
|
|
|
239
|
-
event =
|
|
237
|
+
event = DevCycleEvent(
|
|
240
238
|
type=None,
|
|
241
239
|
target="test_target",
|
|
242
|
-
date=time(),
|
|
243
240
|
value=42,
|
|
244
241
|
metaData={"key": "value"},
|
|
245
242
|
)
|
|
@@ -247,10 +244,9 @@ class DVCCloudClientTest(unittest.TestCase):
|
|
|
247
244
|
self.test_client.track(self.test_user, event)
|
|
248
245
|
mock_track_call.assert_not_called()
|
|
249
246
|
|
|
250
|
-
event =
|
|
247
|
+
event = DevCycleEvent(
|
|
251
248
|
type="",
|
|
252
249
|
target="test_target",
|
|
253
|
-
date=time(),
|
|
254
250
|
value=42,
|
|
255
251
|
metaData={"key": "value"},
|
|
256
252
|
)
|
|
@@ -265,10 +261,9 @@ class DVCCloudClientTest(unittest.TestCase):
|
|
|
265
261
|
with self.assertRaises(CloudClientUnauthorizedError):
|
|
266
262
|
self.test_client.track(
|
|
267
263
|
self.test_user,
|
|
268
|
-
|
|
264
|
+
DevCycleEvent(
|
|
269
265
|
type="user",
|
|
270
266
|
target="test_target",
|
|
271
|
-
date=time(),
|
|
272
267
|
value=42,
|
|
273
268
|
metaData={"key": "value"},
|
|
274
269
|
),
|
|
@@ -278,10 +273,9 @@ class DVCCloudClientTest(unittest.TestCase):
|
|
|
278
273
|
mock_track_call.side_effect = Exception("Some error")
|
|
279
274
|
self.test_client.track(
|
|
280
275
|
self.test_user,
|
|
281
|
-
|
|
276
|
+
DevCycleEvent(
|
|
282
277
|
type="user",
|
|
283
278
|
target="test_target",
|
|
284
|
-
date=time(),
|
|
285
279
|
value=42,
|
|
286
280
|
metaData={"key": "value"},
|
|
287
281
|
),
|