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.

Files changed (129) hide show
  1. eventsourcing/application.py +0 -15
  2. eventsourcing/system.py +25 -2
  3. {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/METADATA +1 -1
  4. eventsourcing-9.3.5.dist-info/RECORD +24 -0
  5. eventsourcing/examples/__init__.py +0 -0
  6. eventsourcing/examples/aggregate1/__init__.py +0 -0
  7. eventsourcing/examples/aggregate1/application.py +0 -27
  8. eventsourcing/examples/aggregate1/domainmodel.py +0 -16
  9. eventsourcing/examples/aggregate1/test_application.py +0 -37
  10. eventsourcing/examples/aggregate2/__init__.py +0 -0
  11. eventsourcing/examples/aggregate2/application.py +0 -27
  12. eventsourcing/examples/aggregate2/domainmodel.py +0 -22
  13. eventsourcing/examples/aggregate2/test_application.py +0 -37
  14. eventsourcing/examples/aggregate3/__init__.py +0 -0
  15. eventsourcing/examples/aggregate3/application.py +0 -27
  16. eventsourcing/examples/aggregate3/domainmodel.py +0 -38
  17. eventsourcing/examples/aggregate3/test_application.py +0 -37
  18. eventsourcing/examples/aggregate4/__init__.py +0 -0
  19. eventsourcing/examples/aggregate4/application.py +0 -27
  20. eventsourcing/examples/aggregate4/domainmodel.py +0 -114
  21. eventsourcing/examples/aggregate4/test_application.py +0 -38
  22. eventsourcing/examples/aggregate5/__init__.py +0 -0
  23. eventsourcing/examples/aggregate5/application.py +0 -27
  24. eventsourcing/examples/aggregate5/domainmodel.py +0 -131
  25. eventsourcing/examples/aggregate5/test_application.py +0 -38
  26. eventsourcing/examples/aggregate6/__init__.py +0 -0
  27. eventsourcing/examples/aggregate6/application.py +0 -30
  28. eventsourcing/examples/aggregate6/domainmodel.py +0 -123
  29. eventsourcing/examples/aggregate6/test_application.py +0 -38
  30. eventsourcing/examples/aggregate6a/__init__.py +0 -0
  31. eventsourcing/examples/aggregate6a/application.py +0 -40
  32. eventsourcing/examples/aggregate6a/domainmodel.py +0 -149
  33. eventsourcing/examples/aggregate6a/test_application.py +0 -45
  34. eventsourcing/examples/aggregate7/__init__.py +0 -0
  35. eventsourcing/examples/aggregate7/application.py +0 -53
  36. eventsourcing/examples/aggregate7/domainmodel.py +0 -142
  37. eventsourcing/examples/aggregate7/persistence.py +0 -57
  38. eventsourcing/examples/aggregate7/test_application.py +0 -45
  39. eventsourcing/examples/aggregate7/test_compression_and_encryption.py +0 -45
  40. eventsourcing/examples/aggregate7/test_snapshotting_intervals.py +0 -67
  41. eventsourcing/examples/aggregate7a/__init__.py +0 -0
  42. eventsourcing/examples/aggregate7a/application.py +0 -56
  43. eventsourcing/examples/aggregate7a/domainmodel.py +0 -168
  44. eventsourcing/examples/aggregate7a/test_application.py +0 -46
  45. eventsourcing/examples/aggregate7a/test_compression_and_encryption.py +0 -45
  46. eventsourcing/examples/aggregate8/__init__.py +0 -0
  47. eventsourcing/examples/aggregate8/application.py +0 -47
  48. eventsourcing/examples/aggregate8/domainmodel.py +0 -71
  49. eventsourcing/examples/aggregate8/persistence.py +0 -57
  50. eventsourcing/examples/aggregate8/test_application.py +0 -44
  51. eventsourcing/examples/aggregate8/test_compression_and_encryption.py +0 -44
  52. eventsourcing/examples/aggregate8/test_snapshotting_intervals.py +0 -38
  53. eventsourcing/examples/bankaccounts/__init__.py +0 -0
  54. eventsourcing/examples/bankaccounts/application.py +0 -70
  55. eventsourcing/examples/bankaccounts/domainmodel.py +0 -56
  56. eventsourcing/examples/bankaccounts/test.py +0 -173
  57. eventsourcing/examples/cargoshipping/__init__.py +0 -0
  58. eventsourcing/examples/cargoshipping/application.py +0 -126
  59. eventsourcing/examples/cargoshipping/domainmodel.py +0 -330
  60. eventsourcing/examples/cargoshipping/interface.py +0 -143
  61. eventsourcing/examples/cargoshipping/test.py +0 -231
  62. eventsourcing/examples/contentmanagement/__init__.py +0 -0
  63. eventsourcing/examples/contentmanagement/application.py +0 -118
  64. eventsourcing/examples/contentmanagement/domainmodel.py +0 -69
  65. eventsourcing/examples/contentmanagement/test.py +0 -180
  66. eventsourcing/examples/contentmanagement/utils.py +0 -26
  67. eventsourcing/examples/contentmanagementsystem/__init__.py +0 -0
  68. eventsourcing/examples/contentmanagementsystem/application.py +0 -54
  69. eventsourcing/examples/contentmanagementsystem/postgres.py +0 -17
  70. eventsourcing/examples/contentmanagementsystem/sqlite.py +0 -17
  71. eventsourcing/examples/contentmanagementsystem/system.py +0 -14
  72. eventsourcing/examples/contentmanagementsystem/test_system.py +0 -180
  73. eventsourcing/examples/searchablecontent/__init__.py +0 -0
  74. eventsourcing/examples/searchablecontent/application.py +0 -45
  75. eventsourcing/examples/searchablecontent/persistence.py +0 -23
  76. eventsourcing/examples/searchablecontent/postgres.py +0 -118
  77. eventsourcing/examples/searchablecontent/sqlite.py +0 -136
  78. eventsourcing/examples/searchablecontent/test_application.py +0 -110
  79. eventsourcing/examples/searchablecontent/test_recorder.py +0 -68
  80. eventsourcing/examples/searchabletimestamps/__init__.py +0 -0
  81. eventsourcing/examples/searchabletimestamps/application.py +0 -32
  82. eventsourcing/examples/searchabletimestamps/persistence.py +0 -20
  83. eventsourcing/examples/searchabletimestamps/postgres.py +0 -110
  84. eventsourcing/examples/searchabletimestamps/sqlite.py +0 -99
  85. eventsourcing/examples/searchabletimestamps/test_searchabletimestamps.py +0 -94
  86. eventsourcing/examples/test_invoice.py +0 -176
  87. eventsourcing/examples/test_parking_lot.py +0 -206
  88. eventsourcing/tests/application_tests/__init__.py +0 -0
  89. eventsourcing/tests/application_tests/test_application_with_automatic_snapshotting.py +0 -55
  90. eventsourcing/tests/application_tests/test_application_with_popo.py +0 -22
  91. eventsourcing/tests/application_tests/test_application_with_postgres.py +0 -75
  92. eventsourcing/tests/application_tests/test_application_with_sqlite.py +0 -72
  93. eventsourcing/tests/application_tests/test_cache.py +0 -134
  94. eventsourcing/tests/application_tests/test_event_sourced_log.py +0 -162
  95. eventsourcing/tests/application_tests/test_notificationlog.py +0 -232
  96. eventsourcing/tests/application_tests/test_notificationlogreader.py +0 -126
  97. eventsourcing/tests/application_tests/test_processapplication.py +0 -110
  98. eventsourcing/tests/application_tests/test_processingpolicy.py +0 -109
  99. eventsourcing/tests/application_tests/test_repository.py +0 -504
  100. eventsourcing/tests/application_tests/test_snapshotting.py +0 -68
  101. eventsourcing/tests/application_tests/test_upcasting.py +0 -459
  102. eventsourcing/tests/docs_tests/__init__.py +0 -0
  103. eventsourcing/tests/docs_tests/test_docs.py +0 -293
  104. eventsourcing/tests/domain_tests/__init__.py +0 -0
  105. eventsourcing/tests/domain_tests/test_aggregate.py +0 -1200
  106. eventsourcing/tests/domain_tests/test_aggregate_decorators.py +0 -1604
  107. eventsourcing/tests/domain_tests/test_domainevent.py +0 -80
  108. eventsourcing/tests/interface_tests/__init__.py +0 -0
  109. eventsourcing/tests/interface_tests/test_remotenotificationlog.py +0 -258
  110. eventsourcing/tests/persistence_tests/__init__.py +0 -0
  111. eventsourcing/tests/persistence_tests/test_aes.py +0 -93
  112. eventsourcing/tests/persistence_tests/test_connection_pool.py +0 -722
  113. eventsourcing/tests/persistence_tests/test_eventstore.py +0 -72
  114. eventsourcing/tests/persistence_tests/test_infrastructure_factory.py +0 -21
  115. eventsourcing/tests/persistence_tests/test_mapper.py +0 -113
  116. eventsourcing/tests/persistence_tests/test_noninterleaving_notification_ids.py +0 -69
  117. eventsourcing/tests/persistence_tests/test_popo.py +0 -124
  118. eventsourcing/tests/persistence_tests/test_postgres.py +0 -1120
  119. eventsourcing/tests/persistence_tests/test_sqlite.py +0 -348
  120. eventsourcing/tests/persistence_tests/test_transcoder.py +0 -44
  121. eventsourcing/tests/system_tests/__init__.py +0 -0
  122. eventsourcing/tests/system_tests/test_runner.py +0 -935
  123. eventsourcing/tests/system_tests/test_system.py +0 -284
  124. eventsourcing/tests/utils_tests/__init__.py +0 -0
  125. eventsourcing/tests/utils_tests/test_utils.py +0 -226
  126. eventsourcing-9.3.3.dist-info/RECORD +0 -145
  127. {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/AUTHORS +0 -0
  128. {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/LICENSE +0 -0
  129. {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/WHEEL +0 -0
@@ -1,109 +0,0 @@
1
- import warnings
2
- from unittest.case import TestCase
3
- from uuid import uuid4
4
-
5
- from eventsourcing.domain import Aggregate
6
- from eventsourcing.persistence import Tracking
7
- from eventsourcing.system import ProcessingEvent
8
- from eventsourcing.tests.domain import BankAccount
9
-
10
-
11
- def policy(domain_event, processing_event: ProcessingEvent):
12
- if isinstance(domain_event, BankAccount.Opened):
13
- notification = EmailNotification.create(
14
- to=domain_event.email_address,
15
- subject="Your New Account",
16
- message=f"Dear {domain_event.full_name}",
17
- )
18
- processing_event.collect_events(notification)
19
-
20
-
21
- def policy_legacy_save(domain_event, processing_event: ProcessingEvent):
22
- if isinstance(domain_event, BankAccount.Opened):
23
- notification = EmailNotification.create(
24
- to=domain_event.email_address,
25
- subject="Your New Account",
26
- message=f"Dear {domain_event.full_name}",
27
- )
28
- processing_event.save(notification)
29
-
30
-
31
- class TestProcessingPolicy(TestCase):
32
- def test_policy(self):
33
- # Open an account.
34
- account = BankAccount.open(
35
- full_name="Alice",
36
- email_address="alice@example.com",
37
- )
38
- events = account.collect_events()
39
- created_event = events[0]
40
-
41
- processing_event = ProcessingEvent(
42
- tracking=Tracking(
43
- application_name="upstream_app",
44
- notification_id=5,
45
- )
46
- )
47
-
48
- policy(created_event, processing_event)
49
-
50
- self.assertEqual(len(processing_event.events), 1)
51
- self.assertIsInstance(
52
- processing_event.events[0],
53
- EmailNotification.Created,
54
- )
55
-
56
- def test_legacy_save(self):
57
- # Open an account.
58
- account = BankAccount.open(
59
- full_name="Alice",
60
- email_address="alice@example.com",
61
- )
62
- events = account.collect_events()
63
- created_event = events[0]
64
-
65
- processing_event = ProcessingEvent(
66
- tracking=Tracking(
67
- application_name="upstream_app",
68
- notification_id=5,
69
- )
70
- )
71
-
72
- # Verify deprecation warning.
73
- with warnings.catch_warnings(record=True) as w:
74
- policy_legacy_save(created_event, processing_event)
75
-
76
- self.assertEqual(1, len(w))
77
- self.assertIs(w[-1].category, DeprecationWarning)
78
- self.assertEqual(
79
- "'save()' is deprecated, use 'collect_events()' instead",
80
- w[-1].message.args[0],
81
- )
82
-
83
- self.assertEqual(len(processing_event.events), 1)
84
- self.assertIsInstance(
85
- processing_event.events[0],
86
- EmailNotification.Created,
87
- )
88
-
89
-
90
- class EmailNotification(Aggregate):
91
- def __init__(self, to, subject, message):
92
- self.to = to
93
- self.subject = subject
94
- self.message = message
95
-
96
- @classmethod
97
- def create(cls, to, subject, message):
98
- return cls._create(
99
- cls.Created,
100
- id=uuid4(),
101
- to=to,
102
- subject=subject,
103
- message=message,
104
- )
105
-
106
- class Created(Aggregate.Created):
107
- to: str
108
- subject: str
109
- message: str
@@ -1,504 +0,0 @@
1
- from decimal import Decimal
2
- from functools import reduce
3
- from unittest.case import TestCase
4
- from uuid import uuid4
5
-
6
- from eventsourcing.application import (
7
- AggregateNotFoundError,
8
- Cache,
9
- LRUCache,
10
- Repository,
11
- )
12
- from eventsourcing.domain import Aggregate, Snapshot
13
- from eventsourcing.persistence import (
14
- DatetimeAsISO,
15
- DecimalAsStr,
16
- EventStore,
17
- JSONTranscoder,
18
- Mapper,
19
- UUIDAsHex,
20
- )
21
- from eventsourcing.popo import POPOAggregateRecorder
22
- from eventsourcing.sqlite import SQLiteAggregateRecorder, SQLiteDatastore
23
- from eventsourcing.tests.application import EmailAddressAsStr
24
- from eventsourcing.tests.domain import BankAccount
25
- from eventsourcing.utils import get_topic
26
-
27
-
28
- class TestRepository(TestCase):
29
- def test_with_snapshot_store(self) -> None:
30
- transcoder = JSONTranscoder()
31
- transcoder.register(UUIDAsHex())
32
- transcoder.register(DecimalAsStr())
33
- transcoder.register(DatetimeAsISO())
34
- transcoder.register(EmailAddressAsStr())
35
-
36
- event_recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
37
- event_recorder.create_table()
38
- event_store = EventStore(
39
- mapper=Mapper(transcoder=transcoder),
40
- recorder=event_recorder,
41
- )
42
- snapshot_recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
43
- snapshot_recorder.create_table()
44
- snapshot_store = EventStore(
45
- mapper=Mapper(transcoder=transcoder),
46
- recorder=snapshot_recorder,
47
- )
48
- repository = Repository(event_store, snapshot_store=snapshot_store)
49
-
50
- # Check key error.
51
- with self.assertRaises(AggregateNotFoundError):
52
- repository.get(uuid4())
53
-
54
- # Open an account.
55
- account = BankAccount.open(
56
- full_name="Alice",
57
- email_address="alice@example.com",
58
- )
59
-
60
- # Credit the account.
61
- account.append_transaction(Decimal("10.00"))
62
- account.append_transaction(Decimal("25.00"))
63
- account.append_transaction(Decimal("30.00"))
64
-
65
- # Collect pending events.
66
- pending = account.collect_events()
67
-
68
- # Store pending events.
69
- event_store.put(pending)
70
-
71
- copy = repository.get(account.id)
72
- assert isinstance(copy, BankAccount)
73
- # Check copy has correct attribute values.
74
- assert copy.id == account.id
75
- assert copy.balance == Decimal("65.00")
76
-
77
- snapshot = Snapshot(
78
- originator_id=account.id,
79
- originator_version=account.version,
80
- timestamp=Snapshot.create_timestamp(),
81
- topic=get_topic(type(account)),
82
- state=account.__dict__,
83
- )
84
- snapshot_store.put([snapshot])
85
-
86
- copy2 = repository.get(account.id)
87
- assert isinstance(copy2, BankAccount)
88
-
89
- # Check copy has correct attribute values.
90
- assert copy2.id == account.id
91
- assert copy2.balance == Decimal("65.00")
92
-
93
- # Credit the account.
94
- account.append_transaction(Decimal("10.00"))
95
- event_store.put(account.collect_events())
96
-
97
- # Check copy has correct attribute values.
98
- copy3 = repository.get(account.id)
99
- assert isinstance(copy3, BankAccount)
100
-
101
- assert copy3.id == account.id
102
- assert copy3.balance == Decimal("75.00")
103
-
104
- # Check can get old version of account.
105
- copy4 = repository.get(account.id, version=copy.version)
106
- assert isinstance(copy4, BankAccount)
107
- assert copy4.balance == Decimal("65.00")
108
-
109
- copy5 = repository.get(account.id, version=1)
110
- assert isinstance(copy5, BankAccount)
111
- assert copy5.balance == Decimal("0.00")
112
-
113
- copy6 = repository.get(account.id, version=2)
114
- assert isinstance(copy6, BankAccount)
115
- assert copy6.balance == Decimal("10.00")
116
-
117
- copy7 = repository.get(account.id, version=3)
118
- assert isinstance(copy7, BankAccount)
119
- assert copy7.balance == Decimal("35.00"), copy7.balance
120
-
121
- copy8 = repository.get(account.id, version=4)
122
- assert isinstance(copy8, BankAccount)
123
- assert copy8.balance == Decimal("65.00"), copy8.balance
124
-
125
- # # Check the __getitem__ method is working
126
- # copy9 = repository[account.uuid]
127
- # self.assertEqual(copy9.balance, Decimal("75.00"))
128
- #
129
- # copy10 = repository[account.uuid, 3]
130
- # # assert isinstance(copy7, BankAccount)
131
- #
132
- # self.assertEqual(copy10.balance, Decimal("35.00"))
133
-
134
- def test_without_snapshot_store(self) -> None:
135
- transcoder = JSONTranscoder()
136
- transcoder.register(UUIDAsHex())
137
- transcoder.register(DecimalAsStr())
138
- transcoder.register(DatetimeAsISO())
139
- transcoder.register(EmailAddressAsStr())
140
-
141
- event_recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
142
- event_recorder.create_table()
143
- event_store = EventStore(
144
- mapper=Mapper(transcoder=transcoder),
145
- recorder=event_recorder,
146
- )
147
- repository = Repository(event_store)
148
-
149
- # Check key error.
150
- with self.assertRaises(AggregateNotFoundError):
151
- repository.get(uuid4())
152
-
153
- # Open an account.
154
- account = BankAccount.open(
155
- full_name="Alice",
156
- email_address="alice@example.com",
157
- )
158
-
159
- # Credit the account.
160
- account.append_transaction(Decimal("10.00"))
161
- account.append_transaction(Decimal("25.00"))
162
- account.append_transaction(Decimal("30.00"))
163
-
164
- # Collect pending events.
165
- pending = account.collect_events()
166
-
167
- # Store pending events.
168
- event_store.put(pending)
169
-
170
- copy = repository.get(account.id)
171
- assert isinstance(copy, BankAccount)
172
- # Check copy has correct attribute values.
173
- assert copy.id == account.id
174
- assert copy.balance == Decimal("65.00")
175
-
176
- # Credit the account.
177
- account.append_transaction(Decimal("10.00"))
178
- event_store.put(account.collect_events())
179
-
180
- # Check copy has correct attribute values.
181
- copy2 = repository.get(account.id)
182
- assert isinstance(copy2, BankAccount)
183
-
184
- assert copy2.id == account.id
185
- assert copy2.balance == Decimal("75.00")
186
-
187
- # Check can get old version of account.
188
- copy3 = repository.get(account.id, version=copy.version)
189
- assert isinstance(copy3, BankAccount)
190
- assert copy3.balance == Decimal("65.00")
191
-
192
- copy4 = repository.get(account.id, version=1)
193
- assert isinstance(copy4, BankAccount)
194
- assert copy4.balance == Decimal("0.00")
195
-
196
- copy5 = repository.get(account.id, version=2)
197
- assert isinstance(copy5, BankAccount)
198
- assert copy5.balance == Decimal("10.00")
199
-
200
- copy6 = repository.get(account.id, version=3)
201
- assert isinstance(copy6, BankAccount)
202
- assert copy6.balance == Decimal("35.00"), copy6.balance
203
-
204
- copy7 = repository.get(account.id, version=4)
205
- assert isinstance(copy7, BankAccount)
206
- assert copy7.balance == Decimal("65.00"), copy7.balance
207
-
208
- def test_with_alternative_mutator_function(self):
209
- def mutator(initial, domain_events):
210
- return reduce(lambda a, e: e.mutate(a), domain_events, initial)
211
-
212
- transcoder = JSONTranscoder()
213
- transcoder.register(UUIDAsHex())
214
- transcoder.register(DecimalAsStr())
215
- transcoder.register(DatetimeAsISO())
216
- transcoder.register(EmailAddressAsStr())
217
-
218
- event_recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
219
- event_recorder.create_table()
220
- event_store = EventStore(
221
- mapper=Mapper(transcoder=transcoder),
222
- recorder=event_recorder,
223
- )
224
- snapshot_recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
225
- snapshot_recorder.create_table()
226
- snapshot_store = EventStore(
227
- mapper=Mapper(transcoder=transcoder),
228
- recorder=snapshot_recorder,
229
- )
230
- repository = Repository(event_store, snapshot_store=snapshot_store)
231
-
232
- # Check key error.
233
- with self.assertRaises(AggregateNotFoundError):
234
- repository.get(uuid4())
235
-
236
- # Open an account.
237
- account = BankAccount.open(
238
- full_name="Alice",
239
- email_address="alice@example.com",
240
- )
241
-
242
- # Credit the account.
243
- account.append_transaction(Decimal("10.00"))
244
- account.append_transaction(Decimal("25.00"))
245
- account.append_transaction(Decimal("30.00"))
246
-
247
- # Collect pending events.
248
- pending = account.collect_events()
249
-
250
- # Store pending events.
251
- event_store.put(pending)
252
-
253
- copy = repository.get(account.id, projector_func=mutator)
254
- assert isinstance(copy, BankAccount)
255
- # Check copy has correct attribute values.
256
- assert copy.id == account.id
257
- assert copy.balance == Decimal("65.00")
258
-
259
- snapshot = Snapshot(
260
- originator_id=account.id,
261
- originator_version=account.version,
262
- timestamp=Snapshot.create_timestamp(),
263
- topic=get_topic(type(account)),
264
- state=account.__dict__,
265
- )
266
- snapshot_store.put([snapshot])
267
-
268
- copy2 = repository.get(account.id)
269
- assert isinstance(copy2, BankAccount)
270
-
271
- # Check copy has correct attribute values.
272
- assert copy2.id == account.id
273
- assert copy2.balance == Decimal("65.00")
274
-
275
- # Credit the account.
276
- account.append_transaction(Decimal("10.00"))
277
- event_store.put(account.collect_events())
278
-
279
- # Check copy has correct attribute values.
280
- copy3 = repository.get(account.id)
281
- assert isinstance(copy3, BankAccount)
282
-
283
- assert copy3.id == account.id
284
- assert copy3.balance == Decimal("75.00")
285
-
286
- # Check can get old version of account.
287
- copy4 = repository.get(account.id, version=copy.version)
288
- assert isinstance(copy4, BankAccount)
289
- assert copy4.balance == Decimal("65.00")
290
-
291
- copy5 = repository.get(account.id, version=1)
292
- assert isinstance(copy5, BankAccount)
293
- assert copy5.balance == Decimal("0.00")
294
-
295
- copy6 = repository.get(account.id, version=2)
296
- assert isinstance(copy6, BankAccount)
297
- assert copy6.balance == Decimal("10.00")
298
-
299
- copy7 = repository.get(account.id, version=3)
300
- assert isinstance(copy7, BankAccount)
301
- assert copy7.balance == Decimal("35.00"), copy7.balance
302
-
303
- copy8 = repository.get(account.id, version=4)
304
- assert isinstance(copy8, BankAccount)
305
- assert copy8.balance == Decimal("65.00"), copy8.balance
306
-
307
- def test_contains(self):
308
- transcoder = JSONTranscoder()
309
- transcoder.register(UUIDAsHex())
310
- transcoder.register(DecimalAsStr())
311
- transcoder.register(DatetimeAsISO())
312
-
313
- event_recorder = POPOAggregateRecorder()
314
- event_store = EventStore(
315
- mapper=Mapper(transcoder=transcoder),
316
- recorder=event_recorder,
317
- )
318
-
319
- aggregate = Aggregate()
320
- event_store.put(aggregate.collect_events())
321
-
322
- repository = Repository(event_store)
323
- self.assertTrue(aggregate.id in repository)
324
- self.assertFalse(uuid4() in repository)
325
-
326
- def test_cache_maxsize_zero(self):
327
- transcoder = JSONTranscoder()
328
- transcoder.register(UUIDAsHex())
329
- transcoder.register(DecimalAsStr())
330
- transcoder.register(DatetimeAsISO())
331
- transcoder.register(EmailAddressAsStr())
332
-
333
- event_recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
334
- event_recorder.create_table()
335
- event_store = EventStore(
336
- mapper=Mapper(transcoder=transcoder),
337
- recorder=event_recorder,
338
- )
339
- repository = Repository(event_store, cache_maxsize=0)
340
- self.assertEqual(type(repository.cache), Cache)
341
-
342
- aggregate = Aggregate()
343
-
344
- self.assertFalse(aggregate.id in repository)
345
- event_store.put(aggregate.collect_events())
346
- self.assertTrue(aggregate.id in repository)
347
-
348
- self.assertEqual(1, repository.get(aggregate.id).version)
349
-
350
- aggregate.trigger_event(Aggregate.Event)
351
- event_store.put(aggregate.collect_events())
352
- self.assertEqual(2, repository.get(aggregate.id).version)
353
-
354
- def test_cache_maxsize_nonzero(self):
355
- transcoder = JSONTranscoder()
356
- transcoder.register(UUIDAsHex())
357
- transcoder.register(DecimalAsStr())
358
- transcoder.register(DatetimeAsISO())
359
- transcoder.register(EmailAddressAsStr())
360
-
361
- event_recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
362
- event_recorder.create_table()
363
- event_store = EventStore(
364
- mapper=Mapper(transcoder=transcoder),
365
- recorder=event_recorder,
366
- )
367
- repository = Repository(event_store, cache_maxsize=2)
368
- self.assertEqual(type(repository.cache), LRUCache)
369
-
370
- aggregate1 = Aggregate()
371
- self.assertFalse(aggregate1.id in repository)
372
- event_store.put(aggregate1.collect_events())
373
- self.assertTrue(aggregate1.id in repository)
374
-
375
- aggregate2 = Aggregate()
376
- self.assertFalse(aggregate2.id in repository)
377
- event_store.put(aggregate2.collect_events())
378
- self.assertTrue(aggregate2.id in repository)
379
-
380
- aggregate3 = Aggregate()
381
- self.assertFalse(aggregate3.id in repository)
382
- event_store.put(aggregate3.collect_events())
383
- self.assertTrue(aggregate3.id in repository)
384
-
385
- self.assertFalse(aggregate1.id in repository.cache.cache)
386
-
387
- self.assertEqual(1, repository.get(aggregate1.id).version)
388
- self.assertEqual(1, repository.get(aggregate2.id).version)
389
- self.assertEqual(1, repository.get(aggregate3.id).version)
390
-
391
- aggregate1.trigger_event(Aggregate.Event)
392
- event_store.put(aggregate1.collect_events())
393
- self.assertEqual(2, repository.get(aggregate1.id).version)
394
-
395
- def test_cache_fastforward_false(self):
396
- transcoder = JSONTranscoder()
397
- transcoder.register(UUIDAsHex())
398
- transcoder.register(DecimalAsStr())
399
- transcoder.register(DatetimeAsISO())
400
- transcoder.register(EmailAddressAsStr())
401
-
402
- event_recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
403
- event_recorder.create_table()
404
- event_store = EventStore(
405
- mapper=Mapper(transcoder=transcoder),
406
- recorder=event_recorder,
407
- )
408
- repository = Repository(
409
- event_store,
410
- cache_maxsize=2,
411
- fastforward=False,
412
- )
413
-
414
- aggregate = Aggregate()
415
- event_store.put(aggregate.collect_events())
416
- self.assertEqual(1, repository.get(aggregate.id).version)
417
-
418
- aggregate.trigger_event(Aggregate.Event)
419
- event_store.put(aggregate.collect_events())
420
- self.assertEqual(1, repository.get(aggregate.id).version)
421
-
422
- def test_cache_raises_aggregate_not_found_when_projector_func_returns_none(self):
423
- transcoder = JSONTranscoder()
424
- transcoder.register(UUIDAsHex())
425
- transcoder.register(DecimalAsStr())
426
- transcoder.register(DatetimeAsISO())
427
- transcoder.register(EmailAddressAsStr())
428
-
429
- event_recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
430
- event_recorder.create_table()
431
- event_store = EventStore(
432
- mapper=Mapper(transcoder=transcoder),
433
- recorder=event_recorder,
434
- )
435
- repository = Repository(
436
- event_store,
437
- cache_maxsize=2,
438
- )
439
-
440
- aggregate = Aggregate()
441
- event_store.put(aggregate.collect_events())
442
- self.assertEqual(1, repository.get(aggregate.id).version)
443
-
444
- aggregate.trigger_event(Aggregate.Event)
445
- event_store.put(aggregate.collect_events())
446
- with self.assertRaises(AggregateNotFoundError):
447
- repository.get(aggregate.id, projector_func=lambda _, __: None)
448
-
449
- def test_fastforward_lock(self):
450
- repository = Repository(
451
- EventStore(
452
- mapper=Mapper(transcoder=JSONTranscoder()),
453
- recorder=POPOAggregateRecorder(),
454
- ),
455
- cache_maxsize=2,
456
- )
457
- cache_maxsize = repository._fastforward_locks_cache.maxsize
458
- aggregate_ids = [uuid4() for i in range(cache_maxsize + 1)]
459
- self.assertEqual(0, len(repository._fastforward_locks_inuse))
460
- self.assertEqual(0, len(repository._fastforward_locks_cache.cache))
461
-
462
- # Use a lock and check it's "in use".
463
- repository._use_fastforward_lock(aggregate_ids[0])
464
- self.assertEqual(1, len(repository._fastforward_locks_inuse))
465
- self.assertEqual(0, len(repository._fastforward_locks_cache.cache))
466
- self.assertEqual(1, repository._fastforward_locks_inuse[aggregate_ids[0]][1])
467
-
468
- # Disuse a lock and check it's "cached" and not "in use".
469
- repository._disuse_fastforward_lock(aggregate_ids[0])
470
- self.assertEqual(0, len(repository._fastforward_locks_inuse))
471
- self.assertEqual(1, len(repository._fastforward_locks_cache.cache))
472
-
473
- # Use two locks and check it's "in use" by two users.
474
- repository._use_fastforward_lock(aggregate_ids[0])
475
- repository._use_fastforward_lock(aggregate_ids[0])
476
- self.assertEqual(1, len(repository._fastforward_locks_inuse))
477
- self.assertEqual(0, len(repository._fastforward_locks_cache.cache))
478
- self.assertEqual(2, repository._fastforward_locks_inuse[aggregate_ids[0]][1])
479
-
480
- # Disuse the lock and check it's still "in use" by one user.
481
- repository._disuse_fastforward_lock(aggregate_ids[0])
482
- self.assertEqual(1, len(repository._fastforward_locks_inuse))
483
- self.assertEqual(0, len(repository._fastforward_locks_cache.cache))
484
- self.assertEqual(1, repository._fastforward_locks_inuse[aggregate_ids[0]][1])
485
-
486
- # Disuse the lock and check it's cached and not "in use".
487
- repository._disuse_fastforward_lock(aggregate_ids[0])
488
- self.assertEqual(0, len(repository._fastforward_locks_inuse))
489
- self.assertEqual(1, len(repository._fastforward_locks_cache.cache))
490
-
491
- # Use more locks that the cache holds.
492
- for aggregate_id in aggregate_ids:
493
- repository._use_fastforward_lock(aggregate_id)
494
- self.assertEqual(len(aggregate_ids), len(repository._fastforward_locks_inuse))
495
- self.assertEqual(0, len(repository._fastforward_locks_cache.cache))
496
-
497
- # Disuse all the locks and check the cache has evicted one.
498
- self.assertEqual(len(aggregate_ids), cache_maxsize + 1)
499
- for aggregate_id in aggregate_ids:
500
- repository._disuse_fastforward_lock(aggregate_id)
501
- self.assertEqual(0, len(repository._fastforward_locks_inuse))
502
- self.assertEqual(
503
- len(aggregate_ids) - 1, len(repository._fastforward_locks_cache.cache)
504
- )
@@ -1,68 +0,0 @@
1
- from decimal import Decimal
2
- from unittest import TestCase
3
-
4
- from eventsourcing.domain import Snapshot
5
- from eventsourcing.persistence import (
6
- DatetimeAsISO,
7
- DecimalAsStr,
8
- EventStore,
9
- JSONTranscoder,
10
- Mapper,
11
- UUIDAsHex,
12
- )
13
- from eventsourcing.sqlite import SQLiteAggregateRecorder, SQLiteDatastore
14
- from eventsourcing.tests.application import EmailAddressAsStr
15
- from eventsourcing.tests.domain import BankAccount
16
-
17
-
18
- class TestSnapshotting(TestCase):
19
- def test(self):
20
- # Open an account.
21
- account = BankAccount.open(
22
- full_name="Alice",
23
- email_address="alice@example.com",
24
- )
25
-
26
- # Credit the account.
27
- account.append_transaction(Decimal("10.00"))
28
- account.append_transaction(Decimal("25.00"))
29
- account.append_transaction(Decimal("30.00"))
30
-
31
- transcoder = JSONTranscoder()
32
- transcoder.register(UUIDAsHex())
33
- transcoder.register(DecimalAsStr())
34
- transcoder.register(DatetimeAsISO())
35
- transcoder.register(EmailAddressAsStr())
36
-
37
- snapshot_store = EventStore(
38
- mapper=Mapper(transcoder=transcoder),
39
- recorder=SQLiteAggregateRecorder(
40
- SQLiteDatastore(":memory:"),
41
- events_table_name="snapshots",
42
- ),
43
- )
44
- snapshot_store.recorder.create_table()
45
-
46
- # Clear pending events.
47
- account.collect_events()
48
-
49
- # Take a snapshot.
50
- snapshot = Snapshot.take(account)
51
-
52
- self.assertNotIn("pending_events", snapshot.state)
53
-
54
- # Store snapshot.
55
- snapshot_store.put([snapshot])
56
-
57
- # Get snapshot.
58
- snapshots = snapshot_store.get(account.id, desc=True, limit=1)
59
- snapshot = next(snapshots)
60
- assert isinstance(snapshot, Snapshot)
61
-
62
- # Reconstruct the bank account.
63
- copy = snapshot.mutate(None)
64
- assert isinstance(copy, BankAccount)
65
-
66
- # Check copy has correct attribute values.
67
- assert copy.id == account.id
68
- assert copy.balance == Decimal("65.00")