Skip to content

Commit 6bf47f7

Browse files
feat(csi-1309): added notifications for failed tests only (#21)
* feat(csi-704): removed unneeded code * feat(csi-1306): added notifications for failed tests * chore(snapshot): 1.8.0-snapshot.0 * feat(csi-1306): added notifications for failed tests * chore(snapshot): 1.8.0-snapshot.1 * feat(csi-1309): added notifications for failed tests only * feat(csi-1309): code cleanup * chore(snapshot): 1.8.0-snapshot.2 * fix: improve readability of brief summary --------- Co-authored-by: Kalin Krustev <kalin.krustev@gmail.com>
1 parent 179920c commit 6bf47f7

File tree

8 files changed

+113
-22
lines changed

8 files changed

+113
-22
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@mojaloop/ml-testing-toolkit-client-lib",
33
"description": "Testing Toolkit Client Library",
4-
"version": "1.7.0",
4+
"version": "1.8.0-snapshot.2",
55
"license": "Apache-2.0",
66
"author": "Vijaya Kumar Guthi, ModusBox Inc. ",
77
"contributors": [

src/client.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ program
4747
.option('--report-auto-filename-enable <reportAutoFilenameEnable>', 'default: false, if true the file name will be generated by the backend')
4848
.option('--report-target <reportTarget>', 'default: "file://<file_name_genrated_by_backend>" --- supported targets: "file://path_to_file", "s3://<bucket_name>[/<file_path>]"')
4949
.option('--slack-webhook-url <slackWebhookUrl>', 'default: "Disabled" --- supported formats: "https://....."')
50+
.option('--slack-webhook-url-for-failed <slackWebhookUrl>', 'default: "Disabled" --- supported formats: "https://....."')
5051
.option('--extra-summary-information <Comma separated values in the format key:value>', 'default: none --- example: "Testcase Name:something,Environment:Dev1"')
5152
.option('--brief-summary-prefix <Prefix to use for a brief summary in Slack>', 'default: none --- example: "environment name, test name"')
5253
.on('--help', () => { // Extra information on help message

src/extras/slack-broadcast.js

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ const millisecondsToTime = (milliseconds) => {
3838
return `${String(hours).padStart(2, '0')}:${String(minutes % 60).padStart(2, '0')}:${String(seconds % 60).padStart(2, '0')}`
3939
}
4040

41+
/**
42+
* @param {FinalReport} progress
43+
* @param {string} reportURL - URL of the report
44+
*/
4145
const generateSlackBlocks = (progress, reportURL) => {
4246
const slackBlocks = []
4347
let totalAssertionsCount = 0
@@ -88,10 +92,10 @@ const generateSlackBlocks = (progress, reportURL) => {
8892
text: [
8993
`${totalAssertionsCount === totalPassedAssertionsCount ? '🟢' : '🔴'}`,
9094
reportURL ? `<${reportURL}|${config.briefSummaryPrefix}>` : `${config.briefSummaryPrefix}`,
95+
`tests: \`${progress.test_cases.length}\`,`,
96+
`requests: \`${totalRequestsCount}\`,`,
9197
`failed: \`${totalAssertionsCount - totalPassedAssertionsCount}/${totalAssertionsCount}`,
9298
`(${(100 * ((totalAssertionsCount - totalPassedAssertionsCount) / totalAssertionsCount)).toFixed(2)}%)\`,`,
93-
`requests: \`${totalRequestsCount}\`,`,
94-
`tests: \`${progress.test_cases.length}\`,`,
9599
`duration: \`${millisecondsToTime(progress.runtimeInformation.runDurationMs)}\``,
96100
top5FailedTestCases.length > 0 && '\nTop 5 failed test cases:\n',
97101
top5FailedTestCases.length > 0 && top5FailedTestCases.map(tc => `• ${tc.name}: \`${tc.failedAssertions}\``).join('\n')
@@ -170,25 +174,52 @@ const generateSlackBlocks = (progress, reportURL) => {
170174
return slackBlocks
171175
}
172176

177+
/**
178+
* @param {FinalReport} progress
179+
* @param {string} reportURL - URL of the report
180+
*/
173181
const sendSlackNotification = async (progress, reportURL = 'http://localhost/') => {
174-
if (config.slackWebhookUrl) {
175-
const url = config.slackWebhookUrl
176-
const webhook = new IncomingWebhook(url)
177-
const blocks = generateSlackBlocks(progress, reportURL)
178-
179-
try {
180-
// console.log(JSON.stringify(slackBlocks, null, 2))
181-
await webhook.send({
182-
text: 'Test Report',
183-
blocks
184-
})
185-
console.log('Slack notification sent.')
186-
} catch (err) {
187-
console.log('ERROR: Sending slack notification failed. ', err.message)
188-
}
182+
const { slackWebhookUrl, slackWebhookUrlForFailed } = config
183+
184+
if (!slackWebhookUrl && !needToNotifyFailed(slackWebhookUrlForFailed, progress)) {
185+
console.log('No Slack webhook URLs configured.')
186+
return
187+
}
188+
const blocks = generateSlackBlocks(progress, reportURL)
189+
190+
if (slackWebhookUrl) {
191+
await sendWebhook(slackWebhookUrl, 'Test Report', blocks)
192+
}
193+
194+
if (needToNotifyFailed(slackWebhookUrlForFailed, progress)) {
195+
await sendWebhook(slackWebhookUrlForFailed, 'Failed Tests Report', blocks)
196+
}
197+
}
198+
199+
const sendWebhook = async (url, text, blocks) => {
200+
const webhook = new IncomingWebhook(url)
201+
try {
202+
await webhook.send({ text, blocks })
203+
console.log('Slack notification sent.')
204+
} catch (err) {
205+
console.log('ERROR: Sending Slack notification failed. ', err.message)
189206
}
190207
}
191208

209+
/**
210+
* Determines if a notification for failed tests needs to be sent.
211+
* @param {string | undefined} webhookUrl
212+
* @param {FinalReport} progress
213+
* @returns {boolean}
214+
*/
215+
const needToNotifyFailed = (webhookUrl, progress) => {
216+
return webhookUrl && (!progress?.runtimeInformation?.totalAssertions
217+
? true
218+
: progress.runtimeInformation.totalPassedAssertions !== progress.runtimeInformation.totalAssertions)
219+
}
220+
192221
module.exports = {
193-
sendSlackNotification
222+
sendSlackNotification,
223+
sendWebhook,
224+
needToNotifyFailed
194225
}

src/modes/outbound.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,36 @@ const sendTemplate = async (sessionId) => {
180180
}
181181
}
182182

183+
/**
184+
* Consolidated final report, created by generateFinalReport function (ml-testing-toolkit repo).
185+
* @typedef {Object} FinalReport
186+
* @property {RuntimeInformation} runtimeInformation - Provides metadata about the runtime environment or execution context.
187+
* @property {Array<Object>} test_cases - An array of objects where each object represents a test case and its results.
188+
* @property {string} status
189+
* @property {Object} totalResult
190+
* @property {Object} saveReportStatus
191+
* @property {unknown} [otherFields] - see ml-testing-toolkit repo.
192+
*/
193+
194+
/**
195+
* @typedef {Object} RuntimeInformation
196+
* @property {string} testReportId - Test report ID
197+
* @property {string} completedTimeISO - Completed time in ISO format
198+
* @property {string} startedTime - Started time in readable format
199+
* @property {string} completedTime - Completed time in readable format
200+
* @property {string} completedTimeUTC - Completed time in UTC format
201+
* @property {number} startedTS - Started timestamp
202+
* @property {number} completedTS - Completed timestamp
203+
* @property {number} runDurationMs - Run duration in milliseconds
204+
* @property {number} totalAssertions - Total number of assertions
205+
* @property {number} totalPassedAssertions - Total number of passed assertions
206+
*/
207+
208+
/**
209+
* Handles incoming progress updates and processes them as needed.
210+
* @param {FinalReport} progress
211+
* @returns {Promise<void>}
212+
*/
183213
const handleIncomingProgress = async (progress) => {
184214
const config = objectStore.get('config')
185215
if (progress.status === 'FINISHED') {

src/router.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const cli = (commanderOptions) => {
6363
reportAutoFilenameEnable: commanderOptions.reportAutoFilenameEnable === 'true' || configFile.reportAutoFilenameEnable === true,
6464
reportTarget: commanderOptions.reportTarget || configFile.reportTarget,
6565
slackWebhookUrl: commanderOptions.slackWebhookUrl || configFile.slackWebhookUrl,
66+
slackWebhookUrlForFailed: commanderOptions.slackWebhookUrlForFailed || configFile.slackWebhookUrlForFailed,
6667
slackPassedImage: configFile.slackPassedImage,
6768
slackFailedImage: configFile.slackFailedImage,
6869
baseURL: commanderOptions.baseUrl || configFile.baseURL,

test/unit/extras/slack-broadcast.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,32 @@ describe('Cli client', () => {
134134
}))
135135
})
136136
})
137+
138+
describe('needToNotifyFailed Tests -->', () => {
139+
it('should not notify if slackWebhookUrlForFailed is not configured', () => {
140+
expect(slackBroadCast.needToNotifyFailed(undefined, {})).toBeFalsy()
141+
})
142+
143+
it('should notify if webhookUrl is set, but no progress.runtimeInformation info', () => {
144+
expect(slackBroadCast.needToNotifyFailed('url', {})).toBe(true)
145+
})
146+
147+
it('should NOT notify success tests', () => {
148+
expect(slackBroadCast.needToNotifyFailed('url', {
149+
runtimeInformation: {
150+
totalAssertions: 1,
151+
totalPassedAssertions: 1
152+
}
153+
})).toBe(false)
154+
})
155+
156+
it('should notify in case failed tests', () => {
157+
expect(slackBroadCast.needToNotifyFailed('url', {
158+
runtimeInformation: {
159+
totalAssertions: 1,
160+
totalPassedAssertions: 0
161+
}
162+
})).toBe(true)
163+
})
164+
})
137165
})

test/unit/router.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('Cli client', () => {
4040
it('when mode is monitoring should not throw an error', async () => {
4141
const config = {
4242
"mode": "monitoring"
43-
}
43+
}
4444
spyExit.mockImplementationOnce(jest.fn())
4545
expect(() => {
4646
cli(config)

0 commit comments

Comments
 (0)