@reventlessdev/reventless-local 3.0.0-alpha.316 → 3.0.0-alpha.318
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 +16 -0
- package/package.json +11 -11
- package/src/LocalPlatformRegistry.res +19 -0
- package/src/LocalPlatformRegistry.res.mjs +19 -1
- package/src/Platform.res +24 -2
- package/src/Platform.res.mjs +22 -2
- package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res +38 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res.mjs +29 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +87 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res.mjs +76 -0
- package/src/adapter/DcbEventLog/LocalDcbEventLogStorage.res +8 -1
- package/src/adapter/DcbEventLog/LocalDcbEventLogStorage.res.mjs +17 -4
- package/src/adapter/EventLog/EventLogStorage_InMemory.res +49 -2
- package/src/adapter/EventLog/EventLogStorage_InMemory.res.mjs +57 -21
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res +85 -0
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res.mjs +50 -0
- package/src/adapter/EventLog/LocalEventLogStorage.res +3 -0
- package/src/adapter/EventLog/LocalEventLogStorage.res.mjs +6 -2
- package/src/adapter/LocalBus.res +32 -5
- package/src/adapter/LocalBus.res.mjs +27 -7
- package/src/adapter/LocalEventLogRead.res +477 -0
- package/src/adapter/LocalEventLogRead.res.mjs +507 -0
- package/src/adapter/LocalEventTap.res +18 -80
- package/src/adapter/LocalEventTap.res.mjs +19 -108
- package/src/adapter/LocalFeed.res +433 -0
- package/src/adapter/LocalFeed.res.mjs +419 -0
- package/src/adapter/LocalLogFeed.res +217 -0
- package/src/adapter/LocalLogFeed.res.mjs +295 -0
- package/src/adapter/LocalPgEventLogRead.res +85 -0
- package/src/adapter/LocalPgEventLogRead.res.mjs +91 -0
- package/src/adapter/LocalPlatformObserve.res +456 -0
- package/src/adapter/LocalPlatformObserve.res.mjs +505 -0
- package/src/adapter/LocalTapLines.res +147 -0
- package/src/adapter/LocalTapLines.res.mjs +229 -0
- package/src/adapter/PlatformGraphQL_Server.res +36 -3
- package/src/adapter/PlatformGraphQL_Server.res.mjs +15 -1
- package/src/reexport/CommandTopic.res.mjs +5 -2
- package/tests/adapter/GraphQLWsFixtures.mjs +46 -0
- package/tests/adapter/LocalEventLogReadTest.res +539 -0
- package/tests/adapter/LocalEventLogReadTest.res.mjs +466 -0
- package/tests/adapter/LocalFeedTest.res +314 -0
- package/tests/adapter/LocalFeedTest.res.mjs +329 -0
- package/tests/adapter/LocalPlatformObserveTest.res +165 -0
- package/tests/adapter/LocalPlatformObserveTest.res.mjs +183 -0
- package/tests/adapter/LocalTapLinesTest.res +182 -0
- package/tests/adapter/LocalTapLinesTest.res.mjs +242 -0
- package/tests/adapter/PlatformSubscriptionsTest.res +123 -0
- package/tests/adapter/PlatformSubscriptionsTest.res.mjs +132 -0
- package/tests/components/automationslice/AutomationSliceCallbackTest.res +80 -0
- package/tests/components/automationslice/AutomationSliceCallbackTest.res.mjs +81 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +17 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res.mjs +24 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockTest.res +30 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockTest.res.mjs +28 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res +58 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res.mjs +71 -19
- package/tests/components/outboundtranslationslice/OutboundTranslationSlicePiiTest.res.mjs +7 -7
|
@@ -191,7 +191,11 @@ let dcbEventLog = DcbLogMaker.make(
|
|
|
191
191
|
// CommandTopic without the runtime wiring overhead.)
|
|
192
192
|
// ─────────────────────────────────────────────────────────────
|
|
193
193
|
|
|
194
|
+
// Every command sent, in order, so a test can read the metas they carried.
|
|
195
|
+
let sentCommands: ref<array<Reventless.Message.commandJson>> = ref([])
|
|
196
|
+
|
|
194
197
|
let publishJsons: ReventlessInfra.CommandTopic.publishJsons = async cmdJsons => {
|
|
198
|
+
sentCommands := sentCommands.contents->Array.concat(cmdJsons)
|
|
195
199
|
let _ = await cmdJsons
|
|
196
200
|
->Array.map(async cmdJson => {
|
|
197
201
|
let typeName = switch cmdJson.commandJson {
|
|
@@ -292,3 +296,16 @@ let readEventTypes = async (orderId: string) => {
|
|
|
292
296
|
)
|
|
293
297
|
result.events->Array.map(e => e.eventType)
|
|
294
298
|
}
|
|
299
|
+
|
|
300
|
+
let readEvents = async (orderId: string) => {
|
|
301
|
+
let logOps = await dcbEventLog->DcbLogMaker.operations->TestRunner.resolve
|
|
302
|
+
let result = await logOps.read(
|
|
303
|
+
~query=[{tags: [{Reventless.DcbTag.key: "orderId", value: orderId}]}],
|
|
304
|
+
)
|
|
305
|
+
result.events
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
let pause = (ms: int): promise<unit> =>
|
|
309
|
+
Promise.make((resolve, _) => {
|
|
310
|
+
let _ = setTimeout(() => resolve(), ms)
|
|
311
|
+
})
|
|
@@ -323,7 +323,12 @@ let dcbEventLog = DcbLogMaker.make("TestLog", undefined, {
|
|
|
323
323
|
}
|
|
324
324
|
}, undefined);
|
|
325
325
|
|
|
326
|
+
let sentCommands = {
|
|
327
|
+
contents: []
|
|
328
|
+
};
|
|
329
|
+
|
|
326
330
|
async function publishJsons(cmdJsons) {
|
|
331
|
+
sentCommands.contents = sentCommands.contents.concat(cmdJsons);
|
|
327
332
|
await Promise.all(cmdJsons.map(async cmdJson => {
|
|
328
333
|
let dict = cmdJson.commandJson;
|
|
329
334
|
let typeName;
|
|
@@ -459,6 +464,22 @@ async function readEventTypes(orderId) {
|
|
|
459
464
|
return result.events.map(e => e.eventType);
|
|
460
465
|
}
|
|
461
466
|
|
|
467
|
+
async function readEvents(orderId) {
|
|
468
|
+
let logOps = await TestRunner$ReventlessLocal.resolve(DcbLogMaker.operations(dcbEventLog));
|
|
469
|
+
return (await logOps.read([{
|
|
470
|
+
tags: [{
|
|
471
|
+
key: "orderId",
|
|
472
|
+
value: orderId
|
|
473
|
+
}]
|
|
474
|
+
}], undefined)).events;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function pause(ms) {
|
|
478
|
+
return new Promise((resolve, param) => {
|
|
479
|
+
setTimeout(() => resolve(), ms);
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
462
483
|
export {
|
|
463
484
|
PlaceSpec,
|
|
464
485
|
PlaceDecision,
|
|
@@ -471,6 +492,7 @@ export {
|
|
|
471
492
|
Bus,
|
|
472
493
|
DcbLogMaker,
|
|
473
494
|
dcbEventLog,
|
|
495
|
+
sentCommands,
|
|
474
496
|
publishJsons,
|
|
475
497
|
publishJsonsOutput,
|
|
476
498
|
PlaceMaker,
|
|
@@ -486,5 +508,7 @@ export {
|
|
|
486
508
|
placeCmdJson,
|
|
487
509
|
withTimeout,
|
|
488
510
|
readEventTypes,
|
|
511
|
+
readEvents,
|
|
512
|
+
pause,
|
|
489
513
|
}
|
|
490
514
|
/* moduleUrl Not a pure module */
|
|
@@ -44,4 +44,34 @@ describe("AutomationSlice self-deadlock regression:", () => {
|
|
|
44
44
|
expect(eventTypes->Array.includes("Placed"))->toBe(true)
|
|
45
45
|
expect(eventTypes->Array.includes("Shipped"))->toBe(true)
|
|
46
46
|
})
|
|
47
|
+
|
|
48
|
+
// The automation's command continues the flow of the event that created its
|
|
49
|
+
// row: one correlation id from the first command to the last event, and each
|
|
50
|
+
// message caused by the one before it.
|
|
51
|
+
testPromise("the automation's command and its events stay in the event's flow", async () => {
|
|
52
|
+
sentCommands := []
|
|
53
|
+
let _ = await withTimeout(publishJsons([placeCmdJson("order-2")]), ~ms=1500)
|
|
54
|
+
let shipped = async () =>
|
|
55
|
+
(await readEvents("order-2"))->Array.find(e => e.eventType == "Shipped")
|
|
56
|
+
let waited = ref(0)
|
|
57
|
+
while (await shipped())->Option.isNone && waited.contents < 100 {
|
|
58
|
+
await pause(10)
|
|
59
|
+
waited := waited.contents + 1
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let events = await readEvents("order-2")
|
|
63
|
+
let placed = events->Array.find(e => e.eventType == "Placed")->Option.getOrThrow
|
|
64
|
+
let shipped = events->Array.find(e => e.eventType == "Shipped")->Option.getOrThrow
|
|
65
|
+
let ship =
|
|
66
|
+
sentCommands.contents
|
|
67
|
+
->Array.find(c => c.id == "order-2" && c.meta.msgId != TestFixtures.testMeta.msgId)
|
|
68
|
+
->Option.getOrThrow
|
|
69
|
+
|
|
70
|
+
expect(placed.meta.correlationId)->toBe(TestFixtures.testMeta.correlationId)
|
|
71
|
+
expect(ship.meta.correlationId)->toBe(TestFixtures.testMeta.correlationId)
|
|
72
|
+
expect(shipped.meta.correlationId)->toBe(TestFixtures.testMeta.correlationId)
|
|
73
|
+
expect(ship.meta.causationId)->toEqual(Some(placed.meta.msgId))
|
|
74
|
+
expect(shipped.meta.causationId)->toEqual(Some(ship.meta.msgId))
|
|
75
|
+
expect(ship.meta.user)->toEqual(TestFixtures.testMeta.user)
|
|
76
|
+
})
|
|
47
77
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
3
4
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
4
5
|
import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
|
|
6
|
+
import * as TestFixtures$ReventlessLocal from "../../TestFixtures.res.mjs";
|
|
5
7
|
import * as AutomationSliceSelfDeadlockFixtures$ReventlessLocal from "./AutomationSliceSelfDeadlockFixtures.res.mjs";
|
|
6
8
|
|
|
7
9
|
globalThis.describe("AutomationSlice self-deadlock regression:", () => {
|
|
@@ -26,6 +28,32 @@ globalThis.describe("AutomationSlice self-deadlock regression:", () => {
|
|
|
26
28
|
globalThis.expect(eventTypes.includes("Placed")).toBe(true);
|
|
27
29
|
globalThis.expect(eventTypes.includes("Shipped")).toBe(true);
|
|
28
30
|
});
|
|
31
|
+
globalThis.test("the automation's command and its events stay in the event's flow", async () => {
|
|
32
|
+
AutomationSliceSelfDeadlockFixtures$ReventlessLocal.sentCommands.contents = [];
|
|
33
|
+
await AutomationSliceSelfDeadlockFixtures$ReventlessLocal.withTimeout(AutomationSliceSelfDeadlockFixtures$ReventlessLocal.publishJsons([AutomationSliceSelfDeadlockFixtures$ReventlessLocal.placeCmdJson("order-2")]), 1500);
|
|
34
|
+
let shipped = async () => (await AutomationSliceSelfDeadlockFixtures$ReventlessLocal.readEvents("order-2")).find(e => e.eventType === "Shipped");
|
|
35
|
+
let waited = 0;
|
|
36
|
+
while (Stdlib_Option.isNone(await shipped()) && waited < 100) {
|
|
37
|
+
await AutomationSliceSelfDeadlockFixtures$ReventlessLocal.pause(10);
|
|
38
|
+
waited = waited + 1 | 0;
|
|
39
|
+
};
|
|
40
|
+
let events = await AutomationSliceSelfDeadlockFixtures$ReventlessLocal.readEvents("order-2");
|
|
41
|
+
let placed = Stdlib_Option.getOrThrow(events.find(e => e.eventType === "Placed"), undefined);
|
|
42
|
+
let shipped$1 = Stdlib_Option.getOrThrow(events.find(e => e.eventType === "Shipped"), undefined);
|
|
43
|
+
let ship = Stdlib_Option.getOrThrow(AutomationSliceSelfDeadlockFixtures$ReventlessLocal.sentCommands.contents.find(c => {
|
|
44
|
+
if (c.id === "order-2") {
|
|
45
|
+
return c.meta.msgId !== TestFixtures$ReventlessLocal.testMeta.msgId;
|
|
46
|
+
} else {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}), undefined);
|
|
50
|
+
globalThis.expect(placed.meta.correlationId).toBe(TestFixtures$ReventlessLocal.testMeta.correlationId);
|
|
51
|
+
globalThis.expect(ship.meta.correlationId).toBe(TestFixtures$ReventlessLocal.testMeta.correlationId);
|
|
52
|
+
globalThis.expect(shipped$1.meta.correlationId).toBe(TestFixtures$ReventlessLocal.testMeta.correlationId);
|
|
53
|
+
globalThis.expect(ship.meta.causationId).toEqual(placed.meta.msgId);
|
|
54
|
+
globalThis.expect(shipped$1.meta.causationId).toEqual(ship.meta.msgId);
|
|
55
|
+
globalThis.expect(ship.meta.user).toEqual(TestFixtures$ReventlessLocal.testMeta.user);
|
|
56
|
+
});
|
|
29
57
|
});
|
|
30
58
|
|
|
31
59
|
/* Not a pure module */
|
|
@@ -472,4 +472,62 @@ describe("OutboundTranslationSlice Callback", () => {
|
|
|
472
472
|
},
|
|
473
473
|
)
|
|
474
474
|
})
|
|
475
|
+
|
|
476
|
+
// A row keeps the event that created it, so the command sent back continues
|
|
477
|
+
// the event's flow; without a meta, or for a row stored before, it starts one.
|
|
478
|
+
describe("the flow", () => {
|
|
479
|
+
let eventMeta: Reventless.Message.meta = {
|
|
480
|
+
service: "Payments",
|
|
481
|
+
time: "2026-01-01T00:00:00Z",
|
|
482
|
+
user: "user-7",
|
|
483
|
+
msgId: "evt-1",
|
|
484
|
+
correlationId: "flow-1",
|
|
485
|
+
}
|
|
486
|
+
let publishInto = (published: ref<array<Reventless.Message.commandJson>>) =>
|
|
487
|
+
async cmds => published := published.contents->Array.concat(cmds)
|
|
488
|
+
|
|
489
|
+
testPromise(
|
|
490
|
+
"the command sent back derives its meta from the event",
|
|
491
|
+
async () => {
|
|
492
|
+
await CommandBackCallback.phase1(
|
|
493
|
+
[("evt", PaymentReceived({orderId: "ord-1", amount: 50.0}))],
|
|
494
|
+
~metas=[Some(eventMeta)],
|
|
495
|
+
~capabilities=Reventless.Capabilities.none,
|
|
496
|
+
)
|
|
497
|
+
let published = ref([])
|
|
498
|
+
await CommandBackCallback.phase2(
|
|
499
|
+
publishInto(published),
|
|
500
|
+
~capabilities=Reventless.Capabilities.none,
|
|
501
|
+
)
|
|
502
|
+
let meta = (published.contents->Array.getUnsafe(0)).meta
|
|
503
|
+
expect(meta.correlationId)->toBe("flow-1")
|
|
504
|
+
expect(meta.causationId)->toEqual(Some("evt-1"))
|
|
505
|
+
expect(meta.user)->toEqual(Some("user-7"))
|
|
506
|
+
expect(meta.service)->toBe("ConfirmPayment")
|
|
507
|
+
},
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
testPromise(
|
|
511
|
+
"without a meta, or from a stored row without a source, it starts a new flow",
|
|
512
|
+
async () => {
|
|
513
|
+
await CommandBackCallback.phase1(
|
|
514
|
+
[("evt", PaymentReceived({orderId: "ord-1", amount: 50.0}))],
|
|
515
|
+
~capabilities=Reventless.Capabilities.none,
|
|
516
|
+
)
|
|
517
|
+
let stored =
|
|
518
|
+
JSON.parseOrThrow(`{"item":{"orderId":"ord-2","amount":5},"status":"Pending","createdAt":"2026-01-01T00:00:00Z","retryCount":0}`)->Reventless.Util_Sury.fromJson(
|
|
519
|
+
todoRowSchema,
|
|
520
|
+
)
|
|
521
|
+
expect(stored.source)->toEqual(None)
|
|
522
|
+
CommandBackCallback.todoItems->Dict.set("ord-2", stored)
|
|
523
|
+
let published = ref([])
|
|
524
|
+
await CommandBackCallback.phase2(
|
|
525
|
+
publishInto(published),
|
|
526
|
+
~capabilities=Reventless.Capabilities.none,
|
|
527
|
+
)
|
|
528
|
+
expect(published.contents->Array.length)->toBe(2)
|
|
529
|
+
published.contents->Array.forEach(c => expect(c.meta.causationId)->toEqual(None))
|
|
530
|
+
},
|
|
531
|
+
)
|
|
532
|
+
})
|
|
475
533
|
})
|
package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
4
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
5
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
6
|
+
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
6
7
|
import * as Capabilities$Reventless from "@reventlessdev/reventless-spec/src/semantic/Capabilities.res.mjs";
|
|
7
8
|
import * as OutboundTranslationSliceFixtures$ReventlessLocal from "./OutboundTranslationSliceFixtures.res.mjs";
|
|
8
9
|
import * as OutboundTranslationSlice_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs";
|
|
@@ -90,7 +91,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
90
91
|
orderId: "ord-1",
|
|
91
92
|
email: "test@example.com"
|
|
92
93
|
}
|
|
93
|
-
]], Capabilities$Reventless.none);
|
|
94
|
+
]], undefined, Capabilities$Reventless.none);
|
|
94
95
|
let items = FireForgetCallback.todoItems;
|
|
95
96
|
globalThis.expect(Object.entries(items).length).toBe(1);
|
|
96
97
|
let row = items["ord-1"];
|
|
@@ -107,7 +108,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
107
108
|
orderId: "ord-1",
|
|
108
109
|
email: "test@example.com"
|
|
109
110
|
}
|
|
110
|
-
]], Capabilities$Reventless.none);
|
|
111
|
+
]], undefined, Capabilities$Reventless.none);
|
|
111
112
|
await FireForgetCallback.phase1([[
|
|
112
113
|
"evt",
|
|
113
114
|
{
|
|
@@ -115,7 +116,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
115
116
|
orderId: "ord-1",
|
|
116
117
|
email: "other@example.com"
|
|
117
118
|
}
|
|
118
|
-
]], Capabilities$Reventless.none);
|
|
119
|
+
]], undefined, Capabilities$Reventless.none);
|
|
119
120
|
globalThis.expect(Object.entries(FireForgetCallback.todoItems).length).toBe(1);
|
|
120
121
|
});
|
|
121
122
|
globalThis.test("multiple different events create multiple items", async () => {
|
|
@@ -136,7 +137,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
136
137
|
email: "b@example.com"
|
|
137
138
|
}
|
|
138
139
|
]
|
|
139
|
-
], Capabilities$Reventless.none);
|
|
140
|
+
], undefined, Capabilities$Reventless.none);
|
|
140
141
|
globalThis.expect(Object.entries(FireForgetCallback.todoItems).length).toBe(2);
|
|
141
142
|
});
|
|
142
143
|
});
|
|
@@ -149,7 +150,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
149
150
|
orderId: "ord-1",
|
|
150
151
|
email: "test@example.com"
|
|
151
152
|
}
|
|
152
|
-
]], Capabilities$Reventless.none);
|
|
153
|
+
]], undefined, Capabilities$Reventless.none);
|
|
153
154
|
let publishedCommands = {
|
|
154
155
|
contents: []
|
|
155
156
|
};
|
|
@@ -181,7 +182,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
181
182
|
orderId: "ord-1",
|
|
182
183
|
amount: 50.0
|
|
183
184
|
}
|
|
184
|
-
]], Capabilities$Reventless.none);
|
|
185
|
+
]], undefined, Capabilities$Reventless.none);
|
|
185
186
|
let publishedCommands = {
|
|
186
187
|
contents: []
|
|
187
188
|
};
|
|
@@ -203,7 +204,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
203
204
|
orderId: "ord-1",
|
|
204
205
|
amount: 50.0
|
|
205
206
|
}
|
|
206
|
-
]], Capabilities$Reventless.none);
|
|
207
|
+
]], undefined, Capabilities$Reventless.none);
|
|
207
208
|
let publishedCommands = {
|
|
208
209
|
contents: []
|
|
209
210
|
};
|
|
@@ -226,7 +227,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
226
227
|
orderId: "ord-1",
|
|
227
228
|
amount: 50.0
|
|
228
229
|
}
|
|
229
|
-
]], Capabilities$Reventless.none);
|
|
230
|
+
]], undefined, Capabilities$Reventless.none);
|
|
230
231
|
let mockPublish = async _cmds => {};
|
|
231
232
|
await CommandBackCallback.phase2(mockPublish, Capabilities$Reventless.none);
|
|
232
233
|
let row = Stdlib_Option.getOrThrow(CommandBackCallback.todoItems["ord-1"], undefined);
|
|
@@ -245,7 +246,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
245
246
|
orderId: "ord-1",
|
|
246
247
|
amount: 50.0
|
|
247
248
|
}
|
|
248
|
-
]], Capabilities$Reventless.none);
|
|
249
|
+
]], undefined, Capabilities$Reventless.none);
|
|
249
250
|
let mockPublish = async _cmds => {};
|
|
250
251
|
await CommandBackCallback.phase2(mockPublish, Capabilities$Reventless.none);
|
|
251
252
|
let row1 = Stdlib_Option.getOrThrow(CommandBackCallback.todoItems["ord-1"], undefined);
|
|
@@ -283,7 +284,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
283
284
|
orderId: "ord-1",
|
|
284
285
|
amount: 50.0
|
|
285
286
|
}
|
|
286
|
-
]], Capabilities$Reventless.none);
|
|
287
|
+
]], undefined, Capabilities$Reventless.none);
|
|
287
288
|
let mockPublish = async _cmds => {};
|
|
288
289
|
await CommandBackCallback.phase2(mockPublish, Capabilities$Reventless.none);
|
|
289
290
|
globalThis.expect(Stdlib_Option.getOrThrow(CommandBackCallback.todoItems["ord-1"], undefined).status).toBe("Failed");
|
|
@@ -315,7 +316,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
315
316
|
orderId: "ord-1",
|
|
316
317
|
amount: 50.0
|
|
317
318
|
}
|
|
318
|
-
]], Capabilities$Reventless.none);
|
|
319
|
+
]], undefined, Capabilities$Reventless.none);
|
|
319
320
|
let row = Stdlib_Option.getOrThrow(CommandBackCallback.todoItems["ord-1"], undefined);
|
|
320
321
|
let newrecord = {...row};
|
|
321
322
|
newrecord.retryCount = 2;
|
|
@@ -336,7 +337,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
336
337
|
orderId: "ord-1",
|
|
337
338
|
amount: 50.0
|
|
338
339
|
}
|
|
339
|
-
]], Capabilities$Reventless.none);
|
|
340
|
+
]], undefined, Capabilities$Reventless.none);
|
|
340
341
|
let publish = async _cmds => {};
|
|
341
342
|
await CommandBackCallback.phase2(publish, Capabilities$Reventless.none);
|
|
342
343
|
await CommandBackCallback.phase2(publish, Capabilities$Reventless.none);
|
|
@@ -352,7 +353,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
352
353
|
orderId: "ord-1",
|
|
353
354
|
amount: 50.0
|
|
354
355
|
}
|
|
355
|
-
]], Capabilities$Reventless.none);
|
|
356
|
+
]], undefined, Capabilities$Reventless.none);
|
|
356
357
|
globalThis.expect(Stdlib_Option.getOrThrow(CommandBackCallback.todoItems["ord-1"], undefined).maxRetries).toEqual(OutboundTranslationSliceFixtures$ReventlessLocal.ProcessPaymentSpec.maxRetries);
|
|
357
358
|
});
|
|
358
359
|
globalThis.test("a slice that answers onExhausted has its command published", async () => {
|
|
@@ -374,7 +375,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
374
375
|
orderId: "ord-1",
|
|
375
376
|
amount: 50.0
|
|
376
377
|
}
|
|
377
|
-
]], Capabilities$Reventless.none);
|
|
378
|
+
]], undefined, Capabilities$Reventless.none);
|
|
378
379
|
let published = {
|
|
379
380
|
contents: []
|
|
380
381
|
};
|
|
@@ -407,7 +408,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
407
408
|
orderId: "ord-1",
|
|
408
409
|
amount: 50.0
|
|
409
410
|
}
|
|
410
|
-
]], Capabilities$Reventless.none);
|
|
411
|
+
]], undefined, Capabilities$Reventless.none);
|
|
411
412
|
let publish = async _cmds => {};
|
|
412
413
|
await CommandBackCallback.phase2(publish, Capabilities$Reventless.none);
|
|
413
414
|
await CommandBackCallback.phase2(publish, Capabilities$Reventless.none);
|
|
@@ -425,7 +426,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
425
426
|
orderId: "ord-1",
|
|
426
427
|
amount: 50.0
|
|
427
428
|
}
|
|
428
|
-
]], Capabilities$Reventless.none);
|
|
429
|
+
]], undefined, Capabilities$Reventless.none);
|
|
429
430
|
let published = {
|
|
430
431
|
contents: []
|
|
431
432
|
};
|
|
@@ -456,7 +457,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
456
457
|
orderId: "ord-1",
|
|
457
458
|
amount: 50.0
|
|
458
459
|
}
|
|
459
|
-
]], Capabilities$Reventless.none);
|
|
460
|
+
]], undefined, Capabilities$Reventless.none);
|
|
460
461
|
let failOnAbandonment = async _cmds => Stdlib_JsError.throwWithMessage("topic unavailable");
|
|
461
462
|
await CommandBackCallback.phase2(failOnAbandonment, Capabilities$Reventless.none);
|
|
462
463
|
await CommandBackCallback.phase2(failOnAbandonment, Capabilities$Reventless.none);
|
|
@@ -479,7 +480,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
479
480
|
orderId: "ord-1",
|
|
480
481
|
amount: 50.0
|
|
481
482
|
}
|
|
482
|
-
]], Capabilities$Reventless.none);
|
|
483
|
+
]], undefined, Capabilities$Reventless.none);
|
|
483
484
|
let row = Stdlib_Option.getOrThrow(CommandBackCallback.todoItems["ord-1"], undefined);
|
|
484
485
|
let newrecord = {...row};
|
|
485
486
|
newrecord.retryCount = 2;
|
|
@@ -535,7 +536,7 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
535
536
|
amount: 75.0
|
|
536
537
|
}
|
|
537
538
|
]
|
|
538
|
-
], Capabilities$Reventless.none);
|
|
539
|
+
], undefined, Capabilities$Reventless.none);
|
|
539
540
|
let publishedCommands = {
|
|
540
541
|
contents: []
|
|
541
542
|
};
|
|
@@ -550,6 +551,57 @@ globalThis.describe("OutboundTranslationSlice Callback", () => {
|
|
|
550
551
|
globalThis.expect(publishedCommands.contents.length).toBe(1);
|
|
551
552
|
});
|
|
552
553
|
});
|
|
554
|
+
globalThis.describe("the flow", () => {
|
|
555
|
+
let publishInto = published => (async cmds => {
|
|
556
|
+
published.contents = published.contents.concat(cmds);
|
|
557
|
+
});
|
|
558
|
+
globalThis.test("the command sent back derives its meta from the event", async () => {
|
|
559
|
+
await CommandBackCallback.phase1([[
|
|
560
|
+
"evt",
|
|
561
|
+
{
|
|
562
|
+
TAG: "PaymentReceived",
|
|
563
|
+
orderId: "ord-1",
|
|
564
|
+
amount: 50.0
|
|
565
|
+
}
|
|
566
|
+
]], [{
|
|
567
|
+
service: "Payments",
|
|
568
|
+
time: "2026-01-01T00:00:00Z",
|
|
569
|
+
user: "user-7",
|
|
570
|
+
msgId: "evt-1",
|
|
571
|
+
correlationId: "flow-1"
|
|
572
|
+
}], Capabilities$Reventless.none);
|
|
573
|
+
let published = {
|
|
574
|
+
contents: []
|
|
575
|
+
};
|
|
576
|
+
await CommandBackCallback.phase2(publishInto(published), Capabilities$Reventless.none);
|
|
577
|
+
let meta = published.contents[0].meta;
|
|
578
|
+
globalThis.expect(meta.correlationId).toBe("flow-1");
|
|
579
|
+
globalThis.expect(meta.causationId).toEqual("evt-1");
|
|
580
|
+
globalThis.expect(meta.user).toEqual("user-7");
|
|
581
|
+
globalThis.expect(meta.service).toBe("ConfirmPayment");
|
|
582
|
+
});
|
|
583
|
+
globalThis.test("without a meta, or from a stored row without a source, it starts a new flow", async () => {
|
|
584
|
+
await CommandBackCallback.phase1([[
|
|
585
|
+
"evt",
|
|
586
|
+
{
|
|
587
|
+
TAG: "PaymentReceived",
|
|
588
|
+
orderId: "ord-1",
|
|
589
|
+
amount: 50.0
|
|
590
|
+
}
|
|
591
|
+
]], undefined, Capabilities$Reventless.none);
|
|
592
|
+
let stored = Util_Sury$Reventless.fromJson(JSON.parse(`{"item":{"orderId":"ord-2","amount":5},"status":"Pending","createdAt":"2026-01-01T00:00:00Z","retryCount":0}`), OutboundTranslationSlice_Callback$ReventlessCore.todoRowSchema);
|
|
593
|
+
globalThis.expect(stored.source).toEqual(undefined);
|
|
594
|
+
CommandBackCallback.todoItems["ord-2"] = stored;
|
|
595
|
+
let published = {
|
|
596
|
+
contents: []
|
|
597
|
+
};
|
|
598
|
+
await CommandBackCallback.phase2(publishInto(published), Capabilities$Reventless.none);
|
|
599
|
+
globalThis.expect(published.contents.length).toBe(2);
|
|
600
|
+
published.contents.forEach(c => {
|
|
601
|
+
globalThis.expect(c.meta.causationId).toEqual(undefined);
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
});
|
|
553
605
|
});
|
|
554
606
|
|
|
555
607
|
export {
|
|
@@ -225,7 +225,7 @@ globalThis.describe("an outbound work item with personal data", () => {
|
|
|
225
225
|
await Callback.phase1([[
|
|
226
226
|
"c-1",
|
|
227
227
|
given
|
|
228
|
-
]], keyed);
|
|
228
|
+
]], undefined, keyed);
|
|
229
229
|
let stored = Stdlib_Option.getOr(JSON.stringify(Object.entries(Callback.todoItems)), "");
|
|
230
230
|
globalThis.expect(stored.includes("ada@example.org")).toBe(false);
|
|
231
231
|
let match = Object.entries(Callback.todoItems);
|
|
@@ -241,18 +241,18 @@ globalThis.describe("an outbound work item with personal data", () => {
|
|
|
241
241
|
await Callback.phase1([[
|
|
242
242
|
"c-1",
|
|
243
243
|
given
|
|
244
|
-
]], keyed);
|
|
244
|
+
]], undefined, keyed);
|
|
245
245
|
await Callback.phase1([[
|
|
246
246
|
"c-1",
|
|
247
247
|
given
|
|
248
|
-
]], keyed);
|
|
248
|
+
]], undefined, keyed);
|
|
249
249
|
globalThis.expect(Object.entries(Callback.todoItems).length).toBe(1);
|
|
250
250
|
});
|
|
251
251
|
globalThis.test("is opened before it is translated", async () => {
|
|
252
252
|
await Callback.phase1([[
|
|
253
253
|
"c-1",
|
|
254
254
|
given
|
|
255
|
-
]], keyed);
|
|
255
|
+
]], undefined, keyed);
|
|
256
256
|
await Callback.phase2(publish, keyed);
|
|
257
257
|
globalThis.expect(sent.contents).toEqual(["ada@example.org"]);
|
|
258
258
|
});
|
|
@@ -260,7 +260,7 @@ globalThis.describe("an outbound work item with personal data", () => {
|
|
|
260
260
|
await Callback.phase1([[
|
|
261
261
|
"c-1",
|
|
262
262
|
given
|
|
263
|
-
]], keyed);
|
|
263
|
+
]], undefined, keyed);
|
|
264
264
|
erased.contents = true;
|
|
265
265
|
await Callback.phase2(publish, keyed);
|
|
266
266
|
globalThis.expect(sent.contents).toEqual([]);
|
|
@@ -270,7 +270,7 @@ globalThis.describe("an outbound work item with personal data", () => {
|
|
|
270
270
|
await Callback.phase1([[
|
|
271
271
|
"c-1",
|
|
272
272
|
given
|
|
273
|
-
]], Capabilities$Reventless.none);
|
|
273
|
+
]], undefined, Capabilities$Reventless.none);
|
|
274
274
|
globalThis.expect(Object.entries(Callback.todoItems).map(prim => prim[0])).toEqual(["c-1:ada@example.org"]);
|
|
275
275
|
});
|
|
276
276
|
globalThis.test("is not stored at all where encryption is required and nothing can fingerprint it", async () => {
|
|
@@ -278,7 +278,7 @@ globalThis.describe("an outbound work item with personal data", () => {
|
|
|
278
278
|
await Callback.phase1([[
|
|
279
279
|
"c-1",
|
|
280
280
|
given
|
|
281
|
-
]], Capabilities$Reventless.none);
|
|
281
|
+
]], undefined, Capabilities$Reventless.none);
|
|
282
282
|
globalThis.expect(Object.entries(Callback.todoItems)).toEqual([]);
|
|
283
283
|
});
|
|
284
284
|
});
|