@reventlessdev/reventless-core 3.0.0-alpha.179 → 3.0.0-alpha.181
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 +21 -0
- package/package.json +8 -8
- package/src/Message.res +2 -1
- package/src/Message.res.mjs +5 -1
- package/src/adapter/Monitoring/Monitoring.res +14 -2
- package/src/adapter/Monitoring/Monitoring.res.mjs +4 -2
- package/src/admin/Platform_Admin_Structure.res +2 -0
- package/src/admin/Platform_Admin_Structure.res.mjs +2 -0
- package/src/admin/Platform_ComponentDefinitionsApi.res +2 -1
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +5 -1
- package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res +1 -1
- package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res.mjs +2 -1
- package/src/components/Api/ApiTargetStateHelpers.res +20 -0
- package/src/components/Api/ApiTargetStateHelpers.res.mjs +16 -0
- package/src/components/Api/GraphQL_FragmentGenerator.res +1 -0
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +1 -0
- package/src/components/Api/SchemaType.res +3 -0
- package/src/components/Api/SchemaType.res.mjs +10 -2
- package/src/components/Api/SuryToJsonSchema.res +16 -0
- package/src/components/Api/SuryToJsonSchema.res.mjs +28 -0
- package/src/components/Dcb/Dcb_Builder.res +4 -5
- package/src/components/Dcb/Dcb_Builder.res.mjs +3 -5
- package/src/components/DcbEventLog/DcbEventLog_Operations.res +6 -1
- package/src/components/DcbEventLog/DcbEventLog_Operations.res.mjs +2 -1
- package/src/components/InboundTranslationSlice/InboundTranslationSlice.res +25 -0
- package/src/components/InboundTranslationSlice/InboundTranslationSlice.res.mjs +31 -0
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res +8 -8
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs +26 -6
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +44 -37
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res.mjs +17 -25
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +27 -2
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +4 -0
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +20 -6
- package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +23 -5
- package/src/components/StateViewSlice/StateViewSlice_Callback.res +1 -1
- package/src/components/StateViewSlice/StateViewSlice_Callback.res.mjs +5 -1
- package/src/plugin/component/Plugin_Helpers.res +4 -1
- package/src/plugin/component/Plugin_Structure.res +15 -1
- package/src/plugin/component/Plugin_Structure.res.mjs +4 -1
- package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +2 -0
- package/tests/IdentityTest.res.mjs +174 -0
- package/tests/ResourceAttributionTest.res.mjs +75 -0
- package/tests/adapter/MonitoringTest.res +32 -2
- package/tests/adapter/MonitoringTest.res.mjs +116 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +10 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +365 -0
- package/tests/admin/Platform_UIFragmentsApiTest.res.mjs +101 -0
- package/tests/aggregate/AggregateCacheTest.res.mjs +433 -0
- package/tests/aggregate/AggregateCallbackTest.res.mjs +143 -0
- package/tests/aggregate/AggregateCausationTest.res.mjs +47 -0
- package/tests/aggregate/AggregateConflictTest.res.mjs +386 -0
- package/tests/aggregate/AggregateFixtures.res.mjs +308 -0
- package/tests/aggregate/AggregateRejectionTest.res.mjs +166 -0
- package/tests/aggregate/AggregateSnapshotTest.res.mjs +645 -0
- package/tests/api/ApiNoApiTest.res.mjs +90 -0
- package/tests/api/GraphQL_StitcherTest.res.mjs +112 -0
- package/tests/api/SimpleTest.res.mjs +10 -0
- package/tests/api/SuryToJsonSchemaTest.res +68 -0
- package/tests/api/SuryToJsonSchemaTest.res.mjs +843 -0
- package/tests/commandgenerator/CommandGeneratorCallbackTest.res.mjs +160 -0
- package/tests/commandgenerator/CommandGeneratorFixtures.res.mjs +197 -0
- package/tests/commandtopic/CommandTopicCallbackFixtures.res.mjs +152 -0
- package/tests/commandtopic/CommandTopicCallbackTest.res.mjs +106 -0
- package/tests/commandtopic/CommandTopicHelpersRejectionTest.res.mjs +109 -0
- package/tests/counter/CounterCallbackTest.res.mjs +140 -0
- package/tests/counter/CounterFixtures.res.mjs +67 -0
- package/tests/dcb/DcbDecodeTest.res.mjs +194 -0
- package/tests/dcb/DcbEventLogOperationsTest.res.mjs +268 -0
- package/tests/dcb/DcbFixtures.res.mjs +534 -0
- package/tests/dcb/DcbScopeInferenceTest.res.mjs +480 -0
- package/tests/dcb/DcbStateChangeSliceTest.res.mjs +540 -0
- package/tests/dcb/DcbTagTest.res.mjs +867 -0
- package/tests/dcb/DcbValidationTest.res.mjs +476 -0
- package/tests/eventlog/EventLogFixtures.res.mjs +196 -0
- package/tests/eventlog/EventLogOperationsTest.res.mjs +192 -0
- package/tests/eventlog/EventLogRetryTest.res.mjs +195 -0
- package/tests/eventmapper/EventMapperCallbackTest.res.mjs +141 -0
- package/tests/eventmapper/EventMapperFixtures.res.mjs +440 -0
- package/tests/eventmapper/EventMapperTest.res.mjs +83 -0
- package/tests/eventtopic/EventTopicFixtures.res.mjs +65 -0
- package/tests/eventtopic/EventTopicOperationsTest.res.mjs +71 -0
- package/tests/extensionpoint/ExtensionPointCallbackTest.res.mjs +94 -0
- package/tests/extensionpoint/ExtensionPointFixtures.res.mjs +205 -0
- package/tests/extensionpoint/ExtensionPointOperationsTest.res.mjs +304 -0
- package/tests/extensionpoint/PluginDisconnectScheduleTest.res.mjs +92 -0
- package/tests/ftp/FTPTest.res.mjs +88 -0
- package/tests/logger/LogFormatTest.res.mjs +329 -0
- package/tests/logger/LoggerLazyTest.res.mjs +40 -0
- package/tests/message/MessageTest.res.mjs +218 -0
- package/tests/message/MetaEnvelopeTest.res.mjs +188 -0
- package/tests/plugin/HeartbeatDisconnectGraceTest.res.mjs +91 -0
- package/tests/plugin/ManifestVisibilityTest.res.mjs +215 -0
- package/tests/plugin/PluginSpecExperiment.res.mjs +31 -0
- package/tests/plugin/PluginStructureTest.res +55 -0
- package/tests/plugin/PluginStructureTest.res.mjs +467 -0
- package/tests/plugin/PluginVersionTest.res.mjs +30 -0
- package/tests/plugin/SchemaWalkerTest.res.mjs +160 -0
- package/tests/plugin/StateChangeSlice/PsPlaceOrder.res.mjs +62 -0
- package/tests/plugin/StateChangeSlice/PsShipOrder.res +4 -1
- package/tests/plugin/StateChangeSlice/PsShipOrder.res.mjs +77 -0
- package/tests/plugin/StateViewSlice/PsAnnotatedView.res +5 -4
- package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +110 -0
- package/tests/plugin/StateViewSlice/PsAvailableProductsView.res +1 -1
- package/tests/plugin/StateViewSlice/PsAvailableProductsView.res.mjs +70 -0
- package/tests/plugin/StateViewSlice/PsCustomersView.res +1 -1
- package/tests/plugin/StateViewSlice/PsCustomersView.res.mjs +70 -0
- package/tests/plugin/StateViewSlice/PsOrdersView.res +1 -1
- package/tests/plugin/StateViewSlice/PsOrdersView.res.mjs +82 -0
- package/tests/poc/StructuralSubtypingPoc.res.mjs +61 -0
- package/tests/projection/ProjectionOptimizeTest.res.mjs +75 -0
- package/tests/querydb/QueryDbFixtures.res.mjs +135 -0
- package/tests/querydb/QueryDbOperationsTest.res.mjs +168 -0
- package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res.mjs +77 -0
- package/tests/sideeffecthandler/SideEffectHandlerFixtures.res.mjs +139 -0
- package/tests/util/CommandPublisherTest.res.mjs +78 -0
- package/tests/util/CsvStreamTest.res.mjs +51 -0
- package/tests/util/LruTest.res.mjs +105 -0
- package/tests/util/MetricsTest.res.mjs +53 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as Stream from "effect/Stream";
|
|
7
|
+
import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
|
|
8
|
+
|
|
9
|
+
S.enableJson();
|
|
10
|
+
|
|
11
|
+
let moduleUrl = import.meta.url;
|
|
12
|
+
|
|
13
|
+
let stateSchema = S.schema(s => ({
|
|
14
|
+
name: s.m(S.string),
|
|
15
|
+
count: s.m(S.int)
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
let config = ReadModel$Reventless.config(undefined, undefined, undefined);
|
|
19
|
+
|
|
20
|
+
let ItemQueryDbSpec = {
|
|
21
|
+
Id: undefined,
|
|
22
|
+
name: "TestItemQueryDb",
|
|
23
|
+
moduleUrl: moduleUrl,
|
|
24
|
+
stateSchema: stateSchema,
|
|
25
|
+
config: config,
|
|
26
|
+
subIdConfig: undefined,
|
|
27
|
+
authorization: "AllowAuthenticated",
|
|
28
|
+
visibility: "Public"
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
let store = {
|
|
32
|
+
contents: {}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
let failNextWrite = {
|
|
36
|
+
contents: false
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
async function mockJsonOps_load(id) {
|
|
40
|
+
return {
|
|
41
|
+
TAG: "Ok",
|
|
42
|
+
_0: Stdlib_Option.getOr(store.contents[id], [])
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function mockJsonOps_loadStream(id) {
|
|
47
|
+
return Stream.fromIterable(Stdlib_Option.getOr(store.contents[id], []));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function mockJsonOps_save(id, state, _saveMode, _ttl) {
|
|
51
|
+
if (failNextWrite.contents) {
|
|
52
|
+
failNextWrite.contents = false;
|
|
53
|
+
return {
|
|
54
|
+
TAG: "Error",
|
|
55
|
+
_0: {
|
|
56
|
+
TAG: "NotSavedToStorage",
|
|
57
|
+
_0: "mock failure"
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
} else {
|
|
61
|
+
store.contents[id] = [state];
|
|
62
|
+
return {
|
|
63
|
+
TAG: "Ok",
|
|
64
|
+
_0: undefined
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function mockJsonOps_saveBatch(batch) {
|
|
70
|
+
if (failNextWrite.contents) {
|
|
71
|
+
failNextWrite.contents = false;
|
|
72
|
+
return {
|
|
73
|
+
TAG: "Error",
|
|
74
|
+
_0: {
|
|
75
|
+
TAG: "BatchNotFullyWrittenToStorage",
|
|
76
|
+
_0: "mock failure"
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
} else {
|
|
80
|
+
batch.forEach(param => {
|
|
81
|
+
store.contents[param[0]] = [param[1]];
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
TAG: "Ok",
|
|
85
|
+
_0: undefined
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function mockJsonOps_count(_id, _field, inc) {
|
|
91
|
+
return {
|
|
92
|
+
TAG: "Ok",
|
|
93
|
+
_0: inc
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function mockJsonOps_delete(id, _subId) {
|
|
98
|
+
Stdlib_Dict.$$delete(store.contents, id);
|
|
99
|
+
return {
|
|
100
|
+
TAG: "Ok",
|
|
101
|
+
_0: undefined
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function mockJsonOps_deleteBatch(ids) {
|
|
106
|
+
ids.forEach(param => Stdlib_Dict.$$delete(store.contents, param[0]));
|
|
107
|
+
return {
|
|
108
|
+
TAG: "Ok",
|
|
109
|
+
_0: undefined
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let mockJsonOps = {
|
|
114
|
+
load: mockJsonOps_load,
|
|
115
|
+
loadStream: mockJsonOps_loadStream,
|
|
116
|
+
save: mockJsonOps_save,
|
|
117
|
+
saveBatch: mockJsonOps_saveBatch,
|
|
118
|
+
count: mockJsonOps_count,
|
|
119
|
+
delete: mockJsonOps_delete,
|
|
120
|
+
deleteBatch: mockJsonOps_deleteBatch
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
function reset() {
|
|
124
|
+
store.contents = {};
|
|
125
|
+
failNextWrite.contents = false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export {
|
|
129
|
+
ItemQueryDbSpec,
|
|
130
|
+
store,
|
|
131
|
+
failNextWrite,
|
|
132
|
+
mockJsonOps,
|
|
133
|
+
reset,
|
|
134
|
+
}
|
|
135
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
|
|
4
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as QueryDbFixtures$ReventlessCore from "./QueryDbFixtures.res.mjs";
|
|
10
|
+
import * as QueryDb_Operations$ReventlessCore from "../../src/components/QueryDb/QueryDb_Operations.res.mjs";
|
|
11
|
+
|
|
12
|
+
let Ops = QueryDb_Operations$ReventlessCore.Make({
|
|
13
|
+
Id: {
|
|
14
|
+
schema: Id$Reventless.StringPure.schema,
|
|
15
|
+
make: prim => prim,
|
|
16
|
+
makeFromString: prim => prim,
|
|
17
|
+
toString: prim => prim,
|
|
18
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
19
|
+
},
|
|
20
|
+
name: QueryDbFixtures$ReventlessCore.ItemQueryDbSpec.name,
|
|
21
|
+
moduleUrl: QueryDbFixtures$ReventlessCore.ItemQueryDbSpec.moduleUrl,
|
|
22
|
+
stateSchema: QueryDbFixtures$ReventlessCore.ItemQueryDbSpec.stateSchema,
|
|
23
|
+
config: QueryDbFixtures$ReventlessCore.ItemQueryDbSpec.config,
|
|
24
|
+
subIdConfig: QueryDbFixtures$ReventlessCore.ItemQueryDbSpec.subIdConfig,
|
|
25
|
+
authorization: QueryDbFixtures$ReventlessCore.ItemQueryDbSpec.authorization,
|
|
26
|
+
visibility: QueryDbFixtures$ReventlessCore.ItemQueryDbSpec.visibility
|
|
27
|
+
})({
|
|
28
|
+
jsonOps: QueryDbFixtures$ReventlessCore.mockJsonOps
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
globalThis.beforeEach(() => QueryDbFixtures$ReventlessCore.reset());
|
|
32
|
+
|
|
33
|
+
globalThis.describe("QueryDb_Operations:", () => {
|
|
34
|
+
globalThis.describe("loadStream", () => {
|
|
35
|
+
globalThis.test("returns empty stream for unknown id", async () => {
|
|
36
|
+
let result = await Effect.runPromise(Stream.runCollect(Ops.loadStream("unknown-id")));
|
|
37
|
+
globalThis.expect(result).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
globalThis.test("decodes saved state back to typed state", async () => {
|
|
40
|
+
await Ops.save("item-1", {
|
|
41
|
+
name: "Widget",
|
|
42
|
+
count: 5
|
|
43
|
+
}, "Init", undefined);
|
|
44
|
+
let result = await Effect.runPromise(Stream.runCollect(Ops.loadStream("item-1")));
|
|
45
|
+
if (result.length !== 1) {
|
|
46
|
+
globalThis.expect("unexpected result").toEqual("Ok([state])");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let s = result[0];
|
|
50
|
+
globalThis.expect([
|
|
51
|
+
s.name,
|
|
52
|
+
s.count
|
|
53
|
+
]).toEqual([
|
|
54
|
+
"Widget",
|
|
55
|
+
5
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
globalThis.test("returns updated state after overwrite", async () => {
|
|
59
|
+
await Ops.save("item-1", {
|
|
60
|
+
name: "Widget",
|
|
61
|
+
count: 1
|
|
62
|
+
}, "Init", undefined);
|
|
63
|
+
await Ops.save("item-1", {
|
|
64
|
+
name: "Widget Updated",
|
|
65
|
+
count: 2
|
|
66
|
+
}, "Overwrite", undefined);
|
|
67
|
+
let result = await Effect.runPromise(Stream.runCollect(Ops.loadStream("item-1")));
|
|
68
|
+
if (result.length !== 1) {
|
|
69
|
+
globalThis.expect("unexpected result").toEqual("Ok([state])");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
let s = result[0];
|
|
73
|
+
globalThis.expect(s.name).toBe("Widget Updated");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
globalThis.describe("save", () => {
|
|
77
|
+
globalThis.test("returns Ok on success", async () => {
|
|
78
|
+
let result = await Ops.save("item-1", {
|
|
79
|
+
name: "Widget",
|
|
80
|
+
count: 1
|
|
81
|
+
}, "Init", undefined);
|
|
82
|
+
globalThis.expect(Stdlib_Result.isOk(result)).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
globalThis.test("stores id field in JSON", async () => {
|
|
85
|
+
await Ops.save("item-42", {
|
|
86
|
+
name: "Widget",
|
|
87
|
+
count: 0
|
|
88
|
+
}, "Init", undefined);
|
|
89
|
+
let stored = Stdlib_Option.getOr(QueryDbFixtures$ReventlessCore.store.contents["item-42"], []);
|
|
90
|
+
globalThis.expect(stored.length).toBe(1);
|
|
91
|
+
let json = stored[0];
|
|
92
|
+
let idField = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d["id"]), j => {
|
|
93
|
+
if (typeof j === "string") {
|
|
94
|
+
return j;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
globalThis.expect(idField).toEqual("item-42");
|
|
98
|
+
});
|
|
99
|
+
globalThis.test("returns Error when storage fails", async () => {
|
|
100
|
+
QueryDbFixtures$ReventlessCore.failNextWrite.contents = true;
|
|
101
|
+
let result = await Ops.save("item-1", {
|
|
102
|
+
name: "Widget",
|
|
103
|
+
count: 1
|
|
104
|
+
}, "Init", undefined);
|
|
105
|
+
globalThis.expect(Stdlib_Result.isError(result)).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
globalThis.describe("saveBatch", () => {
|
|
109
|
+
globalThis.test("saves multiple states", async () => {
|
|
110
|
+
let batch = [
|
|
111
|
+
[
|
|
112
|
+
"item-1",
|
|
113
|
+
{
|
|
114
|
+
name: "A",
|
|
115
|
+
count: 1
|
|
116
|
+
},
|
|
117
|
+
undefined
|
|
118
|
+
],
|
|
119
|
+
[
|
|
120
|
+
"item-2",
|
|
121
|
+
{
|
|
122
|
+
name: "B",
|
|
123
|
+
count: 2
|
|
124
|
+
},
|
|
125
|
+
undefined
|
|
126
|
+
]
|
|
127
|
+
];
|
|
128
|
+
await Ops.saveBatch(batch);
|
|
129
|
+
let r1 = await Effect.runPromise(Stream.runCollect(Ops.loadStream("item-1")));
|
|
130
|
+
let r2 = await Effect.runPromise(Stream.runCollect(Ops.loadStream("item-2")));
|
|
131
|
+
globalThis.expect([
|
|
132
|
+
r1.length !== 0,
|
|
133
|
+
r2.length !== 0
|
|
134
|
+
]).toEqual([
|
|
135
|
+
true,
|
|
136
|
+
true
|
|
137
|
+
]);
|
|
138
|
+
});
|
|
139
|
+
globalThis.test("returns Error when storage fails", async () => {
|
|
140
|
+
QueryDbFixtures$ReventlessCore.failNextWrite.contents = true;
|
|
141
|
+
let result = await Ops.saveBatch([[
|
|
142
|
+
"item-1",
|
|
143
|
+
{
|
|
144
|
+
name: "A",
|
|
145
|
+
count: 1
|
|
146
|
+
},
|
|
147
|
+
undefined
|
|
148
|
+
]]);
|
|
149
|
+
globalThis.expect(Stdlib_Result.isError(result)).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
globalThis.describe("delete", () => {
|
|
153
|
+
globalThis.test("removes state from storage", async () => {
|
|
154
|
+
await Ops.save("item-1", {
|
|
155
|
+
name: "Widget",
|
|
156
|
+
count: 1
|
|
157
|
+
}, "Init", undefined);
|
|
158
|
+
await Ops.$$delete("item-1", undefined);
|
|
159
|
+
let result = await Effect.runPromise(Stream.runCollect(Ops.loadStream("item-1")));
|
|
160
|
+
globalThis.expect(result).toEqual([]);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
export {
|
|
166
|
+
Ops,
|
|
167
|
+
}
|
|
168
|
+
/* Ops Not a pure module */
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Stream from "effect/Stream";
|
|
5
|
+
import * as SideEffectHandlerFixtures$ReventlessCore from "./SideEffectHandlerFixtures.res.mjs";
|
|
6
|
+
|
|
7
|
+
globalThis.beforeEach(() => SideEffectHandlerFixtures$ReventlessCore.reset());
|
|
8
|
+
|
|
9
|
+
globalThis.describe("SideEffectHandler_Callback.handleJsonEvents:", () => {
|
|
10
|
+
globalThis.describe("event matching registered side effect", () => {
|
|
11
|
+
globalThis.test("execute called with decoded id and event", async () => {
|
|
12
|
+
let event = {
|
|
13
|
+
TAG: "SomethingHappened",
|
|
14
|
+
value: "hello"
|
|
15
|
+
};
|
|
16
|
+
let eventJson = SideEffectHandlerFixtures$ReventlessCore.makeEventJson(undefined, "entity-1", event);
|
|
17
|
+
await Effect.runPromise(SideEffectHandlerFixtures$ReventlessCore.TestHandler.handleJsonEvents(Stream.fromIterable([eventJson])));
|
|
18
|
+
let call = SideEffectHandlerFixtures$ReventlessCore.capturedExecuteCalls.contents[0];
|
|
19
|
+
globalThis.expect([
|
|
20
|
+
call.id,
|
|
21
|
+
call.event
|
|
22
|
+
]).toEqual([
|
|
23
|
+
"entity-1",
|
|
24
|
+
event
|
|
25
|
+
]);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
globalThis.describe("event with no matching source name", () => {
|
|
29
|
+
globalThis.test("execute not called, no error", async () => {
|
|
30
|
+
let eventJson = SideEffectHandlerFixtures$ReventlessCore.makeEventJson("UnknownService", "entity-1", {
|
|
31
|
+
TAG: "SomethingHappened",
|
|
32
|
+
value: "ignored"
|
|
33
|
+
});
|
|
34
|
+
await Effect.runPromise(SideEffectHandlerFixtures$ReventlessCore.TestHandler.handleJsonEvents(Stream.fromIterable([eventJson])));
|
|
35
|
+
globalThis.expect(SideEffectHandlerFixtures$ReventlessCore.capturedExecuteCalls.contents.length).toBe(0);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
globalThis.describe("event with malformed JSON", () => {
|
|
39
|
+
globalThis.test("caught gracefully — does not throw, execute not called", async () => {
|
|
40
|
+
await Effect.runPromise(SideEffectHandlerFixtures$ReventlessCore.TestHandler.handleJsonEvents(Stream.fromIterable(["not-an-object"])));
|
|
41
|
+
globalThis.expect(SideEffectHandlerFixtures$ReventlessCore.capturedExecuteCalls.contents.length).toBe(0);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
globalThis.describe("execute throws", () => {
|
|
45
|
+
globalThis.test("error caught per-event, other events still processed", async () => {
|
|
46
|
+
SideEffectHandlerFixtures$ReventlessCore.executeThrowOnCall.contents = 1;
|
|
47
|
+
let event = {
|
|
48
|
+
TAG: "SomethingHappened",
|
|
49
|
+
value: "v"
|
|
50
|
+
};
|
|
51
|
+
let event1 = SideEffectHandlerFixtures$ReventlessCore.makeEventJson(undefined, "entity-1", event);
|
|
52
|
+
let event2 = SideEffectHandlerFixtures$ReventlessCore.makeEventJson(undefined, "entity-2", event);
|
|
53
|
+
await Effect.runPromise(SideEffectHandlerFixtures$ReventlessCore.TestHandler.handleJsonEvents(Stream.fromIterable([
|
|
54
|
+
event1,
|
|
55
|
+
event2
|
|
56
|
+
])));
|
|
57
|
+
globalThis.expect(SideEffectHandlerFixtures$ReventlessCore.capturedExecuteCalls.contents.length).toBe(1);
|
|
58
|
+
globalThis.expect(SideEffectHandlerFixtures$ReventlessCore.capturedExecuteCalls.contents[0].id).toBe("entity-2");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
globalThis.describe("multiple events in batch", () => {
|
|
62
|
+
globalThis.test("each handled independently", async () => {
|
|
63
|
+
let events = [
|
|
64
|
+
"entity-1",
|
|
65
|
+
"entity-2",
|
|
66
|
+
"entity-3"
|
|
67
|
+
].map(id => SideEffectHandlerFixtures$ReventlessCore.makeEventJson(undefined, id, {
|
|
68
|
+
TAG: "SomethingHappened",
|
|
69
|
+
value: "v"
|
|
70
|
+
}));
|
|
71
|
+
await Effect.runPromise(SideEffectHandlerFixtures$ReventlessCore.TestHandler.handleJsonEvents(Stream.fromIterable(events)));
|
|
72
|
+
globalThis.expect(SideEffectHandlerFixtures$ReventlessCore.capturedExecuteCalls.contents.length).toBe(3);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
5
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
6
|
+
import * as Message$ReventlessCore from "../../src/Message.res.mjs";
|
|
7
|
+
import * as SideEffectHandler_Callback$ReventlessCore from "../../src/components/SideEffectHandler/SideEffectHandler_Callback.res.mjs";
|
|
8
|
+
|
|
9
|
+
S.enableJson();
|
|
10
|
+
|
|
11
|
+
let name = "TestSideEffectSource";
|
|
12
|
+
|
|
13
|
+
let eventSchema = S.schema(s => ({
|
|
14
|
+
TAG: "SomethingHappened",
|
|
15
|
+
value: s.m(S.string)
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
let TestSource = {
|
|
19
|
+
Id: undefined,
|
|
20
|
+
name: name,
|
|
21
|
+
eventSchema: eventSchema
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
let capturedExecuteCalls = {
|
|
25
|
+
contents: []
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let executeThrowOnCall = {
|
|
29
|
+
contents: 0
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
let executeCallCount = {
|
|
33
|
+
contents: 0
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let moduleUrl = import.meta.url;
|
|
37
|
+
|
|
38
|
+
async function execute(id, _meta, event, _queryEngine) {
|
|
39
|
+
executeCallCount.contents = executeCallCount.contents + 1 | 0;
|
|
40
|
+
if (executeThrowOnCall.contents > 0 && executeCallCount.contents === executeThrowOnCall.contents) {
|
|
41
|
+
Stdlib_JsError.throwWithMessage("side effect failed");
|
|
42
|
+
}
|
|
43
|
+
capturedExecuteCalls.contents = capturedExecuteCalls.contents.concat([{
|
|
44
|
+
id: id,
|
|
45
|
+
event: event
|
|
46
|
+
}]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let TestSideEffect_Source = {
|
|
50
|
+
name: name,
|
|
51
|
+
Id: {
|
|
52
|
+
schema: Id$Reventless.StringPure.schema,
|
|
53
|
+
make: prim => prim,
|
|
54
|
+
makeFromString: prim => prim,
|
|
55
|
+
toString: prim => prim,
|
|
56
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
57
|
+
},
|
|
58
|
+
eventSchema: eventSchema
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
let TestSideEffect = {
|
|
62
|
+
Source: TestSideEffect_Source,
|
|
63
|
+
moduleUrl: moduleUrl,
|
|
64
|
+
execute: execute
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
let sideEffects = [TestSideEffect];
|
|
68
|
+
|
|
69
|
+
async function queryEngine_scan(param, param$1, param$2) {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function queryEngine_query(param, param$1, param$2, param$3, param$4, param$5, param$6) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let queryEngine = {
|
|
78
|
+
scan: queryEngine_scan,
|
|
79
|
+
query: queryEngine_query
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
let TestSpec = {
|
|
83
|
+
sideEffects: sideEffects,
|
|
84
|
+
queryEngine: queryEngine
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
let TestHandler = SideEffectHandler_Callback$ReventlessCore.Make(TestSpec);
|
|
88
|
+
|
|
89
|
+
let testMeta_ip = "127.0.0.1";
|
|
90
|
+
|
|
91
|
+
let testMeta_user = "test-user";
|
|
92
|
+
|
|
93
|
+
let testMeta = {
|
|
94
|
+
service: name,
|
|
95
|
+
time: "2024-01-01T00:00:00Z",
|
|
96
|
+
ip: testMeta_ip,
|
|
97
|
+
user: testMeta_user,
|
|
98
|
+
msgId: "se-msg-1",
|
|
99
|
+
correlationId: "se-corr-1"
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function makeEventJson(serviceOpt, id, event) {
|
|
103
|
+
let service = serviceOpt !== undefined ? serviceOpt : name;
|
|
104
|
+
let newrecord = {...testMeta};
|
|
105
|
+
return Object.fromEntries([
|
|
106
|
+
[
|
|
107
|
+
"id",
|
|
108
|
+
id
|
|
109
|
+
],
|
|
110
|
+
[
|
|
111
|
+
"meta",
|
|
112
|
+
Message$ReventlessCore.encode((newrecord.service = service, newrecord), Message$ReventlessCore.metaSchema)
|
|
113
|
+
],
|
|
114
|
+
[
|
|
115
|
+
"event",
|
|
116
|
+
Message$ReventlessCore.encode(event, eventSchema)
|
|
117
|
+
]
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function reset() {
|
|
122
|
+
capturedExecuteCalls.contents = [];
|
|
123
|
+
executeThrowOnCall.contents = 0;
|
|
124
|
+
executeCallCount.contents = 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
TestSource,
|
|
129
|
+
capturedExecuteCalls,
|
|
130
|
+
executeThrowOnCall,
|
|
131
|
+
executeCallCount,
|
|
132
|
+
TestSideEffect,
|
|
133
|
+
TestSpec,
|
|
134
|
+
TestHandler,
|
|
135
|
+
testMeta,
|
|
136
|
+
makeEventJson,
|
|
137
|
+
reset,
|
|
138
|
+
}
|
|
139
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as CommandPublisher$ReventlessCore from "../../src/util/CommandPublisher.res.mjs";
|
|
5
|
+
|
|
6
|
+
S.enableJson();
|
|
7
|
+
|
|
8
|
+
let published = {
|
|
9
|
+
contents: []
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let commandSchema = S.schema(s => ({
|
|
13
|
+
value: s.m(S.int)
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
function commandAuthorization(param) {
|
|
17
|
+
return "AllowAuthenticated";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let TestSpec = {
|
|
21
|
+
name: "TestCmd",
|
|
22
|
+
commandSchema: commandSchema,
|
|
23
|
+
commandAuthorization: commandAuthorization
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function publishCommands(_name, cmds) {
|
|
27
|
+
published.contents = published.contents.concat(cmds);
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let TestConfig = {
|
|
32
|
+
user: "tester",
|
|
33
|
+
publishCommands: publishCommands,
|
|
34
|
+
mode: "SendAllInOneChunk"
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
let Pub = CommandPublisher$ReventlessCore.Make(TestSpec)(TestConfig);
|
|
38
|
+
|
|
39
|
+
globalThis.describe("CommandPublisher", () => {
|
|
40
|
+
globalThis.test("flush sends all buffered commands in order and drains the buffer", async () => {
|
|
41
|
+
published.contents = [];
|
|
42
|
+
Pub.publish("id1", {
|
|
43
|
+
value: 1
|
|
44
|
+
});
|
|
45
|
+
Pub.publish("id2", {
|
|
46
|
+
value: 2
|
|
47
|
+
});
|
|
48
|
+
await Pub.flush();
|
|
49
|
+
let ids = published.contents.map(c => c.id);
|
|
50
|
+
globalThis.expect(ids).toEqual([
|
|
51
|
+
"id1",
|
|
52
|
+
"id2"
|
|
53
|
+
]);
|
|
54
|
+
published.contents = [];
|
|
55
|
+
await Pub.flush();
|
|
56
|
+
globalThis.expect(published.contents.length).toBe(0);
|
|
57
|
+
});
|
|
58
|
+
globalThis.test("clear truncates the whole buffer so nothing is later published", async () => {
|
|
59
|
+
published.contents = [];
|
|
60
|
+
Pub.publish("id3", {
|
|
61
|
+
value: 3
|
|
62
|
+
});
|
|
63
|
+
Pub.publish("id4", {
|
|
64
|
+
value: 4
|
|
65
|
+
});
|
|
66
|
+
Pub.clear();
|
|
67
|
+
await Pub.flush();
|
|
68
|
+
globalThis.expect(published.contents.length).toBe(0);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
published,
|
|
74
|
+
TestSpec,
|
|
75
|
+
TestConfig,
|
|
76
|
+
Pub,
|
|
77
|
+
}
|
|
78
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Fs from "fs";
|
|
4
|
+
import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
|
|
5
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Stream$1 from "effect/Stream";
|
|
9
|
+
import * as CsvStream$ReventlessCore from "../../src/util/CsvStream.res.mjs";
|
|
10
|
+
|
|
11
|
+
let fileCounter = {
|
|
12
|
+
contents: 0
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function writeTempCsv(content) {
|
|
16
|
+
fileCounter.contents = fileCounter.contents + 1 | 0;
|
|
17
|
+
let path = `/tmp/reventless-csvstream-test-` + fileCounter.contents.toString() + `.csv`;
|
|
18
|
+
Fs.writeFileSync(path, content);
|
|
19
|
+
return path;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
globalThis.describe("CsvStream.parseRows", () => {
|
|
23
|
+
globalThis.test("parses all rows from a small CSV file", async () => {
|
|
24
|
+
let path = writeTempCsv("name,age\nAlice,30\nBob,25");
|
|
25
|
+
let rows = await Effect.runPromise(Stream.runCollect(CsvStream$ReventlessCore.parseRows(path)));
|
|
26
|
+
globalThis.expect(rows.length).toBe(2);
|
|
27
|
+
});
|
|
28
|
+
globalThis.test("emits rows in file order", async () => {
|
|
29
|
+
let path = writeTempCsv("name\nfirst\nsecond\nthird");
|
|
30
|
+
let first = await Effect.runPromise(Stream.runHead(CsvStream$ReventlessCore.parseRows(path)));
|
|
31
|
+
let name = Stdlib_Option.flatMap(first, row => row["name"]);
|
|
32
|
+
globalThis.expect(name).toEqual("first");
|
|
33
|
+
});
|
|
34
|
+
globalThis.test("take(2) returns only 2 rows", async () => {
|
|
35
|
+
let dataRows = Stdlib_Array.make(10, 0).map((i, param) => `row-` + i.toString()).join("\n");
|
|
36
|
+
let path = writeTempCsv("name\n" + dataRows);
|
|
37
|
+
let result = await Effect.runPromise(Stream.runCollect(Stream$1.take(CsvStream$ReventlessCore.parseRows(path), 2)));
|
|
38
|
+
globalThis.expect(result.length).toBe(2);
|
|
39
|
+
});
|
|
40
|
+
globalThis.test("empty CSV file (header only) returns empty stream", async () => {
|
|
41
|
+
let path = writeTempCsv("name");
|
|
42
|
+
let rows = await Effect.runPromise(Stream.runCollect(CsvStream$ReventlessCore.parseRows(path)));
|
|
43
|
+
globalThis.expect(rows.length).toBe(0);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
fileCounter,
|
|
49
|
+
writeTempCsv,
|
|
50
|
+
}
|
|
51
|
+
/* Not a pure module */
|