@reventlessdev/online-shop-hybrid-ordering 1.0.0-alpha.246 → 1.0.0-alpha.248
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/package.json +10 -10
- package/tests/CatalogProduct/StateChange/SyncCatalogProduct_GWT.res +35 -32
- package/tests/CatalogProduct/StateChange/SyncCatalogProduct_GWT.res.mjs +46 -55
- package/tests/CatalogProduct/StateViewStream/AvailableProducts_GWT.res +16 -13
- package/tests/CatalogProduct/StateViewStream/AvailableProducts_GWT.res.mjs +29 -37
- package/tests/Customer/Aggregate/Customer_GWT.res +60 -36
- package/tests/Customer/Aggregate/Customer_GWT.res.mjs +56 -55
- package/tests/Customer/OutboundTranslation/GeocodeCustomerAddress_GWT.res +30 -18
- package/tests/Customer/OutboundTranslation/GeocodeCustomerAddress_GWT.res.mjs +22 -19
- package/tests/Customer/OutboundTranslation/VerifyCustomerEmail_GWT.res +6 -0
- package/tests/Customer/ReadModelStream/Customers_GWT.res +24 -15
- package/tests/Customer/ReadModelStream/Customers_GWT.res.mjs +17 -24
- package/tests/Customer/StateChange/EmailVerificationChallenges_GWT.res +45 -45
- package/tests/Customer/StateChange/EmailVerificationChallenges_GWT.res.mjs +42 -47
- package/tests/Extension/Products_Extension_GWT.res +23 -6
- package/tests/Extension/Products_Extension_GWT.res.mjs +9 -17
- package/tests/ExtensionPoint/Orders_ExtensionPointMapping_GWT.res +7 -7
- package/tests/ExtensionPoint/Orders_ExtensionPointMapping_GWT.res.mjs +8 -19
- package/tests/Flow/OrderingFlow_GWT.res +29 -30
- package/tests/Flow/OrderingFlow_GWT.res.mjs +28 -43
- package/tests/Notification/Automation/NotificationIntake_GWT.res +28 -20
- package/tests/Notification/Automation/NotificationIntake_GWT.res.mjs +24 -31
- package/tests/Notification/OutboundTranslation/SendNotification_GWT.res +18 -10
- package/tests/Notification/OutboundTranslation/SendNotification_GWT.res.mjs +13 -12
- package/tests/Notification/StateChange/NotificationPreferences_GWT.res +40 -28
- package/tests/Notification/StateChange/NotificationPreferences_GWT.res.mjs +33 -32
- package/tests/Notification/StateChange/NotificationSourceClaims_GWT.res +10 -3
- package/tests/Notification/StateChange/NotificationSourceClaims_GWT.res.mjs +4 -3
- package/tests/Notification/StateViewStream/NotificationDeliveries_GWT.res +45 -15
- package/tests/Notification/StateViewStream/NotificationDeliveries_GWT.res.mjs +42 -19
- package/tests/Order/Automation/AutoShipOrder_GWT.res +17 -10
- package/tests/Order/Automation/AutoShipOrder_GWT.res.mjs +10 -13
- package/tests/Order/StateChange/CancelOrder_GWT.res +25 -18
- package/tests/Order/StateChange/CancelOrder_GWT.res.mjs +20 -27
- package/tests/Order/StateChange/PlaceOrder_Examples.res +13 -0
- package/tests/Order/StateChange/PlaceOrder_Examples.res.mjs +16 -0
- package/tests/Order/StateChange/PlaceOrder_GWT.res +449 -243
- package/tests/Order/StateChange/PlaceOrder_GWT.res.mjs +509 -289
- package/tests/Order/StateChange/ShipOrder_GWT.res +17 -18
- package/tests/Order/StateChange/ShipOrder_GWT.res.mjs +18 -29
- package/tests/Order/StateViewStream/Orders_GWT.res +71 -68
- package/tests/Order/StateViewStream/Orders_GWT.res.mjs +61 -80
- package/tests/Ordering_Examples.res +49 -0
- package/tests/Ordering_Examples.res.mjs +103 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
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.248 (2026-09-23)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @reventlessdev/online-shop-hybrid-ordering
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# 1.0.0-alpha.247 (2026-09-22)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @reventlessdev/online-shop-hybrid-ordering
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
# 1.0.0-alpha.246 (2026-09-21)
|
|
7
23
|
|
|
8
24
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/online-shop-hybrid-ordering",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.248",
|
|
4
4
|
"description": "Online Shop Hybrid example — Ordering plugin (Customer aggregate + Order/CatalogProduct DCB)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -21,20 +21,20 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"sury": "11.0.0",
|
|
24
|
-
"@reventlessdev/online-shop-hybrid-catalog-spec": "1.0.0-alpha.
|
|
24
|
+
"@reventlessdev/online-shop-hybrid-catalog-spec": "1.0.0-alpha.139",
|
|
25
|
+
"@reventlessdev/online-shop-hybrid-ordering-spec": "1.0.0-alpha.138",
|
|
26
|
+
"@reventlessdev/reventless-gwt": "1.0.0-alpha.217",
|
|
25
27
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.22",
|
|
26
|
-
"@reventlessdev/reventless-
|
|
27
|
-
"@reventlessdev/reventless-
|
|
28
|
-
"@reventlessdev/
|
|
29
|
-
"@reventlessdev/
|
|
30
|
-
"@reventlessdev/trait-
|
|
31
|
-
"@reventlessdev/trait-notification": "1.0.0-alpha.18",
|
|
32
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.141"
|
|
28
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.170",
|
|
29
|
+
"@reventlessdev/reventless-local": "3.0.0-alpha.260",
|
|
30
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.142",
|
|
31
|
+
"@reventlessdev/trait-address-geocoding": "1.0.0-alpha.19",
|
|
32
|
+
"@reventlessdev/trait-notification": "1.0.0-alpha.19"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"rescript": "12.3.1",
|
|
36
36
|
"sury-ppx": "11.0.0",
|
|
37
|
-
"@reventlessdev/reventless-ppx": "1.0.0-alpha.
|
|
37
|
+
"@reventlessdev/reventless-ppx": "1.0.0-alpha.89"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"rescript": "^12.3.1"
|
|
@@ -1,86 +1,89 @@
|
|
|
1
1
|
@@reventless.gwt
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
open Ordering_Examples
|
|
4
4
|
|
|
5
5
|
// Prices are money, so a test writes the amount a person would say and converts
|
|
6
6
|
// it once. `ofMajor` scales by the currency's own exponent, which is what keeps
|
|
7
7
|
// the literal honest: 9.99 EUR is 999 cents, and the same call on a JPY price
|
|
8
8
|
// would scale by 1.
|
|
9
|
-
let eur = amount => Reventless.Money.ofMajor(~amount, ~currency=EUR)
|
|
10
9
|
|
|
11
10
|
describe("SyncCatalogProduct StateChangeSlice", () => {
|
|
11
|
+
// scenario-id: 8dfa1851-7779-4c9d-beeb-674e0664e16a
|
|
12
12
|
test("SyncNewProduct produces CatalogProductSynced", () =>
|
|
13
13
|
givenEvents([])
|
|
14
|
-
->whenCmd(SyncNewProduct({productId:
|
|
15
|
-
->thenEvent(CatalogProductSynced({productId:
|
|
14
|
+
->whenCmd(SyncNewProduct({productId: p1, name: laptop, price: laptopPrice}))
|
|
15
|
+
->thenEvent(CatalogProductSynced({productId: p1, name: laptop, price: laptopPrice}))
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
+
// scenario-id: c160117a-1887-4d22-ae68-49751a19a632
|
|
18
19
|
test("ChangeSyncedPrice produces CatalogProductPriceChanged", () =>
|
|
19
|
-
givenEvents([CatalogProductSynced({name:
|
|
20
|
-
->whenCmd(ChangeSyncedPrice({productId:
|
|
21
|
-
->thenEvent(CatalogProductPriceChanged({productId:
|
|
20
|
+
givenEvents([CatalogProductSynced({name: laptop, price: laptopPrice})])
|
|
21
|
+
->whenCmd(ChangeSyncedPrice({productId: p1, price: laptopChangedPrice}))
|
|
22
|
+
->thenEvent(CatalogProductPriceChanged({productId: p1, price: laptopChangedPrice}))
|
|
22
23
|
)
|
|
23
24
|
|
|
25
|
+
// scenario-id: 76ba6d75-b136-413c-b225-d8f40b0fda6c
|
|
24
26
|
test("re-syncing identical product data produces no events (idempotent)", () =>
|
|
25
|
-
givenEvents([CatalogProductSynced({name:
|
|
26
|
-
->whenCmd(SyncNewProduct({productId:
|
|
27
|
+
givenEvents([CatalogProductSynced({name: laptop, price: laptopPrice})])
|
|
28
|
+
->whenCmd(SyncNewProduct({productId: p1, name: laptop, price: laptopPrice}))
|
|
27
29
|
->thenNoEvent
|
|
28
30
|
)
|
|
29
31
|
|
|
32
|
+
// scenario-id: 0ed2ff32-af9f-47b4-88e7-1cea906dca7b
|
|
30
33
|
test("re-applying the current price produces no events (idempotent)", () =>
|
|
31
|
-
givenEvents([CatalogProductSynced({name:
|
|
32
|
-
->whenCmd(ChangeSyncedPrice({productId:
|
|
34
|
+
givenEvents([CatalogProductSynced({name: laptop, price: laptopPrice})])
|
|
35
|
+
->whenCmd(ChangeSyncedPrice({productId: p1, price: laptopPrice}))
|
|
33
36
|
->thenNoEvent
|
|
34
37
|
)
|
|
35
38
|
|
|
39
|
+
// scenario-id: beb2c75d-f258-49e9-bf75-949c00297f86
|
|
36
40
|
test("WithdrawSyncedProduct produces CatalogProductWithdrawn", () =>
|
|
37
|
-
givenEvents([CatalogProductSynced({name:
|
|
38
|
-
->whenCmd(WithdrawSyncedProduct({productId:
|
|
39
|
-
->thenEvent(CatalogProductWithdrawn({productId:
|
|
41
|
+
givenEvents([CatalogProductSynced({name: laptop, price: laptopPrice})])
|
|
42
|
+
->whenCmd(WithdrawSyncedProduct({productId: p1}))
|
|
43
|
+
->thenEvent(CatalogProductWithdrawn({productId: p1}))
|
|
40
44
|
)
|
|
41
45
|
|
|
46
|
+
// scenario-id: 224a3bf7-0231-4ee4-ad3e-6ace53abdc13
|
|
42
47
|
test("withdrawing an already-withdrawn product produces no events", () =>
|
|
43
|
-
givenEvents([
|
|
44
|
-
|
|
45
|
-
CatalogProductWithdrawn,
|
|
46
|
-
])
|
|
47
|
-
->whenCmd(WithdrawSyncedProduct({productId: pid("p1")}))
|
|
48
|
+
givenEvents([CatalogProductSynced({name: laptop, price: laptopPrice}), CatalogProductWithdrawn])
|
|
49
|
+
->whenCmd(WithdrawSyncedProduct({productId: p1}))
|
|
48
50
|
->thenNoEvent
|
|
49
51
|
)
|
|
50
52
|
|
|
51
53
|
// The assertion that pins the shadow as the source. Ordering restores the name
|
|
52
54
|
// and price it kept through the withdrawal — Catalog's `ProductRelisted` carries
|
|
53
55
|
// neither, and is never asked to.
|
|
56
|
+
// scenario-id: c475bba4-415d-4461-bbaf-f511e4999b1e
|
|
54
57
|
test("RelistSyncedProduct restores name and price from the shadow", () =>
|
|
55
|
-
givenEvents([
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
])
|
|
59
|
-
->whenCmd(RelistSyncedProduct({productId: pid("p1")}))
|
|
60
|
-
->thenEvent(CatalogProductRelisted({productId: pid("p1"), name: "Laptop", price: eur(999.99)}))
|
|
58
|
+
givenEvents([CatalogProductSynced({name: laptop, price: laptopPrice}), CatalogProductWithdrawn])
|
|
59
|
+
->whenCmd(RelistSyncedProduct({productId: p1}))
|
|
60
|
+
->thenEvent(CatalogProductRelisted({productId: p1, name: laptop, price: laptopPrice}))
|
|
61
61
|
)
|
|
62
62
|
|
|
63
63
|
// And the latest price, not the one it was first synced at: the shadow keeps
|
|
64
64
|
// being maintained while the product is off the shelf.
|
|
65
|
+
// scenario-id: e8087d7c-15a7-416b-9662-1f0fc1e12b36
|
|
65
66
|
test("and restores the price it was last repriced to", () =>
|
|
66
67
|
givenEvents([
|
|
67
|
-
CatalogProductSynced({name:
|
|
68
|
-
CatalogProductPriceChanged({price:
|
|
68
|
+
CatalogProductSynced({name: laptop, price: laptopPrice}),
|
|
69
|
+
CatalogProductPriceChanged({price: laptopChangedPrice}),
|
|
69
70
|
CatalogProductWithdrawn,
|
|
70
71
|
])
|
|
71
|
-
->whenCmd(RelistSyncedProduct({productId:
|
|
72
|
-
->thenEvent(CatalogProductRelisted({productId:
|
|
72
|
+
->whenCmd(RelistSyncedProduct({productId: p1}))
|
|
73
|
+
->thenEvent(CatalogProductRelisted({productId: p1, name: laptop, price: laptopChangedPrice}))
|
|
73
74
|
)
|
|
74
75
|
|
|
76
|
+
// scenario-id: 832afe90-b0e1-4f59-8ae5-d7ee17eb8f2a
|
|
75
77
|
test("relisting a product that is not withdrawn produces no events", () =>
|
|
76
|
-
givenEvents([CatalogProductSynced({name:
|
|
77
|
-
->whenCmd(RelistSyncedProduct({productId:
|
|
78
|
+
givenEvents([CatalogProductSynced({name: laptop, price: laptopPrice})])
|
|
79
|
+
->whenCmd(RelistSyncedProduct({productId: p1}))
|
|
78
80
|
->thenNoEvent
|
|
79
81
|
)
|
|
80
82
|
|
|
81
83
|
// Nothing to restore, so nothing is invented: a row with a made-up name and a
|
|
82
84
|
// zero price would appear in the shopper's catalog backed by no Catalog product.
|
|
85
|
+
// scenario-id: e44fa5b2-b90c-43cc-bf7b-924ba3582c8a
|
|
83
86
|
test("relisting a product never synced produces no events", () =>
|
|
84
|
-
givenEvents([])->whenCmd(RelistSyncedProduct({productId:
|
|
87
|
+
givenEvents([])->whenCmd(RelistSyncedProduct({productId: p1}))->thenNoEvent
|
|
85
88
|
)
|
|
86
89
|
})
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as Money$Reventless from "@reventlessdev/reventless-spec/src/semantic/Money.res.mjs";
|
|
4
|
-
import * as ProductId$CatalogSpec from "@reventlessdev/online-shop-hybrid-catalog-spec/src/ProductId.res.mjs";
|
|
5
3
|
import * as Behavior_GWT$ReventlessGwt from "@reventlessdev/reventless-gwt/src/Behavior_GWT.res.mjs";
|
|
4
|
+
import * as Ordering_Examples$OrderingPlugin from "../../Ordering_Examples.res.mjs";
|
|
6
5
|
import * as SyncCatalogProduct$OrderingPlugin from "../../../src/CatalogProduct/StateChange/SyncCatalogProduct.res.mjs";
|
|
7
6
|
import * as SyncCatalogProduct_Behavior$OrderingPlugin from "../../../src/CatalogProduct/StateChange/SyncCatalogProduct_Behavior.res.mjs";
|
|
8
7
|
|
|
@@ -30,123 +29,119 @@ let thenEvent = include.thenEvent;
|
|
|
30
29
|
|
|
31
30
|
let thenNoEvent = include.thenNoEvent;
|
|
32
31
|
|
|
33
|
-
function eur(amount) {
|
|
34
|
-
return Money$Reventless.ofMajor(amount, "EUR");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
32
|
describe("SyncCatalogProduct StateChangeSlice", () => {
|
|
38
33
|
test("SyncNewProduct produces CatalogProductSynced", () => thenEvent(whenCmd(givenEvents([]), {
|
|
39
34
|
TAG: "SyncNewProduct",
|
|
40
|
-
productId:
|
|
41
|
-
name:
|
|
42
|
-
price:
|
|
35
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
36
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
37
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
43
38
|
}), {
|
|
44
39
|
TAG: "CatalogProductSynced",
|
|
45
|
-
productId:
|
|
46
|
-
name:
|
|
47
|
-
price:
|
|
40
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
41
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
42
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
48
43
|
}));
|
|
49
44
|
test("ChangeSyncedPrice produces CatalogProductPriceChanged", () => thenEvent(whenCmd(givenEvents([{
|
|
50
45
|
TAG: "CatalogProductSynced",
|
|
51
|
-
name:
|
|
52
|
-
price:
|
|
46
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
47
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
53
48
|
}]), {
|
|
54
49
|
TAG: "ChangeSyncedPrice",
|
|
55
|
-
productId:
|
|
56
|
-
price:
|
|
50
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
51
|
+
price: Ordering_Examples$OrderingPlugin.laptopChangedPrice
|
|
57
52
|
}), {
|
|
58
53
|
TAG: "CatalogProductPriceChanged",
|
|
59
|
-
productId:
|
|
60
|
-
price:
|
|
54
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
55
|
+
price: Ordering_Examples$OrderingPlugin.laptopChangedPrice
|
|
61
56
|
}));
|
|
62
57
|
test("re-syncing identical product data produces no events (idempotent)", () => thenNoEvent(whenCmd(givenEvents([{
|
|
63
58
|
TAG: "CatalogProductSynced",
|
|
64
|
-
name:
|
|
65
|
-
price:
|
|
59
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
60
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
66
61
|
}]), {
|
|
67
62
|
TAG: "SyncNewProduct",
|
|
68
|
-
productId:
|
|
69
|
-
name:
|
|
70
|
-
price:
|
|
63
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
64
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
65
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
71
66
|
})));
|
|
72
67
|
test("re-applying the current price produces no events (idempotent)", () => thenNoEvent(whenCmd(givenEvents([{
|
|
73
68
|
TAG: "CatalogProductSynced",
|
|
74
|
-
name:
|
|
75
|
-
price:
|
|
69
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
70
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
76
71
|
}]), {
|
|
77
72
|
TAG: "ChangeSyncedPrice",
|
|
78
|
-
productId:
|
|
79
|
-
price:
|
|
73
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
74
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
80
75
|
})));
|
|
81
76
|
test("WithdrawSyncedProduct produces CatalogProductWithdrawn", () => thenEvent(whenCmd(givenEvents([{
|
|
82
77
|
TAG: "CatalogProductSynced",
|
|
83
|
-
name:
|
|
84
|
-
price:
|
|
78
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
79
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
85
80
|
}]), {
|
|
86
81
|
TAG: "WithdrawSyncedProduct",
|
|
87
|
-
productId:
|
|
82
|
+
productId: Ordering_Examples$OrderingPlugin.p1
|
|
88
83
|
}), {
|
|
89
84
|
TAG: "CatalogProductWithdrawn",
|
|
90
|
-
productId:
|
|
85
|
+
productId: Ordering_Examples$OrderingPlugin.p1
|
|
91
86
|
}));
|
|
92
87
|
test("withdrawing an already-withdrawn product produces no events", () => thenNoEvent(whenCmd(givenEvents([
|
|
93
88
|
{
|
|
94
89
|
TAG: "CatalogProductSynced",
|
|
95
|
-
name:
|
|
96
|
-
price:
|
|
90
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
91
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
97
92
|
},
|
|
98
93
|
"CatalogProductWithdrawn"
|
|
99
94
|
]), {
|
|
100
95
|
TAG: "WithdrawSyncedProduct",
|
|
101
|
-
productId:
|
|
96
|
+
productId: Ordering_Examples$OrderingPlugin.p1
|
|
102
97
|
})));
|
|
103
98
|
test("RelistSyncedProduct restores name and price from the shadow", () => thenEvent(whenCmd(givenEvents([
|
|
104
99
|
{
|
|
105
100
|
TAG: "CatalogProductSynced",
|
|
106
|
-
name:
|
|
107
|
-
price:
|
|
101
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
102
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
108
103
|
},
|
|
109
104
|
"CatalogProductWithdrawn"
|
|
110
105
|
]), {
|
|
111
106
|
TAG: "RelistSyncedProduct",
|
|
112
|
-
productId:
|
|
107
|
+
productId: Ordering_Examples$OrderingPlugin.p1
|
|
113
108
|
}), {
|
|
114
109
|
TAG: "CatalogProductRelisted",
|
|
115
|
-
productId:
|
|
116
|
-
name:
|
|
117
|
-
price:
|
|
110
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
111
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
112
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
118
113
|
}));
|
|
119
114
|
test("and restores the price it was last repriced to", () => thenEvent(whenCmd(givenEvents([
|
|
120
115
|
{
|
|
121
116
|
TAG: "CatalogProductSynced",
|
|
122
|
-
name:
|
|
123
|
-
price:
|
|
117
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
118
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
124
119
|
},
|
|
125
120
|
{
|
|
126
121
|
TAG: "CatalogProductPriceChanged",
|
|
127
|
-
price:
|
|
122
|
+
price: Ordering_Examples$OrderingPlugin.laptopChangedPrice
|
|
128
123
|
},
|
|
129
124
|
"CatalogProductWithdrawn"
|
|
130
125
|
]), {
|
|
131
126
|
TAG: "RelistSyncedProduct",
|
|
132
|
-
productId:
|
|
127
|
+
productId: Ordering_Examples$OrderingPlugin.p1
|
|
133
128
|
}), {
|
|
134
129
|
TAG: "CatalogProductRelisted",
|
|
135
|
-
productId:
|
|
136
|
-
name:
|
|
137
|
-
price:
|
|
130
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
131
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
132
|
+
price: Ordering_Examples$OrderingPlugin.laptopChangedPrice
|
|
138
133
|
}));
|
|
139
134
|
test("relisting a product that is not withdrawn produces no events", () => thenNoEvent(whenCmd(givenEvents([{
|
|
140
135
|
TAG: "CatalogProductSynced",
|
|
141
|
-
name:
|
|
142
|
-
price:
|
|
136
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
137
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
143
138
|
}]), {
|
|
144
139
|
TAG: "RelistSyncedProduct",
|
|
145
|
-
productId:
|
|
140
|
+
productId: Ordering_Examples$OrderingPlugin.p1
|
|
146
141
|
})));
|
|
147
142
|
test("relisting a product never synced produces no events", () => thenNoEvent(whenCmd(givenEvents([]), {
|
|
148
143
|
TAG: "RelistSyncedProduct",
|
|
149
|
-
productId:
|
|
144
|
+
productId: Ordering_Examples$OrderingPlugin.p1
|
|
150
145
|
})));
|
|
151
146
|
});
|
|
152
147
|
|
|
@@ -166,8 +161,6 @@ let thenAppendsConditionedOn = include.thenAppendsConditionedOn;
|
|
|
166
161
|
|
|
167
162
|
let thenAppendsConditionedOnExactly = include.thenAppendsConditionedOnExactly;
|
|
168
163
|
|
|
169
|
-
let pid = ProductId$CatalogSpec.make;
|
|
170
|
-
|
|
171
164
|
export {
|
|
172
165
|
Spec,
|
|
173
166
|
describe,
|
|
@@ -183,7 +176,5 @@ export {
|
|
|
183
176
|
thenError,
|
|
184
177
|
thenAppendsConditionedOn,
|
|
185
178
|
thenAppendsConditionedOnExactly,
|
|
186
|
-
pid,
|
|
187
|
-
eur,
|
|
188
179
|
}
|
|
189
180
|
/* include Not a pure module */
|
|
@@ -1,42 +1,45 @@
|
|
|
1
1
|
@@reventless.gwt
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
open Ordering_Examples
|
|
4
4
|
|
|
5
5
|
// Prices are money, so a test writes the amount a person would say and converts
|
|
6
6
|
// it once. `ofMajor` scales by the currency's own exponent, which is what keeps
|
|
7
7
|
// the literal honest: 9.99 EUR is 999 cents, and the same call on a JPY price
|
|
8
8
|
// would scale by 1.
|
|
9
|
-
let eur = amount => Reventless.Money.ofMajor(~amount, ~currency=EUR)
|
|
10
9
|
|
|
11
10
|
describe("AvailableProducts StateViewSliceStream", () => {
|
|
11
|
+
// scenario-id: 3296c87a-2296-4561-aafc-078ea72e3613
|
|
12
12
|
test("CatalogProductSynced creates a row", () =>
|
|
13
13
|
givenEvents([])
|
|
14
|
-
->whenEvent(CatalogProductSynced({productId:
|
|
15
|
-
->thenStateWithId("p1", {productId:
|
|
14
|
+
->whenEvent(CatalogProductSynced({productId: p1, name: laptop, price: laptopPrice}))
|
|
15
|
+
->thenStateWithId("p1", {productId: p1, name: laptop, price: laptopPrice})
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
+
// scenario-id: dfe57c51-86f7-4536-b5a2-1cbbb9954ac9
|
|
18
19
|
test("CatalogProductPriceChanged updates the price", () =>
|
|
19
|
-
givenEvents([CatalogProductSynced({productId:
|
|
20
|
-
->whenEvent(CatalogProductPriceChanged({productId:
|
|
21
|
-
->thenStateWithId("p1", {productId:
|
|
20
|
+
givenEvents([CatalogProductSynced({productId: p1, name: laptop, price: laptopPrice})])
|
|
21
|
+
->whenEvent(CatalogProductPriceChanged({productId: p1, price: laptopChangedPrice}))
|
|
22
|
+
->thenStateWithId("p1", {productId: p1, name: laptop, price: laptopChangedPrice})
|
|
22
23
|
)
|
|
23
24
|
|
|
24
25
|
// The row leaves the view rather than being marked. This answers "what can I
|
|
25
26
|
// order", and an operator reading it is elevated — a `@retired` flag here would
|
|
26
27
|
// hide a withdrawn product from the shopper correctly and show it to the
|
|
27
28
|
// operator, in the one catalog that is supposed to be the shopper's.
|
|
29
|
+
// scenario-id: c8a6785b-6fe5-4c03-a0e9-ddc5f32ba867
|
|
28
30
|
test("CatalogProductWithdrawn removes the row", () =>
|
|
29
|
-
givenEvents([CatalogProductSynced({productId:
|
|
30
|
-
->whenEvent(CatalogProductWithdrawn({productId:
|
|
31
|
+
givenEvents([CatalogProductSynced({productId: p1, name: laptop, price: laptopPrice})])
|
|
32
|
+
->whenEvent(CatalogProductWithdrawn({productId: p1}))
|
|
31
33
|
->thenNoState
|
|
32
34
|
)
|
|
33
35
|
|
|
36
|
+
// scenario-id: 8f1acb16-da53-4243-8a09-4af89b226919
|
|
34
37
|
test("CatalogProductRelisted puts it back with its name and price", () =>
|
|
35
38
|
givenEvents([
|
|
36
|
-
CatalogProductSynced({productId:
|
|
37
|
-
CatalogProductWithdrawn({productId:
|
|
39
|
+
CatalogProductSynced({productId: p1, name: laptop, price: laptopPrice}),
|
|
40
|
+
CatalogProductWithdrawn({productId: p1}),
|
|
38
41
|
])
|
|
39
|
-
->whenEvent(CatalogProductRelisted({productId:
|
|
40
|
-
->thenStateWithId("p1", {productId:
|
|
42
|
+
->whenEvent(CatalogProductRelisted({productId: p1, name: laptop, price: laptopChangedPrice}))
|
|
43
|
+
->thenStateWithId("p1", {productId: p1, name: laptop, price: laptopChangedPrice})
|
|
41
44
|
)
|
|
42
45
|
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as Money$Reventless from "@reventlessdev/reventless-spec/src/semantic/Money.res.mjs";
|
|
4
3
|
import * as ProductId$CatalogSpec from "@reventlessdev/online-shop-hybrid-catalog-spec/src/ProductId.res.mjs";
|
|
5
4
|
import * as Projection_GWT$ReventlessGwt from "@reventlessdev/reventless-gwt/src/Projection_GWT.res.mjs";
|
|
6
5
|
import * as AvailableProducts$OrderingPlugin from "../../../src/CatalogProduct/StateViewStream/AvailableProducts.res.mjs";
|
|
6
|
+
import * as Ordering_Examples$OrderingPlugin from "../../Ordering_Examples.res.mjs";
|
|
7
7
|
import * as AvailableProducts_Projection$OrderingPlugin from "../../../src/CatalogProduct/StateViewStream/AvailableProducts_Projection.res.mjs";
|
|
8
8
|
|
|
9
9
|
let include = Projection_GWT$ReventlessGwt.Make({
|
|
@@ -28,64 +28,60 @@ let thenStateWithId = include.thenStateWithId;
|
|
|
28
28
|
|
|
29
29
|
let thenNoState = include.thenNoState;
|
|
30
30
|
|
|
31
|
-
function eur(amount) {
|
|
32
|
-
return Money$Reventless.ofMajor(amount, "EUR");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
31
|
describe("AvailableProducts StateViewSliceStream", () => {
|
|
36
32
|
test("CatalogProductSynced creates a row", undefined, () => thenStateWithId(whenEvent(givenEvents([]), {
|
|
37
33
|
TAG: "CatalogProductSynced",
|
|
38
|
-
productId:
|
|
39
|
-
name:
|
|
40
|
-
price:
|
|
34
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
35
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
36
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
41
37
|
}), "p1", {
|
|
42
|
-
productId:
|
|
43
|
-
name:
|
|
44
|
-
price:
|
|
38
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
39
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
40
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
45
41
|
}));
|
|
46
42
|
test("CatalogProductPriceChanged updates the price", undefined, () => thenStateWithId(whenEvent(givenEvents([{
|
|
47
43
|
TAG: "CatalogProductSynced",
|
|
48
|
-
productId:
|
|
49
|
-
name:
|
|
50
|
-
price:
|
|
44
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
45
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
46
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
51
47
|
}]), {
|
|
52
48
|
TAG: "CatalogProductPriceChanged",
|
|
53
|
-
productId:
|
|
54
|
-
price:
|
|
49
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
50
|
+
price: Ordering_Examples$OrderingPlugin.laptopChangedPrice
|
|
55
51
|
}), "p1", {
|
|
56
|
-
productId:
|
|
57
|
-
name:
|
|
58
|
-
price:
|
|
52
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
53
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
54
|
+
price: Ordering_Examples$OrderingPlugin.laptopChangedPrice
|
|
59
55
|
}));
|
|
60
56
|
test("CatalogProductWithdrawn removes the row", undefined, () => thenNoState(whenEvent(givenEvents([{
|
|
61
57
|
TAG: "CatalogProductSynced",
|
|
62
|
-
productId:
|
|
63
|
-
name:
|
|
64
|
-
price:
|
|
58
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
59
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
60
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
65
61
|
}]), {
|
|
66
62
|
TAG: "CatalogProductWithdrawn",
|
|
67
|
-
productId:
|
|
63
|
+
productId: Ordering_Examples$OrderingPlugin.p1
|
|
68
64
|
})));
|
|
69
65
|
test("CatalogProductRelisted puts it back with its name and price", undefined, () => thenStateWithId(whenEvent(givenEvents([
|
|
70
66
|
{
|
|
71
67
|
TAG: "CatalogProductSynced",
|
|
72
|
-
productId:
|
|
73
|
-
name:
|
|
74
|
-
price:
|
|
68
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
69
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
70
|
+
price: Ordering_Examples$OrderingPlugin.laptopPrice
|
|
75
71
|
},
|
|
76
72
|
{
|
|
77
73
|
TAG: "CatalogProductWithdrawn",
|
|
78
|
-
productId:
|
|
74
|
+
productId: Ordering_Examples$OrderingPlugin.p1
|
|
79
75
|
}
|
|
80
76
|
]), {
|
|
81
77
|
TAG: "CatalogProductRelisted",
|
|
82
|
-
productId:
|
|
83
|
-
name:
|
|
84
|
-
price:
|
|
78
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
79
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
80
|
+
price: Ordering_Examples$OrderingPlugin.laptopChangedPrice
|
|
85
81
|
}), "p1", {
|
|
86
|
-
productId:
|
|
87
|
-
name:
|
|
88
|
-
price:
|
|
82
|
+
productId: Ordering_Examples$OrderingPlugin.p1,
|
|
83
|
+
name: Ordering_Examples$OrderingPlugin.laptop,
|
|
84
|
+
price: Ordering_Examples$OrderingPlugin.laptopChangedPrice
|
|
89
85
|
}));
|
|
90
86
|
});
|
|
91
87
|
|
|
@@ -109,8 +105,6 @@ let thenThrow = include.thenThrow;
|
|
|
109
105
|
|
|
110
106
|
let thenFail = include.thenFail;
|
|
111
107
|
|
|
112
|
-
let pid = ProductId$CatalogSpec.make;
|
|
113
|
-
|
|
114
108
|
export {
|
|
115
109
|
Spec,
|
|
116
110
|
describe,
|
|
@@ -128,7 +122,5 @@ export {
|
|
|
128
122
|
thenNoState,
|
|
129
123
|
thenThrow,
|
|
130
124
|
thenFail,
|
|
131
|
-
pid,
|
|
132
|
-
eur,
|
|
133
125
|
}
|
|
134
126
|
/* include Not a pure module */
|