@reventlessdev/reventless-core 3.0.0-alpha.260 → 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 (203) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +9 -9
  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 +38 -51
  33. package/src/components/Api/GraphQL_SchemaInspector.res +5 -3
  34. package/src/components/Api/GraphQL_Stitcher.res +9 -10
  35. package/src/components/Api/MCP_SchemaGenerator.res +8 -12
  36. package/src/components/Api/OwnerScopeDiagnostics.res +1 -2
  37. package/src/components/Api/QueryDbListQuery.res +13 -6
  38. package/src/components/Api/SchemaType.res +28 -23
  39. package/src/components/Api/SuryToJsonSchema.res +9 -10
  40. package/src/components/AutomationSlice/AutomationSlice.res +4 -4
  41. package/src/components/AutomationSlice/AutomationSlice_Builder.res +15 -14
  42. package/src/components/AutomationSlice/AutomationSlice_Callback.res +3 -3
  43. package/src/components/Builder_Helpers.res +12 -23
  44. package/src/components/CommandGenerator/CommandGenerator.res +6 -1
  45. package/src/components/CommandGenerator/CommandGenerator_Builder.res +29 -21
  46. package/src/components/CommandGenerator/CommandGenerator_Callback.res +17 -21
  47. package/src/components/CommandTopic/CommandTopic.res +3 -3
  48. package/src/components/CommandTopic/CommandTopic_Builder.res +10 -10
  49. package/src/components/CommandTopic/CommandTopic_Callback.res +1 -4
  50. package/src/components/CommandTopic/CommandTopic_Helpers.res +1 -2
  51. package/src/components/Counter/Counter_Builder.res +1 -6
  52. package/src/components/Counter/Counter_Callback.res +3 -3
  53. package/src/components/Dcb/Dcb_Builder.res +970 -956
  54. package/src/components/DcbEventLog/DcbEventLog.res +4 -4
  55. package/src/components/DcbEventLog/DcbEventLog_Builder.res +0 -1
  56. package/src/components/DcbEventLog/DcbEventLog_Operations.res +17 -18
  57. package/src/components/EventCollector/EventCollector_Builder.res +2 -1
  58. package/src/components/EventLog/EventLog.res +5 -1
  59. package/src/components/EventLog/EventLog_Operations.res +12 -8
  60. package/src/components/EventMapper/EventMapper.res +8 -10
  61. package/src/components/EventMapper/EventMapper_Builder.res +4 -1
  62. package/src/components/EventMapper/EventMapper_Callback.res +46 -31
  63. package/src/components/EventTopic/EventTopic.res +3 -3
  64. package/src/components/EventTopic/EventTopic_Builder.res +1 -1
  65. package/src/components/Extension/Extension.res +4 -1
  66. package/src/components/Extension/Extension_Operations.res +11 -3
  67. package/src/components/ExtensionPoint/ExtensionPoint.res +3 -3
  68. package/src/components/ExtensionPoint/ExtensionPoint_Builder.res +9 -10
  69. package/src/components/ExtensionPoint/ExtensionPoint_Callback.res +2 -6
  70. package/src/components/ExtensionPoint/ExtensionPoint_Operations.res +13 -5
  71. package/src/components/Heartbeat/Heartbeat_Callback.res +3 -3
  72. package/src/components/InboundTranslationSlice/InboundTranslationSlice.res +4 -4
  73. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +7 -10
  74. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice.res +4 -4
  75. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +15 -14
  76. package/src/components/QueryDb/QueryDb_Adapter.res +1 -3
  77. package/src/components/QueryDb/QueryDb_Operations.res +10 -10
  78. package/src/components/ReadModel/ReadModel.res +3 -3
  79. package/src/components/ReadModel/ReadModel_Builder.res +1 -2
  80. package/src/components/ReadModel/ReadModel_Callback.res +2 -1
  81. package/src/components/SideEffectHandler/SideEffectHandler_Callback.res +5 -2
  82. package/src/components/StateChangeSlice/StateChangeSlice.res +3 -3
  83. package/src/components/StateChangeSlice/StateChangeSlice_Builder.res +23 -4
  84. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +22 -6
  85. package/src/components/StateTopic.res +1 -3
  86. package/src/components/StateViewSlice/StateViewSlice.res +4 -4
  87. package/src/components/StateViewSlice/StateViewSlice_Builder.res +79 -68
  88. package/src/components/StateViewSlice/StateViewSlice_Callback.res +3 -1
  89. package/src/components/Task/Task.res +3 -3
  90. package/src/components/Task/Task_Adapter.res +4 -1
  91. package/src/components/Task/Task_Builder.res +24 -21
  92. package/src/plugin/api/PluginBaseFragment.res +9 -3
  93. package/src/plugin/component/Capability_Inference.res +8 -10
  94. package/src/plugin/component/Plugin.res +6 -6
  95. package/src/plugin/component/Plugin_Builder.res +54 -50
  96. package/src/plugin/component/Plugin_Callback.res +10 -6
  97. package/src/plugin/component/Plugin_Helpers.res +264 -230
  98. package/src/plugin/component/Plugin_Structure.res +370 -324
  99. package/src/plugin/component/Plugin_SubscriptionSchema.res +8 -6
  100. package/src/plugin/component/SchemaWalker.res +11 -12
  101. package/src/plugin/connect/PluginConnectExtension_Mapping.res +45 -50
  102. package/src/plugin/lifecycle/PluginBehavior.res +3 -4
  103. package/src/plugin/lifecycle/PluginSpec.res +1 -2
  104. package/src/plugin/lifecycle/PluginsProjection.res +16 -5
  105. package/src/plugin/lifecycle/PluginsReadModelSpec.res +0 -2
  106. package/src/util/CommandPublisher.res +9 -2
  107. package/src/util/CsvStream.res +3 -3
  108. package/src/util/Interstack.res +7 -15
  109. package/src/util/Interstack.resi +3 -3
  110. package/src/util/LogFormat.res +5 -9
  111. package/src/util/Logger.res +1 -2
  112. package/src/util/Lru.res +2 -1
  113. package/src/util/Lru.resi +2 -3
  114. package/src/util/ScheduleOps.res +24 -27
  115. package/src/util/StorePrefixCollision.res +6 -9
  116. package/src/util/Util_Promise.res +4 -1
  117. package/src/util/Util_Promise.res.mjs +1 -1
  118. package/tests/IdentityTest.res +166 -113
  119. package/tests/ResourceAttributionTest.res +28 -25
  120. package/tests/StorePrefixCollisionTest.res +2 -1
  121. package/tests/TaskBucketNamingTest.res +3 -2
  122. package/tests/adapter/EventLogProvisioningTest.res +51 -48
  123. package/tests/adapter/MonitoringTest.res +81 -38
  124. package/tests/adapter/QueryInterceptionTest.res +2 -2
  125. package/tests/adapter/RefusalVocabularyTest.res +31 -23
  126. package/tests/adapter/RuntimeExtensionHookReachTest.res +9 -8
  127. package/tests/adapter/RuntimeExtensionTest.res +5 -3
  128. package/tests/admin/AdminApiSchemaDriftTest.res +22 -16
  129. package/tests/admin/Platform_Admin_StructureTest.res +10 -11
  130. package/tests/admin/Platform_BakedManifestTest.res +31 -35
  131. package/tests/admin/Platform_ComponentDefinitionsApiTest.res +18 -15
  132. package/tests/admin/Platform_PluginStructuresApiTest.res +5 -3
  133. package/tests/admin/Platform_UIFragmentsApiTest.res +6 -3
  134. package/tests/aggregate/AggregateCacheTest.res +10 -12
  135. package/tests/aggregate/AggregateCallbackTest.res +75 -46
  136. package/tests/aggregate/AggregateCausationTest.res +19 -20
  137. package/tests/aggregate/AggregateConflictTest.res +41 -19
  138. package/tests/aggregate/AggregateFixtures.res +15 -5
  139. package/tests/aggregate/AggregateRejectionTest.res +97 -80
  140. package/tests/aggregate/AggregateSnapshotTest.res +44 -16
  141. package/tests/api/Api_IdsTest.res +10 -4
  142. package/tests/api/BrandedMarkerCompositionTest.res +48 -36
  143. package/tests/api/GeolocationSemanticTest.res +3 -3
  144. package/tests/api/GraphQL_FragmentGeneratorTest.res +66 -54
  145. package/tests/api/OwnerScopeDiagnosticsTest.res +31 -27
  146. package/tests/api/StorageRefFieldsTest.res +30 -18
  147. package/tests/api/SuryToJsonSchemaTest.res +1315 -1014
  148. package/tests/api/TaggedUnionSchemaTest.res +17 -24
  149. package/tests/commandgenerator/CommandGeneratorCallbackTest.res +235 -179
  150. package/tests/commandgenerator/CommandGeneratorFixtures.res +8 -13
  151. package/tests/commandgenerator/OwnerStampingTest.res +91 -76
  152. package/tests/commandtopic/CommandOutcomeHookTest.res +198 -155
  153. package/tests/commandtopic/CommandTopicCallbackFixtures.res +5 -4
  154. package/tests/commandtopic/CommandTopicCallbackTest.res +61 -43
  155. package/tests/commandtopic/CommandTopicHelpersRejectionTest.res +70 -55
  156. package/tests/counter/CounterCallbackTest.res +40 -34
  157. package/tests/counter/CounterFixtures.res +4 -4
  158. package/tests/dcb/DcbDecodeTest.res +115 -84
  159. package/tests/dcb/DcbEventLogOperationsTest.res +194 -143
  160. package/tests/dcb/DcbEventLogSchemaTest.res +13 -13
  161. package/tests/dcb/DcbFixtures.res +4 -5
  162. package/tests/dcb/DcbScopeInferenceTest.res +173 -114
  163. package/tests/dcb/DcbStateChangeSliceTest.res +391 -262
  164. package/tests/dcb/DcbTagTest.res +22 -11
  165. package/tests/dcb/DcbValidationTest.res +288 -218
  166. package/tests/eventlog/EventLogFixtures.res +4 -4
  167. package/tests/eventlog/EventLogOperationsTest.res +102 -67
  168. package/tests/eventlog/EventLogRetryTest.res +104 -65
  169. package/tests/eventmapper/EventMapperCallbackTest.res +118 -83
  170. package/tests/eventmapper/EventMapperFixtures.res +15 -23
  171. package/tests/eventmapper/EventMapperTest.res +54 -38
  172. package/tests/eventtopic/EventTopicFixtures.res +3 -4
  173. package/tests/eventtopic/EventTopicOperationsTest.res +41 -29
  174. package/tests/extensionpoint/ExtensionPointCallbackTest.res +58 -45
  175. package/tests/extensionpoint/ExtensionPointFixtures.res +16 -20
  176. package/tests/extensionpoint/ExtensionPointOperationsTest.res +70 -49
  177. package/tests/extensionpoint/PluginDisconnectScheduleTest.res +2 -1
  178. package/tests/logger/LogFormatTest.res +104 -82
  179. package/tests/logger/LoggerLazyTest.res +21 -12
  180. package/tests/message/MessageTest.res +96 -93
  181. package/tests/message/MetaEnvelopeTest.res +26 -30
  182. package/tests/plugin/HeartbeatDisconnectGraceTest.res +31 -25
  183. package/tests/plugin/ManifestVisibilityTest.res +20 -12
  184. package/tests/plugin/PluginDefinitionScalars.res +1 -2
  185. package/tests/plugin/PluginLifecycleCorpusTest.res +15 -12
  186. package/tests/plugin/PluginSpecExperiment.res +0 -1
  187. package/tests/plugin/PluginStructureTest.res +1529 -1045
  188. package/tests/plugin/Plugin_EventQuerySchemaTest.res +21 -10
  189. package/tests/plugin/SchemaWalkerTest.res +116 -70
  190. package/tests/plugin/StateChange/PsReserveStock.res +4 -4
  191. package/tests/plugin/StateView/PsAnnotatedView.res +4 -4
  192. package/tests/plugin/StateView/PsCustomersView.res +4 -4
  193. package/tests/plugin/StateView/PsShipmentsView.res +3 -2
  194. package/tests/projection/LifecycleTrailTest.res +1 -4
  195. package/tests/projection/ProjectionOptimizeTest.res +13 -9
  196. package/tests/querydb/QueryDbFixtures.res +0 -1
  197. package/tests/querydb/QueryDbOperationsTest.res +99 -64
  198. package/tests/querydb/TaggedUnionStampTest.res +10 -8
  199. package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res +50 -35
  200. package/tests/sideeffecthandler/SideEffectHandlerFixtures.res +4 -10
  201. package/tests/util/CommandPublisherTest.res +0 -1
  202. package/tests/util/CsvStreamTest.res +3 -3
  203. package/tests/util/LruTest.res +84 -56
@@ -9,41 +9,54 @@ let _ = beforeEach(() => resetMocks())
9
9
 
10
10
  describe("MakeCounterHandler.handleCounterEvents:", () => {
11
11
  describe("event with matching source mapping", () => {
12
- testPromise("publishes command JSON for OrderPlaced", async () => {
13
- let eventJson =
14
- makeEventJson(
12
+ testPromise(
13
+ "publishes command JSON for OrderPlaced",
14
+ async () => {
15
+ let eventJson = makeEventJson(
15
16
  "src-1",
16
17
  CmdSourceSpec.OrderPlaced({orderId: "order-1", amount: 99.99})->Message.encode(
17
18
  CmdSourceSpec.eventSchema,
18
19
  ),
19
20
  )
20
- await TestCounterHandler.handleCounterEvents(Stream.fromIterable([eventJson]))->Effect.runPromise
21
- // publishJsons should have been called with 1 command
22
- expect(capturedCmds.contents->Array.length)->toBe(1)
23
- })
21
+ await TestCounterHandler.handleCounterEvents(
22
+ Stream.fromIterable([eventJson]),
23
+ )->Effect.runPromise
24
+ // publishJsons should have been called with 1 command
25
+ expect(capturedCmds.contents->Array.length)->toBe(1)
26
+ },
27
+ )
24
28
  })
25
29
 
26
30
  describe("event with unknown service", () => {
27
- testPromise("skipped — no publish", async () => {
28
- let eventJson =
29
- makeEventJson(
31
+ testPromise(
32
+ "skipped — no publish",
33
+ async () => {
34
+ let eventJson = makeEventJson(
30
35
  ~service="UnknownService",
31
36
  "src-1",
32
37
  CmdSourceSpec.OrderPlaced({orderId: "order-1", amount: 50.0})->Message.encode(
33
38
  CmdSourceSpec.eventSchema,
34
39
  ),
35
40
  )
36
- await TestCounterHandler.handleCounterEvents(Stream.fromIterable([eventJson]))->Effect.runPromise
37
- expect(capturedCmds.contents->Array.length)->toBe(0)
38
- })
41
+ await TestCounterHandler.handleCounterEvents(
42
+ Stream.fromIterable([eventJson]),
43
+ )->Effect.runPromise
44
+ expect(capturedCmds.contents->Array.length)->toBe(0)
45
+ },
46
+ )
39
47
  })
40
48
 
41
49
  describe("invalid event JSON", () => {
42
- testPromise("skipped gracefully — no throw, no publish", async () => {
43
- let invalidJson = JSON.Encode.string("not-an-object")
44
- await TestCounterHandler.handleCounterEvents(Stream.fromIterable([invalidJson]))->Effect.runPromise
45
- expect(capturedCmds.contents->Array.length)->toBe(0)
46
- })
50
+ testPromise(
51
+ "skipped gracefully — no throw, no publish",
52
+ async () => {
53
+ let invalidJson = JSON.Encode.string("not-an-object")
54
+ await TestCounterHandler.handleCounterEvents(
55
+ Stream.fromIterable([invalidJson]),
56
+ )->Effect.runPromise
57
+ expect(capturedCmds.contents->Array.length)->toBe(0)
58
+ },
59
+ )
47
60
  })
48
61
  })
49
62
 
@@ -53,82 +66,104 @@ describe("MakeCounterHandler.handleCounterEvents:", () => {
53
66
 
54
67
  describe("MakeEventCollectorHandler.handleJsonEvents:", () => {
55
68
  describe("Count action", () => {
56
- testPromise("calls count with the countItem", async () => {
57
- let countItem: ReventlessInfra.Counter.countItem = {
58
- counterId: "c1",
59
- reference: "ref-1",
60
- inc: 1,
61
- }
62
- mockCommonHandler :=
63
- async _ => (Promise.resolve([]), [Counter.Count(countItem)])
64
- await TestECHandler.handleJsonEvents(Stream.fromIterable([JSON.Encode.null]))->Effect.runPromise
65
- expect(capturedCountItems.contents)->toEqual([countItem])
66
- })
69
+ testPromise(
70
+ "calls count with the countItem",
71
+ async () => {
72
+ let countItem: ReventlessInfra.Counter.countItem = {
73
+ counterId: "c1",
74
+ reference: "ref-1",
75
+ inc: 1,
76
+ }
77
+ mockCommonHandler := (async _ => (Promise.resolve([]), [Counter.Count(countItem)]))
78
+ await TestECHandler.handleJsonEvents(
79
+ Stream.fromIterable([JSON.Encode.null]),
80
+ )->Effect.runPromise
81
+ expect(capturedCountItems.contents)->toEqual([countItem])
82
+ },
83
+ )
67
84
  })
68
85
 
69
86
  describe("AddToCounterTarget action", () => {
70
- testPromise("calls addToCounterTarget with the counterTargetRef", async () => {
71
- let target: ReventlessInfra.Counter.counterTargetRef = {
72
- counterId: "c1",
73
- target: 5,
74
- targetRef: "ref-1",
75
- }
76
- mockCommonHandler :=
77
- async _ => (Promise.resolve([]), [Counter.AddToCounterTarget(target)])
78
- await TestECHandler.handleJsonEvents(Stream.fromIterable([JSON.Encode.null]))->Effect.runPromise
79
- expect(capturedCounterTargets.contents)->toEqual([target])
80
- })
87
+ testPromise(
88
+ "calls addToCounterTarget with the counterTargetRef",
89
+ async () => {
90
+ let target: ReventlessInfra.Counter.counterTargetRef = {
91
+ counterId: "c1",
92
+ target: 5,
93
+ targetRef: "ref-1",
94
+ }
95
+ mockCommonHandler :=
96
+ (async _ => (Promise.resolve([]), [Counter.AddToCounterTarget(target)]))
97
+ await TestECHandler.handleJsonEvents(
98
+ Stream.fromIterable([JSON.Encode.null]),
99
+ )->Effect.runPromise
100
+ expect(capturedCounterTargets.contents)->toEqual([target])
101
+ },
102
+ )
81
103
  })
82
104
 
83
105
  describe("Publisher action", () => {
84
- testPromise("calls publishJsons with the command", async () => {
85
- let cmd: Message.commandJson = {
86
- id: "agg-1",
87
- meta: evtMapTestMeta,
88
- commandJson: JSON.Encode.string("DoSomething"),
89
- }
90
- mockCommonHandler := async _ => (Promise.resolve([cmd]), [])
91
- await TestECHandler.handleJsonEvents(Stream.fromIterable([JSON.Encode.null]))->Effect.runPromise
92
- expect(capturedCmds.contents->Array.length)->toBe(1)
93
- })
106
+ testPromise(
107
+ "calls publishJsons with the command",
108
+ async () => {
109
+ let cmd: Message.commandJson = {
110
+ id: "agg-1",
111
+ meta: evtMapTestMeta,
112
+ commandJson: JSON.Encode.string("DoSomething"),
113
+ }
114
+ mockCommonHandler := (async _ => (Promise.resolve([cmd]), []))
115
+ await TestECHandler.handleJsonEvents(
116
+ Stream.fromIterable([JSON.Encode.null]),
117
+ )->Effect.runPromise
118
+ expect(capturedCmds.contents->Array.length)->toBe(1)
119
+ },
120
+ )
94
121
  })
95
122
 
96
123
  describe("mixed actions", () => {
97
- testPromise("both publish and count called", async () => {
98
- let countItem: ReventlessInfra.Counter.countItem = {
99
- counterId: "c1",
100
- reference: "ref-1",
101
- inc: 2,
102
- }
103
- let cmd: Message.commandJson = {
104
- id: "agg-1",
105
- meta: evtMapTestMeta,
106
- commandJson: JSON.Encode.string("Cmd"),
107
- }
108
- mockCommonHandler :=
109
- async _ => (Promise.resolve([cmd]), [Counter.Count(countItem)])
110
- await TestECHandler.handleJsonEvents(Stream.fromIterable([JSON.Encode.null]))->Effect.runPromise
111
- expect((
112
- capturedCmds.contents->Array.length,
113
- capturedCountItems.contents->Array.length,
114
- ))->toEqual((1, 1))
115
- })
124
+ testPromise(
125
+ "both publish and count called",
126
+ async () => {
127
+ let countItem: ReventlessInfra.Counter.countItem = {
128
+ counterId: "c1",
129
+ reference: "ref-1",
130
+ inc: 2,
131
+ }
132
+ let cmd: Message.commandJson = {
133
+ id: "agg-1",
134
+ meta: evtMapTestMeta,
135
+ commandJson: JSON.Encode.string("Cmd"),
136
+ }
137
+ mockCommonHandler := (async _ => (Promise.resolve([cmd]), [Counter.Count(countItem)]))
138
+ await TestECHandler.handleJsonEvents(
139
+ Stream.fromIterable([JSON.Encode.null]),
140
+ )->Effect.runPromise
141
+ expect((
142
+ capturedCmds.contents->Array.length,
143
+ capturedCountItems.contents->Array.length,
144
+ ))->toEqual((1, 1))
145
+ },
146
+ )
116
147
  })
117
148
 
118
149
  describe("doCount retry", () => {
119
- testPromise("retries when count throws, eventually succeeds", async () => {
120
- let countItem: ReventlessInfra.Counter.countItem = {
121
- counterId: "c1",
122
- reference: "ref-1",
123
- inc: 1,
124
- }
125
- // Fail on first call, succeed on second
126
- mockCountFailUntil := 1
127
- mockCommonHandler :=
128
- async _ => (Promise.resolve([]), [Counter.Count(countItem)])
129
- await TestECHandler.handleJsonEvents(Stream.fromIterable([JSON.Encode.null]))->Effect.runPromise
130
- // count was called at least twice (1 failure + 1 success)
131
- expect(mockCountCallCount.contents >= 2)->toBe(true)
132
- })
150
+ testPromise(
151
+ "retries when count throws, eventually succeeds",
152
+ async () => {
153
+ let countItem: ReventlessInfra.Counter.countItem = {
154
+ counterId: "c1",
155
+ reference: "ref-1",
156
+ inc: 1,
157
+ }
158
+ // Fail on first call, succeed on second
159
+ mockCountFailUntil := 1
160
+ mockCommonHandler := (async _ => (Promise.resolve([]), [Counter.Count(countItem)]))
161
+ await TestECHandler.handleJsonEvents(
162
+ Stream.fromIterable([JSON.Encode.null]),
163
+ )->Effect.runPromise
164
+ // count was called at least twice (1 failure + 1 success)
165
+ expect(mockCountCallCount.contents >= 2)->toBe(true)
166
+ },
167
+ )
133
168
  })
134
169
  })
@@ -1,6 +1,5 @@
1
1
  open Reventless.Projection
2
2
 
3
-
4
3
  // ─────────────────────────────────────────────────────────────
5
4
  // Source event spec (aggregate events)
6
5
  // ─────────────────────────────────────────────────────────────
@@ -63,11 +62,11 @@ let testMeta: Reventless.Message.meta = {
63
62
  correlationId: "corr-001",
64
63
  }
65
64
 
66
- let makeSourceEvent' = (id, event) => ({
65
+ let makeSourceEvent' = (id, event): Reventless.Message.event'<string, SourceSpec.event> => {
67
66
  Reventless.Message.id,
68
67
  meta: testMeta,
69
68
  event,
70
- }: Reventless.Message.event'<string, SourceSpec.event>)
69
+ }
71
70
 
72
71
  // ─────────────────────────────────────────────────────────────
73
72
  // EventMapper_Callback test fixtures
@@ -103,14 +102,13 @@ module OrderMapping = {
103
102
  module Source = CmdSourceSpec
104
103
  module Target = CmdTargetSpec
105
104
 
106
- let map = (
107
- id: CmdSourceSpec.Id.t,
108
- event: CmdSourceSpec.event,
109
- _queryEngine,
110
- ): array<Reventless.EventMapping.action<CmdTargetSpec.Id.t, CmdTargetSpec.command>> =>
105
+ let map = (id: CmdSourceSpec.Id.t, event: CmdSourceSpec.event, _queryEngine): array<
106
+ Reventless.EventMapping.action<CmdTargetSpec.Id.t, CmdTargetSpec.command>,
107
+ > =>
111
108
  switch event {
112
- | OrderPlaced({orderId, amount}) =>
113
- [Reventless.EventMapping.Publish(id, CmdTargetSpec.ProcessOrder({orderId, amount}))]
109
+ | OrderPlaced({orderId, amount}) => [
110
+ Reventless.EventMapping.Publish(id, CmdTargetSpec.ProcessOrder({orderId, amount})),
111
+ ]
114
112
  | OrderShipped({orderId}) =>
115
113
  let cmd = CmdTargetSpec.ShipOrder({orderId: orderId})
116
114
  [Reventless.EventMapping.Publish(id, cmd)]
@@ -122,15 +120,12 @@ module CountOrderMapping = {
122
120
  module Source = CmdSourceSpec
123
121
  module Target = CmdTargetSpec
124
122
 
125
- let map = (
126
- _id: CmdSourceSpec.Id.t,
127
- event: CmdSourceSpec.event,
128
- _queryEngine,
129
- ): array<Reventless.EventMapping.action<CmdTargetSpec.Id.t, CmdTargetSpec.command>> =>
123
+ let map = (_id: CmdSourceSpec.Id.t, event: CmdSourceSpec.event, _queryEngine): array<
124
+ Reventless.EventMapping.action<CmdTargetSpec.Id.t, CmdTargetSpec.command>,
125
+ > =>
130
126
  switch event {
131
127
  | OrderPlaced(_) => [Reventless.EventMapping.Count("order-counter")]
132
- | OrderShipped(_) =>
133
- [
128
+ | OrderShipped(_) => [
134
129
  Reventless.EventMapping.AddToCounterTarget({
135
130
  counterId: "order-counter",
136
131
  target: 5,
@@ -191,10 +186,7 @@ module TestCounterHandler = EventMapper_Callback.MakeCounterHandler(
191
186
 
192
187
  // configurable mock for commonEventsHandler — reassigned in each test
193
188
  let mockCommonHandler: ref<
194
- array<JSON.t> => promise<(
195
- promise<array<Reventless.Message.commandJson>>,
196
- array<Counter.action>,
197
- )>,
189
+ array<JSON.t> => promise<(promise<array<Reventless.Message.commandJson>>, array<Counter.action>)>,
198
190
  > = ref(async _ => (Promise.resolve([]), []))
199
191
 
200
192
  let mockCountFailUntil = ref(0)
@@ -237,7 +229,7 @@ let makeEventJson = (~service=CmdSourceSpec.name, id, eventJson): JSON.t =>
237
229
  ("id", JSON.Encode.string(id)),
238
230
  (
239
231
  "meta",
240
- {...evtMapTestMeta, service: service}->Reventless.Message.encode(Reventless.Message.metaSchema),
232
+ {...evtMapTestMeta, service}->Reventless.Message.encode(Reventless.Message.metaSchema),
241
233
  ),
242
234
  ("event", eventJson),
243
235
  ]
@@ -250,5 +242,5 @@ let resetMocks = () => {
250
242
  capturedCounterTargets := []
251
243
  mockCountFailUntil := 0
252
244
  mockCountCallCount := 0
253
- mockCommonHandler := async _ => (Promise.resolve([]), [])
245
+ mockCommonHandler := (async _ => (Promise.resolve([]), []))
254
246
  }
@@ -4,50 +4,66 @@ open EventMapperFixtures
4
4
 
5
5
  describe("Mapping.Make:", () => {
6
6
  describe("sourceName", () => {
7
- testSync("equals source spec name", () =>
8
- expect(ItemMapping.sourceName)->toBe("SourceAggregate")
7
+ testSync(
8
+ "equals source spec name",
9
+ () => expect(ItemMapping.sourceName)->toBe("SourceAggregate"),
9
10
  )
10
11
  })
11
12
 
12
13
  describe("project", () => {
13
- testSync("ItemCreated maps to Create action", () => {
14
- let event' = makeSourceEvent'("item-1", SourceSpec.ItemCreated({name: "Widget", price: 9.99}))
15
- let action = ItemMapping.project(event')
16
- switch action {
17
- | Create(id, state) =>
18
- expect((id, state.name, state.price))->toEqual(("item-1", "Widget", 9.99))
19
- | _ => fail("Expected Create action")
20
- }
21
- })
14
+ testSync(
15
+ "ItemCreated maps to Create action",
16
+ () => {
17
+ let event' = makeSourceEvent'(
18
+ "item-1",
19
+ SourceSpec.ItemCreated({name: "Widget", price: 9.99}),
20
+ )
21
+ let action = ItemMapping.project(event')
22
+ switch action {
23
+ | Create(id, state) =>
24
+ expect((id, state.name, state.price))->toEqual(("item-1", "Widget", 9.99))
25
+ | _ => fail("Expected Create action")
26
+ }
27
+ },
28
+ )
22
29
 
23
- testSync("ItemPriceUpdated maps to Update action", () => {
24
- let event' = makeSourceEvent'("item-1", SourceSpec.ItemPriceUpdated({newPrice: 14.99}))
25
- let action = ItemMapping.project(event')
26
- switch action {
27
- | Update(id, _) => expect(id)->toBe("item-1")
28
- | _ => fail("Expected Update action")
29
- }
30
- })
30
+ testSync(
31
+ "ItemPriceUpdated maps to Update action",
32
+ () => {
33
+ let event' = makeSourceEvent'("item-1", SourceSpec.ItemPriceUpdated({newPrice: 14.99}))
34
+ let action = ItemMapping.project(event')
35
+ switch action {
36
+ | Update(id, _) => expect(id)->toBe("item-1")
37
+ | _ => fail("Expected Update action")
38
+ }
39
+ },
40
+ )
31
41
 
32
- testSync("ItemRemoved maps to Delete action", () => {
33
- let event' = makeSourceEvent'("item-1", SourceSpec.ItemRemoved)
34
- let action = ItemMapping.project(event')
35
- switch action {
36
- | Delete(id) => expect(id)->toBe("item-1")
37
- | _ => fail("Expected Delete action")
38
- }
39
- })
42
+ testSync(
43
+ "ItemRemoved maps to Delete action",
44
+ () => {
45
+ let event' = makeSourceEvent'("item-1", SourceSpec.ItemRemoved)
46
+ let action = ItemMapping.project(event')
47
+ switch action {
48
+ | Delete(id) => expect(id)->toBe("item-1")
49
+ | _ => fail("Expected Delete action")
50
+ }
51
+ },
52
+ )
40
53
 
41
- testSync("Update action applies function correctly", () => {
42
- let event' = makeSourceEvent'("item-1", SourceSpec.ItemPriceUpdated({newPrice: 14.99}))
43
- let action = ItemMapping.project(event')
44
- switch action {
45
- | Update(_, fn) =>
46
- let originalState: TargetSpec.state = {name: "Widget", price: 9.99}
47
- let updated = fn(originalState)
48
- expect(updated.price)->toBe(14.99)
49
- | _ => fail("Expected Update action")
50
- }
51
- })
54
+ testSync(
55
+ "Update action applies function correctly",
56
+ () => {
57
+ let event' = makeSourceEvent'("item-1", SourceSpec.ItemPriceUpdated({newPrice: 14.99}))
58
+ let action = ItemMapping.project(event')
59
+ switch action {
60
+ | Update(_, fn) =>
61
+ let originalState: TargetSpec.state = {name: "Widget", price: 9.99}
62
+ let updated = fn(originalState)
63
+ expect(updated.price)->toBe(14.99)
64
+ | _ => fail("Expected Update action")
65
+ }
66
+ },
67
+ )
52
68
  })
53
69
  })
@@ -1,11 +1,10 @@
1
-
2
1
  // Test event spec for EventTopic
3
2
  module ItemEventTopicSpec = {
4
3
  module Id = Reventless.Id.StringPure
5
4
  let name = "TestItemEventTopic"
6
5
 
7
6
  @schema
8
- type event = | ItemPublished({name: string}) | ItemRemoved({id: string})
7
+ type event = ItemPublished({name: string}) | ItemRemoved({id: string})
9
8
  }
10
9
 
11
10
  // Captured publish calls
@@ -24,11 +23,11 @@ let testMeta: Message.meta = {
24
23
  correlationId: "corr-001",
25
24
  }
26
25
 
27
- let makeEvent' = (id, event) => ({
26
+ let makeEvent' = (id, event): Message.event'<string, ItemEventTopicSpec.event> => {
28
27
  Reventless.Message.id,
29
28
  meta: testMeta,
30
29
  event,
31
- }: Message.event'<string, ItemEventTopicSpec.event>)
30
+ }
32
31
 
33
32
  let reset = () => {
34
33
  capturedCalls := []
@@ -12,37 +12,49 @@ let _ = beforeEach(() => reset())
12
12
 
13
13
  describe("EventTopic_Operations:", () => {
14
14
  describe("publish", () => {
15
- testPromise("calls publishJson for each event", async () => {
16
- let events = [
17
- makeEvent'("item-1", ItemEventTopicSpec.ItemPublished({name: "Widget"})),
18
- makeEvent'("item-2", ItemEventTopicSpec.ItemRemoved({id: "item-2"})),
19
- ]
20
- await Ops.publish(events)
21
- expect(capturedCalls.contents->Array.length)->toBe(2)
22
- })
15
+ testPromise(
16
+ "calls publishJson for each event",
17
+ async () => {
18
+ let events = [
19
+ makeEvent'("item-1", ItemEventTopicSpec.ItemPublished({name: "Widget"})),
20
+ makeEvent'("item-2", ItemEventTopicSpec.ItemRemoved({id: "item-2"})),
21
+ ]
22
+ await Ops.publish(events)
23
+ expect(capturedCalls.contents->Array.length)->toBe(2)
24
+ },
25
+ )
23
26
 
24
- testPromise("calls publishJson with correct service (id string)", async () => {
25
- let event' = makeEvent'("item-1", ItemEventTopicSpec.ItemPublished({name: "Widget"}))
26
- await Ops.publish([event'])
27
- let first = capturedCalls.contents->Array.getUnsafe(0)
28
- let (service, _, _) = first
29
- expect(service)->toBe("item-1")
30
- })
27
+ testPromise(
28
+ "calls publishJson with correct service (id string)",
29
+ async () => {
30
+ let event' = makeEvent'("item-1", ItemEventTopicSpec.ItemPublished({name: "Widget"}))
31
+ await Ops.publish([event'])
32
+ let first = capturedCalls.contents->Array.getUnsafe(0)
33
+ let (service, _, _) = first
34
+ expect(service)->toBe("item-1")
35
+ },
36
+ )
31
37
 
32
- testPromise("publishes empty array without error", async () => {
33
- await Ops.publish([])
34
- expect(capturedCalls.contents->Array.length)->toBe(0)
35
- })
38
+ testPromise(
39
+ "publishes empty array without error",
40
+ async () => {
41
+ await Ops.publish([])
42
+ expect(capturedCalls.contents->Array.length)->toBe(0)
43
+ },
44
+ )
36
45
 
37
- testPromise("encodes event with correct JSON structure", async () => {
38
- let event' = makeEvent'("item-1", ItemEventTopicSpec.ItemPublished({name: "Widget"}))
39
- await Ops.publish([event'])
40
- let (_, _, json) = capturedCalls.contents->Array.getUnsafe(0)
41
- // JSON should contain the event type info
42
- let hasExpectedFields =
43
- json->JSON.Decode.object->Option.map(d => d->Dict.has("event"))->Option.getOr(false) ||
44
- json->JSON.Decode.object->Option.map(d => d->Dict.has("id"))->Option.getOr(false)
45
- expect(hasExpectedFields)->toBe(true)
46
- })
46
+ testPromise(
47
+ "encodes event with correct JSON structure",
48
+ async () => {
49
+ let event' = makeEvent'("item-1", ItemEventTopicSpec.ItemPublished({name: "Widget"}))
50
+ await Ops.publish([event'])
51
+ let (_, _, json) = capturedCalls.contents->Array.getUnsafe(0)
52
+ // JSON should contain the event type info
53
+ let hasExpectedFields =
54
+ json->JSON.Decode.object->Option.map(d => d->Dict.has("event"))->Option.getOr(false) ||
55
+ json->JSON.Decode.object->Option.map(d => d->Dict.has("id"))->Option.getOr(false)
56
+ expect(hasExpectedFields)->toBe(true)
57
+ },
58
+ )
47
59
  })
48
60
  })