@mojaloop/sdk-scheme-adapter 11.18.5 → 11.18.9

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 (27) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/InboundServer/api.yaml +2 -0
  3. package/InboundServer/handlers.js +0 -38
  4. package/InboundServer/middlewares.js +1 -1
  5. package/OutboundServer/api.yaml +23 -270
  6. package/OutboundServer/api_interfaces/openapi.d.ts +10 -127
  7. package/OutboundServer/api_template/openapi.yaml +0 -3
  8. package/OutboundServer/handlers.js +2 -40
  9. package/audit-resolve.json +98 -6
  10. package/lib/cache.js +23 -7
  11. package/lib/model/index.js +1 -3
  12. package/package.json +30 -58
  13. package/test/__mocks__/@mojaloop/sdk-standard-components.js +0 -2
  14. package/test/config/integration.env +0 -3
  15. package/test/unit/inboundApi/handlers.test.js +0 -46
  16. package/test/unit/outboundApi/handlers.test.js +1 -100
  17. package/OutboundServer/api_template/components/responses/authorizationPostSuccess.yaml +0 -5
  18. package/OutboundServer/api_template/components/responses/authorizationsServerError.yaml +0 -5
  19. package/OutboundServer/api_template/components/schemas/authorizationsPostRequest.yaml +0 -15
  20. package/OutboundServer/api_template/components/schemas/authorizationsPostResponse.yaml +0 -19
  21. package/OutboundServer/api_template/components/schemas/errorAuthorizationsResponse.yaml +0 -3
  22. package/OutboundServer/api_template/paths/authorizations.yaml +0 -19
  23. package/lib/model/AuthorizationsModel.js +0 -86
  24. package/test/integration/lib/Outbound/authorizations.test.js +0 -58
  25. package/test/integration/lib/Outbound/data/authorizationsPostRequest.json +0 -43
  26. package/test/unit/lib/model/AuthorizationsModel.test.js +0 -460
  27. package/test/unit/lib/model/data/putAuthorizationsResponse.json +0 -10
@@ -1,86 +0,0 @@
1
- /**************************************************************************
2
- * (C) Copyright ModusBox Inc. 2020 - All rights reserved. *
3
- * *
4
- * This file is made available under the terms of the license agreement *
5
- * specified in the corresponding source code repository. *
6
- * *
7
- * ORIGINAL AUTHOR: *
8
- * Sridhar Voruganti - sridhar.voruganti@modusbox.com *
9
- **************************************************************************/
10
-
11
- 'use strict';
12
-
13
- const Async2SyncModel = require('./Async2SyncModel');
14
-
15
-
16
- // delegated methods for AuthorizationsModel
17
-
18
- /**
19
- * @name channelName
20
- * @description generates the pub/sub channel name
21
- * @param {object} - args
22
- * @param {string} args.transactionRequestId - the transactionRequestId
23
- * @param {string} [args.fspId] - ignored if passed - the destination fsp id
24
- * @param {string} [args.authorization] - ignored if passed - the authorization payload
25
- * @returns {string} - the pub/sub channel name
26
- */
27
- function channelName ({ transactionRequestId /* ,fspId, authorization - are not used here */ }) {
28
- const tokens = ['authorizations', transactionRequestId];
29
- return tokens.map(x => `${x}`).join('-');
30
- }
31
-
32
- /**
33
- * @name requestAction
34
- * @description invokes the call to switch
35
- * @param {object} requests - MojaloopRequests instance
36
- * @param {array} args - the arguments passed as object to `run` method
37
- * @param {string} [args.transactionRequestId] - the transactionRequestId
38
- * @param {string} args.fspId - the destination fsp id
39
- * @param {string} args.authorization - the authorization
40
- */
41
- function requestAction (requests, { /* transactionRequestId, not used here */ fspId, authorization }) {
42
- if (!fspId) {
43
- throw new Error('AuthorizationsModel args requires \'fspId\' to be nonempty string');
44
- }
45
- return requests.postAuthorizations(authorization, fspId);
46
- }
47
-
48
- /**
49
- * @name argsValidationMethod
50
- * @description makes validation of args object, invoked in `run, triggerDeferredJob, generateKey` methods to ensure everything is going well
51
- * @param {array} args - the arguments passed as object to `run` method
52
- * @param {string} args.transactionRequestId - the transactionRequestId
53
- * @param {string} args.fspId - the destination fsp id
54
- * @param {string} [args.authorization] - ignored if passed - the authorization payload
55
- */
56
- function argsValidation ({ transactionRequestId, fspId /* ,authorization not used here */ }) {
57
- if (!(transactionRequestId && typeof (transactionRequestId) === 'string' && transactionRequestId.length > 0)) {
58
- throw new Error('AuthorizationsModel args requires \'transactionRequestId\' is nonempty string and mandatory property');
59
- }
60
- if (fspId && !(typeof (fspId) === 'string' && fspId.length > 0)) {
61
- throw new Error('AuthorizationsModel args requires \'fspId\' to be nonempty string');
62
- }
63
- }
64
-
65
- /**
66
- * @name reformatMessage
67
- * @description reformats message received from PUB/SUB channel, it is optional method, if not specified identify function is used by default
68
- * @param {object} message - message received
69
- * @returns {object} - reformatted message
70
- */
71
- function reformatMessage (message) {
72
- return {
73
- authorizations: { ...message }
74
- };
75
- }
76
-
77
- // generate model
78
- const AuthorizationsModel = Async2SyncModel.generate({
79
- modelName: 'AuthorizationsModel',
80
- channelNameMethod: channelName,
81
- requestActionMethod: requestAction,
82
- argsValidationMethod: argsValidation,
83
- reformatMessageMethod: reformatMessage
84
- });
85
-
86
- module.exports = AuthorizationsModel;
@@ -1,58 +0,0 @@
1
- 'use strict';
2
-
3
- const axios = require('axios');
4
- const { uuid } = require('uuidv4');
5
- const env = require('../../testEnv');
6
- const authorizationsPostRequest = require('./data/authorizationsPostRequest.json');
7
-
8
- jest.dontMock('redis');
9
-
10
- describe('/authorizations', () => {
11
-
12
- test('post - happy flow', async () => {
13
- const postAuthorizationsURI = `${env.OutboundHostURI}/authorizations`;
14
- const transactionRequestId = uuid();
15
- const res = await axios({
16
- method: 'POST',
17
- url: postAuthorizationsURI,
18
- data: {
19
- fspId: 'switch',
20
- authorizationsPostRequest: {
21
- ...authorizationsPostRequest,
22
- transactionRequestId
23
- }
24
- },
25
- headers: {
26
- 'access-control-allow-origin': '*'
27
- }
28
- });
29
-
30
- expect(res.status).toEqual(200);
31
- expect(res.data.currentState).toEqual('COMPLETED');
32
- expect(typeof res.data).toEqual('object');
33
- });
34
-
35
- test('post - timeout', (done) => {
36
- const postAuthorizationsURI = `${env.OutboundHostURI}/authorizations`;
37
- const transactionRequestId = uuid();
38
- axios({
39
- method: 'POST',
40
- url: postAuthorizationsURI,
41
- data: {
42
- fspId: 'timeout-fsp-id',
43
- authorizationsPostRequest: {
44
- ...authorizationsPostRequest,
45
- transactionRequestId
46
- }
47
- },
48
- headers: {
49
- 'access-control-allow-origin': '*'
50
- }
51
- }).catch(err => {
52
- expect(err.response.status).toEqual(500);
53
- expect(err.response.data.message).toEqual('Timeout');
54
- done();
55
- });
56
- });
57
-
58
- });
@@ -1,43 +0,0 @@
1
- {
2
- "authenticationType": "U2F",
3
- "retriesLeft": 1,
4
- "amount": {
5
- "currency": "USD",
6
- "amount": "100"
7
- },
8
- "transactionId": "2f169631-ef99-4cb1-96dc-91e8fc08f539",
9
- "transactionRequestId": "02e28448-3c05-4059-b5f7-d518d0a2d8ea",
10
- "quote": {
11
- "transferAmount": {
12
- "currency": "USD",
13
- "amount": "100"
14
- },
15
- "payeeReceiveAmount": {
16
- "currency": "USD",
17
- "amount": "99"
18
- },
19
- "payeeFspFee": {
20
- "currency": "USD",
21
- "amount": "1"
22
- },
23
- "payeeFspCommission": {
24
- "currency": "USD",
25
- "amount": "0"
26
- },
27
- "expiration": "2020-05-17T15:28:54.250Z",
28
- "geoCode": {
29
- "latitude": "+45.4215",
30
- "longitude": "+75.6972"
31
- },
32
- "ilpPacket": "AQAAAAAAACasIWcuc2UubW9iaWxlbW9uZXkubXNpc2RuLjEyMzQ1Njc4OYIEIXsNCiAgICAidHJhbnNhY3Rpb25JZCI6ICI4NWZlYWMyZi0zOWIyLTQ5MWItODE3ZS00YTAzMjAzZDRmMTQiLA0KICAgICJxdW90ZUlkIjogIjdjMjNlODBjLWQwNzgtNDA3Ny04MjYzLTJjMDQ3ODc2ZmNmNiIsDQogICAgInBheWVlIjogew0KICAgICAgICAicGFydHlJZEluZm8iOiB7DQogICAgICAgICAgICAicGFydHlJZFR5cGUiOiAiTVNJU0ROIiwNCiAgICAgICAgICAgICJwYXJ0eUlkZW50aWZpZXIiOiAiMTIzNDU2Nzg5IiwNCiAgICAgICAgICAgICJmc3BJZCI6ICJNb2JpbGVNb25leSINCiAgICAgICAgfSwNCiAgICAgICAgInBlcnNvbmFsSW5mbyI6IHsNCiAgICAgICAgICAgICJjb21wbGV4TmFtZSI6IHsNCiAgICAgICAgICAgICAgICAiZmlyc3ROYW1lIjogIkhlbnJpayIsDQogICAgICAgICAgICAgICAgImxhc3ROYW1lIjogIkthcmxzc29uIg0KICAgICAgICAgICAgfQ0KICAgICAgICB9DQogICAgfSwNCiAgICAicGF5ZXIiOiB7DQogICAgICAgICJwZXJzb25hbEluZm8iOiB7DQogICAgICAgICAgICAiY29tcGxleE5hbWUiOiB7DQogICAgICAgICAgICAgICAgImZpcnN0TmFtZSI6ICJNYXRzIiwNCiAgICAgICAgICAgICAgICAibGFzdE5hbWUiOiAiSGFnbWFuIg0KICAgICAgICAgICAgfQ0KICAgICAgICB9LA0KICAgICAgICAicGFydHlJZEluZm8iOiB7DQogICAgICAgICAgICAicGFydHlJZFR5cGUiOiAiSUJBTiIsDQogICAgICAgICAgICAicGFydHlJZGVudGlmaWVyIjogIlNFNDU1MDAwMDAwMDA1ODM5ODI1NzQ2NiIsDQogICAgICAgICAgICAiZnNwSWQiOiAiQmFua05yT25lIg0KICAgICAgICB9DQogICAgfSwNCiAgICAiYW1vdW50Ijogew0KICAgICAgICAiYW1vdW50IjogIjEwMCIsDQogICAgICAgICJjdXJyZW5jeSI6ICJVU0QiDQogICAgfSwNCiAgICAidHJhbnNhY3Rpb25UeXBlIjogew0KICAgICAgICAic2NlbmFyaW8iOiAiVFJBTlNGRVIiLA0KICAgICAgICAiaW5pdGlhdG9yIjogIlBBWUVSIiwNCiAgICAgICAgImluaXRpYXRvclR5cGUiOiAiQ09OU1VNRVIiDQogICAgfSwNCiAgICAibm90ZSI6ICJGcm9tIE1hdHMiDQp9DQo\u003d\u003d",
33
- "condition": "f5sqb7tBTWPd5Y8BDFdMm9BJR_MNI4isf8p8n4D5pHA",
34
- "extensionList": {
35
- "extension": [
36
- {
37
- "key": "errorDescription1",
38
- "value": "This is a more detailed error description"
39
- }
40
- ]
41
- }
42
- }
43
- }
@@ -1,460 +0,0 @@
1
- /**************************************************************************
2
- * (C) Copyright ModusBox Inc. 2021 - All rights reserved. *
3
- * *
4
- * This file is made available under the terms of the license agreement *
5
- * specified in the corresponding source code repository. *
6
- * *
7
- * ORIGINAL AUTHOR: *
8
- * Sridhar Voruganti - sridhar.voruganti@modusbox.com *
9
- **************************************************************************/
10
-
11
- 'use strict';
12
-
13
- // we use a mock standard components lib to intercept and mock certain funcs
14
- jest.mock('@mojaloop/sdk-standard-components');
15
-
16
- const { uuid } = require('uuidv4');
17
- const Model = require('../../../../lib/model').AuthorizationsModel;
18
- const PSM = require('../../../../lib/model/common').PersistentStateMachine;
19
- const { MojaloopRequests } = require('@mojaloop/sdk-standard-components');
20
- const defaultConfig = require('./data/defaultConfig');
21
- const mockLogger = require('../../mockLogger');
22
- const deferredJob = require('../../../../lib/model/lib').deferredJob;
23
- const pt = require('promise-timeout');
24
- const authorizationsResponse = require('./data/putAuthorizationsResponse.json');
25
-
26
- describe('AuthorizationsModel', () => {
27
- let cacheKey;
28
- let data;
29
- let modelConfig;
30
-
31
- const subId = uuid();
32
- let handler = null;
33
- beforeEach(async () => {
34
-
35
- modelConfig = {
36
- logger: mockLogger({ app: 'authorizationsModel-test' }),
37
-
38
- // there is no need to mock redis but only Cache
39
- cache: {
40
- get: jest.fn(() => Promise.resolve(data)),
41
- set: jest.fn(() => Promise.resolve),
42
-
43
- // mock subscription and store handler
44
- subscribe: jest.fn(async (channel, h) => {
45
- handler = jest.fn(h);
46
- return subId;
47
- }),
48
-
49
- // mock publish and call stored handler
50
- publish: jest.fn(async (channel, message) => await handler(channel, message, subId)),
51
-
52
- unsubscribe: jest.fn(() => Promise.resolve())
53
- },
54
- ...defaultConfig
55
- };
56
- data = {
57
- the: 'mocked data'
58
- };
59
-
60
- cacheKey = `key-authorizations-${uuid()}`;
61
- });
62
-
63
- describe('create', () => {
64
- test('proper creation of model', async () => {
65
- const model = await Model.create(data, cacheKey, modelConfig);
66
-
67
- expect(model.state).toBe('start');
68
-
69
- // model's methods layout
70
- const methods = [
71
- 'run',
72
- 'getResponse',
73
- 'onRequestAction'
74
- ];
75
-
76
- methods.forEach((method) => expect(typeof model[method]).toEqual('function'));
77
- });
78
- });
79
-
80
- describe('getResponse', () => {
81
-
82
- it('should remap currentState', async () => {
83
- const model = await Model.create(data, cacheKey, modelConfig);
84
- const states = model.allStates();
85
- // should remap for all states except 'init' and 'none'
86
- states.filter((s) => s !== 'init' && s !== 'none').forEach((state) => {
87
- model.context.data.currentState = state;
88
- const result = model.getResponse();
89
- expect(result.currentState).toEqual(Model.mapCurrentState[state]);
90
- });
91
-
92
- });
93
-
94
- it('should handle unexpected state', async () => {
95
- const model = await Model.create(data, cacheKey, modelConfig);
96
-
97
- // simulate lack of state by undefined property
98
- delete model.context.data.currentState;
99
-
100
- const resp = model.getResponse();
101
- expect(resp.currentState).toEqual(Model.mapCurrentState.errored);
102
-
103
- // ensure that we log the problem properly
104
- expect(modelConfig.logger.error).toHaveBeenCalledWith(`AuthorizationsModel model response being returned from an unexpected state: ${undefined}. Returning ERROR_OCCURRED state`);
105
- });
106
- });
107
-
108
- describe('channelName', () => {
109
- it('should validate input', () => {
110
- expect(Model.channelName({})).toEqual('authorizations-undefined');
111
- });
112
-
113
- it('should generate proper channel name', () => {
114
- const transactionRequestId = uuid();
115
- expect(Model.channelName({ transactionRequestId })).toEqual(`authorizations-${transactionRequestId}`);
116
- });
117
-
118
- });
119
-
120
- describe('generateKey', () => {
121
- it('should generate proper cache key', () => {
122
- const transactionRequestId = uuid();
123
- expect(Model.generateKey({ transactionRequestId })).toEqual(`key-${Model.channelName({ transactionRequestId })}`);
124
- });
125
-
126
- it('should handle lack of transactionRequestId param', () => {
127
- expect(() => Model.generateKey({})).toThrowError(new Error('AuthorizationsModel args requires \'transactionRequestId\' is nonempty string and mandatory property'));
128
- });
129
-
130
- });
131
-
132
- describe('onRequestAction', () => {
133
-
134
- it('should implement happy flow', async () => {
135
- const transactionRequestId = uuid();
136
- const fspId = uuid();
137
- // our code takes care only about 'transactionRequestId' property
138
- const authorization = { transactionRequestId };
139
- const channel = Model.channelName({ transactionRequestId });
140
- const model = await Model.create(data, cacheKey, modelConfig);
141
- const { cache } = model.context;
142
- // mock workflow execution which is tested in separate case
143
- model.run = jest.fn(() => Promise.resolve());
144
-
145
- const message = { ...authorizationsResponse };
146
-
147
- const onRequestActionPromise = new Promise((resolve, reject) => {
148
- // manually invoke transition handler
149
- model.onRequestAction(model.fsm, { transactionRequestId, fspId, authorization })
150
- .then(() => {
151
- // subscribe should be called only once
152
- expect(cache.subscribe).toBeCalledTimes(1);
153
-
154
- // subscribe should be done to proper notificationChannel
155
- expect(cache.subscribe.mock.calls[0][0]).toEqual(channel);
156
-
157
- // check invocation of request.getParties
158
- expect(MojaloopRequests.__postAuthorizations).toBeCalledWith(authorization, fspId);
159
-
160
- // check that this.context.data is updated
161
- expect(model.context.data).toEqual({
162
- authorizations: { ...message },
163
- currentState: 'start'
164
- //current state will be updated by onAfterTransition which isn't called
165
- //when manual invocation of transition handler happens
166
-
167
- });
168
- // handler should be called only once
169
- expect(handler).toBeCalledTimes(1);
170
-
171
- // handler should unsubscribe from notification channel
172
- expect(cache.unsubscribe).toBeCalledTimes(1);
173
- expect(cache.unsubscribe).toBeCalledWith(channel, subId);
174
- resolve();
175
- }).catch((err) => { reject(err); } );
176
- });
177
-
178
- // ensure handler wasn't called before publishing the message
179
- expect(handler).not.toBeCalled();
180
-
181
- // ensure that cache.unsubscribe does not happened before fire the message
182
- expect(cache.unsubscribe).not.toBeCalled();
183
-
184
- // fire publication with given message
185
- const df = deferredJob(cache, channel);
186
- setImmediate(() => df.trigger(message));
187
-
188
- // wait for onRequestAction
189
- await onRequestActionPromise;
190
- });
191
-
192
- it('should handle timeouts', async () => {
193
- const transactionRequestId = uuid();
194
- const fspId = uuid();
195
- // our code takes care only about 'transactionRequestId' property
196
- const authorization = { transactionRequestId };
197
- const channel = Model.channelName({ transactionRequestId });
198
-
199
- const model = await Model.create(data, cacheKey, modelConfig);
200
- const { cache } = model.context;
201
- // mock workflow execution which is tested in separate case
202
- model.run = jest.fn(() => Promise.resolve());
203
-
204
- const message = { ...authorizationsResponse };
205
-
206
- const onRequestActionPromise = new Promise((resolve, reject) => {
207
- // manually invoke transition handler
208
- model.onRequestAction(model.fsm, { transactionRequestId, fspId, authorization })
209
- .then(() => reject())
210
- .catch((err) => {
211
- // subscribe should be called only once
212
- expect(err instanceof pt.TimeoutError).toBeTruthy();
213
-
214
- // subscribe should be done to proper notificationChannel
215
- expect(cache.subscribe.mock.calls[0][0]).toEqual(channel);
216
-
217
- // check invocation of request.getParties
218
- expect(MojaloopRequests.__postAuthorizations).toBeCalledWith(authorization, fspId);
219
-
220
- // handler should be called only once
221
- expect(handler).toBeCalledTimes(0);
222
-
223
- // handler should unsubscribe from notification channel
224
- expect(cache.unsubscribe).toBeCalledTimes(1);
225
- expect(cache.unsubscribe).toBeCalledWith(channel, subId);
226
- resolve();
227
- });
228
- });
229
-
230
- // ensure handler wasn't called before publishing the message
231
- expect(handler).not.toBeCalled();
232
-
233
- // ensure that cache.unsubscribe does not happened before fire the message
234
- expect(cache.unsubscribe).not.toBeCalled();
235
-
236
- // fire publication with given message
237
- const df = deferredJob(cache, channel);
238
-
239
- setTimeout(
240
- () => { df.trigger(message); },
241
- // ensure that publication will be far long after timeout should be auto triggered
242
- (modelConfig.requestProcessingTimeoutSeconds + 1) * 1000
243
- );
244
-
245
- // wait for onRequestAction
246
- await onRequestActionPromise;
247
- });
248
-
249
- it('should unsubscribe from cache in case when error happens in workflow run', async () => {
250
- const transactionRequestId = uuid();
251
- const fspId = uuid();
252
- // our code takes care only about 'transactionRequestId' property
253
- const authorization = { transactionRequestId };
254
- const channel = Model.channelName({ transactionRequestId });
255
- const model = await Model.create(data, cacheKey, modelConfig);
256
- const { cache } = model.context;
257
-
258
- // fire publication to channel with invalid message
259
- // should throw the exception from JSON.parse
260
- const df = deferredJob(cache, channel);
261
- setImmediate(() => df.trigger(undefined));
262
-
263
- try {
264
- await model.onRequestAction(model.fsm, { transactionRequestId, fspId, authorization });
265
- throw new Error('this point should not be reached');
266
- } catch(err) {
267
- expect(err.message).toEqual('Unexpected token u in JSON at position 0');
268
- expect(cache.unsubscribe).toBeCalledTimes(1);
269
- expect(cache.unsubscribe).toBeCalledWith(channel, subId);
270
- }
271
- });
272
-
273
- it('should unsubscribe from cache in case when error happens Mojaloop requests', async () => {
274
- // simulate error
275
- MojaloopRequests.__postAuthorizations = jest.fn(() => Promise.reject('postAuthorizations failed'));
276
- const transactionRequestId = uuid();
277
- const fspId = uuid();
278
- // our code takes care only about 'transactionRequestId' property
279
- const authorization = { transactionRequestId };
280
- const channel = Model.channelName({ transactionRequestId });
281
- const model = await Model.create(data, cacheKey, modelConfig);
282
- const { cache } = model.context;
283
-
284
- let theError = null;
285
- // invoke transition handler
286
- try {
287
- await model.onRequestAction(model.fsm, { transactionRequestId, fspId, authorization });
288
- throw new Error('this point should not be reached');
289
- } catch (error) {
290
- theError = error;
291
- expect(theError).toEqual('postAuthorizations failed');
292
- // handler should unsubscribe from notification channel
293
- expect(cache.unsubscribe).toBeCalledTimes(1);
294
- expect(cache.unsubscribe).toBeCalledWith(channel, subId);
295
- }
296
- });
297
-
298
- });
299
-
300
- describe('run workflow', () => {
301
- it('start', async () => {
302
- const transactionRequestId = uuid();
303
- const fspId = uuid();
304
- // our code takes care only about 'transactionRequestId' property
305
- const authorization = { transactionRequestId };
306
-
307
- const model = await Model.create(data, cacheKey, modelConfig);
308
-
309
- model.requestAction = jest.fn();
310
- model.getResponse = jest.fn(() => Promise.resolve({ the: 'response' }));
311
-
312
- model.context.data.currentState = 'start';
313
- const result = await model.run({ transactionRequestId, fspId, authorization });
314
- expect(result).toEqual({ the: 'response' });
315
- expect(model.requestAction).toBeCalledTimes(1);
316
- expect(model.getResponse).toBeCalledTimes(1);
317
- expect(model.context.logger.log.mock.calls).toEqual([
318
- ['State machine transitioned \'init\': none -> start'],
319
- ['Action called successfully'],
320
- [`Persisted model in cache: ${cacheKey}`],
321
- ]);
322
- });
323
- it('succeeded', async () => {
324
- const transactionRequestId = uuid();
325
- const fspId = uuid();
326
- // our code takes care only about 'transactionRequestId' property
327
- const authorization = { transactionRequestId };
328
-
329
- const model = await Model.create(data, cacheKey, modelConfig);
330
-
331
- model.getResponse = jest.fn(() => Promise.resolve({ the: 'response' }));
332
-
333
- model.context.data.currentState = 'succeeded';
334
- const result = await model.run({ transactionRequestId, fspId, authorization });
335
-
336
- expect(result).toEqual({ the: 'response' });
337
- expect(model.getResponse).toBeCalledTimes(1);
338
- expect(model.context.logger.log).toBeCalledWith('Action called successfully');
339
- });
340
-
341
- it('errored', async () => {
342
- const transactionRequestId = uuid();
343
- const fspId = uuid();
344
- // our code takes care only about 'transactionRequestId' property
345
- const authorization = { transactionRequestId };
346
-
347
- const model = await Model.create(data, cacheKey, modelConfig);
348
-
349
- model.getResponse = jest.fn(() => Promise.resolve({ the: 'response' }));
350
-
351
- model.context.data.currentState = 'errored';
352
- const result = await model.run({ transactionRequestId, fspId, authorization });
353
-
354
- expect(result).toBeFalsy();
355
- expect(model.getResponse).not.toBeCalled();
356
- expect(model.context.logger.log).toBeCalledWith('State machine in errored state');
357
- });
358
-
359
- it('handling errors', async () => {
360
- const transactionRequestId = uuid();
361
- const fspId = uuid();
362
- // our code takes care only about 'transactionRequestId' property
363
- const authorization = { transactionRequestId };
364
-
365
- const model = await Model.create(data, cacheKey, modelConfig);
366
-
367
- model.requestAction = jest.fn(() => { throw new Error('mocked error'); });
368
-
369
- model.context.data.currentState = 'start';
370
-
371
- try {
372
- await model.run({ transactionRequestId, fspId, authorization });
373
- throw new Error('this point should not be reached');
374
- } catch (err) {
375
- expect(model.context.data.currentState).toEqual('errored');
376
- expect(err.requestActionState).toEqual({
377
- ...data,
378
- currentState: 'ERROR_OCCURRED',
379
- });
380
- }
381
- });
382
-
383
- it('should handle errors', async () => {
384
- const transactionRequestId = uuid();
385
- const fspId = uuid();
386
- // our code takes care only about 'transactionRequestId' property
387
- const authorization = { transactionRequestId };
388
-
389
- const model = await Model.create(data, cacheKey, modelConfig);
390
-
391
- model.requestAction = jest.fn(() => {
392
- const err = new Error('requestAction failed');
393
- err.requestActionState = 'some';
394
- return Promise.reject(err);
395
- });
396
- model.error = jest.fn();
397
- model.context.data.currentState = 'start';
398
-
399
- let theError = null;
400
- try {
401
- await model.run({ transactionRequestId, fspId, authorization });
402
- throw new Error('this point should not be reached');
403
- } catch (error) {
404
- theError = error;
405
- }
406
- // check propagation of original error
407
- expect(theError.message).toEqual('requestAction failed');
408
-
409
- // ensure we start transition to errored state
410
- expect(model.error).toBeCalledTimes(1);
411
- });
412
-
413
- it('should handle input validation for lack of transactionRequestId param', async () => {
414
- const model = await Model.create(data, cacheKey, modelConfig);
415
-
416
- expect(() => model.run({}))
417
- .rejects.toEqual(
418
- new Error('AuthorizationsModel args requires \'transactionRequestId\' is nonempty string and mandatory property')
419
- );
420
- });
421
-
422
- it('should handle input validation for fspId param', async () => {
423
- const transactionRequestId = uuid();
424
- const model = await Model.create(data, cacheKey, modelConfig);
425
-
426
- expect(() => model.run({ transactionRequestId, fspId: '' }))
427
- .rejects.toEqual(
428
- new Error('AuthorizationsModel args requires \'fspId\' to be nonempty string')
429
- );
430
- });
431
-
432
- });
433
-
434
- describe('loadFromCache', () => {
435
- test('should use PSM.loadFromCache properly', async () => {
436
- const spyLoadFromCache = jest.spyOn(PSM, 'loadFromCache');
437
- const key = uuid();
438
-
439
- // act
440
- const model = await Model.loadFromCache(key, modelConfig);
441
-
442
- // assert
443
- // check does model is proper
444
- expect(typeof model.requestAction).toEqual('function');
445
-
446
- // check how cache.get has been called
447
- expect(modelConfig.cache.get).toBeCalledWith(key);
448
-
449
- // check how loadFromCache from parent PSM module was used
450
- expect(spyLoadFromCache).toBeCalledTimes(1);
451
- expect(spyLoadFromCache).toBeCalledWith(
452
- modelConfig.cache,
453
- key,
454
- modelConfig.logger,
455
- expect.anything(),
456
- expect.anything()
457
- );
458
- });
459
- });
460
- });
@@ -1,10 +0,0 @@
1
- {
2
- "authenticationInfo": {
3
- "authentication": "U2F",
4
- "authenticationValue": {
5
- "pinValue": "101010",
6
- "counter": "3"
7
- }
8
- },
9
- "responseType": "ENTERED"
10
- }