diff --git a/scripts/comment-test-report.js b/scripts/comment-test-report.js
old mode 100644
new mode 100755
index a7fd5b0bef..432c78d1af
--- a/scripts/comment-test-report.js
+++ b/scripts/comment-test-report.js
@@ -1,3 +1,5 @@
+#! /usr/bin/env node
+
//
// The script parses Allure reports and posts a comment with a summary of the test results to the PR or to the latest commit in the branch.
//
@@ -19,7 +21,7 @@
// })
//
-// Analog of Python's defaultdict.
+// Equivalent of Python's defaultdict.
//
// const dm = new DefaultMap(() => new DefaultMap(() => []))
// dm["firstKey"]["secondKey"].push("value")
@@ -32,34 +34,7 @@ class DefaultMap extends Map {
}
}
-module.exports = async ({ github, context, fetch, report }) => {
- // Marker to find the comment in the subsequent runs
- const startMarker = ``
- // If we run the script in the PR or in the branch (main/release/...)
- const isPullRequest = !!context.payload.pull_request
- // Latest commit in PR or in the branch
- const commitSha = isPullRequest ? context.payload.pull_request.head.sha : context.sha
- // Let users know that the comment is updated automatically
- const autoupdateNotice = `
The comment gets automatically updated with the latest test results
${commitSha} at ${new Date().toISOString()} :recycle:
`
- // GitHub bot id taken from (https://api.github.com/users/github-actions[bot])
- const githubActionsBotId = 41898282
- // Commend body itself
- let commentBody = `${startMarker}\n`
-
- // Common parameters for GitHub API requests
- const ownerRepoParams = {
- owner: context.repo.owner,
- repo: context.repo.repo,
- }
-
- const {reportUrl, reportJsonUrl} = report
-
- if (!reportUrl || !reportJsonUrl) {
- commentBody += `#### No tests were run or test report is not available\n`
- commentBody += autoupdateNotice
- return
- }
-
+const parseReportJson = async ({ reportJsonUrl, fetch }) => {
const suites = await (await fetch(reportJsonUrl)).json()
// Allure distinguishes "failed" (with an assertion error) and "broken" (with any other error) tests.
@@ -83,7 +58,7 @@ module.exports = async ({ github, context, fetch, report }) => {
let buildType, pgVersion
const match = test.name.match(/[\[-](?debug|release)-pg(?\d+)[-\]]/)?.groups
if (match) {
- ({buildType, pgVersion} = match)
+ ({ buildType, pgVersion } = match)
} else {
// It's ok, we embed BUILD_TYPE and Postgres Version into the test name only for regress suite and do not for other suites (like performance).
console.info(`Cannot get BUILD_TYPE and Postgres Version from test name: "${test.name}", defaulting to "release" and "14"`)
@@ -123,37 +98,68 @@ module.exports = async ({ github, context, fetch, report }) => {
}
}
+ return {
+ failedTests,
+ failedTestsCount,
+ passedTests,
+ passedTestsCount,
+ skippedTests,
+ skippedTestsCount,
+ flakyTests,
+ flakyTestsCount,
+ retriedTests,
+ pgVersions,
+ }
+}
+
+const reportSummary = async (params) => {
+ const {
+ failedTests,
+ failedTestsCount,
+ passedTests,
+ passedTestsCount,
+ skippedTests,
+ skippedTestsCount,
+ flakyTests,
+ flakyTestsCount,
+ retriedTests,
+ pgVersions,
+ reportUrl,
+ } = params
+
+ let summary = ""
+
const totalTestsCount = failedTestsCount + passedTestsCount + skippedTestsCount
- commentBody += `### ${totalTestsCount} tests run: ${passedTestsCount} passed, ${failedTestsCount} failed, ${skippedTestsCount} skipped ([full report](${reportUrl}))\n___\n`
+ summary += `### ${totalTestsCount} tests run: ${passedTestsCount} passed, ${failedTestsCount} failed, ${skippedTestsCount} skipped ([full report](${reportUrl}))\n___\n`
// Print test resuls from the newest to the oldest Postgres version for release and debug builds.
for (const pgVersion of Array.from(pgVersions).sort().reverse()) {
if (Object.keys(failedTests[pgVersion]).length > 0) {
- commentBody += `#### Failures on Posgres ${pgVersion}\n\n`
+ summary += `#### Failures on Posgres ${pgVersion}\n\n`
for (const [testName, tests] of Object.entries(failedTests[pgVersion])) {
const links = []
for (const test of tests) {
const allureLink = `${reportUrl}#suites/${test.parentUid}/${test.uid}`
links.push(`[${test.buildType}](${allureLink})`)
}
- commentBody += `- \`${testName}\`: ${links.join(", ")}\n`
+ summary += `- \`${testName}\`: ${links.join(", ")}\n`
}
const testsToRerun = Object.values(failedTests[pgVersion]).map(x => x[0].name)
const command = `DEFAULT_PG_VERSION=${pgVersion} scripts/pytest -k "${testsToRerun.join(" or ")}"`
- commentBody += "```\n"
- commentBody += `# Run failed on Postgres ${pgVersion} tests locally:\n`
- commentBody += `${command}\n`
- commentBody += "```\n"
+ summary += "```\n"
+ summary += `# Run failed on Postgres ${pgVersion} tests locally:\n`
+ summary += `${command}\n`
+ summary += "```\n"
}
}
if (flakyTestsCount > 0) {
- commentBody += `\nFlaky tests (${flakyTestsCount})
\n\n`
+ summary += `\nFlaky tests (${flakyTestsCount})
\n\n`
for (const pgVersion of Array.from(pgVersions).sort().reverse()) {
if (Object.keys(flakyTests[pgVersion]).length > 0) {
- commentBody += `#### Postgres ${pgVersion}\n\n`
+ summary += `#### Postgres ${pgVersion}\n\n`
for (const [testName, tests] of Object.entries(flakyTests[pgVersion])) {
const links = []
for (const test of tests) {
@@ -161,11 +167,57 @@ module.exports = async ({ github, context, fetch, report }) => {
const status = test.status === "passed" ? ":white_check_mark:" : ":x:"
links.push(`[${status} ${test.buildType}](${allureLink})`)
}
- commentBody += `- \`${testName}\`: ${links.join(", ")}\n`
+ summary += `- \`${testName}\`: ${links.join(", ")}\n`
}
}
}
- commentBody += "\n \n"
+ summary += "\n \n"
+ }
+
+ return summary
+}
+
+module.exports = async ({ github, context, fetch, report }) => {
+ // Marker to find the comment in the subsequent runs
+ const startMarker = ``
+ // If we run the script in the PR or in the branch (main/release/...)
+ const isPullRequest = !!context.payload.pull_request
+ // Latest commit in PR or in the branch
+ const commitSha = isPullRequest ? context.payload.pull_request.head.sha : context.sha
+ // Let users know that the comment is updated automatically
+ const autoupdateNotice = `The comment gets automatically updated with the latest test results
${commitSha} at ${new Date().toISOString()} :recycle:
`
+ // GitHub bot id taken from (https://api.github.com/users/github-actions[bot])
+ const githubActionsBotId = 41898282
+ // Commend body itself
+ let commentBody = `${startMarker}\n`
+
+ // Common parameters for GitHub API requests
+ const ownerRepoParams = {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ }
+
+ const {reportUrl, reportJsonUrl} = report
+
+ if (!reportUrl || !reportJsonUrl) {
+ commentBody += `#### No tests were run or test report is not available\n`
+ commentBody += autoupdateNotice
+ return
+ }
+
+ try {
+ const parsed = await parseReportJson({ reportJsonUrl, fetch })
+ commentBody += await reportSummary({ ...parsed, reportUrl })
+ } catch (error) {
+ commentBody += `### [full report](${reportUrl})\n___\n`
+ commentBody += `#### Failed to create a summary for the test run: \n`
+ commentBody += "```\n"
+ commentBody += `${error.stack}\n`
+ commentBody += "```\n"
+ commentBody += "\nTo reproduce and debug the error locally run:\n"
+ commentBody += "```\n"
+ commentBody += `scripts/comment-test-report.js ${reportJsonUrl}`
+ commentBody += "\n```\n"
}
commentBody += autoupdateNotice
@@ -207,3 +259,60 @@ module.exports = async ({ github, context, fetch, report }) => {
})
}
}
+
+// Equivalent of Python's `if __name__ == "__main__":`
+// https://nodejs.org/docs/latest/api/modules.html#accessing-the-main-module
+if (require.main === module) {
+ // Poor man's argument parsing: we expect the third argument is a JSON URL (0: node binary, 1: this script, 2: JSON url)
+ if (process.argv.length !== 3) {
+ console.error(`Unexpected number of arguments\nUsage: node ${process.argv[1]} `)
+ process.exit(1)
+ }
+ const jsonUrl = process.argv[2]
+
+ try {
+ new URL(jsonUrl)
+ } catch (error) {
+ console.error(`Invalid URL: ${jsonUrl}\nUsage: node ${process.argv[1]} `)
+ process.exit(1)
+ }
+
+ const htmlUrl = jsonUrl.replace("/data/suites.json", "/index.html")
+
+ const githubMock = {
+ rest: {
+ issues: {
+ createComment: console.log,
+ listComments: async () => ({ data: [] }),
+ updateComment: console.log
+ },
+ repos: {
+ createCommitComment: console.log,
+ listCommentsForCommit: async () => ({ data: [] }),
+ updateCommitComment: console.log
+ }
+ }
+ }
+
+ const contextMock = {
+ repo: {
+ owner: 'testOwner',
+ repo: 'testRepo'
+ },
+ payload: {
+ number: 42,
+ pull_request: null,
+ },
+ sha: '0000000000000000000000000000000000000000',
+ }
+
+ module.exports({
+ github: githubMock,
+ context: contextMock,
+ fetch: fetch,
+ report: {
+ reportUrl: htmlUrl,
+ reportJsonUrl: jsonUrl,
+ }
+ })
+}