@reventlessdev/reventless-local 3.0.0-alpha.285 → 3.0.0-alpha.287
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 +36 -0
- package/package.json +11 -11
- package/src/adapter/Scheduler/LocalScheduledPublisher.res +11 -51
- package/src/adapter/Scheduler/LocalScheduledPublisher.res.mjs +7 -40
- package/src/adapter/Scheduler/LocalTimers.res +62 -0
- package/src/adapter/Scheduler/LocalTimers.res.mjs +63 -0
- package/src/components/AutomationSlice_Builder.res +31 -2
- package/src/components/AutomationSlice_Builder.res.mjs +31 -3
- package/src/components/LocalAutomationSchedules.res +59 -0
- package/src/components/LocalAutomationSchedules.res.mjs +72 -0
- package/src/components/LocalAutomationSweep.res +61 -0
- package/src/components/LocalAutomationSweep.res.mjs +83 -0
- package/src/test/TestRunner.res +2 -0
- package/src/test/TestRunner.res.mjs +12 -0
- package/tests/adapter/ScheduledPublisherTest.res +156 -155
- package/tests/adapter/ScheduledPublisherTest.res.mjs +239 -137
- package/tests/components/automationslice/AutomationRescheduleFixtures.res +142 -0
- package/tests/components/automationslice/AutomationRescheduleFixtures.res.mjs +259 -0
- package/tests/components/automationslice/AutomationRescheduleTest.res +96 -0
- package/tests/components/automationslice/AutomationRescheduleTest.res.mjs +177 -0
- package/tests/components/automationslice/AutomationScheduleFixtures.res +118 -0
- package/tests/components/automationslice/AutomationScheduleFixtures.res.mjs +263 -0
- package/tests/components/automationslice/AutomationScheduleTest.res +88 -0
- package/tests/components/automationslice/AutomationScheduleTest.res.mjs +108 -0
- package/tests/components/automationslice/AutomationSliceFixtures.res +6 -2
- package/tests/components/automationslice/AutomationSliceFixtures.res.mjs +16 -4
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +6 -1
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res.mjs +8 -2
- package/tests/components/automationslice/AutomationSweepFixtures.res +131 -0
- package/tests/components/automationslice/AutomationSweepFixtures.res.mjs +223 -0
- package/tests/components/automationslice/AutomationSweepTest.res +107 -0
- package/tests/components/automationslice/AutomationSweepTest.res.mjs +116 -0
- package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res +4 -1
- package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res.mjs +14 -3
- package/tests/components/scheduler/SchedulerTest.res +5 -5
- package/tests/components/scheduler/SchedulerTest.res.mjs +22 -14
|
@@ -0,0 +1,263 @@
|
|
|
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 Pulumi from "@pulumi/pulumi";
|
|
8
|
+
import * as Clock$Reventless from "@reventlessdev/reventless-spec/src/components/Clock.res.mjs";
|
|
9
|
+
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
10
|
+
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
11
|
+
import * as LocalBus$ReventlessLocal from "../../../src/adapter/LocalBus.res.mjs";
|
|
12
|
+
import * as AutomationSlice$Reventless from "@reventlessdev/reventless-spec/src/components/AutomationSlice.res.mjs";
|
|
13
|
+
import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
|
|
14
|
+
import * as AutomationSlice_Builder$ReventlessLocal from "../../../src/components/AutomationSlice_Builder.res.mjs";
|
|
15
|
+
import * as AutomationSlice_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs";
|
|
16
|
+
|
|
17
|
+
let name = "LocalDemandReport";
|
|
18
|
+
|
|
19
|
+
let moduleUrl = import.meta.url;
|
|
20
|
+
|
|
21
|
+
let todoItemSchema = Sury.$schema(s => ({
|
|
22
|
+
day: s.m(Sury.number)
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
let commandSchema = Sury.$schema(s => ({
|
|
26
|
+
TAG: "RequestDemandReport",
|
|
27
|
+
day: s.m(Sury.number)
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
let targetName = "ReportSettings";
|
|
31
|
+
|
|
32
|
+
function authorizationOf(param) {
|
|
33
|
+
return "AllowAuthenticated";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function commandTransition(param) {
|
|
37
|
+
return "Undeclared";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let traits = [];
|
|
41
|
+
|
|
42
|
+
let DemandReportSpec_schedule = {
|
|
43
|
+
TAG: "Calendar",
|
|
44
|
+
calendar: {
|
|
45
|
+
TAG: "Daily",
|
|
46
|
+
hour: 6,
|
|
47
|
+
minute: 0
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
let DemandReportSpec = {
|
|
52
|
+
name: name,
|
|
53
|
+
moduleUrl: moduleUrl,
|
|
54
|
+
todoItemSchema: todoItemSchema,
|
|
55
|
+
commandSchema: commandSchema,
|
|
56
|
+
maxRetries: 2,
|
|
57
|
+
heartbeatInterval: 60,
|
|
58
|
+
targetName: targetName,
|
|
59
|
+
schedule: DemandReportSpec_schedule,
|
|
60
|
+
authorizationOf: authorizationOf,
|
|
61
|
+
commandTransition: commandTransition,
|
|
62
|
+
traits: traits
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function collect(param, param$1, _ctx) {
|
|
66
|
+
let at = param.at;
|
|
67
|
+
return [[
|
|
68
|
+
Clock$Reventless.itemId(param.key, at),
|
|
69
|
+
{
|
|
70
|
+
day: at
|
|
71
|
+
}
|
|
72
|
+
]];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolve(param) {
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function reschedule(param) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let FromClock = AutomationSlice$Reventless.Mapping.Make({
|
|
84
|
+
Id: Id$Reventless.$$String,
|
|
85
|
+
name: Clock$Reventless.name,
|
|
86
|
+
eventSchema: Clock$Reventless.eventSchema
|
|
87
|
+
})(DemandReportSpec)({
|
|
88
|
+
collect: collect,
|
|
89
|
+
resolve: resolve,
|
|
90
|
+
reschedule: reschedule
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
function process(id, item, param) {
|
|
94
|
+
return [
|
|
95
|
+
id,
|
|
96
|
+
{
|
|
97
|
+
TAG: "RequestDemandReport",
|
|
98
|
+
day: item.day
|
|
99
|
+
}
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function onExhausted(_id, _item) {
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let moduleUrl$1 = import.meta.url;
|
|
108
|
+
|
|
109
|
+
AutomationSlice$Reventless.Mappings.Make(DemandReportSpec);
|
|
110
|
+
|
|
111
|
+
let mappings = [FromClock];
|
|
112
|
+
|
|
113
|
+
let DemandReportAutomation = {
|
|
114
|
+
process: process,
|
|
115
|
+
onExhausted: onExhausted,
|
|
116
|
+
moduleUrl: moduleUrl$1,
|
|
117
|
+
mappings: mappings
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
let name$1 = "LocalUnscheduledReport";
|
|
121
|
+
|
|
122
|
+
let UnscheduledSpec = {
|
|
123
|
+
moduleUrl: moduleUrl,
|
|
124
|
+
todoItemSchema: todoItemSchema,
|
|
125
|
+
commandSchema: commandSchema,
|
|
126
|
+
maxRetries: 2,
|
|
127
|
+
heartbeatInterval: 60,
|
|
128
|
+
targetName: targetName,
|
|
129
|
+
authorizationOf: authorizationOf,
|
|
130
|
+
commandTransition: commandTransition,
|
|
131
|
+
traits: traits,
|
|
132
|
+
name: name$1,
|
|
133
|
+
schedule: undefined
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
function collect$1(param, param$1, _ctx) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function resolve$1(param) {
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function reschedule$1(param) {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let FromClockUnscheduled = AutomationSlice$Reventless.Mapping.Make({
|
|
149
|
+
Id: Id$Reventless.$$String,
|
|
150
|
+
name: Clock$Reventless.name,
|
|
151
|
+
eventSchema: Clock$Reventless.eventSchema
|
|
152
|
+
})({
|
|
153
|
+
name: name$1,
|
|
154
|
+
moduleUrl: moduleUrl,
|
|
155
|
+
todoItemSchema: todoItemSchema,
|
|
156
|
+
commandSchema: commandSchema,
|
|
157
|
+
maxRetries: 2,
|
|
158
|
+
heartbeatInterval: 60,
|
|
159
|
+
targetName: targetName,
|
|
160
|
+
schedule: undefined
|
|
161
|
+
})({
|
|
162
|
+
collect: collect$1,
|
|
163
|
+
resolve: resolve$1,
|
|
164
|
+
reschedule: reschedule$1
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
function process$1(_id, _item, param) {
|
|
168
|
+
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function onExhausted$1(_id, _item) {
|
|
172
|
+
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let moduleUrl$2 = import.meta.url;
|
|
176
|
+
|
|
177
|
+
AutomationSlice$Reventless.Mappings.Make({
|
|
178
|
+
name: name$1,
|
|
179
|
+
moduleUrl: moduleUrl,
|
|
180
|
+
todoItemSchema: todoItemSchema,
|
|
181
|
+
commandSchema: commandSchema,
|
|
182
|
+
maxRetries: 2,
|
|
183
|
+
heartbeatInterval: 60,
|
|
184
|
+
targetName: targetName,
|
|
185
|
+
schedule: undefined
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
let mappings$1 = [FromClockUnscheduled];
|
|
189
|
+
|
|
190
|
+
let UnscheduledAutomation = {
|
|
191
|
+
process: process$1,
|
|
192
|
+
onExhausted: onExhausted$1,
|
|
193
|
+
moduleUrl: moduleUrl$2,
|
|
194
|
+
mappings: mappings$1
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
let Bus = LocalBus$ReventlessLocal.Make({});
|
|
198
|
+
|
|
199
|
+
TestRunner$ReventlessLocal.setup();
|
|
200
|
+
|
|
201
|
+
let AutomationSliceMaker = AutomationSlice_Builder$ReventlessLocal.Make(Bus);
|
|
202
|
+
|
|
203
|
+
let DemandReport = AutomationSliceMaker.Make(DemandReportSpec)(DemandReportAutomation);
|
|
204
|
+
|
|
205
|
+
let Unscheduled = AutomationSliceMaker.Make({
|
|
206
|
+
name: name$1,
|
|
207
|
+
moduleUrl: moduleUrl,
|
|
208
|
+
todoItemSchema: todoItemSchema,
|
|
209
|
+
commandSchema: commandSchema,
|
|
210
|
+
maxRetries: 2,
|
|
211
|
+
heartbeatInterval: 60,
|
|
212
|
+
targetName: targetName,
|
|
213
|
+
schedule: undefined
|
|
214
|
+
})(UnscheduledAutomation);
|
|
215
|
+
|
|
216
|
+
let publishedCommands = {
|
|
217
|
+
contents: []
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
async function publishJsons(cmds) {
|
|
221
|
+
publishedCommands.contents = publishedCommands.contents.concat(cmds);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function context(sliceName) {
|
|
225
|
+
return {
|
|
226
|
+
environment: "local",
|
|
227
|
+
platformName: "local",
|
|
228
|
+
pluginName: "TestPlugin",
|
|
229
|
+
sliceName: sliceName
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function makeSlice() {
|
|
234
|
+
return DemandReport.make({}, Pulumi.output(publishJsons), context(name), undefined, undefined);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function makeUnscheduled() {
|
|
238
|
+
return Unscheduled.make({}, Pulumi.output(publishJsons), context(name$1), undefined, undefined);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function storedRow(id) {
|
|
242
|
+
return Stdlib_Option.map(Stdlib_Option.getOr(Stdlib_Option.map(Bus.getQueryDbScan(DemandReport.queryDbName), scan => scan()), []).find(row => Primitive_object.equal(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(row), o => o["id"]), id)), row => Util_Sury$Reventless.fromJson(row, AutomationSlice_Callback$ReventlessCore.todoRowSchema));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export {
|
|
246
|
+
DemandReportSpec,
|
|
247
|
+
FromClock,
|
|
248
|
+
DemandReportAutomation,
|
|
249
|
+
UnscheduledSpec,
|
|
250
|
+
FromClockUnscheduled,
|
|
251
|
+
UnscheduledAutomation,
|
|
252
|
+
Bus,
|
|
253
|
+
AutomationSliceMaker,
|
|
254
|
+
DemandReport,
|
|
255
|
+
Unscheduled,
|
|
256
|
+
publishedCommands,
|
|
257
|
+
publishJsons,
|
|
258
|
+
context,
|
|
259
|
+
makeSlice,
|
|
260
|
+
makeUnscheduled,
|
|
261
|
+
storedRow,
|
|
262
|
+
}
|
|
263
|
+
/* moduleUrl Not a pure module */
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// A declared schedule on the local platform: it ticks the slice at its time, a tick
|
|
2
|
+
// delivered twice sends one command, a command left unanswered is sent again after
|
|
3
|
+
// the timeout, and a slice that maps the Clock without a schedule is refused.
|
|
4
|
+
|
|
5
|
+
open JestGlobals
|
|
6
|
+
open AutomationScheduleFixtures
|
|
7
|
+
|
|
8
|
+
module Callback = ReventlessCore.AutomationSlice_Callback
|
|
9
|
+
|
|
10
|
+
type jestObj
|
|
11
|
+
@module("@jest/globals") external jest: jestObj = "jest"
|
|
12
|
+
@send external useFakeTimers: jestObj => unit = "useFakeTimers"
|
|
13
|
+
@send external useRealTimers: jestObj => unit = "useRealTimers"
|
|
14
|
+
@send external setSystemTime: (jestObj, float) => unit = "setSystemTime"
|
|
15
|
+
@send
|
|
16
|
+
external advanceTimersByTimeAsync: (jestObj, float) => promise<unit> = "advanceTimersByTimeAsync"
|
|
17
|
+
|
|
18
|
+
let time = iso => Date.fromString(iso)->Date.getTime
|
|
19
|
+
let morning = time("2026-10-16T06:00:00Z")
|
|
20
|
+
let dayItem = "default@2026-10-16T06:00Z"
|
|
21
|
+
|
|
22
|
+
let commandIds = () => publishedCommands.contents->Array.map(c => c.id)
|
|
23
|
+
|
|
24
|
+
// Lets the tick's detached work and Effect's own scheduling run to the end.
|
|
25
|
+
let settle = async () =>
|
|
26
|
+
for _ in 1 to 20 {
|
|
27
|
+
await jest->advanceTimersByTimeAsync(0.)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("a declared schedule, locally", () => {
|
|
31
|
+
let slice = ref(None)
|
|
32
|
+
let ops = async () =>
|
|
33
|
+
await slice.contents->Option.getOrThrow->ReventlessCore.Component.operations->TestRunner.resolve
|
|
34
|
+
|
|
35
|
+
let _ = beforeAllAsync(async () => {
|
|
36
|
+
jest->useFakeTimers
|
|
37
|
+
jest->setSystemTime(time("2026-10-16T05:59:00Z"))
|
|
38
|
+
slice := Some(makeSlice())
|
|
39
|
+
let _ = await ops()
|
|
40
|
+
await settle()
|
|
41
|
+
})
|
|
42
|
+
let _ = afterAll(() => {
|
|
43
|
+
LocalAutomationSchedules.reset()
|
|
44
|
+
jest->useRealTimers
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
testPromise("ticks the slice at its time, and process requests the report", async () => {
|
|
48
|
+
await jest->advanceTimersByTimeAsync(59_000.)
|
|
49
|
+
await settle()
|
|
50
|
+
expect(commandIds())->toEqual([])
|
|
51
|
+
|
|
52
|
+
await jest->advanceTimersByTimeAsync(1_000.)
|
|
53
|
+
await settle()
|
|
54
|
+
expect(commandIds())->toEqual([dayItem])
|
|
55
|
+
expect(storedRow(dayItem)->Option.map(row => row.status))->toEqual(Some(Callback.Processing))
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
testPromise("the same tick delivered twice sends one command", async () => {
|
|
59
|
+
let ops = await ops()
|
|
60
|
+
await ops.tick(~key=Reventless.Clock.defaultKey, ~at=morning)
|
|
61
|
+
await settle()
|
|
62
|
+
expect(commandIds())->toEqual([dayItem])
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
testPromise("a command left unanswered is sent again after the timeout", async () => {
|
|
66
|
+
await LocalAutomationSweep.sweepOnce()
|
|
67
|
+
expect(commandIds())->toEqual([dayItem])
|
|
68
|
+
|
|
69
|
+
jest->setSystemTime(Date.now() +. 15. *. 60_000.)
|
|
70
|
+
await LocalAutomationSweep.sweepOnce()
|
|
71
|
+
expect(commandIds())->toEqual([dayItem, dayItem])
|
|
72
|
+
expect(storedRow(dayItem)->Option.map(row => (row.status, row.retryCount)))->toEqual(
|
|
73
|
+
Some((Callback.Processing, 1)),
|
|
74
|
+
)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
testSync("a slice that maps the Clock without a schedule is refused", () => {
|
|
78
|
+
let refusal = switch makeUnscheduled() {
|
|
79
|
+
| _ => None
|
|
80
|
+
| exception JsExn(err) => err->JsExn.message
|
|
81
|
+
}
|
|
82
|
+
expect(
|
|
83
|
+
refusal->Option.map(
|
|
84
|
+
m => m->String.includes("maps the Clock source but declares no schedule"),
|
|
85
|
+
),
|
|
86
|
+
)->toEqual(Some(true))
|
|
87
|
+
})
|
|
88
|
+
})
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
4
|
+
import * as Globals from "@jest/globals";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as Clock$Reventless from "@reventlessdev/reventless-spec/src/components/Clock.res.mjs";
|
|
7
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
8
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
9
|
+
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
10
|
+
import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
|
|
11
|
+
import * as LocalAutomationSweep$ReventlessLocal from "../../../src/components/LocalAutomationSweep.res.mjs";
|
|
12
|
+
import * as LocalAutomationSchedules$ReventlessLocal from "../../../src/components/LocalAutomationSchedules.res.mjs";
|
|
13
|
+
import * as AutomationScheduleFixtures$ReventlessLocal from "./AutomationScheduleFixtures.res.mjs";
|
|
14
|
+
|
|
15
|
+
function time(iso) {
|
|
16
|
+
return new Date(iso).getTime();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let morning = new Date("2026-10-16T06:00:00Z").getTime();
|
|
20
|
+
|
|
21
|
+
let dayItem = "default@2026-10-16T06:00Z";
|
|
22
|
+
|
|
23
|
+
function commandIds() {
|
|
24
|
+
return AutomationScheduleFixtures$ReventlessLocal.publishedCommands.contents.map(c => c.id);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function settle() {
|
|
28
|
+
for (let _for = 1; _for <= 20; ++_for) {
|
|
29
|
+
await Globals.jest.advanceTimersByTimeAsync(0);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
globalThis.describe("a declared schedule, locally", () => {
|
|
34
|
+
let slice = {
|
|
35
|
+
contents: undefined
|
|
36
|
+
};
|
|
37
|
+
let ops = async () => await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(Stdlib_Option.getOrThrow(slice.contents, undefined)));
|
|
38
|
+
globalThis.beforeAll(async () => {
|
|
39
|
+
Globals.jest.useFakeTimers();
|
|
40
|
+
Globals.jest.setSystemTime(new Date("2026-10-16T05:59:00Z").getTime());
|
|
41
|
+
slice.contents = Primitive_option.some(AutomationScheduleFixtures$ReventlessLocal.makeSlice());
|
|
42
|
+
await ops();
|
|
43
|
+
return await settle();
|
|
44
|
+
});
|
|
45
|
+
globalThis.afterAll(() => {
|
|
46
|
+
LocalAutomationSchedules$ReventlessLocal.reset();
|
|
47
|
+
Globals.jest.useRealTimers();
|
|
48
|
+
});
|
|
49
|
+
globalThis.test("ticks the slice at its time, and process requests the report", async () => {
|
|
50
|
+
await Globals.jest.advanceTimersByTimeAsync(59000);
|
|
51
|
+
await settle();
|
|
52
|
+
globalThis.expect(commandIds()).toEqual([]);
|
|
53
|
+
await Globals.jest.advanceTimersByTimeAsync(1000);
|
|
54
|
+
await settle();
|
|
55
|
+
globalThis.expect(commandIds()).toEqual([dayItem]);
|
|
56
|
+
globalThis.expect(Stdlib_Option.map(AutomationScheduleFixtures$ReventlessLocal.storedRow(dayItem), row => row.status)).toEqual("Processing");
|
|
57
|
+
});
|
|
58
|
+
globalThis.test("the same tick delivered twice sends one command", async () => {
|
|
59
|
+
let ops$1 = await ops();
|
|
60
|
+
await ops$1.tick(Clock$Reventless.defaultKey, morning);
|
|
61
|
+
await settle();
|
|
62
|
+
globalThis.expect(commandIds()).toEqual([dayItem]);
|
|
63
|
+
});
|
|
64
|
+
globalThis.test("a command left unanswered is sent again after the timeout", async () => {
|
|
65
|
+
await LocalAutomationSweep$ReventlessLocal.sweepOnce();
|
|
66
|
+
globalThis.expect(commandIds()).toEqual([dayItem]);
|
|
67
|
+
Globals.jest.setSystemTime(Date.now() + 15 * 60000);
|
|
68
|
+
await LocalAutomationSweep$ReventlessLocal.sweepOnce();
|
|
69
|
+
globalThis.expect(commandIds()).toEqual([
|
|
70
|
+
dayItem,
|
|
71
|
+
dayItem
|
|
72
|
+
]);
|
|
73
|
+
globalThis.expect(Stdlib_Option.map(AutomationScheduleFixtures$ReventlessLocal.storedRow(dayItem), row => [
|
|
74
|
+
row.status,
|
|
75
|
+
row.retryCount
|
|
76
|
+
])).toEqual([
|
|
77
|
+
"Processing",
|
|
78
|
+
1
|
|
79
|
+
]);
|
|
80
|
+
});
|
|
81
|
+
globalThis.test("a slice that maps the Clock without a schedule is refused", () => {
|
|
82
|
+
let refusal;
|
|
83
|
+
try {
|
|
84
|
+
AutomationScheduleFixtures$ReventlessLocal.makeUnscheduled();
|
|
85
|
+
refusal = undefined;
|
|
86
|
+
} catch (raw_err) {
|
|
87
|
+
let err = Primitive_exceptions.internalToException(raw_err);
|
|
88
|
+
if (err.RE_EXN_ID === "JsExn") {
|
|
89
|
+
refusal = Stdlib_JsExn.message(err._1);
|
|
90
|
+
} else {
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
globalThis.expect(Stdlib_Option.map(refusal, m => m.includes("maps the Clock source but declares no schedule"))).toEqual(true);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
let Callback;
|
|
99
|
+
|
|
100
|
+
export {
|
|
101
|
+
Callback,
|
|
102
|
+
time,
|
|
103
|
+
morning,
|
|
104
|
+
dayItem,
|
|
105
|
+
commandIds,
|
|
106
|
+
settle,
|
|
107
|
+
}
|
|
108
|
+
/* morning Not a pure module */
|
|
@@ -41,6 +41,7 @@ module ShipOrderSpec = {
|
|
|
41
41
|
let maxRetries = 3
|
|
42
42
|
let heartbeatInterval = 60
|
|
43
43
|
let targetName = "CreateShipment"
|
|
44
|
+
let schedule = None
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
module SkipProcessSpec = {
|
|
@@ -56,6 +57,7 @@ module SkipProcessSpec = {
|
|
|
56
57
|
let maxRetries = 0
|
|
57
58
|
let heartbeatInterval = 60
|
|
58
59
|
let targetName = "Noop"
|
|
60
|
+
let schedule = None
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -84,6 +86,7 @@ module ShipOrderMapping = Reventless.AutomationSlice.Mapping.Make(
|
|
|
84
86
|
| ShipmentCreated({orderId}) => Some(orderId)
|
|
85
87
|
| OrderPlaced(_) => None
|
|
86
88
|
}
|
|
89
|
+
let reschedule = _ => []
|
|
87
90
|
},
|
|
88
91
|
)
|
|
89
92
|
|
|
@@ -101,12 +104,13 @@ module SkipProcessMapping = Reventless.AutomationSlice.Mapping.Make(
|
|
|
101
104
|
| ShipmentCreated({orderId}) => Some(orderId)
|
|
102
105
|
| OrderPlaced(_) => None
|
|
103
106
|
}
|
|
107
|
+
let reschedule = _ => []
|
|
104
108
|
},
|
|
105
109
|
)
|
|
106
110
|
|
|
107
111
|
module ShipOrderAutomation: Reventless.AutomationSlice.Automation
|
|
108
112
|
with module Spec := ShipOrderSpec = {
|
|
109
|
-
let process = (id, _item: ShipOrderSpec.todoItem) => Some((
|
|
113
|
+
let process = (id, _item: ShipOrderSpec.todoItem, ~now as _) => Some((
|
|
110
114
|
id,
|
|
111
115
|
ShipOrderSpec.CreateShipment({orderId: id}),
|
|
112
116
|
))
|
|
@@ -119,7 +123,7 @@ module ShipOrderAutomation: Reventless.AutomationSlice.Automation
|
|
|
119
123
|
|
|
120
124
|
module SkipProcessAutomation: Reventless.AutomationSlice.Automation
|
|
121
125
|
with module Spec := SkipProcessSpec = {
|
|
122
|
-
let process = (_id, _item: SkipProcessSpec.todoItem) => None
|
|
126
|
+
let process = (_id, _item: SkipProcessSpec.todoItem, ~now as _) => None
|
|
123
127
|
let onExhausted = (_id, _item: SkipProcessSpec.todoItem) => None
|
|
124
128
|
let moduleUrl: string = %raw(`import.meta.url`)
|
|
125
129
|
module M = Reventless.AutomationSlice.Mappings.Make(SkipProcessSpec)
|
|
@@ -77,6 +77,7 @@ let ShipOrderSpec = {
|
|
|
77
77
|
maxRetries: 3,
|
|
78
78
|
heartbeatInterval: 60,
|
|
79
79
|
targetName: "CreateShipment",
|
|
80
|
+
schedule: undefined,
|
|
80
81
|
authorizationOf: authorizationOf,
|
|
81
82
|
commandTransition: commandTransition,
|
|
82
83
|
traits: traits
|
|
@@ -108,6 +109,7 @@ let SkipProcessSpec = {
|
|
|
108
109
|
maxRetries: 0,
|
|
109
110
|
heartbeatInterval: 60,
|
|
110
111
|
targetName: "Noop",
|
|
112
|
+
schedule: undefined,
|
|
111
113
|
authorizationOf: authorizationOf$1,
|
|
112
114
|
commandTransition: commandTransition$1,
|
|
113
115
|
traits: traits$1
|
|
@@ -135,13 +137,18 @@ function resolve(event) {
|
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
|
|
140
|
+
function reschedule(param) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
|
|
138
144
|
let ShipOrderMapping = AutomationSlice$Reventless.Mapping.Make({
|
|
139
145
|
Id: Id$Reventless.$$String,
|
|
140
146
|
name: name,
|
|
141
147
|
eventSchema: eventSchema
|
|
142
148
|
})(ShipOrderSpec)({
|
|
143
149
|
collect: collect,
|
|
144
|
-
resolve: resolve
|
|
150
|
+
resolve: resolve,
|
|
151
|
+
reschedule: reschedule
|
|
145
152
|
});
|
|
146
153
|
|
|
147
154
|
function collect$1(event, param, _ctx) {
|
|
@@ -165,16 +172,21 @@ function resolve$1(event) {
|
|
|
165
172
|
}
|
|
166
173
|
}
|
|
167
174
|
|
|
175
|
+
function reschedule$1(param) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
|
|
168
179
|
let SkipProcessMapping = AutomationSlice$Reventless.Mapping.Make({
|
|
169
180
|
Id: Id$Reventless.$$String,
|
|
170
181
|
name: name$1,
|
|
171
182
|
eventSchema: eventSchema$1
|
|
172
183
|
})(SkipProcessSpec)({
|
|
173
184
|
collect: collect$1,
|
|
174
|
-
resolve: resolve$1
|
|
185
|
+
resolve: resolve$1,
|
|
186
|
+
reschedule: reschedule$1
|
|
175
187
|
});
|
|
176
188
|
|
|
177
|
-
function process(id, _item) {
|
|
189
|
+
function process(id, _item, param) {
|
|
178
190
|
return [
|
|
179
191
|
id,
|
|
180
192
|
{
|
|
@@ -201,7 +213,7 @@ let ShipOrderAutomation = {
|
|
|
201
213
|
mappings: mappings
|
|
202
214
|
};
|
|
203
215
|
|
|
204
|
-
function process$1(_id, _item) {
|
|
216
|
+
function process$1(_id, _item, param) {
|
|
205
217
|
|
|
206
218
|
}
|
|
207
219
|
|
|
@@ -136,6 +136,7 @@ module AutoShipSpec = {
|
|
|
136
136
|
let maxRetries = 3
|
|
137
137
|
let heartbeatInterval = 60
|
|
138
138
|
let targetName = "Ship"
|
|
139
|
+
let schedule = None
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
module FromDcb = AutomationSlice.Mapping.Make(
|
|
@@ -152,11 +153,15 @@ module FromDcb = AutomationSlice.Mapping.Make(
|
|
|
152
153
|
| Shipped({orderId}) => Some(orderId)
|
|
153
154
|
| Placed(_) => None
|
|
154
155
|
}
|
|
156
|
+
let reschedule = _ => []
|
|
155
157
|
},
|
|
156
158
|
)
|
|
157
159
|
|
|
158
160
|
module AutoShipAutomation: AutomationSlice.Automation with module Spec := AutoShipSpec = {
|
|
159
|
-
let process = (id, _item: AutoShipSpec.todoItem) => Some((
|
|
161
|
+
let process = (id, _item: AutoShipSpec.todoItem, ~now as _) => Some((
|
|
162
|
+
id,
|
|
163
|
+
AutoShipSpec.Ship({orderId: id}),
|
|
164
|
+
))
|
|
160
165
|
let onExhausted = (_id, _item: AutoShipSpec.todoItem) => None
|
|
161
166
|
let moduleUrl: string = %raw(`import.meta.url`)
|
|
162
167
|
module M = AutomationSlice.Mappings.Make(AutoShipSpec)
|
|
@@ -243,6 +243,7 @@ let AutoShipSpec = {
|
|
|
243
243
|
maxRetries: 3,
|
|
244
244
|
heartbeatInterval: 60,
|
|
245
245
|
targetName: "Ship",
|
|
246
|
+
schedule: undefined,
|
|
246
247
|
authorizationOf: authorizationOf$2,
|
|
247
248
|
commandTransition: commandTransition$2,
|
|
248
249
|
traits: traits$2
|
|
@@ -269,16 +270,21 @@ function resolve(event) {
|
|
|
269
270
|
}
|
|
270
271
|
}
|
|
271
272
|
|
|
273
|
+
function reschedule(param) {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
|
|
272
277
|
let FromDcb = AutomationSlice$Reventless.Mapping.Make({
|
|
273
278
|
Id: Id$Reventless.$$String,
|
|
274
279
|
name: name$2,
|
|
275
280
|
eventSchema: eventSchema$2
|
|
276
281
|
})(AutoShipSpec)({
|
|
277
282
|
collect: collect,
|
|
278
|
-
resolve: resolve
|
|
283
|
+
resolve: resolve,
|
|
284
|
+
reschedule: reschedule
|
|
279
285
|
});
|
|
280
286
|
|
|
281
|
-
function process(id, _item) {
|
|
287
|
+
function process(id, _item, param) {
|
|
282
288
|
return [
|
|
283
289
|
id,
|
|
284
290
|
{
|