/
fake-message-queue.test.js
154 lines (133 loc) · 4.73 KB
/
fake-message-queue.test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
87
88
89
90
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
const axios = require('axios');
const sinon = require('sinon');
const nock = require('nock');
const testHelpers = require('./test-helpers');
const {
initializeWebServer,
stopWebServer,
} = require('../../example-application/entry-points/api');
const MessageQueueClient = require('../../example-application/libraries/message-queue-client');
const {
FakeMessageQueueProvider,
} = require('../../example-application/libraries/fake-message-queue-provider');
const {
QueueConsumer,
} = require('../../example-application/entry-points/message-queue-consumer');
let axiosAPIClient;
beforeAll(async () => {
// ️️️✅ Best Practice: Place the backend under test within the same process
const apiConnection = await initializeWebServer();
// ️️️✅ Best Practice: Ensure that this component is isolated by preventing unknown calls
nock.disableNetConnect();
nock.enableNetConnect('127.0.0.1');
const axiosConfig = {
baseURL: `http://127.0.0.1:${apiConnection.port}`,
validateStatus: () => true, //Don't throw HTTP exceptions. Delegate to the tests to decide which error is acceptable
};
axiosAPIClient = axios.create(axiosConfig);
process.env.USE_FAKE_MQ = 'true';
});
beforeEach(() => {
nock('http://localhost/user/').get(`/1`).reply(200, {
id: 1,
name: 'John',
});
nock('http://mail.com').post('/send').reply(202);
});
afterEach(() => {
nock.cleanAll();
sinon.restore();
});
afterAll(async () => {
// ️️️✅ Best Practice: Clean-up resources after each run
await stopWebServer();
//await messageQueueClient.close();
nock.enableNetConnect();
process.env.USE_FAKE_MQ = undefined;
});
// ️️️✅ Best Practice: Test a flow that starts via a queue message and ends with removing/confirming the message
test('Whenever a user deletion message arrive, then his orders are deleted', async () => {
// Arrange
const orderToAdd = {
userId: 1,
productId: 2,
mode: 'approved',
};
const addedOrderId = (await axiosAPIClient.post('/order', orderToAdd)).data
.id;
const messageQueueClient = await testHelpers.startMQConsumer('fake');
// Act
await messageQueueClient.publish('user.events', 'user.deleted', {
id: addedOrderId,
});
// Assert
await messageQueueClient.waitFor('ack', 1);
const aQueryForDeletedOrder = await axiosAPIClient.get(
`/order/${addedOrderId}`
);
expect(aQueryForDeletedOrder.status).toBe(404);
});
test('When a poisoned message arrives, then it is being rejected back', async () => {
// Arrange
const messageWithInvalidSchema = { nonExistingProperty: 'invalid❌' };
const messageQueueClient = await testHelpers.startMQConsumer('fake');
// Act
await messageQueueClient.publish(
'user.events',
'user.deleted',
messageWithInvalidSchema
);
// Assert
await messageQueueClient.waitFor('nack', 1);
});
test('When user deleted message arrives, then all corresponding orders are deleted', async () => {
// Arrange
const orderToAdd = { userId: 1, productId: 2, status: 'approved' };
const addedOrderId = (await axiosAPIClient.post('/order', orderToAdd)).data
.id;
const messageQueueClient = new MessageQueueClient(
new FakeMessageQueueProvider()
);
await new QueueConsumer(messageQueueClient, 'user.deleted').start();
// Act
await messageQueueClient.publish('user.events', 'user.deleted', {
id: addedOrderId,
});
// Assert
await messageQueueClient.waitFor('ack', 1);
const aQueryForDeletedOrder = await axiosAPIClient.get(
`/order/${addedOrderId}`
);
expect(aQueryForDeletedOrder.status).toBe(404);
});
// ️️️✅ Best Practice: Verify that messages are put in queue whenever the requirements state so
test('When a valid order is added, then a message is emitted to the new-order queue', async () => {
//Arrange
const orderToAdd = {
userId: 1,
productId: 2,
mode: 'approved',
};
const spyOnSendMessage = sinon.spy(MessageQueueClient.prototype, 'publish');
//Act
await axiosAPIClient.post('/order', orderToAdd);
// Assert
expect(spyOnSendMessage.lastCall.args[0]).toBe('order.events');
expect(spyOnSendMessage.lastCall.args[1]).toBe('order.events.new');
});
test.todo('When an error occurs, then the message is not acknowledged');
test.todo(
'When a new valid user-deletion message is processes, then the message is acknowledged'
);
test.todo(
'When two identical create-order messages arrives, then the app is idempotent and only one is created'
);
test.todo(
'When occasional failure occur during message processing , then the error is handled appropriately'
);
test.todo(
'When multiple user deletion message arrives, then all the user orders are deleted'
);
test.todo(
'When multiple user deletion message arrives and one fails, then only the failed message is not acknowledged'
);