feldera 0.125.0__tar.gz → 0.127.0__tar.gz

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.

Potentially problematic release.


This version of feldera might be problematic. Click here for more details.

Files changed (34) hide show
  1. {feldera-0.125.0 → feldera-0.127.0}/PKG-INFO +1 -1
  2. {feldera-0.125.0 → feldera-0.127.0}/feldera/pipeline.py +13 -0
  3. {feldera-0.125.0 → feldera-0.127.0}/feldera/rest/feldera_client.py +52 -0
  4. {feldera-0.125.0 → feldera-0.127.0}/feldera.egg-info/PKG-INFO +1 -1
  5. {feldera-0.125.0 → feldera-0.127.0}/pyproject.toml +1 -1
  6. feldera-0.127.0/tests/test_checkpoint_sync.py +319 -0
  7. feldera-0.125.0/tests/test_checkpoint_sync.py +0 -160
  8. {feldera-0.125.0 → feldera-0.127.0}/README.md +0 -0
  9. {feldera-0.125.0 → feldera-0.127.0}/feldera/__init__.py +0 -0
  10. {feldera-0.125.0 → feldera-0.127.0}/feldera/_callback_runner.py +0 -0
  11. {feldera-0.125.0 → feldera-0.127.0}/feldera/_helpers.py +0 -0
  12. {feldera-0.125.0 → feldera-0.127.0}/feldera/enums.py +0 -0
  13. {feldera-0.125.0 → feldera-0.127.0}/feldera/output_handler.py +0 -0
  14. {feldera-0.125.0 → feldera-0.127.0}/feldera/pipeline_builder.py +0 -0
  15. {feldera-0.125.0 → feldera-0.127.0}/feldera/rest/__init__.py +0 -0
  16. {feldera-0.125.0 → feldera-0.127.0}/feldera/rest/_helpers.py +0 -0
  17. {feldera-0.125.0 → feldera-0.127.0}/feldera/rest/_httprequests.py +0 -0
  18. {feldera-0.125.0 → feldera-0.127.0}/feldera/rest/config.py +0 -0
  19. {feldera-0.125.0 → feldera-0.127.0}/feldera/rest/errors.py +0 -0
  20. {feldera-0.125.0 → feldera-0.127.0}/feldera/rest/feldera_config.py +0 -0
  21. {feldera-0.125.0 → feldera-0.127.0}/feldera/rest/pipeline.py +0 -0
  22. {feldera-0.125.0 → feldera-0.127.0}/feldera/rest/sql_table.py +0 -0
  23. {feldera-0.125.0 → feldera-0.127.0}/feldera/rest/sql_view.py +0 -0
  24. {feldera-0.125.0 → feldera-0.127.0}/feldera/runtime_config.py +0 -0
  25. {feldera-0.125.0 → feldera-0.127.0}/feldera/stats.py +0 -0
  26. {feldera-0.125.0 → feldera-0.127.0}/feldera.egg-info/SOURCES.txt +0 -0
  27. {feldera-0.125.0 → feldera-0.127.0}/feldera.egg-info/dependency_links.txt +0 -0
  28. {feldera-0.125.0 → feldera-0.127.0}/feldera.egg-info/requires.txt +0 -0
  29. {feldera-0.125.0 → feldera-0.127.0}/feldera.egg-info/top_level.txt +0 -0
  30. {feldera-0.125.0 → feldera-0.127.0}/setup.cfg +0 -0
  31. {feldera-0.125.0 → feldera-0.127.0}/tests/test_pipeline_builder.py +0 -0
  32. {feldera-0.125.0 → feldera-0.127.0}/tests/test_shared_pipeline.py +0 -0
  33. {feldera-0.125.0 → feldera-0.127.0}/tests/test_shared_pipeline_stress.py +0 -0
  34. {feldera-0.125.0 → feldera-0.127.0}/tests/test_udf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: feldera
3
- Version: 0.125.0
3
+ Version: 0.127.0
4
4
  Summary: The feldera python client
5
5
  Author-email: Feldera Team <dev@feldera.com>
6
6
  License: MIT
@@ -474,6 +474,19 @@ metrics"""
474
474
  raise RuntimeError(f"waiting for idle reached timeout ({timeout_s}s)")
475
475
  time.sleep(poll_interval_s)
476
476
 
477
+ def activate(self, wait: bool = True, timeout_s: Optional[float] = None):
478
+ """
479
+ Activates the pipeline when starting from STANDBY mode. Only applicable
480
+ when the pipeline is starting from a checkpoint in object store.
481
+
482
+ :param wait: Set True to wait for the pipeline to activate. True by
483
+ default
484
+ :param timeout_s: The maximum time (in seconds) to wait for the
485
+ pipeline to pause.
486
+ """
487
+
488
+ self.client.activate_pipeline(self.name, wait=wait, timeout_s=timeout_s)
489
+
477
490
  def pause(self, wait: bool = True, timeout_s: Optional[float] = None):
478
491
  """
479
492
  Pause the pipeline.
@@ -268,6 +268,58 @@ class FelderaClient:
268
268
  if chunk:
269
269
  yield chunk.decode("utf-8")
270
270
 
271
+ def activate_pipeline(
272
+ self, pipeline_name: str, wait: bool = True, timeout_s: Optional[float] = 300
273
+ ):
274
+ """
275
+
276
+ :param pipeline_name: The name of the pipeline to activate
277
+ :param wait: Set True to wait for the pipeline to activate. True by default
278
+ :param timeout_s: The amount of time in seconds to wait for the pipeline
279
+ to activate. 300 seconds by default.
280
+ """
281
+
282
+ if timeout_s is None:
283
+ timeout_s = 300
284
+
285
+ self.http.post(
286
+ path=f"/pipelines/{pipeline_name}/activate",
287
+ )
288
+
289
+ if not wait:
290
+ return
291
+
292
+ start_time = time.monotonic()
293
+
294
+ while True:
295
+ if timeout_s is not None:
296
+ elapsed = time.monotonic() - start_time
297
+ if elapsed > timeout_s:
298
+ raise TimeoutError(
299
+ f"Timed out waiting for pipeline {pipeline_name} to activate"
300
+ )
301
+
302
+ resp = self.get_pipeline(pipeline_name)
303
+ status = resp.deployment_status
304
+
305
+ if status == "Running":
306
+ break
307
+ elif (
308
+ status == "Stopped"
309
+ and len(resp.deployment_error or {}) > 0
310
+ and resp.deployment_desired_status == "Stopped"
311
+ ):
312
+ raise RuntimeError(
313
+ f"""Unable to ACTIVATE the pipeline.
314
+ Reason: The pipeline is in a STOPPED state due to the following error:
315
+ {resp.deployment_error.get("message", "")}"""
316
+ )
317
+
318
+ logging.debug(
319
+ "still starting %s, waiting for 100 more milliseconds", pipeline_name
320
+ )
321
+ time.sleep(0.1)
322
+
271
323
  def start_pipeline(
272
324
  self, pipeline_name: str, wait: bool = True, timeout_s: Optional[float] = 300
273
325
  ):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: feldera
3
- Version: 0.125.0
3
+ Version: 0.127.0
4
4
  Summary: The feldera python client
5
5
  Author-email: Feldera Team <dev@feldera.com>
6
6
  License: MIT
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
  name = "feldera"
7
7
  readme = "README.md"
8
8
  description = "The feldera python client"
9
- version = "0.125.0"
9
+ version = "0.127.0"
10
10
  license = { text = "MIT" }
11
11
  requires-python = ">=3.10"
12
12
  authors = [
@@ -0,0 +1,319 @@
1
+ from tests.shared_test_pipeline import SharedTestPipeline
2
+ from tests import enterprise_only
3
+ from feldera.runtime_config import RuntimeConfig, Storage
4
+ from feldera.enums import PipelineStatus, FaultToleranceModel
5
+ from typing import Optional
6
+ import os
7
+ import sys
8
+ import time
9
+ from uuid import uuid4
10
+ import random
11
+
12
+
13
+ DEFAULT_ENDPOINT = os.environ.get(
14
+ "DEFAULT_MINIO_ENDPOINT", "http://minio.extra.svc.cluster.local:9000"
15
+ )
16
+ DEFAULT_BUCKET = "default"
17
+ ACCESS_KEY = "minio"
18
+ SECRET_KEY = "miniopasswd"
19
+
20
+
21
+ def storage_cfg(
22
+ pipeline_name: str,
23
+ endpoint: Optional[str] = None,
24
+ start_from_checkpoint: Optional[str] = None,
25
+ strict: bool = False,
26
+ auth_err: bool = False,
27
+ standby: bool = False,
28
+ pull_interval: int = 2,
29
+ ) -> dict:
30
+ return {
31
+ "backend": {
32
+ "name": "file",
33
+ "config": {
34
+ "sync": {
35
+ "bucket": f"{DEFAULT_BUCKET}/{pipeline_name}",
36
+ "access_key": ACCESS_KEY,
37
+ "secret_key": SECRET_KEY if not auth_err else SECRET_KEY + "extra",
38
+ "provider": "Minio",
39
+ "endpoint": endpoint or DEFAULT_ENDPOINT,
40
+ "start_from_checkpoint": start_from_checkpoint,
41
+ "fail_if_no_checkpoint": strict,
42
+ "standby": standby,
43
+ "pull_interval": pull_interval,
44
+ }
45
+ },
46
+ }
47
+ }
48
+
49
+
50
+ class TestCheckpointSync(SharedTestPipeline):
51
+ @enterprise_only
52
+ def test_checkpoint_sync(
53
+ self,
54
+ from_uuid: bool = False,
55
+ random_uuid: bool = False,
56
+ clear_storage: bool = True,
57
+ auth_err: bool = False,
58
+ strict: bool = False,
59
+ expect_empty: bool = False,
60
+ standby: bool = False,
61
+ ):
62
+ """
63
+ CREATE TABLE t0 (c0 INT, c1 VARCHAR);
64
+ CREATE MATERIALIZED VIEW v0 AS SELECT * FROM t0;
65
+ """
66
+
67
+ storage_config = storage_cfg(self.pipeline.name)
68
+ ft = FaultToleranceModel.AtLeastOnce
69
+
70
+ self.pipeline.set_runtime_config(
71
+ RuntimeConfig(
72
+ fault_tolerance_model=ft, storage=Storage(config=storage_config)
73
+ )
74
+ )
75
+ self.pipeline.start()
76
+
77
+ random.seed(time.time())
78
+ total = random.randint(10, 20)
79
+ data = [{"c0": i, "c1": str(i)} for i in range(1, total)]
80
+ self.pipeline.input_json("t0", data)
81
+ self.pipeline.execute("INSERT INTO t0 VALUES (21, 'exists')")
82
+
83
+ start = time.time()
84
+ timeout = 5
85
+
86
+ while True:
87
+ processed = self.pipeline.stats().global_metrics.total_processed_records
88
+ if processed == total:
89
+ break
90
+
91
+ if time.time() - start > timeout:
92
+ raise TimeoutError(
93
+ f"timed out while waiting for pipeline to process {total} records"
94
+ )
95
+
96
+ time.sleep(0.1)
97
+
98
+ got_before = list(self.pipeline.query("SELECT * FROM v0"))
99
+ print(f"{self.pipeline.name}: records: {total}, {got_before}", file=sys.stderr)
100
+
101
+ if len(got_before) != processed:
102
+ raise RuntimeError(
103
+ f"adhoc query returned {len(got_before)} but {processed} records were processed: {got_before}"
104
+ )
105
+
106
+ self.pipeline.checkpoint(wait=True)
107
+ uuid = self.pipeline.sync_checkpoint(wait=True)
108
+
109
+ self.pipeline.stop(force=True)
110
+
111
+ if clear_storage:
112
+ self.pipeline.clear_storage()
113
+
114
+ if random_uuid:
115
+ uuid = uuid4()
116
+
117
+ # Restart pipeline from checkpoint
118
+ storage_config = storage_cfg(
119
+ pipeline_name=self.pipeline.name,
120
+ start_from_checkpoint=uuid if from_uuid else "latest",
121
+ auth_err=auth_err,
122
+ strict=strict,
123
+ standby=standby,
124
+ )
125
+ self.pipeline.set_runtime_config(
126
+ RuntimeConfig(
127
+ fault_tolerance_model=ft, storage=Storage(config=storage_config)
128
+ )
129
+ )
130
+
131
+ if not standby:
132
+ self.pipeline.start()
133
+ else:
134
+ self.pipeline.start(wait=False)
135
+
136
+ # wait for the pipeline to initialize
137
+ start = time.monotonic()
138
+ # wait for a maximum of 120 seconds for the pipeline to provison
139
+ end = start + 120
140
+
141
+ # wait for the pipeline to finish provisoning
142
+ for log in self.pipeline.logs():
143
+ if "checkpoint pulled successfully" in log:
144
+ break
145
+
146
+ if time.monotonic() > end:
147
+ raise TimeoutError(
148
+ f"{self.pipeline.name} timedout waiting to pull checkpoint"
149
+ )
150
+
151
+ if standby:
152
+ # wait for 8 seconds, this should be more than enough time
153
+ time.sleep(8)
154
+ assert self.pipeline.status() == PipelineStatus.INITIALIZING
155
+
156
+ self.pipeline.activate(timeout_s=10)
157
+
158
+ got_after = list(self.pipeline.query("SELECT * FROM v0"))
159
+
160
+ print(
161
+ f"{self.pipeline.name}: after: {len(got_after)}, {got_after}",
162
+ file=sys.stderr,
163
+ )
164
+
165
+ if expect_empty:
166
+ got_before = []
167
+
168
+ self.assertCountEqual(got_before, got_after)
169
+
170
+ self.pipeline.stop(force=True)
171
+
172
+ if clear_storage:
173
+ self.pipeline.clear_storage()
174
+
175
+ @enterprise_only
176
+ def test_from_uuid(self):
177
+ self.test_checkpoint_sync(from_uuid=True)
178
+
179
+ @enterprise_only
180
+ def test_without_clearing_storage(self):
181
+ self.test_checkpoint_sync(clear_storage=False)
182
+
183
+ @enterprise_only
184
+ def test_autherr_fail(self):
185
+ with self.assertRaisesRegex(RuntimeError, "SignatureDoesNotMatch"):
186
+ self.test_checkpoint_sync(auth_err=True, strict=True)
187
+
188
+ @enterprise_only
189
+ def test_autherr(self):
190
+ self.test_checkpoint_sync(auth_err=True, strict=False, expect_empty=True)
191
+
192
+ @enterprise_only
193
+ def test_nonexistent_checkpoint_fail(self):
194
+ with self.assertRaisesRegex(RuntimeError, "were not found in source"):
195
+ self.test_checkpoint_sync(random_uuid=True, from_uuid=True, strict=True)
196
+
197
+ @enterprise_only
198
+ def test_nonexistent_checkpoint(self):
199
+ self.test_checkpoint_sync(random_uuid=True, from_uuid=True, expect_empty=True)
200
+
201
+ @enterprise_only
202
+ def test_standby_activation(self):
203
+ self.test_checkpoint_sync(standby=True)
204
+
205
+ @enterprise_only
206
+ def test_standby_activation_from_uuid(self):
207
+ self.test_checkpoint_sync(standby=True, from_uuid=True)
208
+
209
+ @enterprise_only
210
+ def test_standby_fallback(self, from_uuid: bool = False):
211
+ # Step 1: Start main pipeline
212
+ storage_config = storage_cfg(self.pipeline.name)
213
+ ft = FaultToleranceModel.AtLeastOnce
214
+ self.pipeline.set_runtime_config(
215
+ RuntimeConfig(
216
+ fault_tolerance_model=ft, storage=Storage(config=storage_config)
217
+ )
218
+ )
219
+ self.pipeline.start()
220
+
221
+ # Insert initial data
222
+ random.seed(time.time())
223
+ total_initial = random.randint(10, 20)
224
+ data_initial = [{"c0": i, "c1": str(i)} for i in range(1, total_initial)]
225
+ self.pipeline.input_json("t0", data_initial)
226
+ self.pipeline.execute("INSERT INTO t0 VALUES (21, 'exists')")
227
+ self.pipeline.wait_for_completion()
228
+
229
+ got_before = list(self.pipeline.query("select * from v0"))
230
+
231
+ # Step 2: Create checkpoint and sync
232
+ self.pipeline.checkpoint(wait=True)
233
+ uuid = self.pipeline.sync_checkpoint(wait=True)
234
+
235
+ # Step 3: Start standby pipeline
236
+ standby = self.new_pipeline_with_suffix("standby")
237
+ pull_interval = 1
238
+ standby.set_runtime_config(
239
+ RuntimeConfig(
240
+ fault_tolerance_model=ft,
241
+ storage=Storage(
242
+ config=storage_cfg(
243
+ self.pipeline.name,
244
+ start_from_checkpoint=uuid if from_uuid else "latest",
245
+ standby=True,
246
+ pull_interval=pull_interval,
247
+ )
248
+ ),
249
+ )
250
+ )
251
+ standby.start(wait=False)
252
+
253
+ # Wait until standby pulls the first checkpoint
254
+ start = time.monotonic()
255
+ end = start + 120
256
+ for log in standby.logs():
257
+ if "checkpoint pulled successfully" in log:
258
+ break
259
+ if time.monotonic() > end:
260
+ raise TimeoutError(
261
+ "Timed out waiting for standby pipeline to pull checkpoint"
262
+ )
263
+
264
+ # Step 4: Add more data and make 3-10 checkpoints
265
+ extra_ckpts = random.randint(3, 10)
266
+ total_additional = 0
267
+
268
+ for i in range(extra_ckpts):
269
+ new_val = 100 + i
270
+ new_data = [{"c0": new_val, "c1": f"extra_{new_val}"}]
271
+ self.pipeline.input_json("t0", new_data)
272
+ self.pipeline.wait_for_completion()
273
+ total_additional += 1
274
+ self.pipeline.checkpoint(wait=True)
275
+ self.pipeline.sync_checkpoint(wait=True)
276
+ time.sleep(0.2)
277
+
278
+ got_expected = (
279
+ got_before if from_uuid else list(self.pipeline.query("SELECT * FROM v0"))
280
+ )
281
+ print(
282
+ f"{self.pipeline.name}: final records before shutdown: {got_expected}",
283
+ file=sys.stderr,
284
+ )
285
+
286
+ # Step 5: Stop main and activate standby
287
+ self.pipeline.stop(force=True)
288
+
289
+ assert standby.status() == PipelineStatus.INITIALIZING
290
+ standby.activate(timeout_s=(pull_interval * extra_ckpts) + 60)
291
+
292
+ for log in standby.logs():
293
+ if "activated" in log:
294
+ break
295
+ if time.monotonic() > end:
296
+ raise TimeoutError("Timed out waiting for standby pipeline to activate")
297
+
298
+ # Step 6: Validate standby has all expected records
299
+ got_after = list(standby.query("SELECT * FROM v0"))
300
+ print(
301
+ f"{standby.name}: final records after activation: {got_after}",
302
+ file=sys.stderr,
303
+ )
304
+ self.assertCountEqual(got_expected, got_after)
305
+
306
+ # Cleanup
307
+ standby.stop(force=True)
308
+
309
+ standby.start()
310
+ got_final = list(standby.query("SELECT * FROM v0"))
311
+ standby.stop(force=True)
312
+
313
+ self.assertCountEqual(got_after, got_final)
314
+
315
+ self.pipeline.clear_storage()
316
+
317
+ @enterprise_only
318
+ def test_standby_fallback_from_uuid(self):
319
+ self.test_standby_fallback(from_uuid=True)
@@ -1,160 +0,0 @@
1
- from tests.shared_test_pipeline import SharedTestPipeline
2
- from tests import enterprise_only
3
- from feldera.runtime_config import RuntimeConfig, Storage
4
- from typing import Optional
5
- import os
6
- import sys
7
- import time
8
- from uuid import uuid4
9
- import random
10
-
11
-
12
- DEFAULT_ENDPOINT = os.environ.get(
13
- "DEFAULT_MINIO_ENDPOINT", "http://minio.extra.svc.cluster.local:9000"
14
- )
15
- DEFAULT_BUCKET = "default"
16
- ACCESS_KEY = "minio"
17
- SECRET_KEY = "miniopasswd"
18
-
19
-
20
- def storage_cfg(
21
- pipeline_name: str,
22
- endpoint: Optional[str] = None,
23
- start_from_checkpoint: Optional[str] = None,
24
- strict: bool = False,
25
- auth_err: bool = False,
26
- ) -> dict:
27
- return {
28
- "backend": {
29
- "name": "file",
30
- "config": {
31
- "sync": {
32
- "bucket": f"{DEFAULT_BUCKET}/{pipeline_name}",
33
- "access_key": ACCESS_KEY,
34
- "secret_key": SECRET_KEY if not auth_err else SECRET_KEY + "extra",
35
- "provider": "Minio",
36
- "endpoint": endpoint or DEFAULT_ENDPOINT,
37
- "start_from_checkpoint": start_from_checkpoint,
38
- "fail_if_no_checkpoint": strict,
39
- }
40
- },
41
- }
42
- }
43
-
44
-
45
- class TestCheckpointSync(SharedTestPipeline):
46
- @enterprise_only
47
- def test_checkpoint_sync(
48
- self,
49
- from_uuid: bool = False,
50
- random_uuid: bool = False,
51
- clear_storage: bool = True,
52
- auth_err: bool = False,
53
- strict: bool = False,
54
- expect_empty: bool = False,
55
- ):
56
- """
57
- CREATE TABLE t0 (c0 INT, c1 VARCHAR);
58
- CREATE MATERIALIZED VIEW v0 AS SELECT * FROM t0;
59
- """
60
-
61
- storage_config = storage_cfg(self.pipeline.name)
62
-
63
- self.pipeline.set_runtime_config(
64
- RuntimeConfig(storage=Storage(config=storage_config))
65
- )
66
- self.pipeline.start()
67
-
68
- random.seed(time.time())
69
- total = random.randint(10, 20)
70
- data = [{"c0": i, "c1": str(i)} for i in range(1, total)]
71
- self.pipeline.input_json("t0", data)
72
- self.pipeline.execute("INSERT INTO t0 VALUES (21, 'exists')")
73
-
74
- start = time.time()
75
- timeout = 5
76
-
77
- while True:
78
- processed = self.pipeline.stats().global_metrics.total_processed_records
79
- if processed == total:
80
- break
81
-
82
- if time.time() - start > timeout:
83
- raise TimeoutError(
84
- f"timed out while waiting for pipeline to process {total} records"
85
- )
86
-
87
- time.sleep(0.1)
88
-
89
- got_before = list(self.pipeline.query("SELECT * FROM v0"))
90
- print(f"{self.pipeline.name}: records: {total}, {got_before}", file=sys.stderr)
91
-
92
- if len(got_before) != processed:
93
- raise RuntimeError(
94
- f"adhoc query returned {len(got_before)} but {processed} records were processed: {got_before}"
95
- )
96
-
97
- self.pipeline.checkpoint(wait=True)
98
- uuid = self.pipeline.sync_checkpoint(wait=True)
99
-
100
- self.pipeline.stop(force=True)
101
-
102
- if clear_storage:
103
- self.pipeline.clear_storage()
104
-
105
- if random_uuid:
106
- uuid = uuid4()
107
-
108
- # Restart pipeline from checkpoint
109
- storage_config = storage_cfg(
110
- pipeline_name=self.pipeline.name,
111
- start_from_checkpoint=uuid if from_uuid else "latest",
112
- auth_err=auth_err,
113
- strict=strict,
114
- )
115
- self.pipeline.set_runtime_config(
116
- RuntimeConfig(storage=Storage(config=storage_config))
117
- )
118
- self.pipeline.start()
119
- got_after = list(self.pipeline.query("SELECT * FROM v0"))
120
-
121
- print(
122
- f"{self.pipeline.name}: after: {len(got_after)}, {got_after}",
123
- file=sys.stderr,
124
- )
125
-
126
- if expect_empty:
127
- got_before = []
128
-
129
- self.assertCountEqual(got_before, got_after)
130
-
131
- self.pipeline.stop(force=True)
132
-
133
- if clear_storage:
134
- self.pipeline.clear_storage()
135
-
136
- @enterprise_only
137
- def test_from_uuid(self):
138
- self.test_checkpoint_sync(from_uuid=True)
139
-
140
- @enterprise_only
141
- def test_without_clearing_storage(self):
142
- self.test_checkpoint_sync(clear_storage=False)
143
-
144
- @enterprise_only
145
- def test_autherr_fail(self):
146
- with self.assertRaisesRegex(RuntimeError, "SignatureDoesNotMatch"):
147
- self.test_checkpoint_sync(auth_err=True, strict=True)
148
-
149
- @enterprise_only
150
- def test_autherr(self):
151
- self.test_checkpoint_sync(auth_err=True, strict=False, expect_empty=True)
152
-
153
- @enterprise_only
154
- def test_nonexistent_checkpoint_fail(self):
155
- with self.assertRaisesRegex(RuntimeError, "were not found in source"):
156
- self.test_checkpoint_sync(random_uuid=True, from_uuid=True, strict=True)
157
-
158
- @enterprise_only
159
- def test_nonexistent_checkpoint(self):
160
- self.test_checkpoint_sync(random_uuid=True, from_uuid=True, expect_empty=True)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes