@reventlessdev/reventless-local 3.0.0-alpha.292 → 3.0.0-alpha.294

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.
Files changed (26) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/package.json +11 -11
  3. package/src/Platform.res +6 -3
  4. package/src/Platform.res.mjs +11 -10
  5. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res +19 -20
  6. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res.mjs +15 -36
  7. package/src/adapter/CommandTopic/LocalCommandTopicChannel.res +1 -1
  8. package/src/adapter/Runtime/LocalRuntimeEnvironment.res +8 -3
  9. package/src/adapter/Task/LocalTaskBucket.res +1 -1
  10. package/src/reexport/CommandTopic.res.mjs +3 -0
  11. package/tests/adapter/CommandAuthorizationTest.res +6 -0
  12. package/tests/adapter/CommandAuthorizationTest.res.mjs +1 -0
  13. package/tests/adapter/GraphQL_SchemaInspectorTest.res +3 -3
  14. package/tests/adapter/GraphQL_SchemaInspectorTest.res.mjs +1 -1
  15. package/tests/adapter/InboundTranslationMutationTest.res +26 -2
  16. package/tests/adapter/InboundTranslationMutationTest.res.mjs +30 -5
  17. package/tests/components/dcb/DcbRejectionCauseTest.res +271 -0
  18. package/tests/components/dcb/DcbRejectionCauseTest.res.mjs +440 -0
  19. package/tests/components/heartbeat/HeartbeatFixtures.res +1 -1
  20. package/tests/components/stateviewslice/StateViewSliceDisplayNameFixtures.res +1 -1
  21. package/tests/components/stateviewslice/StateViewSliceDisplayNameFixtures.res.mjs +2 -2
  22. package/tests/components/stateviewslice/StateViewSliceFixtures.res +1 -1
  23. package/tests/components/stateviewslice/StateViewSliceFixtures.res.mjs +2 -2
  24. package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res +1 -1
  25. package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res.mjs +2 -2
  26. package/tests/plugin/UiFragments_GWT.res.mjs +1 -1
@@ -0,0 +1,271 @@
1
+ // A rejected command says who said no, on the wire a client reads. Driven through
2
+ // the producer path (`runInlineAndCollect` → `commandOutcomeToJson`) over the real
3
+ // in-memory and SQLite DCB stores: the model (`decide`), the caller (ownership),
4
+ // and the platform (exhausted retries, a failing store).
5
+
6
+ open JestGlobals
7
+
8
+ let _ = TestRunner.setup()
9
+ let opts: Pulumi.CustomResourceOptions.t = {}
10
+
11
+ module Message = ReventlessCore.Message
12
+ module CommandTopic_Helpers = ReventlessCore.CommandTopic_Helpers
13
+
14
+ module OrderSlice = {
15
+ let name = "CauseOrders"
16
+ module Id = Reventless.Id.String
17
+ let moduleUrl: string = %raw(`import.meta.url`)
18
+
19
+ @schema
20
+ type event =
21
+ | OrderPlaced({orderId: @s.matches(Reventless.DcbTag.string) string, customerId: string})
22
+ | OrderCancelled({orderId: @s.matches(Reventless.DcbTag.string) string})
23
+
24
+ @schema
25
+ type consumedEvent =
26
+ | OrderPlaced({
27
+ orderId: @s.matches(Reventless.DcbTag.string) string,
28
+ customerId: @s.matches(Reventless.Owner.string) string,
29
+ })
30
+ | OrderCancelled({orderId: @s.matches(Reventless.DcbTag.string) string})
31
+
32
+ @schema
33
+ type command =
34
+ | PlaceOrder({
35
+ orderId: @s.matches(Reventless.DcbTag.string) string,
36
+ customerId: @s.matches(Reventless.Owner.string) string,
37
+ })
38
+ | CancelOrder({orderId: @s.matches(Reventless.DcbTag.string) string})
39
+
40
+ @schema
41
+ type error = OrderAlreadyPlaced | OrderNotFound
42
+ }
43
+
44
+ module OrderDecision = {
45
+ type state = {placed: bool, cancelled: bool}
46
+ let initialState = {placed: false, cancelled: false}
47
+ let moduleUrl = OrderSlice.moduleUrl
48
+
49
+ let evolve = (state, event: OrderSlice.consumedEvent) =>
50
+ switch event {
51
+ | OrderPlaced(_) => {...state, placed: true}
52
+ | OrderCancelled(_) => {...state, cancelled: true}
53
+ }
54
+
55
+ let decide = (state, command: OrderSlice.command): result<
56
+ array<OrderSlice.event>,
57
+ OrderSlice.error,
58
+ > =>
59
+ switch command {
60
+ | PlaceOrder({orderId, customerId}) =>
61
+ state.placed ? Error(OrderAlreadyPlaced) : Ok([OrderPlaced({orderId, customerId})])
62
+ | CancelOrder({orderId}) =>
63
+ state.placed ? Ok([OrderCancelled({orderId: orderId})]) : Error(OrderNotFound)
64
+ }
65
+ }
66
+
67
+ module OrderHandler = ReventlessCore.StateChangeSlice_Callback.Make(OrderSlice, OrderDecision)
68
+
69
+ let partitionTag = Reventless.DcbTag.Simple({key: "orderId"})
70
+
71
+ // ─── Callers ────────────────────────────────────────────────────────────────
72
+
73
+ external asIdentity: 'a => Reventless.Identity.t = "%identity"
74
+
75
+ let cognito = (~userId): Reventless.Identity.t =>
76
+ asIdentity({"userId": userId, "username": userId, "groups": ["User"], "provider": "Cognito"})
77
+
78
+ let owner = cognito(~userId="cust-A")
79
+ let stranger = cognito(~userId="cust-B")
80
+
81
+ // ─── Generator → producer path ──────────────────────────────────────────────
82
+
83
+ let generated: ref<array<Message.commandJson>> = ref([])
84
+
85
+ let generate = ReventlessCore.CommandGenerator_Callback.makeGenerateCommand(
86
+ ~publishJsons=async cmds => generated := generated.contents->Array.concat(cmds),
87
+ ~serviceName=OrderSlice.name,
88
+ ~commandSchema=OrderSlice.commandSchema->S.castToUnknown,
89
+ ~componentKind=ReventlessCore.CommandGenerator_Callback.StateChangeSlice,
90
+ ~stripIdFromParams=false,
91
+ )
92
+
93
+ let envelope = async (~command, ~args, ~identity) => {
94
+ generated := []
95
+ let p: ReventlessCore.CommandGenerator.payload = Obj.magic({
96
+ "command": command,
97
+ "arguments": Obj.magic(Dict.fromArray(args)),
98
+ "meta": {"ip": [], "user": "test", "info": ""},
99
+ "identity": identity,
100
+ })
101
+ let _ = await generate(p)->Effect.runPromise
102
+ generated.contents->Array.getUnsafe(0)
103
+ }
104
+
105
+ let handlerOver = (eventLog): CommandTopic_Helpers.jsonCommandsHandler =>
106
+ stream =>
107
+ OrderHandler.handleCommands(
108
+ ~partitionTag,
109
+ eventLog,
110
+ stream->Stream.map((item: CommandTopic_Helpers.topicItem<JSON.t>) => {
111
+ ReventlessInfra.CommandTopic.command: item.command->Message.decodeCommand'(
112
+ Reventless.Id.String.schema,
113
+ OrderSlice.commandSchema,
114
+ ),
115
+ reference: item.reference,
116
+ }),
117
+ )
118
+
119
+ // The wire answer a client gets for one command: (`__typename`, `errorCode`, `cause`).
120
+ let send = async (eventLog, cmd: Message.commandJson) => {
121
+ let outcomes = await CommandTopic_Helpers.runInlineAndCollect([cmd], handlerOver(eventLog))
122
+ let json = outcomes->Array.getUnsafe(0)->CommandTopic_Helpers.commandOutcomeToJson
123
+ let str = key =>
124
+ json
125
+ ->JSON.Decode.object
126
+ ->Option.flatMap(o => o->Dict.get(key))
127
+ ->Option.flatMap(JSON.Decode.string)
128
+ (str("__typename"), str("errorCode"), str("cause"))
129
+ }
130
+
131
+ let str = JSON.Encode.string
132
+ let place = (~identity) =>
133
+ envelope(
134
+ ~command="PlaceOrder",
135
+ ~args=[("orderId", str("o-1")), ("customerId", str("cust-A"))],
136
+ ~identity,
137
+ )
138
+ let cancel = (~identity) =>
139
+ envelope(~command="CancelOrder", ~args=[("orderId", str("o-1"))], ~identity)
140
+
141
+ // ─── Backends ───────────────────────────────────────────────────────────────
142
+
143
+ type appendMode = Real | AlwaysConflict | StoreFails
144
+
145
+ // One fresh event log per scenario; `mode` replaces the store's append to fail
146
+ // the way the platform can.
147
+ let eventLogOn = async (~name, ~mode) => {
148
+ module Bus = LocalBus.Make()
149
+ module Storage = LocalDcbEventLogStorage.Make(Bus)
150
+ let s = Storage.make(
151
+ ~name,
152
+ ~indexes=[],
153
+ ~partitionTag=Reventless.DcbTag.Simple({key: "orderId"}),
154
+ ~opts,
155
+ )
156
+ let real = await s.operations->TestRunner.resolve
157
+ let storage: ReventlessCore.DcbEventLog_Adapter.operations = switch mode {
158
+ | Real => real
159
+ | AlwaysConflict => {...real, append: async (_, ~condition as _=?) => Error(Conflict)}
160
+ | StoreFails => {
161
+ ...real,
162
+ append: async (_, ~condition as _=?) => Error(StorageFailure("disk unavailable")),
163
+ }
164
+ }
165
+ module Log = ReventlessCore.DcbEventLog_Operations.Make({
166
+ let name = name
167
+ let serviceName = name
168
+ let storage = storage
169
+ let publishJson = async (_, _, _) => ()
170
+ })
171
+ let eventLog: ReventlessCore.DcbEventLog.operations = {
172
+ read: Log.read,
173
+ append: Log.append,
174
+ readStream: Log.readStream,
175
+ appendStream: Log.appendStream,
176
+ }
177
+ eventLog
178
+ }
179
+
180
+ let runUnderMemory = async fn => {
181
+ BackendState.setMemory()
182
+ await fn()
183
+ }
184
+
185
+ let runUnderSqlite = async fn => {
186
+ let db = SqliteDriver.openDb(~path=":memory:")
187
+ BackendState.setSqlite(~db, ~path=":memory:")
188
+ await fn()
189
+ BackendState.setMemory()
190
+ db->SqliteDriver.close
191
+ }
192
+
193
+ let _ = beforeEach(() => {
194
+ Reventless.OwnerScope.setElevatedGroups(["Admin"])
195
+ OrderHandler.resetCache()
196
+ })
197
+
198
+ let counter = ref(0)
199
+ let fresh = prefix => {
200
+ counter := counter.contents + 1
201
+ `${prefix}-${counter.contents->Int.toString}`
202
+ }
203
+
204
+ let backends = [("in-memory", runUnderMemory), ("SQLite", runUnderSqlite)]
205
+
206
+ backends->Array.forEach(((label, runUnder)) =>
207
+ describe(`a rejection's cause (${label} backend)`, () => {
208
+ testPromise(
209
+ "the model's no: a decide error arrives with DOMAIN",
210
+ async () =>
211
+ await runUnder(
212
+ async () => {
213
+ let log = await eventLogOn(~name=fresh("CauseDomain"), ~mode=Real)
214
+ let first = await send(log, await place(~identity=owner))
215
+ expect(first)->toEqual((Some("CommandAccepted"), None, None))
216
+ let again = await send(log, await place(~identity=owner))
217
+ expect(again)->toEqual((
218
+ Some("CommandRejected"),
219
+ Some("OrderAlreadyPlaced"),
220
+ Some("DOMAIN"),
221
+ ))
222
+ },
223
+ ),
224
+ )
225
+
226
+ testPromise(
227
+ "the caller's no: an ownership refusal arrives with ACCESS",
228
+ async () =>
229
+ await runUnder(
230
+ async () => {
231
+ let log = await eventLogOn(~name=fresh("CauseAccess"), ~mode=Real)
232
+ let _ = await send(log, await place(~identity=owner))
233
+ let refused = await send(log, await cancel(~identity=stranger))
234
+ expect(refused)->toEqual((Some("CommandRejected"), Some("Forbidden"), Some("ACCESS")))
235
+ },
236
+ ),
237
+ )
238
+
239
+ testPromise(
240
+ "the platform's no: an exhausted retry loop arrives with INFRASTRUCTURE",
241
+ async () =>
242
+ await runUnder(
243
+ async () => {
244
+ let log = await eventLogOn(~name=fresh("CauseConflict"), ~mode=AlwaysConflict)
245
+ let failed = await send(log, await place(~identity=owner))
246
+ expect(failed)->toEqual((
247
+ Some("CommandRejected"),
248
+ Some("Conflict"),
249
+ Some("INFRASTRUCTURE"),
250
+ ))
251
+ },
252
+ ),
253
+ )
254
+
255
+ testPromise(
256
+ "the platform's no: a failing store arrives with INFRASTRUCTURE",
257
+ async () =>
258
+ await runUnder(
259
+ async () => {
260
+ let log = await eventLogOn(~name=fresh("CauseStore"), ~mode=StoreFails)
261
+ let failed = await send(log, await place(~identity=owner))
262
+ expect(failed)->toEqual((
263
+ Some("CommandRejected"),
264
+ Some("AppendFailed"),
265
+ Some("INFRASTRUCTURE"),
266
+ ))
267
+ },
268
+ ),
269
+ )
270
+ })
271
+ )
@@ -0,0 +1,440 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Sury from "sury";
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 Effect from "effect/Effect";
8
+ import * as Stream from "effect/Stream";
9
+ import * as Owner$Reventless from "@reventlessdev/reventless-spec/src/components/Owner.res.mjs";
10
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
11
+ import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
12
+ import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
13
+ import * as LocalBus$ReventlessLocal from "../../../src/adapter/LocalBus.res.mjs";
14
+ import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
15
+ import * as BackendState$ReventlessLocal from "../../../src/adapter/BackendState.res.mjs";
16
+ import * as SqliteDriver$ReventlessLocal from "../../../src/adapter/SqliteDriver.res.mjs";
17
+ import * as CommandTopic_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Helpers.res.mjs";
18
+ import * as DcbEventLog_Operations$ReventlessCore from "@reventlessdev/reventless-core/src/components/DcbEventLog/DcbEventLog_Operations.res.mjs";
19
+ import * as LocalDcbEventLogStorage$ReventlessLocal from "../../../src/adapter/DcbEventLog/LocalDcbEventLogStorage.res.mjs";
20
+ import * as CommandGenerator_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandGenerator/CommandGenerator_Callback.res.mjs";
21
+ import * as StateChangeSlice_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs";
22
+
23
+ TestRunner$ReventlessLocal.setup();
24
+
25
+ let opts = {};
26
+
27
+ let name = "CauseOrders";
28
+
29
+ let moduleUrl = import.meta.url;
30
+
31
+ let eventSchema = Sury.union([
32
+ Sury.$schema(s => ({
33
+ TAG: "OrderPlaced",
34
+ orderId: s.m(DcbTag$Reventless.string),
35
+ customerId: s.m(Sury.string)
36
+ })),
37
+ Sury.$schema(s => ({
38
+ TAG: "OrderCancelled",
39
+ orderId: s.m(DcbTag$Reventless.string)
40
+ }))
41
+ ]);
42
+
43
+ let consumedEventSchema = Sury.union([
44
+ Sury.$schema(s => ({
45
+ TAG: "OrderPlaced",
46
+ orderId: s.m(DcbTag$Reventless.string),
47
+ customerId: s.m(Owner$Reventless.string)
48
+ })),
49
+ Sury.$schema(s => ({
50
+ TAG: "OrderCancelled",
51
+ orderId: s.m(DcbTag$Reventless.string)
52
+ }))
53
+ ]);
54
+
55
+ let commandSchema = Sury.union([
56
+ Sury.$schema(s => ({
57
+ TAG: "PlaceOrder",
58
+ orderId: s.m(DcbTag$Reventless.string),
59
+ customerId: s.m(Owner$Reventless.string)
60
+ })),
61
+ Sury.$schema(s => ({
62
+ TAG: "CancelOrder",
63
+ orderId: s.m(DcbTag$Reventless.string)
64
+ }))
65
+ ]);
66
+
67
+ let errorSchema = Sury.union([
68
+ Sury.literal("OrderAlreadyPlaced"),
69
+ Sury.literal("OrderNotFound")
70
+ ]);
71
+
72
+ function authorizationOf(param) {
73
+ return "AllowAuthenticated";
74
+ }
75
+
76
+ function commandTransition(param) {
77
+ return "Undeclared";
78
+ }
79
+
80
+ let traits = [];
81
+
82
+ let OrderSlice = {
83
+ name: name,
84
+ Id: undefined,
85
+ moduleUrl: moduleUrl,
86
+ eventSchema: eventSchema,
87
+ consumedEventSchema: consumedEventSchema,
88
+ commandSchema: commandSchema,
89
+ errorSchema: errorSchema,
90
+ authorizationOf: authorizationOf,
91
+ commandTransition: commandTransition,
92
+ traits: traits,
93
+ readConsistency: "EscalateOnRetry"
94
+ };
95
+
96
+ let initialState = {
97
+ placed: false,
98
+ cancelled: false
99
+ };
100
+
101
+ function evolve(state, event) {
102
+ if (event.TAG === "OrderPlaced") {
103
+ return {
104
+ placed: true,
105
+ cancelled: state.cancelled
106
+ };
107
+ } else {
108
+ return {
109
+ placed: state.placed,
110
+ cancelled: true
111
+ };
112
+ }
113
+ }
114
+
115
+ function decide(state, command) {
116
+ if (command.TAG === "PlaceOrder") {
117
+ if (state.placed) {
118
+ return {
119
+ TAG: "Error",
120
+ _0: "OrderAlreadyPlaced"
121
+ };
122
+ } else {
123
+ return {
124
+ TAG: "Ok",
125
+ _0: [{
126
+ TAG: "OrderPlaced",
127
+ orderId: command.orderId,
128
+ customerId: command.customerId
129
+ }]
130
+ };
131
+ }
132
+ } else if (state.placed) {
133
+ return {
134
+ TAG: "Ok",
135
+ _0: [{
136
+ TAG: "OrderCancelled",
137
+ orderId: command.orderId
138
+ }]
139
+ };
140
+ } else {
141
+ return {
142
+ TAG: "Error",
143
+ _0: "OrderNotFound"
144
+ };
145
+ }
146
+ }
147
+
148
+ let OrderDecision = {
149
+ initialState: initialState,
150
+ moduleUrl: moduleUrl,
151
+ evolve: evolve,
152
+ decide: decide
153
+ };
154
+
155
+ let OrderHandler = StateChangeSlice_Callback$ReventlessCore.Make({
156
+ name: name,
157
+ moduleUrl: moduleUrl,
158
+ consumedEventSchema: consumedEventSchema,
159
+ errorSchema: errorSchema,
160
+ eventSchema: eventSchema,
161
+ commandSchema: commandSchema,
162
+ authorizationOf: authorizationOf,
163
+ commandTransition: commandTransition,
164
+ traits: traits,
165
+ readConsistency: "EscalateOnRetry"
166
+ })({
167
+ initialState: initialState,
168
+ evolve: evolve,
169
+ decide: decide,
170
+ moduleUrl: moduleUrl
171
+ });
172
+
173
+ let partitionTag = {
174
+ TAG: "Simple",
175
+ _0: {
176
+ key: "orderId"
177
+ }
178
+ };
179
+
180
+ function cognito(userId) {
181
+ return {
182
+ userId: userId,
183
+ username: userId,
184
+ groups: ["User"],
185
+ provider: "Cognito"
186
+ };
187
+ }
188
+
189
+ let owner = {
190
+ userId: "cust-A",
191
+ username: "cust-A",
192
+ groups: ["User"],
193
+ provider: "Cognito"
194
+ };
195
+
196
+ let stranger = {
197
+ userId: "cust-B",
198
+ username: "cust-B",
199
+ groups: ["User"],
200
+ provider: "Cognito"
201
+ };
202
+
203
+ let generated = {
204
+ contents: []
205
+ };
206
+
207
+ let generate = CommandGenerator_Callback$ReventlessCore.makeGenerateCommand(async cmds => {
208
+ generated.contents = generated.contents.concat(cmds);
209
+ }, undefined, name, commandSchema, "StateChangeSlice", false, undefined);
210
+
211
+ async function envelope(command, args, identity) {
212
+ generated.contents = [];
213
+ let p = {
214
+ command: command,
215
+ arguments: Object.fromEntries(args),
216
+ meta: {
217
+ ip: [],
218
+ user: "test",
219
+ info: ""
220
+ },
221
+ identity: identity
222
+ };
223
+ await Effect.runPromise(generate(p));
224
+ return generated.contents[0];
225
+ }
226
+
227
+ function handlerOver(eventLog) {
228
+ return stream => OrderHandler.handleCommands(undefined, undefined, partitionTag, eventLog, Stream.map(stream, item => ({
229
+ command: Message$ReventlessCore.decodeCommand$p(item.command, Id$Reventless.$$String.schema, commandSchema),
230
+ reference: item.reference
231
+ })));
232
+ }
233
+
234
+ async function send(eventLog, cmd) {
235
+ let outcomes = await CommandTopic_Helpers$ReventlessCore.runInlineAndCollect([cmd], handlerOver(eventLog));
236
+ let json = CommandTopic_Helpers$ReventlessCore.commandOutcomeToJson(outcomes[0]);
237
+ let str = key => Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), o => o[key]), Stdlib_JSON.Decode.string);
238
+ return [
239
+ str("__typename"),
240
+ str("errorCode"),
241
+ str("cause")
242
+ ];
243
+ }
244
+
245
+ function str(prim) {
246
+ return prim;
247
+ }
248
+
249
+ function place(identity) {
250
+ return envelope("PlaceOrder", [
251
+ [
252
+ "orderId",
253
+ "o-1"
254
+ ],
255
+ [
256
+ "customerId",
257
+ "cust-A"
258
+ ]
259
+ ], identity);
260
+ }
261
+
262
+ function cancel(identity) {
263
+ return envelope("CancelOrder", [[
264
+ "orderId",
265
+ "o-1"
266
+ ]], identity);
267
+ }
268
+
269
+ async function eventLogOn(name, mode) {
270
+ let Bus = LocalBus$ReventlessLocal.Make({});
271
+ let Storage = LocalDcbEventLogStorage$ReventlessLocal.Make(Bus);
272
+ let s = Storage.make(name, [], {
273
+ TAG: "Simple",
274
+ _0: {
275
+ key: "orderId"
276
+ }
277
+ }, undefined, opts);
278
+ let real = await TestRunner$ReventlessLocal.resolve(s.operations);
279
+ let storage;
280
+ switch (mode) {
281
+ case "Real" :
282
+ storage = real;
283
+ break;
284
+ case "AlwaysConflict" :
285
+ storage = {
286
+ read: real.read,
287
+ append: async (param, param$1) => ({
288
+ TAG: "Error",
289
+ _0: "Conflict"
290
+ }),
291
+ readStream: real.readStream
292
+ };
293
+ break;
294
+ case "StoreFails" :
295
+ storage = {
296
+ read: real.read,
297
+ append: async (param, param$1) => ({
298
+ TAG: "Error",
299
+ _0: {
300
+ TAG: "StorageFailure",
301
+ _0: "disk unavailable"
302
+ }
303
+ }),
304
+ readStream: real.readStream
305
+ };
306
+ break;
307
+ }
308
+ let publishJson = async (param, param$1, param$2) => {};
309
+ let Log = DcbEventLog_Operations$ReventlessCore.Make({
310
+ name: name,
311
+ serviceName: name,
312
+ storage: storage,
313
+ publishJson: publishJson
314
+ });
315
+ return {
316
+ read: Log.read,
317
+ append: Log.append,
318
+ readStream: Log.readStream,
319
+ appendStream: Log.appendStream
320
+ };
321
+ }
322
+
323
+ async function runUnderMemory(fn) {
324
+ BackendState$ReventlessLocal.setMemory();
325
+ return await fn();
326
+ }
327
+
328
+ async function runUnderSqlite(fn) {
329
+ let db = SqliteDriver$ReventlessLocal.openDb(":memory:");
330
+ BackendState$ReventlessLocal.setSqlite(db, ":memory:");
331
+ await fn();
332
+ BackendState$ReventlessLocal.setMemory();
333
+ return SqliteDriver$ReventlessLocal.close(db);
334
+ }
335
+
336
+ globalThis.beforeEach(() => {
337
+ OwnerScope$Reventless.setElevatedGroups(["Admin"]);
338
+ OrderHandler.resetCache();
339
+ });
340
+
341
+ let counter = {
342
+ contents: 0
343
+ };
344
+
345
+ function fresh(prefix) {
346
+ counter.contents = counter.contents + 1 | 0;
347
+ return prefix + `-` + counter.contents.toString();
348
+ }
349
+
350
+ let backends = [
351
+ [
352
+ "in-memory",
353
+ runUnderMemory
354
+ ],
355
+ [
356
+ "SQLite",
357
+ runUnderSqlite
358
+ ]
359
+ ];
360
+
361
+ backends.forEach(param => {
362
+ let runUnder = param[1];
363
+ globalThis.describe(`a rejection's cause (` + param[0] + ` backend)`, () => {
364
+ globalThis.test("the model's no: a decide error arrives with DOMAIN", async () => await runUnder(async () => {
365
+ let log = await eventLogOn(fresh("CauseDomain"), "Real");
366
+ let first = await send(log, await place(owner));
367
+ globalThis.expect(first).toEqual([
368
+ "CommandAccepted",
369
+ undefined,
370
+ undefined
371
+ ]);
372
+ let again = await send(log, await place(owner));
373
+ globalThis.expect(again).toEqual([
374
+ "CommandRejected",
375
+ "OrderAlreadyPlaced",
376
+ "DOMAIN"
377
+ ]);
378
+ }));
379
+ globalThis.test("the caller's no: an ownership refusal arrives with ACCESS", async () => await runUnder(async () => {
380
+ let log = await eventLogOn(fresh("CauseAccess"), "Real");
381
+ await send(log, await place(owner));
382
+ let refused = await send(log, await cancel(stranger));
383
+ globalThis.expect(refused).toEqual([
384
+ "CommandRejected",
385
+ "Forbidden",
386
+ "ACCESS"
387
+ ]);
388
+ }));
389
+ globalThis.test("the platform's no: an exhausted retry loop arrives with INFRASTRUCTURE", async () => await runUnder(async () => {
390
+ let log = await eventLogOn(fresh("CauseConflict"), "AlwaysConflict");
391
+ let failed = await send(log, await place(owner));
392
+ globalThis.expect(failed).toEqual([
393
+ "CommandRejected",
394
+ "Conflict",
395
+ "INFRASTRUCTURE"
396
+ ]);
397
+ }));
398
+ globalThis.test("the platform's no: a failing store arrives with INFRASTRUCTURE", async () => await runUnder(async () => {
399
+ let log = await eventLogOn(fresh("CauseStore"), "StoreFails");
400
+ let failed = await send(log, await place(owner));
401
+ globalThis.expect(failed).toEqual([
402
+ "CommandRejected",
403
+ "AppendFailed",
404
+ "INFRASTRUCTURE"
405
+ ]);
406
+ }));
407
+ });
408
+ });
409
+
410
+ let Message;
411
+
412
+ let CommandTopic_Helpers;
413
+
414
+ export {
415
+ opts,
416
+ Message,
417
+ CommandTopic_Helpers,
418
+ OrderSlice,
419
+ OrderDecision,
420
+ OrderHandler,
421
+ partitionTag,
422
+ cognito,
423
+ owner,
424
+ stranger,
425
+ generated,
426
+ generate,
427
+ envelope,
428
+ handlerOver,
429
+ send,
430
+ str,
431
+ place,
432
+ cancel,
433
+ eventLogOn,
434
+ runUnderMemory,
435
+ runUnderSqlite,
436
+ counter,
437
+ fresh,
438
+ backends,
439
+ }
440
+ /* Not a pure module */