@reventlessdev/reventless-gwt 1.0.0-alpha.74
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 +698 -0
- package/LICENSE +202 -0
- package/bin/reventless-dev.mjs +21 -0
- package/package.json +54 -0
- package/rescript.json +36 -0
- package/src/.gwtignore +10 -0
- package/src/Automation_GWT.res +207 -0
- package/src/Automation_GWT.res.mjs +160 -0
- package/src/Behavior_GWT.res +603 -0
- package/src/Behavior_GWT.res.mjs +426 -0
- package/src/Bind.res +29 -0
- package/src/Bind.res.mjs +22 -0
- package/src/BuildClassifier.res +137 -0
- package/src/BuildClassifier.res.mjs +136 -0
- package/src/Cancellation.res +31 -0
- package/src/Cancellation.res.mjs +45 -0
- package/src/ChildProcess.res +84 -0
- package/src/ChildProcess.res.mjs +92 -0
- package/src/Cli.res +671 -0
- package/src/Cli.res.mjs +754 -0
- package/src/Collector.res +201 -0
- package/src/Collector.res.mjs +215 -0
- package/src/ComponentMeta.res +104 -0
- package/src/ComponentMeta.res.mjs +120 -0
- package/src/ComponentScan.res +103 -0
- package/src/ComponentScan.res.mjs +105 -0
- package/src/Delegate_GWT.res +303 -0
- package/src/Delegate_GWT.res.mjs +372 -0
- package/src/Diff.res +91 -0
- package/src/Diff.res.mjs +98 -0
- package/src/Discovery.res +105 -0
- package/src/Discovery.res.mjs +103 -0
- package/src/DomainDeadCode.res +93 -0
- package/src/DomainDeadCode.res.mjs +69 -0
- package/src/DomainGraph.res +0 -0
- package/src/DomainGraph.res.mjs +0 -0
- package/src/EventMapping_GWT.res +29 -0
- package/src/EventMapping_GWT.res.mjs +44 -0
- package/src/Filter.res +36 -0
- package/src/Filter.res.mjs +40 -0
- package/src/Flow_GWT.res +664 -0
- package/src/Flow_GWT.res.mjs +604 -0
- package/src/FormatterHuman.res +138 -0
- package/src/FormatterHuman.res.mjs +122 -0
- package/src/FormatterJson.res +257 -0
- package/src/FormatterJson.res.mjs +320 -0
- package/src/FormatterJunit.res +93 -0
- package/src/FormatterJunit.res.mjs +90 -0
- package/src/FormatterTap.res +139 -0
- package/src/FormatterTap.res.mjs +149 -0
- package/src/FormatterVsCode.res +362 -0
- package/src/FormatterVsCode.res.mjs +564 -0
- package/src/Hint.res +119 -0
- package/src/Hint.res.mjs +124 -0
- package/src/InboundTranslation_GWT.res +83 -0
- package/src/InboundTranslation_GWT.res.mjs +94 -0
- package/src/JestBind.res +70 -0
- package/src/JestBind.res.mjs +63 -0
- package/src/Loader.res +24 -0
- package/src/Loader.res.mjs +23 -0
- package/src/LocalHost.res +135 -0
- package/src/LocalHost.res.mjs +127 -0
- package/src/Mapping_GWT.res +474 -0
- package/src/Mapping_GWT.res.mjs +322 -0
- package/src/MultiSourceProjection_GWT.res +316 -0
- package/src/MultiSourceProjection_GWT.res.mjs +354 -0
- package/src/OutboundTranslation_GWT.res +238 -0
- package/src/OutboundTranslation_GWT.res.mjs +186 -0
- package/src/Outcome.res +161 -0
- package/src/Outcome.res.mjs +140 -0
- package/src/PackageScan.res +80 -0
- package/src/PackageScan.res.mjs +81 -0
- package/src/PlatformRunner.res +160 -0
- package/src/PlatformRunner.res.mjs +155 -0
- package/src/PlatformScan.res +138 -0
- package/src/PlatformScan.res.mjs +131 -0
- package/src/ProcessManager.res +91 -0
- package/src/ProcessManager.res.mjs +80 -0
- package/src/Projection_GWT.res +312 -0
- package/src/Projection_GWT.res.mjs +332 -0
- package/src/Query_GWT.res +379 -0
- package/src/Query_GWT.res.mjs +367 -0
- package/src/RenderRescript.res +125 -0
- package/src/RenderRescript.res.mjs +141 -0
- package/src/RunnerTypes.res +68 -0
- package/src/RunnerTypes.res.mjs +60 -0
- package/src/SideEffect_GWT.res +157 -0
- package/src/SideEffect_GWT.res.mjs +113 -0
- package/src/StateChange/ExternalAddCategorySlice.res +25 -0
- package/src/StateChange/ExternalAddCategorySlice.res.mjs +34 -0
- package/src/StateChange/ExternalAddCategorySlice_Behavior.res +19 -0
- package/src/StateChange/ExternalAddCategorySlice_Behavior.res.mjs +46 -0
- package/src/StateChange/WithFixtures.res +22 -0
- package/src/StateChange/WithFixtures.res.mjs +31 -0
- package/src/StateChange/WithFixtures_Behavior.res +17 -0
- package/src/StateChange/WithFixtures_Behavior.res.mjs +37 -0
- package/src/StateChange/WithManualOpen.res +23 -0
- package/src/StateChange/WithManualOpen.res.mjs +31 -0
- package/src/StateChange/WithManualOpen_Behavior.res +17 -0
- package/src/StateChange/WithManualOpen_Behavior.res.mjs +37 -0
- package/src/StubRuntime.res +53 -0
- package/src/StubRuntime.res.mjs +54 -0
- package/src/TestFixtures.res +16 -0
- package/src/TestFixtures.res.mjs +34 -0
- package/src/Watch.res +92 -0
- package/src/Watch.res.mjs +83 -0
- package/src/WatcherProbe.res +48 -0
- package/src/WatcherProbe.res.mjs +46 -0
- package/tests/AutomationGwtTest.res +71 -0
- package/tests/AutomationGwtTest.res.mjs +189 -0
- package/tests/BuildClassifierTest.res +94 -0
- package/tests/BuildClassifierTest.res.mjs +132 -0
- package/tests/ComponentMetaTest.res +52 -0
- package/tests/ComponentMetaTest.res.mjs +57 -0
- package/tests/DelegateGwtTest.res +487 -0
- package/tests/DelegateGwtTest.res.mjs +891 -0
- package/tests/DiscoveryTest.res +42 -0
- package/tests/DiscoveryTest.res.mjs +43 -0
- package/tests/DomainDeadCodeTest.res +116 -0
- package/tests/DomainDeadCodeTest.res.mjs +155 -0
- package/tests/DomainGraphTest.res +365 -0
- package/tests/DomainGraphTest.res.mjs +413 -0
- package/tests/FlowAggregateGwtTest.res +169 -0
- package/tests/FlowAggregateGwtTest.res.mjs +269 -0
- package/tests/FlowCrossPluginGwtTest.res +340 -0
- package/tests/FlowCrossPluginGwtTest.res.mjs +798 -0
- package/tests/FlowGwtTest.res +222 -0
- package/tests/FlowGwtTest.res.mjs +432 -0
- package/tests/FormatterVsCodeMessageTest.res +41 -0
- package/tests/FormatterVsCodeMessageTest.res.mjs +42 -0
- package/tests/InboundTranslationGwtTest.res +34 -0
- package/tests/InboundTranslationGwtTest.res.mjs +98 -0
- package/tests/LocalHostIntegration.res +70 -0
- package/tests/LocalHostIntegration.res.mjs +64 -0
- package/tests/LocalHostTest.res +71 -0
- package/tests/LocalHostTest.res.mjs +63 -0
- package/tests/MappingGwtTest.res +348 -0
- package/tests/MappingGwtTest.res.mjs +592 -0
- package/tests/OutboundTranslationGwtTest.res +59 -0
- package/tests/OutboundTranslationGwtTest.res.mjs +138 -0
- package/tests/PlatformRunnerTest.res +66 -0
- package/tests/PlatformRunnerTest.res.mjs +85 -0
- package/tests/PlatformScanTest.res +61 -0
- package/tests/PlatformScanTest.res.mjs +56 -0
- package/tests/QueryGwtTest.res +161 -0
- package/tests/QueryGwtTest.res.mjs +314 -0
- package/tests/QueryResolverGwtTest.res +71 -0
- package/tests/QueryResolverGwtTest.res.mjs +136 -0
- package/tests/RunnerUnitTest.res +219 -0
- package/tests/RunnerUnitTest.res.mjs +244 -0
- package/tests/SideEffect/FakeOrderNotification.res +23 -0
- package/tests/SideEffect/FakeOrderNotification.res.mjs +36 -0
- package/tests/SideEffect/FakeOrderNotificationGwtTest.res +35 -0
- package/tests/SideEffect/FakeOrderNotificationGwtTest.res.mjs +73 -0
- package/tests/SideEffect/MockEmail.res +18 -0
- package/tests/SideEffect/MockEmail.res.mjs +47 -0
- package/tests/StateChange/ExternalAddCategorySlice_GWT.res +27 -0
- package/tests/StateChange/ExternalAddCategorySlice_GWT.res.mjs +70 -0
- package/tests/StateChange/WithFixtures_Fixtures.res +9 -0
- package/tests/StateChange/WithFixtures_Fixtures.res.mjs +20 -0
- package/tests/StateChange/WithFixtures_GWT.res +23 -0
- package/tests/StateChange/WithFixtures_GWT.res.mjs +56 -0
- package/tests/StateChange/WithManualOpen_Fixtures.res +7 -0
- package/tests/StateChange/WithManualOpen_Fixtures.res.mjs +20 -0
- package/tests/StateChange/WithManualOpen_GWT.res +17 -0
- package/tests/StateChange/WithManualOpen_GWT.res.mjs +56 -0
- package/tests/StateChangeSliceGwtTest.res +160 -0
- package/tests/StateChangeSliceGwtTest.res.mjs +280 -0
- package/tests/StateViewSliceGwtTest.res +64 -0
- package/tests/StateViewSliceGwtTest.res.mjs +161 -0
- package/tests/WatchDebounceTest.res +78 -0
- package/tests/WatchDebounceTest.res.mjs +97 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function statusString(s) {
|
|
5
|
+
switch (s) {
|
|
6
|
+
case "Pass" :
|
|
7
|
+
return "pass";
|
|
8
|
+
case "Fail" :
|
|
9
|
+
return "fail";
|
|
10
|
+
case "Skip" :
|
|
11
|
+
return "skip";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function summaryOf(files, durationMs, startedAt) {
|
|
16
|
+
let total = {
|
|
17
|
+
contents: 0
|
|
18
|
+
};
|
|
19
|
+
let passed = {
|
|
20
|
+
contents: 0
|
|
21
|
+
};
|
|
22
|
+
let failed = {
|
|
23
|
+
contents: 0
|
|
24
|
+
};
|
|
25
|
+
let skipped = {
|
|
26
|
+
contents: 0
|
|
27
|
+
};
|
|
28
|
+
files.forEach(f => {
|
|
29
|
+
f.tests.forEach(t => {
|
|
30
|
+
total.contents = total.contents + 1 | 0;
|
|
31
|
+
let match = t.status;
|
|
32
|
+
switch (match) {
|
|
33
|
+
case "Pass" :
|
|
34
|
+
passed.contents = passed.contents + 1 | 0;
|
|
35
|
+
return;
|
|
36
|
+
case "Fail" :
|
|
37
|
+
failed.contents = failed.contents + 1 | 0;
|
|
38
|
+
return;
|
|
39
|
+
case "Skip" :
|
|
40
|
+
skipped.contents = skipped.contents + 1 | 0;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
total: total.contents,
|
|
47
|
+
passed: passed.contents,
|
|
48
|
+
failed: failed.contents,
|
|
49
|
+
skipped: skipped.contents,
|
|
50
|
+
files: files.length,
|
|
51
|
+
durationMs: durationMs,
|
|
52
|
+
startedAt: startedAt
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
statusString,
|
|
58
|
+
summaryOf,
|
|
59
|
+
}
|
|
60
|
+
/* No side effect */
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
open ReventlessCore
|
|
2
|
+
|
|
3
|
+
// Test DSL for aggregate-style egress (`SideEffect.T`). Mirrors
|
|
4
|
+
// `OutboundTranslation_GWT` adapted to the SideEffect runtime contract: the
|
|
5
|
+
// unit under test is `SE.execute`, which returns `promise<unit>` and reaches
|
|
6
|
+
// external services through direct module imports. Option C of the
|
|
7
|
+
// `sideeffect-gwt` plan: the framework stays out of the import mechanics —
|
|
8
|
+
// per-service `*_Mock.res` modules own the recording state and surface it
|
|
9
|
+
// through this `mock` handle. The GWT runs `execute`, then reads the snapshot
|
|
10
|
+
// and renders assertions against captured calls.
|
|
11
|
+
|
|
12
|
+
// Polymorphic mock handle a test passes to `whenExecuted`. The mock module
|
|
13
|
+
// owns the recording state (a `ref<array<call>>` mutated as the SE calls the
|
|
14
|
+
// service); `snapshot` reads it; `encode` is used to render mismatched calls
|
|
15
|
+
// in failure messages.
|
|
16
|
+
type mock<'call> = {
|
|
17
|
+
snapshot: unit => array<'call>,
|
|
18
|
+
encode: 'call => JSON.t,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Stub `QueryEngine.operations` used when a test doesn't pass its own.
|
|
22
|
+
// Returns empty result sets — enough for SEs that never query, which is the
|
|
23
|
+
// idiomatic shape.
|
|
24
|
+
let stubQueryEngine: Reventless.QueryEngine.operations = {
|
|
25
|
+
scan: async (~readModelName as _, ~filterConfigs as _, ~limit as _) => [],
|
|
26
|
+
query: async (
|
|
27
|
+
~readModelName as _,
|
|
28
|
+
~key as _=?,
|
|
29
|
+
~id as _,
|
|
30
|
+
~subIdConfig as _=?,
|
|
31
|
+
~filterConfigs as _=?,
|
|
32
|
+
~ascending as _=?,
|
|
33
|
+
~limit as _=?,
|
|
34
|
+
) => [],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module type T = {
|
|
38
|
+
module SE: Reventless.SideEffect.T
|
|
39
|
+
|
|
40
|
+
let describe: (string, unit => unit) => unit
|
|
41
|
+
let test: (string, ~timeout: int=?, unit => promise<Outcome.outcome>) => unit
|
|
42
|
+
|
|
43
|
+
type input = {
|
|
44
|
+
id: SE.Source.Id.t,
|
|
45
|
+
meta: Message.meta,
|
|
46
|
+
event: SE.Source.event,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Post-execution state the pipeline carries forward. Holds the inputs (so
|
|
50
|
+
// failure messages can quote them), the captured calls, and the encoder
|
|
51
|
+
// (passed through from the mock) used to render them.
|
|
52
|
+
type attempt<'call> = {
|
|
53
|
+
input: input,
|
|
54
|
+
captured: array<'call>,
|
|
55
|
+
encode: 'call => JSON.t,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// given*
|
|
59
|
+
let givenEvent: SE.Source.event => input
|
|
60
|
+
let givenEventForId: (SE.Source.Id.t, SE.Source.event) => input
|
|
61
|
+
let givenMeta: (input, Message.meta) => input
|
|
62
|
+
|
|
63
|
+
// when — runs `SE.execute` against a stub QueryEngine (override via
|
|
64
|
+
// ~queryEngine) and the supplied mock's snapshot. The mock module is
|
|
65
|
+
// expected to install itself into the service it shadows BEFORE this is
|
|
66
|
+
// called (the per-service `*_Mock.res` convention; see the example).
|
|
67
|
+
let whenExecuted: (
|
|
68
|
+
input,
|
|
69
|
+
mock<'call>,
|
|
70
|
+
~queryEngine: Reventless.QueryEngine.operations=?,
|
|
71
|
+
) => promise<attempt<'call>>
|
|
72
|
+
|
|
73
|
+
// then*
|
|
74
|
+
let thenNoExternalCalls: promise<attempt<'call>> => promise<Outcome.outcome>
|
|
75
|
+
let thenExternalCalls: (promise<attempt<'call>>, array<'call>) => promise<Outcome.outcome>
|
|
76
|
+
let thenExternalCallCount: (promise<attempt<'call>>, int) => promise<Outcome.outcome>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module Make = (SE: Reventless.SideEffect.T): (T with module SE = SE) => {
|
|
80
|
+
module SE = SE
|
|
81
|
+
|
|
82
|
+
S.enableJson()
|
|
83
|
+
|
|
84
|
+
let describe = JestBind.describe
|
|
85
|
+
let test = (name, ~timeout=?, body) =>
|
|
86
|
+
JestBind.testPromise(~slice=SE.Source.name, name, ~timeout?, body)
|
|
87
|
+
|
|
88
|
+
type input = {
|
|
89
|
+
id: SE.Source.Id.t,
|
|
90
|
+
meta: Message.meta,
|
|
91
|
+
event: SE.Source.event,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
type attempt<'call> = {
|
|
95
|
+
input: input,
|
|
96
|
+
captured: array<'call>,
|
|
97
|
+
encode: 'call => JSON.t,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let defaultId = SE.Source.Id.makeFromString("test-id-1")
|
|
101
|
+
let defaultMeta: Message.meta = {
|
|
102
|
+
service: SE.Source.name,
|
|
103
|
+
time: "2024-01-01T00:00:00Z",
|
|
104
|
+
msgId: "test-msg-1",
|
|
105
|
+
correlationId: "test-msg-1",
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let givenEvent = event => {id: defaultId, meta: defaultMeta, event}
|
|
109
|
+
let givenEventForId = (id, event) => {id, meta: defaultMeta, event}
|
|
110
|
+
let givenMeta = (input, meta) => {...input, meta}
|
|
111
|
+
|
|
112
|
+
let whenExecuted = async (input, mock, ~queryEngine=stubQueryEngine) => {
|
|
113
|
+
await SE.execute(input.id, input.meta, input.event, queryEngine)
|
|
114
|
+
{input, captured: mock.snapshot(), encode: mock.encode}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let encCalls = (calls, encode) => calls->Array.map(encode)
|
|
118
|
+
|
|
119
|
+
let thenNoExternalCalls = async pending => {
|
|
120
|
+
let {captured, encode, _} = await pending
|
|
121
|
+
if captured == [] {
|
|
122
|
+
Outcome.pass
|
|
123
|
+
} else {
|
|
124
|
+
Outcome.fail(NoEventExpected({actual: encCalls(captured, encode)}))
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let thenExternalCalls = async (pending, expected) => {
|
|
129
|
+
let {captured, encode, _} = await pending
|
|
130
|
+
if captured == expected {
|
|
131
|
+
Outcome.pass
|
|
132
|
+
} else {
|
|
133
|
+
Outcome.fail(
|
|
134
|
+
EventsMismatch({
|
|
135
|
+
expected: encCalls(expected, encode),
|
|
136
|
+
actual: encCalls(captured, encode),
|
|
137
|
+
}),
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let thenExternalCallCount = async (pending, expected) => {
|
|
143
|
+
let {captured, _} = await pending
|
|
144
|
+
let actual = Array.length(captured)
|
|
145
|
+
if actual == expected {
|
|
146
|
+
Outcome.pass
|
|
147
|
+
} else {
|
|
148
|
+
Outcome.fail(
|
|
149
|
+
StateMismatch({
|
|
150
|
+
key: "external-call-count",
|
|
151
|
+
expected: Some(JSON.Encode.int(expected)),
|
|
152
|
+
actual: Some(JSON.Encode.int(actual)),
|
|
153
|
+
}),
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
5
|
+
import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
|
|
6
|
+
import * as JestBind$ReventlessGwt from "./JestBind.res.mjs";
|
|
7
|
+
|
|
8
|
+
async function stubQueryEngine_scan(param, param$1, param$2) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function stubQueryEngine_query(param, param$1, param$2, param$3, param$4, param$5, param$6) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let stubQueryEngine = {
|
|
17
|
+
scan: stubQueryEngine_scan,
|
|
18
|
+
query: stubQueryEngine_query
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function Make(SE) {
|
|
22
|
+
S.enableJson();
|
|
23
|
+
let test = (name, timeout, body) => JestBind$ReventlessGwt.testPromise(SE.Source.name, name, timeout, body);
|
|
24
|
+
let defaultId = SE.Source.Id.makeFromString("test-id-1");
|
|
25
|
+
let defaultMeta_service = SE.Source.name;
|
|
26
|
+
let defaultMeta = {
|
|
27
|
+
service: defaultMeta_service,
|
|
28
|
+
time: "2024-01-01T00:00:00Z",
|
|
29
|
+
msgId: "test-msg-1",
|
|
30
|
+
correlationId: "test-msg-1"
|
|
31
|
+
};
|
|
32
|
+
let givenEvent = event => ({
|
|
33
|
+
id: defaultId,
|
|
34
|
+
meta: defaultMeta,
|
|
35
|
+
event: event
|
|
36
|
+
});
|
|
37
|
+
let givenEventForId = (id, event) => ({
|
|
38
|
+
id: id,
|
|
39
|
+
meta: defaultMeta,
|
|
40
|
+
event: event
|
|
41
|
+
});
|
|
42
|
+
let givenMeta = (input, meta) => ({
|
|
43
|
+
id: input.id,
|
|
44
|
+
meta: meta,
|
|
45
|
+
event: input.event
|
|
46
|
+
});
|
|
47
|
+
let whenExecuted = async (input, mock, queryEngineOpt) => {
|
|
48
|
+
let queryEngine = queryEngineOpt !== undefined ? queryEngineOpt : stubQueryEngine;
|
|
49
|
+
await SE.execute(input.id, input.meta, input.event, queryEngine);
|
|
50
|
+
return {
|
|
51
|
+
input: input,
|
|
52
|
+
captured: mock.snapshot(),
|
|
53
|
+
encode: mock.encode
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
let thenNoExternalCalls = async pending => {
|
|
57
|
+
let match = await pending;
|
|
58
|
+
let captured = match.captured;
|
|
59
|
+
if (Primitive_object.equal(captured, [])) {
|
|
60
|
+
return Outcome$ReventlessGwt.pass;
|
|
61
|
+
} else {
|
|
62
|
+
return Outcome$ReventlessGwt.fail({
|
|
63
|
+
TAG: "NoEventExpected",
|
|
64
|
+
actual: captured.map(match.encode)
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
let thenExternalCalls = async (pending, expected) => {
|
|
69
|
+
let match = await pending;
|
|
70
|
+
let captured = match.captured;
|
|
71
|
+
if (Primitive_object.equal(captured, expected)) {
|
|
72
|
+
return Outcome$ReventlessGwt.pass;
|
|
73
|
+
}
|
|
74
|
+
let encode = match.encode;
|
|
75
|
+
return Outcome$ReventlessGwt.fail({
|
|
76
|
+
TAG: "EventsMismatch",
|
|
77
|
+
expected: expected.map(encode),
|
|
78
|
+
actual: captured.map(encode)
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
let thenExternalCallCount = async (pending, expected) => {
|
|
82
|
+
let match = await pending;
|
|
83
|
+
let actual = match.captured.length;
|
|
84
|
+
if (actual === expected) {
|
|
85
|
+
return Outcome$ReventlessGwt.pass;
|
|
86
|
+
} else {
|
|
87
|
+
return Outcome$ReventlessGwt.fail({
|
|
88
|
+
TAG: "StateMismatch",
|
|
89
|
+
key: "external-call-count",
|
|
90
|
+
expected: expected,
|
|
91
|
+
actual: actual
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
SE: SE,
|
|
97
|
+
describe: JestBind$ReventlessGwt.describe,
|
|
98
|
+
test: test,
|
|
99
|
+
givenEvent: givenEvent,
|
|
100
|
+
givenEventForId: givenEventForId,
|
|
101
|
+
givenMeta: givenMeta,
|
|
102
|
+
whenExecuted: whenExecuted,
|
|
103
|
+
thenNoExternalCalls: thenNoExternalCalls,
|
|
104
|
+
thenExternalCalls: thenExternalCalls,
|
|
105
|
+
thenExternalCallCount: thenExternalCallCount
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export {
|
|
110
|
+
stubQueryEngine,
|
|
111
|
+
Make,
|
|
112
|
+
}
|
|
113
|
+
/* S Not a pure module */
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Production-shaped StateChangeSlice Spec used by the external-Spec
|
|
2
|
+
// verification test at [tests/StateChange/ExternalAddCategorySlice_GWT.res].
|
|
3
|
+
// Mirrors the worked-example AddCategory slice shape: DCB-tagged categoryId
|
|
4
|
+
// on command + event, payload-less consumed events.
|
|
5
|
+
//
|
|
6
|
+
// Spec half (Plan 02 Phase 6) — types, schemas, name. The matching impl
|
|
7
|
+
// half lives in [ExternalAddCategorySlice_Behavior.res].
|
|
8
|
+
|
|
9
|
+
let name = "ExternalAddCategory"
|
|
10
|
+
|
|
11
|
+
@schema
|
|
12
|
+
type consumedEvent =
|
|
13
|
+
| CategoryAdded
|
|
14
|
+
| CategoryArchived
|
|
15
|
+
|
|
16
|
+
@schema
|
|
17
|
+
type command =
|
|
18
|
+
AddCategory({categoryId: @s.matches(Reventless.DcbTag.string) string, name: string})
|
|
19
|
+
|
|
20
|
+
@schema
|
|
21
|
+
type error = CategoryAlreadyExists
|
|
22
|
+
|
|
23
|
+
@schema
|
|
24
|
+
type event =
|
|
25
|
+
CategoryAdded({categoryId: @s.matches(Reventless.DcbTag.string) string, name: string})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
5
|
+
|
|
6
|
+
let consumedEventSchema = S.union([
|
|
7
|
+
S.literal("CategoryAdded"),
|
|
8
|
+
S.literal("CategoryArchived")
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
let commandSchema = S.schema(s => ({
|
|
12
|
+
TAG: "AddCategory",
|
|
13
|
+
categoryId: s.m(DcbTag$Reventless.string),
|
|
14
|
+
name: s.m(S.string)
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
let errorSchema = S.literal("CategoryAlreadyExists");
|
|
18
|
+
|
|
19
|
+
let eventSchema = S.schema(s => ({
|
|
20
|
+
TAG: "CategoryAdded",
|
|
21
|
+
categoryId: s.m(DcbTag$Reventless.string),
|
|
22
|
+
name: s.m(S.string)
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
let name = "ExternalAddCategory";
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
name,
|
|
29
|
+
consumedEventSchema,
|
|
30
|
+
commandSchema,
|
|
31
|
+
errorSchema,
|
|
32
|
+
eventSchema,
|
|
33
|
+
}
|
|
34
|
+
/* consumedEventSchema Not a pure module */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Implementation half — pure decision/projection functions for the external
|
|
2
|
+
// AddCategory StateChangeSlice. Pairs with [ExternalAddCategorySlice.res].
|
|
3
|
+
|
|
4
|
+
open ExternalAddCategorySlice
|
|
5
|
+
|
|
6
|
+
type state = {exists: bool, archived: bool}
|
|
7
|
+
let initialState: state = {exists: false, archived: false}
|
|
8
|
+
|
|
9
|
+
let evolve = (state: state, event: consumedEvent): state =>
|
|
10
|
+
switch event {
|
|
11
|
+
| CategoryAdded => {exists: true, archived: false}
|
|
12
|
+
| CategoryArchived => {...state, archived: true}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let decide = (state: state, command: command): result<array<event>, error> =>
|
|
16
|
+
switch command {
|
|
17
|
+
| AddCategory({categoryId, name}) =>
|
|
18
|
+
state.exists ? Error(CategoryAlreadyExists) : Ok([CategoryAdded({categoryId, name})])
|
|
19
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function evolve(state, event) {
|
|
5
|
+
if (event === "CategoryAdded") {
|
|
6
|
+
return {
|
|
7
|
+
exists: true,
|
|
8
|
+
archived: false
|
|
9
|
+
};
|
|
10
|
+
} else {
|
|
11
|
+
return {
|
|
12
|
+
exists: state.exists,
|
|
13
|
+
archived: true
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function decide(state, command) {
|
|
19
|
+
if (state.exists) {
|
|
20
|
+
return {
|
|
21
|
+
TAG: "Error",
|
|
22
|
+
_0: "CategoryAlreadyExists"
|
|
23
|
+
};
|
|
24
|
+
} else {
|
|
25
|
+
return {
|
|
26
|
+
TAG: "Ok",
|
|
27
|
+
_0: [{
|
|
28
|
+
TAG: "CategoryAdded",
|
|
29
|
+
categoryId: command.categoryId,
|
|
30
|
+
name: command.name
|
|
31
|
+
}]
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let initialState = {
|
|
37
|
+
exists: false,
|
|
38
|
+
archived: false
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
initialState,
|
|
43
|
+
evolve,
|
|
44
|
+
decide,
|
|
45
|
+
}
|
|
46
|
+
/* No side effect */
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Production-shaped Spec for the companion-fixtures auto-open verification
|
|
2
|
+
// tests at [tests/StateChange/WithFixtures_GWT.res] and
|
|
3
|
+
// [tests/StateChange/WithManualOpen_GWT.res].
|
|
4
|
+
//
|
|
5
|
+
// Spec half — pairs with [WithFixtures_Behavior.res].
|
|
6
|
+
|
|
7
|
+
let name = "WithFixtures"
|
|
8
|
+
|
|
9
|
+
@schema
|
|
10
|
+
type consumedEvent =
|
|
11
|
+
| CategoryAdded
|
|
12
|
+
|
|
13
|
+
@schema
|
|
14
|
+
type command =
|
|
15
|
+
AddCategory({categoryId: @s.matches(Reventless.DcbTag.string) string, name: string})
|
|
16
|
+
|
|
17
|
+
@schema
|
|
18
|
+
type error = CategoryAlreadyExists
|
|
19
|
+
|
|
20
|
+
@schema
|
|
21
|
+
type event =
|
|
22
|
+
CategoryAdded({categoryId: @s.matches(Reventless.DcbTag.string) string, name: string})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
5
|
+
|
|
6
|
+
let consumedEventSchema = S.literal("CategoryAdded");
|
|
7
|
+
|
|
8
|
+
let commandSchema = S.schema(s => ({
|
|
9
|
+
TAG: "AddCategory",
|
|
10
|
+
categoryId: s.m(DcbTag$Reventless.string),
|
|
11
|
+
name: s.m(S.string)
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
let errorSchema = S.literal("CategoryAlreadyExists");
|
|
15
|
+
|
|
16
|
+
let eventSchema = S.schema(s => ({
|
|
17
|
+
TAG: "CategoryAdded",
|
|
18
|
+
categoryId: s.m(DcbTag$Reventless.string),
|
|
19
|
+
name: s.m(S.string)
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
let name = "WithFixtures";
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
name,
|
|
26
|
+
consumedEventSchema,
|
|
27
|
+
commandSchema,
|
|
28
|
+
errorSchema,
|
|
29
|
+
eventSchema,
|
|
30
|
+
}
|
|
31
|
+
/* consumedEventSchema Not a pure module */
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Implementation half — pairs with [WithFixtures.res].
|
|
2
|
+
|
|
3
|
+
open WithFixtures
|
|
4
|
+
|
|
5
|
+
type state = {exists: bool}
|
|
6
|
+
let initialState: state = {exists: false}
|
|
7
|
+
|
|
8
|
+
let evolve = (_state: state, event: consumedEvent): state =>
|
|
9
|
+
switch event {
|
|
10
|
+
| CategoryAdded => {exists: true}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let decide = (state: state, command: command): result<array<event>, error> =>
|
|
14
|
+
switch command {
|
|
15
|
+
| AddCategory({categoryId, name}) =>
|
|
16
|
+
state.exists ? Error(CategoryAlreadyExists) : Ok([CategoryAdded({categoryId, name})])
|
|
17
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function evolve(_state, event) {
|
|
5
|
+
return {
|
|
6
|
+
exists: true
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function decide(state, command) {
|
|
11
|
+
if (state.exists) {
|
|
12
|
+
return {
|
|
13
|
+
TAG: "Error",
|
|
14
|
+
_0: "CategoryAlreadyExists"
|
|
15
|
+
};
|
|
16
|
+
} else {
|
|
17
|
+
return {
|
|
18
|
+
TAG: "Ok",
|
|
19
|
+
_0: [{
|
|
20
|
+
TAG: "CategoryAdded",
|
|
21
|
+
categoryId: command.categoryId,
|
|
22
|
+
name: command.name
|
|
23
|
+
}]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let initialState = {
|
|
29
|
+
exists: false
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
initialState,
|
|
34
|
+
evolve,
|
|
35
|
+
decide,
|
|
36
|
+
}
|
|
37
|
+
/* No side effect */
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Second Spec used by the companion-fixtures manual-open dedup verification
|
|
2
|
+
// test at [tests/StateChange/WithManualOpen_GWT.res]. Identical shape to
|
|
3
|
+
// [WithFixtures]; named differently so it gets its own companion
|
|
4
|
+
// [<Stem>_Fixtures] sibling that the PPX considers for auto-open.
|
|
5
|
+
//
|
|
6
|
+
// Spec half — pairs with [WithManualOpen_Behavior.res].
|
|
7
|
+
|
|
8
|
+
let name = "WithManualOpen"
|
|
9
|
+
|
|
10
|
+
@schema
|
|
11
|
+
type consumedEvent =
|
|
12
|
+
| CategoryAdded
|
|
13
|
+
|
|
14
|
+
@schema
|
|
15
|
+
type command =
|
|
16
|
+
AddCategory({categoryId: @s.matches(Reventless.DcbTag.string) string, name: string})
|
|
17
|
+
|
|
18
|
+
@schema
|
|
19
|
+
type error = CategoryAlreadyExists
|
|
20
|
+
|
|
21
|
+
@schema
|
|
22
|
+
type event =
|
|
23
|
+
CategoryAdded({categoryId: @s.matches(Reventless.DcbTag.string) string, name: string})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
5
|
+
|
|
6
|
+
let consumedEventSchema = S.literal("CategoryAdded");
|
|
7
|
+
|
|
8
|
+
let commandSchema = S.schema(s => ({
|
|
9
|
+
TAG: "AddCategory",
|
|
10
|
+
categoryId: s.m(DcbTag$Reventless.string),
|
|
11
|
+
name: s.m(S.string)
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
let errorSchema = S.literal("CategoryAlreadyExists");
|
|
15
|
+
|
|
16
|
+
let eventSchema = S.schema(s => ({
|
|
17
|
+
TAG: "CategoryAdded",
|
|
18
|
+
categoryId: s.m(DcbTag$Reventless.string),
|
|
19
|
+
name: s.m(S.string)
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
let name = "WithManualOpen";
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
name,
|
|
26
|
+
consumedEventSchema,
|
|
27
|
+
commandSchema,
|
|
28
|
+
errorSchema,
|
|
29
|
+
eventSchema,
|
|
30
|
+
}
|
|
31
|
+
/* consumedEventSchema Not a pure module */
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Implementation half — pairs with [WithManualOpen.res].
|
|
2
|
+
|
|
3
|
+
open WithManualOpen
|
|
4
|
+
|
|
5
|
+
type state = {exists: bool}
|
|
6
|
+
let initialState: state = {exists: false}
|
|
7
|
+
|
|
8
|
+
let evolve = (_state: state, event: consumedEvent): state =>
|
|
9
|
+
switch event {
|
|
10
|
+
| CategoryAdded => {exists: true}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let decide = (state: state, command: command): result<array<event>, error> =>
|
|
14
|
+
switch command {
|
|
15
|
+
| AddCategory({categoryId, name}) =>
|
|
16
|
+
state.exists ? Error(CategoryAlreadyExists) : Ok([CategoryAdded({categoryId, name})])
|
|
17
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function evolve(_state, event) {
|
|
5
|
+
return {
|
|
6
|
+
exists: true
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function decide(state, command) {
|
|
11
|
+
if (state.exists) {
|
|
12
|
+
return {
|
|
13
|
+
TAG: "Error",
|
|
14
|
+
_0: "CategoryAlreadyExists"
|
|
15
|
+
};
|
|
16
|
+
} else {
|
|
17
|
+
return {
|
|
18
|
+
TAG: "Ok",
|
|
19
|
+
_0: [{
|
|
20
|
+
TAG: "CategoryAdded",
|
|
21
|
+
categoryId: command.categoryId,
|
|
22
|
+
name: command.name
|
|
23
|
+
}]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let initialState = {
|
|
29
|
+
exists: false
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
initialState,
|
|
34
|
+
evolve,
|
|
35
|
+
decide,
|
|
36
|
+
}
|
|
37
|
+
/* No side effect */
|