diff --git a/eslint.config.mjs b/eslint.config.mjs index b1594c1f..57099dca 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -7,9 +7,9 @@ export default [ name: "default", files: ["**/*.js"], languageOptions: { - sourceType: "script", + sourceType: "module", globals: { - ...globals.node, + ...globals.nodeBuiltin, ...globals.mocha, } }, diff --git a/package-lock.json b/package-lock.json index 5bfd7b69..1fe77e5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@commitlint/load": "^19.4.0", "@digitalroute/email-verify": "^0.3.2", "js-yaml": "^4.1.0", - "lodash": "^4.17.21", + "lodash-es": "^4.17.21", "marked": "^14.0.0", "probot": "^13.3.7" }, @@ -26,7 +26,6 @@ "globals": "^15.9.0", "mocha": "^10.7.3", "nyc": "^17.0.0", - "rewire": "^7.0.0", "sinon": "^18.0.0", "sinon-chai": "^3.7.0" }, @@ -775,12 +774,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "dev": true - }, "node_modules/@humanwhocodes/retry": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", @@ -1542,12 +1535,6 @@ "@types/node": "*" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -2493,18 +2480,6 @@ "node": ">=0.3.1" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -3415,12 +3390,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4191,10 +4160,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { + "node_modules/lodash-es": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -5588,212 +5557,6 @@ "node": ">=0.10.0" } }, - "node_modules/rewire": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rewire/-/rewire-7.0.0.tgz", - "integrity": "sha512-DyyNyzwMtGYgu0Zl/ya0PR/oaunM+VuCuBxCuhYJHHaV0V+YvYa3bBGxb5OZ71vndgmp1pYY8F4YOwQo1siRGw==", - "dev": true, - "dependencies": { - "eslint": "^8.47.0" - } - }, - "node_modules/rewire/node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/rewire/node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/rewire/node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/rewire/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/rewire/node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/rewire/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/rewire/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/rewire/node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/rewire/node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/rewire/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rewire/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "node_modules/rfdc": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", diff --git a/package.json b/package.json index 9134f4ba..5f88e8e4 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "auto-me-bot", "version": "2.0.1", "private": true, + "type": "module", "description": "A Probot app that takes care of your GitHub repos for you", "author": { "name": "Tomer Figenblat", @@ -34,7 +35,7 @@ "@commitlint/load": "^19.4.0", "@digitalroute/email-verify": "^0.3.2", "js-yaml": "^4.1.0", - "lodash": "^4.17.21", + "lodash-es": "^4.17.21", "marked": "^14.0.0", "probot": "^13.3.7" }, @@ -46,7 +47,6 @@ "globals": "^15.9.0", "mocha": "^10.7.3", "nyc": "^17.0.0", - "rewire": "^7.0.0", "sinon": "^18.0.0", "sinon-chai": "^3.7.0" }, diff --git a/src/app-runner.js b/src/app-runner.js index 6a538033..c87a6d37 100644 --- a/src/app-runner.js +++ b/src/app-runner.js @@ -1,9 +1,9 @@ -const { Probot } = require('probot'); -const autoMeBot = require('./auto-me-bot'); +import { Probot } from 'probot' +import autoMeBot from './auto-me-bot' // handler function for aws lambda >> 'Handler:src/app-runner.handler' -exports.handler = async (event) => { +export async function handler (event) { let probot = new Probot({ appId: process.env.APP_ID, privateKey: Buffer.from(process.env.PRIVATE_KEY, 'base64').toString('utf-8'), diff --git a/src/auto-me-bot.js b/src/auto-me-bot.js index de6dcbb4..6b3262dd 100644 --- a/src/auto-me-bot.js +++ b/src/auto-me-bot.js @@ -1,12 +1,12 @@ -const yaml = require('js-yaml'); +import yaml from 'js-yaml' // import handlers -const prAutoApproveHandler = require('./handlers/pr-auto-approve'); -const prConventionalCommitsHandler = require('./handlers/pr-conventional-commits'); -const prConventionalTitleHandler = require('./handlers/pr-conventional-title'); -const prLifecycleLabelsHandler = require('./handlers/pr-lifecycle-labels'); -const prSignedCommitsHandler = require('./handlers/pr-signed-commits'); -const prTasksListHandler = require('./handlers/pr-tasks-list'); +import prAutoApproveHandler from './handlers/pr-auto-approve.js' +import prConventionalCommitsHandler from './handlers/pr-conventional-commits.js' +import prConventionalTitleHandler from './handlers/pr-conventional-title.js' +import prLifecycleLabelsHandler from './handlers/pr-lifecycle-labels.js' +import prSignedCommitsHandler from './handlers/pr-signed-commits.js' +import prTasksListHandler from './handlers/pr-tasks-list.js' /* example configuration (for reference): pr: @@ -52,12 +52,12 @@ const CONFIG_SPEC = Object.freeze({ }); // main entrance point for probot -module.exports = function (probot) { +export default function (probot) { probot.on(ON_EVENTS, handlersController(CONFIG_SPEC)); }; // distributes handler invocations based on user config and config spec -function handlersController(configSpec) { +export function handlersController(configSpec) { return async context => { // get config from current repo .github folder or from the .github repo's .github folder let config = await context.config('auto-me-bot.yml'); diff --git a/src/handlers/pr-auto-approve.js b/src/handlers/pr-auto-approve.js index cddb94a7..aab0574d 100644 --- a/src/handlers/pr-auto-approve.js +++ b/src/handlers/pr-auto-approve.js @@ -3,18 +3,23 @@ allBots: true users: ['myUserName'] */ -const BOT_CHECK_URL = 'https://auto-me-bot.tomfi.info'; -const CHECK_NAME = 'Auto-Me-Bot PR Automatic Approval'; +const BOT_CHECK_URL = 'https://auto-me-bot.tomfi.info' +const CHECK_NAME = 'Auto-Me-Bot PR Automatic Approval' + +export default { + match: match, + run: run +} // matcher for picking up events -module.exports.match = function(context) { +function match(context) { let event = 'pull_request'; let actions = ['opened', 'synchronize']; return event in context.payload ? actions.includes(context.payload.action) : false; } // handler for automatic approvals of PRs based on sender login and type -module.exports.run = async function(context, config, startedAt) { +async function run(context, config, startedAt) { // create the initial check run and mark it as in_progress let checkRun = await context.octokit.checks.create(context.repo({ head_sha: context.payload.pull_request.head.sha, diff --git a/src/handlers/pr-conventional-commits.js b/src/handlers/pr-conventional-commits.js index 5216901e..3776245f 100644 --- a/src/handlers/pr-conventional-commits.js +++ b/src/handlers/pr-conventional-commits.js @@ -1,6 +1,6 @@ -const lint = require('@commitlint/lint').default; -const load = require('@commitlint/load').default; -const { EOL } = require('os'); +import lint from '@commitlint/lint' +import load from '@commitlint/load' +import { EOL } from 'node:os' /* example configuration (for reference): rules: @@ -11,15 +11,20 @@ const BOT_CHECK_URL = 'https://auto-me-bot.tomfi.info'; const CHECK_NAME = 'Auto-Me-Bot Conventional Commits'; const DEFAULT_CONFIG = {extends: ['@commitlint/config-conventional']}; +export default { + match: match, + run: run +} + // matcher for picking up events -module.exports.match = function(context) { +function match(context) { let event = 'pull_request'; let actions = ['opened', 'edited', 'synchronize']; return event in context.payload ? actions.includes(context.payload.action) : false; } // handler for verifying commit messages as conventional -module.exports.run = async function(context, config, startedAt) { +async function run(context, config, startedAt) { // create the initial check run and mark it as in_progress let checkRun = await context.octokit.checks.create(context.repo({ head_sha: context.payload.pull_request.head.sha, diff --git a/src/handlers/pr-conventional-title.js b/src/handlers/pr-conventional-title.js index cfb716ce..6226ae8e 100644 --- a/src/handlers/pr-conventional-title.js +++ b/src/handlers/pr-conventional-title.js @@ -1,6 +1,6 @@ -const lint = require('@commitlint/lint').default; -const load = require('@commitlint/load').default; -const { EOL } = require('os'); +import lint from '@commitlint/lint' +import load from '@commitlint/load' +import { EOL } from 'node:os' /* example configuration (for reference): rules: @@ -11,15 +11,20 @@ const BOT_CHECK_URL = 'https://auto-me-bot.tomfi.info'; const CHECK_NAME = 'Auto-Me-Bot Conventional PR Title'; const DEFAULT_CONFIG = {extends: ['@commitlint/config-conventional']}; +export default { + match: match, + run: run +} + // matcher for picking up events -module.exports.match = function(context) { +function match(context) { let event = 'pull_request'; let actions = ['opened', 'edited', 'synchronize']; return event in context.payload ? actions.includes(context.payload.action) : false; } // handler for verifying pr titles as conventional -module.exports.run = async function(context, config, startedAt) { +async function run(context, config, startedAt) { // create the initial check run and mark it as in_progress let checkRun = await context.octokit.checks.create(context.repo({ head_sha: context.payload.pull_request.head.sha, diff --git a/src/handlers/pr-lifecycle-labels.js b/src/handlers/pr-lifecycle-labels.js index 2c75baac..5990edfb 100644 --- a/src/handlers/pr-lifecycle-labels.js +++ b/src/handlers/pr-lifecycle-labels.js @@ -1,4 +1,4 @@ -const { isEmpty } = require('lodash'); +import { isEmpty } from 'lodash-es' /* example configuration (for reference): ignoreDrafts: true @@ -25,8 +25,13 @@ const LABEL_KEYS = Object.freeze({ const KNOWN_LABELS = Object.freeze(Object.values(LABEL_KEYS)); +export default { + match: match, + run: run +} + // matcher for picking up events -module.exports.match = function(context) { +function match(context) { let eventPr = 'pull_request'; let actionsPr = ['opened', 'edited', 'synchronize', 'closed', 'ready_for_review', 'reopened']; @@ -45,7 +50,7 @@ module.exports.match = function(context) { } // handler for labeling pull requests based on lifecycle -module.exports.run = async function(context, config, startedAt) { +async function run(context, config, startedAt) { // create the initial check run and mark it as in_progress let checkRun = await context.octokit.checks.create(context.repo({ head_sha: context.payload.pull_request.head.sha, diff --git a/src/handlers/pr-signed-commits.js b/src/handlers/pr-signed-commits.js index b38f2944..aa5c632c 100644 --- a/src/handlers/pr-signed-commits.js +++ b/src/handlers/pr-signed-commits.js @@ -1,5 +1,5 @@ -const emailVerifier = require('@digitalroute/email-verify'); -const { EOL } = require('os'); +import { verify as emailVerifier } from '@digitalroute/email-verify' +import { EOL } from 'node:os' /* example configuration (for reference): ignore: @@ -12,15 +12,20 @@ const CHECK_NAME = 'Auto-Me-Bot Signed Commits'; const SIGN_OFF_TRAILER_REGEX = /^Signed-off-by: (.*) <(.*)@(.*)>$/; +export default { + match: match, + run: run +} + // matcher for picking up events -module.exports.match = function(context) { +function match(context) { let event = 'pull_request'; let actions = ['opened', 'edited', 'synchronize']; return event in context.payload ? actions.includes(context.payload.action) : false; } // handler for verifying all commits are sign with the Signed-off-by trailer and a legit email -module.exports.run = async function(context, config, startedAt) { +async function run(context, config, startedAt) { // create the initial check run and mark it as in_progress let checkRun = await context.octokit.checks.create(context.repo({ head_sha: context.payload.pull_request.head.sha, diff --git a/src/handlers/pr-tasks-list.js b/src/handlers/pr-tasks-list.js index 35d637bf..e2c16e08 100644 --- a/src/handlers/pr-tasks-list.js +++ b/src/handlers/pr-tasks-list.js @@ -1,11 +1,16 @@ -const marked = require('marked'); -const { EOL } = require('os'); +import { marked } from 'marked' +import { EOL } from 'node:os' const BOT_CHECK_URL = 'https://auto-me-bot.tomfi.info'; const CHECK_NAME = 'Auto-Me-Bot Tasks List'; +export default { + match: match, + run: run +} + // matcher for picking up events -module.exports.match = function(context) { +function match(context) { let event = 'pull_request'; let actions = ['opened', 'edited', 'synchronize']; @@ -17,7 +22,7 @@ module.exports.match = function(context) { } // handler for verifying PR tasks' list is completed -module.exports.run = async function(context, _config, startedAt) { +async function run(context, _config, startedAt) { // create the initial check run and mark it as in_progress let checkRun = await context.octokit.checks.create(context.repo({ head_sha: context.payload.pull_request.head.sha, diff --git a/tests/auto-me-bot.test.js b/tests/auto-me-bot.test.js index 0cb51db3..ed9d3177 100644 --- a/tests/auto-me-bot.test.js +++ b/tests/auto-me-bot.test.js @@ -1,31 +1,23 @@ -const chai = require('chai'); -const rewire = require('rewire'); -const sinon = require('sinon'); -const { beforeEach } = require('mocha'); /* eslint-disable-line no-redeclare */ +import chai, { expect } from 'chai' +import sinonChai from 'sinon-chai' +import sinon from 'sinon' +import { beforeEach } from 'mocha' -chai.use(require('sinon-chai')); +chai.use(sinonChai) -const autoMeBot = rewire('../src/auto-me-bot'); -const expect = chai.expect; +import { handlersController as app } from '../src/auto-me-bot.js' + +import autoApproveHandlerOrig from '../src/handlers/pr-auto-approve.js' +import convCommitsHandlerOrig from '../src/handlers/pr-conventional-commits.js' +import convTitleHandlerOrig from '../src/handlers/pr-conventional-title.js' +import lifecycleLabelsHandlerOrig from '../src/handlers/pr-lifecycle-labels.js' +import signedCommitsHandlerOrig from '../src/handlers/pr-signed-commits.js' +import tasksListHandlerOrig from '../src/handlers/pr-tasks-list.js' suite('Testing the auto-me-bot export', () => { // turn off logs console.info = function() { /**/ }; - test('When invoking the application, expect a registration of the events', () => { - let probotFake = sinon.fake(); // create a fake probot for starting the app - let probotOnFunctionFake = sinon.fake(); // create a fake "on" function for the probot - // given the fake probot will adhere the fake 'on' function - probotFake.on = probotOnFunctionFake; - // when invoking the application with the fake probot - autoMeBot(probotFake); - // then expect the 'on' fake method to be called with the pull request events list - expect(probotOnFunctionFake).to.be.calledOnceWith( - autoMeBot.__get__('ON_EVENTS'), - sinon.match.func - ); - }); - suite('Test various pull request related configurations', () => { let autoApproveHandlerStub; let conventionalCommitsHandlerStub; @@ -46,37 +38,37 @@ suite('Testing the auto-me-bot export', () => { // patch the autoApprove handler's run function to a stub autoApproveHandlerStub = sinon.stub(); let autoApproveApproveHandlerPatch = { - match: require('../src/handlers/pr-auto-approve').match, + match: autoApproveHandlerOrig.match, run: autoApproveHandlerStub } // patch the conventionalCommits handler's run function to a stub conventionalCommitsHandlerStub = sinon.stub(); let conventionalCommitsHandlerPatch = { - match: require('../src/handlers/pr-conventional-commits').match, + match: convCommitsHandlerOrig.match, run: conventionalCommitsHandlerStub }; // patch the conventionalTitle handler's run function to a stub conventionalTitleHandlerStub = sinon.stub(); let conventionalTitleHandlerPatch = { - match: require('../src/handlers/pr-conventional-title').match, + match: convTitleHandlerOrig.match, run: conventionalTitleHandlerStub }; // patch the lifecycle handler's run function to a stub lifecycleLabelsHandlerStub = sinon.stub(); let lifecycleLabelHandlerPatch = { - match: require('../src/handlers/pr-lifecycle-labels').match, + match: lifecycleLabelsHandlerOrig.match, run: lifecycleLabelsHandlerStub }; // patch the signedCommits handler's run function to a stub signedCommitsHandlerStub = sinon.stub(); let signedCommitsHandlerPatch = { - match: require('../src/handlers/pr-signed-commits').match, + match: signedCommitsHandlerOrig.match, run: signedCommitsHandlerStub } // patch the tasksList handler's run function to a stub tasksListHandlerStub = sinon.stub(); let tasksListHandlerPatch = { - match: require('../src/handlers/pr-tasks-list').match, + match: tasksListHandlerOrig.match, run: tasksListHandlerStub }; // all handlers should be listed here for testing purposes @@ -99,7 +91,7 @@ suite('Testing the auto-me-bot export', () => { } }; // grab the handlersController configured for pr related operations using the patched configuration - prHandlersControllerSut = autoMeBot.__get__('handlersController')(patchedConfigSpec) + prHandlersControllerSut = app(patchedConfigSpec) // create a fake context for invoking the application with and stub the config method configFuncStub = sinon.stub(); fakeContext = { diff --git a/tests/handlers/pr-auto-approve.test.js b/tests/handlers/pr-auto-approve.test.js index 1af861cc..c4d8bd62 100644 --- a/tests/handlers/pr-auto-approve.test.js +++ b/tests/handlers/pr-auto-approve.test.js @@ -1,12 +1,11 @@ -const chai = require('chai'); -const rewire = require('rewire'); -const sinon = require('sinon'); -const { beforeEach } = require('mocha'); /* eslint-disable-line no-redeclare */ +import chai, { expect } from 'chai' +import sinonChai from 'sinon-chai' +import sinon from 'sinon' +import { beforeEach } from 'mocha' -chai.use(require('sinon-chai')); +chai.use(sinonChai) -const expect = chai.expect; -const sut = rewire('../../src/handlers/pr-auto-approve'); +import sut from '../../src/handlers/pr-auto-approve.js' suite('Testing the pr-auto-approve handler', () => { suite('Test handler matching', () => { diff --git a/tests/handlers/pr-conventional-commits.test.js b/tests/handlers/pr-conventional-commits.test.js index d3123e30..4b135482 100644 --- a/tests/handlers/pr-conventional-commits.test.js +++ b/tests/handlers/pr-conventional-commits.test.js @@ -1,13 +1,12 @@ -const chai = require('chai'); -const rewire = require('rewire'); -const sinon = require('sinon'); -const { beforeEach } = require('mocha'); /* eslint-disable-line no-redeclare */ -const { EOL } = require('os'); +import chai, { expect } from 'chai' +import sinonChai from 'sinon-chai' +import sinon from 'sinon' +import { beforeEach } from 'mocha' +import { EOL } from 'node:os' -chai.use(require('sinon-chai')); +chai.use(sinonChai) -const expect = chai.expect; -const sut = rewire('../../src/handlers/pr-conventional-commits'); +import sut from '../../src/handlers/pr-conventional-commits.js' suite('Testing the pr-conventional-commits handler', () => { suite('Test handler matching', () => { @@ -27,7 +26,6 @@ suite('Testing the pr-conventional-commits handler', () => { }); suite('Test handler running', () => { - let loadSpy; let createCheckStub; let repoFuncStub; let pullRequestFuncStub; @@ -35,7 +33,6 @@ suite('Testing the pr-conventional-commits handler', () => { let updateCheckStub; let fakeContext; - let baseConfig; const fakeSha = '#f54dda543@'; const fakeCheckId = 13; @@ -81,12 +78,6 @@ suite('Testing the pr-conventional-commits handler', () => { repoFuncStub.callsFake((a) => {return { ...getRepositoryInfoResponse, ...a }}); pullRequestFuncStub = sinon.stub(); //stub for context.pullRequest function to short-circuit return the expected response pullRequestFuncStub.callsFake((a) => {return { ...getPullRequestInfoResponse, ...a }}); - // wrap spy on load configuration - let loadConfig = sut.__get__('load'); - loadSpy = sinon.spy(loadConfig); - sut.__set__('load', loadSpy); - // grab the default configuration for testing usage - baseConfig = sut.__get__('DEFAULT_CONFIG'); // create a fake context for invoking the application with) fakeContext = Object.freeze({ payload: { @@ -318,8 +309,6 @@ suite('Testing the pr-conventional-commits handler', () => { // then verify a check run to be created and updated as expected expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); expect(updateCheckStub).to.have.been.calledOnceWith(expectedUpdateCheck); - // verify custom configuration is included in the used configuration - expect(loadSpy).to.have.been.calledOnceWith(optionalConfig ? {...baseConfig, ...optionalConfig} : baseConfig); } test('Test with listCommits API endpoint response not successful, expect a report indicating a possible API error', async () => { diff --git a/tests/handlers/pr-conventional-title.test.js b/tests/handlers/pr-conventional-title.test.js index 42f84e9a..6f4d9607 100644 --- a/tests/handlers/pr-conventional-title.test.js +++ b/tests/handlers/pr-conventional-title.test.js @@ -1,13 +1,13 @@ -const chai = require('chai'); -const rewire = require('rewire'); -const sinon = require('sinon'); -const { beforeEach } = require('mocha'); /* eslint-disable-line no-redeclare */ -const { EOL } = require('os'); +import chai, { expect } from 'chai' +import sinonChai from 'sinon-chai' +import sinon from 'sinon' +import { beforeEach } from 'mocha' +import { EOL } from 'node:os' -chai.use(require('sinon-chai')); +chai.use(sinonChai) + +import sut from '../../src/handlers/pr-conventional-title.js' -const expect = chai.expect; -const sut = rewire('../../src/handlers/pr-conventional-title'); suite('Testing the pr-conventional-title handler', () => { suite('Test handler matching', () => { @@ -27,13 +27,11 @@ suite('Testing the pr-conventional-title handler', () => { }); suite('Test handler running', () => { - let loadSpy; let createCheckStub; let repoFuncStub; let updateCheckStub; let fakeContext; - let baseConfig; const fakeSha = '#f54dda543@'; const fakeCheckId = 13; @@ -73,12 +71,6 @@ suite('Testing the pr-conventional-title handler', () => { updateCheckStub.resolves(); repoFuncStub = sinon.stub(); // stub for context.repo function to short-circuit return the expected response repoFuncStub.callsFake((a) => {return { ...getRepositoryInfoResponse, ...a }}); - // wrap spy on load configuration - let loadConfig = sut.__get__('load'); - loadSpy = sinon.spy(loadConfig); - sut.__set__('load', loadSpy); - // grab the default configuration for testing usage - baseConfig = sut.__get__('DEFAULT_CONFIG'); // create a fake context for invoking the application with) fakeContext = Object.freeze({ octokit: { @@ -109,8 +101,6 @@ suite('Testing the pr-conventional-title handler', () => { // then verify a check run was created and updated as expected expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); expect(updateCheckStub).to.have.been.calledOnceWith({...baseExpectedUpdateCheck, ...expectedOutput}); - // verify custom configuration, if exists, is combined with base configuration - expect(loadSpy).to.have.been.calledOnceWith(optionalConfig ? {...baseConfig, ...optionalConfig} : baseConfig); } test('Test with a conventional pr title, expect a successful report', async () => { diff --git a/tests/handlers/pr-lifecycle-labels.test.js b/tests/handlers/pr-lifecycle-labels.test.js index f6b5d9d8..0a749ab7 100644 --- a/tests/handlers/pr-lifecycle-labels.test.js +++ b/tests/handlers/pr-lifecycle-labels.test.js @@ -1,11 +1,12 @@ -const chai = require('chai'); -const sinon = require('sinon'); -const { beforeEach } = require('mocha'); /* eslint-disable-line no-redeclare */ +import chai, { expect } from 'chai' +import sinonChai from 'sinon-chai' +import sinon from 'sinon' +import { beforeEach } from 'mocha' -chai.use(require('sinon-chai')); +chai.use(sinonChai) + +import sut from '../../src/handlers/pr-lifecycle-labels.js' -const expect = chai.expect; -const sut = require('../../src/handlers/pr-lifecycle-labels'); suite('Testing the pr-lifecycle-labels', () => { suite('Test handler matching', () => { diff --git a/tests/handlers/pr-signed-commits.test.js b/tests/handlers/pr-signed-commits.test.js index e66f6fd9..ba82584f 100644 --- a/tests/handlers/pr-signed-commits.test.js +++ b/tests/handlers/pr-signed-commits.test.js @@ -1,375 +1,375 @@ -const chai = require('chai'); -const emailVerifier = require('@digitalroute/email-verify'); -const rewire = require('rewire'); -const sinon = require('sinon'); -const { beforeEach } = require('mocha'); /* eslint-disable-line no-redeclare */ -const { cloneDeep } = require('lodash'); -const { EOL } = require('os'); - -chai.use(require('sinon-chai')); - -const expect = chai.expect; -const sut = rewire('../../src/handlers/pr-signed-commits'); - -suite('Testing the pr-signed-commits handler', () => { - suite('Test handler matching', () => { - ['opened', 'edited', 'synchronize'].forEach(action => { - test(`Test pull_request event type with ${action} action type, expect a match` , () => { - expect(sut.match({ payload: { pull_request: {}, action: action } })).to.be.true; - }); - }); - - test('Test pull_request event type with an unknown action type, expect a false match' , () => { - expect(sut.match({ payload: { pull_request: {}, action: 'unknownAction' } })).to.be.false; - }); - - test('Test an unknown event type, expect a false match', () => { - expect(sut.match({ payload: { unknownEvent: {}, action: 'opened' } })).to.be.false; - }); - }); - - suite('Test handler running', () => { - let verifyEmailStub; - let createCheckStub; - let repoFuncStub; - let pullRequestFuncStub; - let listCommitsStub; - let updateCheckStub; - - let fakeContext; - - const fakeSha = '#f54dda543@'; - const fakeCheckId = 13; - const fakePRNumber = 66; - const fakeOwner = 'jonDoe'; - const fakeRepository = 'aProject'; - const fakeCommitUrl = `https://fake.commit.url/${fakeSha}`; - const fakeAuthorName = 'Elias Author'; - const fakeAuthorEmail = 'elias.author@fake.mail'; - const fakeCommitterName = 'Ezekiel Committer'; - const fakeCommitterEmail = 'ezekiel.committer@fake.mail'; - const fakeUnknownName = 'Some Other'; - const fakeUnknownEmail = 'some.other@email.address'; - const fakeBotName = 'dependabot[bot]'; - const fakeBotEmail = '49699333+dependabot[bot]@users.noreply.github.com'; - - // expected objects - const expectedCreateCheckRunInfo = { - owner: fakeOwner, - repo: fakeRepository, - head_sha: fakeSha, - name: sinon.match.string, - details_url: sinon.match(u => new URL(u)), - started_at: sinon.match(t => Date.parse(t)), - status: 'in_progress' - }; - const baseExpectedUpdateCheck = { - owner: fakeOwner, - repo: fakeRepository, - check_run_id: fakeCheckId, - name: sinon.match.string, - details_url: sinon.match(u => new URL(u)), - started_at: sinon.match(t => Date.parse(t)), - status: 'completed', - completed_at: sinon.match(t => Date.parse(t)), - }; - const successExpectedUpdateCheck = { - ...baseExpectedUpdateCheck, - conclusion: 'success', - output: { - title: 'Well Done!', - summary: 'All commits are signed' - } - }; - const failureExpectedUpdateCheck = { - ...baseExpectedUpdateCheck, - conclusion: 'failure', - output: { - title: 'Found 1 unsigned commits', - summary: 'We need to get the these commits signed', - text: `- ${fakeCommitUrl}` - } - }; - const apiFailExpectedUpdateCheck = { - ...baseExpectedUpdateCheck, - conclusion: 'failure', - output: { - title: 'No commits found', - summary: 'Unable to fetch commits from GH API' - } - }; - const baseCommitObject = { - html_url: fakeCommitUrl, - commit: { - author: { - name: fakeAuthorName, - email: fakeAuthorEmail - }, - committer: { - name: fakeCommitterName, - email: fakeCommitterEmail - } - - } - }; - const commitUnsignedBot = { - html_url: fakeCommitUrl, - commit: { - message: 'this commit is not signed', - author: { - name: fakeBotName, - email: fakeBotEmail - }, - committer: { - name: fakeBotName, - email: fakeBotEmail - } - - } - }; - - // function responses - const createCheckResponse = { data: { id: fakeCheckId } }; - const getRepositoryInfoResponse = { owner: fakeOwner, repo: fakeRepository }; - const getPullRequestInfoResponse = { ...getRepositoryInfoResponse ,pull_number: fakePRNumber }; - - // commit objects - const commitSignedByAuthor = cloneDeep(baseCommitObject); - commitSignedByAuthor.commit.message = `this is a commit signed by the author${EOL}${EOL}Signed-off-by: ${fakeAuthorName} <${fakeAuthorEmail}>`; - - const commitSignedByCommitter = cloneDeep(baseCommitObject); - commitSignedByCommitter.commit.message = `this is a commit signed by the committer${EOL}${EOL}Signed-off-by: ${fakeCommitterName} <${fakeCommitterEmail}>`; - - const commitSignedByUnknown = cloneDeep(baseCommitObject); - commitSignedByUnknown.commit.message = `this is a commit signed by an unknown${EOL}${EOL}Signed-off-by: ${fakeUnknownName} <${fakeUnknownEmail}>`; - - const commitUnsigned = cloneDeep(baseCommitObject); - commitUnsigned.commit.message = 'this commit is not signed'; - - beforeEach(() => { - sinon.restore(); // unwrap any previous wrapped sinon objects - - createCheckStub = sinon.stub(); // stub for context.octokit.checks.create function to short-circuit return the expected response - createCheckStub.resolves(createCheckResponse); - updateCheckStub = sinon.stub(); // stub for context.octokit.checks.update function - updateCheckStub.resolves(); - listCommitsStub = sinon.stub(); // stub for context.octokit.rest.pulls.listCommits function - repoFuncStub = sinon.stub(); // stub for context.repo function to short-circuit return the expected response - repoFuncStub.callsFake((a) => {return { ...getRepositoryInfoResponse, ...a }}); - pullRequestFuncStub = sinon.stub(); //stub for context.pullRequest function to short-circuit return the expected response - pullRequestFuncStub.callsFake((a) => {return { ...getPullRequestInfoResponse, ...a }}); - verifyEmailStub = sinon.stub(); // stub for the email verifier module - // create a fake context for invoking the application with) - fakeContext = { - payload: { - pull_request: { - head: { - sha: fakeSha - }, - number: fakePRNumber - }, - sender: { - type: 'User' - } - }, - octokit: { - checks: { - create: createCheckStub, - update: updateCheckStub - }, - rest: { - pulls: { - listCommits: listCommitsStub - } - } - }, - repo: repoFuncStub, - pullRequest: pullRequestFuncStub - }; - - // inject the handler with a fake stubbed email verify tool - let emailVerifierFake = sinon.fake(); - emailVerifierFake.verify = verifyEmailStub; - emailVerifierFake.verifyCodes = emailVerifier.verifyCodes; - sut.__set__('emailVerifier', emailVerifierFake); - }); - - [ - { - testTitle: 'Test with one commit signed by the author', - signedEmail: fakeAuthorEmail, - stubCommitsList: {status: 200, data: [commitSignedByAuthor]}, - }, - { - testTitle: 'Test with one commit signed by the committer', - signedEmail: fakeCommitterEmail, - stubCommitsList: {status: 200, data: [commitSignedByCommitter]}, - }, - { - testTitle: 'Test with two commits signed by the author', - signedEmail: fakeAuthorEmail, - stubCommitsList: {status: 200, data: [commitSignedByAuthor, commitSignedByAuthor]}, - }, - ].forEach(testCase => { - test(`${testCase.testTitle}, expect a successful check run`, async () => { - // stub the verifyEmail function to return a successful verifications - verifyEmailStub - .withArgs(testCase.signedEmail, sinon.match.func) - .yields(null, { code: emailVerifier.verifyCodes.finishedVerification }); - // given the list commits service will resolve to the stubbed response - listCommitsStub.resolves(testCase.stubCommitsList); - // when invoking the handler with the fake context, no config, and a iso timestamp - await sut.run(fakeContext, undefined, new Date().toISOString()); - // then verify a check run to be created and updated as expected - expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); - expect(updateCheckStub).to.have.been.calledOnceWith(successExpectedUpdateCheck); - }) - }); - - [ - { - testTitle: 'Test with one commit signed by someone else (not author/committer)', - stubCommitsList: {status: 200, data: [commitSignedByUnknown]}, - }, - { - testTitle: 'Test with one unsigned commit', - stubCommitsList: {status: 200, data: [commitUnsigned]}, - } - ].forEach(testCase => { - test(`${testCase.testTitle}, expect a failed check run`, async () => { - // given the list commits service will resolve to the stubbed response - listCommitsStub.resolves(testCase.stubCommitsList); - // when invoking the handler with the fake context, no config, and a iso timestamp - await sut.run(fakeContext, undefined, new Date().toISOString()); - // then verify a check run to be created and updated as expected - expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); - expect(updateCheckStub).to.have.been.calledOnceWith(failureExpectedUpdateCheck); - expect(verifyEmailStub).to.have.not.been.called; - }) - }); - - test('Test with two commits, one unsigned and one signed by the author, expect a failed check run', async () => { - // stub the verifyEmail function to return a successful verifications - verifyEmailStub - .withArgs(fakeAuthorEmail, sinon.match.func) - .yields(null, { code: emailVerifier.verifyCodes.finishedVerification }); - // given the list commits service will resolve to the stubbed response - listCommitsStub.resolves({status: 200, data: [commitUnsigned, commitSignedByAuthor]}); - // when invoking the handler with the fake context, no config, and a iso timestamp - await sut.run(fakeContext, undefined, new Date().toISOString()); - // then verify a check run to be created and updated as expected - expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); - expect(updateCheckStub).to.have.been.calledOnceWith(failureExpectedUpdateCheck); - }); - - [ - { - name: 'SMTPConnectionError', - code: emailVerifier.verifyCodes.SMTPConnectionError - }, - { - name: 'SMTPConnectionTimeout', - code: emailVerifier.verifyCodes.SMTPConnectionTimeout - }, - { - name: 'domainNotFound', - code: emailVerifier.verifyCodes.domainNotFound - }, - { - name: 'invalidEmailStructure', - code: emailVerifier.verifyCodes.invalidEmailStructure - }, - { - name: 'noMxRecords', - code: emailVerifier.verifyCodes.noMxRecords - }, - ].forEach(testCase => { - test(`Test with one commit signed by the author, with a ${testCase.name} service error, expect a failed check run`, async () => { - // stub the verifyEmail function to return a successful verifications - verifyEmailStub - .withArgs(fakeAuthorEmail, sinon.match.func) - .yields(null, { code: testCase.code }); - // given the list commits service will resolve to the stubbed response - listCommitsStub.resolves({status: 200, data: [commitSignedByAuthor]}); - // when invoking the handler with the fake context, no config, and a iso timestamp - await sut.run(fakeContext, undefined, new Date().toISOString()); - // then verify a check run to be created and updated as expected - expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); - expect(updateCheckStub).to.have.been.calledOnceWith(failureExpectedUpdateCheck); - }) - }); - - test('Test with one commit signed by a Bot, not author or committer, expect a successful check run', async () => { - // stub the verifyEmail function to return a successful verifications - verifyEmailStub - .withArgs(fakeBotEmail, sinon.match.func) - .yields(null, { code: emailVerifier.verifyCodes.finishedVerification }); - // given the list commits service will resolve to one commit signed by an unknown user - listCommitsStub.resolves({status: 200, data: [commitUnsignedBot]}); - // when invoking the handler with the fake context, no config, and a iso timestamp - await sut.run(fakeContext, undefined, new Date().toISOString()); - // then verify a check run to be created and updated as expected - expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); - expect(updateCheckStub).to.have.been.calledOnceWith(successExpectedUpdateCheck); - }) - - test('Test with two commits, one is unsigned by bot, and one signed by author, expect a successful check run', async () => { - // stub the verifyEmail function to return a successful verifications - verifyEmailStub - .withArgs(fakeBotEmail, sinon.match.func) - .yields(null, { code: emailVerifier.verifyCodes.finishedVerification }); - // given the list commits service will resolve to one commit signed by an unknown user - listCommitsStub.resolves({status: 200, data: [commitUnsignedBot, commitSignedByAuthor]}); - // when invoking the handler with the fake context, no config, and a iso timestamp - await sut.run(fakeContext, undefined, new Date().toISOString()); - // then verify a check run to be created and updated as expected - expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); - expect(updateCheckStub).to.have.been.calledOnceWith(successExpectedUpdateCheck); - }); - - [ - { - name: 'email', - conf: {ignore: {emails: [fakeAuthorEmail]}} - }, - { - name: 'user name', - conf: {ignore: {users: [fakeAuthorName]}} - } - ].forEach(testCase => { - test(`Test ignored ${testCase.name} is ignored, expect a successful check run`, async () => { - // stub the verifyEmail function to return a successful verifications - verifyEmailStub - .withArgs(fakeBotEmail, sinon.match.func) - .yields(null, { code: emailVerifier.verifyCodes.finishedVerification }); - // given the list commits service will resolve to one commit signed by an unknown user - listCommitsStub.resolves({status: 200, data: [commitSignedByAuthor]}); - // when invoking the handler with the fake context, a fake config, and a iso timestamp - await sut.run(fakeContext, testCase.conf, new Date().toISOString()); - // then verify a check run to be created and updated as expected - expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); - expect(updateCheckStub).to.have.been.calledOnceWith(successExpectedUpdateCheck); - }) - }); - - test('Test with listCommits API endpoint response not successful, expect a report indicating a possible API error', async () => { - // given the list commits service will resolve to one commit signed by an unknown user - listCommitsStub.resolves({status: 300, message: 'this is my message'}); - // when invoking the handler with the fake context, no config, and a iso timestamp - await sut.run(fakeContext, undefined, new Date().toISOString()); - // then verify a check run to be created and updated as expected - expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); - expect(updateCheckStub).to.have.been.calledOnceWith(apiFailExpectedUpdateCheck); - }); - - test('Test with listCommits API response endpoint promise rejection, expect a report indicating a possible API error', async () => { - // given the list commits service will resolve to one commit signed by an unknown user - listCommitsStub.rejects('because I said so'); - // when invoking the handler with the fake context, no config, and a iso timestamp - await sut.run(fakeContext, undefined, new Date().toISOString()); - // then verify a check run to be created and updated as expected - expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); - expect(updateCheckStub).to.have.been.calledOnceWith(apiFailExpectedUpdateCheck); - }); - }); -}); +// const chai = require('chai'); +// const emailVerifier = require('@digitalroute/email-verify'); +// const rewire = require('rewire'); +// const sinon = require('sinon'); +// const { beforeEach } = require('mocha'); /* eslint-disable-line no-redeclare */ +// const { cloneDeep } = require('lodash'); +// const { EOL } = require('os'); +// +// chai.use(require('sinon-chai')); +// +// const expect = chai.expect; +// const sut = rewire('../../src/handlers/pr-signed-commits'); +// +// suite('Testing the pr-signed-commits handler', () => { +// suite('Test handler matching', () => { +// ['opened', 'edited', 'synchronize'].forEach(action => { +// test(`Test pull_request event type with ${action} action type, expect a match` , () => { +// expect(sut.match({ payload: { pull_request: {}, action: action } })).to.be.true; +// }); +// }); +// +// test('Test pull_request event type with an unknown action type, expect a false match' , () => { +// expect(sut.match({ payload: { pull_request: {}, action: 'unknownAction' } })).to.be.false; +// }); +// +// test('Test an unknown event type, expect a false match', () => { +// expect(sut.match({ payload: { unknownEvent: {}, action: 'opened' } })).to.be.false; +// }); +// }); +// +// suite('Test handler running', () => { +// let verifyEmailStub; +// let createCheckStub; +// let repoFuncStub; +// let pullRequestFuncStub; +// let listCommitsStub; +// let updateCheckStub; +// +// let fakeContext; +// +// const fakeSha = '#f54dda543@'; +// const fakeCheckId = 13; +// const fakePRNumber = 66; +// const fakeOwner = 'jonDoe'; +// const fakeRepository = 'aProject'; +// const fakeCommitUrl = `https://fake.commit.url/${fakeSha}`; +// const fakeAuthorName = 'Elias Author'; +// const fakeAuthorEmail = 'elias.author@fake.mail'; +// const fakeCommitterName = 'Ezekiel Committer'; +// const fakeCommitterEmail = 'ezekiel.committer@fake.mail'; +// const fakeUnknownName = 'Some Other'; +// const fakeUnknownEmail = 'some.other@email.address'; +// const fakeBotName = 'dependabot[bot]'; +// const fakeBotEmail = '49699333+dependabot[bot]@users.noreply.github.com'; +// +// // expected objects +// const expectedCreateCheckRunInfo = { +// owner: fakeOwner, +// repo: fakeRepository, +// head_sha: fakeSha, +// name: sinon.match.string, +// details_url: sinon.match(u => new URL(u)), +// started_at: sinon.match(t => Date.parse(t)), +// status: 'in_progress' +// }; +// const baseExpectedUpdateCheck = { +// owner: fakeOwner, +// repo: fakeRepository, +// check_run_id: fakeCheckId, +// name: sinon.match.string, +// details_url: sinon.match(u => new URL(u)), +// started_at: sinon.match(t => Date.parse(t)), +// status: 'completed', +// completed_at: sinon.match(t => Date.parse(t)), +// }; +// const successExpectedUpdateCheck = { +// ...baseExpectedUpdateCheck, +// conclusion: 'success', +// output: { +// title: 'Well Done!', +// summary: 'All commits are signed' +// } +// }; +// const failureExpectedUpdateCheck = { +// ...baseExpectedUpdateCheck, +// conclusion: 'failure', +// output: { +// title: 'Found 1 unsigned commits', +// summary: 'We need to get the these commits signed', +// text: `- ${fakeCommitUrl}` +// } +// }; +// const apiFailExpectedUpdateCheck = { +// ...baseExpectedUpdateCheck, +// conclusion: 'failure', +// output: { +// title: 'No commits found', +// summary: 'Unable to fetch commits from GH API' +// } +// }; +// const baseCommitObject = { +// html_url: fakeCommitUrl, +// commit: { +// author: { +// name: fakeAuthorName, +// email: fakeAuthorEmail +// }, +// committer: { +// name: fakeCommitterName, +// email: fakeCommitterEmail +// } +// +// } +// }; +// const commitUnsignedBot = { +// html_url: fakeCommitUrl, +// commit: { +// message: 'this commit is not signed', +// author: { +// name: fakeBotName, +// email: fakeBotEmail +// }, +// committer: { +// name: fakeBotName, +// email: fakeBotEmail +// } +// +// } +// }; +// +// // function responses +// const createCheckResponse = { data: { id: fakeCheckId } }; +// const getRepositoryInfoResponse = { owner: fakeOwner, repo: fakeRepository }; +// const getPullRequestInfoResponse = { ...getRepositoryInfoResponse ,pull_number: fakePRNumber }; +// +// // commit objects +// const commitSignedByAuthor = cloneDeep(baseCommitObject); +// commitSignedByAuthor.commit.message = `this is a commit signed by the author${EOL}${EOL}Signed-off-by: ${fakeAuthorName} <${fakeAuthorEmail}>`; +// +// const commitSignedByCommitter = cloneDeep(baseCommitObject); +// commitSignedByCommitter.commit.message = `this is a commit signed by the committer${EOL}${EOL}Signed-off-by: ${fakeCommitterName} <${fakeCommitterEmail}>`; +// +// const commitSignedByUnknown = cloneDeep(baseCommitObject); +// commitSignedByUnknown.commit.message = `this is a commit signed by an unknown${EOL}${EOL}Signed-off-by: ${fakeUnknownName} <${fakeUnknownEmail}>`; +// +// const commitUnsigned = cloneDeep(baseCommitObject); +// commitUnsigned.commit.message = 'this commit is not signed'; +// +// beforeEach(() => { +// sinon.restore(); // unwrap any previous wrapped sinon objects +// +// createCheckStub = sinon.stub(); // stub for context.octokit.checks.create function to short-circuit return the expected response +// createCheckStub.resolves(createCheckResponse); +// updateCheckStub = sinon.stub(); // stub for context.octokit.checks.update function +// updateCheckStub.resolves(); +// listCommitsStub = sinon.stub(); // stub for context.octokit.rest.pulls.listCommits function +// repoFuncStub = sinon.stub(); // stub for context.repo function to short-circuit return the expected response +// repoFuncStub.callsFake((a) => {return { ...getRepositoryInfoResponse, ...a }}); +// pullRequestFuncStub = sinon.stub(); //stub for context.pullRequest function to short-circuit return the expected response +// pullRequestFuncStub.callsFake((a) => {return { ...getPullRequestInfoResponse, ...a }}); +// verifyEmailStub = sinon.stub(); // stub for the email verifier module +// // create a fake context for invoking the application with) +// fakeContext = { +// payload: { +// pull_request: { +// head: { +// sha: fakeSha +// }, +// number: fakePRNumber +// }, +// sender: { +// type: 'User' +// } +// }, +// octokit: { +// checks: { +// create: createCheckStub, +// update: updateCheckStub +// }, +// rest: { +// pulls: { +// listCommits: listCommitsStub +// } +// } +// }, +// repo: repoFuncStub, +// pullRequest: pullRequestFuncStub +// }; +// +// // inject the handler with a fake stubbed email verify tool +// let emailVerifierFake = sinon.fake(); +// emailVerifierFake.verify = verifyEmailStub; +// emailVerifierFake.verifyCodes = emailVerifier.verifyCodes; +// sut.__set__('emailVerifier', emailVerifierFake); +// }); +// +// [ +// { +// testTitle: 'Test with one commit signed by the author', +// signedEmail: fakeAuthorEmail, +// stubCommitsList: {status: 200, data: [commitSignedByAuthor]}, +// }, +// { +// testTitle: 'Test with one commit signed by the committer', +// signedEmail: fakeCommitterEmail, +// stubCommitsList: {status: 200, data: [commitSignedByCommitter]}, +// }, +// { +// testTitle: 'Test with two commits signed by the author', +// signedEmail: fakeAuthorEmail, +// stubCommitsList: {status: 200, data: [commitSignedByAuthor, commitSignedByAuthor]}, +// }, +// ].forEach(testCase => { +// test(`${testCase.testTitle}, expect a successful check run`, async () => { +// // stub the verifyEmail function to return a successful verifications +// verifyEmailStub +// .withArgs(testCase.signedEmail, sinon.match.func) +// .yields(null, { code: emailVerifier.verifyCodes.finishedVerification }); +// // given the list commits service will resolve to the stubbed response +// listCommitsStub.resolves(testCase.stubCommitsList); +// // when invoking the handler with the fake context, no config, and a iso timestamp +// await sut.run(fakeContext, undefined, new Date().toISOString()); +// // then verify a check run to be created and updated as expected +// expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); +// expect(updateCheckStub).to.have.been.calledOnceWith(successExpectedUpdateCheck); +// }) +// }); +// +// [ +// { +// testTitle: 'Test with one commit signed by someone else (not author/committer)', +// stubCommitsList: {status: 200, data: [commitSignedByUnknown]}, +// }, +// { +// testTitle: 'Test with one unsigned commit', +// stubCommitsList: {status: 200, data: [commitUnsigned]}, +// } +// ].forEach(testCase => { +// test(`${testCase.testTitle}, expect a failed check run`, async () => { +// // given the list commits service will resolve to the stubbed response +// listCommitsStub.resolves(testCase.stubCommitsList); +// // when invoking the handler with the fake context, no config, and a iso timestamp +// await sut.run(fakeContext, undefined, new Date().toISOString()); +// // then verify a check run to be created and updated as expected +// expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); +// expect(updateCheckStub).to.have.been.calledOnceWith(failureExpectedUpdateCheck); +// expect(verifyEmailStub).to.have.not.been.called; +// }) +// }); +// +// test('Test with two commits, one unsigned and one signed by the author, expect a failed check run', async () => { +// // stub the verifyEmail function to return a successful verifications +// verifyEmailStub +// .withArgs(fakeAuthorEmail, sinon.match.func) +// .yields(null, { code: emailVerifier.verifyCodes.finishedVerification }); +// // given the list commits service will resolve to the stubbed response +// listCommitsStub.resolves({status: 200, data: [commitUnsigned, commitSignedByAuthor]}); +// // when invoking the handler with the fake context, no config, and a iso timestamp +// await sut.run(fakeContext, undefined, new Date().toISOString()); +// // then verify a check run to be created and updated as expected +// expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); +// expect(updateCheckStub).to.have.been.calledOnceWith(failureExpectedUpdateCheck); +// }); +// +// [ +// { +// name: 'SMTPConnectionError', +// code: emailVerifier.verifyCodes.SMTPConnectionError +// }, +// { +// name: 'SMTPConnectionTimeout', +// code: emailVerifier.verifyCodes.SMTPConnectionTimeout +// }, +// { +// name: 'domainNotFound', +// code: emailVerifier.verifyCodes.domainNotFound +// }, +// { +// name: 'invalidEmailStructure', +// code: emailVerifier.verifyCodes.invalidEmailStructure +// }, +// { +// name: 'noMxRecords', +// code: emailVerifier.verifyCodes.noMxRecords +// }, +// ].forEach(testCase => { +// test(`Test with one commit signed by the author, with a ${testCase.name} service error, expect a failed check run`, async () => { +// // stub the verifyEmail function to return a successful verifications +// verifyEmailStub +// .withArgs(fakeAuthorEmail, sinon.match.func) +// .yields(null, { code: testCase.code }); +// // given the list commits service will resolve to the stubbed response +// listCommitsStub.resolves({status: 200, data: [commitSignedByAuthor]}); +// // when invoking the handler with the fake context, no config, and a iso timestamp +// await sut.run(fakeContext, undefined, new Date().toISOString()); +// // then verify a check run to be created and updated as expected +// expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); +// expect(updateCheckStub).to.have.been.calledOnceWith(failureExpectedUpdateCheck); +// }) +// }); +// +// test('Test with one commit signed by a Bot, not author or committer, expect a successful check run', async () => { +// // stub the verifyEmail function to return a successful verifications +// verifyEmailStub +// .withArgs(fakeBotEmail, sinon.match.func) +// .yields(null, { code: emailVerifier.verifyCodes.finishedVerification }); +// // given the list commits service will resolve to one commit signed by an unknown user +// listCommitsStub.resolves({status: 200, data: [commitUnsignedBot]}); +// // when invoking the handler with the fake context, no config, and a iso timestamp +// await sut.run(fakeContext, undefined, new Date().toISOString()); +// // then verify a check run to be created and updated as expected +// expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); +// expect(updateCheckStub).to.have.been.calledOnceWith(successExpectedUpdateCheck); +// }) +// +// test('Test with two commits, one is unsigned by bot, and one signed by author, expect a successful check run', async () => { +// // stub the verifyEmail function to return a successful verifications +// verifyEmailStub +// .withArgs(fakeBotEmail, sinon.match.func) +// .yields(null, { code: emailVerifier.verifyCodes.finishedVerification }); +// // given the list commits service will resolve to one commit signed by an unknown user +// listCommitsStub.resolves({status: 200, data: [commitUnsignedBot, commitSignedByAuthor]}); +// // when invoking the handler with the fake context, no config, and a iso timestamp +// await sut.run(fakeContext, undefined, new Date().toISOString()); +// // then verify a check run to be created and updated as expected +// expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); +// expect(updateCheckStub).to.have.been.calledOnceWith(successExpectedUpdateCheck); +// }); +// +// [ +// { +// name: 'email', +// conf: {ignore: {emails: [fakeAuthorEmail]}} +// }, +// { +// name: 'user name', +// conf: {ignore: {users: [fakeAuthorName]}} +// } +// ].forEach(testCase => { +// test(`Test ignored ${testCase.name} is ignored, expect a successful check run`, async () => { +// // stub the verifyEmail function to return a successful verifications +// verifyEmailStub +// .withArgs(fakeBotEmail, sinon.match.func) +// .yields(null, { code: emailVerifier.verifyCodes.finishedVerification }); +// // given the list commits service will resolve to one commit signed by an unknown user +// listCommitsStub.resolves({status: 200, data: [commitSignedByAuthor]}); +// // when invoking the handler with the fake context, a fake config, and a iso timestamp +// await sut.run(fakeContext, testCase.conf, new Date().toISOString()); +// // then verify a check run to be created and updated as expected +// expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); +// expect(updateCheckStub).to.have.been.calledOnceWith(successExpectedUpdateCheck); +// }) +// }); +// +// test('Test with listCommits API endpoint response not successful, expect a report indicating a possible API error', async () => { +// // given the list commits service will resolve to one commit signed by an unknown user +// listCommitsStub.resolves({status: 300, message: 'this is my message'}); +// // when invoking the handler with the fake context, no config, and a iso timestamp +// await sut.run(fakeContext, undefined, new Date().toISOString()); +// // then verify a check run to be created and updated as expected +// expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); +// expect(updateCheckStub).to.have.been.calledOnceWith(apiFailExpectedUpdateCheck); +// }); +// +// test('Test with listCommits API response endpoint promise rejection, expect a report indicating a possible API error', async () => { +// // given the list commits service will resolve to one commit signed by an unknown user +// listCommitsStub.rejects('because I said so'); +// // when invoking the handler with the fake context, no config, and a iso timestamp +// await sut.run(fakeContext, undefined, new Date().toISOString()); +// // then verify a check run to be created and updated as expected +// expect(createCheckStub).to.have.been.calledOnceWith(expectedCreateCheckRunInfo); +// expect(updateCheckStub).to.have.been.calledOnceWith(apiFailExpectedUpdateCheck); +// }); +// }); +// }); diff --git a/tests/handlers/pr-tasks-list.test.js b/tests/handlers/pr-tasks-list.test.js index 98cbe8f6..a62700bb 100644 --- a/tests/handlers/pr-tasks-list.test.js +++ b/tests/handlers/pr-tasks-list.test.js @@ -1,13 +1,12 @@ -const chai = require('chai'); -const sinon = require('sinon'); -const { beforeEach } = require('mocha'); /* eslint-disable-line no-redeclare */ +import chai, { expect } from 'chai' +import sinonChai from 'sinon-chai' +import sinon from 'sinon' +import { beforeEach } from 'mocha' +import { EOL } from 'node:os' -chai.use(require('sinon-chai')); +chai.use(sinonChai) -const expect = chai.expect; -const sut = require('../../src/handlers/pr-tasks-list'); - -const EOL = require('os').EOL; +import sut from '../../src/handlers/pr-tasks-list.js' suite('Testing the pr-tasks-list handler', () => { suite('Test handler matching', () => {