From 79beb09da6ef145dc907a392d9fb037e0389e8ea Mon Sep 17 00:00:00 2001 From: Rob Anderson Date: Mon, 18 Dec 2023 17:39:17 -0500 Subject: [PATCH] add optional authorized team configuration (#88) --- README.md | 10 ++ app.js | 112 ++++++++++++++- template.yml | 4 + test.js | 393 +++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 500 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index acf6581..7e007f8 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,16 @@ You will need to decide the label that this app looks for, the contents of the i - `ISSUE_BODY_FILE` This is the file containing the body of the issue created - `ISSUE_ASSIGNEES` This is a comma separated list of the issue assignees +Optionally, you can define a team name in the `AUTHORIZED_TEAM` variable. The app will consider members of this team authorized to use this app. The app will add a comment on the PR if an user who is not a member of this team attemps to do any of the following: +- Opens a PR with the `TRIGGER_STRING` in the body +- Adds a PR comment with the `TRIGGER_STRING` in the body +- Adds the `EMERGENCY_LABEL` to a PR + +The comment will read: +``` +@username is not authorized to apply the emergency label. +``` + To make the emergency label permanent set `EMERGENCY_LABEL_PERMANENT` to true. Doing this will cause the app to reapply the emergency label if it is removed. To trigger the label (and therefore everything configured) set `TRIGGER_STRING` to the value you want the app to look for in PRs and PR comments. diff --git a/app.js b/app.js index 9d9bdfe..947f750 100644 --- a/app.js +++ b/app.js @@ -12,7 +12,10 @@ const emergencyLabel = process.env.EMERGENCY_LABEL || 'emergency'; module.exports = (app) => { //console.log("Yay! The app was loaded!"); app.on("pull_request.labeled", async (context) => { - if (context.payload.label.name == emergencyLabel && context.payload.pull_request.merged == false) { + let authorized = await isAuthorized(context.payload.sender.login, context.payload.organization.login, context.octokit) + if (context.payload.label.name == emergencyLabel + && context.payload.pull_request.merged == false + && authorized) { // emergency label exists and pull request is not merged, so do stuff... console.log(`${emergencyLabel} label detected`); @@ -122,11 +125,25 @@ module.exports = (app) => { } else { return true; } - } + } else if (context.payload.label.name == emergencyLabel + && context.payload.pull_request.merged == false + && ! authorized) { + await postUnauthorizedIssueComment(context) + // remove emergency label + await context.octokit.rest.issues.removeLabel({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + issue_number: context.payload.pull_request.number, + name: emergencyLabel + }) + } }); app.on("pull_request.unlabeled", async (context) => { - if (context.payload.label.name == emergencyLabel && process.env.EMERGENCY_LABEL_PERMANENT == 'true') { + if (context.payload.label.name == emergencyLabel + && process.env.EMERGENCY_LABEL_PERMANENT == 'true' + && ! context.payload.sender.login.endsWith("[bot]")) { + // emergencyLabel was removed and it should be permanent, so do stuff... console.log(`Reaplying ${emergencyLabel} label to PR: ${context.payload.pull_request.html_url}`); @@ -156,7 +173,10 @@ module.exports = (app) => { }); app.on("issues.unlabeled", async (context) => { - if (context.payload.label.name == emergencyLabel && process.env.EMERGENCY_LABEL_PERMANENT == 'true') { + if (context.payload.label.name == emergencyLabel + && process.env.EMERGENCY_LABEL_PERMANENT == 'true' + && ! context.payload.sender.login.endsWith("[bot]")) { + // emergencyLabel was removed and it should be permanent, so do stuff... console.log(`Reaplying ${emergencyLabel} label to PR: ${context.payload.issue.html_url}`); @@ -186,7 +206,10 @@ module.exports = (app) => { }); app.on("pull_request.opened", async (context) => { - if (context.payload.pull_request.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING)) { + let authorized = await isAuthorized(context.payload.sender.login, context.payload.organization.login, context.octokit) + if (context.payload.pull_request.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING) + && authorized) { + // Found the trigger string, so add the emergency label to trigger the other stuff... let errorsArray = []; await context.octokit.rest.issues.addLabels({ @@ -209,11 +232,18 @@ module.exports = (app) => { } else { return true; } + } else if (context.payload.pull_request.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING) + && ! authorized){ + await postUnauthorizedIssueComment(context) } }); app.on("issue_comment.created", async (context) => { - if (context.payload.issue.pull_request && context.payload.comment.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING)) { + let authorized = await isAuthorized(context.payload.sender.login, context.payload.organization.login, context.octokit) + if (context.payload.issue.pull_request + && context.payload.comment.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING) + && authorized) { + // This is a comment on a PR and we found the trigger string, so add the emergency label to trigger the other stuff... let errorsArray = []; await context.octokit.rest.issues.addLabels({ @@ -236,6 +266,74 @@ module.exports = (app) => { } else { return true; } - } + } else if (context.payload.issue.pull_request + && context.payload.comment.body.toLocaleLowerCase().includes(process.env.TRIGGER_STRING) + && ! authorized) { + await postUnauthorizedIssueComment(context) + } }); }; + +async function isAuthorized(login, org, octokit) { + // check if process.env.AUTHORIZED_TEAM is defined + if (process.env.AUTHORIZED_TEAM == undefined || process.env.AUTHORIZED_TEAM == "") { + console.log("No authorized team specified. Skipping authorization check.") + return true; + } + // if login ends with [bot] then it's a bot and we don't need to check + if (login.endsWith("[bot]")) { + console.log("Bot detected. Skipping authorization check.") + return true; + } + console.log(`Checking if ${login} is a member of ${org}/${process.env.AUTHORIZED_TEAM} team`) + try { + let membership = await octokit.request(`GET /orgs/${org}/teams/${process.env.AUTHORIZED_TEAM}/memberships/${login}`, { + org: org, + team_slug: process.env.AUTHORIZED_TEAM, + username: login + }) + + if (membership.data.state == 'active') { + console.log( "Membership active") + return true; + } else { + console.log( "Membership not active") + return false; + + } + } catch (error) { + if (error.status == 404) { + console.log("Membership not found") + return false; + } else { + console.log(`error: ${error}`); + console.log("Error checking membership. Check the ADMIN_OPS_ORG and ACTIONS_APPROVER_TEAM variables.") + throw new Error("Error checking membership"); + } + } +} + +async function postUnauthorizedIssueComment(context) { + // Comment on github issue that user is not authorized to apply the emergency label + let errorsArray = []; + let number = context.payload.issue ? context.payload.issue.number : context.payload.pull_request.number + let url = context.payload.issue ? context.payload.issue.html_url : context.payload.pull_request.html_url + await context.octokit.rest.issues.createComment({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + issue_number: number, + body: `@${context.payload.sender.login} is not authorized to apply the emergency label.` + }).then(response => { + console.log(`Commented on issue: ${url}`); + }).catch(error => { + console.log(`Error commenting on issue: ${error} to PR: ${url}`); + errorsArray.push(error); + }); + + if (errorsArray.length > 0) { + console.log(`Errors: ${errorsArray}`); + throw errorsArray; + } else { + return true; + } +} \ No newline at end of file diff --git a/template.yml b/template.yml index 3f5a19e..26e10c1 100644 --- a/template.yml +++ b/template.yml @@ -75,6 +75,9 @@ Parameters: Description: The string value in a PR or Issue title that triggers appling the emegency label Default: emergency landing Type: String + authorizedTeam: + Description: The team who's members are authorized to use this app + Type: String Resources: webhooks: @@ -108,6 +111,7 @@ Resources: SLACK_MESSAGE_FILE: !Ref slackMessageFile EMERGENCY_LABEL_PERMANENT: !Ref emergencyLabelPermanent TRIGGER_STRING: !Ref triggerString + AUTHORIZED_TEAM: !Ref authorizedTeam Outputs: WebhooksUrl: diff --git a/test.js b/test.js index b520d7c..eb9c4c5 100644 --- a/test.js +++ b/test.js @@ -9,8 +9,8 @@ const { ProbotOctokit, } = require("@probot/adapter-aws-lambda-serverless"); -const payload = { - loadName: "payload", +const payloadPrLabeled = { + loadName: "payloadPrLabeled", name: "pull_request", id: "1", payload: { @@ -31,6 +31,43 @@ const payload = { html_url: "https://github.com/robandpdx/superbigmono/pull/1", merged: false }, + organization: { + login: "robandpdx", + }, + sender: { + login: "robandpdx", + } + }, +} + +const payloadPrLabeledByBot = { + loadName: "payloadPrLabeled", + name: "pull_request", + id: "1", + payload: { + action: "labeled", + label: { + name: "emergency" + }, + repository: { + owner: { + login: "robandpdx", + }, + name: "superbigmono", + url: "https://api.github.com/repos/robandpdx/superbigmono" + }, + pull_request: { + number: 1, + url: "https://api.github.com/repos/robandpdx/superbigmono/pulls/1", + html_url: "https://github.com/robandpdx/superbigmono/pull/1", + merged: false + }, + organization: { + login: "robandpdx", + }, + sender: { + login: "emergency-pr[bot]", + } }, } @@ -54,6 +91,35 @@ const payloadUnlabeled = { }, name: "superbigmono" }, + sender: { + login: "robandpdx", + } + }, +} + +const payloadUnlabeledByBot = { + loadName: "payloadUnlabeledByBoy", + name: "pull_request", + id: "1", + payload: { + action: "unlabeled", + label: { + name: "emergency" + }, + pull_request: { + number: 1, + issue_url: "https://api.github.com/repos/robandpdx/superbigmono/issues/1", + html_url: "https://github.com/robandpdx/superbigmono/pull/1", + }, + repository: { + owner: { + login: "robandpdx", + }, + name: "superbigmono" + }, + sender: { + login: "emergency-pr[bot]", + } }, } @@ -77,6 +143,35 @@ const payloadIssueUnlabeled = { }, name: "superbigmono" }, + sender: { + login: "robandpdx", + } + }, +} + +const payloadIssueUnlabeledByBot = { + loadName: "payloadIssueUnlabeled", + name: "issues", + id: "1", + payload: { + action: "unlabeled", + label: { + name: "emergency" + }, + issue: { + url: "https://api.github.com/repos/robandpdx/superbigmono/issues/1", + html_url: "https://github.com/robandpdx/superbigmono/pull/1", + number: 1, + }, + repository: { + owner: { + login: "robandpdx", + }, + name: "superbigmono" + }, + sender: { + login: "emergency-pr[bot]", + } }, } @@ -98,6 +193,12 @@ const payloadPrOpened = { }, name: "superbigmono" }, + organization: { + login: "robandpdx", + }, + sender: { + login: "robandpdx", + } } } @@ -126,9 +227,27 @@ const payloadPrComment = { }, name: "superbigmono" }, + organization: { + login: "robandpdx", + }, + sender: { + login: "robandpdx", + } } } +const payloadMembershipResponse = { + "url": "https://api.github.com/teams/1/memberships/robandpdx", + "role": "maintainer", + "state": "active" +} + +const payloadNonMembershipResponse = { + "url": "https://api.github.com/teams/1/memberships/robandpdx", + "role": "member", + "state": "inactive" +} + const app = require("./app"); /** @type {import('probot').Probot */ @@ -165,6 +284,7 @@ test.after.each(() => { delete process.env.SLACK_NOTIFY; delete process.env.SLACK_MESSAGE_FILE; delete process.env.EMERGENCY_LABEL_PERMANENT; + delete process.env.AUTHORIZED_TEAM; }); // This test sends a payload that is not an emergency label @@ -177,6 +297,12 @@ test("recieves pull_request.labeled event, does nothing because not emergency la label: { name: "other" }, + organization: { + login: 'robandpdx' + }, + sender: { + login: 'robandpdx' + } }, }); }); @@ -239,7 +365,7 @@ test("recieves pull_request.labeled event, approve, create issue, merge, slack n } }); - await probot.receive(payload); + await probot.receive(payloadPrLabeled); assert.equal(mock.pendingMocks(), []); assert.equal(mockSlack.pendingMocks(), []); }); @@ -263,7 +389,7 @@ test("recieves pull_request.labeled event, approve PR", async function () { ) .reply(200); - await probot.receive(payload); + await probot.receive(payloadPrLabeled); assert.equal(mock.pendingMocks(), []); }); @@ -284,7 +410,7 @@ test("recieves pull_request.labeled event, create issue", async function () { ) .reply(200); - await probot.receive(payload); + await probot.receive(payloadPrLabeled); assert.equal(mock.pendingMocks(), []); }); @@ -306,7 +432,7 @@ test("recieves pull_request.labeled event, create issue no assignees", async fun ) .reply(200); - await probot.receive(payload); + await probot.receive(payloadPrLabeled); assert.equal(mock.pendingMocks(), []); }); @@ -321,7 +447,107 @@ test("recieves pull_request.labeled event, merge the PR", async function () { const mock = nock("https://api.github.com") .put("/repos/robandpdx/superbigmono/pulls/1/merge").reply(200); - await probot.receive(payload); + await probot.receive(payloadPrLabeled); + assert.equal(mock.pendingMocks(), []); +}); + +// This test will merge the PR because the user is a member of the emergency team +test("recieves pull_request.labeled event, check team membership, merge the PR", async function () { + process.env.APPROVE_PR = 'false'; + process.env.CREATE_ISSUE = 'false'; + process.env.MERGE_PR = 'true'; + process.env.SLACK_NOTIFY = 'false'; + process.env.AUTHORIZED_TEAM = 'emergency-team' + + // mock the request to add approval to the pr + const mock = nock("https://api.github.com") + .put("/repos/robandpdx/superbigmono/pulls/1/merge").reply(200); + + // mock the request to check if the user is a member of the emergency team + mock.get(`/orgs/robandpdx/teams/emergency-team/memberships/robandpdx?org=robandpdx&team_slug=emergency-team&username=robandpdx`) + .reply(200, payloadMembershipResponse); + + await probot.receive(payloadPrLabeled); + assert.equal(mock.pendingMocks(), []); +}); + +// This test will merge the PR because label was applied by a bot +test("recieves pull_request.labeled event from a bot, merge the PR", async function () { + process.env.APPROVE_PR = 'false'; + process.env.CREATE_ISSUE = 'false'; + process.env.MERGE_PR = 'true'; + process.env.SLACK_NOTIFY = 'false'; + process.env.AUTHORIZED_TEAM = 'emergency-team' + + // mock the request to add approval to the pr + const mock = nock("https://api.github.com") + .put("/repos/robandpdx/superbigmono/pulls/1/merge").reply(200); + + await probot.receive(payloadPrLabeledByBot); + assert.equal(mock.pendingMocks(), []); +}); + +// This test will not merge the PR because the user is not a member of the emergency team +test("recieves pull_request.labeled event, check non team membership, do not merge the PR", async function () { + process.env.APPROVE_PR = 'false'; + process.env.CREATE_ISSUE = 'false'; + process.env.MERGE_PR = 'true'; + process.env.SLACK_NOTIFY = 'false'; + process.env.AUTHORIZED_TEAM = 'emergency-team' + + // mock the request to check if the user is a member of the emergency team + const mock = nock("https://api.github.com") + .get(`/orgs/robandpdx/teams/emergency-team/memberships/robandpdx?org=robandpdx&team_slug=emergency-team&username=robandpdx`) + .reply(200, payloadNonMembershipResponse); + + // mock the request to create the issue comment + mock.post("/repos/robandpdx/superbigmono/issues/1/comments", + (requestBody) => { + assert.equal(requestBody.body, "@robandpdx is not authorized to apply the emergency label."); + return true; + } + ).reply(200); + + // mock the request to delete the label + mock.delete("/repos/robandpdx/superbigmono/issues/1/labels/emergency", + (requestBody) => { + return true; + } + ).reply(200); + + await probot.receive(payloadPrLabeled); + assert.equal(mock.pendingMocks(), []); +}); + +// This test will not merge the PR because non team membership +test("recieves pull_request.labeled event, non team membership, do not merge the PR", async function () { + process.env.APPROVE_PR = 'false'; + process.env.CREATE_ISSUE = 'false'; + process.env.MERGE_PR = 'true'; + process.env.SLACK_NOTIFY = 'false'; + process.env.AUTHORIZED_TEAM = 'emergency-team' + + // mock the request to check if the user is a member of the emergency team + const mock = nock("https://api.github.com") + .get(`/orgs/robandpdx/teams/emergency-team/memberships/robandpdx?org=robandpdx&team_slug=emergency-team&username=robandpdx`) + .reply(404); + + // mock the request to create the issue comment + mock.post("/repos/robandpdx/superbigmono/issues/1/comments", + (requestBody) => { + assert.equal(requestBody.body, "@robandpdx is not authorized to apply the emergency label."); + return true; + } + ).reply(200); + + // mock the request to delete the label + mock.delete("/repos/robandpdx/superbigmono/issues/1/labels/emergency", + (requestBody) => { + return true; + } + ).reply(200); + + await probot.receive(payloadPrLabeled); assert.equal(mock.pendingMocks(), []); }); @@ -362,7 +588,7 @@ test("recieves pull_request.labeled event, slack notify", async function () { } }); - await probot.receive(payload); + await probot.receive(payloadPrLabeled); assert.equal(mockSlack.pendingMocks(), []); }); @@ -386,7 +612,7 @@ test("recieves pull_request.labeled event, slack notify (fail)", async function ).reply(500); try { - await probot.receive(payload); + await probot.receive(payloadPrLabeled); } catch (err) { assert.equal(mockSlack.pendingMocks(), []); assert.equal(err.errors[0][0].message, "An HTTP protocol error occurred: statusCode = 500"); @@ -423,7 +649,7 @@ test("recieves pull_request.labeled event, approve (fails), create issue, merge" mock.put("/repos/robandpdx/superbigmono/pulls/1/merge").reply(200); try { - await probot.receive(payload); + await probot.receive(payloadPrLabeled); } catch (err) { assert.equal(mock.pendingMocks(), []); assert.equal(err.errors[0][0].message, "request to https://api.github.com/repos/robandpdx/superbigmono/pulls/1/reviews failed, reason: something awful happened"); @@ -460,7 +686,7 @@ test("recieves pull_request.labeled event, approve, create issue (fails), merge" mock.put("/repos/robandpdx/superbigmono/pulls/1/merge").reply(200); try { - await probot.receive(payload); + await probot.receive(payloadPrLabeled); } catch (err) { assert.equal(mock.pendingMocks(), []); assert.equal(err.errors[0][0].message, "request to https://api.github.com/repos/robandpdx/superbigmono/issues failed, reason: something awful happened"); @@ -497,7 +723,7 @@ test("recieves pull_request.labeled event, approve, create issue, merge (fails)" mock.put("/repos/robandpdx/superbigmono/pulls/1/merge").replyWithError('something awful happened') try { - await probot.receive(payload); + await probot.receive(payloadPrLabeled); } catch (err) { assert.equal(mock.pendingMocks(), []); assert.equal(err.errors[0][0].message, "request to https://api.github.com/repos/robandpdx/superbigmono/pulls/1/merge failed, reason: something awful happened"); @@ -523,6 +749,13 @@ test("recieves pull_request.unlabeled event, reapply emergency label", async fun assert.equal(mock.pendingMocks(), []); }); +// This test will not reapply the emergency label to a PR if removed by bot +test("recieves pull_request.unlabeled event from bot user, do not reapply emergency label", async function () { + process.env.EMERGENCY_LABEL_PERMANENT = 'true'; + + await probot.receive(payloadUnlabeledByBot); +}); + // This test will fail to reapply the emergency label to a PR test("recieves pull_request.unlabeled event, fail to reapply emergency label", async function () { process.env.EMERGENCY_LABEL_PERMANENT = 'true'; @@ -571,6 +804,13 @@ test("recieves issue.unlabeled event, reapply emergency label", async function ( assert.equal(mock.pendingMocks(), []); }); +// This test will not reapply the emergency label to an issue if removed by bot +test("recieves issue.unlabeled event from bot, do not reapply emergency label", async function () { + process.env.EMERGENCY_LABEL_PERMANENT = 'true'; + + await probot.receive(payloadIssueUnlabeledByBot); +}); + // This test will fail to reapply the emergency label to an issue test("recieves issue.unlabeled event, fail to reapply emergency label", async function () { process.env.EMERGENCY_LABEL_PERMANENT = 'true'; @@ -618,6 +858,49 @@ test("recieves pull_request.opened event, applies emergency label", async functi assert.equal(mock.pendingMocks(), []); }); +// This test will apply the emergency label based on the PR contents checking team membership +test("recieves pull_request.opened event, applies emergency label checking team membership", async function () { + process.env.AUTHORIZED_TEAM = 'emergency-team' + + // mock the request to apply the emergency label + const mock = nock("https://api.github.com") + .post("/repos/robandpdx/superbigmono/issues/1/labels", + (requestBody) => { + assert.equal(requestBody[0], "emergency"); + return true; + } + ) + .reply(200); + + // mock the request to check if the user is a member of the emergency team + mock.get(`/orgs/robandpdx/teams/emergency-team/memberships/robandpdx?org=robandpdx&team_slug=emergency-team&username=robandpdx`) + .reply(200, payloadMembershipResponse); + + await probot.receive(payloadPrOpened); + assert.equal(mock.pendingMocks(), []); +}); + +// This test will not apply the emergency label due to non team membership +test("recieves pull_request.opened event, does not apply emergency label due to non team membership", async function () { + process.env.AUTHORIZED_TEAM = 'emergency-team' + + // mock the request to apply the emergency label + const mock = nock("https://api.github.com") + .get(`/orgs/robandpdx/teams/emergency-team/memberships/robandpdx?org=robandpdx&team_slug=emergency-team&username=robandpdx`) + .reply(200, payloadNonMembershipResponse); + + // mock the request to create the issue comment + mock.post("/repos/robandpdx/superbigmono/issues/1/comments", + (requestBody) => { + assert.equal(requestBody.body, "@robandpdx is not authorized to apply the emergency label."); + return true; + } + ).reply(200); + + await probot.receive(payloadPrOpened); + assert.equal(mock.pendingMocks(), []); +}); + // This test will fail to apply the emergency label based on the PR contents test("recieves pull_request.opened event, fails to apply emergency label", async function () { // mock the request to apply the emergency label @@ -662,6 +945,92 @@ test("recieves issue_comment.created event, applies emergency label", async func assert.equal(mock.pendingMocks(), []); }); +// This test will apply the emergency label based on the contents of a comment on the PR, checking team membership +test("recieves issue_comment.created event, applies emergency label", async function () { + process.env.AUTHORIZED_TEAM = 'emergency-team' + + // mock the request to apply the emergency label + const mock = nock("https://api.github.com") + .post("/repos/robandpdx/superbigmono/issues/1/labels", + (requestBody) => { + assert.equal(requestBody[0], "emergency"); + return true; + } + ) + .reply(200); + + // mock the request to check if the user is a member of the emergency team + mock.get(`/orgs/robandpdx/teams/emergency-team/memberships/robandpdx?org=robandpdx&team_slug=emergency-team&username=robandpdx`) + .reply(200, payloadMembershipResponse); + + await probot.receive(payloadPrComment); + assert.equal(mock.pendingMocks(), []); +}); + +// This test will not apply the emergency label due to non team membership and comment +test("recieves issue_comment.created event, does not apply emergency label due to non membership", async function () { + process.env.AUTHORIZED_TEAM = 'emergency-team' + + // mock the request to apply the emergency label + const mock = nock("https://api.github.com") + .get(`/orgs/robandpdx/teams/emergency-team/memberships/robandpdx?org=robandpdx&team_slug=emergency-team&username=robandpdx`) + .reply(200, payloadNonMembershipResponse); + + // mock the request to create the issue comment + mock.post("/repos/robandpdx/superbigmono/issues/1/comments", + (requestBody) => { + assert.equal(requestBody.body, "@robandpdx is not authorized to apply the emergency label."); + return true; + } + ).reply(200); + + await probot.receive(payloadPrComment); + assert.equal(mock.pendingMocks(), []); +}); + +// This test will not apply the emergency label due failed membership check +test("recieves issue_comment.created event, does not apply emergency label due to failed membership check", async function () { + process.env.AUTHORIZED_TEAM = 'emergency-team' + + // mock the request to apply the emergency label + const mock = nock("https://api.github.com") + .get(`/orgs/robandpdx/teams/emergency-team/memberships/robandpdx?org=robandpdx&team_slug=emergency-team&username=robandpdx`) + .reply(500); + + try { + await probot.receive(payloadPrComment); + } catch (err) { + assert.equal(mock.pendingMocks(), []); + assert.equal(err.errors[0].message, "Error checking membership"); + } +}); + +// This test will not apply the emergency label due to non team membership, fail to comment +test("recieves issue_comment.created event, does not applies emergency label, fail to comment", async function () { + process.env.AUTHORIZED_TEAM = 'emergency-team' + + // mock the request to apply the emergency label + const mock = nock("https://api.github.com") + .get(`/orgs/robandpdx/teams/emergency-team/memberships/robandpdx?org=robandpdx&team_slug=emergency-team&username=robandpdx`) + .reply(200, payloadNonMembershipResponse); + + // mock the request to create the issue comment + mock.post("/repos/robandpdx/superbigmono/issues/1/comments", + (requestBody) => { + assert.equal(requestBody.body, "@robandpdx is not authorized to apply the emergency label."); + return true; + } + ).reply(404); + + try { + await probot.receive(payloadPrComment); + } catch (err) { + assert.equal(mock.pendingMocks(), []); + assert.equal(err.errors.length, 1); + } + assert.equal(mock.pendingMocks(), []); +}); + // This test will fail to apply the emergency label based on the contents of a comment on the PR test("recieves issue_comment.created event, failes to apply emergency label", async function () { // mock the request to apply the emergency label