@event-driven-io/emmett-expressjs 0.4.0 → 0.5.0
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/dist/e2e/decider/api.d.ts +6 -12
- package/dist/e2e/decider/api.js +79 -145
- package/dist/e2e/decider/api.js.map +1 -1
- package/dist/e2e/decider/applicationLogicWithOC.int.spec.js +132 -124
- package/dist/e2e/decider/applicationLogicWithOC.int.spec.js.map +1 -1
- package/dist/e2e/decider/applicationLogicWithOC.int.spec.mjs +0 -1
- package/dist/e2e/decider/applicationLogicWithOC.int.spec.mjs.map +1 -1
- package/dist/e2e/decider/businessLogic.js +89 -101
- package/dist/e2e/decider/businessLogic.js.map +1 -1
- package/dist/e2e/decider/shoppingCart.js +73 -89
- package/dist/e2e/decider/shoppingCart.js.map +1 -1
- package/dist/e2e/testing.js +23 -28
- package/dist/e2e/testing.js.map +1 -1
- package/dist/etag.js +54 -59
- package/dist/etag.js.map +1 -1
- package/dist/handler.d.mts +3 -1
- package/dist/handler.d.ts +3 -1
- package/dist/handler.js.map +1 -1
- package/dist/handler.mjs.map +1 -1
- package/dist/index.d.mts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/middlewares/problemDetailsMiddleware.d.mts +3 -1
- package/dist/middlewares/problemDetailsMiddleware.d.ts +3 -1
- package/dist/middlewares/problemDetailsMiddleware.js +19 -22
- package/dist/middlewares/problemDetailsMiddleware.js.map +1 -1
- package/dist/testing/apiE2ESpecification.d.mts +17 -0
- package/dist/testing/apiE2ESpecification.d.ts +17 -0
- package/dist/testing/apiE2ESpecification.js +34 -0
- package/dist/testing/apiE2ESpecification.js.map +1 -0
- package/dist/testing/apiE2ESpecification.mjs +37 -0
- package/dist/testing/apiE2ESpecification.mjs.map +1 -0
- package/dist/testing/apiSpecification.d.mts +4 -7
- package/dist/testing/apiSpecification.d.ts +4 -7
- package/dist/testing/apiSpecification.js +59 -98
- package/dist/testing/apiSpecification.js.map +1 -1
- package/dist/testing/apiSpecification.mjs +5 -32
- package/dist/testing/apiSpecification.mjs.map +1 -1
- package/dist/testing/index.d.mts +5 -3
- package/dist/testing/index.d.ts +5 -3
- package/dist/testing/index.js +2 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/index.mjs +1 -0
- package/dist/testing/index.mjs.map +1 -1
- package/dist/testing/utils.d.mts +12 -0
- package/dist/testing/utils.d.ts +12 -0
- package/dist/testing/utils.js +25 -0
- package/dist/testing/utils.js.map +1 -0
- package/dist/testing/utils.mjs +32 -0
- package/dist/testing/utils.mjs.map +1 -0
- package/package.json +2 -2
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
declare const handle: (eventStore: EventStore<bigint>, id: string, command: ShoppingCartCommand, options?: {
|
|
8
|
-
expectedStreamVersion?: _event_driven_io_emmett_dist_eventStore_N_YMFCDT.e<bigint> | undefined;
|
|
9
|
-
} | undefined) => Promise<_event_driven_io_emmett_dist_eventStore_N_YMFCDT.d<bigint>>;
|
|
10
|
-
declare const shoppingCartApi: (eventStore: EventStore) => (router: Router) => void;
|
|
11
|
-
|
|
12
|
-
export { handle, shoppingCartApi };
|
|
1
|
+
import { type EventStore } from '@event-driven-io/emmett';
|
|
2
|
+
import { type Router } from 'express';
|
|
3
|
+
export declare const handle: (eventStore: EventStore<bigint>, id: string, command: import("./businessLogic").ShoppingCartCommand, options?: {
|
|
4
|
+
expectedStreamVersion?: import("packages/emmett/dist/eventStore-N_YMFCDT").e<bigint> | undefined;
|
|
5
|
+
} | undefined) => Promise<import("packages/emmett/dist/commandHandling/handleCommand").CommandHandlerResult<import("./shoppingCart").ShoppingCart, bigint>>;
|
|
6
|
+
export declare const shoppingCartApi: (eventStore: EventStore) => (router: Router) => void;
|
package/dist/e2e/decider/api.js
CHANGED
|
@@ -1,149 +1,83 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var _emmett = require('@event-driven-io/emmett');
|
|
8
|
-
require('express');
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
var _ = require('../../');
|
|
16
|
-
var _businessLogic = require('./businessLogic');
|
|
17
|
-
require('./shoppingCart');
|
|
18
|
-
const handle = _emmett.DeciderCommandHandler.call(void 0, _businessLogic.decider);
|
|
1
|
+
import { DeciderCommandHandler, STREAM_DOES_NOT_EXIST, assertNotEmptyString, assertPositiveNumber, assertUnsignedBigInt, } from '@event-driven-io/emmett';
|
|
2
|
+
import {} from 'express';
|
|
3
|
+
import { Created, NoContent, getETagValueFromIfMatch, on, toWeakETag, } from '../../';
|
|
4
|
+
import { decider } from './businessLogic';
|
|
5
|
+
import {} from './shoppingCart';
|
|
6
|
+
export const handle = DeciderCommandHandler(decider);
|
|
19
7
|
const dummyPriceProvider = (_productId) => {
|
|
20
|
-
|
|
8
|
+
return 100;
|
|
21
9
|
};
|
|
22
|
-
const shoppingCartApi = (eventStore) => (router) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
shoppingCartId,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
);
|
|
97
|
-
return _.NoContent.call(void 0, { eTag: _.toWeakETag.call(void 0, result.nextExpectedStreamVersion) });
|
|
98
|
-
})
|
|
99
|
-
);
|
|
100
|
-
router.post(
|
|
101
|
-
"/clients/:clientId/shopping-carts/:shoppingCartId/confirm",
|
|
102
|
-
_.on.call(void 0, async (request) => {
|
|
103
|
-
const shoppingCartId = _emmett.assertNotEmptyString.call(void 0,
|
|
104
|
-
request.params.shoppingCartId
|
|
105
|
-
);
|
|
106
|
-
const result = await handle(
|
|
107
|
-
eventStore,
|
|
108
|
-
shoppingCartId,
|
|
109
|
-
{
|
|
110
|
-
type: "ConfirmShoppingCart",
|
|
111
|
-
data: { shoppingCartId, now: /* @__PURE__ */ new Date() }
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
expectedStreamVersion: _emmett.assertUnsignedBigInt.call(void 0,
|
|
115
|
-
_.getETagValueFromIfMatch.call(void 0, request)
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
);
|
|
119
|
-
return _.NoContent.call(void 0, { eTag: _.toWeakETag.call(void 0, result.nextExpectedStreamVersion) });
|
|
120
|
-
})
|
|
121
|
-
);
|
|
122
|
-
router.delete(
|
|
123
|
-
"/clients/:clientId/shopping-carts/:shoppingCartId",
|
|
124
|
-
_.on.call(void 0, async (request) => {
|
|
125
|
-
const shoppingCartId = _emmett.assertNotEmptyString.call(void 0,
|
|
126
|
-
request.params.shoppingCartId
|
|
127
|
-
);
|
|
128
|
-
const result = await handle(
|
|
129
|
-
eventStore,
|
|
130
|
-
shoppingCartId,
|
|
131
|
-
{
|
|
132
|
-
type: "CancelShoppingCart",
|
|
133
|
-
data: { shoppingCartId, now: /* @__PURE__ */ new Date() }
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
expectedStreamVersion: _emmett.assertUnsignedBigInt.call(void 0,
|
|
137
|
-
_.getETagValueFromIfMatch.call(void 0, request)
|
|
138
|
-
)
|
|
139
|
-
}
|
|
140
|
-
);
|
|
141
|
-
return _.NoContent.call(void 0, { eTag: _.toWeakETag.call(void 0, result.nextExpectedStreamVersion) });
|
|
142
|
-
})
|
|
143
|
-
);
|
|
10
|
+
export const shoppingCartApi = (eventStore) => (router) => {
|
|
11
|
+
// Open Shopping cart
|
|
12
|
+
// #region created-example
|
|
13
|
+
router.post('/clients/:clientId/shopping-carts/', on(async (request) => {
|
|
14
|
+
const clientId = assertNotEmptyString(request.params.clientId);
|
|
15
|
+
const shoppingCartId = clientId;
|
|
16
|
+
const result = await handle(eventStore, shoppingCartId, {
|
|
17
|
+
type: 'OpenShoppingCart',
|
|
18
|
+
data: { clientId, shoppingCartId, now: new Date() },
|
|
19
|
+
}, { expectedStreamVersion: STREAM_DOES_NOT_EXIST });
|
|
20
|
+
return Created({
|
|
21
|
+
createdId: shoppingCartId,
|
|
22
|
+
eTag: toWeakETag(result.nextExpectedStreamVersion),
|
|
23
|
+
});
|
|
24
|
+
}));
|
|
25
|
+
// #endregion created-example
|
|
26
|
+
router.post('/clients/:clientId/shopping-carts/:shoppingCartId/product-items', on(async (request) => {
|
|
27
|
+
const shoppingCartId = assertNotEmptyString(request.params.shoppingCartId);
|
|
28
|
+
const productItem = {
|
|
29
|
+
productId: assertNotEmptyString(request.body.productId),
|
|
30
|
+
quantity: assertPositiveNumber(request.body.quantity),
|
|
31
|
+
};
|
|
32
|
+
const unitPrice = dummyPriceProvider(productItem.productId);
|
|
33
|
+
const result = await handle(eventStore, shoppingCartId, {
|
|
34
|
+
type: 'AddProductItemToShoppingCart',
|
|
35
|
+
data: {
|
|
36
|
+
shoppingCartId,
|
|
37
|
+
productItem: { ...productItem, unitPrice },
|
|
38
|
+
},
|
|
39
|
+
}, {
|
|
40
|
+
expectedStreamVersion: assertUnsignedBigInt(getETagValueFromIfMatch(request)),
|
|
41
|
+
});
|
|
42
|
+
return NoContent({ eTag: toWeakETag(result.nextExpectedStreamVersion) });
|
|
43
|
+
}));
|
|
44
|
+
// Remove Product Item
|
|
45
|
+
router.delete('/clients/:clientId/shopping-carts/:shoppingCartId/product-items', on(async (request) => {
|
|
46
|
+
const shoppingCartId = assertNotEmptyString(request.params.shoppingCartId);
|
|
47
|
+
const productItem = {
|
|
48
|
+
productId: assertNotEmptyString(request.query.productId),
|
|
49
|
+
quantity: assertPositiveNumber(Number(request.query.quantity)),
|
|
50
|
+
unitPrice: assertPositiveNumber(Number(request.query.unitPrice)),
|
|
51
|
+
};
|
|
52
|
+
const result = await handle(eventStore, shoppingCartId, {
|
|
53
|
+
type: 'RemoveProductItemFromShoppingCart',
|
|
54
|
+
data: { shoppingCartId, productItem },
|
|
55
|
+
}, {
|
|
56
|
+
expectedStreamVersion: assertUnsignedBigInt(getETagValueFromIfMatch(request)),
|
|
57
|
+
});
|
|
58
|
+
return NoContent({ eTag: toWeakETag(result.nextExpectedStreamVersion) });
|
|
59
|
+
}));
|
|
60
|
+
// Confirm Shopping Cart
|
|
61
|
+
router.post('/clients/:clientId/shopping-carts/:shoppingCartId/confirm', on(async (request) => {
|
|
62
|
+
const shoppingCartId = assertNotEmptyString(request.params.shoppingCartId);
|
|
63
|
+
const result = await handle(eventStore, shoppingCartId, {
|
|
64
|
+
type: 'ConfirmShoppingCart',
|
|
65
|
+
data: { shoppingCartId, now: new Date() },
|
|
66
|
+
}, {
|
|
67
|
+
expectedStreamVersion: assertUnsignedBigInt(getETagValueFromIfMatch(request)),
|
|
68
|
+
});
|
|
69
|
+
return NoContent({ eTag: toWeakETag(result.nextExpectedStreamVersion) });
|
|
70
|
+
}));
|
|
71
|
+
// Cancel Shopping Cart
|
|
72
|
+
router.delete('/clients/:clientId/shopping-carts/:shoppingCartId', on(async (request) => {
|
|
73
|
+
const shoppingCartId = assertNotEmptyString(request.params.shoppingCartId);
|
|
74
|
+
const result = await handle(eventStore, shoppingCartId, {
|
|
75
|
+
type: 'CancelShoppingCart',
|
|
76
|
+
data: { shoppingCartId, now: new Date() },
|
|
77
|
+
}, {
|
|
78
|
+
expectedStreamVersion: assertUnsignedBigInt(getETagValueFromIfMatch(request)),
|
|
79
|
+
});
|
|
80
|
+
return NoContent({ eTag: toWeakETag(result.nextExpectedStreamVersion) });
|
|
81
|
+
}));
|
|
144
82
|
};
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
exports.handle = handle; exports.shoppingCartApi = shoppingCartApi;
|
|
149
83
|
//# sourceMappingURL=api.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/e2e/decider/api.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/e2e/decider/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,128 +1,136 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
_nodetest.describe.call(void 0, "Application logic with optimistic concurrency", () => {
|
|
20
|
-
let app;
|
|
21
|
-
let eventStore;
|
|
22
|
-
_nodetest.beforeEach.call(void 0, () => {
|
|
23
|
-
eventStore = _emmett.getInMemoryEventStore.call(void 0, );
|
|
24
|
-
app = _.getApplication.call(void 0, { apis: [_api.shoppingCartApi.call(void 0, eventStore)] });
|
|
25
|
-
});
|
|
26
|
-
_nodetest.it.call(void 0, "Should handle requests correctly", async () => {
|
|
27
|
-
const clientId = _uuid.v4.call(void 0, );
|
|
28
|
-
const createResponse = await _testing.runTwice.call(void 0,
|
|
29
|
-
() => _supertest2.default.call(void 0, app).post(`/clients/${clientId}/shopping-carts`).send()
|
|
30
|
-
).expect(_testing.statuses.call(void 0, 201, 412));
|
|
31
|
-
let currentRevision = _testing.expectNextRevisionInResponseEtag.call(void 0, createResponse);
|
|
32
|
-
const current = createResponse.body;
|
|
33
|
-
if (!current.id) {
|
|
34
|
-
_strict2.default.fail();
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
_strict2.default.ok(current.id);
|
|
38
|
-
const shoppingCartId = current.id;
|
|
39
|
-
const twoPairsOfShoes = {
|
|
40
|
-
quantity: 2,
|
|
41
|
-
productId: "123"
|
|
42
|
-
};
|
|
43
|
-
let response = await _testing.runTwice.call(void 0,
|
|
44
|
-
() => _supertest2.default.call(void 0, app).post(
|
|
45
|
-
`/clients/${clientId}/shopping-carts/${shoppingCartId}/product-items`
|
|
46
|
-
).set(_etag.HeaderNames.IF_MATCH, _etag.toWeakETag.call(void 0, currentRevision)).send(twoPairsOfShoes)
|
|
47
|
-
).expect(_testing.statuses.call(void 0, 204, 412));
|
|
48
|
-
currentRevision = _testing.expectNextRevisionInResponseEtag.call(void 0, response);
|
|
49
|
-
const tShirt = {
|
|
50
|
-
productId: "456",
|
|
51
|
-
quantity: 1
|
|
52
|
-
};
|
|
53
|
-
response = await _testing.runTwice.call(void 0,
|
|
54
|
-
() => _supertest2.default.call(void 0, app).post(
|
|
55
|
-
`/clients/${clientId}/shopping-carts/${shoppingCartId}/product-items`
|
|
56
|
-
).set(_etag.HeaderNames.IF_MATCH, _etag.toWeakETag.call(void 0, currentRevision)).send(tShirt)
|
|
57
|
-
).expect(_testing.statuses.call(void 0, 204, 412));
|
|
58
|
-
currentRevision = _testing.expectNextRevisionInResponseEtag.call(void 0, response);
|
|
59
|
-
const pairOfShoes = {
|
|
60
|
-
productId: "123",
|
|
61
|
-
quantity: 1,
|
|
62
|
-
unitPrice: 100
|
|
63
|
-
};
|
|
64
|
-
response = await _testing.runTwice.call(void 0,
|
|
65
|
-
() => _supertest2.default.call(void 0, app).delete(
|
|
66
|
-
`/clients/${clientId}/shopping-carts/${shoppingCartId}/product-items?productId=${pairOfShoes.productId}&quantity=${pairOfShoes.quantity}&unitPrice=${pairOfShoes.unitPrice}`
|
|
67
|
-
).set(_etag.HeaderNames.IF_MATCH, _etag.toWeakETag.call(void 0, currentRevision))
|
|
68
|
-
).expect(_testing.statuses.call(void 0, 204, 412));
|
|
69
|
-
currentRevision = _testing.expectNextRevisionInResponseEtag.call(void 0, response);
|
|
70
|
-
response = await _testing.runTwice.call(void 0,
|
|
71
|
-
() => _supertest2.default.call(void 0, app).post(`/clients/${clientId}/shopping-carts/${shoppingCartId}/confirm`).set(_etag.HeaderNames.IF_MATCH, _etag.toWeakETag.call(void 0, currentRevision))
|
|
72
|
-
).expect(_testing.statuses.call(void 0, 204, 412));
|
|
73
|
-
currentRevision = _testing.expectNextRevisionInResponseEtag.call(void 0, response);
|
|
74
|
-
response = await _supertest2.default.call(void 0, app).delete(`/clients/${clientId}/shopping-carts/${shoppingCartId}`).set(_etag.HeaderNames.IF_MATCH, _etag.toWeakETag.call(void 0, currentRevision)).expect((response2) => {
|
|
75
|
-
_strict2.default.equal(response2.statusCode, 403);
|
|
76
|
-
_emmett.assertMatches.call(void 0, response2.body, {
|
|
77
|
-
detail: _businessLogic.ShoppingCartErrors.CART_IS_ALREADY_CLOSED
|
|
78
|
-
});
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
2
|
+
import { assertMatches, getInMemoryEventStore, } from '@event-driven-io/emmett';
|
|
3
|
+
import {} from 'express';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { beforeEach, describe, it } from 'node:test';
|
|
6
|
+
import request from 'supertest';
|
|
7
|
+
import { v4 as uuid } from 'uuid';
|
|
8
|
+
import { getApplication } from '../..';
|
|
9
|
+
import { HeaderNames, toWeakETag } from '../../etag';
|
|
10
|
+
import { expectNextRevisionInResponseEtag, runTwice, statuses, } from '../testing';
|
|
11
|
+
import { shoppingCartApi } from './api';
|
|
12
|
+
import { ShoppingCartErrors } from './businessLogic';
|
|
13
|
+
describe('Application logic with optimistic concurrency', () => {
|
|
14
|
+
let app;
|
|
15
|
+
let eventStore;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
eventStore = getInMemoryEventStore();
|
|
18
|
+
app = getApplication({ apis: [shoppingCartApi(eventStore)] });
|
|
79
19
|
});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
20
|
+
it('Should handle requests correctly', async () => {
|
|
21
|
+
const clientId = uuid();
|
|
22
|
+
///////////////////////////////////////////////////
|
|
23
|
+
// 1. Open Shopping Cart
|
|
24
|
+
///////////////////////////////////////////////////
|
|
25
|
+
const createResponse = (await runTwice(() => request(app).post(`/clients/${clientId}/shopping-carts`).send()).expect(statuses(201, 412)));
|
|
26
|
+
let currentRevision = expectNextRevisionInResponseEtag(createResponse);
|
|
27
|
+
const current = createResponse.body;
|
|
28
|
+
if (!current.id) {
|
|
29
|
+
assert.fail();
|
|
90
30
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
31
|
+
assert.ok(current.id);
|
|
32
|
+
const shoppingCartId = current.id;
|
|
33
|
+
///////////////////////////////////////////////////
|
|
34
|
+
// 2. Add Two Pair of Shoes
|
|
35
|
+
///////////////////////////////////////////////////
|
|
36
|
+
const twoPairsOfShoes = {
|
|
37
|
+
quantity: 2,
|
|
38
|
+
productId: '123',
|
|
39
|
+
};
|
|
40
|
+
let response = await runTwice(() => request(app)
|
|
41
|
+
.post(`/clients/${clientId}/shopping-carts/${shoppingCartId}/product-items`)
|
|
42
|
+
.set(HeaderNames.IF_MATCH, toWeakETag(currentRevision))
|
|
43
|
+
.send(twoPairsOfShoes)).expect(statuses(204, 412));
|
|
44
|
+
currentRevision = expectNextRevisionInResponseEtag(response);
|
|
45
|
+
///////////////////////////////////////////////////
|
|
46
|
+
// 3. Add T-Shirt
|
|
47
|
+
///////////////////////////////////////////////////
|
|
48
|
+
const tShirt = {
|
|
49
|
+
productId: '456',
|
|
50
|
+
quantity: 1,
|
|
51
|
+
};
|
|
52
|
+
response = await runTwice(() => request(app)
|
|
53
|
+
.post(`/clients/${clientId}/shopping-carts/${shoppingCartId}/product-items`)
|
|
54
|
+
.set(HeaderNames.IF_MATCH, toWeakETag(currentRevision))
|
|
55
|
+
.send(tShirt)).expect(statuses(204, 412));
|
|
56
|
+
currentRevision = expectNextRevisionInResponseEtag(response);
|
|
57
|
+
///////////////////////////////////////////////////
|
|
58
|
+
// 4. Remove pair of shoes
|
|
59
|
+
///////////////////////////////////////////////////
|
|
60
|
+
const pairOfShoes = {
|
|
61
|
+
productId: '123',
|
|
62
|
+
quantity: 1,
|
|
63
|
+
unitPrice: 100,
|
|
64
|
+
};
|
|
65
|
+
response = await runTwice(() => request(app)
|
|
66
|
+
.delete(`/clients/${clientId}/shopping-carts/${shoppingCartId}/product-items?productId=${pairOfShoes.productId}&quantity=${pairOfShoes.quantity}&unitPrice=${pairOfShoes.unitPrice}`)
|
|
67
|
+
.set(HeaderNames.IF_MATCH, toWeakETag(currentRevision))).expect(statuses(204, 412));
|
|
68
|
+
currentRevision = expectNextRevisionInResponseEtag(response);
|
|
69
|
+
///////////////////////////////////////////////////
|
|
70
|
+
// 5. Confirm cart
|
|
71
|
+
///////////////////////////////////////////////////
|
|
72
|
+
response = await runTwice(() => request(app)
|
|
73
|
+
.post(`/clients/${clientId}/shopping-carts/${shoppingCartId}/confirm`)
|
|
74
|
+
.set(HeaderNames.IF_MATCH, toWeakETag(currentRevision))).expect(statuses(204, 412));
|
|
75
|
+
currentRevision = expectNextRevisionInResponseEtag(response);
|
|
76
|
+
///////////////////////////////////////////////////
|
|
77
|
+
// 6. Try Cancel Cart
|
|
78
|
+
///////////////////////////////////////////////////
|
|
79
|
+
response = await request(app)
|
|
80
|
+
.delete(`/clients/${clientId}/shopping-carts/${shoppingCartId}`)
|
|
81
|
+
.set(HeaderNames.IF_MATCH, toWeakETag(currentRevision))
|
|
82
|
+
.expect((response) => {
|
|
83
|
+
assert.equal(response.statusCode, 403);
|
|
84
|
+
assertMatches(response.body, {
|
|
85
|
+
detail: ShoppingCartErrors.CART_IS_ALREADY_CLOSED,
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
const result = await eventStore.readStream(shoppingCartId);
|
|
89
|
+
assert.ok(result);
|
|
90
|
+
assert.equal(result.events.length, Number(currentRevision));
|
|
91
|
+
assertMatches(result?.events, [
|
|
92
|
+
{
|
|
93
|
+
type: 'ShoppingCartOpened',
|
|
94
|
+
data: {
|
|
95
|
+
shoppingCartId,
|
|
96
|
+
clientId,
|
|
97
|
+
//openedAt,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
type: 'ProductItemAddedToShoppingCart',
|
|
102
|
+
data: {
|
|
103
|
+
shoppingCartId,
|
|
104
|
+
productItem: twoPairsOfShoes,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
type: 'ProductItemAddedToShoppingCart',
|
|
109
|
+
data: {
|
|
110
|
+
shoppingCartId,
|
|
111
|
+
productItem: tShirt,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'ProductItemRemovedFromShoppingCart',
|
|
116
|
+
data: { shoppingCartId, productItem: pairOfShoes },
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
type: 'ShoppingCartConfirmed',
|
|
120
|
+
data: {
|
|
121
|
+
shoppingCartId,
|
|
122
|
+
//confirmedAt,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
// This should fail
|
|
126
|
+
// {
|
|
127
|
+
// type: 'ShoppingCartCanceled',
|
|
128
|
+
// data: {
|
|
129
|
+
// shoppingCartId,
|
|
130
|
+
// canceledAt,
|
|
131
|
+
// },
|
|
132
|
+
// },
|
|
133
|
+
]);
|
|
134
|
+
});
|
|
127
135
|
});
|
|
128
136
|
//# sourceMappingURL=applicationLogicWithOC.int.spec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/e2e/decider/applicationLogicWithOC.int.spec.ts"],"names":[
|
|
1
|
+
{"version":3,"file":"applicationLogicWithOC.int.spec.js","sourceRoot":"","sources":["../../../src/e2e/decider/applicationLogicWithOC.int.spec.ts"],"names":[],"mappings":"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"}
|