@reventlessdev/reventless-local 3.0.0-alpha.249 → 3.0.0-alpha.250
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.
- package/CHANGELOG.md +8 -0
- package/package.json +13 -13
- package/src/BakedManifest.res +5 -6
- package/src/EmitCapabilities.res +3 -1
- package/src/LocalPlatformRegistry.res +3 -1
- package/src/LocalPlatformStart.res +7 -6
- package/src/LocalSeedTarget.res +3 -1
- package/src/Platform.res +291 -215
- package/src/ShellConfig.res +10 -17
- package/src/UiHints.res +8 -14
- package/src/UiSlots.res +10 -17
- package/src/adapter/Api/LocalEvents_Server.res +5 -3
- package/src/adapter/Api/LocalGraphQL_Adapter.res +4 -4
- package/src/adapter/Auth/LocalAuth.res +11 -15
- package/src/adapter/Auth/UserStore.res +5 -7
- package/src/adapter/Backend.res +1 -2
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res +42 -26
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res +13 -3
- package/src/adapter/CommandTopic/LocalCommandTopicChannel.res +15 -17
- package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res +18 -22
- package/src/adapter/Counter/LocalCounterHandler.res +1 -1
- package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res +33 -22
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +57 -57
- package/src/adapter/DomainGraphQL_Server.res +35 -34
- package/src/adapter/DomainMCP_Server.res +45 -41
- package/src/adapter/EventCollector/LocalEventCollectorChannel.res +47 -36
- package/src/adapter/EventHistory/EventHistoryResolvers_GraphQL.res +15 -12
- package/src/adapter/EventLog/EventLogStorage_InMemory.res +9 -4
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res +36 -24
- package/src/adapter/EventTopic/LocalEventTopicPublisher.res +9 -3
- package/src/adapter/Heartbeat/LocalHeartbeatRunner.res +9 -14
- package/src/adapter/LocalBus.res +12 -12
- package/src/adapter/LocalCapabilities.res.mjs +1 -1
- package/src/adapter/LocalGeocodeResolvers.res +1 -4
- package/src/adapter/LocalSideEffectHandler.res +1 -1
- package/src/adapter/LocalStateChangeDescriptor.res +2 -1
- package/src/adapter/MCP_ServerInstance.res +51 -39
- package/src/adapter/ObjectStore/LocalObjectStore.res +5 -2
- package/src/adapter/ObjectStore/ObjectStoreStorage_FileSystem.res +1 -2
- package/src/adapter/PlatformGraphQL_Server.res +9 -5
- package/src/adapter/ProjectionCheckpoint.res +8 -1
- package/src/adapter/ProjectionPending.res +1 -3
- package/src/adapter/QueryDb/LocalQueryDbStorage.res +2 -1
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +367 -269
- package/src/adapter/QueryDb/QueryDbStorage_InMemory.res +5 -3
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +63 -40
- package/src/adapter/QueryDb/SortKey_Filter.res +14 -6
- package/src/adapter/Runtime/LocalAggregateRuntime_Builder.res +12 -10
- package/src/adapter/Runtime/LocalEventCollectorRuntime_Builder.res +8 -2
- package/src/adapter/Runtime/LocalPluginRuntime_Builder.res +1 -4
- package/src/adapter/Scheduler/LocalScheduledPublisher.res +12 -12
- package/src/adapter/SqliteDriver.res +16 -4
- package/src/adapter/Task/TaskBucket_Sqlite.res +5 -6
- package/src/components/Aggregate_Builder.res +6 -1
- package/src/components/Counter_Builder.res +1 -5
- package/src/components/DcbEventLog_Builder.res +5 -3
- package/src/components/ExtensionPoint_Builder.res +13 -15
- package/src/components/InboundTranslationSlice_Builder.res +3 -2
- package/src/components/OutboundTranslationSlice_Builder.res +3 -2
- package/src/components/ReadModel_Builder.res +18 -20
- package/src/components/StateViewSlice_Builder.res +3 -2
- package/src/components/Task_Builder.res +12 -13
- package/src/reset/LocalSeedReset.res +2 -5
- package/src/test/Mocks/MockDcbEventLogStorage.res +5 -1
- package/src/test/Mocks/MockEventLogStorage.res +8 -3
- package/src/test/Mocks/MockQueryDbStorage.res +3 -2
- package/src/test/TestRunner.res +1 -1
- package/tests/BakedManifestFilesTest.res +7 -10
- package/tests/LocalEventTapTest.res +26 -21
- package/tests/LocalPlatformStartTest.res +33 -28
- package/tests/PluginEventDecodeTest.res +3 -3
- package/tests/ShellConfigTest.res +30 -21
- package/tests/SplitApiFixtures.res +34 -10
- package/tests/SplitApiTest.res +92 -62
- package/tests/adapter/BackendParityTest.res +180 -130
- package/tests/adapter/CollectNEventsTest.res +6 -4
- package/tests/adapter/CommandAuthorizationTest.res +47 -41
- package/tests/adapter/CommandTopicChannelTest.res +163 -139
- package/tests/adapter/CounterHandlerTest.res +95 -77
- package/tests/adapter/DcbEventLogStorageSqliteTest.res +171 -168
- package/tests/adapter/DcbEventLogStorageTest.res +264 -207
- package/tests/adapter/DomainGraphQL_ServerTest.res +53 -43
- package/tests/adapter/EventCollectorChannelTest.res +153 -124
- package/tests/adapter/EventHistoryResolverTest.res +46 -18
- package/tests/adapter/EventLogSnapshotParityTest.res +57 -45
- package/tests/adapter/EventLogStorageSqliteTest.res +9 -10
- package/tests/adapter/EventTapTest.res +37 -20
- package/tests/adapter/EventTopicPublisherTest.res +74 -50
- package/tests/adapter/GraphQLSubscriptionBridgeTest.res +4 -3
- package/tests/adapter/GraphQL_SchemaInspectorTest.res +666 -568
- package/tests/adapter/GraphQL_SubscriptionResolversTest.res +105 -96
- package/tests/adapter/HeartbeatRunnerTest.res +99 -81
- package/tests/adapter/InboundTranslationMutationTest.res +4 -1
- package/tests/adapter/LocalAuthHttpTest.res +79 -85
- package/tests/adapter/LocalAuthLoginTest.res +42 -50
- package/tests/adapter/LocalAuthTest.res +8 -16
- package/tests/adapter/LocalAuthUserStoreTest.res +2 -7
- package/tests/adapter/LocalBusBoundedTest.res +187 -117
- package/tests/adapter/LocalBusPubSubTest.res +118 -76
- package/tests/adapter/LocalDispatchContextTest.res +1 -2
- package/tests/adapter/LocalEvents_ServerTest.res +242 -187
- package/tests/adapter/PgProjectionCatchupTest.res +44 -10
- package/tests/adapter/ProjectionCheckpointTest.res +261 -206
- package/tests/adapter/QueryDbGsiTtlTest.res +123 -83
- package/tests/adapter/QueryDbListPushdownParityTest.res +107 -67
- package/tests/adapter/QueryDbListResolverTest.res +225 -208
- package/tests/adapter/QueryDbResolvedFieldsTest.res +17 -14
- package/tests/adapter/QueryDbStorageSqliteTest.res +33 -49
- package/tests/adapter/QueryDbStorageTest.res +163 -117
- package/tests/adapter/QueryDbUnionRoundTripTest.res +8 -10
- package/tests/adapter/QueryEngineTest.res +179 -126
- package/tests/adapter/ScheduledPublisherTest.res +171 -135
- package/tests/adapter/ServedBucketHttpTest.res +65 -59
- package/tests/adapter/SortKey_FilterTest.res +19 -3
- package/tests/adapter/SqliteDriverTest.res +13 -10
- package/tests/adapter/TaskBucketTest.res +94 -80
- package/tests/components/aggregate/AggregateTest.res +31 -18
- package/tests/components/automationslice/AutomationSliceCallbackTest.res +259 -222
- package/tests/components/automationslice/AutomationSliceFixtures.res +4 -2
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +38 -38
- package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res +12 -14
- package/tests/components/automationslice/MixedSourceAutomationSliceTest.res +8 -32
- package/tests/components/commandgenerator/CommandGeneratorFixtures.res +7 -5
- package/tests/components/commandgenerator/CommandGeneratorTest.res +40 -31
- package/tests/components/commandtopic/CommandTopicFixtures.res +3 -4
- package/tests/components/commandtopic/CommandTopicStreamFixtures.res +7 -5
- package/tests/components/commandtopic/CommandTopicTest.res +24 -15
- package/tests/components/counter/CounterFixtures.res +6 -8
- package/tests/components/dcb/DcbCrossPartitionFixtures.res +8 -2
- package/tests/components/dcb/DcbCrossPartitionReadTest.res +20 -13
- package/tests/components/dcb/DcbEventLogAppendStreamTest.res +12 -8
- package/tests/components/dcb/DcbEventLogStreamTest.res +88 -61
- package/tests/components/dcb/DcbFixtures.res +4 -1
- package/tests/components/dcb/DcbTest.res +9 -6
- package/tests/components/eventcollector/EventCollectorFixtures.res +0 -1
- package/tests/components/eventcollector/EventCollectorTest.res +6 -3
- package/tests/components/eventlog/EventLogAppendStreamTest.res +88 -68
- package/tests/components/eventlog/EventLogConflictTest.res +8 -4
- package/tests/components/eventlog/EventLogFixtures.res +3 -3
- package/tests/components/eventlog/EventLogProvisioningSeamTest.res +26 -13
- package/tests/components/eventlog/EventLogStreamTest.res +119 -56
- package/tests/components/eventlog/EventLogTest.res +20 -4
- package/tests/components/eventtopic/EventTopicFixtures.res +3 -3
- package/tests/components/eventtopic/EventTopicStreamFixtures.res +1 -2
- package/tests/components/eventtopic/EventTopicStreamTest.res +7 -1
- package/tests/components/extensionpoint/ExtensionPointFixtures.res +14 -9
- package/tests/components/extensionpoint/ExtensionPointTest.res +3 -2
- package/tests/components/heartbeat/HeartbeatFixtures.res +5 -6
- package/tests/components/heartbeat/HeartbeatTest.res +56 -47
- package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +271 -249
- package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res +3 -2
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res +361 -276
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res +8 -10
- package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res +37 -43
- package/tests/components/querydb/QueryDbFixtures.res +1 -5
- package/tests/components/querydb/QueryDbTest.res +2 -4
- package/tests/components/readmodel/DcbReadModelE2EFixtures.res +15 -18
- package/tests/components/readmodel/MixedSourceReadModelFixtures.res +5 -7
- package/tests/components/readmodel/ReadModelFixtures.res +8 -8
- package/tests/components/scheduler/SchedulerTest.res +76 -58
- package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res +4 -4
- package/tests/components/stateviewslice/StateViewSliceDisplayNameFixtures.res +5 -7
- package/tests/components/stateviewslice/StateViewSliceFixtures.res +8 -6
- package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res +17 -11
- package/tests/components/stateviewslice/StateViewSliceSubIdTest.res +12 -2
- package/tests/components/task/TaskFixtures.res +5 -5
- package/tests/components/task/TaskTest.res +95 -80
- package/tests/plugin/PluginsProjection_GWT.res +3 -1
- package/tests/plugin/UiFragments_GWT.res +6 -1
- package/tests/reset/LocalSeedResetTest.res +1 -0
|
@@ -13,86 +13,149 @@ describe("EventLog.replayStream (in-memory adapter)", () => {
|
|
|
13
13
|
let _ = beforeEach(() => reset())
|
|
14
14
|
|
|
15
15
|
describe("basic streaming", () => {
|
|
16
|
-
testPromise(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
testPromise(
|
|
17
|
+
"replayStream returns all appended events",
|
|
18
|
+
async () => {
|
|
19
|
+
let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
|
|
20
|
+
let _ = await ops.append(
|
|
21
|
+
0,
|
|
22
|
+
"stream-1",
|
|
23
|
+
[makeEvent'("stream-1", ItemEventLogSpec.ItemCreated({name: "Widget"}))],
|
|
24
|
+
)
|
|
25
|
+
let _ = await ops.append(
|
|
26
|
+
1,
|
|
27
|
+
"stream-1",
|
|
28
|
+
[makeEvent'("stream-1", ItemEventLogSpec.ItemDeleted({id: "stream-1"}))],
|
|
29
|
+
)
|
|
30
|
+
let count = await ops.replayStream("stream-1")
|
|
21
31
|
->Stream.runFold(0, (n, _) => n + 1)
|
|
22
32
|
->Effect.runPromise
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
expect(count)->toBe(2)
|
|
34
|
+
},
|
|
35
|
+
)
|
|
25
36
|
|
|
26
|
-
testPromise(
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
testPromise(
|
|
38
|
+
"replayStream for unknown id returns empty stream",
|
|
39
|
+
async () => {
|
|
40
|
+
let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
|
|
41
|
+
let count = await ops.replayStream("no-such-id")
|
|
29
42
|
->Stream.runFold(0, (n, _) => n + 1)
|
|
30
43
|
->Effect.runPromise
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
expect(count)->toBe(0)
|
|
45
|
+
},
|
|
46
|
+
)
|
|
33
47
|
|
|
34
|
-
testPromise(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
48
|
+
testPromise(
|
|
49
|
+
"replayStream emits events in append order",
|
|
50
|
+
async () => {
|
|
51
|
+
let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
|
|
52
|
+
let _ = await ops.append(
|
|
53
|
+
0,
|
|
54
|
+
"stream-2",
|
|
55
|
+
[makeEvent'("stream-2", ItemEventLogSpec.ItemCreated({name: "First"}))],
|
|
56
|
+
)
|
|
57
|
+
let _ = await ops.append(
|
|
58
|
+
1,
|
|
59
|
+
"stream-2",
|
|
60
|
+
[makeEvent'("stream-2", ItemEventLogSpec.ItemDeleted({id: "stream-2"}))],
|
|
61
|
+
)
|
|
62
|
+
let events = await ops.replayStream("stream-2")
|
|
39
63
|
->Stream.runCollect
|
|
40
64
|
->Effect.runPromise
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
65
|
+
expect(events->Array.length)->toBe(2)
|
|
66
|
+
// First emitted is the Created event
|
|
67
|
+
let first = events->Array.getUnsafe(0)
|
|
68
|
+
expect(first)->toEqual(ItemEventLogSpec.ItemCreated({name: "First"}))
|
|
69
|
+
},
|
|
70
|
+
)
|
|
46
71
|
|
|
47
|
-
testPromise(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
72
|
+
testPromise(
|
|
73
|
+
"separate aggregate IDs have independent streams",
|
|
74
|
+
async () => {
|
|
75
|
+
let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
|
|
76
|
+
let _ = await ops.append(
|
|
77
|
+
0,
|
|
78
|
+
"agg-X",
|
|
79
|
+
[makeEvent'("agg-X", ItemEventLogSpec.ItemCreated({name: "X"}))],
|
|
80
|
+
)
|
|
81
|
+
let _ = await ops.append(
|
|
82
|
+
0,
|
|
83
|
+
"agg-Y",
|
|
84
|
+
[
|
|
85
|
+
makeEvent'("agg-Y", ItemEventLogSpec.ItemCreated({name: "Y1"})),
|
|
86
|
+
makeEvent'("agg-Y", ItemEventLogSpec.ItemDeleted({id: "agg-Y"})),
|
|
87
|
+
],
|
|
88
|
+
)
|
|
89
|
+
let countX = await ops.replayStream("agg-X")
|
|
52
90
|
->Stream.runFold(0, (n, _) => n + 1)
|
|
53
91
|
->Effect.runPromise
|
|
54
|
-
|
|
92
|
+
let countY = await ops.replayStream("agg-Y")
|
|
55
93
|
->Stream.runFold(0, (n, _) => n + 1)
|
|
56
94
|
->Effect.runPromise
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
95
|
+
expect(countX)->toBe(1)
|
|
96
|
+
expect(countY)->toBe(2)
|
|
97
|
+
},
|
|
98
|
+
)
|
|
60
99
|
|
|
61
|
-
testPromise(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
100
|
+
testPromise(
|
|
101
|
+
"take(2) on a 5-event log returns only 2 events",
|
|
102
|
+
async () => {
|
|
103
|
+
let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
|
|
104
|
+
let _ = await ops.append(
|
|
105
|
+
0,
|
|
106
|
+
"stream-3",
|
|
107
|
+
[
|
|
108
|
+
makeEvent'("stream-3", ItemEventLogSpec.ItemCreated({name: "e1"})),
|
|
109
|
+
makeEvent'("stream-3", ItemEventLogSpec.ItemDeleted({id: "e2"})),
|
|
110
|
+
makeEvent'("stream-3", ItemEventLogSpec.ItemCreated({name: "e3"})),
|
|
111
|
+
makeEvent'("stream-3", ItemEventLogSpec.ItemDeleted({id: "e4"})),
|
|
112
|
+
makeEvent'("stream-3", ItemEventLogSpec.ItemCreated({name: "e5"})),
|
|
113
|
+
],
|
|
114
|
+
)
|
|
115
|
+
let first2 = await ops.replayStream("stream-3")
|
|
71
116
|
->Stream.take(2)
|
|
72
117
|
->Stream.runCollect
|
|
73
118
|
->Effect.runPromise
|
|
74
|
-
|
|
75
|
-
|
|
119
|
+
expect(first2->Array.length)->toBe(2)
|
|
120
|
+
},
|
|
121
|
+
)
|
|
76
122
|
})
|
|
77
123
|
|
|
78
124
|
describe("tuple fold — (state, count) pattern used in Aggregate_Callback", () => {
|
|
79
|
-
testPromise(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
125
|
+
testPromise(
|
|
126
|
+
"runFold produces correct count for sequenceNr",
|
|
127
|
+
async () => {
|
|
128
|
+
let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
|
|
129
|
+
let _ = await ops.append(
|
|
130
|
+
0,
|
|
131
|
+
"fold-1",
|
|
132
|
+
[
|
|
133
|
+
makeEvent'("fold-1", ItemEventLogSpec.ItemCreated({name: "e1"})),
|
|
134
|
+
makeEvent'("fold-1", ItemEventLogSpec.ItemDeleted({id: "e2"})),
|
|
135
|
+
],
|
|
136
|
+
)
|
|
137
|
+
let _ = await ops.append(
|
|
138
|
+
2,
|
|
139
|
+
"fold-1",
|
|
140
|
+
[makeEvent'("fold-1", ItemEventLogSpec.ItemCreated({name: "e3"}))],
|
|
141
|
+
)
|
|
142
|
+
let (_state, count) = await ops.replayStream("fold-1")
|
|
84
143
|
->Stream.runFold((None, 0), ((st, n), _ev) => (st, n + 1))
|
|
85
144
|
->Effect.runPromise
|
|
86
|
-
|
|
87
|
-
|
|
145
|
+
expect(count)->toBe(3)
|
|
146
|
+
},
|
|
147
|
+
)
|
|
88
148
|
|
|
89
|
-
testPromise(
|
|
90
|
-
|
|
91
|
-
|
|
149
|
+
testPromise(
|
|
150
|
+
"runFold on empty stream returns initial accumulator",
|
|
151
|
+
async () => {
|
|
152
|
+
let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
|
|
153
|
+
let (state, count) = await ops.replayStream("unknown-fold")
|
|
92
154
|
->Stream.runFold((None, 0), ((st, n), _ev) => (st, n + 1))
|
|
93
155
|
->Effect.runPromise
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
156
|
+
expect(state)->toEqual(None)
|
|
157
|
+
expect(count)->toBe(0)
|
|
158
|
+
},
|
|
159
|
+
)
|
|
97
160
|
})
|
|
98
161
|
})
|
|
@@ -36,16 +36,32 @@ describe("EventLog (in-memory)", () => {
|
|
|
36
36
|
|
|
37
37
|
testPromise("multiple appends accumulate events", async () => {
|
|
38
38
|
let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
|
|
39
|
-
let _ = await ops.append(
|
|
40
|
-
|
|
39
|
+
let _ = await ops.append(
|
|
40
|
+
0,
|
|
41
|
+
"agg-1",
|
|
42
|
+
[makeEvent'("agg-1", ItemEventLogSpec.ItemCreated({name: "First"}))],
|
|
43
|
+
)
|
|
44
|
+
let _ = await ops.append(
|
|
45
|
+
1,
|
|
46
|
+
"agg-1",
|
|
47
|
+
[makeEvent'("agg-1", ItemEventLogSpec.ItemDeleted({id: "agg-1"}))],
|
|
48
|
+
)
|
|
41
49
|
let replayed = await ops.replay("agg-1")
|
|
42
50
|
expect(replayed->Array.length)->toBe(2)
|
|
43
51
|
})
|
|
44
52
|
|
|
45
53
|
testPromise("separate aggregates have independent event logs", async () => {
|
|
46
54
|
let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
|
|
47
|
-
let _ = await ops.append(
|
|
48
|
-
|
|
55
|
+
let _ = await ops.append(
|
|
56
|
+
0,
|
|
57
|
+
"agg-A",
|
|
58
|
+
[makeEvent'("agg-A", ItemEventLogSpec.ItemCreated({name: "A"}))],
|
|
59
|
+
)
|
|
60
|
+
let _ = await ops.append(
|
|
61
|
+
0,
|
|
62
|
+
"agg-B",
|
|
63
|
+
[makeEvent'("agg-B", ItemEventLogSpec.ItemDeleted({id: "B"}))],
|
|
64
|
+
)
|
|
49
65
|
let eventsA = await ops.replay("agg-A")
|
|
50
66
|
let eventsB = await ops.replay("agg-B")
|
|
51
67
|
expect((eventsA->Array.length, eventsB->Array.length))->toEqual((1, 1))
|
|
@@ -7,7 +7,7 @@ module ItemEventTopicSpec = {
|
|
|
7
7
|
let name = "TestItemEventTopic"
|
|
8
8
|
|
|
9
9
|
@schema
|
|
10
|
-
type event =
|
|
10
|
+
type event = ItemPublished({name: string}) | ItemRemoved({id: string})
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -36,11 +36,11 @@ module EventTopicMaker = ReventlessCore.EventTopic_Builder.Make(
|
|
|
36
36
|
|
|
37
37
|
let eventTopic = EventTopicMaker.make(~name="TestItemEventTopic", ~storageResources=[])
|
|
38
38
|
|
|
39
|
-
let makeEvent' = (id, event) =>
|
|
39
|
+
let makeEvent' = (id, event): Reventless.Message.event'<string, ItemEventTopicSpec.event> => {
|
|
40
40
|
Reventless.Message.id,
|
|
41
41
|
meta: testMeta,
|
|
42
42
|
event,
|
|
43
|
-
}
|
|
43
|
+
}
|
|
44
44
|
|
|
45
45
|
let reset = () => {
|
|
46
46
|
capturedEventCount := 0
|
|
@@ -5,7 +5,7 @@ module ItemStreamSpec = {
|
|
|
5
5
|
let name = "StreamEvtItem"
|
|
6
6
|
|
|
7
7
|
@schema
|
|
8
|
-
type event =
|
|
8
|
+
type event = ItemPublished({name: string}) | ItemRemoved({id: string})
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
module StreamEvtBus = LocalBus.Make()
|
|
@@ -24,4 +24,3 @@ module StreamEvtTopicMaker = ReventlessCore.EventTopic_Builder.Make(
|
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
let evtTopic = StreamEvtTopicMaker.make(~name="StreamEvtTopic", ~storageResources=[])
|
|
27
|
-
|
|
@@ -17,7 +17,13 @@ describe("EventTopic.publishJsonStream", () => {
|
|
|
17
17
|
testPromise("streams 2 events to all subscribers", async () => {
|
|
18
18
|
let ops = await evtTopic->ReventlessCore.Component.operations->TestRunner.resolve
|
|
19
19
|
let stream = [
|
|
20
|
-
(
|
|
20
|
+
(
|
|
21
|
+
{
|
|
22
|
+
service: "svc",
|
|
23
|
+
meta: testMeta,
|
|
24
|
+
json: JSON.parseOrThrow("{\"ev\":1}"),
|
|
25
|
+
}: ReventlessInfra.EventTopic.publishJsonStreamItem
|
|
26
|
+
),
|
|
21
27
|
{service: "svc", meta: testMeta, json: JSON.parseOrThrow("{\"ev\":2}")},
|
|
22
28
|
]->Stream.fromIterable
|
|
23
29
|
let _ = await ops.publishJsonStream(stream)->Effect.runPromise
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
open TestFixtures
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
// ─────────────────────────────────────────────────────────────
|
|
8
7
|
// Isolated bus + Pulumi mock mode
|
|
9
8
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -20,13 +19,13 @@ module TestEPSpec = {
|
|
|
20
19
|
let name = "TestEP"
|
|
21
20
|
|
|
22
21
|
@schema
|
|
23
|
-
type command =
|
|
22
|
+
type command = Forward({targetId: string})
|
|
24
23
|
|
|
25
24
|
@schema
|
|
26
|
-
type event =
|
|
25
|
+
type event = TEPNoEvent // unused — mapOutgoingEvent = None
|
|
27
26
|
|
|
28
27
|
@schema
|
|
29
|
-
type directive =
|
|
28
|
+
type directive = TEPNoDirective // unused — mapIncomingCommand only uses PublishCommand
|
|
30
29
|
|
|
31
30
|
let moduleUrl: string = %raw(`import.meta.url`)
|
|
32
31
|
}
|
|
@@ -40,13 +39,13 @@ module DelegateAggSpec = {
|
|
|
40
39
|
let name = "TargetAgg"
|
|
41
40
|
|
|
42
41
|
@schema
|
|
43
|
-
type command =
|
|
42
|
+
type command = Execute({targetId: string})
|
|
44
43
|
|
|
45
44
|
@schema
|
|
46
|
-
type event =
|
|
45
|
+
type event = Executed({targetId: string}) // unused but required by Aggregate.Spec
|
|
47
46
|
|
|
48
47
|
@schema
|
|
49
|
-
type error =
|
|
48
|
+
type error = TEAggNoError // unused but required by Aggregate.Spec
|
|
50
49
|
|
|
51
50
|
let moduleUrl: string = %raw(`import.meta.url`)
|
|
52
51
|
}
|
|
@@ -65,7 +64,12 @@ module ForwardMapping = {
|
|
|
65
64
|
_id: string,
|
|
66
65
|
cmd: TestEPSpec.command,
|
|
67
66
|
_meta: Reventless.Message.meta,
|
|
68
|
-
): array<
|
|
67
|
+
): array<
|
|
68
|
+
ReventlessInfra.ExtensionPointMapping.commandAction<
|
|
69
|
+
DelegateAggSpec.command,
|
|
70
|
+
TestEPSpec.directive,
|
|
71
|
+
>,
|
|
72
|
+
> =>
|
|
69
73
|
switch cmd {
|
|
70
74
|
| Forward({targetId}) =>
|
|
71
75
|
let execCmd = DelegateAggSpec.Execute({targetId: targetId})
|
|
@@ -93,7 +97,8 @@ module TestEPMapping1 = ReventlessInfra.ExtensionPointMapping.Make(ForwardMappin
|
|
|
93
97
|
|
|
94
98
|
module TestEPMappings = {
|
|
95
99
|
module Spec = TestEPSpec
|
|
96
|
-
module type Mapping = ReventlessInfra.ExtensionPointMapping.T
|
|
100
|
+
module type Mapping = ReventlessInfra.ExtensionPointMapping.T
|
|
101
|
+
with module ExtensionPoint := TestEPSpec
|
|
97
102
|
let name = "TestEPMappings"
|
|
98
103
|
let moduleUrl: string = %raw(`import.meta.url`)
|
|
99
104
|
let mappings: array<module(Mapping)> = [module(TestEPMapping1)]
|
|
@@ -29,8 +29,9 @@ describe("ExtensionPoint (in-memory)", () => {
|
|
|
29
29
|
async () => {
|
|
30
30
|
// Build the full command body: {id, meta, command}
|
|
31
31
|
let forwardCmdJson =
|
|
32
|
-
TestEPSpec.Forward({targetId: "target-1"})
|
|
33
|
-
|
|
32
|
+
TestEPSpec.Forward({targetId: "target-1"})->Reventless.Util_Sury.toJson(
|
|
33
|
+
TestEPSpec.commandSchema,
|
|
34
|
+
)
|
|
34
35
|
let body = JSON.Encode.object(
|
|
35
36
|
Dict.fromArray([
|
|
36
37
|
("id", JSON.Encode.string("ep-id-1")),
|
|
@@ -38,11 +38,10 @@ let mockPublish: ReventlessInfra.CommandTopic.publishJsons = async cmds => {
|
|
|
38
38
|
let resolvedHandler: ref<option<(unit, unit) => promise<unit>>> = ref(None)
|
|
39
39
|
|
|
40
40
|
let _ = beforeAllAsync(async () => {
|
|
41
|
-
let h =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
)->TestRunner.resolve
|
|
41
|
+
let h = await HeartbeatMaker.makeHandler(
|
|
42
|
+
~id="hb-id-1",
|
|
43
|
+
~timeout=1,
|
|
44
|
+
~publishToPluginExtensionPoint=mockPublish,
|
|
45
|
+
)->TestRunner.resolve
|
|
47
46
|
resolvedHandler := Some(h)
|
|
48
47
|
})
|
|
@@ -12,56 +12,65 @@ describe("Heartbeat_Builder.Make:", () => {
|
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
describe("makeHandler + connect:", () => {
|
|
15
|
-
testPromise(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
15
|
+
testPromise(
|
|
16
|
+
"handler fires once after 1-minute interval",
|
|
17
|
+
async () => {
|
|
18
|
+
let handler = resolvedHandler.contents->Option.getUnsafe
|
|
19
|
+
let heartbeat = HeartbeatMaker.make(~name="hb-test-1")
|
|
20
|
+
let handlerDeferred: Deferred.t<LocalRuntimeEnvironment.handler, unit> =
|
|
21
|
+
Deferred.make()->Effect.runSync
|
|
22
|
+
Deferred.succeed(handlerDeferred, Obj.magic(handler))->Effect.runSync->ignore
|
|
23
|
+
let runtime: ReventlessCore.Runtime.environment<LocalHeartbeatRunner.runtimeParts> = {
|
|
24
|
+
parts: {
|
|
25
|
+
handlerDeferred,
|
|
26
|
+
subscriptionLatch: Effect.makeLatch(false)->Effect.runSync,
|
|
27
|
+
},
|
|
28
|
+
resources: [],
|
|
29
|
+
}
|
|
30
|
+
HeartbeatMaker.connect(~runtime, ~remoteChannel=Obj.magic(), ~timeout=1, heartbeat)
|
|
31
|
+
jest->advanceTimersByTime(1 * 60 * 1000)
|
|
32
|
+
// Give the Effect.runPromise microtask one tick to complete
|
|
33
|
+
let _ = await Promise.resolve()
|
|
34
|
+
expect(capturedCount.contents)->toBe(1)
|
|
35
|
+
LocalHeartbeatRunner.reset()
|
|
36
|
+
},
|
|
37
|
+
)
|
|
35
38
|
|
|
36
|
-
testPromise(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
39
|
+
testPromise(
|
|
40
|
+
"handler fires twice after two interval advances",
|
|
41
|
+
async () => {
|
|
42
|
+
let handler = resolvedHandler.contents->Option.getUnsafe
|
|
43
|
+
let heartbeat = HeartbeatMaker.make(~name="hb-test-2")
|
|
44
|
+
let handlerDeferred: Deferred.t<LocalRuntimeEnvironment.handler, unit> =
|
|
45
|
+
Deferred.make()->Effect.runSync
|
|
46
|
+
Deferred.succeed(handlerDeferred, Obj.magic(handler))->Effect.runSync->ignore
|
|
47
|
+
let runtime: ReventlessCore.Runtime.environment<LocalHeartbeatRunner.runtimeParts> = {
|
|
48
|
+
parts: {
|
|
49
|
+
handlerDeferred,
|
|
50
|
+
subscriptionLatch: Effect.makeLatch(false)->Effect.runSync,
|
|
51
|
+
},
|
|
52
|
+
resources: [],
|
|
53
|
+
}
|
|
54
|
+
HeartbeatMaker.connect(~runtime, ~remoteChannel=Obj.magic(), ~timeout=1, heartbeat)
|
|
55
|
+
jest->advanceTimersByTime(1 * 60 * 1000)
|
|
56
|
+
let _ = await Promise.resolve()
|
|
57
|
+
jest->advanceTimersByTime(1 * 60 * 1000)
|
|
58
|
+
let _ = await Promise.resolve()
|
|
59
|
+
expect(capturedCount.contents)->toBe(2)
|
|
60
|
+
LocalHeartbeatRunner.reset()
|
|
61
|
+
},
|
|
62
|
+
)
|
|
57
63
|
})
|
|
58
64
|
|
|
59
65
|
describe("make:", () => {
|
|
60
|
-
testPromise(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
testPromise(
|
|
67
|
+
"creates a heartbeat component without throwing",
|
|
68
|
+
async () => {
|
|
69
|
+
let heartbeat = HeartbeatMaker.make(~name="hb-make-smoke")
|
|
70
|
+
// Just verify component creation succeeds
|
|
71
|
+
let _ = heartbeat
|
|
72
|
+
expect(true)->toBe(true)
|
|
73
|
+
},
|
|
74
|
+
)
|
|
66
75
|
})
|
|
67
76
|
})
|