@reventlessdev/reventless-core 3.0.0-alpha.259 → 3.0.0-alpha.261

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 (209) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/package.json +11 -11
  3. package/src/Behavior.res +1 -4
  4. package/src/Message.res +17 -14
  5. package/src/Projection.res +51 -25
  6. package/src/RequestContext.res +8 -9
  7. package/src/ResourceAttribution.res +28 -14
  8. package/src/adapter/Adapter.res +48 -4
  9. package/src/adapter/EventLogProvisioning/EventLogProvisioning.res +5 -5
  10. package/src/adapter/Messaging/Messaging_Log_Backend.res +10 -7
  11. package/src/adapter/Messaging/Messaging_Log_Backend.res.mjs +2 -4
  12. package/src/adapter/Monitoring/Monitoring.res +16 -17
  13. package/src/adapter/QueryInterception/QueryInterception.res +0 -1
  14. package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res +12 -5
  15. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +11 -7
  16. package/src/adapter/Runtime/PluginRuntime_Builder_Micro.res +1 -7
  17. package/src/adapter/Runtime/PluginRuntime_Builder_Single.res +1 -7
  18. package/src/adapter/RuntimeExtension/RuntimeExtension.res +0 -1
  19. package/src/admin/Platform_Admin.res +17 -6
  20. package/src/admin/Platform_AdminApi.res +14 -9
  21. package/src/admin/Platform_Admin_Callback.res +1 -1
  22. package/src/admin/Platform_BakedManifest.res +34 -34
  23. package/src/admin/Platform_ComponentDefinitionsApi.res +9 -21
  24. package/src/admin/Platform_PluginStructuresApi.res +15 -12
  25. package/src/admin/UiFragmentRegistry/StateChange/UiFragmentRegistry.res +1 -1
  26. package/src/components/Aggregate/Aggregate.res +13 -12
  27. package/src/components/Aggregate/Aggregate_Builder.res +22 -21
  28. package/src/components/Aggregate/Aggregate_Callback.res +91 -86
  29. package/src/components/Api/ApiNoApiHelpers.res +1 -3
  30. package/src/components/Api/Api_Ids.res +0 -1
  31. package/src/components/Api/Api_Naming.res +41 -18
  32. package/src/components/Api/GraphQL_FragmentGenerator.res +42 -52
  33. package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +11 -4
  34. package/src/components/Api/GraphQL_SchemaInspector.res +5 -3
  35. package/src/components/Api/GraphQL_Stitcher.res +9 -10
  36. package/src/components/Api/MCP_SchemaGenerator.res +8 -12
  37. package/src/components/Api/OwnerScopeDiagnostics.res +1 -2
  38. package/src/components/Api/QueryDbListQuery.res +13 -6
  39. package/src/components/Api/SchemaType.res +40 -20
  40. package/src/components/Api/SchemaType.res.mjs +16 -0
  41. package/src/components/Api/SuryToJsonSchema.res +18 -10
  42. package/src/components/Api/SuryToJsonSchema.res.mjs +135 -121
  43. package/src/components/AutomationSlice/AutomationSlice.res +4 -4
  44. package/src/components/AutomationSlice/AutomationSlice_Builder.res +15 -14
  45. package/src/components/AutomationSlice/AutomationSlice_Callback.res +3 -3
  46. package/src/components/Builder_Helpers.res +12 -23
  47. package/src/components/CommandGenerator/CommandGenerator.res +6 -1
  48. package/src/components/CommandGenerator/CommandGenerator_Builder.res +29 -21
  49. package/src/components/CommandGenerator/CommandGenerator_Callback.res +17 -21
  50. package/src/components/CommandTopic/CommandTopic.res +3 -3
  51. package/src/components/CommandTopic/CommandTopic_Builder.res +10 -10
  52. package/src/components/CommandTopic/CommandTopic_Callback.res +1 -4
  53. package/src/components/CommandTopic/CommandTopic_Helpers.res +1 -2
  54. package/src/components/Counter/Counter_Builder.res +1 -6
  55. package/src/components/Counter/Counter_Callback.res +3 -3
  56. package/src/components/Dcb/Dcb_Builder.res +970 -956
  57. package/src/components/DcbEventLog/DcbEventLog.res +4 -4
  58. package/src/components/DcbEventLog/DcbEventLog_Builder.res +0 -1
  59. package/src/components/DcbEventLog/DcbEventLog_Operations.res +17 -18
  60. package/src/components/EventCollector/EventCollector_Builder.res +2 -1
  61. package/src/components/EventLog/EventLog.res +5 -1
  62. package/src/components/EventLog/EventLog_Operations.res +12 -8
  63. package/src/components/EventMapper/EventMapper.res +8 -10
  64. package/src/components/EventMapper/EventMapper_Builder.res +4 -1
  65. package/src/components/EventMapper/EventMapper_Callback.res +46 -31
  66. package/src/components/EventTopic/EventTopic.res +3 -3
  67. package/src/components/EventTopic/EventTopic_Builder.res +1 -1
  68. package/src/components/Extension/Extension.res +4 -1
  69. package/src/components/Extension/Extension_Operations.res +11 -3
  70. package/src/components/ExtensionPoint/ExtensionPoint.res +3 -3
  71. package/src/components/ExtensionPoint/ExtensionPoint_Builder.res +9 -10
  72. package/src/components/ExtensionPoint/ExtensionPoint_Callback.res +2 -6
  73. package/src/components/ExtensionPoint/ExtensionPoint_Operations.res +13 -5
  74. package/src/components/Heartbeat/Heartbeat_Callback.res +3 -3
  75. package/src/components/InboundTranslationSlice/InboundTranslationSlice.res +4 -4
  76. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +7 -10
  77. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice.res +4 -4
  78. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +15 -14
  79. package/src/components/QueryDb/QueryDb_Adapter.res +1 -3
  80. package/src/components/QueryDb/QueryDb_Operations.res +10 -10
  81. package/src/components/ReadModel/ReadModel.res +3 -3
  82. package/src/components/ReadModel/ReadModel_Builder.res +1 -2
  83. package/src/components/ReadModel/ReadModel_Callback.res +2 -1
  84. package/src/components/SideEffectHandler/SideEffectHandler_Callback.res +5 -2
  85. package/src/components/StateChangeSlice/StateChangeSlice.res +3 -3
  86. package/src/components/StateChangeSlice/StateChangeSlice_Builder.res +23 -4
  87. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +22 -6
  88. package/src/components/StateTopic.res +1 -3
  89. package/src/components/StateViewSlice/StateViewSlice.res +4 -4
  90. package/src/components/StateViewSlice/StateViewSlice_Builder.res +79 -68
  91. package/src/components/StateViewSlice/StateViewSlice_Callback.res +3 -1
  92. package/src/components/Task/Task.res +3 -3
  93. package/src/components/Task/Task_Adapter.res +4 -1
  94. package/src/components/Task/Task_Builder.res +24 -21
  95. package/src/plugin/api/PluginBaseFragment.res +9 -3
  96. package/src/plugin/component/Capability_Inference.res +8 -10
  97. package/src/plugin/component/Plugin.res +6 -6
  98. package/src/plugin/component/Plugin_Builder.res +54 -50
  99. package/src/plugin/component/Plugin_Callback.res +10 -6
  100. package/src/plugin/component/Plugin_Helpers.res +264 -230
  101. package/src/plugin/component/Plugin_Structure.res +370 -324
  102. package/src/plugin/component/Plugin_SubscriptionSchema.res +8 -6
  103. package/src/plugin/component/SchemaWalker.res +11 -12
  104. package/src/plugin/connect/PluginConnectExtension_Mapping.res +45 -50
  105. package/src/plugin/lifecycle/PluginBehavior.res +3 -4
  106. package/src/plugin/lifecycle/PluginSpec.res +1 -2
  107. package/src/plugin/lifecycle/PluginsProjection.res +16 -5
  108. package/src/plugin/lifecycle/PluginsReadModelSpec.res +0 -2
  109. package/src/util/CommandPublisher.res +9 -2
  110. package/src/util/CsvStream.res +3 -3
  111. package/src/util/Interstack.res +7 -15
  112. package/src/util/Interstack.resi +3 -3
  113. package/src/util/LogFormat.res +5 -9
  114. package/src/util/Logger.res +1 -2
  115. package/src/util/Lru.res +2 -1
  116. package/src/util/Lru.resi +2 -3
  117. package/src/util/ScheduleOps.res +24 -27
  118. package/src/util/StorePrefixCollision.res +6 -9
  119. package/src/util/Util_Promise.res +4 -1
  120. package/src/util/Util_Promise.res.mjs +1 -1
  121. package/tests/IdentityTest.res +166 -113
  122. package/tests/ResourceAttributionTest.res +28 -25
  123. package/tests/StorePrefixCollisionTest.res +2 -1
  124. package/tests/TaskBucketNamingTest.res +3 -2
  125. package/tests/adapter/EventLogProvisioningTest.res +51 -48
  126. package/tests/adapter/MonitoringTest.res +81 -38
  127. package/tests/adapter/QueryInterceptionTest.res +2 -2
  128. package/tests/adapter/RefusalVocabularyTest.res +31 -23
  129. package/tests/adapter/RuntimeExtensionHookReachTest.res +9 -8
  130. package/tests/adapter/RuntimeExtensionTest.res +5 -3
  131. package/tests/admin/AdminApiSchemaDriftTest.res +22 -16
  132. package/tests/admin/Platform_Admin_StructureTest.res +10 -11
  133. package/tests/admin/Platform_BakedManifestTest.res +31 -35
  134. package/tests/admin/Platform_ComponentDefinitionsApiTest.res +18 -15
  135. package/tests/admin/Platform_PluginStructuresApiTest.res +5 -3
  136. package/tests/admin/Platform_UIFragmentsApiTest.res +6 -3
  137. package/tests/aggregate/AggregateCacheTest.res +10 -12
  138. package/tests/aggregate/AggregateCallbackTest.res +75 -46
  139. package/tests/aggregate/AggregateCausationTest.res +19 -20
  140. package/tests/aggregate/AggregateConflictTest.res +41 -19
  141. package/tests/aggregate/AggregateFixtures.res +15 -5
  142. package/tests/aggregate/AggregateRejectionTest.res +97 -80
  143. package/tests/aggregate/AggregateSnapshotTest.res +44 -16
  144. package/tests/api/Api_IdsTest.res +10 -4
  145. package/tests/api/BrandedMarkerCompositionTest.res +48 -36
  146. package/tests/api/GeolocationSemanticTest.res +3 -3
  147. package/tests/api/GraphQL_FragmentGeneratorTest.res +66 -54
  148. package/tests/api/OwnerScopeDiagnosticsTest.res +31 -27
  149. package/tests/api/StorageRefFieldsTest.res +30 -18
  150. package/tests/api/SuryToJsonSchemaTest.res +1338 -981
  151. package/tests/api/SuryToJsonSchemaTest.res.mjs +31 -0
  152. package/tests/api/TaggedUnionSchemaTest.res +17 -24
  153. package/tests/commandgenerator/CommandGeneratorCallbackTest.res +235 -179
  154. package/tests/commandgenerator/CommandGeneratorFixtures.res +8 -13
  155. package/tests/commandgenerator/OwnerStampingTest.res +91 -76
  156. package/tests/commandtopic/CommandOutcomeHookTest.res +198 -155
  157. package/tests/commandtopic/CommandTopicCallbackFixtures.res +5 -4
  158. package/tests/commandtopic/CommandTopicCallbackTest.res +61 -43
  159. package/tests/commandtopic/CommandTopicHelpersRejectionTest.res +70 -55
  160. package/tests/counter/CounterCallbackTest.res +40 -34
  161. package/tests/counter/CounterFixtures.res +4 -4
  162. package/tests/dcb/DcbDecodeTest.res +115 -84
  163. package/tests/dcb/DcbEventLogOperationsTest.res +194 -143
  164. package/tests/dcb/DcbEventLogSchemaTest.res +13 -13
  165. package/tests/dcb/DcbFixtures.res +4 -5
  166. package/tests/dcb/DcbScopeInferenceTest.res +173 -114
  167. package/tests/dcb/DcbStateChangeSliceTest.res +391 -262
  168. package/tests/dcb/DcbTagTest.res +22 -11
  169. package/tests/dcb/DcbValidationTest.res +288 -218
  170. package/tests/eventlog/EventLogFixtures.res +4 -4
  171. package/tests/eventlog/EventLogOperationsTest.res +102 -67
  172. package/tests/eventlog/EventLogRetryTest.res +104 -65
  173. package/tests/eventmapper/EventMapperCallbackTest.res +118 -83
  174. package/tests/eventmapper/EventMapperFixtures.res +15 -23
  175. package/tests/eventmapper/EventMapperTest.res +54 -38
  176. package/tests/eventtopic/EventTopicFixtures.res +3 -4
  177. package/tests/eventtopic/EventTopicOperationsTest.res +41 -29
  178. package/tests/extensionpoint/ExtensionPointCallbackTest.res +58 -45
  179. package/tests/extensionpoint/ExtensionPointFixtures.res +16 -20
  180. package/tests/extensionpoint/ExtensionPointOperationsTest.res +70 -49
  181. package/tests/extensionpoint/PluginDisconnectScheduleTest.res +2 -1
  182. package/tests/logger/LogFormatTest.res +104 -82
  183. package/tests/logger/LoggerLazyTest.res +21 -12
  184. package/tests/message/MessageTest.res +96 -93
  185. package/tests/message/MetaEnvelopeTest.res +26 -30
  186. package/tests/plugin/HeartbeatDisconnectGraceTest.res +31 -25
  187. package/tests/plugin/ManifestVisibilityTest.res +20 -12
  188. package/tests/plugin/PluginDefinitionScalars.res +1 -2
  189. package/tests/plugin/PluginLifecycleCorpusTest.res +15 -12
  190. package/tests/plugin/PluginSpecExperiment.res +0 -1
  191. package/tests/plugin/PluginStructureTest.res +1533 -1030
  192. package/tests/plugin/PluginStructureTest.res.mjs +6 -0
  193. package/tests/plugin/Plugin_EventQuerySchemaTest.res +21 -10
  194. package/tests/plugin/SchemaWalkerTest.res +116 -70
  195. package/tests/plugin/StateChange/PsReserveStock.res +4 -4
  196. package/tests/plugin/StateView/PsAnnotatedView.res +6 -4
  197. package/tests/plugin/StateView/PsAnnotatedView.res.mjs +7 -3
  198. package/tests/plugin/StateView/PsCustomersView.res +4 -4
  199. package/tests/plugin/StateView/PsShipmentsView.res +3 -2
  200. package/tests/projection/LifecycleTrailTest.res +1 -4
  201. package/tests/projection/ProjectionOptimizeTest.res +13 -9
  202. package/tests/querydb/QueryDbFixtures.res +0 -1
  203. package/tests/querydb/QueryDbOperationsTest.res +99 -64
  204. package/tests/querydb/TaggedUnionStampTest.res +10 -8
  205. package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res +50 -35
  206. package/tests/sideeffecthandler/SideEffectHandlerFixtures.res +4 -10
  207. package/tests/util/CommandPublisherTest.res +0 -1
  208. package/tests/util/CsvStreamTest.res +3 -3
  209. package/tests/util/LruTest.res +84 -56
@@ -12,7 +12,6 @@
12
12
 
13
13
  open JestGlobals
14
14
 
15
-
16
15
  module AggSpec = {
17
16
  module Id = Reventless.Id.StringPure
18
17
  let name = "CacheTestAggregate"
@@ -29,7 +28,7 @@ module AggSpec = {
29
28
  | CheckpointTaken({count: int})
30
29
 
31
30
  @schema
32
- type error = | Never
31
+ type error = Never
33
32
 
34
33
  let moduleUrl: string = %raw(`import.meta.url`)
35
34
  }
@@ -103,7 +102,7 @@ let makeMockEL = (): mockEL => {
103
102
  Error(EventLog.Conflict)
104
103
  } else {
105
104
  storedRef := storedRef.contents->Array.concat(newEvents)
106
- Ok(())
105
+ Ok()
107
106
  }
108
107
  }
109
108
 
@@ -155,11 +154,11 @@ module TestOps = {
155
154
  ) => promise<result<unit, EventLog.appendError>>,
156
155
  replay: string => promise<array<AggSpec.event>>,
157
156
  replayStream: (string, ~fromSeq: int=?) => Stream.t<AggSpec.event, string, unit>,
158
- appendStream: (int, string, Stream.t<AggSpec.event, string, unit>) => Effect.t<
159
- unit,
157
+ appendStream: (
158
+ int,
160
159
  string,
161
- unit,
162
- >,
160
+ Stream.t<AggSpec.event, string, unit>,
161
+ ) => Effect.t<unit, string, unit>,
163
162
  latestSnapshot: string => promise<result<option<EventLog.snapshot>, string>>,
164
163
  writeSnapshot: (string, EventLog.snapshot) => promise<result<unit, string>>,
165
164
  }
@@ -223,14 +222,13 @@ describe("Aggregate_Callback — replay cache:", () => {
223
222
  let _ = await run(AggSpec.Checkpoint)
224
223
  expect(mock.replayCallCount.contents)->toBe(2)
225
224
  // Both checkpoints observed the same state — cached fold ≡ replayed fold.
226
- let checkpoints =
227
- mock.getAll()
228
- ->Array.filterMap(e =>
225
+ let checkpoints = mock.getAll()->Array.filterMap(
226
+ e =>
229
227
  switch e.event {
230
228
  | CheckpointTaken({count}) => Some(count)
231
229
  | _ => None
232
- }
233
- )
230
+ },
231
+ )
234
232
  expect(checkpoints)->toEqual([3, 3])
235
233
  })
236
234
 
@@ -8,77 +8,106 @@ let _ = beforeEach(() => {
8
8
 
9
9
  describe("Aggregate_Callback.handleCommands:", () => {
10
10
  describe("single command — new aggregate", () => {
11
- testPromise("Create returns Ok(reference) and stores 1 event", async () => {
12
- let results = await Stream.fromIterable([
13
- makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
14
- ])->TestHandler.handleCommands->Effect.runPromise
15
- expect((results, mock.getAll()->Array.length))->toEqual(([Ok("ref-1")], 1))
16
- })
11
+ testPromise(
12
+ "Create returns Ok(reference) and stores 1 event",
13
+ async () => {
14
+ let results = await Stream.fromIterable([
15
+ makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
16
+ ])
17
+ ->TestHandler.handleCommands
18
+ ->Effect.runPromise
19
+ expect((results, mock.getAll()->Array.length))->toEqual(([Ok("ref-1")], 1))
20
+ },
21
+ )
17
22
  })
18
23
 
19
24
  describe("command on existing aggregate", () => {
20
- testPromise("Rename after Create replays history and appends new event", async () => {
21
- let _ =
22
- await Stream.fromIterable([
25
+ testPromise(
26
+ "Rename after Create replays history and appends new event",
27
+ async () => {
28
+ let _ = await Stream.fromIterable([
23
29
  makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
24
- ])->TestHandler.handleCommands->Effect.runPromise
25
- let results =
26
- await Stream.fromIterable([
30
+ ])
31
+ ->TestHandler.handleCommands
32
+ ->Effect.runPromise
33
+ let results = await Stream.fromIterable([
27
34
  makeTopicItem("ref-2", AggSpec.Rename({newName: "Gadget"})),
28
- ])->TestHandler.handleCommands->Effect.runPromise
29
- expect((results, mock.getAll()->Array.length))->toEqual(([Ok("ref-2")], 2))
30
- })
35
+ ])
36
+ ->TestHandler.handleCommands
37
+ ->Effect.runPromise
38
+ expect((results, mock.getAll()->Array.length))->toEqual(([Ok("ref-2")], 2))
39
+ },
40
+ )
31
41
  })
32
42
 
33
43
  describe("behavior returns no events", () => {
34
- testPromise("Rename on new aggregate returns Ok with no event stored", async () => {
35
- // Rename on empty history: Behavior.decide(NotCreated, Rename) returns Error(NotFound)
36
- let results =
37
- await Stream.fromIterable([
44
+ testPromise(
45
+ "Rename on new aggregate returns Ok with no event stored",
46
+ async () => {
47
+ // Rename on empty history: Behavior.decide(NotCreated, Rename) returns Error(NotFound)
48
+ let results = await Stream.fromIterable([
38
49
  makeTopicItem("ref-1", AggSpec.Rename({newName: "Gadget"})),
39
- ])->TestHandler.handleCommands->Effect.runPromise
40
- expect((results, mock.getAll()->Array.length))->toEqual(([Ok("ref-1")], 0))
41
- })
50
+ ])
51
+ ->TestHandler.handleCommands
52
+ ->Effect.runPromise
53
+ expect((results, mock.getAll()->Array.length))->toEqual(([Ok("ref-1")], 0))
54
+ },
55
+ )
42
56
  })
43
57
 
44
58
  describe("append failure", () => {
45
- testPromise("eventLog.append fails → reference returned Error", async () => {
46
- mock.failNextAppend := true
47
- let results =
48
- await Stream.fromIterable([
59
+ testPromise(
60
+ "eventLog.append fails → reference returned Error",
61
+ async () => {
62
+ mock.failNextAppend := true
63
+ let results = await Stream.fromIterable([
49
64
  makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
50
- ])->TestHandler.handleCommands->Effect.runPromise
51
- expect(results)->toEqual([Error("ref-1")])
52
- })
65
+ ])
66
+ ->TestHandler.handleCommands
67
+ ->Effect.runPromise
68
+ expect(results)->toEqual([Error("ref-1")])
69
+ },
70
+ )
53
71
  })
54
72
 
55
73
  describe("multiple commands for same aggregate ID", () => {
56
- testPromise("Create then Rename in one batch accumulate state correctly", async () => {
57
- let results =
58
- await Stream.fromIterable([
74
+ testPromise(
75
+ "Create then Rename in one batch accumulate state correctly",
76
+ async () => {
77
+ let results = await Stream.fromIterable([
59
78
  makeTopicItem(~aggId="agg-1", "ref-1", AggSpec.Create({name: "Widget"})),
60
79
  makeTopicItem(~aggId="agg-1", "ref-2", AggSpec.Rename({newName: "Gadget"})),
61
- ])->TestHandler.handleCommands->Effect.runPromise
62
- // Both commands for same ID processed sequentially
63
- expect((results, mock.getAll()->Array.length))->toEqual(([Ok("ref-1"), Ok("ref-2")], 2))
64
- })
80
+ ])
81
+ ->TestHandler.handleCommands
82
+ ->Effect.runPromise
83
+ // Both commands for same ID processed sequentially
84
+ expect((results, mock.getAll()->Array.length))->toEqual(([Ok("ref-1"), Ok("ref-2")], 2))
85
+ },
86
+ )
65
87
  })
66
88
 
67
89
  describe("commands for different aggregate IDs", () => {
68
- testPromise("each ID processed independently", async () => {
69
- let results =
70
- await Stream.fromIterable([
90
+ testPromise(
91
+ "each ID processed independently",
92
+ async () => {
93
+ let results = await Stream.fromIterable([
71
94
  makeTopicItem(~aggId="agg-a", "ref-a", AggSpec.Create({name: "Alpha"})),
72
95
  makeTopicItem(~aggId="agg-b", "ref-b", AggSpec.Create({name: "Beta"})),
73
- ])->TestHandler.handleCommands->Effect.runPromise
74
- expect((results->Array.length, mock.getAll()->Array.length))->toEqual((2, 2))
75
- })
96
+ ])
97
+ ->TestHandler.handleCommands
98
+ ->Effect.runPromise
99
+ expect((results->Array.length, mock.getAll()->Array.length))->toEqual((2, 2))
100
+ },
101
+ )
76
102
  })
77
103
 
78
104
  describe("empty batch", () => {
79
- testPromise("returns empty array", async () => {
80
- let results = await Stream.fromIterable([])->TestHandler.handleCommands->Effect.runPromise
81
- expect(results)->toEqual([])
82
- })
105
+ testPromise(
106
+ "returns empty array",
107
+ async () => {
108
+ let results = await Stream.fromIterable([])->TestHandler.handleCommands->Effect.runPromise
109
+ expect(results)->toEqual([])
110
+ },
111
+ )
83
112
  })
84
113
  })
@@ -15,37 +15,36 @@ let _ = beforeEach(() => {
15
15
 
16
16
  describe("Aggregate_Callback causation propagation:", () => {
17
17
  testPromise("event.meta.causationId = command.meta.msgId", async () => {
18
- let _ =
19
- await Stream.fromIterable([
20
- makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
21
- ])->TestHandler.handleCommands->Effect.runPromise
18
+ let _ = await Stream.fromIterable([makeTopicItem("ref-1", AggSpec.Create({name: "Widget"}))])
19
+ ->TestHandler.handleCommands
20
+ ->Effect.runPromise
22
21
  let stored = mock.getAll()->Array.getUnsafe(0)
23
22
  expect(stored.meta.causationId)->toEqual(Some(testMeta.msgId))
24
23
  })
25
24
 
26
- testPromise("event.meta.correlationId = command.meta.correlationId (chain root stable)", async () => {
27
- let _ =
28
- await Stream.fromIterable([
29
- makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
30
- ])->TestHandler.handleCommands->Effect.runPromise
31
- let stored = mock.getAll()->Array.getUnsafe(0)
32
- expect(stored.meta.correlationId)->toBe(testMeta.correlationId)
33
- })
25
+ testPromise(
26
+ "event.meta.correlationId = command.meta.correlationId (chain root stable)",
27
+ async () => {
28
+ let _ = await Stream.fromIterable([makeTopicItem("ref-1", AggSpec.Create({name: "Widget"}))])
29
+ ->TestHandler.handleCommands
30
+ ->Effect.runPromise
31
+ let stored = mock.getAll()->Array.getUnsafe(0)
32
+ expect(stored.meta.correlationId)->toBe(testMeta.correlationId)
33
+ },
34
+ )
34
35
 
35
36
  testPromise("event has a fresh msgId distinct from the command's msgId", async () => {
36
- let _ =
37
- await Stream.fromIterable([
38
- makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
39
- ])->TestHandler.handleCommands->Effect.runPromise
37
+ let _ = await Stream.fromIterable([makeTopicItem("ref-1", AggSpec.Create({name: "Widget"}))])
38
+ ->TestHandler.handleCommands
39
+ ->Effect.runPromise
40
40
  let stored = mock.getAll()->Array.getUnsafe(0)
41
41
  expect(stored.meta.msgId == testMeta.msgId)->toBe(false)
42
42
  })
43
43
 
44
44
  testPromise("event inherits ip / user / service from the command's meta", async () => {
45
- let _ =
46
- await Stream.fromIterable([
47
- makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
48
- ])->TestHandler.handleCommands->Effect.runPromise
45
+ let _ = await Stream.fromIterable([makeTopicItem("ref-1", AggSpec.Create({name: "Widget"}))])
46
+ ->TestHandler.handleCommands
47
+ ->Effect.runPromise
49
48
  let stored = mock.getAll()->Array.getUnsafe(0)
50
49
  expect(stored.meta.service)->toBe(testMeta.service)
51
50
  })
@@ -3,19 +3,18 @@
3
3
 
4
4
  open JestGlobals
5
5
 
6
-
7
6
  module AggSpec = {
8
7
  module Id = Reventless.Id.StringPure
9
8
  let name = "ConflictTestAggregate"
10
9
 
11
10
  @schema
12
- type command = | Create({name: string})
11
+ type command = Create({name: string})
13
12
 
14
13
  @schema
15
- type event = | Created({name: string})
14
+ type event = Created({name: string})
16
15
 
17
16
  @schema
18
- type error = | AlreadyExists
17
+ type error = AlreadyExists
19
18
 
20
19
  let moduleUrl: string = %raw(`import.meta.url`)
21
20
  }
@@ -37,14 +36,21 @@ module TestBehavior = {
37
36
  | Created({name}) => {name: name}
38
37
  }
39
38
 
40
- let decide = (_state: state, command: AggSpec.command): result<array<AggSpec.event>, AggSpec.error> =>
39
+ let decide = (_state: state, command: AggSpec.command): result<
40
+ array<AggSpec.event>,
41
+ AggSpec.error,
42
+ > =>
41
43
  switch command {
42
44
  | Create({name}) => Ok([AggSpec.Created({name: name})])
43
45
  }
44
46
  }
45
47
 
46
48
  type mockEL = {
47
- appendFn: (int, string, array<Message.event'<string, AggSpec.event>>) => promise<result<unit, EventLog.appendError>>,
49
+ appendFn: (
50
+ int,
51
+ string,
52
+ array<Message.event'<string, AggSpec.event>>,
53
+ ) => promise<result<unit, EventLog.appendError>>,
48
54
  replayFn: string => promise<array<AggSpec.event>>,
49
55
  replayStreamFn: string => Stream.t<AggSpec.event, string, unit>,
50
56
  getAll: unit => array<Message.event'<string, AggSpec.event>>,
@@ -67,7 +73,7 @@ let makeMockEL = (): mockEL => {
67
73
  Error(EventLog.Conflict)
68
74
  } else {
69
75
  storedRef := storedRef.contents->Array.concat(newEvents)
70
- Ok(())
76
+ Ok()
71
77
  }
72
78
  }
73
79
 
@@ -117,10 +123,18 @@ module TestOps = {
117
123
  type event = AggSpec.event
118
124
  }
119
125
  type operations = {
120
- append: (int, string, array<Message.event'<string, AggSpec.event>>) => promise<result<unit, EventLog.appendError>>,
126
+ append: (
127
+ int,
128
+ string,
129
+ array<Message.event'<string, AggSpec.event>>,
130
+ ) => promise<result<unit, EventLog.appendError>>,
121
131
  replay: string => promise<array<AggSpec.event>>,
122
132
  replayStream: (string, ~fromSeq: int=?) => Stream.t<AggSpec.event, string, unit>,
123
- appendStream: (int, string, Stream.t<AggSpec.event, string, unit>) => Effect.t<unit, string, unit>,
133
+ appendStream: (
134
+ int,
135
+ string,
136
+ Stream.t<AggSpec.event, string, unit>,
137
+ ) => Effect.t<unit, string, unit>,
124
138
  latestSnapshot: string => promise<result<option<EventLog.snapshot>, string>>,
125
139
  writeSnapshot: (string, EventLog.snapshot) => promise<result<unit, string>>,
126
140
  }
@@ -169,7 +183,9 @@ describe("Aggregate_Callback — conflict retry:", () => {
169
183
  mock.failNextAppendsWithConflict := 1
170
184
  let results = await Stream.fromIterable([
171
185
  makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
172
- ])->TestHandler.handleCommands->Effect.runPromise
186
+ ])
187
+ ->TestHandler.handleCommands
188
+ ->Effect.runPromise
173
189
  expect(results)->toEqual([Ok("ref-1")])
174
190
  // 1 initial append (conflict) + 1 retry append (success) = 2
175
191
  expect(mock.appendCallCount.contents)->toBe(2)
@@ -177,9 +193,9 @@ describe("Aggregate_Callback — conflict retry:", () => {
177
193
 
178
194
  testPromise("conflict triggers re-replay", async () => {
179
195
  mock.failNextAppendsWithConflict := 1
180
- let _ = await Stream.fromIterable([
181
- makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
182
- ])->TestHandler.handleCommands->Effect.runPromise
196
+ let _ = await Stream.fromIterable([makeTopicItem("ref-1", AggSpec.Create({name: "Widget"}))])
197
+ ->TestHandler.handleCommands
198
+ ->Effect.runPromise
183
199
  // 1 initial replay + 1 retry replay = 2
184
200
  expect(mock.replayCallCount.contents)->toBe(2)
185
201
  })
@@ -188,7 +204,9 @@ describe("Aggregate_Callback — conflict retry:", () => {
188
204
  mock.failNextAppendsWithConflict := 2
189
205
  let results = await Stream.fromIterable([
190
206
  makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
191
- ])->TestHandler.handleCommands->Effect.runPromise
207
+ ])
208
+ ->TestHandler.handleCommands
209
+ ->Effect.runPromise
192
210
  expect(results)->toEqual([Ok("ref-1")])
193
211
  expect(mock.appendCallCount.contents)->toBe(3)
194
212
  })
@@ -198,23 +216,27 @@ describe("Aggregate_Callback — conflict retry:", () => {
198
216
  mock.failNextAppendsWithConflict := 4
199
217
  let results = await Stream.fromIterable([
200
218
  makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
201
- ])->TestHandler.handleCommands->Effect.runPromise
219
+ ])
220
+ ->TestHandler.handleCommands
221
+ ->Effect.runPromise
202
222
  expect(results)->toEqual([Error("concurrent modification (3 retries exhausted): conflict")])
203
223
  expect(mock.appendCallCount.contents)->toBe(4)
204
224
  })
205
225
 
206
226
  testPromise("successful append after conflict stores event", async () => {
207
227
  mock.failNextAppendsWithConflict := 1
208
- let _ = await Stream.fromIterable([
209
- makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
210
- ])->TestHandler.handleCommands->Effect.runPromise
228
+ let _ = await Stream.fromIterable([makeTopicItem("ref-1", AggSpec.Create({name: "Widget"}))])
229
+ ->TestHandler.handleCommands
230
+ ->Effect.runPromise
211
231
  expect(mock.getAll()->Array.length)->toBe(1)
212
232
  })
213
233
 
214
234
  testPromise("no conflict does not retry", async () => {
215
235
  let results = await Stream.fromIterable([
216
236
  makeTopicItem("ref-1", AggSpec.Create({name: "Widget"})),
217
- ])->TestHandler.handleCommands->Effect.runPromise
237
+ ])
238
+ ->TestHandler.handleCommands
239
+ ->Effect.runPromise
218
240
  expect(results)->toEqual([Ok("ref-1")])
219
241
  expect(mock.appendCallCount.contents)->toBe(1)
220
242
  expect(mock.replayCallCount.contents)->toBe(1)
@@ -1,4 +1,3 @@
1
-
2
1
  // ─────────────────────────────────────────────────────────────
3
2
  // Aggregate spec
4
3
  // ─────────────────────────────────────────────────────────────
@@ -49,7 +48,10 @@ module TestBehavior = {
49
48
  | AggSpec.Renamed({newName}) => Created({name: newName})
50
49
  }
51
50
 
52
- let decide = (state: state, command: AggSpec.command): result<array<AggSpec.event>, AggSpec.error> =>
51
+ let decide = (state: state, command: AggSpec.command): result<
52
+ array<AggSpec.event>,
53
+ AggSpec.error,
54
+ > =>
53
55
  switch (state, command) {
54
56
  | (NotCreated, Create({name})) => Ok([AggSpec.Created({name: name})])
55
57
  | (NotCreated, Rename(_)) => Error(NotFound)
@@ -63,7 +65,11 @@ module TestBehavior = {
63
65
  // ─────────────────────────────────────────────────────────────
64
66
 
65
67
  type mockEL = {
66
- appendFn: (int, string, array<Message.event'<string, AggSpec.event>>) => promise<result<unit, EventLog.appendError>>,
68
+ appendFn: (
69
+ int,
70
+ string,
71
+ array<Message.event'<string, AggSpec.event>>,
72
+ ) => promise<result<unit, EventLog.appendError>>,
67
73
  replayFn: string => promise<array<AggSpec.event>>,
68
74
  replayStreamFn: string => Stream.t<AggSpec.event, string, unit>,
69
75
  getAll: unit => array<Message.event'<string, AggSpec.event>>,
@@ -81,7 +87,7 @@ let makeMockEL = (): mockEL => {
81
87
  Error(EventLog.StorageFailure("append failed"))
82
88
  } else {
83
89
  storedRef := storedRef.contents->Array.concat(newEvents)
84
- Ok(())
90
+ Ok()
85
91
  }
86
92
 
87
93
  let replayFn = async id =>
@@ -135,7 +141,11 @@ module TestOps = {
135
141
  ) => promise<result<unit, EventLog.appendError>>,
136
142
  replay: string => promise<array<AggSpec.event>>,
137
143
  replayStream: (string, ~fromSeq: int=?) => Stream.t<AggSpec.event, string, unit>,
138
- appendStream: (int, string, Stream.t<AggSpec.event, string, unit>) => Effect.t<unit, string, unit>,
144
+ appendStream: (
145
+ int,
146
+ string,
147
+ Stream.t<AggSpec.event, string, unit>,
148
+ ) => Effect.t<unit, string, unit>,
139
149
  latestSnapshot: string => promise<result<option<EventLog.snapshot>, string>>,
140
150
  writeSnapshot: (string, EventLog.snapshot) => promise<result<unit, string>>,
141
151
  }