@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.
- package/CHANGELOG.md +8 -0
- package/package.json +9 -9
- 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/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/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 +10 -11
- 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
|
@@ -52,13 +52,13 @@ describe("Plugin_EventQuerySchema.generate", () => {
|
|
|
52
52
|
),
|
|
53
53
|
)
|
|
54
54
|
expect(sdl->String.includes("type EventMeta {"))->toBe(true)
|
|
55
|
-
["user", "time", "service", "msgId", "correlationId", "causationId"]->Array.forEach(
|
|
56
|
-
expect(sdl->String.includes(f))->toBe(true)
|
|
55
|
+
["user", "time", "service", "msgId", "correlationId", "causationId"]->Array.forEach(
|
|
56
|
+
f => expect(sdl->String.includes(f))->toBe(true),
|
|
57
57
|
)
|
|
58
58
|
// Transport / tracing detail and the cross-cutting context bag must not
|
|
59
59
|
// leave the server — see the module comment.
|
|
60
|
-
["ip:", "traceparent", "schemaVersion", "headers"]->Array.forEach(
|
|
61
|
-
expect(sdl->String.includes(f))->toBe(false)
|
|
60
|
+
["ip:", "traceparent", "schemaVersion", "headers"]->Array.forEach(
|
|
61
|
+
f => expect(sdl->String.includes(f))->toBe(false),
|
|
62
62
|
)
|
|
63
63
|
})
|
|
64
64
|
|
|
@@ -70,9 +70,14 @@ describe("Plugin_EventQuerySchema.generate", () => {
|
|
|
70
70
|
),
|
|
71
71
|
)
|
|
72
72
|
expect(sdl->String.includes("type Ordering_OrderEventRecord {"))->toBe(true)
|
|
73
|
-
[
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
[
|
|
74
|
+
"position: String!",
|
|
75
|
+
"eventType: String!",
|
|
76
|
+
"payload: AWSJSON!",
|
|
77
|
+
"tags: [EventTag!]!",
|
|
78
|
+
"meta: EventMeta!",
|
|
79
|
+
"recordedAt: String!",
|
|
80
|
+
]->Array.forEach(f => expect(sdl->String.includes(f))->toBe(true))
|
|
76
81
|
})
|
|
77
82
|
|
|
78
83
|
testSync("filter offers the entity shortcut and the precise tag pair", () => {
|
|
@@ -83,9 +88,15 @@ describe("Plugin_EventQuerySchema.generate", () => {
|
|
|
83
88
|
),
|
|
84
89
|
)
|
|
85
90
|
expect(sdl->String.includes("input Ordering_OrderEventHistoryFilter {"))->toBe(true)
|
|
86
|
-
[
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
[
|
|
92
|
+
"entityId: ID",
|
|
93
|
+
"tagKey: String",
|
|
94
|
+
"tagValue: String",
|
|
95
|
+
"eventTypes: [String!]",
|
|
96
|
+
"user: String",
|
|
97
|
+
"timeFrom: String",
|
|
98
|
+
"timeTo: String",
|
|
99
|
+
]->Array.forEach(f => expect(sdl->String.includes(f))->toBe(true))
|
|
89
100
|
})
|
|
90
101
|
|
|
91
102
|
testSync("a plugin with no event logs emits nothing at all", () => {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
open JestGlobals
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
@schema
|
|
5
4
|
type simpleRecord = {
|
|
6
5
|
label: string,
|
|
@@ -21,28 +20,41 @@ describe("SchemaWalker:", () => {
|
|
|
21
20
|
testSync("kind is record", () => expect(result.kind)->toBe("record"))
|
|
22
21
|
testSync("constructors is absent", () => expect(result.constructors)->toEqual(None))
|
|
23
22
|
|
|
24
|
-
testSync(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
testSync(
|
|
24
|
+
"fields are sorted alphabetically",
|
|
25
|
+
() => {
|
|
26
|
+
let names = result.fields->Array.map(f => f.name)
|
|
27
|
+
expect(names)->toEqual(["count", "label", "rating"])
|
|
28
|
+
},
|
|
29
|
+
)
|
|
28
30
|
|
|
29
|
-
testSync(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
testSync(
|
|
32
|
+
"required string field",
|
|
33
|
+
() => {
|
|
34
|
+
let f = result.fields->Array.getUnsafe(1)
|
|
35
|
+
expect((f.typeDescription, f.isRequired))->toEqual(("string", true))
|
|
36
|
+
},
|
|
37
|
+
)
|
|
33
38
|
|
|
34
|
-
testSync(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
testSync(
|
|
40
|
+
"required int field typeDescription is number",
|
|
41
|
+
() => {
|
|
42
|
+
let f = result.fields->Array.getUnsafe(0)
|
|
43
|
+
expect((f.typeDescription, f.isRequired))->toEqual(("number", true))
|
|
44
|
+
},
|
|
45
|
+
)
|
|
38
46
|
|
|
39
|
-
testSync(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
testSync(
|
|
48
|
+
"optional float field",
|
|
49
|
+
() => {
|
|
50
|
+
let f = result.fields->Array.getUnsafe(2)
|
|
51
|
+
expect((f.typeDescription, f.isRequired))->toEqual(("option<number>", false))
|
|
52
|
+
},
|
|
53
|
+
)
|
|
43
54
|
|
|
44
|
-
testSync(
|
|
45
|
-
|
|
55
|
+
testSync(
|
|
56
|
+
"structuralHash is non-empty",
|
|
57
|
+
() => expect(result.structuralHash->String.length > 0)->toBe(true),
|
|
46
58
|
)
|
|
47
59
|
})
|
|
48
60
|
|
|
@@ -52,63 +64,97 @@ describe("SchemaWalker:", () => {
|
|
|
52
64
|
testSync("kind is variant", () => expect(result.kind)->toBe("variant"))
|
|
53
65
|
testSync("fields array is empty", () => expect(result.fields)->toEqual([]))
|
|
54
66
|
|
|
55
|
-
testSync(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
testSync(
|
|
68
|
+
"payload-less constructor Removed is excluded",
|
|
69
|
+
() => {
|
|
70
|
+
let ctors = result.constructors->Option.getOr([])
|
|
71
|
+
let names = ctors->Array.map(c => c.name)->Array.toSorted(String.compare)
|
|
72
|
+
expect(names)->toEqual(["Added", "Renamed"])
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
testSync(
|
|
77
|
+
"Added constructor has sorted fields",
|
|
78
|
+
() => {
|
|
79
|
+
let ctors = result.constructors->Option.getOr([])
|
|
80
|
+
let added =
|
|
81
|
+
ctors
|
|
82
|
+
->Array.find(c => c.name == "Added")
|
|
83
|
+
->Option.getOr({Plugin_BuiltHook.name: "", fields: []})
|
|
84
|
+
expect(added.fields->Array.map(f => f.name))->toEqual(["id", "name"])
|
|
85
|
+
},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
testSync(
|
|
89
|
+
"Renamed constructor has sorted fields",
|
|
90
|
+
() => {
|
|
91
|
+
let ctors = result.constructors->Option.getOr([])
|
|
92
|
+
let renamed =
|
|
93
|
+
ctors
|
|
94
|
+
->Array.find(c => c.name == "Renamed")
|
|
95
|
+
->Option.getOr({Plugin_BuiltHook.name: "", fields: []})
|
|
96
|
+
expect(renamed.fields->Array.map(f => f.name))->toEqual(["id", "newName"])
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
testSync(
|
|
101
|
+
"structuralHash is non-empty",
|
|
102
|
+
() => expect(result.structuralHash->String.length > 0)->toBe(true),
|
|
75
103
|
)
|
|
76
104
|
})
|
|
77
105
|
|
|
78
106
|
describe("hash properties", () => {
|
|
79
|
-
testSync(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
testSync(
|
|
108
|
+
"same schema produces identical hash on repeated calls",
|
|
109
|
+
() => {
|
|
110
|
+
let h1 = SchemaWalker.walk("X", simpleRecordSchema).structuralHash
|
|
111
|
+
let h2 = SchemaWalker.walk("X", simpleRecordSchema).structuralHash
|
|
112
|
+
expect(h1)->toBe(h2)
|
|
113
|
+
},
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
testSync(
|
|
117
|
+
"typeName does not affect structuralHash",
|
|
118
|
+
() => {
|
|
119
|
+
let h1 = SchemaWalker.walk("TypeA", simpleRecordSchema).structuralHash
|
|
120
|
+
let h2 = SchemaWalker.walk("TypeB", simpleRecordSchema).structuralHash
|
|
121
|
+
expect(h1)->toBe(h2)
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
testSync(
|
|
126
|
+
"structurally distinct schemas produce different hashes",
|
|
127
|
+
() => {
|
|
128
|
+
let h1 = SchemaWalker.walk("R", simpleRecordSchema).structuralHash
|
|
129
|
+
let h2 = SchemaWalker.walk("V", simpleVariantSchema).structuralHash
|
|
130
|
+
expect(h1 == h2)->toBe(false)
|
|
131
|
+
},
|
|
132
|
+
)
|
|
96
133
|
})
|
|
97
134
|
|
|
98
135
|
describe("scalar / unsupported schema", () => {
|
|
99
|
-
testSync(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
136
|
+
testSync(
|
|
137
|
+
"plain string schema → kind unknown",
|
|
138
|
+
() => {
|
|
139
|
+
let result = SchemaWalker.walk("Scalar", S.string)
|
|
140
|
+
expect(result.kind)->toBe("unknown")
|
|
141
|
+
},
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
testSync(
|
|
145
|
+
"plain string schema → empty fields",
|
|
146
|
+
() => {
|
|
147
|
+
let result = SchemaWalker.walk("Scalar", S.string)
|
|
148
|
+
expect(result.fields)->toEqual([])
|
|
149
|
+
},
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
testSync(
|
|
153
|
+
"plain string schema → empty structuralHash",
|
|
154
|
+
() => {
|
|
155
|
+
let result = SchemaWalker.walk("Scalar", S.string)
|
|
156
|
+
expect(result.structuralHash)->toBe("")
|
|
157
|
+
},
|
|
158
|
+
)
|
|
113
159
|
})
|
|
114
160
|
})
|
|
@@ -49,9 +49,9 @@ type error = OutOfStock
|
|
|
49
49
|
|
|
50
50
|
@schema
|
|
51
51
|
type event =
|
|
52
|
-
StockReserved({
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
| StockReserved({
|
|
53
|
+
@partitionTag reservationId: string,
|
|
54
|
+
@ref("AvailableProducts") productIds: array<string>,
|
|
55
|
+
})
|
|
56
56
|
|
|
57
57
|
let decide = (_state, _command): result<array<event>, error> => Ok([])
|
|
@@ -19,8 +19,7 @@ type consumedEvent =
|
|
|
19
19
|
@default(1) quantity: int,
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
@live(false)
|
|
23
|
-
@schema
|
|
22
|
+
@live(false) @schema
|
|
24
23
|
type state = {
|
|
25
24
|
@id itemId: string,
|
|
26
25
|
@subId version: string,
|
|
@@ -33,6 +32,7 @@ type state = {
|
|
|
33
32
|
|
|
34
33
|
let project = ({event}: Reventless.StateViewSlice.consumed<consumedEvent>) =>
|
|
35
34
|
switch event {
|
|
36
|
-
| ItemRecorded({itemId, ownerId, version, name, total, contact, quantity}) =>
|
|
37
|
-
|
|
35
|
+
| ItemRecorded({itemId, ownerId, version, name, total, contact, quantity}) => [
|
|
36
|
+
Set(itemId, {itemId, ownerId, version, name, total, contact, quantity}),
|
|
37
|
+
]
|
|
38
38
|
}
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
@@reventless.spec("Customers")
|
|
6
6
|
|
|
7
7
|
@schema
|
|
8
|
-
type consumedEvent =
|
|
9
|
-
| CustomerAdded({customerId: string, firstName: string, lastName: string})
|
|
8
|
+
type consumedEvent = CustomerAdded({customerId: string, firstName: string, lastName: string})
|
|
10
9
|
|
|
11
10
|
@schema
|
|
12
11
|
type state = {
|
|
@@ -17,6 +16,7 @@ type state = {
|
|
|
17
16
|
|
|
18
17
|
let project = ({event}: Reventless.StateViewSlice.consumed<consumedEvent>) =>
|
|
19
18
|
switch event {
|
|
20
|
-
| CustomerAdded({customerId, firstName, lastName}) =>
|
|
21
|
-
|
|
19
|
+
| CustomerAdded({customerId, firstName, lastName}) => [
|
|
20
|
+
Set(customerId, {customerId, firstName, lastName}),
|
|
21
|
+
]
|
|
22
22
|
}
|
|
@@ -21,6 +21,7 @@ type state = {shipmentId: string, @lifecycle shipmentStatus: shipmentStatus}
|
|
|
21
21
|
let project = ({event}: Reventless.StateViewSlice.consumed<consumedEvent>) =>
|
|
22
22
|
switch event {
|
|
23
23
|
| ShipmentBooked({shipmentId}) => [Set(shipmentId, {shipmentId, shipmentStatus: Booked})]
|
|
24
|
-
| ShipmentDispatched({shipmentId}) =>
|
|
25
|
-
|
|
24
|
+
| ShipmentDispatched({shipmentId}) => [
|
|
25
|
+
Update(shipmentId, s => {...s, shipmentStatus: Dispatched}),
|
|
26
|
+
]
|
|
26
27
|
}
|
|
@@ -87,10 +87,7 @@ describe("the lifecycle trail:", () => {
|
|
|
87
87
|
// Derived from the log and the envelope's own time, so the same events in the
|
|
88
88
|
// same order produce the same trail however often they are replayed.
|
|
89
89
|
testSync("a rebuild from the same events produces an identical trail", () => {
|
|
90
|
-
let events = [
|
|
91
|
-
(nine, Set("o1", placed)),
|
|
92
|
-
(ten, Update("o1", s => {...s, lifecycle: Shipped})),
|
|
93
|
-
]
|
|
90
|
+
let events = [(nine, Set("o1", placed)), (ten, Update("o1", s => {...s, lifecycle: Shipped}))]
|
|
94
91
|
expect(run(events)->statesOf)->toEqual(run(events)->statesOf)
|
|
95
92
|
})
|
|
96
93
|
|
|
@@ -14,25 +14,29 @@ describe("Projection.optimizeActions", () => {
|
|
|
14
14
|
let optimized = Projection.optimizeActions([Delete("a"), Update("a", x => x + 1), Set("a", 99)])
|
|
15
15
|
// Was 3 (with a duplicated Update("a")) before the fix.
|
|
16
16
|
expect(optimized->Array.length)->toBe(2)
|
|
17
|
-
let sets =
|
|
18
|
-
|
|
17
|
+
let sets = optimized->Array.filterMap(
|
|
18
|
+
a =>
|
|
19
19
|
switch a {
|
|
20
20
|
| Set(id, s) => Some((id, s))
|
|
21
21
|
| _ => None
|
|
22
|
-
}
|
|
23
|
-
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
24
|
expect(sets)->toEqual([("a", 99)])
|
|
25
25
|
})
|
|
26
26
|
|
|
27
27
|
testSync("merges a same-id Create+Update chain into a single Create", () => {
|
|
28
|
-
let optimized = Projection.optimizeActions([
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
let optimized = Projection.optimizeActions([
|
|
29
|
+
Create("a", 0),
|
|
30
|
+
Update("a", x => x + 1),
|
|
31
|
+
Update("a", x => x + 10),
|
|
32
|
+
])
|
|
33
|
+
let creates = optimized->Array.filterMap(
|
|
34
|
+
a =>
|
|
31
35
|
switch a {
|
|
32
36
|
| Create(id, s) => Some((id, s))
|
|
33
37
|
| _ => None
|
|
34
|
-
}
|
|
35
|
-
|
|
38
|
+
},
|
|
39
|
+
)
|
|
36
40
|
expect(creates)->toEqual([("a", 11)])
|
|
37
41
|
})
|
|
38
42
|
})
|
|
@@ -12,83 +12,118 @@ let _ = beforeEach(() => reset())
|
|
|
12
12
|
|
|
13
13
|
describe("QueryDb_Operations:", () => {
|
|
14
14
|
describe("loadStream", () => {
|
|
15
|
-
testPromise(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
testPromise(
|
|
16
|
+
"returns empty stream for unknown id",
|
|
17
|
+
async () => {
|
|
18
|
+
let result = await Ops.loadStream("unknown-id")->Stream.runCollect->Effect.runPromise
|
|
19
|
+
expect(result)->toEqual([])
|
|
20
|
+
},
|
|
21
|
+
)
|
|
19
22
|
|
|
20
|
-
testPromise(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
testPromise(
|
|
24
|
+
"decodes saved state back to typed state",
|
|
25
|
+
async () => {
|
|
26
|
+
let state: ItemQueryDbSpec.state = {name: "Widget", count: 5}
|
|
27
|
+
let _ = await Ops.save("item-1", state, Init, None)
|
|
28
|
+
let result = await Ops.loadStream("item-1")->Stream.runCollect->Effect.runPromise
|
|
29
|
+
switch result {
|
|
30
|
+
| [s] => expect((s.name, s.count))->toEqual(("Widget", 5))
|
|
31
|
+
| _ => expect("unexpected result")->toEqual("Ok([state])")
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
)
|
|
29
35
|
|
|
30
|
-
testPromise(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
testPromise(
|
|
37
|
+
"returns updated state after overwrite",
|
|
38
|
+
async () => {
|
|
39
|
+
let _ = await Ops.save("item-1", {name: "Widget", count: 1}, Init, None)
|
|
40
|
+
let _ = await Ops.save("item-1", {name: "Widget Updated", count: 2}, Overwrite, None)
|
|
41
|
+
let result = await Ops.loadStream("item-1")->Stream.runCollect->Effect.runPromise
|
|
42
|
+
switch result {
|
|
43
|
+
| [s] => expect(s.name)->toBe("Widget Updated")
|
|
44
|
+
| _ => expect("unexpected result")->toEqual("Ok([state])")
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
)
|
|
39
48
|
})
|
|
40
49
|
|
|
41
50
|
describe("save", () => {
|
|
42
|
-
testPromise(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
testPromise(
|
|
52
|
+
"returns Ok on success",
|
|
53
|
+
async () => {
|
|
54
|
+
let result = await Ops.save("item-1", {name: "Widget", count: 1}, Init, None)
|
|
55
|
+
expect(Result.isOk(result))->toBe(true)
|
|
56
|
+
},
|
|
57
|
+
)
|
|
46
58
|
|
|
47
|
-
testPromise(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
json
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
testPromise(
|
|
60
|
+
"stores id field in JSON",
|
|
61
|
+
async () => {
|
|
62
|
+
let _ = await Ops.save("item-42", {name: "Widget", count: 0}, Init, None)
|
|
63
|
+
let stored = store.contents->Dict.get("item-42")->Option.getOr([])
|
|
64
|
+
expect(stored->Array.length)->toBe(1)
|
|
65
|
+
let json = stored->Array.getUnsafe(0)
|
|
66
|
+
let idField =
|
|
67
|
+
json
|
|
68
|
+
->JSON.Decode.object
|
|
69
|
+
->Option.flatMap(d => d->Dict.get("id"))
|
|
70
|
+
->Option.flatMap(
|
|
71
|
+
j =>
|
|
72
|
+
switch j {
|
|
73
|
+
| JSON.String(s) => Some(s)
|
|
74
|
+
| _ => None
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
expect(idField)->toEqual(Some("item-42"))
|
|
78
|
+
},
|
|
79
|
+
)
|
|
59
80
|
|
|
60
|
-
testPromise(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
81
|
+
testPromise(
|
|
82
|
+
"returns Error when storage fails",
|
|
83
|
+
async () => {
|
|
84
|
+
failNextWrite := true
|
|
85
|
+
let result = await Ops.save("item-1", {name: "Widget", count: 1}, Init, None)
|
|
86
|
+
expect(Result.isError(result))->toBe(true)
|
|
87
|
+
},
|
|
88
|
+
)
|
|
65
89
|
})
|
|
66
90
|
|
|
67
91
|
describe("saveBatch", () => {
|
|
68
|
-
testPromise(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
testPromise(
|
|
93
|
+
"saves multiple states",
|
|
94
|
+
async () => {
|
|
95
|
+
let batch = [
|
|
96
|
+
("item-1", ({name: "A", count: 1}: ItemQueryDbSpec.state), None),
|
|
97
|
+
("item-2", ({name: "B", count: 2}: ItemQueryDbSpec.state), None),
|
|
98
|
+
]
|
|
99
|
+
let _ = await Ops.saveBatch(batch)
|
|
100
|
+
let r1 = await Ops.loadStream("item-1")->Stream.runCollect->Effect.runPromise
|
|
101
|
+
let r2 = await Ops.loadStream("item-2")->Stream.runCollect->Effect.runPromise
|
|
102
|
+
expect((r1->Array.length > 0, r2->Array.length > 0))->toEqual((true, true))
|
|
103
|
+
},
|
|
104
|
+
)
|
|
78
105
|
|
|
79
|
-
testPromise(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
106
|
+
testPromise(
|
|
107
|
+
"returns Error when storage fails",
|
|
108
|
+
async () => {
|
|
109
|
+
failNextWrite := true
|
|
110
|
+
let result = await Ops.saveBatch([
|
|
111
|
+
("item-1", ({name: "A", count: 1}: ItemQueryDbSpec.state), None),
|
|
112
|
+
])
|
|
113
|
+
expect(Result.isError(result))->toBe(true)
|
|
114
|
+
},
|
|
115
|
+
)
|
|
84
116
|
})
|
|
85
117
|
|
|
86
118
|
describe("delete", () => {
|
|
87
|
-
testPromise(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
119
|
+
testPromise(
|
|
120
|
+
"removes state from storage",
|
|
121
|
+
async () => {
|
|
122
|
+
let _ = await Ops.save("item-1", {name: "Widget", count: 1}, Init, None)
|
|
123
|
+
let _ = await Ops.delete("item-1", None)
|
|
124
|
+
let result = await Ops.loadStream("item-1")->Stream.runCollect->Effect.runPromise
|
|
125
|
+
expect(result)->toEqual([])
|
|
126
|
+
},
|
|
127
|
+
)
|
|
93
128
|
})
|
|
94
129
|
})
|
|
@@ -81,9 +81,7 @@ describe("QueryDb_Operations stamps union members", () => {
|
|
|
81
81
|
}
|
|
82
82
|
let _ = await Sightings.save("s-1", state, Init, None)
|
|
83
83
|
let row = storedRow("s-1")
|
|
84
|
-
expect(row->field("lastSeen")->field("__typename")->string)->toEqual(
|
|
85
|
-
Some("GeolocationLocated"),
|
|
86
|
-
)
|
|
84
|
+
expect(row->field("lastSeen")->field("__typename")->string)->toEqual(Some("GeolocationLocated"))
|
|
87
85
|
let history = row->field("history")->JSON.Decode.array->Option.getOr([])
|
|
88
86
|
expect(history->Array.map(h => h->field("__typename")->string))->toEqual([
|
|
89
87
|
Some("GeolocationPending"),
|
|
@@ -105,16 +103,19 @@ describe("QueryDb_Operations stamps union members", () => {
|
|
|
105
103
|
// is walked and written exactly as it was written before the walk existed.
|
|
106
104
|
testPromise("a view with no union field stores what it always stored", async () => {
|
|
107
105
|
let _ = await Items.save("item-1", {name: "Widget", count: 5}, Init, None)
|
|
108
|
-
expect(storedRow("item-1")->JSON.stringify)->toBe(
|
|
109
|
-
`{"name":"Widget","count":5,"id":"item-1"}`,
|
|
110
|
-
)
|
|
106
|
+
expect(storedRow("item-1")->JSON.stringify)->toBe(`{"name":"Widget","count":5,"id":"item-1"}`)
|
|
111
107
|
})
|
|
112
108
|
|
|
113
109
|
testPromise("saveBatch stamps every row", async () => {
|
|
114
110
|
let batch = [
|
|
115
111
|
(
|
|
116
112
|
"c-4",
|
|
117
|
-
(
|
|
113
|
+
(
|
|
114
|
+
{
|
|
115
|
+
customerId: "c-4",
|
|
116
|
+
geolocation: Located({point: {lat: 0.0, lng: 0.0}}),
|
|
117
|
+
}: CustomersSpec.state
|
|
118
|
+
),
|
|
118
119
|
None,
|
|
119
120
|
),
|
|
120
121
|
(
|
|
@@ -141,7 +142,8 @@ describe("a stamped row decodes back", () => {
|
|
|
141
142
|
)
|
|
142
143
|
let result = await Customers.loadStream("c-6")->Stream.runCollect->Effect.runPromise
|
|
143
144
|
switch result {
|
|
144
|
-
| [{geolocation: Located({point})}] =>
|
|
145
|
+
| [{geolocation: Located({point})}] =>
|
|
146
|
+
expect((point.lat, point.lng))->toEqual((48.2082, 16.3738))
|
|
145
147
|
| _ => expect("round trip")->toBe("Located with its point intact")
|
|
146
148
|
}
|
|
147
149
|
})
|