@reventlessdev/reventless-gwt 1.0.0-alpha.230 → 1.0.0-alpha.232
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 +15 -0
- package/package.json +5 -5
- package/src/Behavior_GWT.res +196 -19
- package/src/Behavior_GWT.res.mjs +299 -41
- package/tests/CallerTest.res +33 -0
- package/tests/CallerTest.res.mjs +38 -0
- package/tests/StateChange/ExternalAddCategorySlice_GWT.res.mjs +9 -0
- package/tests/StateChange/WithFixtures_GWT.res.mjs +9 -0
- package/tests/StateChange/WithManualOpen_GWT.res.mjs +9 -0
- package/tests/StateChangeSliceGwtTest.res.mjs +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 1.0.0-alpha.232 (2026-10-01)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **spec, core, gwt, ppx:** a command acts only on what its caller owns ([2636f0e](https://github.com/ReventlessDev/reventless-core/commit/2636f0ee44797ee2c0b0c40a7107cf306113173f))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 1.0.0-alpha.231 (2026-09-29)
|
|
14
|
+
|
|
15
|
+
**Note:** Version bump only for package @reventlessdev/reventless-gwt
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
6
21
|
# 1.0.0-alpha.230 (2026-09-28)
|
|
7
22
|
|
|
8
23
|
**Note:** Version bump only for package @reventlessdev/reventless-gwt
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-gwt",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.232",
|
|
4
4
|
"description": "Given-When-Then DSLs and test harness for Reventless slice testing",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"sury": "11.0.0",
|
|
17
17
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.34",
|
|
18
18
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.11",
|
|
19
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
20
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
21
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.
|
|
22
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
19
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.285",
|
|
20
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.183",
|
|
21
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.20",
|
|
22
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.155"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"rescript": "12.3.1",
|
package/src/Behavior_GWT.res
CHANGED
|
@@ -22,6 +22,121 @@ open ReventlessCore
|
|
|
22
22
|
// and slice users *cannot* use [MakeFromAggregate] (their Spec/Behavior shape
|
|
23
23
|
// differs). Pick the matching entry point.
|
|
24
24
|
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Who issues the command, for slices and aggregates that mark `@owner` on the
|
|
27
|
+
// events they decide on. `asCaller` names the caller before `whenCmd`; the
|
|
28
|
+
// command is then checked against the owners the given events record, exactly
|
|
29
|
+
// as the handler checks it before `decide`. Without `asCaller` the command
|
|
30
|
+
// carries no caller claim, which the handler reads as the platform acting for
|
|
31
|
+
// itself — so every scenario written before this existed is unchanged.
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
module Caller = {
|
|
34
|
+
external unsafeAsString: 'id => string = "%identity"
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
A caller who owns only what records their id.
|
|
38
|
+
|
|
39
|
+
Takes the id as the scenario has it — `Caller.owner(c1)` with `c1: CustomerId.t`,
|
|
40
|
+
or a plain string — because every `Id.Make` identity is a string on the wire.
|
|
41
|
+
The type is not checked, and need not be: an id of the wrong entity matches no
|
|
42
|
+
recorded owner, so it can only be refused, which fails the scenario that
|
|
43
|
+
expected the owner to act. A value that is not a string at all is refused here.
|
|
44
|
+
*/
|
|
45
|
+
let owner = (id: 'id): Message.CallerClaim.t =>
|
|
46
|
+
if Reventless.OwnerScope.isJsString(id) {
|
|
47
|
+
Owned({userId: id->unsafeAsString})
|
|
48
|
+
} else {
|
|
49
|
+
JsError.throwWithMessage(
|
|
50
|
+
"Caller.owner: expected an id (a string at runtime, such as an Id.Make identity)",
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
/** A caller in an elevated group, acting on anyone's behalf. */
|
|
54
|
+
let operator: Message.CallerClaim.t = Exempt
|
|
55
|
+
/** A caller with no identity. */
|
|
56
|
+
let anonymous: Message.CallerClaim.t = Unidentified
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module type ActingSpec = {
|
|
60
|
+
let name: string
|
|
61
|
+
@schema
|
|
62
|
+
type history
|
|
63
|
+
@schema
|
|
64
|
+
type command
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module Acting = (Spec: ActingSpec) => {
|
|
68
|
+
let caller: ref<option<Message.CallerClaim.t>> = ref(None)
|
|
69
|
+
let refusal: ref<option<CommandTopic_Helpers.rejectedResult>> = ref(None)
|
|
70
|
+
|
|
71
|
+
let ownerFieldsByEventType = Reventless.Owner.fieldNamesByVariant(
|
|
72
|
+
Spec.historySchema->S.castToUnknown,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
let asCaller = (history, claim) => {
|
|
76
|
+
caller := Some(claim)
|
|
77
|
+
history
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Every given event is the command's own history here, so all of them count.
|
|
81
|
+
// The caller is consumed: the next scenario starts with no claim.
|
|
82
|
+
let refuses = (history: array<Spec.history>, command: Spec.command): bool => {
|
|
83
|
+
let claim = caller.contents
|
|
84
|
+
caller := None
|
|
85
|
+
refusal := None
|
|
86
|
+
if ownerFieldsByEventType->Dict.keysToArray->Array.length == 0 {
|
|
87
|
+
false
|
|
88
|
+
} else {
|
|
89
|
+
let owners = history->Array.reduce([], (owners, event) => {
|
|
90
|
+
let (eventType, data) = event->Message.encode(Spec.historySchema)->Message.splitMessage
|
|
91
|
+
switch ownerFieldsByEventType->Dict.get(eventType) {
|
|
92
|
+
| Some(fields) => owners->Reventless.OwnerScope.recordOwners(~fields, data)
|
|
93
|
+
| None => owners
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
refusal :=
|
|
97
|
+
Reventless.OwnerScope.decideActing(~claim, ~recordedOwners=Some(owners))
|
|
98
|
+
->CommandTopic_Helpers.ownershipRefusal(
|
|
99
|
+
~component=Spec.name,
|
|
100
|
+
~command=command->Message.encode(Spec.commandSchema)->Message.variantNameOfJson,
|
|
101
|
+
)
|
|
102
|
+
->Option.map(r => r.rejected)
|
|
103
|
+
refusal.contents->Option.isSome
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let encRefusal = (r: CommandTopic_Helpers.rejectedResult) =>
|
|
108
|
+
JSON.Encode.object(
|
|
109
|
+
Dict.fromArray([
|
|
110
|
+
("errorCode", JSON.Encode.string(r.errorCode)),
|
|
111
|
+
("errorDetail", JSON.Encode.string(r.errorDetail)),
|
|
112
|
+
]),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
// Any other `then*` after a refused command fails: `decide` never ran, so
|
|
116
|
+
// there is nothing for it to compare.
|
|
117
|
+
let unexpectedRefusal = (): option<Outcome.outcome> =>
|
|
118
|
+
refusal.contents->Option.map(r =>
|
|
119
|
+
Outcome.fail(
|
|
120
|
+
ErrorMismatch({expected: JSON.Encode.null, actual: Some(encRefusal(r)), actualEvents: []}),
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
let thenRefused = (~encEvents, ~decided: option<JSON.t>, events) =>
|
|
125
|
+
switch refusal.contents {
|
|
126
|
+
| Some(_) => Outcome.pass
|
|
127
|
+
| None =>
|
|
128
|
+
Outcome.fail(
|
|
129
|
+
ErrorMismatch({
|
|
130
|
+
expected: JSON.Encode.object(
|
|
131
|
+
Dict.fromArray([("errorCode", JSON.Encode.string("Forbidden"))]),
|
|
132
|
+
),
|
|
133
|
+
actual: decided,
|
|
134
|
+
actualEvents: encEvents(events),
|
|
135
|
+
}),
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
25
140
|
// ---------------------------------------------------------------------------
|
|
26
141
|
// Slice form — replaces the legacy `StateChangeSlice_GWT`.
|
|
27
142
|
// ---------------------------------------------------------------------------
|
|
@@ -60,6 +175,10 @@ module type T = {
|
|
|
60
175
|
|
|
61
176
|
let givenEvents: array<Spec.consumedEvent> => array<Spec.consumedEvent>
|
|
62
177
|
|
|
178
|
+
// Who issues the next `whenCmd`; see `Caller`.
|
|
179
|
+
module Caller: module type of Caller
|
|
180
|
+
let asCaller: (array<Spec.consumedEvent>, Message.CallerClaim.t) => array<Spec.consumedEvent>
|
|
181
|
+
|
|
63
182
|
let whenCmd: (array<Spec.consumedEvent>, Spec.command) => array<Spec.event>
|
|
64
183
|
|
|
65
184
|
let thenEvent: (array<Spec.event>, Spec.event) => Outcome.outcome
|
|
@@ -68,6 +187,8 @@ module type T = {
|
|
|
68
187
|
let thenEventWithError: (array<Spec.event>, Spec.event, Spec.error) => Outcome.outcome
|
|
69
188
|
let thenEventsWithError: (array<Spec.event>, array<Spec.event>, Spec.error) => Outcome.outcome
|
|
70
189
|
let thenError: (array<Spec.event>, Spec.error) => Outcome.outcome
|
|
190
|
+
// Refused before `decide` because the caller does not own what it acts on.
|
|
191
|
+
let thenRefused: array<Spec.event> => Outcome.outcome
|
|
71
192
|
|
|
72
193
|
// DCB optimistic-concurrency assertions.
|
|
73
194
|
let thenAppendsConditionedOn: (array<Spec.event>, Reventless.DcbTag.query) => Outcome.outcome
|
|
@@ -242,6 +363,16 @@ module Make = (Spec: BehaviorSpec, Behavior: Behavior with module Spec := Spec):
|
|
|
242
363
|
module Core = AssertionCore(Spec)
|
|
243
364
|
let errors = Core.errors
|
|
244
365
|
|
|
366
|
+
module Caller = Caller
|
|
367
|
+
module Guard = Acting({
|
|
368
|
+
let name = Spec.name
|
|
369
|
+
type history = Spec.consumedEvent
|
|
370
|
+
let historySchema = Spec.consumedEventSchema
|
|
371
|
+
type command = Spec.command
|
|
372
|
+
let commandSchema = Spec.commandSchema
|
|
373
|
+
})
|
|
374
|
+
let asCaller = Guard.asCaller
|
|
375
|
+
|
|
245
376
|
// DCB append-condition derived inside [whenCmd]; [None] until first call.
|
|
246
377
|
let derivedCondition: ref<option<Reventless.DcbTag.appendCondition>> = ref(None)
|
|
247
378
|
|
|
@@ -379,12 +510,15 @@ module Make = (Spec: BehaviorSpec, Behavior: Behavior with module Spec := Spec):
|
|
|
379
510
|
}
|
|
380
511
|
}
|
|
381
512
|
|
|
382
|
-
|
|
383
|
-
switch Behavior.decide(state, command) {
|
|
384
|
-
| Ok(events) => events
|
|
385
|
-
| Error(error) =>
|
|
386
|
-
errors := [error]
|
|
513
|
+
if Guard.refuses(history, command) {
|
|
387
514
|
[]
|
|
515
|
+
} else {
|
|
516
|
+
switch Behavior.decide(currentState(history), command) {
|
|
517
|
+
| Ok(events) => events
|
|
518
|
+
| Error(error) =>
|
|
519
|
+
errors := [error]
|
|
520
|
+
[]
|
|
521
|
+
}
|
|
388
522
|
}
|
|
389
523
|
}
|
|
390
524
|
|
|
@@ -392,7 +526,17 @@ module Make = (Spec: BehaviorSpec, Behavior: Behavior with module Spec := Spec):
|
|
|
392
526
|
let whenCmd = (history, cmd) => history->exec(cmd)
|
|
393
527
|
|
|
394
528
|
let checkAppendCondition = (): option<Outcome.outcome> =>
|
|
395
|
-
appendConditionFailure.contents
|
|
529
|
+
switch appendConditionFailure.contents {
|
|
530
|
+
| Some(m) => Some(Outcome.fail(m))
|
|
531
|
+
| None => Guard.unexpectedRefusal()
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
let thenRefused = events =>
|
|
535
|
+
Guard.thenRefused(
|
|
536
|
+
~encEvents=Core.encEvents,
|
|
537
|
+
~decided=errors.contents->Array.get(0)->Option.map(Core.encError),
|
|
538
|
+
events,
|
|
539
|
+
)
|
|
396
540
|
|
|
397
541
|
// The append-condition footgun is surfaced before the shared comparison core
|
|
398
542
|
// runs; `thenAppends*` below bypass it deliberately.
|
|
@@ -503,6 +647,10 @@ module type AggregateT = {
|
|
|
503
647
|
|
|
504
648
|
let givenEvents: array<Spec.event> => array<Spec.event>
|
|
505
649
|
|
|
650
|
+
// Who issues the next `whenCmd`; see `Caller`.
|
|
651
|
+
module Caller: module type of Caller
|
|
652
|
+
let asCaller: (array<Spec.event>, Message.CallerClaim.t) => array<Spec.event>
|
|
653
|
+
|
|
506
654
|
let whenCmd: (array<Spec.event>, Spec.command) => array<Spec.event>
|
|
507
655
|
|
|
508
656
|
let thenEvent: (array<Spec.event>, Spec.event) => Outcome.outcome
|
|
@@ -521,6 +669,8 @@ module type AggregateT = {
|
|
|
521
669
|
) => Outcome.outcome
|
|
522
670
|
let thenEventsWithError: (array<Spec.event>, array<Spec.event>, Spec.error) => Outcome.outcome
|
|
523
671
|
let thenError: (array<Spec.event>, Spec.error) => Outcome.outcome
|
|
672
|
+
// Refused before `decide` because the caller does not own what it acts on.
|
|
673
|
+
let thenRefused: array<Spec.event> => Outcome.outcome
|
|
524
674
|
}
|
|
525
675
|
|
|
526
676
|
module MakeFromAggregate = (Spec: AggregateSpec, Behavior: Behavior.T with module Spec = Spec): (
|
|
@@ -536,14 +686,27 @@ module MakeFromAggregate = (Spec: AggregateSpec, Behavior: Behavior.T with modul
|
|
|
536
686
|
module Core = AssertionCore(Spec)
|
|
537
687
|
let errors = Core.errors
|
|
538
688
|
|
|
689
|
+
module Caller = Caller
|
|
690
|
+
module Guard = Acting({
|
|
691
|
+
let name = Spec.name
|
|
692
|
+
type history = Spec.event
|
|
693
|
+
let historySchema = Spec.eventSchema
|
|
694
|
+
type command = Spec.command
|
|
695
|
+
let commandSchema = Spec.commandSchema
|
|
696
|
+
})
|
|
697
|
+
let asCaller = Guard.asCaller
|
|
698
|
+
|
|
539
699
|
let exec = (history, command): array<Spec.event> => {
|
|
540
700
|
errors := []
|
|
541
|
-
|
|
542
|
-
switch Behavior.decide(state, command) {
|
|
543
|
-
| Ok(events) => events
|
|
544
|
-
| Error(error) =>
|
|
545
|
-
errors := [error]
|
|
701
|
+
if Guard.refuses(history, command) {
|
|
546
702
|
[]
|
|
703
|
+
} else {
|
|
704
|
+
switch Behavior.decide(currentState(history), command) {
|
|
705
|
+
| Ok(events) => events
|
|
706
|
+
| Error(error) =>
|
|
707
|
+
errors := [error]
|
|
708
|
+
[]
|
|
709
|
+
}
|
|
547
710
|
}
|
|
548
711
|
}
|
|
549
712
|
|
|
@@ -551,20 +714,34 @@ module MakeFromAggregate = (Spec: AggregateSpec, Behavior: Behavior.T with modul
|
|
|
551
714
|
let whenCmd = (history, cmd) => history->exec(cmd)
|
|
552
715
|
|
|
553
716
|
// The aggregate flavour has no append-condition footgun, so the shared core's
|
|
554
|
-
// comparisons are exposed directly
|
|
555
|
-
// specific to this surface.
|
|
556
|
-
let
|
|
557
|
-
|
|
717
|
+
// comparisons are exposed directly once a refusal has been ruled out; only
|
|
718
|
+
// `thenCompare*` (custom equality) is specific to this surface.
|
|
719
|
+
let unlessRefused = (events, compare) =>
|
|
720
|
+
switch Guard.unexpectedRefusal() {
|
|
721
|
+
| Some(o) => o
|
|
722
|
+
| None => compare(events)
|
|
723
|
+
}
|
|
724
|
+
let thenEvents = (events, expected) => events->unlessRefused(Core.compareEvents(_, expected))
|
|
725
|
+
let thenCompareEvents = (events, expected, cmp) =>
|
|
726
|
+
events->unlessRefused(Core.compareEventsWith(_, expected, cmp))
|
|
558
727
|
let thenEvent = (events, expectedEvent) => thenEvents(events, [expectedEvent])
|
|
559
728
|
let thenCompareEvent = (events, expectedEvent, cmp) =>
|
|
560
729
|
thenCompareEvents(events, [expectedEvent], cmp)
|
|
561
|
-
let thenNoEvent = Core.compareNoEvent
|
|
730
|
+
let thenNoEvent = events => events->unlessRefused(Core.compareNoEvent)
|
|
562
731
|
|
|
563
|
-
let thenError = (events, expectedError) =>
|
|
732
|
+
let thenError = (events, expectedError) =>
|
|
733
|
+
events->unlessRefused(Core.matchesError(_, [], expectedError))
|
|
564
734
|
|
|
565
735
|
let thenEventWithError = (events, expectedEvent, expectedError) =>
|
|
566
|
-
Core.matchesError(
|
|
736
|
+
events->unlessRefused(Core.matchesError(_, [expectedEvent], expectedError))
|
|
567
737
|
|
|
568
738
|
let thenEventsWithError = (events, expectedEvents, expectedError) =>
|
|
569
|
-
Core.matchesError(
|
|
739
|
+
events->unlessRefused(Core.matchesError(_, expectedEvents, expectedError))
|
|
740
|
+
|
|
741
|
+
let thenRefused = events =>
|
|
742
|
+
Guard.thenRefused(
|
|
743
|
+
~encEvents=Core.encEvents,
|
|
744
|
+
~decided=errors.contents->Array.get(0)->Option.map(Core.encError),
|
|
745
|
+
events,
|
|
746
|
+
)
|
|
570
747
|
}
|
package/src/Behavior_GWT.res.mjs
CHANGED
|
@@ -2,15 +2,111 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
4
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
6
|
+
import * as Owner$Reventless from "@reventlessdev/reventless-spec/src/components/Owner.res.mjs";
|
|
5
7
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
6
8
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
7
9
|
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
8
10
|
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
9
11
|
import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/components/DcbDecode.res.mjs";
|
|
10
12
|
import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
|
|
13
|
+
import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
|
|
11
14
|
import * as JestBind$ReventlessGwt from "./JestBind.res.mjs";
|
|
12
15
|
import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
|
|
13
16
|
import * as DcbScopeInference$Reventless from "@reventlessdev/reventless-spec/src/components/DcbScopeInference.res.mjs";
|
|
17
|
+
import * as CommandTopic_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Helpers.res.mjs";
|
|
18
|
+
|
|
19
|
+
function owner(id) {
|
|
20
|
+
if (OwnerScope$Reventless.isJsString(id)) {
|
|
21
|
+
return {
|
|
22
|
+
TAG: "Owned",
|
|
23
|
+
userId: id
|
|
24
|
+
};
|
|
25
|
+
} else {
|
|
26
|
+
return Stdlib_JsError.throwWithMessage("Caller.owner: expected an id (a string at runtime, such as an Id.Make identity)");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let Caller = {
|
|
31
|
+
owner: owner,
|
|
32
|
+
operator: "Exempt",
|
|
33
|
+
anonymous: "Unidentified"
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function Acting(Spec) {
|
|
37
|
+
let caller = {
|
|
38
|
+
contents: undefined
|
|
39
|
+
};
|
|
40
|
+
let refusal = {
|
|
41
|
+
contents: undefined
|
|
42
|
+
};
|
|
43
|
+
let ownerFieldsByEventType = Owner$Reventless.fieldNamesByVariant(Spec.historySchema);
|
|
44
|
+
let asCaller = (history, claim) => {
|
|
45
|
+
caller.contents = claim;
|
|
46
|
+
return history;
|
|
47
|
+
};
|
|
48
|
+
let refuses = (history, command) => {
|
|
49
|
+
let claim = caller.contents;
|
|
50
|
+
caller.contents = undefined;
|
|
51
|
+
refusal.contents = undefined;
|
|
52
|
+
if (Object.keys(ownerFieldsByEventType).length === 0) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
let owners = Stdlib_Array.reduce(history, [], (owners, event) => {
|
|
56
|
+
let match = Message$ReventlessCore.splitMessage(Message$ReventlessCore.encode(event, Spec.historySchema));
|
|
57
|
+
let fields = ownerFieldsByEventType[match[0]];
|
|
58
|
+
if (fields !== undefined) {
|
|
59
|
+
return OwnerScope$Reventless.recordOwners(owners, fields, match[1]);
|
|
60
|
+
} else {
|
|
61
|
+
return owners;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
refusal.contents = Stdlib_Option.map(CommandTopic_Helpers$ReventlessCore.ownershipRefusal(OwnerScope$Reventless.decideActing(claim, owners), Spec.name, Message$ReventlessCore.variantNameOfJson(Message$ReventlessCore.encode(command, Spec.commandSchema))), r => r.rejected);
|
|
65
|
+
return Stdlib_Option.isSome(refusal.contents);
|
|
66
|
+
};
|
|
67
|
+
let encRefusal = r => Object.fromEntries([
|
|
68
|
+
[
|
|
69
|
+
"errorCode",
|
|
70
|
+
r.errorCode
|
|
71
|
+
],
|
|
72
|
+
[
|
|
73
|
+
"errorDetail",
|
|
74
|
+
r.errorDetail
|
|
75
|
+
]
|
|
76
|
+
]);
|
|
77
|
+
let unexpectedRefusal = () => Stdlib_Option.map(refusal.contents, r => Outcome$ReventlessGwt.fail({
|
|
78
|
+
TAG: "ErrorMismatch",
|
|
79
|
+
expected: null,
|
|
80
|
+
actual: encRefusal(r),
|
|
81
|
+
actualEvents: []
|
|
82
|
+
}));
|
|
83
|
+
let thenRefused = (encEvents, decided, events) => {
|
|
84
|
+
let match = refusal.contents;
|
|
85
|
+
if (match !== undefined) {
|
|
86
|
+
return Outcome$ReventlessGwt.pass;
|
|
87
|
+
} else {
|
|
88
|
+
return Outcome$ReventlessGwt.fail({
|
|
89
|
+
TAG: "ErrorMismatch",
|
|
90
|
+
expected: Object.fromEntries([[
|
|
91
|
+
"errorCode",
|
|
92
|
+
"Forbidden"
|
|
93
|
+
]]),
|
|
94
|
+
actual: decided,
|
|
95
|
+
actualEvents: encEvents(events)
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
caller: caller,
|
|
101
|
+
refusal: refusal,
|
|
102
|
+
ownerFieldsByEventType: ownerFieldsByEventType,
|
|
103
|
+
asCaller: asCaller,
|
|
104
|
+
refuses: refuses,
|
|
105
|
+
encRefusal: encRefusal,
|
|
106
|
+
unexpectedRefusal: unexpectedRefusal,
|
|
107
|
+
thenRefused: thenRefused
|
|
108
|
+
};
|
|
109
|
+
}
|
|
14
110
|
|
|
15
111
|
function encodeTag(t) {
|
|
16
112
|
let d = {};
|
|
@@ -149,6 +245,7 @@ function Make(Spec) {
|
|
|
149
245
|
contents: []
|
|
150
246
|
};
|
|
151
247
|
let encEvent = e => Message$ReventlessCore.encode(e, Spec.eventSchema);
|
|
248
|
+
let encEvents = evs => evs.map(encEvent);
|
|
152
249
|
let encError = err => Message$ReventlessCore.encode(err, Spec.errorSchema);
|
|
153
250
|
let unexpectedError = events => {
|
|
154
251
|
let actual = Stdlib_Option.map(errors.contents[0], encError);
|
|
@@ -188,6 +285,55 @@ function Make(Spec) {
|
|
|
188
285
|
});
|
|
189
286
|
}
|
|
190
287
|
};
|
|
288
|
+
let name = Spec.name;
|
|
289
|
+
let historySchema = Spec.consumedEventSchema;
|
|
290
|
+
let commandSchema = Spec.commandSchema;
|
|
291
|
+
let caller = {
|
|
292
|
+
contents: undefined
|
|
293
|
+
};
|
|
294
|
+
let refusal = {
|
|
295
|
+
contents: undefined
|
|
296
|
+
};
|
|
297
|
+
let ownerFieldsByEventType = Owner$Reventless.fieldNamesByVariant(historySchema);
|
|
298
|
+
let asCaller = (history, claim) => {
|
|
299
|
+
caller.contents = claim;
|
|
300
|
+
return history;
|
|
301
|
+
};
|
|
302
|
+
let refuses = (history, command) => {
|
|
303
|
+
let claim = caller.contents;
|
|
304
|
+
caller.contents = undefined;
|
|
305
|
+
refusal.contents = undefined;
|
|
306
|
+
if (Object.keys(ownerFieldsByEventType).length === 0) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
let owners = Stdlib_Array.reduce(history, [], (owners, event) => {
|
|
310
|
+
let match = Message$ReventlessCore.splitMessage(Message$ReventlessCore.encode(event, historySchema));
|
|
311
|
+
let fields = ownerFieldsByEventType[match[0]];
|
|
312
|
+
if (fields !== undefined) {
|
|
313
|
+
return OwnerScope$Reventless.recordOwners(owners, fields, match[1]);
|
|
314
|
+
} else {
|
|
315
|
+
return owners;
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
refusal.contents = Stdlib_Option.map(CommandTopic_Helpers$ReventlessCore.ownershipRefusal(OwnerScope$Reventless.decideActing(claim, owners), name, Message$ReventlessCore.variantNameOfJson(Message$ReventlessCore.encode(command, commandSchema))), r => r.rejected);
|
|
319
|
+
return Stdlib_Option.isSome(refusal.contents);
|
|
320
|
+
};
|
|
321
|
+
let encRefusal = r => Object.fromEntries([
|
|
322
|
+
[
|
|
323
|
+
"errorCode",
|
|
324
|
+
r.errorCode
|
|
325
|
+
],
|
|
326
|
+
[
|
|
327
|
+
"errorDetail",
|
|
328
|
+
r.errorDetail
|
|
329
|
+
]
|
|
330
|
+
]);
|
|
331
|
+
let unexpectedRefusal = () => Stdlib_Option.map(refusal.contents, r => Outcome$ReventlessGwt.fail({
|
|
332
|
+
TAG: "ErrorMismatch",
|
|
333
|
+
expected: null,
|
|
334
|
+
actual: encRefusal(r),
|
|
335
|
+
actualEvents: []
|
|
336
|
+
}));
|
|
191
337
|
let derivedCondition = {
|
|
192
338
|
contents: undefined
|
|
193
339
|
};
|
|
@@ -270,15 +416,41 @@ function Make(Spec) {
|
|
|
270
416
|
};
|
|
271
417
|
}
|
|
272
418
|
}
|
|
273
|
-
|
|
274
|
-
|
|
419
|
+
if (refuses(history, cmd)) {
|
|
420
|
+
return [];
|
|
421
|
+
}
|
|
422
|
+
let events = Behavior.decide(Stdlib_Array.reduce(history, Behavior.initialState, Behavior.evolve), cmd);
|
|
275
423
|
if (events.TAG === "Ok") {
|
|
276
424
|
return events._0;
|
|
277
425
|
}
|
|
278
426
|
errors.contents = [events._0];
|
|
279
427
|
return [];
|
|
280
428
|
};
|
|
281
|
-
let checkAppendCondition = () =>
|
|
429
|
+
let checkAppendCondition = () => {
|
|
430
|
+
let m = appendConditionFailure.contents;
|
|
431
|
+
if (m !== undefined) {
|
|
432
|
+
return Outcome$ReventlessGwt.fail(m);
|
|
433
|
+
} else {
|
|
434
|
+
return unexpectedRefusal();
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
let thenRefused = events => {
|
|
438
|
+
let decided = Stdlib_Option.map(errors.contents[0], encError);
|
|
439
|
+
let match = refusal.contents;
|
|
440
|
+
if (match !== undefined) {
|
|
441
|
+
return Outcome$ReventlessGwt.pass;
|
|
442
|
+
} else {
|
|
443
|
+
return Outcome$ReventlessGwt.fail({
|
|
444
|
+
TAG: "ErrorMismatch",
|
|
445
|
+
expected: Object.fromEntries([[
|
|
446
|
+
"errorCode",
|
|
447
|
+
"Forbidden"
|
|
448
|
+
]]),
|
|
449
|
+
actual: decided,
|
|
450
|
+
actualEvents: encEvents(events)
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
};
|
|
282
454
|
let thenEvents = (events, expectedEvents) => {
|
|
283
455
|
let o = checkAppendCondition();
|
|
284
456
|
if (o !== undefined) {
|
|
@@ -383,6 +555,8 @@ function Make(Spec) {
|
|
|
383
555
|
todo: JestBind$ReventlessGwt.todo,
|
|
384
556
|
test: test,
|
|
385
557
|
givenEvents: givenEvents,
|
|
558
|
+
Caller: Caller,
|
|
559
|
+
asCaller: asCaller,
|
|
386
560
|
whenCmd: whenCmd,
|
|
387
561
|
thenEvent: thenEvent,
|
|
388
562
|
thenEvents: thenEvents,
|
|
@@ -390,6 +564,7 @@ function Make(Spec) {
|
|
|
390
564
|
thenEventWithError: thenEventWithError,
|
|
391
565
|
thenEventsWithError: thenEventsWithError,
|
|
392
566
|
thenError: thenError,
|
|
567
|
+
thenRefused: thenRefused,
|
|
393
568
|
thenAppendsConditionedOn: thenAppendsConditionedOn,
|
|
394
569
|
thenAppendsConditionedOnExactly: thenAppendsConditionedOnExactly
|
|
395
570
|
};
|
|
@@ -403,6 +578,7 @@ function MakeFromAggregate(Spec) {
|
|
|
403
578
|
contents: []
|
|
404
579
|
};
|
|
405
580
|
let encEvent = e => Message$ReventlessCore.encode(e, Spec.eventSchema);
|
|
581
|
+
let encEvents = evs => evs.map(encEvent);
|
|
406
582
|
let encError = err => Message$ReventlessCore.encode(err, Spec.errorSchema);
|
|
407
583
|
let unexpectedError = events => {
|
|
408
584
|
let actual = Stdlib_Option.map(errors.contents[0], encError);
|
|
@@ -413,32 +589,6 @@ function MakeFromAggregate(Spec) {
|
|
|
413
589
|
actualEvents: events.map(encEvent)
|
|
414
590
|
});
|
|
415
591
|
};
|
|
416
|
-
let compareEvents = (events, expectedEvents) => {
|
|
417
|
-
if (errors.contents.length !== 0) {
|
|
418
|
-
return unexpectedError(events);
|
|
419
|
-
} else if (Primitive_object.equal(events.map(encEvent), expectedEvents.map(encEvent))) {
|
|
420
|
-
return Outcome$ReventlessGwt.pass;
|
|
421
|
-
} else {
|
|
422
|
-
return Outcome$ReventlessGwt.fail({
|
|
423
|
-
TAG: "EventsMismatch",
|
|
424
|
-
expected: expectedEvents.map(encEvent),
|
|
425
|
-
actual: events.map(encEvent)
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
};
|
|
429
|
-
let compareEventsWith = (events, expectedEvents, cmp) => {
|
|
430
|
-
if (errors.contents.length !== 0) {
|
|
431
|
-
return unexpectedError(events);
|
|
432
|
-
} else if (events.length === expectedEvents.length && Stdlib_Array.zip(events, expectedEvents).every(param => cmp(param[0], param[1]))) {
|
|
433
|
-
return Outcome$ReventlessGwt.pass;
|
|
434
|
-
} else {
|
|
435
|
-
return Outcome$ReventlessGwt.fail({
|
|
436
|
-
TAG: "EventsMismatch",
|
|
437
|
-
expected: expectedEvents.map(encEvent),
|
|
438
|
-
actual: events.map(encEvent)
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
592
|
let compareNoEvent = events => {
|
|
443
593
|
if (errors.contents.length !== 0) {
|
|
444
594
|
return unexpectedError(events);
|
|
@@ -480,41 +630,149 @@ function MakeFromAggregate(Spec) {
|
|
|
480
630
|
});
|
|
481
631
|
}
|
|
482
632
|
};
|
|
633
|
+
let name = Spec.name;
|
|
634
|
+
let historySchema = Spec.eventSchema;
|
|
635
|
+
let commandSchema = Spec.commandSchema;
|
|
636
|
+
let caller = {
|
|
637
|
+
contents: undefined
|
|
638
|
+
};
|
|
639
|
+
let refusal = {
|
|
640
|
+
contents: undefined
|
|
641
|
+
};
|
|
642
|
+
let ownerFieldsByEventType = Owner$Reventless.fieldNamesByVariant(historySchema);
|
|
643
|
+
let asCaller = (history, claim) => {
|
|
644
|
+
caller.contents = claim;
|
|
645
|
+
return history;
|
|
646
|
+
};
|
|
647
|
+
let refuses = (history, command) => {
|
|
648
|
+
let claim = caller.contents;
|
|
649
|
+
caller.contents = undefined;
|
|
650
|
+
refusal.contents = undefined;
|
|
651
|
+
if (Object.keys(ownerFieldsByEventType).length === 0) {
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
let owners = Stdlib_Array.reduce(history, [], (owners, event) => {
|
|
655
|
+
let match = Message$ReventlessCore.splitMessage(Message$ReventlessCore.encode(event, historySchema));
|
|
656
|
+
let fields = ownerFieldsByEventType[match[0]];
|
|
657
|
+
if (fields !== undefined) {
|
|
658
|
+
return OwnerScope$Reventless.recordOwners(owners, fields, match[1]);
|
|
659
|
+
} else {
|
|
660
|
+
return owners;
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
refusal.contents = Stdlib_Option.map(CommandTopic_Helpers$ReventlessCore.ownershipRefusal(OwnerScope$Reventless.decideActing(claim, owners), name, Message$ReventlessCore.variantNameOfJson(Message$ReventlessCore.encode(command, commandSchema))), r => r.rejected);
|
|
664
|
+
return Stdlib_Option.isSome(refusal.contents);
|
|
665
|
+
};
|
|
666
|
+
let encRefusal = r => Object.fromEntries([
|
|
667
|
+
[
|
|
668
|
+
"errorCode",
|
|
669
|
+
r.errorCode
|
|
670
|
+
],
|
|
671
|
+
[
|
|
672
|
+
"errorDetail",
|
|
673
|
+
r.errorDetail
|
|
674
|
+
]
|
|
675
|
+
]);
|
|
676
|
+
let unexpectedRefusal = () => Stdlib_Option.map(refusal.contents, r => Outcome$ReventlessGwt.fail({
|
|
677
|
+
TAG: "ErrorMismatch",
|
|
678
|
+
expected: null,
|
|
679
|
+
actual: encRefusal(r),
|
|
680
|
+
actualEvents: []
|
|
681
|
+
}));
|
|
483
682
|
let givenEvents = events => events;
|
|
484
683
|
let whenCmd = (history, cmd) => {
|
|
485
684
|
errors.contents = [];
|
|
486
|
-
|
|
487
|
-
|
|
685
|
+
if (refuses(history, cmd)) {
|
|
686
|
+
return [];
|
|
687
|
+
}
|
|
688
|
+
let events = Behavior.decide(Stdlib_Array.reduce(history, Behavior.initialState, Behavior.evolve), cmd);
|
|
488
689
|
if (events.TAG === "Ok") {
|
|
489
690
|
return events._0;
|
|
490
691
|
}
|
|
491
692
|
errors.contents = [events._0];
|
|
492
693
|
return [];
|
|
493
694
|
};
|
|
494
|
-
let
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
695
|
+
let unlessRefused = (events, compare) => {
|
|
696
|
+
let o = unexpectedRefusal();
|
|
697
|
+
if (o !== undefined) {
|
|
698
|
+
return o;
|
|
699
|
+
} else {
|
|
700
|
+
return compare(events);
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
let thenEvents = (events, expected) => unlessRefused(events, __x => {
|
|
704
|
+
if (errors.contents.length !== 0) {
|
|
705
|
+
return unexpectedError(__x);
|
|
706
|
+
} else if (Primitive_object.equal(__x.map(encEvent), expected.map(encEvent))) {
|
|
707
|
+
return Outcome$ReventlessGwt.pass;
|
|
708
|
+
} else {
|
|
709
|
+
return Outcome$ReventlessGwt.fail({
|
|
710
|
+
TAG: "EventsMismatch",
|
|
711
|
+
expected: expected.map(encEvent),
|
|
712
|
+
actual: __x.map(encEvent)
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
let thenCompareEvents = (events, expected, cmp) => unlessRefused(events, __x => {
|
|
717
|
+
if (errors.contents.length !== 0) {
|
|
718
|
+
return unexpectedError(__x);
|
|
719
|
+
} else if (__x.length === expected.length && Stdlib_Array.zip(__x, expected).every(param => cmp(param[0], param[1]))) {
|
|
720
|
+
return Outcome$ReventlessGwt.pass;
|
|
721
|
+
} else {
|
|
722
|
+
return Outcome$ReventlessGwt.fail({
|
|
723
|
+
TAG: "EventsMismatch",
|
|
724
|
+
expected: expected.map(encEvent),
|
|
725
|
+
actual: __x.map(encEvent)
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
let thenEvent = (events, expectedEvent) => thenEvents(events, [expectedEvent]);
|
|
730
|
+
let thenCompareEvent = (events, expectedEvent, cmp) => thenCompareEvents(events, [expectedEvent], cmp);
|
|
731
|
+
let thenNoEvent = events => unlessRefused(events, compareNoEvent);
|
|
732
|
+
let thenError = (events, expectedError) => unlessRefused(events, __x => matchesError(__x, [], expectedError));
|
|
733
|
+
let thenEventWithError = (events, expectedEvent, expectedError) => unlessRefused(events, __x => matchesError(__x, [expectedEvent], expectedError));
|
|
734
|
+
let thenEventsWithError = (events, expectedEvents, expectedError) => unlessRefused(events, __x => matchesError(__x, expectedEvents, expectedError));
|
|
735
|
+
let thenRefused = events => {
|
|
736
|
+
let decided = Stdlib_Option.map(errors.contents[0], encError);
|
|
737
|
+
let match = refusal.contents;
|
|
738
|
+
if (match !== undefined) {
|
|
739
|
+
return Outcome$ReventlessGwt.pass;
|
|
740
|
+
} else {
|
|
741
|
+
return Outcome$ReventlessGwt.fail({
|
|
742
|
+
TAG: "ErrorMismatch",
|
|
743
|
+
expected: Object.fromEntries([[
|
|
744
|
+
"errorCode",
|
|
745
|
+
"Forbidden"
|
|
746
|
+
]]),
|
|
747
|
+
actual: decided,
|
|
748
|
+
actualEvents: encEvents(events)
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
};
|
|
499
752
|
return {
|
|
500
753
|
Spec: Spec,
|
|
501
754
|
describe: JestBind$ReventlessGwt.describe,
|
|
502
755
|
test: test,
|
|
503
756
|
givenEvents: givenEvents,
|
|
757
|
+
Caller: Caller,
|
|
758
|
+
asCaller: asCaller,
|
|
504
759
|
whenCmd: whenCmd,
|
|
505
760
|
thenEvent: thenEvent,
|
|
506
761
|
thenCompareEvent: thenCompareEvent,
|
|
507
|
-
thenNoEvent:
|
|
762
|
+
thenNoEvent: thenNoEvent,
|
|
508
763
|
thenEventWithError: thenEventWithError,
|
|
509
|
-
thenEvents:
|
|
510
|
-
thenCompareEvents:
|
|
764
|
+
thenEvents: thenEvents,
|
|
765
|
+
thenCompareEvents: thenCompareEvents,
|
|
511
766
|
thenEventsWithError: thenEventsWithError,
|
|
512
|
-
thenError: thenError
|
|
767
|
+
thenError: thenError,
|
|
768
|
+
thenRefused: thenRefused
|
|
513
769
|
};
|
|
514
770
|
};
|
|
515
771
|
}
|
|
516
772
|
|
|
517
773
|
export {
|
|
774
|
+
Caller,
|
|
775
|
+
Acting,
|
|
518
776
|
encodeTag,
|
|
519
777
|
encodeQueryItem,
|
|
520
778
|
encodeQuery,
|
|
@@ -523,4 +781,4 @@ export {
|
|
|
523
781
|
Make,
|
|
524
782
|
MakeFromAggregate,
|
|
525
783
|
}
|
|
526
|
-
/*
|
|
784
|
+
/* Owner-Reventless Not a pure module */
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// `Caller.owner` takes an id as a scenario holds it, typed or not, so a test
|
|
4
|
+
// never converts one by hand.
|
|
5
|
+
|
|
6
|
+
module CustomerId = Reventless.Id.Make({
|
|
7
|
+
let key = "customerId"
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
describe("Caller.owner", () => {
|
|
11
|
+
testSync("takes a typed id as the user id it stands for", () =>
|
|
12
|
+
expect(Behavior_GWT.Caller.owner(CustomerId.makeFromString("c1")))->toEqual(
|
|
13
|
+
Reventless.Message.CallerClaim.Owned({userId: "c1"}),
|
|
14
|
+
)
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
testSync("takes a plain string too", () =>
|
|
18
|
+
expect(Behavior_GWT.Caller.owner("c1"))->toEqual(
|
|
19
|
+
Reventless.Message.CallerClaim.Owned({userId: "c1"}),
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
// Anything that is not a string at runtime is not an id, and comparing it
|
|
24
|
+
// with a recorded owner would only ever refuse — a silent wrong answer.
|
|
25
|
+
testSync("refuses a value that is not an id", () =>
|
|
26
|
+
expect(
|
|
27
|
+
switch Behavior_GWT.Caller.owner(42) {
|
|
28
|
+
| _ => false
|
|
29
|
+
| exception _ => true
|
|
30
|
+
},
|
|
31
|
+
)->toBe(true)
|
|
32
|
+
)
|
|
33
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
4
|
+
import * as Behavior_GWT$ReventlessGwt from "../src/Behavior_GWT.res.mjs";
|
|
5
|
+
|
|
6
|
+
let CustomerId = Id$Reventless.Make({
|
|
7
|
+
key: "customerId"
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
globalThis.describe("Caller.owner", () => {
|
|
11
|
+
globalThis.test("takes a typed id as the user id it stands for", () => {
|
|
12
|
+
globalThis.expect(Behavior_GWT$ReventlessGwt.Caller.owner(CustomerId.makeFromString("c1"))).toEqual({
|
|
13
|
+
TAG: "Owned",
|
|
14
|
+
userId: "c1"
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
globalThis.test("takes a plain string too", () => {
|
|
18
|
+
globalThis.expect(Behavior_GWT$ReventlessGwt.Caller.owner("c1")).toEqual({
|
|
19
|
+
TAG: "Owned",
|
|
20
|
+
userId: "c1"
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
globalThis.test("refuses a value that is not an id", () => {
|
|
24
|
+
let tmp;
|
|
25
|
+
try {
|
|
26
|
+
Behavior_GWT$ReventlessGwt.Caller.owner(42);
|
|
27
|
+
tmp = false;
|
|
28
|
+
} catch (exn) {
|
|
29
|
+
tmp = true;
|
|
30
|
+
}
|
|
31
|
+
globalThis.expect(tmp).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
CustomerId,
|
|
37
|
+
}
|
|
38
|
+
/* CustomerId Not a pure module */
|
|
@@ -39,6 +39,10 @@ let Spec = include.Spec;
|
|
|
39
39
|
|
|
40
40
|
let todo = include.todo;
|
|
41
41
|
|
|
42
|
+
let Caller = include.Caller;
|
|
43
|
+
|
|
44
|
+
let asCaller = include.asCaller;
|
|
45
|
+
|
|
42
46
|
let thenEvents = include.thenEvents;
|
|
43
47
|
|
|
44
48
|
let thenNoEvent = include.thenNoEvent;
|
|
@@ -47,6 +51,8 @@ let thenEventWithError = include.thenEventWithError;
|
|
|
47
51
|
|
|
48
52
|
let thenEventsWithError = include.thenEventsWithError;
|
|
49
53
|
|
|
54
|
+
let thenRefused = include.thenRefused;
|
|
55
|
+
|
|
50
56
|
let thenAppendsConditionedOn = include.thenAppendsConditionedOn;
|
|
51
57
|
|
|
52
58
|
let thenAppendsConditionedOnExactly = include.thenAppendsConditionedOnExactly;
|
|
@@ -57,6 +63,8 @@ export {
|
|
|
57
63
|
todo,
|
|
58
64
|
test,
|
|
59
65
|
givenEvents,
|
|
66
|
+
Caller,
|
|
67
|
+
asCaller,
|
|
60
68
|
whenCmd,
|
|
61
69
|
thenEvent,
|
|
62
70
|
thenEvents,
|
|
@@ -64,6 +72,7 @@ export {
|
|
|
64
72
|
thenEventWithError,
|
|
65
73
|
thenEventsWithError,
|
|
66
74
|
thenError,
|
|
75
|
+
thenRefused,
|
|
67
76
|
thenAppendsConditionedOn,
|
|
68
77
|
thenAppendsConditionedOnExactly,
|
|
69
78
|
}
|
|
@@ -23,6 +23,10 @@ let Spec = include.Spec;
|
|
|
23
23
|
|
|
24
24
|
let todo = include.todo;
|
|
25
25
|
|
|
26
|
+
let Caller = include.Caller;
|
|
27
|
+
|
|
28
|
+
let asCaller = include.asCaller;
|
|
29
|
+
|
|
26
30
|
let thenEvents = include.thenEvents;
|
|
27
31
|
|
|
28
32
|
let thenNoEvent = include.thenNoEvent;
|
|
@@ -33,6 +37,8 @@ let thenEventsWithError = include.thenEventsWithError;
|
|
|
33
37
|
|
|
34
38
|
let thenError = include.thenError;
|
|
35
39
|
|
|
40
|
+
let thenRefused = include.thenRefused;
|
|
41
|
+
|
|
36
42
|
let thenAppendsConditionedOn = include.thenAppendsConditionedOn;
|
|
37
43
|
|
|
38
44
|
let thenAppendsConditionedOnExactly = include.thenAppendsConditionedOnExactly;
|
|
@@ -43,6 +49,8 @@ export {
|
|
|
43
49
|
todo,
|
|
44
50
|
test,
|
|
45
51
|
givenEvents,
|
|
52
|
+
Caller,
|
|
53
|
+
asCaller,
|
|
46
54
|
whenCmd,
|
|
47
55
|
thenEvent,
|
|
48
56
|
thenEvents,
|
|
@@ -50,6 +58,7 @@ export {
|
|
|
50
58
|
thenEventWithError,
|
|
51
59
|
thenEventsWithError,
|
|
52
60
|
thenError,
|
|
61
|
+
thenRefused,
|
|
53
62
|
thenAppendsConditionedOn,
|
|
54
63
|
thenAppendsConditionedOnExactly,
|
|
55
64
|
}
|
|
@@ -23,6 +23,10 @@ let Spec = include.Spec;
|
|
|
23
23
|
|
|
24
24
|
let todo = include.todo;
|
|
25
25
|
|
|
26
|
+
let Caller = include.Caller;
|
|
27
|
+
|
|
28
|
+
let asCaller = include.asCaller;
|
|
29
|
+
|
|
26
30
|
let thenEvents = include.thenEvents;
|
|
27
31
|
|
|
28
32
|
let thenNoEvent = include.thenNoEvent;
|
|
@@ -33,6 +37,8 @@ let thenEventsWithError = include.thenEventsWithError;
|
|
|
33
37
|
|
|
34
38
|
let thenError = include.thenError;
|
|
35
39
|
|
|
40
|
+
let thenRefused = include.thenRefused;
|
|
41
|
+
|
|
36
42
|
let thenAppendsConditionedOn = include.thenAppendsConditionedOn;
|
|
37
43
|
|
|
38
44
|
let thenAppendsConditionedOnExactly = include.thenAppendsConditionedOnExactly;
|
|
@@ -43,6 +49,8 @@ export {
|
|
|
43
49
|
todo,
|
|
44
50
|
test,
|
|
45
51
|
givenEvents,
|
|
52
|
+
Caller,
|
|
53
|
+
asCaller,
|
|
46
54
|
whenCmd,
|
|
47
55
|
thenEvent,
|
|
48
56
|
thenEvents,
|
|
@@ -50,6 +58,7 @@ export {
|
|
|
50
58
|
thenEventWithError,
|
|
51
59
|
thenEventsWithError,
|
|
52
60
|
thenError,
|
|
61
|
+
thenRefused,
|
|
53
62
|
thenAppendsConditionedOn,
|
|
54
63
|
thenAppendsConditionedOnExactly,
|
|
55
64
|
}
|
|
@@ -405,6 +405,10 @@ let Spec = include.Spec;
|
|
|
405
405
|
|
|
406
406
|
let todo = include.todo;
|
|
407
407
|
|
|
408
|
+
let Caller = include.Caller;
|
|
409
|
+
|
|
410
|
+
let asCaller = include.asCaller;
|
|
411
|
+
|
|
408
412
|
let thenEvents = include.thenEvents;
|
|
409
413
|
|
|
410
414
|
let thenNoEvent = include.thenNoEvent;
|
|
@@ -413,6 +417,8 @@ let thenEventWithError = include.thenEventWithError;
|
|
|
413
417
|
|
|
414
418
|
let thenEventsWithError = include.thenEventsWithError;
|
|
415
419
|
|
|
420
|
+
let thenRefused = include.thenRefused;
|
|
421
|
+
|
|
416
422
|
export {
|
|
417
423
|
AddCategorySlice,
|
|
418
424
|
AddCategorySliceBehavior,
|
|
@@ -421,6 +427,8 @@ export {
|
|
|
421
427
|
todo,
|
|
422
428
|
test,
|
|
423
429
|
givenEvents,
|
|
430
|
+
Caller,
|
|
431
|
+
asCaller,
|
|
424
432
|
whenCmd,
|
|
425
433
|
thenEvent,
|
|
426
434
|
thenEvents,
|
|
@@ -428,6 +436,7 @@ export {
|
|
|
428
436
|
thenEventWithError,
|
|
429
437
|
thenEventsWithError,
|
|
430
438
|
thenError,
|
|
439
|
+
thenRefused,
|
|
431
440
|
thenAppendsConditionedOn,
|
|
432
441
|
thenAppendsConditionedOnExactly,
|
|
433
442
|
MissingTagSlice,
|