eventsourcing 9.2.22__py3-none-any.whl → 9.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of eventsourcing might be problematic. Click here for more details.
- eventsourcing/__init__.py +1 -1
- eventsourcing/application.py +116 -135
- eventsourcing/cipher.py +15 -12
- eventsourcing/dispatch.py +31 -91
- eventsourcing/domain.py +220 -226
- eventsourcing/examples/__init__.py +0 -0
- eventsourcing/examples/aggregate1/__init__.py +0 -0
- eventsourcing/examples/aggregate1/application.py +27 -0
- eventsourcing/examples/aggregate1/domainmodel.py +16 -0
- eventsourcing/examples/aggregate1/test_application.py +37 -0
- eventsourcing/examples/aggregate2/__init__.py +0 -0
- eventsourcing/examples/aggregate2/application.py +27 -0
- eventsourcing/examples/aggregate2/domainmodel.py +22 -0
- eventsourcing/examples/aggregate2/test_application.py +37 -0
- eventsourcing/examples/aggregate3/__init__.py +0 -0
- eventsourcing/examples/aggregate3/application.py +27 -0
- eventsourcing/examples/aggregate3/domainmodel.py +38 -0
- eventsourcing/examples/aggregate3/test_application.py +37 -0
- eventsourcing/examples/aggregate4/__init__.py +0 -0
- eventsourcing/examples/aggregate4/application.py +27 -0
- eventsourcing/examples/aggregate4/domainmodel.py +114 -0
- eventsourcing/examples/aggregate4/test_application.py +38 -0
- eventsourcing/examples/aggregate5/__init__.py +0 -0
- eventsourcing/examples/aggregate5/application.py +27 -0
- eventsourcing/examples/aggregate5/domainmodel.py +131 -0
- eventsourcing/examples/aggregate5/test_application.py +38 -0
- eventsourcing/examples/aggregate6/__init__.py +0 -0
- eventsourcing/examples/aggregate6/application.py +30 -0
- eventsourcing/examples/aggregate6/domainmodel.py +123 -0
- eventsourcing/examples/aggregate6/test_application.py +38 -0
- eventsourcing/examples/aggregate6a/__init__.py +0 -0
- eventsourcing/examples/aggregate6a/application.py +40 -0
- eventsourcing/examples/aggregate6a/domainmodel.py +149 -0
- eventsourcing/examples/aggregate6a/test_application.py +45 -0
- eventsourcing/examples/aggregate7/__init__.py +0 -0
- eventsourcing/examples/aggregate7/application.py +48 -0
- eventsourcing/examples/aggregate7/domainmodel.py +144 -0
- eventsourcing/examples/aggregate7/persistence.py +57 -0
- eventsourcing/examples/aggregate7/test_application.py +38 -0
- eventsourcing/examples/aggregate7/test_compression_and_encryption.py +45 -0
- eventsourcing/examples/aggregate7/test_snapshotting_intervals.py +67 -0
- eventsourcing/examples/aggregate7a/__init__.py +0 -0
- eventsourcing/examples/aggregate7a/application.py +56 -0
- eventsourcing/examples/aggregate7a/domainmodel.py +170 -0
- eventsourcing/examples/aggregate7a/test_application.py +46 -0
- eventsourcing/examples/aggregate7a/test_compression_and_encryption.py +45 -0
- eventsourcing/examples/aggregate8/__init__.py +0 -0
- eventsourcing/examples/aggregate8/application.py +47 -0
- eventsourcing/examples/aggregate8/domainmodel.py +65 -0
- eventsourcing/examples/aggregate8/persistence.py +57 -0
- eventsourcing/examples/aggregate8/test_application.py +37 -0
- eventsourcing/examples/aggregate8/test_compression_and_encryption.py +44 -0
- eventsourcing/examples/aggregate8/test_snapshotting_intervals.py +38 -0
- eventsourcing/examples/bankaccounts/__init__.py +0 -0
- eventsourcing/examples/bankaccounts/application.py +70 -0
- eventsourcing/examples/bankaccounts/domainmodel.py +56 -0
- eventsourcing/examples/bankaccounts/test.py +173 -0
- eventsourcing/examples/cargoshipping/__init__.py +0 -0
- eventsourcing/examples/cargoshipping/application.py +126 -0
- eventsourcing/examples/cargoshipping/domainmodel.py +330 -0
- eventsourcing/examples/cargoshipping/interface.py +143 -0
- eventsourcing/examples/cargoshipping/test.py +231 -0
- eventsourcing/examples/contentmanagement/__init__.py +0 -0
- eventsourcing/examples/contentmanagement/application.py +118 -0
- eventsourcing/examples/contentmanagement/domainmodel.py +69 -0
- eventsourcing/examples/contentmanagement/test.py +180 -0
- eventsourcing/examples/contentmanagement/utils.py +26 -0
- eventsourcing/examples/contentmanagementsystem/__init__.py +0 -0
- eventsourcing/examples/contentmanagementsystem/application.py +54 -0
- eventsourcing/examples/contentmanagementsystem/postgres.py +17 -0
- eventsourcing/examples/contentmanagementsystem/sqlite.py +17 -0
- eventsourcing/examples/contentmanagementsystem/system.py +14 -0
- eventsourcing/examples/contentmanagementsystem/test_system.py +180 -0
- eventsourcing/examples/searchablecontent/__init__.py +0 -0
- eventsourcing/examples/searchablecontent/application.py +45 -0
- eventsourcing/examples/searchablecontent/persistence.py +23 -0
- eventsourcing/examples/searchablecontent/postgres.py +118 -0
- eventsourcing/examples/searchablecontent/sqlite.py +136 -0
- eventsourcing/examples/searchablecontent/test_application.py +110 -0
- eventsourcing/examples/searchablecontent/test_recorder.py +68 -0
- eventsourcing/examples/searchabletimestamps/__init__.py +0 -0
- eventsourcing/examples/searchabletimestamps/application.py +32 -0
- eventsourcing/examples/searchabletimestamps/persistence.py +20 -0
- eventsourcing/examples/searchabletimestamps/postgres.py +110 -0
- eventsourcing/examples/searchabletimestamps/sqlite.py +99 -0
- eventsourcing/examples/searchabletimestamps/test_searchabletimestamps.py +94 -0
- eventsourcing/examples/test_invoice.py +176 -0
- eventsourcing/examples/test_parking_lot.py +206 -0
- eventsourcing/interface.py +2 -2
- eventsourcing/persistence.py +85 -81
- eventsourcing/popo.py +30 -31
- eventsourcing/postgres.py +379 -590
- eventsourcing/sqlite.py +91 -99
- eventsourcing/system.py +52 -57
- eventsourcing/tests/application.py +20 -32
- eventsourcing/tests/application_tests/__init__.py +0 -0
- eventsourcing/tests/application_tests/test_application_with_automatic_snapshotting.py +55 -0
- eventsourcing/tests/application_tests/test_application_with_popo.py +22 -0
- eventsourcing/tests/application_tests/test_application_with_postgres.py +75 -0
- eventsourcing/tests/application_tests/test_application_with_sqlite.py +72 -0
- eventsourcing/tests/application_tests/test_cache.py +134 -0
- eventsourcing/tests/application_tests/test_event_sourced_log.py +162 -0
- eventsourcing/tests/application_tests/test_notificationlog.py +232 -0
- eventsourcing/tests/application_tests/test_notificationlogreader.py +126 -0
- eventsourcing/tests/application_tests/test_processapplication.py +110 -0
- eventsourcing/tests/application_tests/test_processingpolicy.py +109 -0
- eventsourcing/tests/application_tests/test_repository.py +504 -0
- eventsourcing/tests/application_tests/test_snapshotting.py +68 -0
- eventsourcing/tests/application_tests/test_upcasting.py +459 -0
- eventsourcing/tests/docs_tests/__init__.py +0 -0
- eventsourcing/tests/docs_tests/test_docs.py +293 -0
- eventsourcing/tests/domain.py +1 -1
- eventsourcing/tests/domain_tests/__init__.py +0 -0
- eventsourcing/tests/domain_tests/test_aggregate.py +1180 -0
- eventsourcing/tests/domain_tests/test_aggregate_decorators.py +1604 -0
- eventsourcing/tests/domain_tests/test_domainevent.py +80 -0
- eventsourcing/tests/interface_tests/__init__.py +0 -0
- eventsourcing/tests/interface_tests/test_remotenotificationlog.py +258 -0
- eventsourcing/tests/persistence.py +52 -50
- eventsourcing/tests/persistence_tests/__init__.py +0 -0
- eventsourcing/tests/persistence_tests/test_aes.py +93 -0
- eventsourcing/tests/persistence_tests/test_connection_pool.py +722 -0
- eventsourcing/tests/persistence_tests/test_eventstore.py +72 -0
- eventsourcing/tests/persistence_tests/test_infrastructure_factory.py +21 -0
- eventsourcing/tests/persistence_tests/test_mapper.py +113 -0
- eventsourcing/tests/persistence_tests/test_noninterleaving_notification_ids.py +69 -0
- eventsourcing/tests/persistence_tests/test_popo.py +124 -0
- eventsourcing/tests/persistence_tests/test_postgres.py +1119 -0
- eventsourcing/tests/persistence_tests/test_sqlite.py +348 -0
- eventsourcing/tests/persistence_tests/test_transcoder.py +44 -0
- eventsourcing/tests/postgres_utils.py +7 -7
- eventsourcing/tests/system_tests/__init__.py +0 -0
- eventsourcing/tests/system_tests/test_runner.py +935 -0
- eventsourcing/tests/system_tests/test_system.py +284 -0
- eventsourcing/tests/utils_tests/__init__.py +0 -0
- eventsourcing/tests/utils_tests/test_utils.py +226 -0
- eventsourcing/utils.py +47 -50
- {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0.dist-info}/METADATA +29 -79
- eventsourcing-9.3.0.dist-info/RECORD +145 -0
- {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0.dist-info}/WHEEL +1 -2
- eventsourcing-9.2.22.dist-info/RECORD +0 -25
- eventsourcing-9.2.22.dist-info/top_level.txt +0 -1
- {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0.dist-info}/AUTHORS +0 -0
- {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
from sqlite3 import Connection
|
|
3
|
+
from unittest import TestCase
|
|
4
|
+
from unittest.mock import Mock
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
from eventsourcing.persistence import (
|
|
8
|
+
DatabaseError,
|
|
9
|
+
DataError,
|
|
10
|
+
InfrastructureFactory,
|
|
11
|
+
IntegrityError,
|
|
12
|
+
InterfaceError,
|
|
13
|
+
InternalError,
|
|
14
|
+
NotSupportedError,
|
|
15
|
+
OperationalError,
|
|
16
|
+
PersistenceError,
|
|
17
|
+
ProgrammingError,
|
|
18
|
+
StoredEvent,
|
|
19
|
+
)
|
|
20
|
+
from eventsourcing.sqlite import (
|
|
21
|
+
Factory,
|
|
22
|
+
SQLiteAggregateRecorder,
|
|
23
|
+
SQLiteApplicationRecorder,
|
|
24
|
+
SQLiteConnectionPool,
|
|
25
|
+
SQLiteDatastore,
|
|
26
|
+
SQLiteProcessRecorder,
|
|
27
|
+
SQLiteTransaction,
|
|
28
|
+
)
|
|
29
|
+
from eventsourcing.tests.persistence import (
|
|
30
|
+
AggregateRecorderTestCase,
|
|
31
|
+
ApplicationRecorderTestCase,
|
|
32
|
+
InfrastructureFactoryTestCase,
|
|
33
|
+
ProcessRecorderTestCase,
|
|
34
|
+
tmpfile_uris,
|
|
35
|
+
)
|
|
36
|
+
from eventsourcing.tests.persistence_tests.test_connection_pool import (
|
|
37
|
+
TestConnectionPool,
|
|
38
|
+
)
|
|
39
|
+
from eventsourcing.utils import Environment
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestTransaction(TestCase):
|
|
43
|
+
def setUp(self) -> None:
|
|
44
|
+
self.mock = Mock(Connection)
|
|
45
|
+
self.t = SQLiteTransaction(self.mock, commit=True)
|
|
46
|
+
|
|
47
|
+
def test_calls_commit_if_error_not_raised_during_transaction(self):
|
|
48
|
+
with self.t:
|
|
49
|
+
pass
|
|
50
|
+
self.mock.commit.assert_called()
|
|
51
|
+
self.mock.rollback.assert_not_called()
|
|
52
|
+
|
|
53
|
+
def test_calls_rollback_if_error_is_raised_during_transaction(self):
|
|
54
|
+
with self.assertRaises(TypeError), self.t:
|
|
55
|
+
raise TypeError
|
|
56
|
+
self.mock.commit.assert_not_called()
|
|
57
|
+
self.mock.rollback.assert_called()
|
|
58
|
+
|
|
59
|
+
def test_converts_errors_raised_in_transactions(self):
|
|
60
|
+
errors = [
|
|
61
|
+
(InterfaceError, sqlite3.InterfaceError),
|
|
62
|
+
(DataError, sqlite3.DataError),
|
|
63
|
+
(OperationalError, sqlite3.OperationalError),
|
|
64
|
+
(IntegrityError, sqlite3.IntegrityError),
|
|
65
|
+
(InternalError, sqlite3.InternalError),
|
|
66
|
+
(ProgrammingError, sqlite3.ProgrammingError),
|
|
67
|
+
(NotSupportedError, sqlite3.NotSupportedError),
|
|
68
|
+
(DatabaseError, sqlite3.DatabaseError),
|
|
69
|
+
(PersistenceError, sqlite3.Error),
|
|
70
|
+
]
|
|
71
|
+
for es_err, psy_err in errors:
|
|
72
|
+
with self.assertRaises(es_err), self.t:
|
|
73
|
+
raise psy_err
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class SQLiteConnectionPoolTestCase(TestConnectionPool):
|
|
77
|
+
db_name: str
|
|
78
|
+
|
|
79
|
+
def create_pool(
|
|
80
|
+
self,
|
|
81
|
+
pool_size=1,
|
|
82
|
+
max_overflow=0,
|
|
83
|
+
pool_timeout=5.0,
|
|
84
|
+
max_age=None,
|
|
85
|
+
pre_ping=False,
|
|
86
|
+
mutually_exclusive_read_write=True,
|
|
87
|
+
):
|
|
88
|
+
return SQLiteConnectionPool(
|
|
89
|
+
db_name=self.db_name,
|
|
90
|
+
pool_size=pool_size,
|
|
91
|
+
max_overflow=max_overflow,
|
|
92
|
+
pool_timeout=pool_timeout,
|
|
93
|
+
max_age=max_age,
|
|
94
|
+
pre_ping=pre_ping,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def test_close_on_server_after_returning_with_pre_ping(self):
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
def test_close_on_server_after_returning_without_pre_ping(self):
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TestSQLiteConnectionPoolWithInMemoryDB(SQLiteConnectionPoolTestCase):
|
|
105
|
+
allowed_connecting_time = 0.01
|
|
106
|
+
|
|
107
|
+
def setUp(self) -> None:
|
|
108
|
+
self.db_name = ":memory:"
|
|
109
|
+
|
|
110
|
+
def test_reader_writer(self):
|
|
111
|
+
super()._test_reader_writer_with_mutually_exclusive_read_write()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class TestSQLiteConnectionPoolWithFileDB(SQLiteConnectionPoolTestCase):
|
|
115
|
+
allowed_connecting_time = 0.01
|
|
116
|
+
|
|
117
|
+
def setUp(self) -> None:
|
|
118
|
+
self.tmp_urls = tmpfile_uris()
|
|
119
|
+
self.db_name = next(self.tmp_urls)
|
|
120
|
+
|
|
121
|
+
def test_reader_writer(self):
|
|
122
|
+
super()._test_reader_writer_without_mutually_exclusive_read_write()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class TestSqliteDatastore(TestCase):
|
|
126
|
+
def setUp(self) -> None:
|
|
127
|
+
self.datastore = SQLiteDatastore(":memory:")
|
|
128
|
+
|
|
129
|
+
def test_connect_failure_raises_interface_error(self):
|
|
130
|
+
datastore = SQLiteDatastore(None)
|
|
131
|
+
with self.assertRaises(InterfaceError), datastore.transaction(commit=False):
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
def test_transaction(self):
|
|
135
|
+
transaction = self.datastore.transaction(commit=False)
|
|
136
|
+
with transaction as cursor:
|
|
137
|
+
cursor.execute("SELECT 1")
|
|
138
|
+
rows = cursor.fetchall()
|
|
139
|
+
self.assertEqual(len(rows), 1)
|
|
140
|
+
self.assertEqual(len(rows[0]), 1)
|
|
141
|
+
self.assertEqual(rows[0][0], 1)
|
|
142
|
+
|
|
143
|
+
def test_sets_wal_journal_mode_if_not_memory(self):
|
|
144
|
+
# Check datastore for in-memory database.
|
|
145
|
+
with self.datastore.transaction(commit=False):
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
self.assertFalse(self.datastore.pool.is_journal_mode_wal)
|
|
149
|
+
self.assertFalse(self.datastore.pool.journal_mode_was_changed_to_wal)
|
|
150
|
+
|
|
151
|
+
# Create datastore for non-existing file database.
|
|
152
|
+
self.uris = tmpfile_uris()
|
|
153
|
+
self.db_uri = next(self.uris)
|
|
154
|
+
datastore = SQLiteDatastore(self.db_uri)
|
|
155
|
+
|
|
156
|
+
with datastore.transaction(commit=False):
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
self.assertTrue(datastore.pool.is_journal_mode_wal)
|
|
160
|
+
self.assertTrue(datastore.pool.journal_mode_was_changed_to_wal)
|
|
161
|
+
|
|
162
|
+
datastore.close()
|
|
163
|
+
del datastore
|
|
164
|
+
|
|
165
|
+
# Recreate datastore for existing database.
|
|
166
|
+
datastore = SQLiteDatastore(self.db_uri)
|
|
167
|
+
with datastore.transaction(commit=False):
|
|
168
|
+
pass
|
|
169
|
+
self.assertTrue(datastore.pool.is_journal_mode_wal)
|
|
170
|
+
self.assertFalse(datastore.pool.journal_mode_was_changed_to_wal)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class TestSQLiteAggregateRecorder(AggregateRecorderTestCase):
|
|
174
|
+
def create_recorder(self):
|
|
175
|
+
recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
|
|
176
|
+
recorder.create_table()
|
|
177
|
+
return recorder
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class TestSQLiteAggregateRecorderErrors(TestCase):
|
|
181
|
+
def test_raises_operational_error_when_creating_table_fails(self):
|
|
182
|
+
recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
|
|
183
|
+
# Broken create table statements.
|
|
184
|
+
recorder.create_table_statements = ["BLAH"]
|
|
185
|
+
with self.assertRaises(OperationalError):
|
|
186
|
+
recorder.create_table()
|
|
187
|
+
|
|
188
|
+
def test_raises_operational_error_when_inserting_fails(self):
|
|
189
|
+
recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
|
|
190
|
+
# Don't create table.
|
|
191
|
+
with self.assertRaises(OperationalError):
|
|
192
|
+
recorder.insert_events([])
|
|
193
|
+
|
|
194
|
+
def test_raises_operational_error_when_selecting_fails(self):
|
|
195
|
+
recorder = SQLiteAggregateRecorder(SQLiteDatastore(":memory:"))
|
|
196
|
+
# Don't create table.
|
|
197
|
+
with self.assertRaises(OperationalError):
|
|
198
|
+
recorder.select_events(uuid4())
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class TestSQLiteApplicationRecorder(ApplicationRecorderTestCase):
|
|
202
|
+
def create_recorder(self):
|
|
203
|
+
recorder = SQLiteApplicationRecorder(
|
|
204
|
+
SQLiteDatastore(db_name=self.db_uri, pool_size=100)
|
|
205
|
+
)
|
|
206
|
+
recorder.create_table()
|
|
207
|
+
return recorder
|
|
208
|
+
|
|
209
|
+
def test_insert_select(self):
|
|
210
|
+
self.db_uri = ":memory:"
|
|
211
|
+
super().test_insert_select()
|
|
212
|
+
|
|
213
|
+
def test_concurrent_no_conflicts(self):
|
|
214
|
+
self.uris = tmpfile_uris()
|
|
215
|
+
self.db_uri = next(self.uris)
|
|
216
|
+
super().test_concurrent_no_conflicts()
|
|
217
|
+
|
|
218
|
+
def test_concurrent_no_conflicts_in_memory_db(self):
|
|
219
|
+
self.db_uri = "file::memory:?cache=shared"
|
|
220
|
+
super().test_concurrent_no_conflicts()
|
|
221
|
+
|
|
222
|
+
def test_concurrent_throughput(self):
|
|
223
|
+
self.uris = tmpfile_uris()
|
|
224
|
+
self.db_uri = next(self.uris)
|
|
225
|
+
super().test_concurrent_throughput()
|
|
226
|
+
|
|
227
|
+
def test_concurrent_throughput_in_memory_db(self):
|
|
228
|
+
self.db_uri = "file::memory:?cache=shared"
|
|
229
|
+
super().test_concurrent_throughput()
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class TestSQLiteApplicationRecorderErrors(TestCase):
|
|
233
|
+
def test_insert_raises_operational_error_if_table_not_created(self):
|
|
234
|
+
recorder = SQLiteApplicationRecorder(SQLiteDatastore(":memory:"))
|
|
235
|
+
stored_event1 = StoredEvent(
|
|
236
|
+
originator_id=uuid4(),
|
|
237
|
+
originator_version=1,
|
|
238
|
+
topic="topic1",
|
|
239
|
+
state=b"",
|
|
240
|
+
)
|
|
241
|
+
with self.assertRaises(OperationalError):
|
|
242
|
+
# Haven't created table.
|
|
243
|
+
recorder.insert_events([stored_event1])
|
|
244
|
+
|
|
245
|
+
def test_select_raises_operational_error_if_table_not_created(self):
|
|
246
|
+
recorder = SQLiteApplicationRecorder(SQLiteDatastore(":memory:"))
|
|
247
|
+
with self.assertRaises(OperationalError):
|
|
248
|
+
recorder.select_events(uuid4())
|
|
249
|
+
|
|
250
|
+
with self.assertRaises(OperationalError):
|
|
251
|
+
recorder.select_notifications(start=1, limit=1)
|
|
252
|
+
|
|
253
|
+
with self.assertRaises(OperationalError):
|
|
254
|
+
recorder.max_notification_id()
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class TestSQLiteProcessRecorder(ProcessRecorderTestCase):
|
|
258
|
+
def create_recorder(self):
|
|
259
|
+
recorder = SQLiteProcessRecorder(SQLiteDatastore(":memory:"))
|
|
260
|
+
recorder.create_table()
|
|
261
|
+
return recorder
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class TestSQLiteProcessRecorderErrors(TestCase):
|
|
265
|
+
def test_insert_raises_operational_error_if_table_not_created(self):
|
|
266
|
+
recorder = SQLiteProcessRecorder(SQLiteDatastore(":memory:"))
|
|
267
|
+
stored_event1 = StoredEvent(
|
|
268
|
+
originator_id=uuid4(),
|
|
269
|
+
originator_version=1,
|
|
270
|
+
topic="topic1",
|
|
271
|
+
state=b"",
|
|
272
|
+
)
|
|
273
|
+
with self.assertRaises(OperationalError):
|
|
274
|
+
recorder.insert_events([stored_event1])
|
|
275
|
+
|
|
276
|
+
def test_select_raises_operational_error_if_table_not_created(self):
|
|
277
|
+
recorder = SQLiteProcessRecorder(SQLiteDatastore(":memory:"))
|
|
278
|
+
with self.assertRaises(OperationalError):
|
|
279
|
+
recorder.select_events(uuid4())
|
|
280
|
+
|
|
281
|
+
with self.assertRaises(OperationalError):
|
|
282
|
+
recorder.max_tracking_id("application name")
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class TestSQLiteInfrastructureFactory(InfrastructureFactoryTestCase):
|
|
286
|
+
def expected_factory_class(self):
|
|
287
|
+
return Factory
|
|
288
|
+
|
|
289
|
+
def expected_aggregate_recorder_class(self):
|
|
290
|
+
return SQLiteAggregateRecorder
|
|
291
|
+
|
|
292
|
+
def expected_application_recorder_class(self):
|
|
293
|
+
return SQLiteApplicationRecorder
|
|
294
|
+
|
|
295
|
+
def expected_process_recorder_class(self):
|
|
296
|
+
return SQLiteProcessRecorder
|
|
297
|
+
|
|
298
|
+
def setUp(self) -> None:
|
|
299
|
+
self.env = Environment("TestCase")
|
|
300
|
+
self.env[InfrastructureFactory.PERSISTENCE_MODULE] = Factory.__module__
|
|
301
|
+
self.env[Factory.SQLITE_DBNAME] = ":memory:"
|
|
302
|
+
super().setUp()
|
|
303
|
+
|
|
304
|
+
def tearDown(self) -> None:
|
|
305
|
+
super().tearDown()
|
|
306
|
+
if Factory.SQLITE_DBNAME in self.env:
|
|
307
|
+
del self.env[Factory.SQLITE_DBNAME]
|
|
308
|
+
if Factory.SQLITE_LOCK_TIMEOUT in self.env:
|
|
309
|
+
del self.env[Factory.SQLITE_LOCK_TIMEOUT]
|
|
310
|
+
|
|
311
|
+
def test_construct_raises_environment_error_when_dbname_missing(self):
|
|
312
|
+
del self.env[Factory.SQLITE_DBNAME]
|
|
313
|
+
with self.assertRaises(EnvironmentError) as cm:
|
|
314
|
+
InfrastructureFactory.construct(self.env)
|
|
315
|
+
self.assertEqual(
|
|
316
|
+
cm.exception.args[0],
|
|
317
|
+
"SQLite database name not found in environment with keys: "
|
|
318
|
+
"TESTCASE_SQLITE_DBNAME, SQLITE_DBNAME",
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
def test_environment_error_raised_when_lock_timeout_not_an_int(self):
|
|
322
|
+
self.env[Factory.SQLITE_LOCK_TIMEOUT] = "abc"
|
|
323
|
+
with self.assertRaises(EnvironmentError) as cm:
|
|
324
|
+
Factory(self.env)
|
|
325
|
+
self.assertEqual(
|
|
326
|
+
cm.exception.args[0],
|
|
327
|
+
"SQLite environment value for key 'SQLITE_LOCK_TIMEOUT' "
|
|
328
|
+
"is invalid. If set, an int or empty string is expected: 'abc'",
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
def test_lock_timeout_value(self):
|
|
332
|
+
factory = Factory(self.env)
|
|
333
|
+
self.assertEqual(factory.datastore.pool.lock_timeout, None)
|
|
334
|
+
|
|
335
|
+
self.env[Factory.SQLITE_LOCK_TIMEOUT] = ""
|
|
336
|
+
factory = Factory(self.env)
|
|
337
|
+
self.assertEqual(factory.datastore.pool.lock_timeout, None)
|
|
338
|
+
|
|
339
|
+
self.env[Factory.SQLITE_LOCK_TIMEOUT] = "10"
|
|
340
|
+
factory = Factory(self.env)
|
|
341
|
+
self.assertEqual(factory.datastore.pool.lock_timeout, 10)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
del AggregateRecorderTestCase
|
|
345
|
+
del ApplicationRecorderTestCase
|
|
346
|
+
del ProcessRecorderTestCase
|
|
347
|
+
del InfrastructureFactoryTestCase
|
|
348
|
+
del SQLiteConnectionPoolTestCase
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from unittest import skip
|
|
2
|
+
|
|
3
|
+
from eventsourcing.persistence import JSONTranscoder, UUIDAsHex
|
|
4
|
+
from eventsourcing.tests.persistence import (
|
|
5
|
+
CustomType1AsDict,
|
|
6
|
+
CustomType2AsDict,
|
|
7
|
+
TranscoderTestCase,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestJSONTranscoder(TranscoderTestCase):
|
|
12
|
+
def construct_transcoder(self):
|
|
13
|
+
transcoder = JSONTranscoder()
|
|
14
|
+
transcoder.register(CustomType1AsDict())
|
|
15
|
+
transcoder.register(CustomType2AsDict())
|
|
16
|
+
transcoder.register(UUIDAsHex())
|
|
17
|
+
return transcoder
|
|
18
|
+
|
|
19
|
+
@skip("test_tuple(): JSONTranscoder converts tuples to lists")
|
|
20
|
+
def test_tuple(self):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@skip("test_mixed(): JSONTranscoder converts tuples to lists")
|
|
24
|
+
def test_mixed(self):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@skip("test_dict_subclass(): JSONTranscoder converts dict subclasses to dict")
|
|
28
|
+
def test_dict_subclass(self):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@skip("test_list_subclass(): JSONTranscoder converts list subclasses to list")
|
|
32
|
+
def test_list_subclass(self):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
@skip("test_str_subclass(): JSONTranscoder converts str subclasses to str")
|
|
36
|
+
def test_str_subclass(self):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
@skip("test_int_subclass(): JSONTranscoder converts int subclasses to int")
|
|
40
|
+
def test_int_subclass(self):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
del TranscoderTestCase
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import psycopg
|
|
2
2
|
|
|
3
3
|
from eventsourcing.persistence import PersistenceError
|
|
4
4
|
from eventsourcing.postgres import PostgresDatastore
|
|
@@ -9,19 +9,19 @@ def pg_close_all_connections(
|
|
|
9
9
|
host="127.0.0.1",
|
|
10
10
|
port="5432",
|
|
11
11
|
user="postgres",
|
|
12
|
-
password="postgres",
|
|
12
|
+
password="postgres", # noqa: S107
|
|
13
13
|
):
|
|
14
14
|
try:
|
|
15
15
|
# For local development... probably.
|
|
16
|
-
pg_conn =
|
|
16
|
+
pg_conn = psycopg.connect(
|
|
17
17
|
dbname=name,
|
|
18
18
|
host=host,
|
|
19
19
|
port=port,
|
|
20
20
|
)
|
|
21
|
-
except
|
|
21
|
+
except psycopg.Error:
|
|
22
22
|
# For GitHub actions.
|
|
23
23
|
"""CREATE ROLE postgres LOGIN SUPERUSER PASSWORD 'postgres';"""
|
|
24
|
-
pg_conn =
|
|
24
|
+
pg_conn = psycopg.connect(
|
|
25
25
|
dbname=name,
|
|
26
26
|
host=host,
|
|
27
27
|
port=port,
|
|
@@ -44,9 +44,9 @@ def pg_close_all_connections(
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
def drop_postgres_table(datastore: PostgresDatastore, table_name):
|
|
47
|
-
statement = f"DROP TABLE {table_name}
|
|
47
|
+
statement = f"DROP TABLE {table_name}"
|
|
48
48
|
try:
|
|
49
49
|
with datastore.transaction(commit=True) as curs:
|
|
50
|
-
curs.execute(statement)
|
|
50
|
+
curs.execute(statement, prepare=False)
|
|
51
51
|
except PersistenceError:
|
|
52
52
|
pass
|
|
File without changes
|