@reventlessdev/reventless-core 3.0.0-alpha.137 → 3.0.0-alpha.139
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 +14 -0
- package/package.json +5 -5
- package/src/components/Aggregate/Aggregate_Callback.res +158 -12
- package/src/components/Aggregate/Aggregate_Callback.res.mjs +131 -9
- package/src/components/DcbEventLog/DcbEventLog_Adapter.res +1 -1
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +10 -5
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +24 -14
- package/tests/aggregate/AggregateSnapshotTest.res +357 -0
- package/tests/aggregate/AggregateSnapshotTest.res.mjs +645 -0
- package/tests/dcb/DcbFixtures.res +2 -2
- package/tests/dcb/DcbFixtures.res.mjs +2 -2
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
|
|
5
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
6
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Stream$1 from "effect/Stream";
|
|
9
|
+
import * as Aggregate_Callback$ReventlessCore from "../../src/components/Aggregate/Aggregate_Callback.res.mjs";
|
|
10
|
+
|
|
11
|
+
S.enableJson();
|
|
12
|
+
|
|
13
|
+
let name = "SnapshotTestAggregate";
|
|
14
|
+
|
|
15
|
+
let commandSchema = S.union([
|
|
16
|
+
S.schema(s => ({
|
|
17
|
+
TAG: "Add",
|
|
18
|
+
item: s.m(S.string)
|
|
19
|
+
})),
|
|
20
|
+
S.literal("Noop")
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
let eventSchema = S.schema(s => ({
|
|
24
|
+
TAG: "Added",
|
|
25
|
+
item: s.m(S.string)
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
let errorSchema = S.literal("Never");
|
|
29
|
+
|
|
30
|
+
let moduleUrl = import.meta.url;
|
|
31
|
+
|
|
32
|
+
function commandAuthorization(param) {
|
|
33
|
+
return "AllowAuthenticated";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let AggSpec = {
|
|
37
|
+
Id: undefined,
|
|
38
|
+
name: name,
|
|
39
|
+
commandSchema: commandSchema,
|
|
40
|
+
eventSchema: eventSchema,
|
|
41
|
+
errorSchema: errorSchema,
|
|
42
|
+
moduleUrl: moduleUrl,
|
|
43
|
+
commandAuthorization: commandAuthorization
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let stateSchema = S.schema(s => ({
|
|
47
|
+
items: s.m(S.array(S.string))
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
let initialState = {
|
|
51
|
+
items: []
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let snapshot = {
|
|
55
|
+
interval: 10,
|
|
56
|
+
stateSchema: stateSchema
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
let moduleUrl$1 = import.meta.url;
|
|
60
|
+
|
|
61
|
+
function evolve(state, event) {
|
|
62
|
+
return {
|
|
63
|
+
items: state.items.concat([event.item])
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function decide(_state, command) {
|
|
68
|
+
if (typeof command !== "object") {
|
|
69
|
+
return {
|
|
70
|
+
TAG: "Ok",
|
|
71
|
+
_0: []
|
|
72
|
+
};
|
|
73
|
+
} else {
|
|
74
|
+
return {
|
|
75
|
+
TAG: "Ok",
|
|
76
|
+
_0: [{
|
|
77
|
+
TAG: "Added",
|
|
78
|
+
item: command.item
|
|
79
|
+
}]
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let TestBehavior = {
|
|
85
|
+
Spec: undefined,
|
|
86
|
+
stateSchema: stateSchema,
|
|
87
|
+
initialState: initialState,
|
|
88
|
+
snapshot: snapshot,
|
|
89
|
+
moduleUrl: moduleUrl$1,
|
|
90
|
+
evolve: evolve,
|
|
91
|
+
decide: decide
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
let testMeta = {
|
|
95
|
+
service: "SnapshotTestAggregate",
|
|
96
|
+
time: "2024-01-01T00:00:00Z",
|
|
97
|
+
ip: "127.0.0.1",
|
|
98
|
+
user: "test-user",
|
|
99
|
+
msgId: "msg-1",
|
|
100
|
+
correlationId: "corr-1"
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function makeMockEL() {
|
|
104
|
+
let stored = {
|
|
105
|
+
contents: []
|
|
106
|
+
};
|
|
107
|
+
let snapshots = {};
|
|
108
|
+
let replayCount = {
|
|
109
|
+
contents: 0
|
|
110
|
+
};
|
|
111
|
+
let lastFromSeq = {
|
|
112
|
+
contents: -1
|
|
113
|
+
};
|
|
114
|
+
let snapReads = {
|
|
115
|
+
contents: 0
|
|
116
|
+
};
|
|
117
|
+
let snapWrites = {
|
|
118
|
+
contents: 0
|
|
119
|
+
};
|
|
120
|
+
let failWrite = {
|
|
121
|
+
contents: false
|
|
122
|
+
};
|
|
123
|
+
let eventsFor = id => stored.contents.filter(e => e.id === id);
|
|
124
|
+
let appendFn = async (seqNr, id, newEvents) => {
|
|
125
|
+
if (seqNr !== eventsFor(id).length) {
|
|
126
|
+
return {
|
|
127
|
+
TAG: "Error",
|
|
128
|
+
_0: "Conflict"
|
|
129
|
+
};
|
|
130
|
+
} else {
|
|
131
|
+
stored.contents = stored.contents.concat(newEvents);
|
|
132
|
+
return {
|
|
133
|
+
TAG: "Ok",
|
|
134
|
+
_0: undefined
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
let replayStreamFn = (id, fromSeqOpt) => {
|
|
139
|
+
let fromSeq = fromSeqOpt !== undefined ? fromSeqOpt : 0;
|
|
140
|
+
replayCount.contents = replayCount.contents + 1 | 0;
|
|
141
|
+
lastFromSeq.contents = fromSeq;
|
|
142
|
+
return Stream$1.fromIterable(eventsFor(id).map(e => e.event).filter((param, i) => i >= fromSeq));
|
|
143
|
+
};
|
|
144
|
+
let latestSnapshotFn = async id => {
|
|
145
|
+
snapReads.contents = snapReads.contents + 1 | 0;
|
|
146
|
+
return {
|
|
147
|
+
TAG: "Ok",
|
|
148
|
+
_0: snapshots[id]
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
let writeSnapshotFn = async (id, snap) => {
|
|
152
|
+
if (failWrite.contents) {
|
|
153
|
+
failWrite.contents = false;
|
|
154
|
+
return {
|
|
155
|
+
TAG: "Error",
|
|
156
|
+
_0: "mock write failure"
|
|
157
|
+
};
|
|
158
|
+
} else {
|
|
159
|
+
snapWrites.contents = snapWrites.contents + 1 | 0;
|
|
160
|
+
snapshots[id] = snap;
|
|
161
|
+
return {
|
|
162
|
+
TAG: "Ok",
|
|
163
|
+
_0: undefined
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
return {
|
|
168
|
+
appendFn: appendFn,
|
|
169
|
+
replayStreamFn: replayStreamFn,
|
|
170
|
+
latestSnapshotFn: latestSnapshotFn,
|
|
171
|
+
writeSnapshotFn: writeSnapshotFn,
|
|
172
|
+
getAll: () => stored.contents,
|
|
173
|
+
getSnapshot: id => snapshots[id],
|
|
174
|
+
setSnapshot: (id, snap) => {
|
|
175
|
+
snapshots[id] = snap;
|
|
176
|
+
},
|
|
177
|
+
replayCallCount: replayCount,
|
|
178
|
+
lastReplayFromSeq: lastFromSeq,
|
|
179
|
+
snapshotReadCount: snapReads,
|
|
180
|
+
snapshotWriteCount: snapWrites,
|
|
181
|
+
failNextWrite: failWrite,
|
|
182
|
+
reset: () => {
|
|
183
|
+
stored.contents = [];
|
|
184
|
+
Stdlib_Dict.forEachWithKey(snapshots, (param, k) => Stdlib_Dict.$$delete(snapshots, k));
|
|
185
|
+
replayCount.contents = 0;
|
|
186
|
+
lastFromSeq.contents = -1;
|
|
187
|
+
snapReads.contents = 0;
|
|
188
|
+
snapWrites.contents = 0;
|
|
189
|
+
failWrite.contents = false;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let mock = makeMockEL();
|
|
195
|
+
|
|
196
|
+
function MakeOps(B) {
|
|
197
|
+
let Spec_name = B.name;
|
|
198
|
+
let Spec = {
|
|
199
|
+
Id: undefined,
|
|
200
|
+
name: Spec_name,
|
|
201
|
+
eventSchema: eventSchema
|
|
202
|
+
};
|
|
203
|
+
let make = (param, param$1) => 0;
|
|
204
|
+
let EventLog = {
|
|
205
|
+
Spec: Spec,
|
|
206
|
+
make: make
|
|
207
|
+
};
|
|
208
|
+
let eventLog_append = mock.appendFn;
|
|
209
|
+
let eventLog_replay = async id => await Effect.runPromise(Stream.runCollect(mock.replayStreamFn(id, undefined)));
|
|
210
|
+
let eventLog_replayStream = mock.replayStreamFn;
|
|
211
|
+
let eventLog_appendStream = (_startingSeqNr, _id, stream) => Stream$1.runDrain(stream);
|
|
212
|
+
let eventLog_latestSnapshot = mock.latestSnapshotFn;
|
|
213
|
+
let eventLog_writeSnapshot = mock.writeSnapshotFn;
|
|
214
|
+
let eventLog = {
|
|
215
|
+
append: eventLog_append,
|
|
216
|
+
replay: eventLog_replay,
|
|
217
|
+
replayStream: eventLog_replayStream,
|
|
218
|
+
appendStream: eventLog_appendStream,
|
|
219
|
+
latestSnapshot: eventLog_latestSnapshot,
|
|
220
|
+
writeSnapshot: eventLog_writeSnapshot
|
|
221
|
+
};
|
|
222
|
+
return {
|
|
223
|
+
Spec: undefined,
|
|
224
|
+
EventLog: EventLog,
|
|
225
|
+
eventLog: eventLog
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let Spec = {
|
|
230
|
+
Id: undefined,
|
|
231
|
+
name: "SnapshotMockEventLog",
|
|
232
|
+
eventSchema: eventSchema
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
function make(param, param$1) {
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let EventLog = {
|
|
240
|
+
Spec: Spec,
|
|
241
|
+
make: make
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
let eventLog_append = mock.appendFn;
|
|
245
|
+
|
|
246
|
+
async function eventLog_replay(id) {
|
|
247
|
+
return await Effect.runPromise(Stream.runCollect(mock.replayStreamFn(id, undefined)));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let eventLog_replayStream = mock.replayStreamFn;
|
|
251
|
+
|
|
252
|
+
function eventLog_appendStream(_startingSeqNr, _id, stream) {
|
|
253
|
+
return Stream$1.runDrain(stream);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let eventLog_latestSnapshot = mock.latestSnapshotFn;
|
|
257
|
+
|
|
258
|
+
let eventLog_writeSnapshot = mock.writeSnapshotFn;
|
|
259
|
+
|
|
260
|
+
let eventLog = {
|
|
261
|
+
append: eventLog_append,
|
|
262
|
+
replay: eventLog_replay,
|
|
263
|
+
replayStream: eventLog_replayStream,
|
|
264
|
+
appendStream: eventLog_appendStream,
|
|
265
|
+
latestSnapshot: eventLog_latestSnapshot,
|
|
266
|
+
writeSnapshot: eventLog_writeSnapshot
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
let TestOps = {
|
|
270
|
+
Spec: undefined,
|
|
271
|
+
EventLog: EventLog,
|
|
272
|
+
eventLog: eventLog
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
let TestHandler = Aggregate_Callback$ReventlessCore.Make({
|
|
276
|
+
Id: {
|
|
277
|
+
schema: Id$Reventless.StringPure.schema,
|
|
278
|
+
make: prim => prim,
|
|
279
|
+
makeFromString: prim => prim,
|
|
280
|
+
toString: prim => prim,
|
|
281
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
282
|
+
},
|
|
283
|
+
name: name,
|
|
284
|
+
eventSchema: eventSchema,
|
|
285
|
+
errorSchema: errorSchema,
|
|
286
|
+
commandSchema: commandSchema,
|
|
287
|
+
moduleUrl: moduleUrl,
|
|
288
|
+
commandAuthorization: commandAuthorization
|
|
289
|
+
})({
|
|
290
|
+
initialState: initialState,
|
|
291
|
+
evolve: evolve,
|
|
292
|
+
decide: decide,
|
|
293
|
+
snapshot: snapshot,
|
|
294
|
+
moduleUrl: moduleUrl$1
|
|
295
|
+
})({
|
|
296
|
+
Spec: {
|
|
297
|
+
Id: {
|
|
298
|
+
schema: Id$Reventless.StringPure.schema,
|
|
299
|
+
make: prim => prim,
|
|
300
|
+
makeFromString: prim => prim,
|
|
301
|
+
toString: prim => prim,
|
|
302
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
303
|
+
},
|
|
304
|
+
name: name,
|
|
305
|
+
eventSchema: eventSchema,
|
|
306
|
+
errorSchema: errorSchema,
|
|
307
|
+
commandSchema: commandSchema,
|
|
308
|
+
moduleUrl: moduleUrl,
|
|
309
|
+
commandAuthorization: commandAuthorization
|
|
310
|
+
},
|
|
311
|
+
EventLog: {
|
|
312
|
+
Spec: {
|
|
313
|
+
Id: {
|
|
314
|
+
schema: Id$Reventless.StringPure.schema,
|
|
315
|
+
make: prim => prim,
|
|
316
|
+
makeFromString: prim => prim,
|
|
317
|
+
toString: prim => prim,
|
|
318
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
319
|
+
},
|
|
320
|
+
name: "SnapshotMockEventLog",
|
|
321
|
+
eventSchema: eventSchema
|
|
322
|
+
},
|
|
323
|
+
make: make
|
|
324
|
+
},
|
|
325
|
+
eventLog: eventLog
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
let stateSchema$1 = S.schema(s => ({
|
|
329
|
+
items: s.m(S.array(S.string))
|
|
330
|
+
}));
|
|
331
|
+
|
|
332
|
+
let initialState$1 = {
|
|
333
|
+
items: []
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
let moduleUrl$2 = import.meta.url;
|
|
337
|
+
|
|
338
|
+
function evolve$1(state, event) {
|
|
339
|
+
return {
|
|
340
|
+
items: state.items.concat([event.item])
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function decide$1(_state, command) {
|
|
345
|
+
if (typeof command !== "object") {
|
|
346
|
+
return {
|
|
347
|
+
TAG: "Ok",
|
|
348
|
+
_0: []
|
|
349
|
+
};
|
|
350
|
+
} else {
|
|
351
|
+
return {
|
|
352
|
+
TAG: "Ok",
|
|
353
|
+
_0: [{
|
|
354
|
+
TAG: "Added",
|
|
355
|
+
item: command.item
|
|
356
|
+
}]
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let PlainBehavior = {
|
|
362
|
+
Spec: undefined,
|
|
363
|
+
stateSchema: stateSchema$1,
|
|
364
|
+
initialState: initialState$1,
|
|
365
|
+
snapshot: undefined,
|
|
366
|
+
moduleUrl: moduleUrl$2,
|
|
367
|
+
evolve: evolve$1,
|
|
368
|
+
decide: decide$1
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
let Spec$1 = {
|
|
372
|
+
Id: undefined,
|
|
373
|
+
name: "PlainMockEventLog",
|
|
374
|
+
eventSchema: eventSchema
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
function make$1(param, param$1) {
|
|
378
|
+
return 0;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
let EventLog$1 = {
|
|
382
|
+
Spec: Spec$1,
|
|
383
|
+
make: make$1
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
let eventLog_append$1 = mock.appendFn;
|
|
387
|
+
|
|
388
|
+
async function eventLog_replay$1(id) {
|
|
389
|
+
return await Effect.runPromise(Stream.runCollect(mock.replayStreamFn(id, undefined)));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
let eventLog_replayStream$1 = mock.replayStreamFn;
|
|
393
|
+
|
|
394
|
+
function eventLog_appendStream$1(_startingSeqNr, _id, stream) {
|
|
395
|
+
return Stream$1.runDrain(stream);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
let eventLog_latestSnapshot$1 = mock.latestSnapshotFn;
|
|
399
|
+
|
|
400
|
+
let eventLog_writeSnapshot$1 = mock.writeSnapshotFn;
|
|
401
|
+
|
|
402
|
+
let eventLog$1 = {
|
|
403
|
+
append: eventLog_append$1,
|
|
404
|
+
replay: eventLog_replay$1,
|
|
405
|
+
replayStream: eventLog_replayStream$1,
|
|
406
|
+
appendStream: eventLog_appendStream$1,
|
|
407
|
+
latestSnapshot: eventLog_latestSnapshot$1,
|
|
408
|
+
writeSnapshot: eventLog_writeSnapshot$1
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
let PlainOps = {
|
|
412
|
+
Spec: undefined,
|
|
413
|
+
EventLog: EventLog$1,
|
|
414
|
+
eventLog: eventLog$1
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
let PlainHandler = Aggregate_Callback$ReventlessCore.Make({
|
|
418
|
+
Id: {
|
|
419
|
+
schema: Id$Reventless.StringPure.schema,
|
|
420
|
+
make: prim => prim,
|
|
421
|
+
makeFromString: prim => prim,
|
|
422
|
+
toString: prim => prim,
|
|
423
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
424
|
+
},
|
|
425
|
+
name: name,
|
|
426
|
+
eventSchema: eventSchema,
|
|
427
|
+
errorSchema: errorSchema,
|
|
428
|
+
commandSchema: commandSchema,
|
|
429
|
+
moduleUrl: moduleUrl,
|
|
430
|
+
commandAuthorization: commandAuthorization
|
|
431
|
+
})({
|
|
432
|
+
initialState: initialState$1,
|
|
433
|
+
evolve: evolve$1,
|
|
434
|
+
decide: decide$1,
|
|
435
|
+
snapshot: undefined,
|
|
436
|
+
moduleUrl: moduleUrl$2
|
|
437
|
+
})({
|
|
438
|
+
Spec: {
|
|
439
|
+
Id: {
|
|
440
|
+
schema: Id$Reventless.StringPure.schema,
|
|
441
|
+
make: prim => prim,
|
|
442
|
+
makeFromString: prim => prim,
|
|
443
|
+
toString: prim => prim,
|
|
444
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
445
|
+
},
|
|
446
|
+
name: name,
|
|
447
|
+
eventSchema: eventSchema,
|
|
448
|
+
errorSchema: errorSchema,
|
|
449
|
+
commandSchema: commandSchema,
|
|
450
|
+
moduleUrl: moduleUrl,
|
|
451
|
+
commandAuthorization: commandAuthorization
|
|
452
|
+
},
|
|
453
|
+
EventLog: {
|
|
454
|
+
Spec: {
|
|
455
|
+
Id: {
|
|
456
|
+
schema: Id$Reventless.StringPure.schema,
|
|
457
|
+
make: prim => prim,
|
|
458
|
+
makeFromString: prim => prim,
|
|
459
|
+
toString: prim => prim,
|
|
460
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
461
|
+
},
|
|
462
|
+
name: "PlainMockEventLog",
|
|
463
|
+
eventSchema: eventSchema
|
|
464
|
+
},
|
|
465
|
+
make: make$1
|
|
466
|
+
},
|
|
467
|
+
eventLog: eventLog$1
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
function makeItem(idOpt, reference, command) {
|
|
471
|
+
let id = idOpt !== undefined ? idOpt : "agg-1";
|
|
472
|
+
return {
|
|
473
|
+
command: {
|
|
474
|
+
id: id,
|
|
475
|
+
meta: testMeta,
|
|
476
|
+
command: command
|
|
477
|
+
},
|
|
478
|
+
reference: reference
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async function run(handler, idOpt, command) {
|
|
483
|
+
let id = idOpt !== undefined ? idOpt : "agg-1";
|
|
484
|
+
return await Effect.runPromise(handler(Stream$1.fromIterable([makeItem(id, "ref", command)])));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function tick() {
|
|
488
|
+
return Promise.resolve().then(() => Promise.resolve());
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
globalThis.beforeEach(() => {
|
|
492
|
+
mock.reset();
|
|
493
|
+
TestHandler.resetCache();
|
|
494
|
+
PlainHandler.resetCache();
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
globalThis.describe("Aggregate_Callback — snapshots:", () => {
|
|
498
|
+
globalThis.test("writes a snapshot each time an interval boundary is crossed", async () => {
|
|
499
|
+
for (let i = 1; i <= 25; ++i) {
|
|
500
|
+
await run(TestHandler.handleCommands, undefined, {
|
|
501
|
+
TAG: "Add",
|
|
502
|
+
item: `i` + i.toString()
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
await tick();
|
|
506
|
+
globalThis.expect(mock.snapshotWriteCount.contents).toBe(2);
|
|
507
|
+
let snap = mock.getSnapshot("agg-1");
|
|
508
|
+
if (snap !== undefined) {
|
|
509
|
+
globalThis.expect(snap.seqNr).toBe(20);
|
|
510
|
+
} else {
|
|
511
|
+
globalThis.expect("expected a snapshot").toBe("none");
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
globalThis.test("cold replay seeds from the snapshot and reads only the delta", async () => {
|
|
515
|
+
for (let i = 1; i <= 12; ++i) {
|
|
516
|
+
await run(TestHandler.handleCommands, undefined, {
|
|
517
|
+
TAG: "Add",
|
|
518
|
+
item: `i` + i.toString()
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
await tick();
|
|
522
|
+
TestHandler.resetCache();
|
|
523
|
+
mock.replayCallCount.contents = 0;
|
|
524
|
+
await run(TestHandler.handleCommands, undefined, {
|
|
525
|
+
TAG: "Add",
|
|
526
|
+
item: "i13"
|
|
527
|
+
});
|
|
528
|
+
globalThis.expect(mock.snapshotReadCount.contents >= 1).toBe(true);
|
|
529
|
+
globalThis.expect(mock.lastReplayFromSeq.contents).toBe(10);
|
|
530
|
+
globalThis.expect(mock.getAll().length).toBe(13);
|
|
531
|
+
});
|
|
532
|
+
globalThis.test("state seeded from snapshot equals a full-replay fold", async () => {
|
|
533
|
+
for (let i = 1; i <= 15; ++i) {
|
|
534
|
+
await run(TestHandler.handleCommands, undefined, {
|
|
535
|
+
TAG: "Add",
|
|
536
|
+
item: `x` + i.toString()
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
await tick();
|
|
540
|
+
TestHandler.resetCache();
|
|
541
|
+
await run(TestHandler.handleCommands, undefined, "Noop");
|
|
542
|
+
let seededSnap = mock.getSnapshot("agg-1");
|
|
543
|
+
for (let i$1 = 16; i$1 <= 20; ++i$1) {
|
|
544
|
+
await run(TestHandler.handleCommands, undefined, {
|
|
545
|
+
TAG: "Add",
|
|
546
|
+
item: `x` + i$1.toString()
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
await tick();
|
|
550
|
+
let snap = mock.getSnapshot("agg-1");
|
|
551
|
+
if (snap !== undefined) {
|
|
552
|
+
globalThis.expect(snap.seqNr).toBe(20);
|
|
553
|
+
let decoded = S.parseJsonOrThrow(snap.state, stateSchema);
|
|
554
|
+
globalThis.expect(decoded.items.length).toBe(20);
|
|
555
|
+
} else {
|
|
556
|
+
globalThis.expect("expected snapshot at 20").toBe("none");
|
|
557
|
+
}
|
|
558
|
+
if (seededSnap !== undefined) {
|
|
559
|
+
globalThis.expect(seededSnap.seqNr).toBe(10);
|
|
560
|
+
} else {
|
|
561
|
+
globalThis.expect("expected seeded snapshot").toBe("none");
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
globalThis.test("schema-drift snapshot is ignored → full replay from 0", async () => {
|
|
565
|
+
for (let i = 1; i <= 12; ++i) {
|
|
566
|
+
await run(TestHandler.handleCommands, undefined, {
|
|
567
|
+
TAG: "Add",
|
|
568
|
+
item: `i` + i.toString()
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
await tick();
|
|
572
|
+
let snap = mock.getSnapshot("agg-1");
|
|
573
|
+
if (snap !== undefined) {
|
|
574
|
+
mock.setSnapshot("agg-1", {
|
|
575
|
+
seqNr: snap.seqNr,
|
|
576
|
+
state: snap.state,
|
|
577
|
+
schemaHash: "STALE-HASH"
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
TestHandler.resetCache();
|
|
581
|
+
mock.replayCallCount.contents = 0;
|
|
582
|
+
await run(TestHandler.handleCommands, undefined, "Noop");
|
|
583
|
+
globalThis.expect(mock.lastReplayFromSeq.contents).toBe(0);
|
|
584
|
+
});
|
|
585
|
+
globalThis.test("snapshot write failure does not fail the command", async () => {
|
|
586
|
+
for (let i = 1; i <= 9; ++i) {
|
|
587
|
+
await run(TestHandler.handleCommands, undefined, {
|
|
588
|
+
TAG: "Add",
|
|
589
|
+
item: `i` + i.toString()
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
mock.failNextWrite.contents = true;
|
|
593
|
+
let result = await run(TestHandler.handleCommands, undefined, {
|
|
594
|
+
TAG: "Add",
|
|
595
|
+
item: "i10"
|
|
596
|
+
});
|
|
597
|
+
await tick();
|
|
598
|
+
globalThis.expect(result).toEqual([{
|
|
599
|
+
TAG: "Ok",
|
|
600
|
+
_0: "ref"
|
|
601
|
+
}]);
|
|
602
|
+
globalThis.expect(mock.getAll().length).toBe(10);
|
|
603
|
+
globalThis.expect(mock.getSnapshot("agg-1")).toEqual(undefined);
|
|
604
|
+
});
|
|
605
|
+
globalThis.test("disabled aggregate does zero snapshot reads and writes", async () => {
|
|
606
|
+
for (let i = 1; i <= 12; ++i) {
|
|
607
|
+
await run(PlainHandler.handleCommands, undefined, {
|
|
608
|
+
TAG: "Add",
|
|
609
|
+
item: `i` + i.toString()
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
await tick();
|
|
613
|
+
PlainHandler.resetCache();
|
|
614
|
+
await run(PlainHandler.handleCommands, undefined, "Noop");
|
|
615
|
+
globalThis.expect([
|
|
616
|
+
mock.snapshotReadCount.contents,
|
|
617
|
+
mock.snapshotWriteCount.contents
|
|
618
|
+
]).toEqual([
|
|
619
|
+
0,
|
|
620
|
+
0
|
|
621
|
+
]);
|
|
622
|
+
globalThis.expect(mock.lastReplayFromSeq.contents).toBe(0);
|
|
623
|
+
});
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
let OuterEventLog;
|
|
627
|
+
|
|
628
|
+
export {
|
|
629
|
+
AggSpec,
|
|
630
|
+
TestBehavior,
|
|
631
|
+
testMeta,
|
|
632
|
+
makeMockEL,
|
|
633
|
+
mock,
|
|
634
|
+
OuterEventLog,
|
|
635
|
+
MakeOps,
|
|
636
|
+
TestOps,
|
|
637
|
+
TestHandler,
|
|
638
|
+
PlainBehavior,
|
|
639
|
+
PlainOps,
|
|
640
|
+
PlainHandler,
|
|
641
|
+
makeItem,
|
|
642
|
+
run,
|
|
643
|
+
tick,
|
|
644
|
+
}
|
|
645
|
+
/* Not a pure module */
|
|
@@ -167,7 +167,7 @@ let makeMockStorage = (): mockStorage => {
|
|
|
167
167
|
let append = async (newEvents, ~condition=?) => {
|
|
168
168
|
if failNextAppendsRef.contents > 0 {
|
|
169
169
|
failNextAppendsRef := failNextAppendsRef.contents - 1
|
|
170
|
-
Error(
|
|
170
|
+
Error(ReventlessInfra.DcbEventLog.Conflict)
|
|
171
171
|
} else {
|
|
172
172
|
let conflictDetected = switch condition {
|
|
173
173
|
| Some(cond: Reventless.DcbTag.appendCondition) =>
|
|
@@ -181,7 +181,7 @@ let makeMockStorage = (): mockStorage => {
|
|
|
181
181
|
| None => false
|
|
182
182
|
}
|
|
183
183
|
if conflictDetected {
|
|
184
|
-
Error(
|
|
184
|
+
Error(ReventlessInfra.DcbEventLog.Conflict)
|
|
185
185
|
} else {
|
|
186
186
|
let storedEvents = newEvents->Array.map((event: DcbEventLog_Adapter.rawStoredEvent) => {
|
|
187
187
|
position := position.contents + 1
|
|
@@ -245,7 +245,7 @@ function makeMockStorage() {
|
|
|
245
245
|
failNextAppendsRef.contents = failNextAppendsRef.contents - 1 | 0;
|
|
246
246
|
return {
|
|
247
247
|
TAG: "Error",
|
|
248
|
-
_0: "
|
|
248
|
+
_0: "Conflict"
|
|
249
249
|
};
|
|
250
250
|
}
|
|
251
251
|
let conflictDetected = condition !== undefined ? events.contents.some(event => {
|
|
@@ -260,7 +260,7 @@ function makeMockStorage() {
|
|
|
260
260
|
if (conflictDetected) {
|
|
261
261
|
return {
|
|
262
262
|
TAG: "Error",
|
|
263
|
-
_0: "
|
|
263
|
+
_0: "Conflict"
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
266
|
let storedEvents = newEvents.map(event => {
|