@reventlessdev/reventless-core 3.0.0-alpha.260 → 3.0.0-alpha.262
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 +24 -0
- package/package.json +10 -10
- package/src/Behavior.res +1 -4
- package/src/Message.res +17 -14
- package/src/Projection.res +51 -25
- package/src/RequestContext.res +8 -9
- package/src/ResourceAttribution.res +28 -14
- package/src/adapter/Adapter.res +48 -4
- package/src/adapter/EventLogProvisioning/EventLogProvisioning.res +5 -5
- package/src/adapter/Messaging/Messaging_Log_Backend.res +10 -7
- package/src/adapter/Messaging/Messaging_Log_Backend.res.mjs +2 -4
- package/src/adapter/Monitoring/Monitoring.res +16 -17
- package/src/adapter/QueryInterception/QueryInterception.res +0 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res +12 -5
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +11 -7
- package/src/adapter/Runtime/PluginRuntime_Builder_Micro.res +1 -7
- package/src/adapter/Runtime/PluginRuntime_Builder_Single.res +1 -7
- package/src/adapter/RuntimeExtension/RuntimeExtension.res +0 -1
- package/src/adapter/Secrets/Secrets_Node_Backend.res +60 -0
- package/src/adapter/Secrets/Secrets_Node_Backend.res.mjs +69 -0
- package/src/admin/Platform_Admin.res +17 -6
- package/src/admin/Platform_AdminApi.res +14 -9
- package/src/admin/Platform_Admin_Callback.res +1 -1
- package/src/admin/Platform_BakedManifest.res +34 -34
- package/src/admin/Platform_ComponentDefinitionsApi.res +9 -21
- package/src/admin/Platform_PluginStructuresApi.res +15 -12
- package/src/admin/UiFragmentRegistry/StateChange/UiFragmentRegistry.res +1 -1
- package/src/components/Aggregate/Aggregate.res +13 -12
- package/src/components/Aggregate/Aggregate_Builder.res +22 -21
- package/src/components/Aggregate/Aggregate_Callback.res +91 -86
- package/src/components/Api/ApiNoApiHelpers.res +1 -3
- package/src/components/Api/Api_Ids.res +0 -1
- package/src/components/Api/Api_Naming.res +41 -18
- package/src/components/Api/GraphQL_FragmentGenerator.res +38 -51
- package/src/components/Api/GraphQL_SchemaInspector.res +5 -3
- package/src/components/Api/GraphQL_Stitcher.res +9 -10
- package/src/components/Api/MCP_SchemaGenerator.res +8 -12
- package/src/components/Api/OwnerScopeDiagnostics.res +1 -2
- package/src/components/Api/QueryDbListQuery.res +13 -6
- package/src/components/Api/SchemaType.res +28 -23
- package/src/components/Api/SuryToJsonSchema.res +9 -10
- package/src/components/AutomationSlice/AutomationSlice.res +4 -4
- package/src/components/AutomationSlice/AutomationSlice_Builder.res +15 -14
- package/src/components/AutomationSlice/AutomationSlice_Callback.res +3 -3
- package/src/components/Builder_Helpers.res +12 -23
- package/src/components/CommandGenerator/CommandGenerator.res +6 -1
- package/src/components/CommandGenerator/CommandGenerator_Builder.res +29 -21
- package/src/components/CommandGenerator/CommandGenerator_Callback.res +17 -21
- package/src/components/CommandTopic/CommandTopic.res +3 -3
- package/src/components/CommandTopic/CommandTopic_Builder.res +10 -10
- package/src/components/CommandTopic/CommandTopic_Callback.res +1 -4
- package/src/components/CommandTopic/CommandTopic_Helpers.res +1 -2
- package/src/components/Counter/Counter_Builder.res +1 -6
- package/src/components/Counter/Counter_Callback.res +3 -3
- package/src/components/Dcb/Dcb_Builder.res +970 -956
- package/src/components/DcbEventLog/DcbEventLog.res +4 -4
- package/src/components/DcbEventLog/DcbEventLog_Builder.res +0 -1
- package/src/components/DcbEventLog/DcbEventLog_Operations.res +17 -18
- package/src/components/EventCollector/EventCollector_Builder.res +2 -1
- package/src/components/EventLog/EventLog.res +5 -1
- package/src/components/EventLog/EventLog_Operations.res +12 -8
- package/src/components/EventMapper/EventMapper.res +8 -10
- package/src/components/EventMapper/EventMapper_Builder.res +4 -1
- package/src/components/EventMapper/EventMapper_Callback.res +46 -31
- package/src/components/EventTopic/EventTopic.res +3 -3
- package/src/components/EventTopic/EventTopic_Builder.res +1 -1
- package/src/components/Extension/Extension.res +4 -1
- package/src/components/Extension/Extension_Operations.res +11 -3
- package/src/components/ExtensionPoint/ExtensionPoint.res +3 -3
- package/src/components/ExtensionPoint/ExtensionPoint_Builder.res +9 -10
- package/src/components/ExtensionPoint/ExtensionPoint_Callback.res +2 -6
- package/src/components/ExtensionPoint/ExtensionPoint_Operations.res +13 -5
- package/src/components/Heartbeat/Heartbeat_Callback.res +3 -3
- package/src/components/InboundTranslationSlice/InboundTranslationSlice.res +4 -4
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +7 -10
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice.res +4 -4
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +15 -14
- package/src/components/QueryDb/QueryDb_Adapter.res +1 -3
- package/src/components/QueryDb/QueryDb_Operations.res +10 -10
- package/src/components/ReadModel/ReadModel.res +3 -3
- package/src/components/ReadModel/ReadModel_Builder.res +1 -2
- package/src/components/ReadModel/ReadModel_Callback.res +2 -1
- package/src/components/SideEffectHandler/SideEffectHandler_Callback.res +5 -2
- package/src/components/StateChangeSlice/StateChangeSlice.res +3 -3
- package/src/components/StateChangeSlice/StateChangeSlice_Builder.res +23 -4
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +22 -6
- package/src/components/StateTopic.res +1 -3
- package/src/components/StateViewSlice/StateViewSlice.res +4 -4
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +79 -68
- package/src/components/StateViewSlice/StateViewSlice_Callback.res +3 -1
- package/src/components/Task/Task.res +3 -3
- package/src/components/Task/Task_Adapter.res +4 -1
- package/src/components/Task/Task_Builder.res +24 -21
- package/src/plugin/api/PluginBaseFragment.res +9 -3
- package/src/plugin/component/Capability_Inference.res +8 -10
- package/src/plugin/component/Plugin.res +6 -6
- package/src/plugin/component/Plugin_Builder.res +54 -50
- package/src/plugin/component/Plugin_Callback.res +10 -6
- package/src/plugin/component/Plugin_Helpers.res +264 -230
- package/src/plugin/component/Plugin_Structure.res +370 -324
- package/src/plugin/component/Plugin_SubscriptionSchema.res +8 -6
- package/src/plugin/component/SchemaWalker.res +11 -12
- package/src/plugin/connect/PluginConnectExtension_Mapping.res +45 -50
- package/src/plugin/lifecycle/PluginBehavior.res +3 -4
- package/src/plugin/lifecycle/PluginSpec.res +1 -2
- package/src/plugin/lifecycle/PluginsProjection.res +16 -5
- package/src/plugin/lifecycle/PluginsReadModelSpec.res +0 -2
- package/src/util/CommandPublisher.res +9 -2
- package/src/util/CsvStream.res +3 -3
- package/src/util/Interstack.res +7 -15
- package/src/util/Interstack.resi +3 -3
- package/src/util/LogFormat.res +5 -9
- package/src/util/Logger.res +1 -2
- package/src/util/Lru.res +2 -1
- package/src/util/Lru.resi +2 -3
- package/src/util/ScheduleOps.res +24 -27
- package/src/util/StorePrefixCollision.res +6 -9
- package/src/util/Util_Promise.res +4 -1
- package/src/util/Util_Promise.res.mjs +1 -1
- package/tests/IdentityTest.res +166 -113
- package/tests/ResourceAttributionTest.res +28 -25
- package/tests/Secrets_Node_BackendTest.res +141 -0
- package/tests/Secrets_Node_BackendTest.res.mjs +152 -0
- package/tests/StorePrefixCollisionTest.res +2 -1
- package/tests/TaskBucketNamingTest.res +3 -2
- package/tests/adapter/EventLogProvisioningTest.res +51 -48
- package/tests/adapter/MonitoringTest.res +81 -38
- package/tests/adapter/QueryInterceptionTest.res +2 -2
- package/tests/adapter/RefusalVocabularyTest.res +31 -23
- package/tests/adapter/RuntimeExtensionHookReachTest.res +9 -8
- package/tests/adapter/RuntimeExtensionTest.res +5 -3
- package/tests/admin/AdminApiSchemaDriftTest.res +22 -16
- package/tests/admin/Platform_Admin_StructureTest.res +22 -14
- package/tests/admin/Platform_Admin_StructureTest.res.mjs +4 -3
- package/tests/admin/Platform_BakedManifestTest.res +31 -35
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +18 -15
- package/tests/admin/Platform_PluginStructuresApiTest.res +5 -3
- package/tests/admin/Platform_UIFragmentsApiTest.res +6 -3
- package/tests/aggregate/AggregateCacheTest.res +10 -12
- package/tests/aggregate/AggregateCallbackTest.res +75 -46
- package/tests/aggregate/AggregateCausationTest.res +19 -20
- package/tests/aggregate/AggregateConflictTest.res +41 -19
- package/tests/aggregate/AggregateFixtures.res +15 -5
- package/tests/aggregate/AggregateRejectionTest.res +97 -80
- package/tests/aggregate/AggregateSnapshotTest.res +44 -16
- package/tests/api/Api_IdsTest.res +10 -4
- package/tests/api/BrandedMarkerCompositionTest.res +48 -36
- package/tests/api/GeolocationSemanticTest.res +3 -3
- package/tests/api/GraphQL_FragmentGeneratorTest.res +66 -54
- package/tests/api/OwnerScopeDiagnosticsTest.res +31 -27
- package/tests/api/StorageRefFieldsTest.res +30 -18
- package/tests/api/SuryToJsonSchemaTest.res +1315 -1014
- package/tests/api/TaggedUnionSchemaTest.res +17 -24
- package/tests/commandgenerator/CommandGeneratorCallbackTest.res +235 -179
- package/tests/commandgenerator/CommandGeneratorFixtures.res +8 -13
- package/tests/commandgenerator/OwnerStampingTest.res +91 -76
- package/tests/commandtopic/CommandOutcomeHookTest.res +198 -155
- package/tests/commandtopic/CommandTopicCallbackFixtures.res +5 -4
- package/tests/commandtopic/CommandTopicCallbackTest.res +61 -43
- package/tests/commandtopic/CommandTopicHelpersRejectionTest.res +70 -55
- package/tests/counter/CounterCallbackTest.res +40 -34
- package/tests/counter/CounterFixtures.res +4 -4
- package/tests/dcb/DcbDecodeTest.res +115 -84
- package/tests/dcb/DcbEventLogOperationsTest.res +194 -143
- package/tests/dcb/DcbEventLogSchemaTest.res +13 -13
- package/tests/dcb/DcbFixtures.res +4 -5
- package/tests/dcb/DcbScopeInferenceTest.res +173 -114
- package/tests/dcb/DcbStateChangeSliceTest.res +391 -262
- package/tests/dcb/DcbTagTest.res +22 -11
- package/tests/dcb/DcbValidationTest.res +288 -218
- package/tests/eventlog/EventLogFixtures.res +4 -4
- package/tests/eventlog/EventLogOperationsTest.res +102 -67
- package/tests/eventlog/EventLogRetryTest.res +104 -65
- package/tests/eventmapper/EventMapperCallbackTest.res +118 -83
- package/tests/eventmapper/EventMapperFixtures.res +15 -23
- package/tests/eventmapper/EventMapperTest.res +54 -38
- package/tests/eventtopic/EventTopicFixtures.res +3 -4
- package/tests/eventtopic/EventTopicOperationsTest.res +41 -29
- package/tests/extensionpoint/ExtensionPointCallbackTest.res +58 -45
- package/tests/extensionpoint/ExtensionPointFixtures.res +16 -20
- package/tests/extensionpoint/ExtensionPointOperationsTest.res +70 -49
- package/tests/extensionpoint/PluginDisconnectScheduleTest.res +2 -1
- package/tests/logger/LogFormatTest.res +104 -82
- package/tests/logger/LoggerLazyTest.res +21 -12
- package/tests/message/MessageTest.res +96 -93
- package/tests/message/MetaEnvelopeTest.res +26 -30
- package/tests/plugin/HeartbeatDisconnectGraceTest.res +31 -25
- package/tests/plugin/ManifestVisibilityTest.res +20 -12
- package/tests/plugin/PluginDefinitionScalars.res +1 -2
- package/tests/plugin/PluginLifecycleCorpusTest.res +15 -12
- package/tests/plugin/PluginSpecExperiment.res +0 -1
- package/tests/plugin/PluginStructureTest.res +1529 -1045
- package/tests/plugin/Plugin_EventQuerySchemaTest.res +21 -10
- package/tests/plugin/SchemaWalkerTest.res +116 -70
- package/tests/plugin/StateChange/PsReserveStock.res +4 -4
- package/tests/plugin/StateView/PsAnnotatedView.res +4 -4
- package/tests/plugin/StateView/PsCustomersView.res +4 -4
- package/tests/plugin/StateView/PsShipmentsView.res +3 -2
- package/tests/projection/LifecycleTrailTest.res +1 -4
- package/tests/projection/ProjectionOptimizeTest.res +13 -9
- package/tests/querydb/QueryDbFixtures.res +0 -1
- package/tests/querydb/QueryDbOperationsTest.res +99 -64
- package/tests/querydb/TaggedUnionStampTest.res +10 -8
- package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res +50 -35
- package/tests/sideeffecthandler/SideEffectHandlerFixtures.res +4 -10
- package/tests/util/CommandPublisherTest.res +0 -1
- package/tests/util/CsvStreamTest.res +3 -3
- package/tests/util/LruTest.res +84 -56
|
@@ -44,8 +44,7 @@ describe("the Plugins read model declares its lifecycle", () => {
|
|
|
44
44
|
})
|
|
45
45
|
|
|
46
46
|
testSync("is served on the encoded def under the new key", () => {
|
|
47
|
-
let json =
|
|
48
|
-
rm->Platform_ComponentDefinitionsApi.encodeQueryableDef->JSON.stringify
|
|
47
|
+
let json = rm->Platform_ComponentDefinitionsApi.encodeQueryableDef->JSON.stringify
|
|
49
48
|
expect(json->String.includes(`"lifecycleField":"status"`))->toEqual(true)
|
|
50
49
|
})
|
|
51
50
|
|
|
@@ -137,11 +136,7 @@ describe("the Plugin command defs carry every argument the SDL requires", () =>
|
|
|
137
136
|
Platform_Admin_Structure.pluginCommands
|
|
138
137
|
->Array.filter(c => c.apiExposed != Some(false))
|
|
139
138
|
->Array.map(c => c.mutationField),
|
|
140
|
-
)->toEqual([
|
|
141
|
-
"Platform_Plugin_Activate",
|
|
142
|
-
"Platform_Plugin_Deactivate",
|
|
143
|
-
"Platform_Plugin_Retire",
|
|
144
|
-
])
|
|
139
|
+
)->toEqual(["Platform_Plugin_Activate", "Platform_Plugin_Deactivate", "Platform_Plugin_Retire"])
|
|
145
140
|
)
|
|
146
141
|
|
|
147
142
|
// The `@noApi` protocol variants come back from the same walk. They belong on
|
|
@@ -179,16 +174,20 @@ describe("the internal commands declare the edges they move rows along", () => {
|
|
|
179
174
|
}
|
|
180
175
|
|
|
181
176
|
testSync("the only edge into Disconnected is declared", () =>
|
|
182
|
-
expect((
|
|
183
|
-
|
|
177
|
+
expect((
|
|
178
|
+
commandNamed("Disconnect").allowedStates,
|
|
179
|
+
commandNamed("Disconnect").targetState,
|
|
180
|
+
))->toEqual((Some(["Connected"]), Some("Disconnected")))
|
|
184
181
|
)
|
|
185
182
|
|
|
186
183
|
// The creating form's claim: a target and no from-set. `Connect` brings the
|
|
187
184
|
// version's row into being, so there is no state it runs from — and an empty
|
|
188
185
|
// `allowedStates` would say the opposite, that it is legal in none.
|
|
189
186
|
testSync("the handshake declares a target and no from-set", () =>
|
|
190
|
-
expect((commandNamed("Connect").allowedStates, commandNamed("Connect").targetState))
|
|
191
|
-
|
|
187
|
+
expect((commandNamed("Connect").allowedStates, commandNamed("Connect").targetState))->toEqual((
|
|
188
|
+
None,
|
|
189
|
+
Some("Connected"),
|
|
190
|
+
))
|
|
192
191
|
)
|
|
193
192
|
|
|
194
193
|
testSync("and no state is left unreachable", () =>
|
|
@@ -207,14 +206,23 @@ describe("the internal commands declare the edges they move rows along", () => {
|
|
|
207
206
|
// was open to everyone and the server refused anyone outside `Admin`. The
|
|
208
207
|
// mismatch is invisible until a non-admin clicks the page.
|
|
209
208
|
describe("the Plugins view publishes the rule the server enforces", () => {
|
|
210
|
-
|
|
209
|
+
// 🚨 **This is also the only check `Reventless.AdminGroup.name` can carry.** A
|
|
210
|
+
// PPX annotation takes a literal, so `@@reventless.authorize(AllowGroups(
|
|
211
|
+
// ["Admin"]))` above cannot reference the constant — the constant's job is to
|
|
212
|
+
// make everything *else* agree with the annotation, and nothing in the compiler
|
|
213
|
+
// spans that gap. The actual side here comes from the expanded annotation and
|
|
214
|
+
// the expected side from the constant, so the two are compared rather than
|
|
215
|
+
// restated. Writing the literal on both sides would pass while they diverged.
|
|
216
|
+
testSync("the spec declares the group, and it is the one the constant names", () =>
|
|
211
217
|
expect(PluginsReadModelSpec.authorization)->toEqual(
|
|
212
|
-
Reventless.Authorization.AllowGroups([
|
|
218
|
+
Reventless.Authorization.AllowGroups([Reventless.AdminGroup.name]),
|
|
213
219
|
)
|
|
214
220
|
)
|
|
215
221
|
|
|
216
222
|
testSync("and the published access keys are derived from it", () =>
|
|
217
|
-
expect(Platform_Admin_Structure.pluginReadModel.requiredAccess)->toEqual(
|
|
223
|
+
expect(Platform_Admin_Structure.pluginReadModel.requiredAccess)->toEqual(
|
|
224
|
+
Some([Reventless.AdminGroup.name]),
|
|
225
|
+
)
|
|
218
226
|
)
|
|
219
227
|
|
|
220
228
|
// The API entry reads the same binding rather than restating it, so the two
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
5
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
6
|
+
import * as AdminGroup$Reventless from "@reventlessdev/reventless-spec/src/types/AdminGroup.res.mjs";
|
|
6
7
|
import * as Plugin_Structure$ReventlessCore from "../../src/plugin/component/Plugin_Structure.res.mjs";
|
|
7
8
|
import * as PluginBaseFragment$ReventlessCore from "../../src/plugin/api/PluginBaseFragment.res.mjs";
|
|
8
9
|
import * as PluginsReadModelSpec$ReventlessCore from "../../src/plugin/lifecycle/PluginsReadModelSpec.res.mjs";
|
|
@@ -154,14 +155,14 @@ globalThis.describe("the internal commands declare the edges they move rows alon
|
|
|
154
155
|
});
|
|
155
156
|
|
|
156
157
|
globalThis.describe("the Plugins view publishes the rule the server enforces", () => {
|
|
157
|
-
globalThis.test("the spec declares the group", () => {
|
|
158
|
+
globalThis.test("the spec declares the group, and it is the one the constant names", () => {
|
|
158
159
|
globalThis.expect(PluginsReadModelSpec$ReventlessCore.authorization).toEqual({
|
|
159
160
|
TAG: "AllowGroups",
|
|
160
|
-
_0: [
|
|
161
|
+
_0: [AdminGroup$Reventless.name]
|
|
161
162
|
});
|
|
162
163
|
});
|
|
163
164
|
globalThis.test("and the published access keys are derived from it", () => {
|
|
164
|
-
globalThis.expect(Platform_Admin_Structure$ReventlessCore.pluginReadModel.requiredAccess).toEqual([
|
|
165
|
+
globalThis.expect(Platform_Admin_Structure$ReventlessCore.pluginReadModel.requiredAccess).toEqual([AdminGroup$Reventless.name]);
|
|
165
166
|
});
|
|
166
167
|
globalThis.test("the query entry carries the spec's rule and no hand-written pair", () => {
|
|
167
168
|
let entry = PluginBaseFragment$ReventlessCore.queryEntries[0];
|
|
@@ -91,8 +91,7 @@ let structure: pluginStructure = {
|
|
|
91
91
|
|
|
92
92
|
let structures = [("Ordering@1.0.0", structure)]
|
|
93
93
|
|
|
94
|
-
let bake = selections =>
|
|
95
|
-
Platform_BakedManifest.curate(~structures, ~selections)
|
|
94
|
+
let bake = selections => Platform_BakedManifest.curate(~structures, ~selections)
|
|
96
95
|
|
|
97
96
|
// Every case below varies one include-list and means "everything" by the rest,
|
|
98
97
|
// which is exactly what an omitted labelled argument says.
|
|
@@ -112,7 +111,10 @@ let names = (entry: JSON.t, field: string) =>
|
|
|
112
111
|
->Option.flatMap(JSON.Decode.array)
|
|
113
112
|
->Option.getOr([])
|
|
114
113
|
->Array.filterMap(v =>
|
|
115
|
-
v
|
|
114
|
+
v
|
|
115
|
+
->JSON.Decode.object
|
|
116
|
+
->Option.flatMap(d => d->Dict.get("name"))
|
|
117
|
+
->Option.flatMap(JSON.Decode.string)
|
|
116
118
|
)
|
|
117
119
|
|
|
118
120
|
let firstEntry = (r: result<JSON.t, Platform_BakedManifest.error>) =>
|
|
@@ -146,15 +148,13 @@ describe("curate", () => {
|
|
|
146
148
|
// from, so an Internal view a kept command points at has to survive the bake.
|
|
147
149
|
testSync("carries an Internal view referenced by an included command", () => {
|
|
148
150
|
let entry =
|
|
149
|
-
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["PlaceOrder"])])
|
|
150
|
-
->firstEntry
|
|
151
|
+
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["PlaceOrder"])])->firstEntry
|
|
151
152
|
expect(entry->names("internalQueryables"))->toEqual(["AvailableProducts"])
|
|
152
153
|
})
|
|
153
154
|
|
|
154
155
|
testSync("omits an Internal view no included command references", () => {
|
|
155
156
|
let entry =
|
|
156
|
-
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["ImportOrders"])])
|
|
157
|
-
->firstEntry
|
|
157
|
+
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["ImportOrders"])])->firstEntry
|
|
158
158
|
expect(entry->names("internalQueryables"))->toEqual([])
|
|
159
159
|
})
|
|
160
160
|
|
|
@@ -193,9 +193,9 @@ describe("curate", () => {
|
|
|
193
193
|
)
|
|
194
194
|
|
|
195
195
|
testSync("accepts a kind this plugin generates no page for", () =>
|
|
196
|
-
expect(
|
|
197
|
-
|
|
198
|
-
)
|
|
196
|
+
expect(bake([sel(~plugin="Ordering", ~derived=["scheduler"])])->firstEntry->derivedOf)->toEqual(
|
|
197
|
+
Some(["scheduler"]),
|
|
198
|
+
)
|
|
199
199
|
)
|
|
200
200
|
|
|
201
201
|
testSync("an Internal view is never selectable as a view", () =>
|
|
@@ -228,27 +228,26 @@ describe("curate", () => {
|
|
|
228
228
|
})
|
|
229
229
|
|
|
230
230
|
testSync("one entry per selection, in declaration order", () => {
|
|
231
|
-
let json =
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
~
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
->Array.filterMap(e =>
|
|
231
|
+
let json = Platform_BakedManifest.curate(
|
|
232
|
+
~structures=[("Ordering@1.0.0", structure), ("Catalog@1.0.0", structure)],
|
|
233
|
+
~selections=[
|
|
234
|
+
sel(~plugin="Catalog", ~views=[], ~commands=[]),
|
|
235
|
+
sel(~plugin="Ordering", ~views=[], ~commands=[]),
|
|
236
|
+
],
|
|
237
|
+
)
|
|
238
|
+
let ids = switch json {
|
|
239
|
+
| Ok(j) =>
|
|
240
|
+
j
|
|
241
|
+
->entries
|
|
242
|
+
->Array.filterMap(
|
|
243
|
+
e =>
|
|
245
244
|
e
|
|
246
245
|
->JSON.Decode.object
|
|
247
246
|
->Option.flatMap(d => d->Dict.get("pluginId"))
|
|
248
|
-
->Option.flatMap(JSON.Decode.string)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
247
|
+
->Option.flatMap(JSON.Decode.string),
|
|
248
|
+
)
|
|
249
|
+
| Error(_) => []
|
|
250
|
+
}
|
|
252
251
|
expect(ids)->toEqual(["Catalog", "Ordering"])
|
|
253
252
|
})
|
|
254
253
|
|
|
@@ -258,8 +257,7 @@ describe("curate", () => {
|
|
|
258
257
|
// makes this hold — the assertion guards that property, not a second code path.
|
|
259
258
|
testSync("a curated entry carries the owner field the served entry carries", () => {
|
|
260
259
|
let entry =
|
|
261
|
-
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["PlaceOrder"])])
|
|
262
|
-
->firstEntry
|
|
260
|
+
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["PlaceOrder"])])->firstEntry
|
|
263
261
|
let ownerOf = (key, name) =>
|
|
264
262
|
entry
|
|
265
263
|
->JSON.Decode.object
|
|
@@ -267,9 +265,7 @@ describe("curate", () => {
|
|
|
267
265
|
->Option.flatMap(JSON.Decode.array)
|
|
268
266
|
->Option.getOr([])
|
|
269
267
|
->Array.filterMap(JSON.Decode.object)
|
|
270
|
-
->Array.find(d =>
|
|
271
|
-
d->Dict.get("name")->Option.flatMap(JSON.Decode.string) == Some(name)
|
|
272
|
-
)
|
|
268
|
+
->Array.find(d => d->Dict.get("name")->Option.flatMap(JSON.Decode.string) == Some(name))
|
|
273
269
|
->Option.flatMap(d => d->Dict.get("ownerField"))
|
|
274
270
|
->Option.flatMap(JSON.Decode.string)
|
|
275
271
|
let commandOwner =
|
|
@@ -279,8 +275,8 @@ describe("curate", () => {
|
|
|
279
275
|
->Option.flatMap(JSON.Decode.array)
|
|
280
276
|
->Option.getOr([])
|
|
281
277
|
->Array.filterMap(JSON.Decode.object)
|
|
282
|
-
->Array.flatMap(
|
|
283
|
-
w->Dict.get("commands")->Option.flatMap(JSON.Decode.array)->Option.getOr([])
|
|
278
|
+
->Array.flatMap(
|
|
279
|
+
w => w->Dict.get("commands")->Option.flatMap(JSON.Decode.array)->Option.getOr([]),
|
|
284
280
|
)
|
|
285
281
|
->Array.filterMap(JSON.Decode.object)
|
|
286
282
|
->Array.get(0)
|
|
@@ -11,9 +11,7 @@ let cmd: commandDef = {
|
|
|
11
11
|
level: Collection,
|
|
12
12
|
aggregateIdField: None,
|
|
13
13
|
mutationField: "AddProduct",
|
|
14
|
-
references: [
|
|
15
|
-
{fieldName: "categoryId", entity: "Category", plugin: Some("Catalog")},
|
|
16
|
-
],
|
|
14
|
+
references: [{fieldName: "categoryId", entity: "Category", plugin: Some("Catalog")}],
|
|
17
15
|
allowedStates: None,
|
|
18
16
|
targetState: None,
|
|
19
17
|
apiExposed: Some(true),
|
|
@@ -276,10 +274,10 @@ describe("visibility filtering (deployed AutoUI hides Internal)", () => {
|
|
|
276
274
|
idField: Some("productId"),
|
|
277
275
|
idFieldSource: Some("sole"),
|
|
278
276
|
requiredAccess: None,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
277
|
+
ownerField: None,
|
|
278
|
+
retiredField: None,
|
|
279
|
+
retiredValues: None,
|
|
280
|
+
namedWhenRetired: None,
|
|
283
281
|
}
|
|
284
282
|
// A distinct name per source array: the complement has to be fed by both, and
|
|
285
283
|
// reusing one def would hide a version that only walks `readModels`.
|
|
@@ -293,8 +291,10 @@ describe("visibility filtering (deployed AutoUI hides Internal)", () => {
|
|
|
293
291
|
readModels: [qbl, internalQbl],
|
|
294
292
|
stateViewSlices: [internalSlice],
|
|
295
293
|
}
|
|
296
|
-
let mixedEntry =
|
|
297
|
-
|
|
294
|
+
let mixedEntry = Platform_ComponentDefinitionsApi.encodePluginStructureEntry(
|
|
295
|
+
~pluginId="Ordering",
|
|
296
|
+
mixed,
|
|
297
|
+
)
|
|
298
298
|
|
|
299
299
|
// Names under one key of the encoded entry. `String.includes` over the whole
|
|
300
300
|
// JSON can no longer answer this: an Internal view is legitimately present
|
|
@@ -308,7 +308,10 @@ describe("visibility filtering (deployed AutoUI hides Internal)", () => {
|
|
|
308
308
|
->Option.flatMap(JSON.Decode.array)
|
|
309
309
|
->Option.getOr([])
|
|
310
310
|
->Array.filterMap(c =>
|
|
311
|
-
c
|
|
311
|
+
c
|
|
312
|
+
->JSON.Decode.object
|
|
313
|
+
->Option.flatMap(o => o->Dict.get("name"))
|
|
314
|
+
->Option.flatMap(JSON.Decode.string)
|
|
312
315
|
)
|
|
313
316
|
|
|
314
317
|
testSync("excludes an Internal queryableDef from the encoded read-side", () => {
|
|
@@ -358,7 +361,7 @@ describe("allowedStates + lifecycleField populated", () => {
|
|
|
358
361
|
allowedStatesSource: "declared",
|
|
359
362
|
apiExposed: Some(true),
|
|
360
363
|
requiredAccess: None,
|
|
361
|
-
|
|
364
|
+
ownerField: None,
|
|
362
365
|
}
|
|
363
366
|
|
|
364
367
|
let qblWithStatus: queryableDef = {
|
|
@@ -377,10 +380,10 @@ describe("allowedStates + lifecycleField populated", () => {
|
|
|
377
380
|
idField: None,
|
|
378
381
|
idFieldSource: None,
|
|
379
382
|
requiredAccess: None,
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
383
|
+
ownerField: None,
|
|
384
|
+
retiredField: None,
|
|
385
|
+
retiredValues: None,
|
|
386
|
+
namedWhenRetired: None,
|
|
384
387
|
}
|
|
385
388
|
|
|
386
389
|
let wblWithStates: writableDef = {
|
|
@@ -84,7 +84,10 @@ let namesOf = (key): array<string> =>
|
|
|
84
84
|
->Option.flatMap(JSON.Decode.array)
|
|
85
85
|
->Option.getOr([])
|
|
86
86
|
->Array.filterMap(v =>
|
|
87
|
-
v
|
|
87
|
+
v
|
|
88
|
+
->JSON.Decode.object
|
|
89
|
+
->Option.flatMap(o => o->Dict.get("name"))
|
|
90
|
+
->Option.flatMap(JSON.Decode.string)
|
|
88
91
|
)
|
|
89
92
|
|
|
90
93
|
describe("Platform_PluginStructures entry", () => {
|
|
@@ -130,8 +133,7 @@ describe("Platform_PluginStructures entry", () => {
|
|
|
130
133
|
expect(
|
|
131
134
|
json->String.includes(
|
|
132
135
|
"\"extensionPoints\":[{\"name\":\"Catalog.Products\",\"delegateNames\":[\"onAdded\"],\"sourceEventTypes\":[\"Catalog.ProductAdded\"],\"commandTypes\":null," ++
|
|
133
|
-
|
|
134
|
-
"\"acceptedCommands\":[{\"name\":\"Catalog.Products.Reserve\",\"toCommandTypes\":[\"Catalog.ReserveStock\"]}]}]",
|
|
136
|
+
"\"publishedEvents\":[{\"name\":\"Catalog.Products.ProductBecameAvailable\",\"fromEventTypes\":[\"Catalog.ProductAdded\"]}]," ++ "\"acceptedCommands\":[{\"name\":\"Catalog.Products.Reserve\",\"toCommandTypes\":[\"Catalog.ReserveStock\"]}]}]",
|
|
135
137
|
),
|
|
136
138
|
)->toEqual(true)
|
|
137
139
|
)
|
|
@@ -57,7 +57,9 @@ describe("encodeUIFragmentEntry", () => {
|
|
|
57
57
|
|
|
58
58
|
testSync("encodes remoteEntryUrl", () =>
|
|
59
59
|
expect(
|
|
60
|
-
json->String.includes(
|
|
60
|
+
json->String.includes(
|
|
61
|
+
"\"remoteEntryUrl\":\"https://cdn.example.com/catalog@1.0/remoteEntry.js\"",
|
|
62
|
+
),
|
|
61
63
|
)->toEqual(true)
|
|
62
64
|
)
|
|
63
65
|
|
|
@@ -80,8 +82,9 @@ describe("encodeUIFragmentEntry", () => {
|
|
|
80
82
|
|
|
81
83
|
describe("Platform_UIFragmentsApi.sdl", () => {
|
|
82
84
|
testSync("query field declares the list type", () =>
|
|
83
|
-
expect(
|
|
84
|
-
|
|
85
|
+
expect(
|
|
86
|
+
Platform_UIFragmentsApi.sdlQueryField->String.includes("[Platform_UIFragmentEntry!]!"),
|
|
87
|
+
)->toEqual(true)
|
|
85
88
|
)
|
|
86
89
|
|
|
87
90
|
testSync("sdlTypes includes Platform_UIFragmentEntry", () => {
|
|
@@ -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 =
|
|
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: (
|
|
159
|
-
|
|
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
|
-
|
|
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(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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(
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
])
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
])
|
|
31
|
+
->TestHandler.handleCommands
|
|
32
|
+
->Effect.runPromise
|
|
33
|
+
let results = await Stream.fromIterable([
|
|
27
34
|
makeTopicItem("ref-2", AggSpec.Rename({newName: "Gadget"})),
|
|
28
|
-
])
|
|
29
|
-
|
|
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(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
])
|
|
40
|
-
|
|
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(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
])
|
|
51
|
-
|
|
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(
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
])
|
|
62
|
-
|
|
63
|
-
|
|
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(
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
])
|
|
74
|
-
|
|
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(
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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
|
})
|