@reventlessdev/online-shop-hybrid-ordering 1.0.0-alpha.100

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.
Files changed (86) hide show
  1. package/CHANGELOG.md +904 -0
  2. package/LICENSE +202 -0
  3. package/__mocks__/emptyModule.js +1 -0
  4. package/package.json +57 -0
  5. package/rescript.json +37 -0
  6. package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct.res +29 -0
  7. package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct.res.mjs +71 -0
  8. package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct_Behavior.res +27 -0
  9. package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct_Behavior.res.mjs +73 -0
  10. package/src/CatalogProduct/StateViewSliceStream/AvailableProducts.res +15 -0
  11. package/src/CatalogProduct/StateViewSliceStream/AvailableProducts.res.mjs +69 -0
  12. package/src/CatalogProduct/StateViewSliceStream/AvailableProducts_Projection.res +8 -0
  13. package/src/CatalogProduct/StateViewSliceStream/AvailableProducts_Projection.res.mjs +38 -0
  14. package/src/Customer/Aggregate/Customer.res +24 -0
  15. package/src/Customer/Aggregate/Customer.res.mjs +64 -0
  16. package/src/Customer/Aggregate/Customer_Behavior.res +41 -0
  17. package/src/Customer/Aggregate/Customer_Behavior.res.mjs +143 -0
  18. package/src/Customer/ReadModelStream/Customers.res +15 -0
  19. package/src/Customer/ReadModelStream/Customers.res.mjs +44 -0
  20. package/src/Customer/ReadModelStream/Customers_Projections.res +78 -0
  21. package/src/Customer/ReadModelStream/Customers_Projections.res.mjs +153 -0
  22. package/src/Extension/Products_Extension.res +23 -0
  23. package/src/Extension/Products_Extension.res.mjs +43 -0
  24. package/src/ExtensionPoint/Orders_ExtensionPointMapping.res +33 -0
  25. package/src/ExtensionPoint/Orders_ExtensionPointMapping.res.mjs +82 -0
  26. package/src/Order/AutomationSlice/AutoShipOrder.res +19 -0
  27. package/src/Order/AutomationSlice/AutoShipOrder.res.mjs +42 -0
  28. package/src/Order/AutomationSlice/AutoShipOrder_Automation.res +37 -0
  29. package/src/Order/AutomationSlice/AutoShipOrder_Automation.res.mjs +101 -0
  30. package/src/Order/OutboundTranslationSlice/SendOrderConfirmation.res +22 -0
  31. package/src/Order/OutboundTranslationSlice/SendOrderConfirmation.res.mjs +45 -0
  32. package/src/Order/OutboundTranslationSlice/SendOrderConfirmation_Translation.res +24 -0
  33. package/src/Order/OutboundTranslationSlice/SendOrderConfirmation_Translation.res.mjs +46 -0
  34. package/src/Order/StateChangeSlice/CancelOrder.res +29 -0
  35. package/src/Order/StateChangeSlice/CancelOrder.res.mjs +75 -0
  36. package/src/Order/StateChangeSlice/CancelOrder_Behavior.res +35 -0
  37. package/src/Order/StateChangeSlice/CancelOrder_Behavior.res.mjs +110 -0
  38. package/src/Order/StateChangeSlice/PlaceOrder.res +28 -0
  39. package/src/Order/StateChangeSlice/PlaceOrder.res.mjs +63 -0
  40. package/src/Order/StateChangeSlice/PlaceOrder_Behavior.res +39 -0
  41. package/src/Order/StateChangeSlice/PlaceOrder_Behavior.res.mjs +70 -0
  42. package/src/Order/StateChangeSlice/RefundOrder.res +27 -0
  43. package/src/Order/StateChangeSlice/RefundOrder.res.mjs +59 -0
  44. package/src/Order/StateChangeSlice/RefundOrder_Behavior.res +26 -0
  45. package/src/Order/StateChangeSlice/RefundOrder_Behavior.res.mjs +77 -0
  46. package/src/Order/StateChangeSlice/ShipOrder.res +23 -0
  47. package/src/Order/StateChangeSlice/ShipOrder.res.mjs +56 -0
  48. package/src/Order/StateChangeSlice/ShipOrder_Behavior.res +26 -0
  49. package/src/Order/StateChangeSlice/ShipOrder_Behavior.res.mjs +73 -0
  50. package/src/Order/StateViewSliceStream/Orders.res +24 -0
  51. package/src/Order/StateViewSliceStream/Orders.res.mjs +80 -0
  52. package/src/Order/StateViewSliceStream/Orders_Projection.res +10 -0
  53. package/src/Order/StateViewSliceStream/Orders_Projection.res.mjs +52 -0
  54. package/src/Plugin.res +74 -0
  55. package/src/Plugin.res.mjs +304 -0
  56. package/src/Service/EmailService.res +3 -0
  57. package/src/Service/EmailService.res.mjs +11 -0
  58. package/src/plugin.json +1 -0
  59. package/tests/CatalogProduct/StateChangeSlice/SyncCatalogProduct_GWT.res +27 -0
  60. package/tests/CatalogProduct/StateChangeSlice/SyncCatalogProduct_GWT.res.mjs +109 -0
  61. package/tests/CatalogProduct/StateViewSliceStream/AvailableProducts_GWT.res +15 -0
  62. package/tests/CatalogProduct/StateViewSliceStream/AvailableProducts_GWT.res.mjs +93 -0
  63. package/tests/Customer/Aggregate/Customer_GWT.res +63 -0
  64. package/tests/Customer/Aggregate/Customer_GWT.res.mjs +153 -0
  65. package/tests/Customer/ReadModelStream/Customers_GWT.res +84 -0
  66. package/tests/Customer/ReadModelStream/Customers_GWT.res.mjs +90 -0
  67. package/tests/Extension/Products_Extension_GWT.res +23 -0
  68. package/tests/Extension/Products_Extension_GWT.res.mjs +108 -0
  69. package/tests/ExtensionPoint/Orders_ExtensionPointMapping_GWT.res +23 -0
  70. package/tests/ExtensionPoint/Orders_ExtensionPointMapping_GWT.res.mjs +139 -0
  71. package/tests/Flow/OrderingFlow_GWT.res +69 -0
  72. package/tests/Flow/OrderingFlow_GWT.res.mjs +184 -0
  73. package/tests/Order/AutomationSlice/AutoShipOrder_GWT.res +55 -0
  74. package/tests/Order/AutomationSlice/AutoShipOrder_GWT.res.mjs +133 -0
  75. package/tests/Order/OutboundTranslationSlice/SendOrderConfirmation_GWT.res +30 -0
  76. package/tests/Order/OutboundTranslationSlice/SendOrderConfirmation_GWT.res.mjs +102 -0
  77. package/tests/Order/StateChangeSlice/CancelOrder_GWT.res +45 -0
  78. package/tests/Order/StateChangeSlice/CancelOrder_GWT.res.mjs +131 -0
  79. package/tests/Order/StateChangeSlice/PlaceOrder_GWT.res +33 -0
  80. package/tests/Order/StateChangeSlice/PlaceOrder_GWT.res.mjs +139 -0
  81. package/tests/Order/StateChangeSlice/RefundOrder_GWT.res +31 -0
  82. package/tests/Order/StateChangeSlice/RefundOrder_GWT.res.mjs +100 -0
  83. package/tests/Order/StateChangeSlice/ShipOrder_GWT.res +27 -0
  84. package/tests/Order/StateChangeSlice/ShipOrder_GWT.res.mjs +91 -0
  85. package/tests/Order/StateViewSliceStream/Orders_GWT.res +36 -0
  86. package/tests/Order/StateViewSliceStream/Orders_GWT.res.mjs +114 -0
@@ -0,0 +1,102 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as SendOrderConfirmation$OrderingPlugin from "../../../src/Order/OutboundTranslationSlice/SendOrderConfirmation.res.mjs";
4
+ import * as OutboundTranslation_GWT$ReventlessGwt from "@reventlessdev/reventless-gwt/src/OutboundTranslation_GWT.res.mjs";
5
+ import * as SendOrderConfirmation_Translation$OrderingPlugin from "../../../src/Order/OutboundTranslationSlice/SendOrderConfirmation_Translation.res.mjs";
6
+
7
+ let SendOrderConfirmationSlice = {
8
+ name: SendOrderConfirmation$OrderingPlugin.name,
9
+ Id: SendOrderConfirmation$OrderingPlugin.Id,
10
+ consumedEventSchema: SendOrderConfirmation$OrderingPlugin.consumedEventSchema,
11
+ outboundItemSchema: SendOrderConfirmation$OrderingPlugin.outboundItemSchema,
12
+ inboundCommandSchema: SendOrderConfirmation$OrderingPlugin.inboundCommandSchema,
13
+ maxRetries: SendOrderConfirmation$OrderingPlugin.maxRetries,
14
+ heartbeatInterval: SendOrderConfirmation$OrderingPlugin.heartbeatInterval,
15
+ targetName: undefined,
16
+ externalSystem: SendOrderConfirmation$OrderingPlugin.externalSystem,
17
+ moduleUrl: SendOrderConfirmation$OrderingPlugin.moduleUrl,
18
+ collect: SendOrderConfirmation_Translation$OrderingPlugin.collect
19
+ };
20
+
21
+ let include = OutboundTranslation_GWT$ReventlessGwt.Make({
22
+ name: SendOrderConfirmation$OrderingPlugin.name,
23
+ consumedEventSchema: SendOrderConfirmation$OrderingPlugin.consumedEventSchema,
24
+ outboundItemSchema: SendOrderConfirmation$OrderingPlugin.outboundItemSchema,
25
+ inboundCommandSchema: SendOrderConfirmation$OrderingPlugin.inboundCommandSchema,
26
+ collect: SendOrderConfirmation_Translation$OrderingPlugin.collect
27
+ });
28
+
29
+ let describe = include.describe;
30
+
31
+ let test = include.test;
32
+
33
+ let testSync = include.testSync;
34
+
35
+ let givenEvent = include.givenEvent;
36
+
37
+ let whenCollect = include.whenCollect;
38
+
39
+ let thenTodos = include.thenTodos;
40
+
41
+ let givenTodo = include.givenTodo;
42
+
43
+ let whenTranslateMocked = include.whenTranslateMocked;
44
+
45
+ let thenTodoStatus = include.thenTodoStatus;
46
+
47
+ describe("SendOrderConfirmation OutboundTranslationSlice", () => {
48
+ testSync("collect: OrderPlaced queues an outbound TODO", () => thenTodos(whenCollect(givenEvent({
49
+ TAG: "OrderPlaced",
50
+ orderId: "o1",
51
+ customerId: "c1"
52
+ })), [[
53
+ "o1",
54
+ {
55
+ orderId: "o1",
56
+ customerId: "c1"
57
+ }
58
+ ]]));
59
+ test("translate success marks the TODO Completed", undefined, () => thenTodoStatus(whenTranslateMocked(givenTodo("o1", {
60
+ orderId: "o1",
61
+ customerId: "c1"
62
+ }), (_id, _item) => Promise.resolve({
63
+ TAG: "Ok",
64
+ _0: undefined
65
+ })), "o1", "Completed"));
66
+ test("translate failure leaves the TODO Pending for retry", undefined, () => thenTodoStatus(whenTranslateMocked(givenTodo("o1", {
67
+ orderId: "o1",
68
+ customerId: "c1"
69
+ }), (_id, _item) => Promise.resolve({
70
+ TAG: "Error",
71
+ _0: "smtp down"
72
+ })), "o1", "Pending"));
73
+ });
74
+
75
+ let Spec = include.Spec;
76
+
77
+ let whenTranslateRetrying = include.whenTranslateRetrying;
78
+
79
+ let thenCommand = include.thenCommand;
80
+
81
+ let thenNoCommand = include.thenNoCommand;
82
+
83
+ let thenRetryRecorded = include.thenRetryRecorded;
84
+
85
+ export {
86
+ SendOrderConfirmationSlice,
87
+ Spec,
88
+ describe,
89
+ test,
90
+ testSync,
91
+ givenEvent,
92
+ whenCollect,
93
+ thenTodos,
94
+ givenTodo,
95
+ whenTranslateMocked,
96
+ whenTranslateRetrying,
97
+ thenCommand,
98
+ thenNoCommand,
99
+ thenRetryRecorded,
100
+ thenTodoStatus,
101
+ }
102
+ /* include Not a pure module */
@@ -0,0 +1,45 @@
1
+ @@reventless.gwt
2
+
3
+ describe("CancelOrder StateChangeSlice", () => {
4
+ test("non-existent order returns OrderNotFound for CancelOrder", () =>
5
+ givenEvents([])
6
+ ->whenCmd(CancelOrder({orderId: "o1"}))
7
+ ->thenError(OrderNotFound)
8
+ )
9
+
10
+ test("placed order produces OrderCancelled with productIds carried over", () =>
11
+ givenEvents([OrderPlaced({productIds: ["p1", "p2"]})])
12
+ ->whenCmd(CancelOrder({orderId: "o1"}))
13
+ ->thenEvent(OrderCancelled({orderId: "o1", productIds: ["p1", "p2"]}))
14
+ )
15
+
16
+ test("already cancelled order produces no events for CancelOrder (idempotent)", () =>
17
+ givenEvents([OrderPlaced({productIds: ["p1"]}), OrderCancelled])
18
+ ->whenCmd(CancelOrder({orderId: "o1"}))
19
+ ->thenNoEvent
20
+ )
21
+
22
+ test("shipped order returns OrderAlreadyShipped for CancelOrder", () =>
23
+ givenEvents([OrderPlaced({productIds: ["p1"]}), OrderShipped])
24
+ ->whenCmd(CancelOrder({orderId: "o1"}))
25
+ ->thenError(OrderAlreadyShipped)
26
+ )
27
+
28
+ test("ReopenOrder on cancelled order produces OrderReopened", () =>
29
+ givenEvents([OrderPlaced({productIds: ["p1"]}), OrderCancelled])
30
+ ->whenCmd(ReopenOrder({orderId: "o1"}))
31
+ ->thenEvent(OrderReopened({orderId: "o1"}))
32
+ )
33
+
34
+ test("ReopenOrder on non-cancelled order produces no events (idempotent)", () =>
35
+ givenEvents([OrderPlaced({productIds: ["p1"]})])
36
+ ->whenCmd(ReopenOrder({orderId: "o1"}))
37
+ ->thenNoEvent
38
+ )
39
+
40
+ test("ReopenOrder on non-existent order returns OrderNotFound", () =>
41
+ givenEvents([])
42
+ ->whenCmd(ReopenOrder({orderId: "o1"}))
43
+ ->thenError(OrderNotFound)
44
+ )
45
+ })
@@ -0,0 +1,131 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Behavior_GWT$ReventlessGwt from "@reventlessdev/reventless-gwt/src/Behavior_GWT.res.mjs";
4
+ import * as CancelOrder$OrderingPlugin from "../../../src/Order/StateChangeSlice/CancelOrder.res.mjs";
5
+ import * as CancelOrder_Behavior$OrderingPlugin from "../../../src/Order/StateChangeSlice/CancelOrder_Behavior.res.mjs";
6
+
7
+ let include = Behavior_GWT$ReventlessGwt.Make({
8
+ name: CancelOrder$OrderingPlugin.name,
9
+ consumedEventSchema: CancelOrder$OrderingPlugin.consumedEventSchema,
10
+ commandSchema: CancelOrder$OrderingPlugin.commandSchema,
11
+ errorSchema: CancelOrder$OrderingPlugin.errorSchema,
12
+ eventSchema: CancelOrder$OrderingPlugin.eventSchema
13
+ })({
14
+ initialState: CancelOrder_Behavior$OrderingPlugin.initialState,
15
+ evolve: CancelOrder_Behavior$OrderingPlugin.evolve,
16
+ decide: CancelOrder_Behavior$OrderingPlugin.decide
17
+ });
18
+
19
+ let describe = include.describe;
20
+
21
+ let test = include.test;
22
+
23
+ let givenEvents = include.givenEvents;
24
+
25
+ let whenCmd = include.whenCmd;
26
+
27
+ let thenEvent = include.thenEvent;
28
+
29
+ let thenNoEvent = include.thenNoEvent;
30
+
31
+ let thenError = include.thenError;
32
+
33
+ describe("CancelOrder StateChangeSlice", () => {
34
+ test("non-existent order returns OrderNotFound for CancelOrder", () => thenError(whenCmd(givenEvents([]), {
35
+ TAG: "CancelOrder",
36
+ orderId: "o1"
37
+ }), "OrderNotFound"));
38
+ test("placed order produces OrderCancelled with productIds carried over", () => thenEvent(whenCmd(givenEvents([{
39
+ TAG: "OrderPlaced",
40
+ productIds: [
41
+ "p1",
42
+ "p2"
43
+ ]
44
+ }]), {
45
+ TAG: "CancelOrder",
46
+ orderId: "o1"
47
+ }), {
48
+ TAG: "OrderCancelled",
49
+ orderId: "o1",
50
+ productIds: [
51
+ "p1",
52
+ "p2"
53
+ ]
54
+ }));
55
+ test("already cancelled order produces no events for CancelOrder (idempotent)", () => thenNoEvent(whenCmd(givenEvents([
56
+ {
57
+ TAG: "OrderPlaced",
58
+ productIds: ["p1"]
59
+ },
60
+ "OrderCancelled"
61
+ ]), {
62
+ TAG: "CancelOrder",
63
+ orderId: "o1"
64
+ })));
65
+ test("shipped order returns OrderAlreadyShipped for CancelOrder", () => thenError(whenCmd(givenEvents([
66
+ {
67
+ TAG: "OrderPlaced",
68
+ productIds: ["p1"]
69
+ },
70
+ "OrderShipped"
71
+ ]), {
72
+ TAG: "CancelOrder",
73
+ orderId: "o1"
74
+ }), "OrderAlreadyShipped"));
75
+ test("ReopenOrder on cancelled order produces OrderReopened", () => thenEvent(whenCmd(givenEvents([
76
+ {
77
+ TAG: "OrderPlaced",
78
+ productIds: ["p1"]
79
+ },
80
+ "OrderCancelled"
81
+ ]), {
82
+ TAG: "ReopenOrder",
83
+ orderId: "o1"
84
+ }), {
85
+ TAG: "OrderReopened",
86
+ orderId: "o1"
87
+ }));
88
+ test("ReopenOrder on non-cancelled order produces no events (idempotent)", () => thenNoEvent(whenCmd(givenEvents([{
89
+ TAG: "OrderPlaced",
90
+ productIds: ["p1"]
91
+ }]), {
92
+ TAG: "ReopenOrder",
93
+ orderId: "o1"
94
+ })));
95
+ test("ReopenOrder on non-existent order returns OrderNotFound", () => thenError(whenCmd(givenEvents([]), {
96
+ TAG: "ReopenOrder",
97
+ orderId: "o1"
98
+ }), "OrderNotFound"));
99
+ });
100
+
101
+ let Spec = include.Spec;
102
+
103
+ let todo = include.todo;
104
+
105
+ let thenEvents = include.thenEvents;
106
+
107
+ let thenEventWithError = include.thenEventWithError;
108
+
109
+ let thenEventsWithError = include.thenEventsWithError;
110
+
111
+ let thenAppendsConditionedOn = include.thenAppendsConditionedOn;
112
+
113
+ let thenAppendsConditionedOnExactly = include.thenAppendsConditionedOnExactly;
114
+
115
+ export {
116
+ Spec,
117
+ describe,
118
+ todo,
119
+ test,
120
+ givenEvents,
121
+ whenCmd,
122
+ thenEvent,
123
+ thenEvents,
124
+ thenNoEvent,
125
+ thenEventWithError,
126
+ thenEventsWithError,
127
+ thenError,
128
+ thenAppendsConditionedOn,
129
+ thenAppendsConditionedOnExactly,
130
+ }
131
+ /* include Not a pure module */
@@ -0,0 +1,33 @@
1
+ @@reventless.gwt
2
+
3
+ describe("PlaceOrder StateChangeSlice", () => {
4
+ test("requires referenced products to be synced first", () =>
5
+ givenEvents([])
6
+ ->whenCmd(PlaceOrder({orderId: "o1", customerId: "c1", productIds: ["p1"]}))
7
+ ->thenError(ProductsNotAvailable({missing: ["p1"]}))
8
+ )
9
+
10
+ test("placement succeeds when products are available", () =>
11
+ givenEvents([CatalogProductSynced({productId: "p1"})])
12
+ ->whenCmd(PlaceOrder({orderId: "o1", customerId: "c1", productIds: ["p1"]}))
13
+ ->thenEvent(OrderPlaced({orderId: "o1", customerId: "c1", productIds: ["p1"]}))
14
+ )
15
+
16
+ test("partial product availability returns ProductsNotAvailable with missing list", () =>
17
+ givenEvents([CatalogProductSynced({productId: "p1"})])
18
+ ->whenCmd(PlaceOrder({orderId: "o1", customerId: "c1", productIds: ["p1", "p2"]}))
19
+ ->thenError(ProductsNotAvailable({missing: ["p2"]}))
20
+ )
21
+
22
+ test("re-placing the same orderId returns OrderAlreadyPlaced", () =>
23
+ givenEvents([CatalogProductSynced({productId: "p1"}), OrderPlaced({orderId: "o1"})])
24
+ ->whenCmd(PlaceOrder({orderId: "o1", customerId: "c1", productIds: ["p1"]}))
25
+ ->thenError(OrderAlreadyPlaced)
26
+ )
27
+
28
+ test("a sibling OrderPlaced for a different orderId does not block placement", () =>
29
+ givenEvents([CatalogProductSynced({productId: "p1"}), OrderPlaced({orderId: "o2"})])
30
+ ->whenCmd(PlaceOrder({orderId: "o1", customerId: "c1", productIds: ["p1"]}))
31
+ ->thenEvent(OrderPlaced({orderId: "o1", customerId: "c1", productIds: ["p1"]}))
32
+ )
33
+ })
@@ -0,0 +1,139 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as PlaceOrder$OrderingPlugin from "../../../src/Order/StateChangeSlice/PlaceOrder.res.mjs";
4
+ import * as Behavior_GWT$ReventlessGwt from "@reventlessdev/reventless-gwt/src/Behavior_GWT.res.mjs";
5
+ import * as PlaceOrder_Behavior$OrderingPlugin from "../../../src/Order/StateChangeSlice/PlaceOrder_Behavior.res.mjs";
6
+
7
+ let include = Behavior_GWT$ReventlessGwt.Make({
8
+ name: PlaceOrder$OrderingPlugin.name,
9
+ consumedEventSchema: PlaceOrder$OrderingPlugin.consumedEventSchema,
10
+ commandSchema: PlaceOrder$OrderingPlugin.commandSchema,
11
+ errorSchema: PlaceOrder$OrderingPlugin.errorSchema,
12
+ eventSchema: PlaceOrder$OrderingPlugin.eventSchema
13
+ })({
14
+ initialState: PlaceOrder_Behavior$OrderingPlugin.initialState,
15
+ evolve: PlaceOrder_Behavior$OrderingPlugin.evolve,
16
+ decide: PlaceOrder_Behavior$OrderingPlugin.decide
17
+ });
18
+
19
+ let describe = include.describe;
20
+
21
+ let test = include.test;
22
+
23
+ let givenEvents = include.givenEvents;
24
+
25
+ let whenCmd = include.whenCmd;
26
+
27
+ let thenEvent = include.thenEvent;
28
+
29
+ let thenError = include.thenError;
30
+
31
+ describe("PlaceOrder StateChangeSlice", () => {
32
+ test("requires referenced products to be synced first", () => thenError(whenCmd(givenEvents([]), {
33
+ TAG: "PlaceOrder",
34
+ orderId: "o1",
35
+ customerId: "c1",
36
+ productIds: ["p1"]
37
+ }), {
38
+ TAG: "ProductsNotAvailable",
39
+ missing: ["p1"]
40
+ }));
41
+ test("placement succeeds when products are available", () => thenEvent(whenCmd(givenEvents([{
42
+ TAG: "CatalogProductSynced",
43
+ productId: "p1"
44
+ }]), {
45
+ TAG: "PlaceOrder",
46
+ orderId: "o1",
47
+ customerId: "c1",
48
+ productIds: ["p1"]
49
+ }), {
50
+ TAG: "OrderPlaced",
51
+ orderId: "o1",
52
+ customerId: "c1",
53
+ productIds: ["p1"]
54
+ }));
55
+ test("partial product availability returns ProductsNotAvailable with missing list", () => thenError(whenCmd(givenEvents([{
56
+ TAG: "CatalogProductSynced",
57
+ productId: "p1"
58
+ }]), {
59
+ TAG: "PlaceOrder",
60
+ orderId: "o1",
61
+ customerId: "c1",
62
+ productIds: [
63
+ "p1",
64
+ "p2"
65
+ ]
66
+ }), {
67
+ TAG: "ProductsNotAvailable",
68
+ missing: ["p2"]
69
+ }));
70
+ test("re-placing the same orderId returns OrderAlreadyPlaced", () => thenError(whenCmd(givenEvents([
71
+ {
72
+ TAG: "CatalogProductSynced",
73
+ productId: "p1"
74
+ },
75
+ {
76
+ TAG: "OrderPlaced",
77
+ orderId: "o1"
78
+ }
79
+ ]), {
80
+ TAG: "PlaceOrder",
81
+ orderId: "o1",
82
+ customerId: "c1",
83
+ productIds: ["p1"]
84
+ }), "OrderAlreadyPlaced"));
85
+ test("a sibling OrderPlaced for a different orderId does not block placement", () => thenEvent(whenCmd(givenEvents([
86
+ {
87
+ TAG: "CatalogProductSynced",
88
+ productId: "p1"
89
+ },
90
+ {
91
+ TAG: "OrderPlaced",
92
+ orderId: "o2"
93
+ }
94
+ ]), {
95
+ TAG: "PlaceOrder",
96
+ orderId: "o1",
97
+ customerId: "c1",
98
+ productIds: ["p1"]
99
+ }), {
100
+ TAG: "OrderPlaced",
101
+ orderId: "o1",
102
+ customerId: "c1",
103
+ productIds: ["p1"]
104
+ }));
105
+ });
106
+
107
+ let Spec = include.Spec;
108
+
109
+ let todo = include.todo;
110
+
111
+ let thenEvents = include.thenEvents;
112
+
113
+ let thenNoEvent = include.thenNoEvent;
114
+
115
+ let thenEventWithError = include.thenEventWithError;
116
+
117
+ let thenEventsWithError = include.thenEventsWithError;
118
+
119
+ let thenAppendsConditionedOn = include.thenAppendsConditionedOn;
120
+
121
+ let thenAppendsConditionedOnExactly = include.thenAppendsConditionedOnExactly;
122
+
123
+ export {
124
+ Spec,
125
+ describe,
126
+ todo,
127
+ test,
128
+ givenEvents,
129
+ whenCmd,
130
+ thenEvent,
131
+ thenEvents,
132
+ thenNoEvent,
133
+ thenEventWithError,
134
+ thenEventsWithError,
135
+ thenError,
136
+ thenAppendsConditionedOn,
137
+ thenAppendsConditionedOnExactly,
138
+ }
139
+ /* include Not a pure module */
@@ -0,0 +1,31 @@
1
+ @@reventless.gwt
2
+
3
+ describe("RefundOrder StateChangeSlice", () => {
4
+ test("non-existent order returns OrderNotFound", () =>
5
+ givenEvents([])
6
+ ->whenCmd(IssueRefund({orderId: "o1", reason: "duplicate"}))
7
+ ->thenError(OrderNotFound)
8
+ )
9
+
10
+ test("non-cancelled order returns OrderNotCancelled", () =>
11
+ givenEvents([OrderPlaced])
12
+ ->whenCmd(IssueRefund({orderId: "o1", reason: "duplicate"}))
13
+ ->thenError(OrderNotCancelled)
14
+ )
15
+
16
+ test("cancelled order produces RefundIssued", () =>
17
+ givenEvents([OrderPlaced, OrderCancelled])
18
+ ->whenCmd(IssueRefund({orderId: "o1", reason: "duplicate"}))
19
+ ->thenEvent(RefundIssued({orderId: "o1", reason: "duplicate"}))
20
+ )
21
+
22
+ test("already refunded order returns RefundAlreadyIssued", () =>
23
+ givenEvents([
24
+ OrderPlaced,
25
+ OrderCancelled,
26
+ RefundIssued({reason: "duplicate"}),
27
+ ])
28
+ ->whenCmd(IssueRefund({orderId: "o1", reason: "again"}))
29
+ ->thenError(RefundAlreadyIssued)
30
+ )
31
+ })
@@ -0,0 +1,100 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Behavior_GWT$ReventlessGwt from "@reventlessdev/reventless-gwt/src/Behavior_GWT.res.mjs";
4
+ import * as RefundOrder$OrderingPlugin from "../../../src/Order/StateChangeSlice/RefundOrder.res.mjs";
5
+ import * as RefundOrder_Behavior$OrderingPlugin from "../../../src/Order/StateChangeSlice/RefundOrder_Behavior.res.mjs";
6
+
7
+ let include = Behavior_GWT$ReventlessGwt.Make({
8
+ name: RefundOrder$OrderingPlugin.name,
9
+ consumedEventSchema: RefundOrder$OrderingPlugin.consumedEventSchema,
10
+ commandSchema: RefundOrder$OrderingPlugin.commandSchema,
11
+ errorSchema: RefundOrder$OrderingPlugin.errorSchema,
12
+ eventSchema: RefundOrder$OrderingPlugin.eventSchema
13
+ })({
14
+ initialState: RefundOrder_Behavior$OrderingPlugin.initialState,
15
+ evolve: RefundOrder_Behavior$OrderingPlugin.evolve,
16
+ decide: RefundOrder_Behavior$OrderingPlugin.decide
17
+ });
18
+
19
+ let describe = include.describe;
20
+
21
+ let test = include.test;
22
+
23
+ let givenEvents = include.givenEvents;
24
+
25
+ let whenCmd = include.whenCmd;
26
+
27
+ let thenEvent = include.thenEvent;
28
+
29
+ let thenError = include.thenError;
30
+
31
+ describe("RefundOrder StateChangeSlice", () => {
32
+ test("non-existent order returns OrderNotFound", () => thenError(whenCmd(givenEvents([]), {
33
+ TAG: "IssueRefund",
34
+ orderId: "o1",
35
+ reason: "duplicate"
36
+ }), "OrderNotFound"));
37
+ test("non-cancelled order returns OrderNotCancelled", () => thenError(whenCmd(givenEvents(["OrderPlaced"]), {
38
+ TAG: "IssueRefund",
39
+ orderId: "o1",
40
+ reason: "duplicate"
41
+ }), "OrderNotCancelled"));
42
+ test("cancelled order produces RefundIssued", () => thenEvent(whenCmd(givenEvents([
43
+ "OrderPlaced",
44
+ "OrderCancelled"
45
+ ]), {
46
+ TAG: "IssueRefund",
47
+ orderId: "o1",
48
+ reason: "duplicate"
49
+ }), {
50
+ TAG: "RefundIssued",
51
+ orderId: "o1",
52
+ reason: "duplicate"
53
+ }));
54
+ test("already refunded order returns RefundAlreadyIssued", () => thenError(whenCmd(givenEvents([
55
+ "OrderPlaced",
56
+ "OrderCancelled",
57
+ {
58
+ TAG: "RefundIssued",
59
+ reason: "duplicate"
60
+ }
61
+ ]), {
62
+ TAG: "IssueRefund",
63
+ orderId: "o1",
64
+ reason: "again"
65
+ }), "RefundAlreadyIssued"));
66
+ });
67
+
68
+ let Spec = include.Spec;
69
+
70
+ let todo = include.todo;
71
+
72
+ let thenEvents = include.thenEvents;
73
+
74
+ let thenNoEvent = include.thenNoEvent;
75
+
76
+ let thenEventWithError = include.thenEventWithError;
77
+
78
+ let thenEventsWithError = include.thenEventsWithError;
79
+
80
+ let thenAppendsConditionedOn = include.thenAppendsConditionedOn;
81
+
82
+ let thenAppendsConditionedOnExactly = include.thenAppendsConditionedOnExactly;
83
+
84
+ export {
85
+ Spec,
86
+ describe,
87
+ todo,
88
+ test,
89
+ givenEvents,
90
+ whenCmd,
91
+ thenEvent,
92
+ thenEvents,
93
+ thenNoEvent,
94
+ thenEventWithError,
95
+ thenEventsWithError,
96
+ thenError,
97
+ thenAppendsConditionedOn,
98
+ thenAppendsConditionedOnExactly,
99
+ }
100
+ /* include Not a pure module */
@@ -0,0 +1,27 @@
1
+ @@reventless.gwt
2
+
3
+ describe("ShipOrder StateChangeSlice", () => {
4
+ test("non-existent order returns OrderNotFound", () =>
5
+ givenEvents([])
6
+ ->whenCmd(ShipOrder({orderId: "o1"}))
7
+ ->thenError(OrderNotFound)
8
+ )
9
+
10
+ test("placed order produces OrderShipped", () =>
11
+ givenEvents([OrderPlaced])
12
+ ->whenCmd(ShipOrder({orderId: "o1"}))
13
+ ->thenEvent(OrderShipped({orderId: "o1"}))
14
+ )
15
+
16
+ test("already shipped order produces no events (idempotent)", () =>
17
+ givenEvents([OrderPlaced, OrderShipped])
18
+ ->whenCmd(ShipOrder({orderId: "o1"}))
19
+ ->thenNoEvent
20
+ )
21
+
22
+ test("cancelled order returns OrderAlreadyCancelled", () =>
23
+ givenEvents([OrderPlaced, OrderCancelled])
24
+ ->whenCmd(ShipOrder({orderId: "o1"}))
25
+ ->thenError(OrderAlreadyCancelled)
26
+ )
27
+ })