@reventlessdev/reventless-core 3.0.0-alpha.180 → 3.0.0-alpha.181

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.
Files changed (115) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +7 -7
  3. package/src/Message.res +2 -1
  4. package/src/Message.res.mjs +5 -1
  5. package/src/admin/Platform_Admin_Structure.res +2 -0
  6. package/src/admin/Platform_Admin_Structure.res.mjs +2 -0
  7. package/src/admin/Platform_ComponentDefinitionsApi.res +2 -1
  8. package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +5 -1
  9. package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res +1 -1
  10. package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res.mjs +2 -1
  11. package/src/components/Api/ApiTargetStateHelpers.res +20 -0
  12. package/src/components/Api/ApiTargetStateHelpers.res.mjs +16 -0
  13. package/src/components/Api/GraphQL_FragmentGenerator.res +1 -0
  14. package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +1 -0
  15. package/src/components/Api/SchemaType.res +3 -0
  16. package/src/components/Api/SchemaType.res.mjs +10 -2
  17. package/src/components/Api/SuryToJsonSchema.res +16 -0
  18. package/src/components/Api/SuryToJsonSchema.res.mjs +28 -0
  19. package/src/components/Dcb/Dcb_Builder.res +4 -5
  20. package/src/components/Dcb/Dcb_Builder.res.mjs +3 -5
  21. package/src/components/DcbEventLog/DcbEventLog_Operations.res +6 -1
  22. package/src/components/DcbEventLog/DcbEventLog_Operations.res.mjs +2 -1
  23. package/src/components/InboundTranslationSlice/InboundTranslationSlice.res +25 -0
  24. package/src/components/InboundTranslationSlice/InboundTranslationSlice.res.mjs +31 -0
  25. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res +8 -8
  26. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs +26 -6
  27. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +44 -37
  28. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res.mjs +17 -25
  29. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +27 -2
  30. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +4 -0
  31. package/src/components/StateViewSlice/StateViewSlice_Builder.res +20 -6
  32. package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +23 -5
  33. package/src/components/StateViewSlice/StateViewSlice_Callback.res +1 -1
  34. package/src/components/StateViewSlice/StateViewSlice_Callback.res.mjs +5 -1
  35. package/src/plugin/component/Plugin_Helpers.res +4 -1
  36. package/src/plugin/component/Plugin_Structure.res +15 -1
  37. package/src/plugin/component/Plugin_Structure.res.mjs +4 -1
  38. package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +2 -0
  39. package/tests/IdentityTest.res.mjs +174 -0
  40. package/tests/ResourceAttributionTest.res.mjs +75 -0
  41. package/tests/adapter/MonitoringTest.res.mjs +116 -0
  42. package/tests/admin/Platform_ComponentDefinitionsApiTest.res +10 -0
  43. package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +365 -0
  44. package/tests/admin/Platform_UIFragmentsApiTest.res.mjs +101 -0
  45. package/tests/aggregate/AggregateCacheTest.res.mjs +433 -0
  46. package/tests/aggregate/AggregateCallbackTest.res.mjs +143 -0
  47. package/tests/aggregate/AggregateCausationTest.res.mjs +47 -0
  48. package/tests/aggregate/AggregateConflictTest.res.mjs +386 -0
  49. package/tests/aggregate/AggregateFixtures.res.mjs +308 -0
  50. package/tests/aggregate/AggregateRejectionTest.res.mjs +166 -0
  51. package/tests/aggregate/AggregateSnapshotTest.res.mjs +645 -0
  52. package/tests/api/ApiNoApiTest.res.mjs +90 -0
  53. package/tests/api/GraphQL_StitcherTest.res.mjs +112 -0
  54. package/tests/api/SimpleTest.res.mjs +10 -0
  55. package/tests/api/SuryToJsonSchemaTest.res +68 -0
  56. package/tests/api/SuryToJsonSchemaTest.res.mjs +843 -0
  57. package/tests/commandgenerator/CommandGeneratorCallbackTest.res.mjs +160 -0
  58. package/tests/commandgenerator/CommandGeneratorFixtures.res.mjs +197 -0
  59. package/tests/commandtopic/CommandTopicCallbackFixtures.res.mjs +152 -0
  60. package/tests/commandtopic/CommandTopicCallbackTest.res.mjs +106 -0
  61. package/tests/commandtopic/CommandTopicHelpersRejectionTest.res.mjs +109 -0
  62. package/tests/counter/CounterCallbackTest.res.mjs +140 -0
  63. package/tests/counter/CounterFixtures.res.mjs +67 -0
  64. package/tests/dcb/DcbDecodeTest.res.mjs +194 -0
  65. package/tests/dcb/DcbEventLogOperationsTest.res.mjs +268 -0
  66. package/tests/dcb/DcbFixtures.res.mjs +534 -0
  67. package/tests/dcb/DcbScopeInferenceTest.res.mjs +480 -0
  68. package/tests/dcb/DcbStateChangeSliceTest.res.mjs +540 -0
  69. package/tests/dcb/DcbTagTest.res.mjs +867 -0
  70. package/tests/dcb/DcbValidationTest.res.mjs +476 -0
  71. package/tests/eventlog/EventLogFixtures.res.mjs +196 -0
  72. package/tests/eventlog/EventLogOperationsTest.res.mjs +192 -0
  73. package/tests/eventlog/EventLogRetryTest.res.mjs +195 -0
  74. package/tests/eventmapper/EventMapperCallbackTest.res.mjs +141 -0
  75. package/tests/eventmapper/EventMapperFixtures.res.mjs +440 -0
  76. package/tests/eventmapper/EventMapperTest.res.mjs +83 -0
  77. package/tests/eventtopic/EventTopicFixtures.res.mjs +65 -0
  78. package/tests/eventtopic/EventTopicOperationsTest.res.mjs +71 -0
  79. package/tests/extensionpoint/ExtensionPointCallbackTest.res.mjs +94 -0
  80. package/tests/extensionpoint/ExtensionPointFixtures.res.mjs +205 -0
  81. package/tests/extensionpoint/ExtensionPointOperationsTest.res.mjs +304 -0
  82. package/tests/extensionpoint/PluginDisconnectScheduleTest.res.mjs +92 -0
  83. package/tests/ftp/FTPTest.res.mjs +88 -0
  84. package/tests/logger/LogFormatTest.res.mjs +329 -0
  85. package/tests/logger/LoggerLazyTest.res.mjs +40 -0
  86. package/tests/message/MessageTest.res.mjs +218 -0
  87. package/tests/message/MetaEnvelopeTest.res.mjs +188 -0
  88. package/tests/plugin/HeartbeatDisconnectGraceTest.res.mjs +91 -0
  89. package/tests/plugin/ManifestVisibilityTest.res.mjs +215 -0
  90. package/tests/plugin/PluginSpecExperiment.res.mjs +31 -0
  91. package/tests/plugin/PluginStructureTest.res +55 -0
  92. package/tests/plugin/PluginStructureTest.res.mjs +467 -0
  93. package/tests/plugin/PluginVersionTest.res.mjs +30 -0
  94. package/tests/plugin/SchemaWalkerTest.res.mjs +160 -0
  95. package/tests/plugin/StateChangeSlice/PsPlaceOrder.res.mjs +62 -0
  96. package/tests/plugin/StateChangeSlice/PsShipOrder.res +4 -1
  97. package/tests/plugin/StateChangeSlice/PsShipOrder.res.mjs +77 -0
  98. package/tests/plugin/StateViewSlice/PsAnnotatedView.res +5 -4
  99. package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +110 -0
  100. package/tests/plugin/StateViewSlice/PsAvailableProductsView.res +1 -1
  101. package/tests/plugin/StateViewSlice/PsAvailableProductsView.res.mjs +70 -0
  102. package/tests/plugin/StateViewSlice/PsCustomersView.res +1 -1
  103. package/tests/plugin/StateViewSlice/PsCustomersView.res.mjs +70 -0
  104. package/tests/plugin/StateViewSlice/PsOrdersView.res +1 -1
  105. package/tests/plugin/StateViewSlice/PsOrdersView.res.mjs +82 -0
  106. package/tests/poc/StructuralSubtypingPoc.res.mjs +61 -0
  107. package/tests/projection/ProjectionOptimizeTest.res.mjs +75 -0
  108. package/tests/querydb/QueryDbFixtures.res.mjs +135 -0
  109. package/tests/querydb/QueryDbOperationsTest.res.mjs +168 -0
  110. package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res.mjs +77 -0
  111. package/tests/sideeffecthandler/SideEffectHandlerFixtures.res.mjs +139 -0
  112. package/tests/util/CommandPublisherTest.res.mjs +78 -0
  113. package/tests/util/CsvStreamTest.res.mjs +51 -0
  114. package/tests/util/LruTest.res.mjs +105 -0
  115. package/tests/util/MetricsTest.res.mjs +53 -0
@@ -0,0 +1,540 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Stream from "effect/Stream";
6
+ import * as DcbFixtures$ReventlessCore from "./DcbFixtures.res.mjs";
7
+ import * as DcbEventLog_Operations$ReventlessCore from "../../src/components/DcbEventLog/DcbEventLog_Operations.res.mjs";
8
+ import * as StateChangeSlice_Callback$ReventlessCore from "../../src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs";
9
+
10
+ let mock = DcbFixtures$ReventlessCore.makeMockStorage();
11
+
12
+ let storage = mock.operations;
13
+
14
+ let publishJson = mock.mockPublishJson;
15
+
16
+ let TestDcbOps = {
17
+ name: "TestDcbEventLog",
18
+ serviceName: "TestDcbEventLog",
19
+ storage: storage,
20
+ publishJson: publishJson
21
+ };
22
+
23
+ let EventLogOps = DcbEventLog_Operations$ReventlessCore.Make(TestDcbOps);
24
+
25
+ let testDcbEventLog_read = EventLogOps.read;
26
+
27
+ let testDcbEventLog_append = EventLogOps.append;
28
+
29
+ let testDcbEventLog_readStream = EventLogOps.readStream;
30
+
31
+ let testDcbEventLog_appendStream = EventLogOps.appendStream;
32
+
33
+ let testDcbEventLog = {
34
+ read: testDcbEventLog_read,
35
+ append: testDcbEventLog_append,
36
+ readStream: testDcbEventLog_readStream,
37
+ appendStream: testDcbEventLog_appendStream
38
+ };
39
+
40
+ let initialState = DcbFixtures$ReventlessCore.TestCommandSpec.initialState;
41
+
42
+ let evolve = DcbFixtures$ReventlessCore.TestCommandSpec.evolve;
43
+
44
+ let decide = DcbFixtures$ReventlessCore.TestCommandSpec.decide;
45
+
46
+ let moduleUrl = DcbFixtures$ReventlessCore.TestCommandSpec.moduleUrl;
47
+
48
+ let TestCommandBehavior = {
49
+ initialState: initialState,
50
+ evolve: evolve,
51
+ decide: decide,
52
+ moduleUrl: moduleUrl
53
+ };
54
+
55
+ let TestHandler = StateChangeSlice_Callback$ReventlessCore.Make({
56
+ name: DcbFixtures$ReventlessCore.TestCommandSpec.name,
57
+ moduleUrl: DcbFixtures$ReventlessCore.TestCommandSpec.moduleUrl,
58
+ Id: Id$Reventless.$$String,
59
+ consumedEventSchema: DcbFixtures$ReventlessCore.TestCommandSpec.consumedEventSchema,
60
+ errorSchema: DcbFixtures$ReventlessCore.TestCommandSpec.errorSchema,
61
+ eventSchema: DcbFixtures$ReventlessCore.TestCommandSpec.eventSchema,
62
+ commandSchema: DcbFixtures$ReventlessCore.TestCommandSpec.commandSchema,
63
+ commandAuthorization: DcbFixtures$ReventlessCore.TestCommandSpec.commandAuthorization,
64
+ readConsistency: DcbFixtures$ReventlessCore.TestCommandSpec.readConsistency
65
+ })(TestCommandBehavior);
66
+
67
+ let name = DcbFixtures$ReventlessCore.TestCommandSpec.name;
68
+
69
+ let moduleUrl$1 = DcbFixtures$ReventlessCore.TestCommandSpec.moduleUrl;
70
+
71
+ let eventSchema = DcbFixtures$ReventlessCore.TestCommandSpec.eventSchema;
72
+
73
+ let consumedEventSchema = DcbFixtures$ReventlessCore.TestCommandSpec.consumedEventSchema;
74
+
75
+ let commandSchema = DcbFixtures$ReventlessCore.TestCommandSpec.commandSchema;
76
+
77
+ let errorSchema = DcbFixtures$ReventlessCore.TestCommandSpec.errorSchema;
78
+
79
+ let commandAuthorization = DcbFixtures$ReventlessCore.TestCommandSpec.commandAuthorization;
80
+
81
+ let StrongSpec_Id = DcbFixtures$ReventlessCore.TestCommandSpec.Id;
82
+
83
+ let StrongSpec_initialState = DcbFixtures$ReventlessCore.TestCommandSpec.initialState;
84
+
85
+ let StrongSpec_evolve = DcbFixtures$ReventlessCore.TestCommandSpec.evolve;
86
+
87
+ let StrongSpec_decide = DcbFixtures$ReventlessCore.TestCommandSpec.decide;
88
+
89
+ let StrongSpec = {
90
+ name: name,
91
+ Id: StrongSpec_Id,
92
+ moduleUrl: moduleUrl$1,
93
+ eventSchema: eventSchema,
94
+ consumedEventSchema: consumedEventSchema,
95
+ commandSchema: commandSchema,
96
+ errorSchema: errorSchema,
97
+ initialState: StrongSpec_initialState,
98
+ evolve: StrongSpec_evolve,
99
+ decide: StrongSpec_decide,
100
+ commandAuthorization: commandAuthorization,
101
+ readConsistency: "AlwaysStrong"
102
+ };
103
+
104
+ let name$1 = DcbFixtures$ReventlessCore.TestCommandSpec.name;
105
+
106
+ let moduleUrl$2 = DcbFixtures$ReventlessCore.TestCommandSpec.moduleUrl;
107
+
108
+ let eventSchema$1 = DcbFixtures$ReventlessCore.TestCommandSpec.eventSchema;
109
+
110
+ let consumedEventSchema$1 = DcbFixtures$ReventlessCore.TestCommandSpec.consumedEventSchema;
111
+
112
+ let commandSchema$1 = DcbFixtures$ReventlessCore.TestCommandSpec.commandSchema;
113
+
114
+ let errorSchema$1 = DcbFixtures$ReventlessCore.TestCommandSpec.errorSchema;
115
+
116
+ let commandAuthorization$1 = DcbFixtures$ReventlessCore.TestCommandSpec.commandAuthorization;
117
+
118
+ let EventualSpec_Id = DcbFixtures$ReventlessCore.TestCommandSpec.Id;
119
+
120
+ let EventualSpec_initialState = DcbFixtures$ReventlessCore.TestCommandSpec.initialState;
121
+
122
+ let EventualSpec_evolve = DcbFixtures$ReventlessCore.TestCommandSpec.evolve;
123
+
124
+ let EventualSpec_decide = DcbFixtures$ReventlessCore.TestCommandSpec.decide;
125
+
126
+ let EventualSpec = {
127
+ name: name$1,
128
+ Id: EventualSpec_Id,
129
+ moduleUrl: moduleUrl$2,
130
+ eventSchema: eventSchema$1,
131
+ consumedEventSchema: consumedEventSchema$1,
132
+ commandSchema: commandSchema$1,
133
+ errorSchema: errorSchema$1,
134
+ initialState: EventualSpec_initialState,
135
+ evolve: EventualSpec_evolve,
136
+ decide: EventualSpec_decide,
137
+ commandAuthorization: commandAuthorization$1,
138
+ readConsistency: "AlwaysEventual"
139
+ };
140
+
141
+ let StrongHandler = StateChangeSlice_Callback$ReventlessCore.Make({
142
+ name: name,
143
+ moduleUrl: moduleUrl$1,
144
+ Id: Id$Reventless.$$String,
145
+ consumedEventSchema: consumedEventSchema,
146
+ errorSchema: errorSchema,
147
+ eventSchema: eventSchema,
148
+ commandSchema: commandSchema,
149
+ commandAuthorization: commandAuthorization,
150
+ readConsistency: "AlwaysStrong"
151
+ })(TestCommandBehavior);
152
+
153
+ let EventualHandler = StateChangeSlice_Callback$ReventlessCore.Make({
154
+ name: name$1,
155
+ moduleUrl: moduleUrl$2,
156
+ Id: Id$Reventless.$$String,
157
+ consumedEventSchema: consumedEventSchema$1,
158
+ errorSchema: errorSchema$1,
159
+ eventSchema: eventSchema$1,
160
+ commandSchema: commandSchema$1,
161
+ commandAuthorization: commandAuthorization$1,
162
+ readConsistency: "AlwaysEventual"
163
+ })(TestCommandBehavior);
164
+
165
+ function makeTopicItem(reference, command) {
166
+ return {
167
+ command: {
168
+ id: Id$Reventless.$$String.makeFromString("cmd-" + reference),
169
+ meta: DcbFixtures$ReventlessCore.testMeta,
170
+ command: command
171
+ },
172
+ reference: reference
173
+ };
174
+ }
175
+
176
+ globalThis.beforeEach(() => {
177
+ mock.reset();
178
+ TestHandler.resetCache();
179
+ StrongHandler.resetCache();
180
+ EventualHandler.resetCache();
181
+ });
182
+
183
+ globalThis.describe("StateChangeSlice_Callback:", () => {
184
+ globalThis.describe("handleCommands - happy path", () => {
185
+ globalThis.test("CreateItem on empty log succeeds", async () => {
186
+ let results = await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
187
+ TAG: "CreateItem",
188
+ itemId: "item-1",
189
+ name: "Test"
190
+ })])));
191
+ globalThis.expect([
192
+ results,
193
+ mock.getEvents().length,
194
+ mock.publishedEvents.contents.length
195
+ ]).toEqual([
196
+ [{
197
+ TAG: "Ok",
198
+ _0: "ref-1"
199
+ }],
200
+ 1,
201
+ 1
202
+ ]);
203
+ });
204
+ globalThis.test("stored event carries only its entity tags (no framework provenance tag)", async () => {
205
+ await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
206
+ TAG: "CreateItem",
207
+ itemId: "item-1",
208
+ name: "Test"
209
+ })])));
210
+ let storedEvent = mock.getEvents()[0];
211
+ globalThis.expect(storedEvent.tags).toEqual([{
212
+ key: "itemId",
213
+ value: "item-1"
214
+ }]);
215
+ });
216
+ });
217
+ globalThis.describe("handleCommands - decide returns Ok([])", () => {
218
+ globalThis.test("NoOp returns Ok without storing events", async () => {
219
+ let results = await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-noop", "NoOp")])));
220
+ globalThis.expect([
221
+ results,
222
+ mock.getEvents().length
223
+ ]).toEqual([
224
+ [{
225
+ TAG: "Ok",
226
+ _0: "ref-noop"
227
+ }],
228
+ 0
229
+ ]);
230
+ });
231
+ });
232
+ globalThis.describe("handleCommands - decide returns Error", () => {
233
+ globalThis.test("CreateItem when item exists returns Ok (business-rule violations ACK, not NACK)", async () => {
234
+ await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
235
+ TAG: "CreateItem",
236
+ itemId: "item-1",
237
+ name: "Test"
238
+ })])));
239
+ let results = await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-2", {
240
+ TAG: "CreateItem",
241
+ itemId: "item-1",
242
+ name: "Duplicate"
243
+ })])));
244
+ globalThis.expect(results).toEqual([{
245
+ TAG: "Ok",
246
+ _0: "ref-2"
247
+ }]);
248
+ });
249
+ });
250
+ globalThis.describe("handleCommands - retry on conflict", () => {
251
+ globalThis.test("retries and succeeds after 1 append failure", async () => {
252
+ mock.failNextAppends.contents = 1;
253
+ let results = await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
254
+ TAG: "CreateItem",
255
+ itemId: "item-1",
256
+ name: "Test"
257
+ })])));
258
+ globalThis.expect([
259
+ results,
260
+ mock.getEvents().length
261
+ ]).toEqual([
262
+ [{
263
+ TAG: "Ok",
264
+ _0: "ref-1"
265
+ }],
266
+ 1
267
+ ]);
268
+ });
269
+ globalThis.test("returns Error after retries exhausted (4 failures)", async () => {
270
+ mock.failNextAppends.contents = 4;
271
+ let results = await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
272
+ TAG: "CreateItem",
273
+ itemId: "item-1",
274
+ name: "Test"
275
+ })])));
276
+ globalThis.expect([
277
+ results,
278
+ mock.getEvents().length
279
+ ]).toEqual([
280
+ [{
281
+ TAG: "Error",
282
+ _0: "ref-1"
283
+ }],
284
+ 0
285
+ ]);
286
+ });
287
+ });
288
+ globalThis.describe("handleCommands - read consistency (eventual-first, strong-on-retry)", () => {
289
+ globalThis.test("a conflict-free command reads eventually-consistent (no strong read)", async () => {
290
+ await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
291
+ TAG: "CreateItem",
292
+ itemId: "item-1",
293
+ name: "Test"
294
+ })])));
295
+ globalThis.expect(mock.readStrongConsistency.contents).toEqual([false]);
296
+ });
297
+ globalThis.test("escalates to strong consistency on retry after a conflict", async () => {
298
+ mock.failNextAppends.contents = 1;
299
+ await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
300
+ TAG: "CreateItem",
301
+ itemId: "item-1",
302
+ name: "Test"
303
+ })])));
304
+ globalThis.expect(mock.readStrongConsistency.contents).toEqual([
305
+ false,
306
+ true
307
+ ]);
308
+ });
309
+ globalThis.test("every retry after the first is strong (2 failures)", async () => {
310
+ mock.failNextAppends.contents = 2;
311
+ await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
312
+ TAG: "CreateItem",
313
+ itemId: "item-1",
314
+ name: "Test"
315
+ })])));
316
+ globalThis.expect(mock.readStrongConsistency.contents).toEqual([
317
+ false,
318
+ true,
319
+ true
320
+ ]);
321
+ });
322
+ });
323
+ globalThis.describe("handleCommands - read consistency (per-slice override)", () => {
324
+ globalThis.test("AlwaysStrong reads strong on the first attempt", async () => {
325
+ await Effect.runPromise(StrongHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
326
+ TAG: "CreateItem",
327
+ itemId: "item-1",
328
+ name: "Test"
329
+ })])));
330
+ globalThis.expect(mock.readStrongConsistency.contents).toEqual([true]);
331
+ });
332
+ globalThis.test("AlwaysStrong stays strong across retries (2 failures)", async () => {
333
+ mock.failNextAppends.contents = 2;
334
+ await Effect.runPromise(StrongHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
335
+ TAG: "CreateItem",
336
+ itemId: "item-1",
337
+ name: "Test"
338
+ })])));
339
+ globalThis.expect(mock.readStrongConsistency.contents).toEqual([
340
+ true,
341
+ true,
342
+ true
343
+ ]);
344
+ });
345
+ globalThis.test("AlwaysEventual never reads strong, even on retry (2 failures)", async () => {
346
+ mock.failNextAppends.contents = 2;
347
+ await Effect.runPromise(EventualHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
348
+ TAG: "CreateItem",
349
+ itemId: "item-1",
350
+ name: "Test"
351
+ })])));
352
+ globalThis.expect(mock.readStrongConsistency.contents).toEqual([
353
+ false,
354
+ false,
355
+ false
356
+ ]);
357
+ });
358
+ });
359
+ globalThis.describe("handleCommands - conditional append", () => {
360
+ globalThis.test("RenameItem after CreateItem uses headPosition in condition", async () => {
361
+ await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
362
+ TAG: "CreateItem",
363
+ itemId: "item-1",
364
+ name: "Test"
365
+ })])));
366
+ let results = await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-2", {
367
+ TAG: "RenameItem",
368
+ itemId: "item-1",
369
+ newName: "Updated"
370
+ })])));
371
+ globalThis.expect([
372
+ results,
373
+ mock.getEvents().length
374
+ ]).toEqual([
375
+ [{
376
+ TAG: "Ok",
377
+ _0: "ref-2"
378
+ }],
379
+ 2
380
+ ]);
381
+ });
382
+ });
383
+ globalThis.describe("handleCommands - batch", () => {
384
+ globalThis.test("multiple successful commands", async () => {
385
+ let results = await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([
386
+ makeTopicItem("ref-1", {
387
+ TAG: "CreateItem",
388
+ itemId: "item-1",
389
+ name: "First"
390
+ }),
391
+ makeTopicItem("ref-2", {
392
+ TAG: "CreateItem",
393
+ itemId: "item-2",
394
+ name: "Second"
395
+ })
396
+ ])));
397
+ globalThis.expect(results).toEqual([
398
+ {
399
+ TAG: "Ok",
400
+ _0: "ref-1"
401
+ },
402
+ {
403
+ TAG: "Ok",
404
+ _0: "ref-2"
405
+ }
406
+ ]);
407
+ });
408
+ globalThis.test("mixed: new item succeeds, duplicate item ACKs as Ok (business-rule violation)", async () => {
409
+ await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-0", {
410
+ TAG: "CreateItem",
411
+ itemId: "item-1",
412
+ name: "Existing"
413
+ })])));
414
+ let results = await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([
415
+ makeTopicItem("ref-1", {
416
+ TAG: "CreateItem",
417
+ itemId: "item-2",
418
+ name: "New"
419
+ }),
420
+ makeTopicItem("ref-2", {
421
+ TAG: "CreateItem",
422
+ itemId: "item-1",
423
+ name: "Duplicate"
424
+ })
425
+ ])));
426
+ globalThis.expect(results).toEqual([
427
+ {
428
+ TAG: "Ok",
429
+ _0: "ref-1"
430
+ },
431
+ {
432
+ TAG: "Ok",
433
+ _0: "ref-2"
434
+ }
435
+ ]);
436
+ });
437
+ globalThis.test("empty batch returns empty array", async () => {
438
+ let results = await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([])));
439
+ globalThis.expect(results).toEqual([]);
440
+ });
441
+ });
442
+ globalThis.describe("handleCommands - projection cache", () => {
443
+ globalThis.test("a third same-entity command reads only the delta", async () => {
444
+ let run = (reference, command) => Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem(reference, command)])));
445
+ await run("ref-1", {
446
+ TAG: "CreateItem",
447
+ itemId: "item-1",
448
+ name: "A"
449
+ });
450
+ await run("ref-2", {
451
+ TAG: "RenameItem",
452
+ itemId: "item-1",
453
+ newName: "B"
454
+ });
455
+ let r3 = await run("ref-3", {
456
+ TAG: "RenameItem",
457
+ itemId: "item-1",
458
+ newName: "C"
459
+ });
460
+ globalThis.expect([
461
+ r3,
462
+ mock.readAfters.contents,
463
+ mock.getEvents().length
464
+ ]).toEqual([
465
+ [{
466
+ TAG: "Ok",
467
+ _0: "ref-3"
468
+ }],
469
+ [
470
+ undefined,
471
+ undefined,
472
+ "1"
473
+ ],
474
+ 3
475
+ ]);
476
+ });
477
+ globalThis.test("a different entity does not hit another entity's cache (full read)", async () => {
478
+ let run = (reference, command) => Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem(reference, command)])));
479
+ await run("ref-1", {
480
+ TAG: "CreateItem",
481
+ itemId: "item-1",
482
+ name: "A"
483
+ });
484
+ await run("ref-2", {
485
+ TAG: "CreateItem",
486
+ itemId: "item-2",
487
+ name: "B"
488
+ });
489
+ globalThis.expect(mock.readAfters.contents).toEqual([
490
+ undefined,
491
+ undefined
492
+ ]);
493
+ });
494
+ globalThis.test("conflict re-seeds the cache so the retry reads the delta", async () => {
495
+ let run = (reference, command) => Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem(reference, command)])));
496
+ await run("ref-1", {
497
+ TAG: "CreateItem",
498
+ itemId: "item-1",
499
+ name: "A"
500
+ });
501
+ mock.failNextAppends.contents = 1;
502
+ let r2 = await run("ref-2", {
503
+ TAG: "RenameItem",
504
+ itemId: "item-1",
505
+ newName: "B"
506
+ });
507
+ globalThis.expect([
508
+ r2,
509
+ mock.readAfters.contents,
510
+ mock.getEvents().length
511
+ ]).toEqual([
512
+ [{
513
+ TAG: "Ok",
514
+ _0: "ref-2"
515
+ }],
516
+ [
517
+ undefined,
518
+ undefined,
519
+ "1"
520
+ ],
521
+ 2
522
+ ]);
523
+ });
524
+ });
525
+ });
526
+
527
+ export {
528
+ mock,
529
+ TestDcbOps,
530
+ EventLogOps,
531
+ testDcbEventLog,
532
+ TestCommandBehavior,
533
+ TestHandler,
534
+ StrongSpec,
535
+ EventualSpec,
536
+ StrongHandler,
537
+ EventualHandler,
538
+ makeTopicItem,
539
+ }
540
+ /* mock Not a pure module */