eventsourcing 9.3.3__py3-none-any.whl → 9.3.5__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 +0 -15
- eventsourcing/system.py +25 -2
- {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/METADATA +1 -1
- eventsourcing-9.3.5.dist-info/RECORD +24 -0
- eventsourcing/examples/__init__.py +0 -0
- eventsourcing/examples/aggregate1/__init__.py +0 -0
- eventsourcing/examples/aggregate1/application.py +0 -27
- eventsourcing/examples/aggregate1/domainmodel.py +0 -16
- eventsourcing/examples/aggregate1/test_application.py +0 -37
- eventsourcing/examples/aggregate2/__init__.py +0 -0
- eventsourcing/examples/aggregate2/application.py +0 -27
- eventsourcing/examples/aggregate2/domainmodel.py +0 -22
- eventsourcing/examples/aggregate2/test_application.py +0 -37
- eventsourcing/examples/aggregate3/__init__.py +0 -0
- eventsourcing/examples/aggregate3/application.py +0 -27
- eventsourcing/examples/aggregate3/domainmodel.py +0 -38
- eventsourcing/examples/aggregate3/test_application.py +0 -37
- eventsourcing/examples/aggregate4/__init__.py +0 -0
- eventsourcing/examples/aggregate4/application.py +0 -27
- eventsourcing/examples/aggregate4/domainmodel.py +0 -114
- eventsourcing/examples/aggregate4/test_application.py +0 -38
- eventsourcing/examples/aggregate5/__init__.py +0 -0
- eventsourcing/examples/aggregate5/application.py +0 -27
- eventsourcing/examples/aggregate5/domainmodel.py +0 -131
- eventsourcing/examples/aggregate5/test_application.py +0 -38
- eventsourcing/examples/aggregate6/__init__.py +0 -0
- eventsourcing/examples/aggregate6/application.py +0 -30
- eventsourcing/examples/aggregate6/domainmodel.py +0 -123
- eventsourcing/examples/aggregate6/test_application.py +0 -38
- eventsourcing/examples/aggregate6a/__init__.py +0 -0
- eventsourcing/examples/aggregate6a/application.py +0 -40
- eventsourcing/examples/aggregate6a/domainmodel.py +0 -149
- eventsourcing/examples/aggregate6a/test_application.py +0 -45
- eventsourcing/examples/aggregate7/__init__.py +0 -0
- eventsourcing/examples/aggregate7/application.py +0 -53
- eventsourcing/examples/aggregate7/domainmodel.py +0 -142
- eventsourcing/examples/aggregate7/persistence.py +0 -57
- eventsourcing/examples/aggregate7/test_application.py +0 -45
- eventsourcing/examples/aggregate7/test_compression_and_encryption.py +0 -45
- eventsourcing/examples/aggregate7/test_snapshotting_intervals.py +0 -67
- eventsourcing/examples/aggregate7a/__init__.py +0 -0
- eventsourcing/examples/aggregate7a/application.py +0 -56
- eventsourcing/examples/aggregate7a/domainmodel.py +0 -168
- eventsourcing/examples/aggregate7a/test_application.py +0 -46
- eventsourcing/examples/aggregate7a/test_compression_and_encryption.py +0 -45
- eventsourcing/examples/aggregate8/__init__.py +0 -0
- eventsourcing/examples/aggregate8/application.py +0 -47
- eventsourcing/examples/aggregate8/domainmodel.py +0 -71
- eventsourcing/examples/aggregate8/persistence.py +0 -57
- eventsourcing/examples/aggregate8/test_application.py +0 -44
- eventsourcing/examples/aggregate8/test_compression_and_encryption.py +0 -44
- eventsourcing/examples/aggregate8/test_snapshotting_intervals.py +0 -38
- eventsourcing/examples/bankaccounts/__init__.py +0 -0
- eventsourcing/examples/bankaccounts/application.py +0 -70
- eventsourcing/examples/bankaccounts/domainmodel.py +0 -56
- eventsourcing/examples/bankaccounts/test.py +0 -173
- eventsourcing/examples/cargoshipping/__init__.py +0 -0
- eventsourcing/examples/cargoshipping/application.py +0 -126
- eventsourcing/examples/cargoshipping/domainmodel.py +0 -330
- eventsourcing/examples/cargoshipping/interface.py +0 -143
- eventsourcing/examples/cargoshipping/test.py +0 -231
- eventsourcing/examples/contentmanagement/__init__.py +0 -0
- eventsourcing/examples/contentmanagement/application.py +0 -118
- eventsourcing/examples/contentmanagement/domainmodel.py +0 -69
- eventsourcing/examples/contentmanagement/test.py +0 -180
- eventsourcing/examples/contentmanagement/utils.py +0 -26
- eventsourcing/examples/contentmanagementsystem/__init__.py +0 -0
- eventsourcing/examples/contentmanagementsystem/application.py +0 -54
- eventsourcing/examples/contentmanagementsystem/postgres.py +0 -17
- eventsourcing/examples/contentmanagementsystem/sqlite.py +0 -17
- eventsourcing/examples/contentmanagementsystem/system.py +0 -14
- eventsourcing/examples/contentmanagementsystem/test_system.py +0 -180
- eventsourcing/examples/searchablecontent/__init__.py +0 -0
- eventsourcing/examples/searchablecontent/application.py +0 -45
- eventsourcing/examples/searchablecontent/persistence.py +0 -23
- eventsourcing/examples/searchablecontent/postgres.py +0 -118
- eventsourcing/examples/searchablecontent/sqlite.py +0 -136
- eventsourcing/examples/searchablecontent/test_application.py +0 -110
- eventsourcing/examples/searchablecontent/test_recorder.py +0 -68
- eventsourcing/examples/searchabletimestamps/__init__.py +0 -0
- eventsourcing/examples/searchabletimestamps/application.py +0 -32
- eventsourcing/examples/searchabletimestamps/persistence.py +0 -20
- eventsourcing/examples/searchabletimestamps/postgres.py +0 -110
- eventsourcing/examples/searchabletimestamps/sqlite.py +0 -99
- eventsourcing/examples/searchabletimestamps/test_searchabletimestamps.py +0 -94
- eventsourcing/examples/test_invoice.py +0 -176
- eventsourcing/examples/test_parking_lot.py +0 -206
- eventsourcing/tests/application_tests/__init__.py +0 -0
- eventsourcing/tests/application_tests/test_application_with_automatic_snapshotting.py +0 -55
- eventsourcing/tests/application_tests/test_application_with_popo.py +0 -22
- eventsourcing/tests/application_tests/test_application_with_postgres.py +0 -75
- eventsourcing/tests/application_tests/test_application_with_sqlite.py +0 -72
- eventsourcing/tests/application_tests/test_cache.py +0 -134
- eventsourcing/tests/application_tests/test_event_sourced_log.py +0 -162
- eventsourcing/tests/application_tests/test_notificationlog.py +0 -232
- eventsourcing/tests/application_tests/test_notificationlogreader.py +0 -126
- eventsourcing/tests/application_tests/test_processapplication.py +0 -110
- eventsourcing/tests/application_tests/test_processingpolicy.py +0 -109
- eventsourcing/tests/application_tests/test_repository.py +0 -504
- eventsourcing/tests/application_tests/test_snapshotting.py +0 -68
- eventsourcing/tests/application_tests/test_upcasting.py +0 -459
- eventsourcing/tests/docs_tests/__init__.py +0 -0
- eventsourcing/tests/docs_tests/test_docs.py +0 -293
- eventsourcing/tests/domain_tests/__init__.py +0 -0
- eventsourcing/tests/domain_tests/test_aggregate.py +0 -1200
- eventsourcing/tests/domain_tests/test_aggregate_decorators.py +0 -1604
- eventsourcing/tests/domain_tests/test_domainevent.py +0 -80
- eventsourcing/tests/interface_tests/__init__.py +0 -0
- eventsourcing/tests/interface_tests/test_remotenotificationlog.py +0 -258
- eventsourcing/tests/persistence_tests/__init__.py +0 -0
- eventsourcing/tests/persistence_tests/test_aes.py +0 -93
- eventsourcing/tests/persistence_tests/test_connection_pool.py +0 -722
- eventsourcing/tests/persistence_tests/test_eventstore.py +0 -72
- eventsourcing/tests/persistence_tests/test_infrastructure_factory.py +0 -21
- eventsourcing/tests/persistence_tests/test_mapper.py +0 -113
- eventsourcing/tests/persistence_tests/test_noninterleaving_notification_ids.py +0 -69
- eventsourcing/tests/persistence_tests/test_popo.py +0 -124
- eventsourcing/tests/persistence_tests/test_postgres.py +0 -1120
- eventsourcing/tests/persistence_tests/test_sqlite.py +0 -348
- eventsourcing/tests/persistence_tests/test_transcoder.py +0 -44
- eventsourcing/tests/system_tests/__init__.py +0 -0
- eventsourcing/tests/system_tests/test_runner.py +0 -935
- eventsourcing/tests/system_tests/test_system.py +0 -284
- eventsourcing/tests/utils_tests/__init__.py +0 -0
- eventsourcing/tests/utils_tests/test_utils.py +0 -226
- eventsourcing-9.3.3.dist-info/RECORD +0 -145
- {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/AUTHORS +0 -0
- {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/LICENSE +0 -0
- {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/WHEEL +0 -0
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
from unittest import TestCase
|
|
5
|
-
from uuid import NAMESPACE_URL, UUID, uuid4, uuid5
|
|
6
|
-
|
|
7
|
-
from eventsourcing.application import Application, EventSourcedLog
|
|
8
|
-
from eventsourcing.domain import Aggregate, DomainEvent
|
|
9
|
-
from eventsourcing.persistence import (
|
|
10
|
-
DatetimeAsISO,
|
|
11
|
-
DecimalAsStr,
|
|
12
|
-
EventStore,
|
|
13
|
-
JSONTranscoder,
|
|
14
|
-
Mapper,
|
|
15
|
-
UUIDAsHex,
|
|
16
|
-
)
|
|
17
|
-
from eventsourcing.popo import POPOAggregateRecorder
|
|
18
|
-
|
|
19
|
-
if TYPE_CHECKING: # pragma: nocover
|
|
20
|
-
from eventsourcing.utils import EnvType
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class TestEventSourcedLog(TestCase):
|
|
24
|
-
def test_logging_aggregate_ids(self) -> None:
|
|
25
|
-
class LoggedID(DomainEvent):
|
|
26
|
-
aggregate_id: UUID
|
|
27
|
-
|
|
28
|
-
transcoder = JSONTranscoder()
|
|
29
|
-
transcoder.register(UUIDAsHex())
|
|
30
|
-
transcoder.register(DecimalAsStr())
|
|
31
|
-
transcoder.register(DatetimeAsISO())
|
|
32
|
-
|
|
33
|
-
event_recorder = POPOAggregateRecorder()
|
|
34
|
-
event_store = EventStore(
|
|
35
|
-
mapper=Mapper(transcoder=transcoder),
|
|
36
|
-
recorder=event_recorder,
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
log: EventSourcedLog[LoggedID] = EventSourcedLog(
|
|
40
|
-
events=event_store,
|
|
41
|
-
originator_id=uuid5(NAMESPACE_URL, "/aggregates"),
|
|
42
|
-
logged_cls=LoggedID,
|
|
43
|
-
)
|
|
44
|
-
id1 = uuid4()
|
|
45
|
-
id2 = uuid4()
|
|
46
|
-
id3 = uuid4()
|
|
47
|
-
|
|
48
|
-
self.assertEqual(log.get_first(), None)
|
|
49
|
-
self.assertEqual(log.get_last(), None)
|
|
50
|
-
logged = log.trigger_event(aggregate_id=id1)
|
|
51
|
-
event_store.put([logged])
|
|
52
|
-
first = log.get_first()
|
|
53
|
-
assert first
|
|
54
|
-
self.assertEqual(first.aggregate_id, id1)
|
|
55
|
-
last = log.get_last()
|
|
56
|
-
assert last
|
|
57
|
-
self.assertEqual(last.aggregate_id, id1)
|
|
58
|
-
logged = log.trigger_event(aggregate_id=id2)
|
|
59
|
-
event_store.put([logged])
|
|
60
|
-
last = log.get_last()
|
|
61
|
-
assert last
|
|
62
|
-
self.assertEqual(last.aggregate_id, id2)
|
|
63
|
-
logged = log.trigger_event(aggregate_id=id3, next_originator_version=3)
|
|
64
|
-
event_store.put([logged])
|
|
65
|
-
last = log.get_last()
|
|
66
|
-
assert last
|
|
67
|
-
self.assertEqual(last.aggregate_id, id3)
|
|
68
|
-
first = log.get_first()
|
|
69
|
-
assert first
|
|
70
|
-
self.assertEqual(first.aggregate_id, id1)
|
|
71
|
-
|
|
72
|
-
ids = [e.aggregate_id for e in log.get()]
|
|
73
|
-
self.assertEqual(ids, [id1, id2, id3])
|
|
74
|
-
|
|
75
|
-
ids = [e.aggregate_id for e in log.get(gt=1)]
|
|
76
|
-
self.assertEqual(ids, [id2, id3])
|
|
77
|
-
|
|
78
|
-
ids = [e.aggregate_id for e in log.get(lte=2)]
|
|
79
|
-
self.assertEqual(ids, [id1, id2])
|
|
80
|
-
|
|
81
|
-
ids = [e.aggregate_id for e in log.get(limit=1)]
|
|
82
|
-
self.assertEqual(ids, [id1])
|
|
83
|
-
|
|
84
|
-
ids = [e.aggregate_id for e in log.get(desc=True)]
|
|
85
|
-
self.assertEqual(ids, [id3, id2, id1])
|
|
86
|
-
|
|
87
|
-
def test_with_application(self) -> None:
|
|
88
|
-
class LoggedID(DomainEvent):
|
|
89
|
-
aggregate_id: UUID
|
|
90
|
-
|
|
91
|
-
class MyApplication(Application):
|
|
92
|
-
def __init__(self, env: EnvType | None = None) -> None:
|
|
93
|
-
super().__init__(env=env)
|
|
94
|
-
self.aggregate_log = EventSourcedLog(
|
|
95
|
-
events=self.events,
|
|
96
|
-
originator_id=uuid5(NAMESPACE_URL, "/aggregates"),
|
|
97
|
-
logged_cls=LoggedID,
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
def create_aggregate(self) -> UUID:
|
|
101
|
-
aggregate = Aggregate()
|
|
102
|
-
logged_id = self.aggregate_log.trigger_event(aggregate_id=aggregate.id)
|
|
103
|
-
self.save(aggregate, logged_id)
|
|
104
|
-
return aggregate.id
|
|
105
|
-
|
|
106
|
-
app = MyApplication()
|
|
107
|
-
|
|
108
|
-
self.assertEqual(app.aggregate_log.get_last(), None)
|
|
109
|
-
|
|
110
|
-
aggregate1_id = app.create_aggregate()
|
|
111
|
-
last = app.aggregate_log.get_last()
|
|
112
|
-
assert last
|
|
113
|
-
self.assertEqual(last.aggregate_id, aggregate1_id)
|
|
114
|
-
|
|
115
|
-
aggregate2_id = app.create_aggregate()
|
|
116
|
-
last = app.aggregate_log.get_last()
|
|
117
|
-
assert last
|
|
118
|
-
self.assertEqual(last.aggregate_id, aggregate2_id)
|
|
119
|
-
|
|
120
|
-
aggregate_ids = [i.aggregate_id for i in app.aggregate_log.get()]
|
|
121
|
-
self.assertEqual(aggregate_ids, [aggregate1_id, aggregate2_id])
|
|
122
|
-
|
|
123
|
-
def test_subclasses(self) -> None:
|
|
124
|
-
transcoder = JSONTranscoder()
|
|
125
|
-
transcoder.register(UUIDAsHex())
|
|
126
|
-
transcoder.register(DecimalAsStr())
|
|
127
|
-
transcoder.register(DatetimeAsISO())
|
|
128
|
-
|
|
129
|
-
event_recorder = POPOAggregateRecorder()
|
|
130
|
-
event_store = EventStore(
|
|
131
|
-
mapper=Mapper(transcoder=transcoder),
|
|
132
|
-
recorder=event_recorder,
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
class TransactionLogEvent(DomainEvent):
|
|
136
|
-
pass
|
|
137
|
-
|
|
138
|
-
class AccountCredited(TransactionLogEvent):
|
|
139
|
-
pass
|
|
140
|
-
|
|
141
|
-
class AccountDebited(TransactionLogEvent):
|
|
142
|
-
pass
|
|
143
|
-
|
|
144
|
-
# Subclass EventSourcedLog.
|
|
145
|
-
class TransactionLog(EventSourcedLog[TransactionLogEvent]):
|
|
146
|
-
def account_credited(self) -> AccountCredited:
|
|
147
|
-
return self._trigger_event(logged_cls=AccountCredited)
|
|
148
|
-
|
|
149
|
-
def account_debited(self) -> AccountDebited:
|
|
150
|
-
return self._trigger_event(logged_cls=AccountDebited)
|
|
151
|
-
|
|
152
|
-
transaction_log = TransactionLog(
|
|
153
|
-
events=event_store,
|
|
154
|
-
originator_id=uuid5(NAMESPACE_URL, "/aggregates"),
|
|
155
|
-
logged_cls=TransactionLogEvent,
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
account_credited = transaction_log.account_credited()
|
|
159
|
-
self.assertIsInstance(account_credited, AccountCredited)
|
|
160
|
-
|
|
161
|
-
account_debited = transaction_log.account_debited()
|
|
162
|
-
self.assertIsInstance(account_debited, AccountDebited)
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
from unittest.case import TestCase
|
|
2
|
-
from uuid import uuid4
|
|
3
|
-
|
|
4
|
-
from eventsourcing.application import LocalNotificationLog
|
|
5
|
-
from eventsourcing.persistence import StoredEvent
|
|
6
|
-
from eventsourcing.sqlite import SQLiteApplicationRecorder, SQLiteDatastore
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TestNotificationLog(TestCase):
|
|
10
|
-
def test_get_section(self):
|
|
11
|
-
recorder = SQLiteApplicationRecorder(SQLiteDatastore(":memory:"))
|
|
12
|
-
recorder.create_table()
|
|
13
|
-
|
|
14
|
-
# Construct notification log.
|
|
15
|
-
notification_log = LocalNotificationLog(recorder, section_size=5)
|
|
16
|
-
|
|
17
|
-
# Get the "current" section of log.
|
|
18
|
-
section = notification_log["1,10"]
|
|
19
|
-
self.assertEqual(len(section.items), 0) # event notifications
|
|
20
|
-
self.assertEqual(section.id, None)
|
|
21
|
-
self.assertEqual(section.next_id, None)
|
|
22
|
-
|
|
23
|
-
# Write 5 events.
|
|
24
|
-
originator_id = uuid4()
|
|
25
|
-
for i in range(5):
|
|
26
|
-
stored_event = StoredEvent(
|
|
27
|
-
originator_id=originator_id,
|
|
28
|
-
originator_version=i,
|
|
29
|
-
topic="topic",
|
|
30
|
-
state=b"state",
|
|
31
|
-
)
|
|
32
|
-
recorder.insert_events([stored_event])
|
|
33
|
-
|
|
34
|
-
# Get the "head" section of log.
|
|
35
|
-
section = notification_log["1,10"]
|
|
36
|
-
self.assertEqual(len(section.items), 5) # event notifications
|
|
37
|
-
self.assertEqual(section.items[0].id, 1)
|
|
38
|
-
self.assertEqual(section.items[1].id, 2)
|
|
39
|
-
self.assertEqual(section.items[2].id, 3)
|
|
40
|
-
self.assertEqual(section.items[3].id, 4)
|
|
41
|
-
self.assertEqual(section.items[4].id, 5)
|
|
42
|
-
self.assertEqual(section.id, "1,5")
|
|
43
|
-
self.assertEqual(section.next_id, "6,10")
|
|
44
|
-
|
|
45
|
-
# Get the "1,5" section of log.
|
|
46
|
-
section = notification_log["1,5"]
|
|
47
|
-
self.assertEqual(len(section.items), 5) # event notifications
|
|
48
|
-
self.assertEqual(section.items[0].id, 1)
|
|
49
|
-
self.assertEqual(section.items[1].id, 2)
|
|
50
|
-
self.assertEqual(section.items[2].id, 3)
|
|
51
|
-
self.assertEqual(section.items[3].id, 4)
|
|
52
|
-
self.assertEqual(section.items[4].id, 5)
|
|
53
|
-
self.assertEqual(section.id, "1,5")
|
|
54
|
-
self.assertEqual(section.next_id, "6,10")
|
|
55
|
-
|
|
56
|
-
# Get the next section of log.
|
|
57
|
-
section = notification_log["6,10"]
|
|
58
|
-
self.assertEqual(len(section.items), 0) # event notifications
|
|
59
|
-
self.assertEqual(section.id, None)
|
|
60
|
-
self.assertEqual(section.next_id, None)
|
|
61
|
-
|
|
62
|
-
# Write 4 events.
|
|
63
|
-
originator_id = uuid4()
|
|
64
|
-
for i in range(4):
|
|
65
|
-
stored_event = StoredEvent(
|
|
66
|
-
originator_id=originator_id,
|
|
67
|
-
originator_version=i,
|
|
68
|
-
topic="topic",
|
|
69
|
-
state=b"state",
|
|
70
|
-
)
|
|
71
|
-
recorder.insert_events([stored_event])
|
|
72
|
-
|
|
73
|
-
# Get the next section of log.
|
|
74
|
-
section = notification_log["6,10"]
|
|
75
|
-
self.assertEqual(len(section.items), 4) # event notifications
|
|
76
|
-
self.assertEqual(section.items[0].id, 6)
|
|
77
|
-
self.assertEqual(section.items[1].id, 7)
|
|
78
|
-
self.assertEqual(section.items[2].id, 8)
|
|
79
|
-
self.assertEqual(section.items[3].id, 9)
|
|
80
|
-
self.assertEqual(section.id, "6,9")
|
|
81
|
-
self.assertEqual(section.next_id, None)
|
|
82
|
-
|
|
83
|
-
# Start at non-regular section start.
|
|
84
|
-
section = notification_log["3,7"]
|
|
85
|
-
self.assertEqual(len(section.items), 5) # event notifications
|
|
86
|
-
self.assertEqual(section.items[0].id, 3)
|
|
87
|
-
self.assertEqual(section.items[1].id, 4)
|
|
88
|
-
self.assertEqual(section.items[2].id, 5)
|
|
89
|
-
self.assertEqual(section.items[3].id, 6)
|
|
90
|
-
self.assertEqual(section.items[4].id, 7)
|
|
91
|
-
self.assertEqual(section.id, "3,7")
|
|
92
|
-
self.assertEqual(section.next_id, "8,12")
|
|
93
|
-
|
|
94
|
-
# Notification log limits section size.
|
|
95
|
-
section = notification_log["3,10"]
|
|
96
|
-
self.assertEqual(len(section.items), 5) # event notifications
|
|
97
|
-
self.assertEqual(section.items[0].id, 3)
|
|
98
|
-
self.assertEqual(section.items[1].id, 4)
|
|
99
|
-
self.assertEqual(section.items[2].id, 5)
|
|
100
|
-
self.assertEqual(section.items[3].id, 6)
|
|
101
|
-
self.assertEqual(section.items[4].id, 7)
|
|
102
|
-
self.assertEqual(section.id, "3,7")
|
|
103
|
-
self.assertEqual(section.next_id, "8,12")
|
|
104
|
-
|
|
105
|
-
# Reader limits section size.
|
|
106
|
-
section = notification_log["3,4"]
|
|
107
|
-
self.assertEqual(len(section.items), 2) # event notifications
|
|
108
|
-
self.assertEqual(section.items[0].id, 3)
|
|
109
|
-
self.assertEqual(section.items[1].id, 4)
|
|
110
|
-
self.assertEqual(section.id, "3,4")
|
|
111
|
-
self.assertEqual(section.next_id, "5,6")
|
|
112
|
-
|
|
113
|
-
# Meaningless section ID.
|
|
114
|
-
section = notification_log["3,2"]
|
|
115
|
-
self.assertEqual(len(section.items), 0) # event notifications
|
|
116
|
-
self.assertEqual(section.id, None)
|
|
117
|
-
self.assertEqual(section.next_id, None)
|
|
118
|
-
|
|
119
|
-
# # Numbers below 1.
|
|
120
|
-
# section = notification_log["-3,0"]
|
|
121
|
-
# self.assertEqual(
|
|
122
|
-
# len(section.items), 4
|
|
123
|
-
# ) # event notifications
|
|
124
|
-
# self.assertEqual(section.items[0].id, 6)
|
|
125
|
-
# self.assertEqual(section.items[1].id, 7)
|
|
126
|
-
# self.assertEqual(section.items[2].id, 8)
|
|
127
|
-
# self.assertEqual(section.items[3].id, 9)
|
|
128
|
-
# self.assertEqual(section.section_id, "6,9")
|
|
129
|
-
# self.assertEqual(section.next_id, None)
|
|
130
|
-
#
|
|
131
|
-
# section = notification_log["-4,0"]
|
|
132
|
-
# self.assertEqual(
|
|
133
|
-
# len(section.items), 5
|
|
134
|
-
# ) # event notifications
|
|
135
|
-
# self.assertEqual(section.items[0].id, 5)
|
|
136
|
-
# self.assertEqual(section.items[1].id, 6)
|
|
137
|
-
# self.assertEqual(section.items[2].id, 7)
|
|
138
|
-
# self.assertEqual(section.items[3].id, 8)
|
|
139
|
-
# self.assertEqual(section.items[4].id, 9)
|
|
140
|
-
# self.assertEqual(section.section_id, "5,9")
|
|
141
|
-
# self.assertEqual(section.next_id, "10,14")
|
|
142
|
-
#
|
|
143
|
-
# section = notification_log["-4,-1"]
|
|
144
|
-
# self.assertEqual(
|
|
145
|
-
# len(section.items), 4
|
|
146
|
-
# ) # event notifications
|
|
147
|
-
# self.assertEqual(section.items[0].id, 5)
|
|
148
|
-
# self.assertEqual(section.items[1].id, 6)
|
|
149
|
-
# self.assertEqual(section.items[2].id, 7)
|
|
150
|
-
# self.assertEqual(section.items[3].id, 8)
|
|
151
|
-
# self.assertEqual(section.section_id, "5,8")
|
|
152
|
-
# self.assertEqual(section.next_id, "9,12")
|
|
153
|
-
|
|
154
|
-
def test_select(self):
|
|
155
|
-
recorder = SQLiteApplicationRecorder(SQLiteDatastore(":memory:"))
|
|
156
|
-
recorder.create_table()
|
|
157
|
-
|
|
158
|
-
# Construct notification log.
|
|
159
|
-
notification_log = LocalNotificationLog(recorder, section_size=10)
|
|
160
|
-
|
|
161
|
-
# Select start 1, limit 10
|
|
162
|
-
notifications = notification_log.select(1, 10)
|
|
163
|
-
self.assertEqual(len(notifications), 0)
|
|
164
|
-
|
|
165
|
-
# Write 5 events.
|
|
166
|
-
originator_id = uuid4()
|
|
167
|
-
for i in range(5):
|
|
168
|
-
stored_event = StoredEvent(
|
|
169
|
-
originator_id=originator_id,
|
|
170
|
-
originator_version=i,
|
|
171
|
-
topic="topic",
|
|
172
|
-
state=b"state",
|
|
173
|
-
)
|
|
174
|
-
recorder.insert_events([stored_event])
|
|
175
|
-
|
|
176
|
-
# Select start 1, limit 10
|
|
177
|
-
notifications = notification_log.select(1, 5)
|
|
178
|
-
self.assertEqual(len(notifications), 5)
|
|
179
|
-
self.assertEqual(notifications[0].id, 1)
|
|
180
|
-
self.assertEqual(notifications[1].id, 2)
|
|
181
|
-
self.assertEqual(notifications[2].id, 3)
|
|
182
|
-
self.assertEqual(notifications[3].id, 4)
|
|
183
|
-
self.assertEqual(notifications[4].id, 5)
|
|
184
|
-
|
|
185
|
-
# Select start 1, limit 10
|
|
186
|
-
notifications = notification_log.select(1, 5)
|
|
187
|
-
self.assertEqual(len(notifications), 5)
|
|
188
|
-
self.assertEqual(notifications[0].id, 1)
|
|
189
|
-
self.assertEqual(notifications[1].id, 2)
|
|
190
|
-
self.assertEqual(notifications[2].id, 3)
|
|
191
|
-
self.assertEqual(notifications[3].id, 4)
|
|
192
|
-
self.assertEqual(notifications[4].id, 5)
|
|
193
|
-
|
|
194
|
-
# Select start 6, limit 5
|
|
195
|
-
notifications = notification_log.select(6, 5)
|
|
196
|
-
self.assertEqual(len(notifications), 0)
|
|
197
|
-
|
|
198
|
-
# Write 4 events.
|
|
199
|
-
originator_id = uuid4()
|
|
200
|
-
for i in range(4):
|
|
201
|
-
stored_event = StoredEvent(
|
|
202
|
-
originator_id=originator_id,
|
|
203
|
-
originator_version=i,
|
|
204
|
-
topic="topic",
|
|
205
|
-
state=b"state",
|
|
206
|
-
)
|
|
207
|
-
recorder.insert_events([stored_event])
|
|
208
|
-
|
|
209
|
-
# Select start 6, limit 5
|
|
210
|
-
notifications = notification_log.select(6, 5)
|
|
211
|
-
self.assertEqual(len(notifications), 4) # event notifications
|
|
212
|
-
self.assertEqual(notifications[0].id, 6)
|
|
213
|
-
self.assertEqual(notifications[1].id, 7)
|
|
214
|
-
self.assertEqual(notifications[2].id, 8)
|
|
215
|
-
self.assertEqual(notifications[3].id, 9)
|
|
216
|
-
|
|
217
|
-
# Select start 3, limit 5
|
|
218
|
-
notifications = notification_log.select(3, 5)
|
|
219
|
-
self.assertEqual(len(notifications), 5) # event notifications
|
|
220
|
-
self.assertEqual(notifications[0].id, 3)
|
|
221
|
-
self.assertEqual(notifications[1].id, 4)
|
|
222
|
-
self.assertEqual(notifications[2].id, 5)
|
|
223
|
-
self.assertEqual(notifications[3].id, 6)
|
|
224
|
-
self.assertEqual(notifications[4].id, 7)
|
|
225
|
-
|
|
226
|
-
# Notification log limits limit.
|
|
227
|
-
# Select start 1, limit 20
|
|
228
|
-
with self.assertRaises(ValueError) as cm:
|
|
229
|
-
notification_log.select(1, 20)
|
|
230
|
-
self.assertEqual(
|
|
231
|
-
cm.exception.args[0], "Requested limit 20 greater than section size 10"
|
|
232
|
-
)
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
from itertools import chain
|
|
2
|
-
from unittest.case import TestCase
|
|
3
|
-
from uuid import uuid4
|
|
4
|
-
|
|
5
|
-
from eventsourcing.application import LocalNotificationLog
|
|
6
|
-
from eventsourcing.persistence import StoredEvent
|
|
7
|
-
from eventsourcing.sqlite import SQLiteDatastore, SQLiteProcessRecorder
|
|
8
|
-
from eventsourcing.system import NotificationLogReader
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class TestNotificationLogReader(TestCase):
|
|
12
|
-
def test_read(self):
|
|
13
|
-
recorder = SQLiteProcessRecorder(SQLiteDatastore(":memory:"))
|
|
14
|
-
recorder.create_table()
|
|
15
|
-
|
|
16
|
-
# Construct notification log.
|
|
17
|
-
notification_log = LocalNotificationLog(recorder, section_size=5)
|
|
18
|
-
reader = NotificationLogReader(notification_log, section_size=10)
|
|
19
|
-
notifications = list(reader.read(start=1))
|
|
20
|
-
self.assertEqual(len(notifications), 0)
|
|
21
|
-
|
|
22
|
-
# Write 5 events.
|
|
23
|
-
originator_id = uuid4()
|
|
24
|
-
for i in range(5):
|
|
25
|
-
stored_event = StoredEvent(
|
|
26
|
-
originator_id=originator_id,
|
|
27
|
-
originator_version=i,
|
|
28
|
-
topic="topic",
|
|
29
|
-
state=b"state",
|
|
30
|
-
)
|
|
31
|
-
recorder.insert_events(
|
|
32
|
-
[stored_event],
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
notifications = list(reader.read(start=1))
|
|
36
|
-
self.assertEqual(len(notifications), 5)
|
|
37
|
-
|
|
38
|
-
# Write 4 events.
|
|
39
|
-
originator_id = uuid4()
|
|
40
|
-
for i in range(4):
|
|
41
|
-
stored_event = StoredEvent(
|
|
42
|
-
originator_id=originator_id,
|
|
43
|
-
originator_version=i,
|
|
44
|
-
topic="topic",
|
|
45
|
-
state=b"state",
|
|
46
|
-
)
|
|
47
|
-
recorder.insert_events([stored_event])
|
|
48
|
-
|
|
49
|
-
notifications = list(reader.read(start=1))
|
|
50
|
-
self.assertEqual(len(notifications), 9)
|
|
51
|
-
|
|
52
|
-
notifications = list(reader.read(start=2))
|
|
53
|
-
self.assertEqual(len(notifications), 8)
|
|
54
|
-
|
|
55
|
-
notifications = list(reader.read(start=3))
|
|
56
|
-
self.assertEqual(len(notifications), 7)
|
|
57
|
-
|
|
58
|
-
notifications = list(reader.read(start=4))
|
|
59
|
-
self.assertEqual(len(notifications), 6)
|
|
60
|
-
|
|
61
|
-
notifications = list(reader.read(start=8))
|
|
62
|
-
self.assertEqual(len(notifications), 2)
|
|
63
|
-
|
|
64
|
-
notifications = list(reader.read(start=9))
|
|
65
|
-
self.assertEqual(len(notifications), 1)
|
|
66
|
-
|
|
67
|
-
notifications = list(reader.read(start=10))
|
|
68
|
-
self.assertEqual(len(notifications), 0)
|
|
69
|
-
|
|
70
|
-
def test_select(self):
|
|
71
|
-
recorder = SQLiteProcessRecorder(SQLiteDatastore(":memory:"))
|
|
72
|
-
recorder.create_table()
|
|
73
|
-
|
|
74
|
-
# Construct notification log.
|
|
75
|
-
notification_log = LocalNotificationLog(recorder, section_size=5)
|
|
76
|
-
reader = NotificationLogReader(notification_log, section_size=5)
|
|
77
|
-
notifications = list(chain(*reader.select(start=1)))
|
|
78
|
-
self.assertEqual(len(notifications), 0)
|
|
79
|
-
|
|
80
|
-
# Write 5 events.
|
|
81
|
-
originator_id = uuid4()
|
|
82
|
-
for i in range(5):
|
|
83
|
-
stored_event = StoredEvent(
|
|
84
|
-
originator_id=originator_id,
|
|
85
|
-
originator_version=i,
|
|
86
|
-
topic="topic",
|
|
87
|
-
state=b"state",
|
|
88
|
-
)
|
|
89
|
-
recorder.insert_events(
|
|
90
|
-
[stored_event],
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
notifications = list(chain(*reader.select(start=1)))
|
|
94
|
-
self.assertEqual(len(notifications), 5)
|
|
95
|
-
|
|
96
|
-
# Write 4 events.
|
|
97
|
-
originator_id = uuid4()
|
|
98
|
-
for i in range(4):
|
|
99
|
-
stored_event = StoredEvent(
|
|
100
|
-
originator_id=originator_id,
|
|
101
|
-
originator_version=i,
|
|
102
|
-
topic="topic",
|
|
103
|
-
state=b"state",
|
|
104
|
-
)
|
|
105
|
-
recorder.insert_events([stored_event])
|
|
106
|
-
|
|
107
|
-
notifications = list(chain(*reader.select(start=1)))
|
|
108
|
-
self.assertEqual(len(notifications), 9)
|
|
109
|
-
|
|
110
|
-
notifications = list(chain(*reader.select(start=2)))
|
|
111
|
-
self.assertEqual(len(notifications), 8)
|
|
112
|
-
|
|
113
|
-
notifications = list(chain(*reader.select(start=3)))
|
|
114
|
-
self.assertEqual(len(notifications), 7)
|
|
115
|
-
|
|
116
|
-
notifications = list(chain(*reader.select(start=4)))
|
|
117
|
-
self.assertEqual(len(notifications), 6)
|
|
118
|
-
|
|
119
|
-
notifications = list(chain(*reader.select(start=8)))
|
|
120
|
-
self.assertEqual(len(notifications), 2)
|
|
121
|
-
|
|
122
|
-
notifications = list(chain(*reader.select(start=9)))
|
|
123
|
-
self.assertEqual(len(notifications), 1)
|
|
124
|
-
|
|
125
|
-
notifications = list(chain(*reader.select(start=10)))
|
|
126
|
-
self.assertEqual(len(notifications), 0)
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
from unittest.case import TestCase
|
|
2
|
-
|
|
3
|
-
from eventsourcing.application import RecordingEvent
|
|
4
|
-
from eventsourcing.dispatch import singledispatchmethod
|
|
5
|
-
from eventsourcing.domain import AggregateEvent
|
|
6
|
-
from eventsourcing.persistence import Transcoder
|
|
7
|
-
from eventsourcing.system import (
|
|
8
|
-
Follower,
|
|
9
|
-
Leader,
|
|
10
|
-
ProcessApplication,
|
|
11
|
-
ProcessingEvent,
|
|
12
|
-
RecordingEventReceiver,
|
|
13
|
-
)
|
|
14
|
-
from eventsourcing.tests.application import BankAccounts, EmailAddressAsStr
|
|
15
|
-
from eventsourcing.tests.application_tests.test_processingpolicy import (
|
|
16
|
-
EmailNotification,
|
|
17
|
-
)
|
|
18
|
-
from eventsourcing.tests.domain import BankAccount
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class TestProcessApplication(TestCase):
|
|
22
|
-
def test_pull_and_process(self):
|
|
23
|
-
leader_cls = type(
|
|
24
|
-
BankAccounts.__name__,
|
|
25
|
-
(BankAccounts, Leader),
|
|
26
|
-
{},
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
accounts = leader_cls()
|
|
30
|
-
email_process = EmailProcess()
|
|
31
|
-
email_process.follow(
|
|
32
|
-
accounts.name,
|
|
33
|
-
accounts.notification_log,
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
section = email_process.notification_log["1,5"]
|
|
37
|
-
self.assertEqual(len(section.items), 0)
|
|
38
|
-
|
|
39
|
-
accounts.open_account("Alice", "alice@example.com")
|
|
40
|
-
|
|
41
|
-
email_process.pull_and_process(BankAccounts.name)
|
|
42
|
-
|
|
43
|
-
section = email_process.notification_log["1,5"]
|
|
44
|
-
self.assertEqual(len(section.items), 1)
|
|
45
|
-
|
|
46
|
-
# Check we have processed the first event.
|
|
47
|
-
self.assertEqual(email_process.recorder.max_tracking_id(BankAccounts.name), 1)
|
|
48
|
-
|
|
49
|
-
# Check reprocessing first event changes nothing (swallows IntegrityError).
|
|
50
|
-
email_process.pull_and_process(BankAccounts.name, start=1)
|
|
51
|
-
self.assertEqual(email_process.recorder.max_tracking_id(BankAccounts.name), 1)
|
|
52
|
-
|
|
53
|
-
# Check we can continue from the next position.
|
|
54
|
-
email_process.pull_and_process(BankAccounts.name, start=2)
|
|
55
|
-
|
|
56
|
-
# Check we haven't actually processed anything further.
|
|
57
|
-
self.assertEqual(email_process.recorder.max_tracking_id(BankAccounts.name), 1)
|
|
58
|
-
section = email_process.notification_log["1,5"]
|
|
59
|
-
self.assertEqual(len(section.items), 1)
|
|
60
|
-
|
|
61
|
-
# Subscribe for notifications.
|
|
62
|
-
accounts.lead(PromptForwarder(email_process))
|
|
63
|
-
|
|
64
|
-
# Create another notification.
|
|
65
|
-
accounts.open_account("Bob", "bob@example.com")
|
|
66
|
-
|
|
67
|
-
# Check we have processed the next notification.
|
|
68
|
-
section = email_process.notification_log["1,5"]
|
|
69
|
-
self.assertEqual(len(section.items), 2)
|
|
70
|
-
|
|
71
|
-
# Check we have actually processed the second event.
|
|
72
|
-
self.assertEqual(email_process.recorder.max_tracking_id(BankAccounts.name), 2)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class EmailProcess(ProcessApplication):
|
|
76
|
-
def register_transcodings(self, transcoder: Transcoder) -> None:
|
|
77
|
-
super().register_transcodings(transcoder)
|
|
78
|
-
transcoder.register(EmailAddressAsStr())
|
|
79
|
-
|
|
80
|
-
@singledispatchmethod
|
|
81
|
-
def policy(
|
|
82
|
-
self,
|
|
83
|
-
domain_event: AggregateEvent,
|
|
84
|
-
processing_event: ProcessingEvent,
|
|
85
|
-
):
|
|
86
|
-
"""Default policy"""
|
|
87
|
-
|
|
88
|
-
@policy.register
|
|
89
|
-
def _(
|
|
90
|
-
self,
|
|
91
|
-
domain_event: BankAccount.Opened,
|
|
92
|
-
processing_event: ProcessingEvent,
|
|
93
|
-
):
|
|
94
|
-
notification = EmailNotification.create(
|
|
95
|
-
to=domain_event.email_address,
|
|
96
|
-
subject="Your New Account",
|
|
97
|
-
message=f"Dear {domain_event.full_name}, ...",
|
|
98
|
-
)
|
|
99
|
-
processing_event.collect_events(notification)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
class PromptForwarder(RecordingEventReceiver):
|
|
103
|
-
def __init__(self, application: Follower):
|
|
104
|
-
self.application = application
|
|
105
|
-
|
|
106
|
-
def receive_recording_event(self, recording_event: RecordingEvent) -> None:
|
|
107
|
-
self.application.pull_and_process(
|
|
108
|
-
leader_name=recording_event.application_name,
|
|
109
|
-
start=recording_event.recordings[0].notification.id,
|
|
110
|
-
)
|