@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
@@ -1,4 +1,3 @@
1
-
2
1
  // ─────────────────────────────────────────────────────────────
3
2
  // Test event spec
4
3
  // ─────────────────────────────────────────────────────────────
@@ -8,7 +7,7 @@ module ItemEventLogSpec = {
8
7
  let name = "TestItemEventLog"
9
8
 
10
9
  @schema
11
- type event = | ItemCreated({name: string}) | ItemDeleted({id: string})
10
+ type event = ItemCreated({name: string}) | ItemDeleted({id: string})
12
11
  }
13
12
 
14
13
  // ─────────────────────────────────────────────────────────────
@@ -83,7 +82,8 @@ module MockEventTopic: EventTopic.T
83
82
  publishJsonStream: ReventlessInfra.EventTopic.publishJsonStream,
84
83
  }
85
84
  type component = Component.t<EventTopic.t, EventTopic.outputs, operations>
86
- let make = (~name as _, ~storageResources as _, ~owner as _=?, ~opts as _=?): component => Obj.magic(0)
85
+ let make = (~name as _, ~storageResources as _, ~owner as _=?, ~opts as _=?): component =>
86
+ Obj.magic(0)
87
87
  }
88
88
 
89
89
  let mockEventTopicOps: MockEventTopic.operations = {
@@ -91,7 +91,7 @@ let mockEventTopicOps: MockEventTopic.operations = {
91
91
  capturedPublishes := capturedPublishes.contents->Array.concat(events)
92
92
  },
93
93
  publishJson: async (_service, _meta, _json) => (),
94
- publishJsonStream: _stream => Effect.succeed(()),
94
+ publishJsonStream: _stream => Effect.succeed(),
95
95
  }
96
96
 
97
97
  // ─────────────────────────────────────────────────────────────
@@ -15,82 +15,117 @@ let _ = beforeEach(() => reset())
15
15
 
16
16
  describe("EventLog_Operations:", () => {
17
17
  describe("append", () => {
18
- testPromise("stores encoded events in storage", async () => {
19
- let event = ItemEventLogSpec.ItemCreated({name: "Widget"})
20
- let event' = makeEvent'("item-1", event)
21
- let _ = await Ops.append(1, "item-1", [event'])
22
- let stored = storedEvents.contents->Dict.get("item-1")->Option.getOr([])
23
- expect(stored->Array.length)->toBe(1)
24
- })
18
+ testPromise(
19
+ "stores encoded events in storage",
20
+ async () => {
21
+ let event = ItemEventLogSpec.ItemCreated({name: "Widget"})
22
+ let event' = makeEvent'("item-1", event)
23
+ let _ = await Ops.append(1, "item-1", [event'])
24
+ let stored = storedEvents.contents->Dict.get("item-1")->Option.getOr([])
25
+ expect(stored->Array.length)->toBe(1)
26
+ },
27
+ )
25
28
 
26
- testPromise("publishes events to event topic", async () => {
27
- let event = ItemEventLogSpec.ItemCreated({name: "Widget"})
28
- let event' = makeEvent'("item-1", event)
29
- let _ = await Ops.append(1, "item-1", [event'])
30
- expect(capturedPublishes.contents->Array.length)->toBe(1)
31
- })
29
+ testPromise(
30
+ "publishes events to event topic",
31
+ async () => {
32
+ let event = ItemEventLogSpec.ItemCreated({name: "Widget"})
33
+ let event' = makeEvent'("item-1", event)
34
+ let _ = await Ops.append(1, "item-1", [event'])
35
+ expect(capturedPublishes.contents->Array.length)->toBe(1)
36
+ },
37
+ )
32
38
 
33
- testPromise("storage failure returns Error result", async () => {
34
- failNextAppend := true
35
- let event = ItemEventLogSpec.ItemCreated({name: "Widget"})
36
- let event' = makeEvent'("item-1", event)
37
- let result = await Ops.append(1, "item-1", [event'])
38
- // When storage returns Error (not throws), the operation still publishes
39
- // but the Error result is propagated back to the caller.
40
- expect(Result.isError(result))->toBe(true)
41
- })
39
+ testPromise(
40
+ "storage failure returns Error result",
41
+ async () => {
42
+ failNextAppend := true
43
+ let event = ItemEventLogSpec.ItemCreated({name: "Widget"})
44
+ let event' = makeEvent'("item-1", event)
45
+ let result = await Ops.append(1, "item-1", [event'])
46
+ // When storage returns Error (not throws), the operation still publishes
47
+ // but the Error result is propagated back to the caller.
48
+ expect(Result.isError(result))->toBe(true)
49
+ },
50
+ )
42
51
 
43
- testPromise("multiple events all stored and published", async () => {
44
- let events' = [
45
- makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "First"})),
46
- makeEvent'("item-2", ItemEventLogSpec.ItemCreated({name: "Second"})),
47
- ]
48
- let _ = await Ops.append(1, "agg-1", events')
49
- let stored = storedEvents.contents->Dict.get("agg-1")->Option.getOr([])
50
- expect((stored->Array.length, capturedPublishes.contents->Array.length))->toEqual((2, 2))
51
- })
52
+ testPromise(
53
+ "multiple events all stored and published",
54
+ async () => {
55
+ let events' = [
56
+ makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "First"})),
57
+ makeEvent'("item-2", ItemEventLogSpec.ItemCreated({name: "Second"})),
58
+ ]
59
+ let _ = await Ops.append(1, "agg-1", events')
60
+ let stored = storedEvents.contents->Dict.get("agg-1")->Option.getOr([])
61
+ expect((stored->Array.length, capturedPublishes.contents->Array.length))->toEqual((2, 2))
62
+ },
63
+ )
52
64
 
53
- testPromise("returns Ok on success", async () => {
54
- let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
55
- let result = await Ops.append(1, "item-1", [event'])
56
- expect(Result.isOk(result))->toBe(true)
57
- })
65
+ testPromise(
66
+ "returns Ok on success",
67
+ async () => {
68
+ let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
69
+ let result = await Ops.append(1, "item-1", [event'])
70
+ expect(Result.isOk(result))->toBe(true)
71
+ },
72
+ )
58
73
  })
59
74
 
60
75
  describe("replay", () => {
61
- testPromise("returns empty array for unknown id", async () => {
62
- let events = await Ops.replay("unknown-id")
63
- expect(events->Array.length)->toBe(0)
64
- })
76
+ testPromise(
77
+ "returns empty array for unknown id",
78
+ async () => {
79
+ let events = await Ops.replay("unknown-id")
80
+ expect(events->Array.length)->toBe(0)
81
+ },
82
+ )
65
83
 
66
- testPromise("decodes stored events back to typed events", async () => {
67
- let event = ItemEventLogSpec.ItemCreated({name: "Widget"})
68
- let event' = makeEvent'("item-1", event)
69
- let _ = await Ops.append(1, "item-1", [event'])
70
- let replayed = await Ops.replay("item-1")
71
- expect(replayed->Array.length)->toBe(1)
72
- let first = replayed->Array.getUnsafe(0)
73
- expect(first)->toEqual(event)
74
- })
84
+ testPromise(
85
+ "decodes stored events back to typed events",
86
+ async () => {
87
+ let event = ItemEventLogSpec.ItemCreated({name: "Widget"})
88
+ let event' = makeEvent'("item-1", event)
89
+ let _ = await Ops.append(1, "item-1", [event'])
90
+ let replayed = await Ops.replay("item-1")
91
+ expect(replayed->Array.length)->toBe(1)
92
+ let first = replayed->Array.getUnsafe(0)
93
+ expect(first)->toEqual(event)
94
+ },
95
+ )
75
96
 
76
- testPromise("decodes multiple events in order", async () => {
77
- let events = [
78
- ItemEventLogSpec.ItemCreated({name: "Widget"}),
79
- ItemEventLogSpec.ItemDeleted({id: "item-1"}),
80
- ]
81
- let events' = events->Array.map(event => makeEvent'("item-1", event))
82
- let _ = await Ops.append(1, "item-1", events')
83
- let replayed = await Ops.replay("item-1")
84
- expect(replayed->Array.length)->toBe(2)
85
- expect(replayed)->toEqual(events)
86
- })
97
+ testPromise(
98
+ "decodes multiple events in order",
99
+ async () => {
100
+ let events = [
101
+ ItemEventLogSpec.ItemCreated({name: "Widget"}),
102
+ ItemEventLogSpec.ItemDeleted({id: "item-1"}),
103
+ ]
104
+ let events' = events->Array.map(event => makeEvent'("item-1", event))
105
+ let _ = await Ops.append(1, "item-1", events')
106
+ let replayed = await Ops.replay("item-1")
107
+ expect(replayed->Array.length)->toBe(2)
108
+ expect(replayed)->toEqual(events)
109
+ },
110
+ )
87
111
 
88
- testPromise("separate aggregates have independent event logs", async () => {
89
- let _ = await Ops.append(1, "agg-A", [makeEvent'("agg-A", ItemEventLogSpec.ItemCreated({name: "A"}))])
90
- let _ = await Ops.append(1, "agg-B", [makeEvent'("agg-B", ItemEventLogSpec.ItemDeleted({id: "B"}))])
91
- let eventsA = await Ops.replay("agg-A")
92
- let eventsB = await Ops.replay("agg-B")
93
- expect((eventsA->Array.length, eventsB->Array.length))->toEqual((1, 1))
94
- })
112
+ testPromise(
113
+ "separate aggregates have independent event logs",
114
+ async () => {
115
+ let _ = await Ops.append(
116
+ 1,
117
+ "agg-A",
118
+ [makeEvent'("agg-A", ItemEventLogSpec.ItemCreated({name: "A"}))],
119
+ )
120
+ let _ = await Ops.append(
121
+ 1,
122
+ "agg-B",
123
+ [makeEvent'("agg-B", ItemEventLogSpec.ItemDeleted({id: "B"}))],
124
+ )
125
+ let eventsA = await Ops.replay("agg-A")
126
+ let eventsB = await Ops.replay("agg-B")
127
+ expect((eventsA->Array.length, eventsB->Array.length))->toEqual((1, 1))
128
+ },
129
+ )
95
130
  })
96
131
  })
@@ -17,84 +17,123 @@ let _ = beforeEach(() => reset())
17
17
  describe("EventLog_Operations — retry:", () => {
18
18
  describe("isTransient predicate:", () => {
19
19
  let transient = msg => EventLog_Operations.isTransient(EventLog.StorageFailure(msg))
20
- testPromise("ThrottlingException is transient", async () => {
21
- expect(transient("ThrottlingException: Rate exceeded"))->toBe(true)
22
- })
20
+ testPromise(
21
+ "ThrottlingException is transient",
22
+ async () => {
23
+ expect(transient("ThrottlingException: Rate exceeded"))->toBe(true)
24
+ },
25
+ )
23
26
 
24
- testPromise("ProvisionedThroughputExceededException is transient", async () => {
25
- expect(transient("ProvisionedThroughputExceededException"))->toBe(true)
26
- })
27
+ testPromise(
28
+ "ProvisionedThroughputExceededException is transient",
29
+ async () => {
30
+ expect(transient("ProvisionedThroughputExceededException"))->toBe(true)
31
+ },
32
+ )
27
33
 
28
- testPromise("ServiceUnavailable is transient", async () => {
29
- expect(transient("ServiceUnavailable"))->toBe(true)
30
- })
34
+ testPromise(
35
+ "ServiceUnavailable is transient",
36
+ async () => {
37
+ expect(transient("ServiceUnavailable"))->toBe(true)
38
+ },
39
+ )
31
40
 
32
- testPromise("RequestLimitExceeded is transient", async () => {
33
- expect(transient("RequestLimitExceeded"))->toBe(true)
34
- })
41
+ testPromise(
42
+ "RequestLimitExceeded is transient",
43
+ async () => {
44
+ expect(transient("RequestLimitExceeded"))->toBe(true)
45
+ },
46
+ )
35
47
 
36
- testPromise("InternalServerError is transient", async () => {
37
- expect(transient("InternalServerError: something went wrong"))->toBe(true)
38
- })
48
+ testPromise(
49
+ "InternalServerError is transient",
50
+ async () => {
51
+ expect(transient("InternalServerError: something went wrong"))->toBe(true)
52
+ },
53
+ )
39
54
 
40
- testPromise("ValidationException is not transient", async () => {
41
- expect(transient("ValidationException: invalid field"))->toBe(false)
42
- })
55
+ testPromise(
56
+ "ValidationException is not transient",
57
+ async () => {
58
+ expect(transient("ValidationException: invalid field"))->toBe(false)
59
+ },
60
+ )
43
61
 
44
- testPromise("generic mock storage failure is not transient", async () => {
45
- expect(transient("mock storage failure"))->toBe(false)
46
- })
62
+ testPromise(
63
+ "generic mock storage failure is not transient",
64
+ async () => {
65
+ expect(transient("mock storage failure"))->toBe(false)
66
+ },
67
+ )
47
68
 
48
- testPromise("a Conflict is never transient (retried a layer up, not here)", async () => {
49
- expect(EventLog_Operations.isTransient(EventLog.Conflict))->toBe(false)
50
- })
69
+ testPromise(
70
+ "a Conflict is never transient (retried a layer up, not here)",
71
+ async () => {
72
+ expect(EventLog_Operations.isTransient(EventLog.Conflict))->toBe(false)
73
+ },
74
+ )
51
75
  })
52
76
 
53
77
  describe("permanent storage failure:", () => {
54
- testPromise("returns Error immediately without retry", async () => {
55
- failNextAppend := true // "mock storage failure" — not transient, no retry
56
- let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
57
- let result = await Ops.append(1, "item-1", [event'])
58
- expect(Result.isError(result))->toBe(true)
59
- // Exactly one attempt — permanent error is not retried
60
- expect(appendCallCount.contents)->toBe(1)
61
- })
62
-
63
- testPromise("does not publish events after permanent storage failure", async () => {
64
- failNextAppend := true
65
- let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
66
- let _ = await Ops.append(1, "item-1", [event'])
67
- expect(capturedPublishes.contents->Array.length)->toBe(0)
68
- })
78
+ testPromise(
79
+ "returns Error immediately without retry",
80
+ async () => {
81
+ failNextAppend := true // "mock storage failure" — not transient, no retry
82
+ let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
83
+ let result = await Ops.append(1, "item-1", [event'])
84
+ expect(Result.isError(result))->toBe(true)
85
+ // Exactly one attempt — permanent error is not retried
86
+ expect(appendCallCount.contents)->toBe(1)
87
+ },
88
+ )
89
+
90
+ testPromise(
91
+ "does not publish events after permanent storage failure",
92
+ async () => {
93
+ failNextAppend := true
94
+ let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
95
+ let _ = await Ops.append(1, "item-1", [event'])
96
+ expect(capturedPublishes.contents->Array.length)->toBe(0)
97
+ },
98
+ )
69
99
  })
70
100
 
71
101
  describe("transient storage failure — retry:", () => {
72
- testPromise("1 transient failure retries and returns Ok", async () => {
73
- failNextAppendsWithTransient := 1
74
- let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
75
- let result = await Ops.append(1, "item-1", [event'])
76
- expect(Result.isOk(result))->toBe(true)
77
- // 1 initial attempt + 1 retry = 2 total calls to storage.append
78
- expect(appendCallCount.contents)->toBe(2)
79
- })
80
-
81
- testPromise("events are stored and published after successful retry", async () => {
82
- failNextAppendsWithTransient := 1
83
- let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
84
- let _ = await Ops.append(1, "item-1", [event'])
85
- let stored = storedEvents.contents->Dict.get("item-1")->Option.getOr([])
86
- expect(stored->Array.length)->toBe(1)
87
- expect(capturedPublishes.contents->Array.length)->toBe(1)
88
- })
89
-
90
- testPromise("2 transient failures retry twice and return Ok", async () => {
91
- failNextAppendsWithTransient := 2
92
- let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
93
- let result = await Ops.append(1, "item-1", [event'])
94
- expect(Result.isOk(result))->toBe(true)
95
- // 1 initial + 2 retries = 3 total calls
96
- expect(appendCallCount.contents)->toBe(3)
97
- })
102
+ testPromise(
103
+ "1 transient failure retries and returns Ok",
104
+ async () => {
105
+ failNextAppendsWithTransient := 1
106
+ let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
107
+ let result = await Ops.append(1, "item-1", [event'])
108
+ expect(Result.isOk(result))->toBe(true)
109
+ // 1 initial attempt + 1 retry = 2 total calls to storage.append
110
+ expect(appendCallCount.contents)->toBe(2)
111
+ },
112
+ )
113
+
114
+ testPromise(
115
+ "events are stored and published after successful retry",
116
+ async () => {
117
+ failNextAppendsWithTransient := 1
118
+ let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
119
+ let _ = await Ops.append(1, "item-1", [event'])
120
+ let stored = storedEvents.contents->Dict.get("item-1")->Option.getOr([])
121
+ expect(stored->Array.length)->toBe(1)
122
+ expect(capturedPublishes.contents->Array.length)->toBe(1)
123
+ },
124
+ )
125
+
126
+ testPromise(
127
+ "2 transient failures retry twice and return Ok",
128
+ async () => {
129
+ failNextAppendsWithTransient := 2
130
+ let event' = makeEvent'("item-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))
131
+ let result = await Ops.append(1, "item-1", [event'])
132
+ expect(Result.isOk(result))->toBe(true)
133
+ // 1 initial + 2 retries = 3 total calls
134
+ expect(appendCallCount.contents)->toBe(3)
135
+ },
136
+ )
98
137
  })
99
138
 
100
139
  describe("retry exhaustion:", () => {