eventsourcing 9.3.4__py3-none-any.whl → 9.4.0a1__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.
Potentially problematic release.
This version of eventsourcing might be problematic. Click here for more details.
- eventsourcing/application.py +75 -26
- eventsourcing/cipher.py +1 -1
- eventsourcing/domain.py +29 -3
- eventsourcing/interface.py +23 -5
- eventsourcing/persistence.py +292 -71
- eventsourcing/popo.py +113 -32
- eventsourcing/postgres.py +265 -103
- eventsourcing/projection.py +157 -0
- eventsourcing/sqlite.py +143 -36
- eventsourcing/system.py +89 -44
- eventsourcing/tests/application.py +48 -12
- eventsourcing/tests/persistence.py +304 -75
- eventsourcing/utils.py +1 -1
- {eventsourcing-9.3.4.dist-info → eventsourcing-9.4.0a1.dist-info}/LICENSE +1 -1
- {eventsourcing-9.3.4.dist-info → eventsourcing-9.4.0a1.dist-info}/METADATA +2 -2
- eventsourcing-9.4.0a1.dist-info/RECORD +25 -0
- eventsourcing-9.3.4.dist-info/RECORD +0 -24
- {eventsourcing-9.3.4.dist-info → eventsourcing-9.4.0a1.dist-info}/AUTHORS +0 -0
- {eventsourcing-9.3.4.dist-info → eventsourcing-9.4.0a1.dist-info}/WHEEL +0 -0
|
@@ -26,11 +26,14 @@ from eventsourcing.persistence import (
|
|
|
26
26
|
IntegrityError,
|
|
27
27
|
JSONTranscoder,
|
|
28
28
|
Mapper,
|
|
29
|
+
Notification,
|
|
29
30
|
ProcessRecorder,
|
|
30
31
|
StoredEvent,
|
|
31
32
|
Tracking,
|
|
33
|
+
TrackingRecorder,
|
|
32
34
|
Transcoding,
|
|
33
35
|
UUIDAsHex,
|
|
36
|
+
WaitInterruptedError,
|
|
34
37
|
)
|
|
35
38
|
from eventsourcing.utils import Environment, get_topic
|
|
36
39
|
|
|
@@ -206,6 +209,7 @@ class AggregateRecorderTestCase(TestCase, ABC):
|
|
|
206
209
|
|
|
207
210
|
class ApplicationRecorderTestCase(TestCase, ABC):
|
|
208
211
|
INITIAL_VERSION = 1
|
|
212
|
+
EXPECT_CONTIGUOUS_NOTIFICATION_IDS = True
|
|
209
213
|
|
|
210
214
|
@abstractmethod
|
|
211
215
|
def create_recorder(self) -> ApplicationRecorder:
|
|
@@ -215,26 +219,15 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
215
219
|
# Construct the recorder.
|
|
216
220
|
recorder = self.create_recorder()
|
|
217
221
|
|
|
218
|
-
max_notification_id = recorder.max_notification_id()
|
|
219
|
-
|
|
220
222
|
# Check notifications methods work when there aren't any.
|
|
223
|
+
self.assertEqual(len(recorder.select_notifications(start=None, limit=3)), 0)
|
|
221
224
|
self.assertEqual(
|
|
222
|
-
recorder.
|
|
223
|
-
max_notification_id,
|
|
224
|
-
)
|
|
225
|
-
self.assertEqual(
|
|
226
|
-
len(recorder.select_notifications(max_notification_id + 1, 3)),
|
|
227
|
-
0,
|
|
228
|
-
)
|
|
229
|
-
self.assertEqual(
|
|
230
|
-
len(
|
|
231
|
-
recorder.select_notifications(
|
|
232
|
-
max_notification_id + 1, 3, topics=["topic1"]
|
|
233
|
-
)
|
|
234
|
-
),
|
|
225
|
+
len(recorder.select_notifications(start=None, limit=3, topics=["topic1"])),
|
|
235
226
|
0,
|
|
236
227
|
)
|
|
237
228
|
|
|
229
|
+
self.assertIsNone(recorder.max_notification_id())
|
|
230
|
+
|
|
238
231
|
# Write two stored events.
|
|
239
232
|
originator_id1 = uuid4()
|
|
240
233
|
originator_id2 = uuid4()
|
|
@@ -253,9 +246,7 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
253
246
|
)
|
|
254
247
|
|
|
255
248
|
notification_ids = recorder.insert_events([stored_event1, stored_event2])
|
|
256
|
-
self.assertEqual(
|
|
257
|
-
notification_ids, [max_notification_id + 1, max_notification_id + 2]
|
|
258
|
-
)
|
|
249
|
+
self.assertEqual(notification_ids, [1, 2])
|
|
259
250
|
|
|
260
251
|
# Store a third event.
|
|
261
252
|
stored_event3 = StoredEvent(
|
|
@@ -265,7 +256,7 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
265
256
|
state=b"state3",
|
|
266
257
|
)
|
|
267
258
|
notification_ids = recorder.insert_events([stored_event3])
|
|
268
|
-
self.assertEqual(notification_ids, [
|
|
259
|
+
self.assertEqual(notification_ids, [3])
|
|
269
260
|
|
|
270
261
|
stored_events1 = recorder.select_events(originator_id1)
|
|
271
262
|
stored_events2 = recorder.select_events(originator_id2)
|
|
@@ -279,111 +270,158 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
279
270
|
recorder.insert_events([stored_event3])
|
|
280
271
|
|
|
281
272
|
sleep(1) # Added to make eventsourcing-axon tests work, perhaps not necessary.
|
|
282
|
-
notifications = recorder.select_notifications(
|
|
273
|
+
notifications = recorder.select_notifications(start=None, limit=10)
|
|
283
274
|
self.assertEqual(len(notifications), 3)
|
|
284
|
-
self.assertEqual(notifications[0].id,
|
|
275
|
+
self.assertEqual(notifications[0].id, 1)
|
|
285
276
|
self.assertEqual(notifications[0].originator_id, originator_id1)
|
|
286
277
|
self.assertEqual(notifications[0].topic, "topic1")
|
|
287
278
|
self.assertEqual(notifications[0].state, b"state1")
|
|
288
|
-
self.assertEqual(notifications[1].id,
|
|
279
|
+
self.assertEqual(notifications[1].id, 2)
|
|
289
280
|
self.assertEqual(notifications[1].originator_id, originator_id1)
|
|
290
281
|
self.assertEqual(notifications[1].topic, "topic2")
|
|
291
282
|
self.assertEqual(notifications[1].state, b"state2")
|
|
292
|
-
self.assertEqual(notifications[2].id,
|
|
283
|
+
self.assertEqual(notifications[2].id, 3)
|
|
293
284
|
self.assertEqual(notifications[2].originator_id, originator_id2)
|
|
294
285
|
self.assertEqual(notifications[2].topic, "topic3")
|
|
295
286
|
self.assertEqual(notifications[2].state, b"state3")
|
|
296
287
|
|
|
297
|
-
notifications = recorder.select_notifications(
|
|
298
|
-
max_notification_id + 1, 3, topics=["topic1", "topic2", "topic3"]
|
|
299
|
-
)
|
|
288
|
+
notifications = recorder.select_notifications(start=1, limit=10)
|
|
300
289
|
self.assertEqual(len(notifications), 3)
|
|
301
|
-
self.assertEqual(notifications[0].id,
|
|
290
|
+
self.assertEqual(notifications[0].id, 1)
|
|
302
291
|
self.assertEqual(notifications[0].originator_id, originator_id1)
|
|
303
292
|
self.assertEqual(notifications[0].topic, "topic1")
|
|
304
293
|
self.assertEqual(notifications[0].state, b"state1")
|
|
305
|
-
self.assertEqual(notifications[1].id,
|
|
294
|
+
self.assertEqual(notifications[1].id, 2)
|
|
306
295
|
self.assertEqual(notifications[1].originator_id, originator_id1)
|
|
307
296
|
self.assertEqual(notifications[1].topic, "topic2")
|
|
308
297
|
self.assertEqual(notifications[1].state, b"state2")
|
|
309
|
-
self.assertEqual(notifications[2].id,
|
|
298
|
+
self.assertEqual(notifications[2].id, 3)
|
|
310
299
|
self.assertEqual(notifications[2].originator_id, originator_id2)
|
|
311
300
|
self.assertEqual(notifications[2].topic, "topic3")
|
|
312
301
|
self.assertEqual(notifications[2].state, b"state3")
|
|
313
302
|
|
|
314
|
-
notifications = recorder.select_notifications(
|
|
315
|
-
|
|
316
|
-
)
|
|
317
|
-
self.assertEqual(len(notifications), 1)
|
|
318
|
-
self.assertEqual(notifications[0].id, max_notification_id + 1)
|
|
303
|
+
notifications = recorder.select_notifications(start=None, stop=2, limit=10)
|
|
304
|
+
self.assertEqual(len(notifications), 2)
|
|
305
|
+
self.assertEqual(notifications[0].id, 1)
|
|
319
306
|
self.assertEqual(notifications[0].originator_id, originator_id1)
|
|
320
307
|
self.assertEqual(notifications[0].topic, "topic1")
|
|
321
308
|
self.assertEqual(notifications[0].state, b"state1")
|
|
309
|
+
self.assertEqual(notifications[1].id, 2)
|
|
310
|
+
self.assertEqual(notifications[1].originator_id, originator_id1)
|
|
311
|
+
self.assertEqual(notifications[1].topic, "topic2")
|
|
312
|
+
self.assertEqual(notifications[1].state, b"state2")
|
|
322
313
|
|
|
323
314
|
notifications = recorder.select_notifications(
|
|
324
|
-
|
|
315
|
+
start=1, limit=10, inclusive_of_start=False
|
|
325
316
|
)
|
|
326
|
-
self.assertEqual(len(notifications),
|
|
327
|
-
self.assertEqual(notifications[0].id,
|
|
317
|
+
self.assertEqual(len(notifications), 2)
|
|
318
|
+
self.assertEqual(notifications[0].id, 2)
|
|
328
319
|
self.assertEqual(notifications[0].originator_id, originator_id1)
|
|
329
320
|
self.assertEqual(notifications[0].topic, "topic2")
|
|
330
321
|
self.assertEqual(notifications[0].state, b"state2")
|
|
322
|
+
self.assertEqual(notifications[1].id, 3)
|
|
323
|
+
self.assertEqual(notifications[1].originator_id, originator_id2)
|
|
324
|
+
self.assertEqual(notifications[1].topic, "topic3")
|
|
325
|
+
self.assertEqual(notifications[1].state, b"state3")
|
|
331
326
|
|
|
332
327
|
notifications = recorder.select_notifications(
|
|
333
|
-
|
|
328
|
+
start=2, limit=10, inclusive_of_start=False
|
|
334
329
|
)
|
|
335
330
|
self.assertEqual(len(notifications), 1)
|
|
336
|
-
self.assertEqual(notifications[0].id,
|
|
331
|
+
self.assertEqual(notifications[0].id, 3)
|
|
337
332
|
self.assertEqual(notifications[0].originator_id, originator_id2)
|
|
338
333
|
self.assertEqual(notifications[0].topic, "topic3")
|
|
339
334
|
self.assertEqual(notifications[0].state, b"state3")
|
|
340
335
|
|
|
341
336
|
notifications = recorder.select_notifications(
|
|
342
|
-
|
|
337
|
+
start=None, limit=10, topics=["topic1", "topic2", "topic3"]
|
|
343
338
|
)
|
|
339
|
+
self.assertEqual(len(notifications), 3)
|
|
340
|
+
self.assertEqual(notifications[0].id, 1)
|
|
341
|
+
self.assertEqual(notifications[0].originator_id, originator_id1)
|
|
342
|
+
self.assertEqual(notifications[0].topic, "topic1")
|
|
343
|
+
self.assertEqual(notifications[0].state, b"state1")
|
|
344
|
+
self.assertEqual(notifications[1].id, 2)
|
|
345
|
+
self.assertEqual(notifications[1].originator_id, originator_id1)
|
|
346
|
+
self.assertEqual(notifications[1].topic, "topic2")
|
|
347
|
+
self.assertEqual(notifications[1].state, b"state2")
|
|
348
|
+
self.assertEqual(notifications[2].id, 3)
|
|
349
|
+
self.assertEqual(notifications[2].originator_id, originator_id2)
|
|
350
|
+
self.assertEqual(notifications[2].topic, "topic3")
|
|
351
|
+
self.assertEqual(notifications[2].state, b"state3")
|
|
352
|
+
|
|
353
|
+
notifications = recorder.select_notifications(1, 10, topics=["topic1"])
|
|
354
|
+
self.assertEqual(len(notifications), 1)
|
|
355
|
+
self.assertEqual(notifications[0].id, 1)
|
|
356
|
+
self.assertEqual(notifications[0].originator_id, originator_id1)
|
|
357
|
+
self.assertEqual(notifications[0].topic, "topic1")
|
|
358
|
+
self.assertEqual(notifications[0].state, b"state1")
|
|
359
|
+
|
|
360
|
+
notifications = recorder.select_notifications(1, 3, topics=["topic2"])
|
|
361
|
+
self.assertEqual(len(notifications), 1)
|
|
362
|
+
self.assertEqual(notifications[0].id, 2)
|
|
363
|
+
self.assertEqual(notifications[0].originator_id, originator_id1)
|
|
364
|
+
self.assertEqual(notifications[0].topic, "topic2")
|
|
365
|
+
self.assertEqual(notifications[0].state, b"state2")
|
|
366
|
+
|
|
367
|
+
notifications = recorder.select_notifications(1, 3, topics=["topic3"])
|
|
368
|
+
self.assertEqual(len(notifications), 1)
|
|
369
|
+
self.assertEqual(notifications[0].id, 3)
|
|
370
|
+
self.assertEqual(notifications[0].originator_id, originator_id2)
|
|
371
|
+
self.assertEqual(notifications[0].topic, "topic3")
|
|
372
|
+
self.assertEqual(notifications[0].state, b"state3")
|
|
373
|
+
|
|
374
|
+
notifications = recorder.select_notifications(1, 3, topics=["topic1", "topic3"])
|
|
344
375
|
self.assertEqual(len(notifications), 2)
|
|
345
|
-
self.assertEqual(notifications[0].id,
|
|
376
|
+
self.assertEqual(notifications[0].id, 1)
|
|
346
377
|
self.assertEqual(notifications[0].originator_id, originator_id1)
|
|
347
378
|
self.assertEqual(notifications[0].topic, "topic1")
|
|
348
379
|
self.assertEqual(notifications[0].state, b"state1")
|
|
349
|
-
self.assertEqual(notifications[1].id,
|
|
380
|
+
self.assertEqual(notifications[1].id, 3)
|
|
350
381
|
self.assertEqual(notifications[1].topic, "topic3")
|
|
351
382
|
self.assertEqual(notifications[1].state, b"state3")
|
|
352
383
|
|
|
353
|
-
self.assertEqual(
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
)
|
|
384
|
+
self.assertEqual(recorder.max_notification_id(), 3)
|
|
385
|
+
|
|
386
|
+
# Check limit is working
|
|
387
|
+
notifications = recorder.select_notifications(None, 1)
|
|
388
|
+
self.assertEqual(len(notifications), 1)
|
|
389
|
+
self.assertEqual(notifications[0].id, 1)
|
|
357
390
|
|
|
358
|
-
notifications = recorder.select_notifications(
|
|
391
|
+
notifications = recorder.select_notifications(2, 1)
|
|
359
392
|
self.assertEqual(len(notifications), 1)
|
|
360
|
-
self.assertEqual(notifications[0].id,
|
|
393
|
+
self.assertEqual(notifications[0].id, 2)
|
|
361
394
|
|
|
362
|
-
notifications = recorder.select_notifications(
|
|
395
|
+
notifications = recorder.select_notifications(1, 1, inclusive_of_start=False)
|
|
363
396
|
self.assertEqual(len(notifications), 1)
|
|
364
|
-
self.assertEqual(notifications[0].id,
|
|
397
|
+
self.assertEqual(notifications[0].id, 2)
|
|
365
398
|
|
|
366
|
-
notifications = recorder.select_notifications(
|
|
399
|
+
notifications = recorder.select_notifications(2, 2)
|
|
367
400
|
self.assertEqual(len(notifications), 2)
|
|
368
|
-
self.assertEqual(notifications[0].id,
|
|
369
|
-
self.assertEqual(notifications[1].id,
|
|
401
|
+
self.assertEqual(notifications[0].id, 2)
|
|
402
|
+
self.assertEqual(notifications[1].id, 3)
|
|
370
403
|
|
|
371
|
-
notifications = recorder.select_notifications(
|
|
404
|
+
notifications = recorder.select_notifications(3, 1)
|
|
372
405
|
self.assertEqual(len(notifications), 1)
|
|
373
|
-
self.assertEqual(notifications[0].id,
|
|
406
|
+
self.assertEqual(notifications[0].id, 3)
|
|
374
407
|
|
|
375
|
-
notifications = recorder.select_notifications(
|
|
376
|
-
|
|
377
|
-
|
|
408
|
+
notifications = recorder.select_notifications(3, 1, inclusive_of_start=False)
|
|
409
|
+
self.assertEqual(len(notifications), 0)
|
|
410
|
+
|
|
411
|
+
notifications = recorder.select_notifications(start=2, limit=10, stop=2)
|
|
378
412
|
self.assertEqual(len(notifications), 1)
|
|
379
|
-
self.assertEqual(notifications[0].id,
|
|
413
|
+
self.assertEqual(notifications[0].id, 2)
|
|
414
|
+
|
|
415
|
+
notifications = recorder.select_notifications(start=1, limit=10, stop=2)
|
|
416
|
+
self.assertEqual(len(notifications), 2, len(notifications))
|
|
417
|
+
self.assertEqual(notifications[0].id, 1)
|
|
418
|
+
self.assertEqual(notifications[1].id, 2)
|
|
380
419
|
|
|
381
420
|
notifications = recorder.select_notifications(
|
|
382
|
-
start=
|
|
421
|
+
start=1, limit=10, stop=2, inclusive_of_start=False
|
|
383
422
|
)
|
|
384
|
-
self.assertEqual(len(notifications),
|
|
385
|
-
self.assertEqual(notifications[0].id,
|
|
386
|
-
self.assertEqual(notifications[1].id, max_notification_id + 2)
|
|
423
|
+
self.assertEqual(len(notifications), 1, len(notifications))
|
|
424
|
+
self.assertEqual(notifications[0].id, 2)
|
|
387
425
|
|
|
388
426
|
def test_concurrent_no_conflicts(self) -> None:
|
|
389
427
|
print(self)
|
|
@@ -430,7 +468,7 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
430
468
|
try:
|
|
431
469
|
recorder.insert_events(stored_events)
|
|
432
470
|
|
|
433
|
-
except Exception as e: # pragma:
|
|
471
|
+
except Exception as e: # pragma: no cover
|
|
434
472
|
if errors:
|
|
435
473
|
return
|
|
436
474
|
ended = datetime.now()
|
|
@@ -451,7 +489,7 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
451
489
|
while not stop_reading.is_set():
|
|
452
490
|
try:
|
|
453
491
|
recorder.select_notifications(0, 10)
|
|
454
|
-
except Exception as e: # pragma:
|
|
492
|
+
except Exception as e: # pragma: no cover
|
|
455
493
|
errors.append(e)
|
|
456
494
|
return
|
|
457
495
|
# else:
|
|
@@ -466,22 +504,22 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
466
504
|
with ThreadPoolExecutor(max_workers=num_writers) as executor:
|
|
467
505
|
futures = []
|
|
468
506
|
for _ in range(num_writes_per_writer):
|
|
469
|
-
if errors: # pragma:
|
|
507
|
+
if errors: # pragma: no cover
|
|
470
508
|
break
|
|
471
509
|
future = executor.submit(insert_events)
|
|
472
510
|
futures.append(future)
|
|
473
511
|
for future in futures:
|
|
474
|
-
if errors: # pragma:
|
|
512
|
+
if errors: # pragma: no cover
|
|
475
513
|
break
|
|
476
514
|
try:
|
|
477
515
|
future.result()
|
|
478
|
-
except Exception as e: # pragma:
|
|
516
|
+
except Exception as e: # pragma: no cover
|
|
479
517
|
errors.append(e)
|
|
480
518
|
break
|
|
481
519
|
|
|
482
520
|
stop_reading.set()
|
|
483
521
|
|
|
484
|
-
if errors: # pragma:
|
|
522
|
+
if errors: # pragma: no cover
|
|
485
523
|
raise errors[0]
|
|
486
524
|
|
|
487
525
|
for thread_id, thread_num in threads.items():
|
|
@@ -529,7 +567,7 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
529
567
|
try:
|
|
530
568
|
recorder.insert_events(stored_events)
|
|
531
569
|
|
|
532
|
-
except Exception: # pragma:
|
|
570
|
+
except Exception: # pragma: no cover
|
|
533
571
|
errors_happened.set()
|
|
534
572
|
tb = traceback.format_exc()
|
|
535
573
|
print(tb)
|
|
@@ -555,10 +593,174 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
555
593
|
rate = num_jobs * num_events / (ended - started).total_seconds()
|
|
556
594
|
print(f"Rate: {rate:.0f} inserts per second")
|
|
557
595
|
|
|
596
|
+
def optional_test_insert_subscribe(self) -> None:
|
|
597
|
+
|
|
598
|
+
recorder = self.create_recorder()
|
|
599
|
+
|
|
600
|
+
# Get the max notification ID.
|
|
601
|
+
max_notification_id1 = recorder.max_notification_id()
|
|
602
|
+
|
|
603
|
+
# Write two stored events.
|
|
604
|
+
originator_id1 = uuid4()
|
|
605
|
+
originator_id2 = uuid4()
|
|
606
|
+
|
|
607
|
+
stored_event1 = StoredEvent(
|
|
608
|
+
originator_id=originator_id1,
|
|
609
|
+
originator_version=self.INITIAL_VERSION,
|
|
610
|
+
topic="topic1",
|
|
611
|
+
state=b"state1",
|
|
612
|
+
)
|
|
613
|
+
stored_event2 = StoredEvent(
|
|
614
|
+
originator_id=originator_id1,
|
|
615
|
+
originator_version=self.INITIAL_VERSION + 1,
|
|
616
|
+
topic="topic2",
|
|
617
|
+
state=b"state2",
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
notification_ids = recorder.insert_events([stored_event1, stored_event2])
|
|
621
|
+
if self.EXPECT_CONTIGUOUS_NOTIFICATION_IDS:
|
|
622
|
+
self.assertEqual(notification_ids, [1, 2])
|
|
623
|
+
|
|
624
|
+
# Get the max notification ID.
|
|
625
|
+
max_notification_id2 = recorder.max_notification_id()
|
|
626
|
+
|
|
627
|
+
# Start a subscription with default value for 'start'.
|
|
628
|
+
with recorder.subscribe() as subscription:
|
|
629
|
+
|
|
630
|
+
# Receive events from the subscription.
|
|
631
|
+
for _ in subscription:
|
|
632
|
+
break
|
|
633
|
+
|
|
634
|
+
# Start a subscription with None value for 'start'.
|
|
635
|
+
with recorder.subscribe(gt=None) as subscription:
|
|
636
|
+
|
|
637
|
+
# Receive events from the subscription.
|
|
638
|
+
for _ in subscription:
|
|
639
|
+
break
|
|
640
|
+
|
|
641
|
+
# Start a subscription with int value for 'start'.
|
|
642
|
+
with recorder.subscribe(gt=max_notification_id1) as subscription:
|
|
643
|
+
|
|
644
|
+
# Receive events from the subscription.
|
|
645
|
+
notifications: List[Notification] = []
|
|
646
|
+
for notification in subscription:
|
|
647
|
+
notifications.append(notification)
|
|
648
|
+
if len(notifications) == 2:
|
|
649
|
+
break
|
|
650
|
+
|
|
651
|
+
# Check the events we received are the ones that were written.
|
|
652
|
+
self.assertEqual(
|
|
653
|
+
stored_event1.originator_id, notifications[0].originator_id
|
|
654
|
+
)
|
|
655
|
+
self.assertEqual(
|
|
656
|
+
stored_event1.originator_version, notifications[0].originator_version
|
|
657
|
+
)
|
|
658
|
+
self.assertEqual(
|
|
659
|
+
stored_event2.originator_id, notifications[1].originator_id
|
|
660
|
+
)
|
|
661
|
+
self.assertEqual(
|
|
662
|
+
stored_event2.originator_version, notifications[1].originator_version
|
|
663
|
+
)
|
|
664
|
+
if self.EXPECT_CONTIGUOUS_NOTIFICATION_IDS:
|
|
665
|
+
self.assertEqual(1, notifications[0].id)
|
|
666
|
+
self.assertEqual(2, notifications[1].id)
|
|
667
|
+
|
|
668
|
+
# Store a third event.
|
|
669
|
+
stored_event3 = StoredEvent(
|
|
670
|
+
originator_id=originator_id2,
|
|
671
|
+
originator_version=self.INITIAL_VERSION,
|
|
672
|
+
topic="topic3",
|
|
673
|
+
state=b"state3",
|
|
674
|
+
)
|
|
675
|
+
notification_ids = recorder.insert_events([stored_event3])
|
|
676
|
+
if self.EXPECT_CONTIGUOUS_NOTIFICATION_IDS:
|
|
677
|
+
self.assertEqual(notification_ids, [3])
|
|
678
|
+
|
|
679
|
+
# Receive events from the subscription.
|
|
680
|
+
for notification in subscription:
|
|
681
|
+
notifications.append(notification)
|
|
682
|
+
if len(notifications) == 3:
|
|
683
|
+
break
|
|
684
|
+
|
|
685
|
+
# Check the events we received are the ones that were written.
|
|
686
|
+
self.assertEqual(
|
|
687
|
+
stored_event3.originator_id, notifications[2].originator_id
|
|
688
|
+
)
|
|
689
|
+
self.assertEqual(
|
|
690
|
+
stored_event3.originator_version, notifications[2].originator_version
|
|
691
|
+
)
|
|
692
|
+
if self.EXPECT_CONTIGUOUS_NOTIFICATION_IDS:
|
|
693
|
+
self.assertEqual(3, notifications[2].id)
|
|
694
|
+
|
|
695
|
+
# Start a subscription with int value for 'start'.
|
|
696
|
+
with recorder.subscribe(gt=max_notification_id2) as subscription:
|
|
697
|
+
|
|
698
|
+
# Receive events from the subscription.
|
|
699
|
+
notifications: List[Notification] = []
|
|
700
|
+
for notification in subscription:
|
|
701
|
+
notifications.append(notification)
|
|
702
|
+
if len(notifications) == 1:
|
|
703
|
+
break
|
|
704
|
+
|
|
705
|
+
# Check the events we received are the ones that were written.
|
|
706
|
+
self.assertEqual(
|
|
707
|
+
stored_event3.originator_id, notifications[0].originator_id
|
|
708
|
+
)
|
|
709
|
+
|
|
558
710
|
def close_db_connection(self, *args: Any) -> None:
|
|
559
711
|
""""""
|
|
560
712
|
|
|
561
713
|
|
|
714
|
+
class TrackingRecorderTestCase(TestCase, ABC):
|
|
715
|
+
@abstractmethod
|
|
716
|
+
def create_recorder(self) -> ProcessRecorder:
|
|
717
|
+
""""""
|
|
718
|
+
|
|
719
|
+
def test_insert_tracking(self):
|
|
720
|
+
tracking_recorder = self.create_recorder()
|
|
721
|
+
|
|
722
|
+
# Construct tracking objects.
|
|
723
|
+
tracking1 = Tracking(notification_id=21, application_name="upstream1")
|
|
724
|
+
tracking2 = Tracking(notification_id=22, application_name="upstream1")
|
|
725
|
+
tracking3 = Tracking(notification_id=21, application_name="upstream2")
|
|
726
|
+
|
|
727
|
+
# Insert tracking objects.
|
|
728
|
+
tracking_recorder.insert_tracking(tracking=tracking1)
|
|
729
|
+
tracking_recorder.insert_tracking(tracking=tracking2)
|
|
730
|
+
tracking_recorder.insert_tracking(tracking=tracking3)
|
|
731
|
+
|
|
732
|
+
# Fail to insert same tracking object twice.
|
|
733
|
+
with self.assertRaises(IntegrityError):
|
|
734
|
+
tracking_recorder.insert_tracking(tracking=tracking1)
|
|
735
|
+
with self.assertRaises(IntegrityError):
|
|
736
|
+
tracking_recorder.insert_tracking(tracking=tracking2)
|
|
737
|
+
with self.assertRaises(IntegrityError):
|
|
738
|
+
tracking_recorder.insert_tracking(tracking=tracking3)
|
|
739
|
+
|
|
740
|
+
# Get latest tracked position.
|
|
741
|
+
self.assertEqual(tracking_recorder.max_tracking_id("upstream1"), 22)
|
|
742
|
+
self.assertEqual(tracking_recorder.max_tracking_id("upstream2"), 21)
|
|
743
|
+
self.assertIsNone(tracking_recorder.max_tracking_id("upstream3"))
|
|
744
|
+
|
|
745
|
+
# Check if an event notification has been processed.
|
|
746
|
+
assert tracking_recorder.has_tracking_id("upstream1", 21)
|
|
747
|
+
assert tracking_recorder.has_tracking_id("upstream1", 22)
|
|
748
|
+
assert tracking_recorder.has_tracking_id("upstream2", 21)
|
|
749
|
+
assert not tracking_recorder.has_tracking_id("upstream2", 22)
|
|
750
|
+
|
|
751
|
+
def test_wait(self):
|
|
752
|
+
tracking_recorder = self.create_recorder()
|
|
753
|
+
tracking1 = Tracking(notification_id=21, application_name="upstream1")
|
|
754
|
+
tracking_recorder.insert_tracking(tracking=tracking1)
|
|
755
|
+
tracking_recorder.wait("upstream1", 21)
|
|
756
|
+
with self.assertRaises(TimeoutError):
|
|
757
|
+
tracking_recorder.wait("upstream1", 22, timeout=0.1)
|
|
758
|
+
with self.assertRaises(WaitInterruptedError):
|
|
759
|
+
interrupt = Event()
|
|
760
|
+
interrupt.set()
|
|
761
|
+
tracking_recorder.wait("upstream1", 22, interrupt=interrupt)
|
|
762
|
+
|
|
763
|
+
|
|
562
764
|
class ProcessRecorderTestCase(TestCase, ABC):
|
|
563
765
|
@abstractmethod
|
|
564
766
|
def create_recorder(self) -> ProcessRecorder:
|
|
@@ -569,10 +771,7 @@ class ProcessRecorderTestCase(TestCase, ABC):
|
|
|
569
771
|
recorder = self.create_recorder()
|
|
570
772
|
|
|
571
773
|
# Get current position.
|
|
572
|
-
self.
|
|
573
|
-
recorder.max_tracking_id("upstream_app"),
|
|
574
|
-
0,
|
|
575
|
-
)
|
|
774
|
+
self.assertIsNone(recorder.max_tracking_id("upstream_app"))
|
|
576
775
|
|
|
577
776
|
# Write two stored events.
|
|
578
777
|
originator_id1 = uuid4()
|
|
@@ -775,7 +974,9 @@ class NonInterleavingNotificationIDsBaseCase(ABC, TestCase):
|
|
|
775
974
|
|
|
776
975
|
sleep(1) # Added to make eventsourcing-axon tests work, perhaps not necessary.
|
|
777
976
|
notifications = recorder.select_notifications(
|
|
778
|
-
start=max_notification_id
|
|
977
|
+
start=max_notification_id,
|
|
978
|
+
limit=2 * self.insert_num,
|
|
979
|
+
inclusive_of_start=False,
|
|
779
980
|
)
|
|
780
981
|
ids_for_sequence1 = [
|
|
781
982
|
e.id for e in notifications if e.originator_id == originator1_id
|
|
@@ -827,6 +1028,14 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
827
1028
|
def expected_application_recorder_class(self):
|
|
828
1029
|
pass
|
|
829
1030
|
|
|
1031
|
+
@abstractmethod
|
|
1032
|
+
def expected_tracking_recorder_class(self):
|
|
1033
|
+
pass
|
|
1034
|
+
|
|
1035
|
+
@abstractmethod
|
|
1036
|
+
def tracking_recorder_subclass(self):
|
|
1037
|
+
pass
|
|
1038
|
+
|
|
830
1039
|
@abstractmethod
|
|
831
1040
|
def expected_process_recorder_class(self):
|
|
832
1041
|
pass
|
|
@@ -978,6 +1187,26 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
978
1187
|
recorder = self.factory.application_recorder()
|
|
979
1188
|
self.assertEqual(type(recorder), self.expected_application_recorder_class())
|
|
980
1189
|
|
|
1190
|
+
def test_create_tracking_recorder(self):
|
|
1191
|
+
recorder = self.factory.tracking_recorder()
|
|
1192
|
+
self.assertEqual(type(recorder), self.expected_tracking_recorder_class())
|
|
1193
|
+
self.assertIsInstance(recorder, TrackingRecorder)
|
|
1194
|
+
|
|
1195
|
+
# Exercise code path where table is not created.
|
|
1196
|
+
self.env["CREATE_TABLE"] = "f"
|
|
1197
|
+
recorder = self.factory.tracking_recorder()
|
|
1198
|
+
self.assertEqual(type(recorder), self.expected_tracking_recorder_class())
|
|
1199
|
+
|
|
1200
|
+
# Exercise code path where tracking recorder class is specified as arg.
|
|
1201
|
+
subclass = self.tracking_recorder_subclass()
|
|
1202
|
+
recorder = self.factory.tracking_recorder(subclass)
|
|
1203
|
+
self.assertEqual(type(recorder), subclass)
|
|
1204
|
+
|
|
1205
|
+
# Exercise code path where tracking recorder class is specified as topic.
|
|
1206
|
+
self.factory.env[self.factory.TRACKING_RECORDER_TOPIC] = get_topic(subclass)
|
|
1207
|
+
recorder = self.factory.tracking_recorder()
|
|
1208
|
+
self.assertEqual(type(recorder), subclass)
|
|
1209
|
+
|
|
981
1210
|
def test_create_process_recorder(self):
|
|
982
1211
|
recorder = self.factory.process_recorder()
|
|
983
1212
|
self.assertEqual(type(recorder), self.expected_process_recorder_class())
|
eventsourcing/utils.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: eventsourcing
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.4.0a1
|
|
4
4
|
Summary: Event sourcing in Python
|
|
5
5
|
Home-page: https://github.com/pyeventsourcing/eventsourcing
|
|
6
6
|
License: BSD 3-Clause
|
|
@@ -8,7 +8,7 @@ Keywords: event sourcing,event store,domain driven design,domain-driven design,d
|
|
|
8
8
|
Author: John Bywater
|
|
9
9
|
Author-email: john.bywater@appropriatesoftware.net
|
|
10
10
|
Requires-Python: >=3.8,<4.0
|
|
11
|
-
Classifier: Development Status ::
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: Intended Audience :: Education
|
|
14
14
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
eventsourcing/__init__.py,sha256=st2H3shrhTk5rqoUeZHUW8XD9iOX9tGGtQFWr2HGYmo,26
|
|
2
|
+
eventsourcing/application.py,sha256=YuZEE7le3GX3kVBje16stGziSIoUt6pUEBdlCDwkXf8,37847
|
|
3
|
+
eventsourcing/cipher.py,sha256=S43YMycOCoEnzvWSanClv_MzMwDU5nBu5RU72G8BpYA,3201
|
|
4
|
+
eventsourcing/compressor.py,sha256=IdvrJUB9B2td871oifInv4lGXmHwYL9d69MbHHCr7uI,421
|
|
5
|
+
eventsourcing/dispatch.py,sha256=yYSpT-jqc6l_wTdqEnfPJJfvsZN2Ta8g2anrVPWIcqQ,1412
|
|
6
|
+
eventsourcing/domain.py,sha256=b25jDhgP0aWsSEXXOxzN7plLi0TmDpoW7iZ5UHLTd8w,58339
|
|
7
|
+
eventsourcing/interface.py,sha256=LIFI9AZhoVWUAq4YjKosGCpinf51jmVLqw1Ii4npSHo,5079
|
|
8
|
+
eventsourcing/persistence.py,sha256=q4iJ_mFWiHgWVuJHYScXZQPvVMIdl-FvRlBruYH9SKE,45752
|
|
9
|
+
eventsourcing/popo.py,sha256=FQmD7zJXoIyt3t-4RFADQ7UAhGWo1c7Cp0AkTQUjIW8,9413
|
|
10
|
+
eventsourcing/postgres.py,sha256=tOCHY1xCMzEwDZ_JOMtWS5C8OXsbCrBOYaX-fz1oxA4,36158
|
|
11
|
+
eventsourcing/projection.py,sha256=I1YrhMt5TyLdZru4ojtppo1W1r8fTrFZJ3VB61lzk-s,5158
|
|
12
|
+
eventsourcing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
eventsourcing/sqlite.py,sha256=pbiuBU8x1J5uJqjqB1P9LshzDQqINYaZ-oBaYYNvo2s,21929
|
|
14
|
+
eventsourcing/system.py,sha256=8FaI7LlwiAQGP2dULFbDwby30eUpnAePcOdmqGlm4EQ,47134
|
|
15
|
+
eventsourcing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
eventsourcing/tests/application.py,sha256=s7GA3FEi6IV_wHk2lNZKJ25A1T3PkkfJo4AVVW6-cnc,19009
|
|
17
|
+
eventsourcing/tests/domain.py,sha256=lHSlY6jIoSeqlcPSbrrozEPUJGvJ8bgPrznlmzTxn2w,3254
|
|
18
|
+
eventsourcing/tests/persistence.py,sha256=uink4W_CjeAlaSfzqRBuaDH2fYKAlZ1rvL7lhyUobp8,55553
|
|
19
|
+
eventsourcing/tests/postgres_utils.py,sha256=xymcGYasUXeZTBenkHz-ykD8HtrFjVM1Z7-qRrH6OQk,1364
|
|
20
|
+
eventsourcing/utils.py,sha256=cGS_R5gZt9mp9Sj4qzXwJKvDkVvRUcFyQbUQfNLoy4M,8248
|
|
21
|
+
eventsourcing-9.4.0a1.dist-info/AUTHORS,sha256=8aHOM4UbNZcKlD-cHpFRcM6RWyCqtwtxRev6DeUgVRs,137
|
|
22
|
+
eventsourcing-9.4.0a1.dist-info/LICENSE,sha256=CQEQzcZO8AWXL5i3hIo4yVKrYjh2FBz6hCM7kpXWpw4,1512
|
|
23
|
+
eventsourcing-9.4.0a1.dist-info/METADATA,sha256=HvyZX_hguCQdfvYovDJLfkgm2YMTNl3nDmCX_21Q0e8,9724
|
|
24
|
+
eventsourcing-9.4.0a1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
25
|
+
eventsourcing-9.4.0a1.dist-info/RECORD,,
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
eventsourcing/__init__.py,sha256=st2H3shrhTk5rqoUeZHUW8XD9iOX9tGGtQFWr2HGYmo,26
|
|
2
|
-
eventsourcing/application.py,sha256=c5aTi86mBOiQicmqEUsqseAeL7CwDie3G9e0Y8kWLYM,36324
|
|
3
|
-
eventsourcing/cipher.py,sha256=NJcVfZdSlCER6xryM4zVoY3cmKstF-iSSniB4jmpaKg,3200
|
|
4
|
-
eventsourcing/compressor.py,sha256=IdvrJUB9B2td871oifInv4lGXmHwYL9d69MbHHCr7uI,421
|
|
5
|
-
eventsourcing/dispatch.py,sha256=yYSpT-jqc6l_wTdqEnfPJJfvsZN2Ta8g2anrVPWIcqQ,1412
|
|
6
|
-
eventsourcing/domain.py,sha256=rvm4Sv2MmLcha8_5wqJ13AjmqyWvuzkquYsexUaePIg,57264
|
|
7
|
-
eventsourcing/interface.py,sha256=KzDWLeIkREf-TAFl5AFHtKJwJFA-IthqMKClFkUFqdc,4676
|
|
8
|
-
eventsourcing/persistence.py,sha256=TJseAtsWwdC33XLvcoyHdgwTv6s6KsvQS8XLKIq472s,37672
|
|
9
|
-
eventsourcing/popo.py,sha256=AApSGneHuXa8yHOWdDfsFTMVDI-9ivEpuKTX1BSOXr8,6547
|
|
10
|
-
eventsourcing/postgres.py,sha256=NatfdA-YQQPB1qWsQg23Uhyy6R-hcGNOJHJ1bKmFuKQ,29772
|
|
11
|
-
eventsourcing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
eventsourcing/sqlite.py,sha256=jz7SZk26Gcsjw88KL1xnr4-1tStTW16f1NR6TjMsZnQ,18466
|
|
13
|
-
eventsourcing/system.py,sha256=il05rAzPtKaxragIktMBOEyWGhz16VK0F7cEBlsl4_Q,45561
|
|
14
|
-
eventsourcing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
eventsourcing/tests/application.py,sha256=wBanhWzAZvL3fYxCFe5cloN-Up7uLw6KcdRQ2dhKVAg,17586
|
|
16
|
-
eventsourcing/tests/domain.py,sha256=lHSlY6jIoSeqlcPSbrrozEPUJGvJ8bgPrznlmzTxn2w,3254
|
|
17
|
-
eventsourcing/tests/persistence.py,sha256=giLWEZDdViXY8_SHCJerJ0sYVbJUP3UkLKaEbel9sqs,45777
|
|
18
|
-
eventsourcing/tests/postgres_utils.py,sha256=xymcGYasUXeZTBenkHz-ykD8HtrFjVM1Z7-qRrH6OQk,1364
|
|
19
|
-
eventsourcing/utils.py,sha256=PIWDvoGiKCXsNbR5DirkoJ_svopg80SoH37bqxOcjkU,8247
|
|
20
|
-
eventsourcing-9.3.4.dist-info/AUTHORS,sha256=8aHOM4UbNZcKlD-cHpFRcM6RWyCqtwtxRev6DeUgVRs,137
|
|
21
|
-
eventsourcing-9.3.4.dist-info/LICENSE,sha256=bSE_F-T6cQPmMY5LuJC27km_pGB1XCVuUFx1uY0Nueg,1512
|
|
22
|
-
eventsourcing-9.3.4.dist-info/METADATA,sha256=zGQTtZA_1u5kZOUW7wy5a86JHQoVU4j5tJR1OaIaLFo,9734
|
|
23
|
-
eventsourcing-9.3.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
24
|
-
eventsourcing-9.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|