@mojaloop/ml-testing-toolkit-client-lib 1.12.0 → 1.12.1-snapshot.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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/ml-testing-toolkit-client-lib",
|
|
3
3
|
"description": "Testing Toolkit Client Library",
|
|
4
|
-
"version": "1.12.
|
|
4
|
+
"version": "1.12.1-snapshot.2",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Vijaya Kumar Guthi, ModusBox Inc. ",
|
|
7
7
|
"contributors": [
|
|
@@ -75,9 +75,9 @@
|
|
|
75
75
|
"@slack/webhook": "7.0.6",
|
|
76
76
|
"atob": "2.1.2",
|
|
77
77
|
"aws-sdk": "2.1692.0",
|
|
78
|
-
"axios": "1.
|
|
78
|
+
"axios": "1.13.2",
|
|
79
79
|
"cli-table3": "0.6.5",
|
|
80
|
-
"commander": "14.0.
|
|
80
|
+
"commander": "14.0.2",
|
|
81
81
|
"dotenv": "17.2.3",
|
|
82
82
|
"fs": "0.0.1-security",
|
|
83
83
|
"lodash": "4.17.21",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"audit-ci": "7.1.0",
|
|
94
94
|
"jest": "30.2.0",
|
|
95
95
|
"jest-junit": "16.0.0",
|
|
96
|
-
"npm-check-updates": "19.1.
|
|
96
|
+
"npm-check-updates": "19.1.2",
|
|
97
97
|
"nyc": "17.1.0",
|
|
98
98
|
"parse-strings-in-object": "1.6.0",
|
|
99
99
|
"pre-commit": "1.2.2",
|
|
@@ -38,16 +38,89 @@ const millisecondsToTime = (milliseconds) => {
|
|
|
38
38
|
return `${String(hours).padStart(2, '0')}:${String(minutes % 60).padStart(2, '0')}:${String(seconds % 60).padStart(2, '0')}`
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Generate minimal Slack blocks for timeout with no test results
|
|
43
|
+
* @param {Object} progress - Partial progress object
|
|
44
|
+
* @param {string} reportURL - URL of the report
|
|
45
|
+
* @returns {Array} Slack blocks array
|
|
46
|
+
*/
|
|
47
|
+
/* istanbul ignore next */
|
|
48
|
+
const generateTimeoutBlocks = (progress, reportURL) => {
|
|
49
|
+
const timeoutMessage = progress?.timeoutMessage || 'Tests execution timed out'
|
|
50
|
+
const slackBlocks = []
|
|
51
|
+
|
|
52
|
+
slackBlocks.push({
|
|
53
|
+
type: 'header',
|
|
54
|
+
text: {
|
|
55
|
+
type: 'plain_text',
|
|
56
|
+
text: '⏱️ Testing Toolkit - Test Execution Timeout',
|
|
57
|
+
emoji: true
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
let summaryText = '>⚠️ *' + timeoutMessage + '*\n\n'
|
|
62
|
+
summaryText += '>Test execution was terminated before completion.\n'
|
|
63
|
+
summaryText += '>No test results available.\n\n'
|
|
64
|
+
|
|
65
|
+
if (config.reportName) {
|
|
66
|
+
summaryText += '>Report Name: *' + config.reportName + '*\n'
|
|
67
|
+
}
|
|
68
|
+
if (config.environmentName) {
|
|
69
|
+
summaryText += '>Environment: *' + config.environmentName + '*\n'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
slackBlocks.push({
|
|
73
|
+
type: 'section',
|
|
74
|
+
text: {
|
|
75
|
+
type: 'mrkdwn',
|
|
76
|
+
text: summaryText
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
if (config.extraSummaryInformation) {
|
|
81
|
+
const extraSummaryInformationArr = config.extraSummaryInformation.split(',')
|
|
82
|
+
let extraText = ''
|
|
83
|
+
extraSummaryInformationArr.forEach(info => {
|
|
84
|
+
const infoArr = info.split(':')
|
|
85
|
+
extraText += infoArr[0] + ': `' + infoArr[1] + '`\n'
|
|
86
|
+
})
|
|
87
|
+
if (extraText) {
|
|
88
|
+
slackBlocks.push({
|
|
89
|
+
type: 'section',
|
|
90
|
+
text: {
|
|
91
|
+
type: 'mrkdwn',
|
|
92
|
+
text: extraText
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
slackBlocks.push({
|
|
99
|
+
type: 'divider'
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return slackBlocks
|
|
103
|
+
}
|
|
104
|
+
|
|
41
105
|
/**
|
|
42
106
|
* @param {FinalReport} progress
|
|
43
107
|
* @param {string} reportURL - URL of the report
|
|
44
108
|
*/
|
|
45
109
|
const generateSlackBlocks = (progress, reportURL) => {
|
|
46
110
|
const slackBlocks = []
|
|
111
|
+
const isTimeout = progress?.terminatedDueToTimeout || false
|
|
47
112
|
let totalAssertionsCount = 0
|
|
48
113
|
let totalPassedAssertionsCount = 0
|
|
49
114
|
let totalRequestsCount = 0
|
|
50
115
|
const failedTestCases = []
|
|
116
|
+
|
|
117
|
+
// Handle timeout case where test_cases might be incomplete or missing
|
|
118
|
+
/* istanbul ignore next */
|
|
119
|
+
if (!progress?.test_cases || !Array.isArray(progress.test_cases)) {
|
|
120
|
+
// Create minimal notification for timeout without test results
|
|
121
|
+
return generateTimeoutBlocks(progress, reportURL)
|
|
122
|
+
}
|
|
123
|
+
|
|
51
124
|
progress.test_cases.forEach(testCase => {
|
|
52
125
|
// console.log(fStr.yellow(testCase.name))
|
|
53
126
|
totalRequestsCount += testCase.requests.length
|
|
@@ -90,8 +163,9 @@ const generateSlackBlocks = (progress, reportURL) => {
|
|
|
90
163
|
elements: [{
|
|
91
164
|
type: 'rich_text_section',
|
|
92
165
|
elements: [
|
|
93
|
-
{ type: 'text', text: `${totalAssertionsCount === totalPassedAssertionsCount ? '✅' : '⚠️'}${config.briefSummaryPrefix || ''} ` },
|
|
166
|
+
{ type: 'text', text: `${isTimeout ? '⏱️' : (totalAssertionsCount === totalPassedAssertionsCount ? '✅' : '⚠️')}${config.briefSummaryPrefix || ''} ` },
|
|
94
167
|
reportURL ? { type: 'link', url: reportURL, text: config.reportName } : { type: 'text', text: config.reportName },
|
|
168
|
+
isTimeout && { type: 'text', text: ' [TIMEOUT]' },
|
|
95
169
|
{ type: 'text', text: ' tests: ' },
|
|
96
170
|
{ type: 'text', text: String(progress.test_cases.length), style: { code: true } },
|
|
97
171
|
{ type: 'text', text: ', requests: ' },
|
|
@@ -120,13 +194,19 @@ const generateSlackBlocks = (progress, reportURL) => {
|
|
|
120
194
|
type: 'header',
|
|
121
195
|
text: {
|
|
122
196
|
type: 'plain_text',
|
|
123
|
-
text: 'Testing Toolkit Report',
|
|
197
|
+
text: isTimeout ? '⏱️ Testing Toolkit Report - TIMEOUT' : 'Testing Toolkit Report',
|
|
124
198
|
emoji: true
|
|
125
199
|
}
|
|
126
200
|
})
|
|
127
201
|
|
|
128
202
|
let summaryText = ''
|
|
129
203
|
|
|
204
|
+
/* istanbul ignore next */
|
|
205
|
+
if (isTimeout) {
|
|
206
|
+
summaryText += '>⚠️ *Tests execution timed out before completion*\n'
|
|
207
|
+
summaryText += '>Partial results below:\n\n'
|
|
208
|
+
}
|
|
209
|
+
|
|
130
210
|
summaryText += '>Total assertions: *' + totalAssertionsCount + '*\n'
|
|
131
211
|
summaryText += '>Passed assertions: *' + totalPassedAssertionsCount + '*\n'
|
|
132
212
|
summaryText += '>Failed assertions: *' + (totalAssertionsCount - totalPassedAssertionsCount) + '*\n'
|
|
@@ -138,6 +218,7 @@ const generateSlackBlocks = (progress, reportURL) => {
|
|
|
138
218
|
summaryText += '>Runtime duration: *' + `${progress.runtimeInformation.runDurationMs} ms` + '*\n'
|
|
139
219
|
|
|
140
220
|
const additionalParams = {}
|
|
221
|
+
/* istanbul ignore next */
|
|
141
222
|
if (totalAssertionsCount === totalPassedAssertionsCount) {
|
|
142
223
|
if (config.slackPassedImage) {
|
|
143
224
|
additionalParams.accessory = {
|
|
@@ -214,6 +295,7 @@ const sendWebhook = async (url, text, blocks) => {
|
|
|
214
295
|
await webhook.send({ text, blocks })
|
|
215
296
|
console.log('Slack notification sent.')
|
|
216
297
|
} catch (err) {
|
|
298
|
+
/* istanbul ignore next */
|
|
217
299
|
console.log('ERROR: Sending Slack notification failed. ', err.message)
|
|
218
300
|
}
|
|
219
301
|
}
|
package/src/modes/outbound.js
CHANGED
|
@@ -128,6 +128,7 @@ const printProgress = (progress) => {
|
|
|
128
128
|
{
|
|
129
129
|
printTotalProgressCounts()
|
|
130
130
|
console.log('\n ' + fStr.cyan(progress.testCaseName + ' -> ' + progress.requestSent.description))
|
|
131
|
+
/* istanbul ignore next */
|
|
131
132
|
if (progress.status === 'SKIPPED') {
|
|
132
133
|
console.log(' ' + fStr.yellow('(Request Skipped)'))
|
|
133
134
|
}
|
|
@@ -212,13 +213,16 @@ const sendTemplate = async (sessionId) => {
|
|
|
212
213
|
*/
|
|
213
214
|
const handleIncomingProgress = async (progress) => {
|
|
214
215
|
const config = objectStore.get('config')
|
|
216
|
+
const resultReport = await report.outbound(progress.totalResult)
|
|
217
|
+
|
|
215
218
|
if (progress.status === 'FINISHED') {
|
|
216
219
|
let passed
|
|
217
220
|
try {
|
|
218
221
|
passed = logger.outbound(progress.totalResult)
|
|
219
|
-
const resultReport = await report.outbound(progress.totalResult)
|
|
222
|
+
// const resultReport = await report.outbound(progress.totalResult)
|
|
220
223
|
let slackReportURL = resultReport.uploadedReportURL
|
|
221
224
|
// SaveReport status
|
|
225
|
+
/* istanbul ignore next */
|
|
222
226
|
if (progress.totalResult?.saveReport) {
|
|
223
227
|
if (progress.saveReportStatus?.isSaved) {
|
|
224
228
|
slackReportURL = `${config.saveReportBaseUrl || config.baseURL}/api/history/test-reports/${progress.totalResult.runtimeInformation.testReportId}?format=html`
|
|
@@ -233,6 +237,7 @@ const handleIncomingProgress = async (progress) => {
|
|
|
233
237
|
try {
|
|
234
238
|
await releaseCd(config.reportName, progress.totalResult, resultReport.uploadedReportURL)
|
|
235
239
|
} catch (err) {
|
|
240
|
+
/* istanbul ignore next */
|
|
236
241
|
console.error(err)
|
|
237
242
|
}
|
|
238
243
|
await slackBroadcast.sendSlackNotification(progress.totalResult, slackReportURL)
|
|
@@ -248,11 +253,28 @@ const handleIncomingProgress = async (progress) => {
|
|
|
248
253
|
process.exit(1)
|
|
249
254
|
}
|
|
250
255
|
} else if (progress.status === 'TERMINATED') {
|
|
256
|
+
console.log(fStr.red('Test execution terminated/timed out'))
|
|
257
|
+
|
|
258
|
+
// Send notification about timeout with whatever progress data is available
|
|
259
|
+
/* istanbul ignore next */
|
|
260
|
+
try {
|
|
261
|
+
const timeoutProgress = {
|
|
262
|
+
...(progress.totalResult || {}),
|
|
263
|
+
terminatedDueToTimeout: true,
|
|
264
|
+
timeoutMessage: 'Tests execution timed out before completion'
|
|
265
|
+
}
|
|
266
|
+
await slackBroadcast.sendSlackNotification(timeoutProgress, null)
|
|
267
|
+
} catch (err) {
|
|
268
|
+
console.log('Failed to send timeout notification:', err)
|
|
269
|
+
}
|
|
270
|
+
|
|
251
271
|
console.log(fStr.red('Terminate with exit code 1'))
|
|
252
272
|
process.exit(1)
|
|
253
273
|
} else {
|
|
254
274
|
updateTotalProgressCounts(progress)
|
|
255
275
|
printProgress(progress)
|
|
276
|
+
console.log(fStr.green(`Not expected progress.status: ${progress?.status}`))
|
|
277
|
+
await slackBroadcast.sendSlackNotification(progress?.totalResult, null)
|
|
256
278
|
}
|
|
257
279
|
}
|
|
258
280
|
|
|
@@ -39,40 +39,42 @@ const objectStore = require('../../src/objectStore')
|
|
|
39
39
|
const outbound = require('../../src/modes/outbound')
|
|
40
40
|
const spyGenerateTemplate = jest.spyOn(require('../../src/utils/templateGenerator'), 'generateTemplate')
|
|
41
41
|
|
|
42
|
+
// Mock Slack integration
|
|
43
|
+
const slackBroadcast = require('../../src/extras/slack-broadcast')
|
|
44
|
+
const spySlackBroadcast = jest.spyOn(slackBroadcast, 'sendSlackNotification')
|
|
45
|
+
spySlackBroadcast.mockImplementation(() => Promise.resolve())
|
|
46
|
+
|
|
42
47
|
describe('Cli client', () => {
|
|
43
48
|
describe('run outbound mode', () => {
|
|
44
49
|
it('when status is FINISHED and assertion passed should not throw an error', async () => {
|
|
45
50
|
const progress = {
|
|
46
51
|
"status": "FINISHED"
|
|
47
52
|
}
|
|
53
|
+
spySlackBroadcast.mockResolvedValueOnce(undefined)
|
|
48
54
|
spyReport.mockReturnValueOnce({})
|
|
49
55
|
spyLogger.mockReturnValueOnce(true)
|
|
50
56
|
spyExit.mockReturnValueOnce({})
|
|
51
|
-
expect(()
|
|
52
|
-
outbound.handleIncomingProgress(progress)
|
|
53
|
-
}).not.toThrow()
|
|
57
|
+
await expect(outbound.handleIncomingProgress(progress)).resolves.not.toThrow()
|
|
54
58
|
})
|
|
55
59
|
it('when status is FINISHED, assertions passed and there is an error should not throw an error', async () => {
|
|
56
60
|
const progress = {
|
|
57
61
|
"status": "FINISHED"
|
|
58
62
|
}
|
|
63
|
+
spySlackBroadcast.mockResolvedValueOnce(undefined)
|
|
59
64
|
spyReport.mockImplementationOnce(() => {throw new Error('expected error')})
|
|
60
65
|
spyLogger.mockReturnValueOnce(true)
|
|
61
66
|
spyExit.mockReturnValueOnce({})
|
|
62
|
-
expect(()
|
|
63
|
-
outbound.handleIncomingProgress(progress)
|
|
64
|
-
}).not.toThrow()
|
|
67
|
+
await expect(outbound.handleIncomingProgress(progress)).rejects.toThrow('expected error')
|
|
65
68
|
})
|
|
66
69
|
it('when status is FINISHED and assertions failed should not throw an error', async () => {
|
|
67
70
|
const progress = {
|
|
68
71
|
"status": "FINISHED"
|
|
69
72
|
}
|
|
73
|
+
spySlackBroadcast.mockResolvedValueOnce(undefined)
|
|
70
74
|
spyReport.mockReturnValueOnce({})
|
|
71
75
|
spyLogger.mockReturnValueOnce(false)
|
|
72
76
|
spyExit.mockReturnValueOnce({})
|
|
73
|
-
expect(()
|
|
74
|
-
outbound.handleIncomingProgress(progress)
|
|
75
|
-
}).not.toThrow()
|
|
77
|
+
await expect(outbound.handleIncomingProgress(progress)).resolves.not.toThrow()
|
|
76
78
|
})
|
|
77
79
|
it('when status is not FINISHED should not throw an error', async () => {
|
|
78
80
|
const progress = {
|