From 475c340c954ad6621acae5a97468e63aab293ecc Mon Sep 17 00:00:00 2001 From: Catherine Lee Date: Mon, 24 Feb 2025 15:16:48 -0800 Subject: [PATCH 1/2] tc --- torchci/lib/bot/utils.ts | 8 +++- torchci/test/utils.test.ts | 93 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 torchci/test/utils.test.ts diff --git a/torchci/lib/bot/utils.ts b/torchci/lib/bot/utils.ts index 76795405c8..6ce9eaeded 100644 --- a/torchci/lib/bot/utils.ts +++ b/torchci/lib/bot/utils.ts @@ -270,7 +270,13 @@ export async function hasApprovedPullRuns( if (pr_runs == null || pr_runs?.length == 0) { return false; } - return pr_runs.every((run) => run.conclusion != "action_required"); + return !( + pr_runs.some((run) => run.conclusion === "action_required") || + // Everything being a start up failure could be hiding a need for approval + pr_runs.every( + (run) => run.conclusion === "failure" && run.created_at == run.updated_at + ) + ); } export async function isFirstTimeContributor( diff --git a/torchci/test/utils.test.ts b/torchci/test/utils.test.ts new file mode 100644 index 0000000000..66968eafe5 --- /dev/null +++ b/torchci/test/utils.test.ts @@ -0,0 +1,93 @@ +import { hasApprovedPullRuns } from "lib/bot/utils"; +import nock from "nock"; +import { Probot } from "probot"; +import triggerInductorTestsBot from "../lib/bot/triggerInductorTestsBot"; +import * as utils from "./utils"; + +nock.disableNetConnect(); + +describe("utils: hasApprovedPullRuns", () => { + let probot: Probot; + let octokit = utils.testOctokit(); + let REPO = "pytorch/pytorch"; + let SHA = "random sha"; + + beforeEach(() => { + probot = utils.testProbot(); + probot.load(triggerInductorTestsBot); + }); + + function mockRuns( + runs: { conclusion: string; created_at?: string; updated_at?: string }[] + ) { + return nock("https://api.github.com") + .get(`/repos/${REPO}/actions/runs?head_sha=${SHA}`) + .reply(200, { + workflow_runs: runs.map((run) => ({ + event: "pull_request", + ...run, + })), + }); + } + + async function checkhasApprovedPullRunsReturns(value: boolean) { + expect(await hasApprovedPullRuns(octokit, "pytorch", "pytorch", SHA)).toBe( + value + ); + } + + afterEach(() => { + nock.cleanAll(); + jest.restoreAllMocks(); + }); + + test("successful runs = good", async () => { + mockRuns([{ conclusion: "success" }, { conclusion: "success" }]); + await checkhasApprovedPullRunsReturns(true); + }); + + test("at least 1 action required run = bad", async () => { + mockRuns([{ conclusion: "action_required" }, { conclusion: "success" }]); + await checkhasApprovedPullRunsReturns(false); + }); + + test("no runs = bad", async () => { + mockRuns([]); + await checkhasApprovedPullRunsReturns(false); + }); + + test("one startup failure = bad", async () => { + mockRuns([ + { + conclusion: "failure", + created_at: "time", + updated_at: "time", + }, + ]); + await checkhasApprovedPullRunsReturns(false); + }); + + test("one startup failure and one action required = bad", async () => { + mockRuns([ + { + conclusion: "failure", + created_at: "time", + updated_at: "time", + }, + { conclusion: "action_required" }, + ]); + await checkhasApprovedPullRunsReturns(false); + }); + + test("one startup failure and one success = good", async () => { + mockRuns([ + { + conclusion: "failure", + created_at: "time", + updated_at: "time", + }, + { conclusion: "success" }, + ]); + await checkhasApprovedPullRunsReturns(true); + }); +}); From 4f64e1dc0325f86e66d0c9deaddd0b4b2078e009 Mon Sep 17 00:00:00 2001 From: Catherine Lee Date: Mon, 24 Feb 2025 15:21:28 -0800 Subject: [PATCH 2/2] tc --- torchci/lib/bot/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/torchci/lib/bot/utils.ts b/torchci/lib/bot/utils.ts index 6ce9eaeded..ed20364f61 100644 --- a/torchci/lib/bot/utils.ts +++ b/torchci/lib/bot/utils.ts @@ -272,7 +272,8 @@ export async function hasApprovedPullRuns( } return !( pr_runs.some((run) => run.conclusion === "action_required") || - // Everything being a start up failure could be hiding a need for approval + // Everything being a start up failure could hide the need for workflow + // approval pr_runs.every( (run) => run.conclusion === "failure" && run.created_at == run.updated_at )