ml-testing-toolkit 18.19.0 → 18.19.2
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/.grype.yaml +27 -14
- package/CHANGELOG.md +19 -0
- package/Dockerfile +1 -2
- package/audit-ci.jsonc +4 -3
- package/package.json +25 -20
- package/{sbom-v18.17.3.csv → sbom-v18.19.0.csv} +48 -47
- package/src/lib/mocking/openApiMockHandler.js +11 -4
- package/src/lib/mocking/openApiRulesEngine.js +3 -1
- package/src/lib/test-outbound/outbound-initiator.js +7 -3
- package/src/lib/utils.js +82 -0
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
* Mojaloop Foundation
|
|
23
23
|
- Name Surname <name.surname@mojaloop.io>
|
|
24
|
+
- Shashikant Hirugade <shashi.mojaloop@gmail.com>
|
|
24
25
|
|
|
25
26
|
* ModusBox
|
|
26
27
|
* Georgi Logodazhki <georgi.logodazhki@modusbox.com>
|
|
@@ -275,8 +276,11 @@ const openApiBackendNotImplementedHandler = async (context, req, h, item) => {
|
|
|
275
276
|
let responseBody, responseStatus
|
|
276
277
|
// Check for response map file
|
|
277
278
|
try {
|
|
278
|
-
const respMapRawdata = await utils.
|
|
279
|
-
const responseMap =
|
|
279
|
+
const respMapRawdata = await utils.resolveAndLoad(item.responseMapFile)
|
|
280
|
+
const responseMap =
|
|
281
|
+
(typeof respMapRawdata === 'string')
|
|
282
|
+
? JSON.parse(respMapRawdata)
|
|
283
|
+
: respMapRawdata
|
|
280
284
|
if (responseMap && responseMap[context.operation.path] && responseMap[context.operation.path][context.request.method]) {
|
|
281
285
|
const responseInfo = responseMap[context.operation.path][context.request.method]
|
|
282
286
|
req.customInfo.responseInfo = responseInfo
|
|
@@ -346,8 +350,11 @@ const generateAsyncCallback = async (item, context, req) => {
|
|
|
346
350
|
|
|
347
351
|
// Getting callback info from callback map file
|
|
348
352
|
try {
|
|
349
|
-
const cbMapRawdata = await utils.
|
|
350
|
-
const callbackMap =
|
|
353
|
+
const cbMapRawdata = await utils.resolveAndLoad(item.callbackMapFile)
|
|
354
|
+
const callbackMap =
|
|
355
|
+
(typeof cbMapRawdata === 'string')
|
|
356
|
+
? JSON.parse(cbMapRawdata)
|
|
357
|
+
: cbMapRawdata
|
|
351
358
|
if (!callbackMap[context.operation.path]) {
|
|
352
359
|
customLogger.logMessage('error', 'Callback not found for path in callback map file for ' + context.operation.path, req.customInfo.user, { request: req })
|
|
353
360
|
return
|
|
@@ -41,6 +41,7 @@ const uuid = require('uuid')
|
|
|
41
41
|
const postmanContext = require('../scripting-engines/postman-sandbox')
|
|
42
42
|
const javascriptContext = require('../scripting-engines/vm-javascript-sandbox')
|
|
43
43
|
const { OpenApiMockGenerator } = require('@mojaloop/ml-testing-toolkit-shared-lib')
|
|
44
|
+
const utils = require('../utils')
|
|
44
45
|
|
|
45
46
|
// const jsfRefFilePathPrefix = 'spec_files/jsf_ref_files/'
|
|
46
47
|
|
|
@@ -342,7 +343,8 @@ const responseRules = async (context, req) => {
|
|
|
342
343
|
} else if (curEvent.type === 'MOCK_RESPONSE') {
|
|
343
344
|
if (req.customInfo.specFile) {
|
|
344
345
|
const responseGenerator = new OpenApiMockGenerator()
|
|
345
|
-
await
|
|
346
|
+
const filePath = await utils.checkUrl(path.join(req.customInfo.specFile))
|
|
347
|
+
await responseGenerator.load(filePath)
|
|
346
348
|
let jsfRefs1 = []
|
|
347
349
|
if (req.customInfo.jsfRefFile) {
|
|
348
350
|
try {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
* Mojaloop Foundation
|
|
23
23
|
- Name Surname <name.surname@mojaloop.io>
|
|
24
|
+
- Shashikant Hirugade <shashi.mojaloop@gmail.com>
|
|
24
25
|
|
|
25
26
|
* ModusBox
|
|
26
27
|
* Georgi Logodazhki <georgi.logodazhki@modusbox.com>
|
|
@@ -42,7 +43,7 @@ const Config = require('../config')
|
|
|
42
43
|
const customLogger = require('../requestLogger')
|
|
43
44
|
const MyEventEmitter = require('../MyEventEmitter')
|
|
44
45
|
const notificationEmitter = require('../notificationEmitter.js')
|
|
45
|
-
const {
|
|
46
|
+
const { resolveAndLoad, headersToLowerCase } = require('../utils')
|
|
46
47
|
const expectOriginal = require('chai').expect // eslint-disable-line
|
|
47
48
|
const JwsSigning = require('../jws/JwsSigning')
|
|
48
49
|
const ConnectionProvider = require('../configuration-providers/mb-connection-manager')
|
|
@@ -362,8 +363,11 @@ const processTestCase = async (
|
|
|
362
363
|
let successCallbackUrl = null
|
|
363
364
|
let errorCallbackUrl = null
|
|
364
365
|
if (reqApiDefinition?.asynchronous === true) {
|
|
365
|
-
const cbMapRawdata = await
|
|
366
|
-
const reqCallbackMap =
|
|
366
|
+
const cbMapRawdata = await resolveAndLoad(reqApiDefinition.callbackMapFile)
|
|
367
|
+
const reqCallbackMap =
|
|
368
|
+
(typeof cbMapRawdata === 'string')
|
|
369
|
+
? JSON.parse(cbMapRawdata)
|
|
370
|
+
: cbMapRawdata
|
|
367
371
|
if (reqCallbackMap[request.operationPath] && reqCallbackMap[request.operationPath][request.method]) {
|
|
368
372
|
const successCallback = reqCallbackMap[request.operationPath][request.method].successCallback
|
|
369
373
|
const errorCallback = reqCallbackMap[request.operationPath][request.method].errorCallback
|
package/src/lib/utils.js
CHANGED
|
@@ -21,12 +21,14 @@
|
|
|
21
21
|
|
|
22
22
|
* Mojaloop Foundation
|
|
23
23
|
- Name Surname <name.surname@mojaloop.io>
|
|
24
|
+
- Shashikant Hirugade <shashi.mojaloop@gmail.com>
|
|
24
25
|
|
|
25
26
|
* ModusBox
|
|
26
27
|
* Georgi Logodazhki <georgi.logodazhki@modusbox.com> (Original Author)
|
|
27
28
|
--------------
|
|
28
29
|
******/
|
|
29
30
|
|
|
31
|
+
'use strict'
|
|
30
32
|
const fs = require('fs')
|
|
31
33
|
const mv = require('mv')
|
|
32
34
|
const { files } = require('node-dir')
|
|
@@ -44,6 +46,8 @@ const rmdirAsync = promisify(fs.rmdir)
|
|
|
44
46
|
const mvAsync = promisify(mv)
|
|
45
47
|
const { resolve } = require('path')
|
|
46
48
|
const yaml = require('js-yaml')
|
|
49
|
+
const http = require('http')
|
|
50
|
+
const https = require('https')
|
|
47
51
|
|
|
48
52
|
const getHeader = (headers, name) => {
|
|
49
53
|
return Object.entries(headers).find(
|
|
@@ -97,6 +101,83 @@ const checkUrl = async fileName => {
|
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
103
|
|
|
104
|
+
const isHttpUrl = (s) => typeof s === 'string' && /^https?:\/\//i.test(s)
|
|
105
|
+
|
|
106
|
+
const fetchText = (url) => new Promise((resolve, reject) => {
|
|
107
|
+
const lib = url.startsWith('https://') ? https : http
|
|
108
|
+
const req = lib.get(
|
|
109
|
+
url,
|
|
110
|
+
{ headers: { 'User-Agent': 'mojaloop-ttk' } },
|
|
111
|
+
(res) => {
|
|
112
|
+
// follow redirects
|
|
113
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
114
|
+
res.resume()
|
|
115
|
+
return resolve(fetchText(res.headers.location))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
119
|
+
const chunks = []
|
|
120
|
+
res.on('data', (c) => chunks.push(c))
|
|
121
|
+
res.on('end', () => {
|
|
122
|
+
const body = Buffer.concat(chunks).toString('utf8')
|
|
123
|
+
reject(new Error(`Failed to fetch ${url}: HTTP ${res.statusCode}\n${body.slice(0, 500)}`))
|
|
124
|
+
})
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const chunks = []
|
|
129
|
+
res.on('data', (c) => chunks.push(c))
|
|
130
|
+
res.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
req.on('error', reject)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
const parseJsonOrYaml = (content, label = 'content') => {
|
|
137
|
+
try {
|
|
138
|
+
return JSON.parse(content)
|
|
139
|
+
} catch (jsonErr) {
|
|
140
|
+
try {
|
|
141
|
+
return yaml.load(content)
|
|
142
|
+
} catch (yamlErr) {
|
|
143
|
+
throw new Error(`Failed to parse ${label} as JSON or YAML: ${jsonErr.message}; ${yamlErr.message}`)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* resolveAndLoad:
|
|
150
|
+
* - accepts a local file path OR a direct URL
|
|
151
|
+
* - if local file content is JSON/YAML -> returns parsed value
|
|
152
|
+
* - if local file content is a URL string -> fetches it and returns parsed value
|
|
153
|
+
* - if argument itself is a URL -> fetches it and returns parsed value
|
|
154
|
+
*
|
|
155
|
+
* NOTE: This function resolves relative file paths using TTK_ROOT.
|
|
156
|
+
*/
|
|
157
|
+
const resolveAndLoad = async (filePathOrUrl) => {
|
|
158
|
+
// If caller passes URL directly
|
|
159
|
+
if (isHttpUrl(filePathOrUrl)) {
|
|
160
|
+
const remoteText = await fetchText(filePathOrUrl)
|
|
161
|
+
return parseJsonOrYaml(remoteText, filePathOrUrl)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Treat as local file; resolve relative path via TTK_ROOT
|
|
165
|
+
const localPath = resolveRoot(filePathOrUrl)
|
|
166
|
+
const localText = await fs.promises.readFile(localPath, 'utf8')
|
|
167
|
+
|
|
168
|
+
// Parse local JSON/YAML
|
|
169
|
+
const parsedLocal = parseJsonOrYaml(localText, localPath)
|
|
170
|
+
|
|
171
|
+
// If local content is a URL string, follow it
|
|
172
|
+
if (isHttpUrl(parsedLocal)) {
|
|
173
|
+
const remoteText = await fetchText(parsedLocal)
|
|
174
|
+
return parseJsonOrYaml(remoteText, parsedLocal)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Otherwise local file contained actual JSON/YAML content
|
|
178
|
+
return parsedLocal
|
|
179
|
+
}
|
|
180
|
+
|
|
100
181
|
module.exports = {
|
|
101
182
|
readFileAsync: resolve1(readFileAsync),
|
|
102
183
|
writeFileAsync: resolve1(writeFileAsync),
|
|
@@ -113,5 +194,6 @@ module.exports = {
|
|
|
113
194
|
headersToLowerCase,
|
|
114
195
|
urlToPath,
|
|
115
196
|
checkUrl,
|
|
197
|
+
resolveAndLoad,
|
|
116
198
|
resolve: resolveRoot
|
|
117
199
|
}
|