From ad6feb254e350aeea236c2c9c52190bab4506468 Mon Sep 17 00:00:00 2001 From: Javier Bullrich Date: Mon, 4 Sep 2023 13:43:12 -0300 Subject: [PATCH] added more cases --- src/test/index.test.ts | 120 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 7 deletions(-) diff --git a/src/test/index.test.ts b/src/test/index.test.ts index 222550b..4c4ce17 100644 --- a/src/test/index.test.ts +++ b/src/test/index.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ import { PullRequest, PullRequestReview } from "@octokit/webhooks-types"; -import { readFileSync } from "fs"; +import { existsSync, openSync, readFileSync, unlinkSync } from "fs"; import { DeepMockProxy, Matcher, mock, mockDeep, MockProxy } from "jest-mock-extended"; import { join } from "path"; @@ -8,7 +8,24 @@ import { GitHubChecksApi } from "../github/check"; import { PullRequestApi } from "../github/pullRequest"; import { GitHubTeamsApi, TeamApi } from "../github/teams"; import { ActionLogger, GitHubClient } from "../github/types"; -import { ActionRunner } from "../runner"; +import { ActionRunner, RuleReport } from "../runner"; + +type ReportName = + | "CI files" + | "Core developers" + | "Runtime files cumulus" + | "Bridges subtree files" + | "FRAME coders substrate"; + +/** Utility method to get a particular report from a list */ +const getReport = (reports: RuleReport[], name: ReportName): RuleReport => { + for (const report of reports) { + if (report.name === name) { + return report; + } + } + throw new Error(`Report ${name} not found. Available reports are: ${reports.map((r) => r.name).join(". ")}`); +}; describe("Integration testing", () => { const file = join(__dirname, "./", "config.yml"); @@ -19,6 +36,7 @@ describe("Integration testing", () => { ["release-engineering", ["re-1", "re-2", "re-3"]], ["core-devs", ["gavofyork", "bkchr", "core-1", "core-2"]], ["locks-review", ["gavofyork", "bkchr", "lock-1"]], + ["polkadot-review", ["gavofyork", "bkchr", "pr-1", "pr-2"]], ["bridges-core", ["bridge-1", "bridge-2", "bridge-3"]], ["frame-coders", ["frame-1", "frame-2", "frame-3"]], ]; @@ -41,11 +59,27 @@ describe("Integration testing", () => { id: Math.floor(Math.random() * 1000), }) as PullRequestReview; - const mockReviews = (reviews: PullRequestReview[]) => { + const mockReviews = (reviews: (Pick & { login: string })[]) => { + const getHash = (input: string): number => { + let hash = 0; + const len = input.length; + for (let i = 0; i < len; i++) { + hash = (hash << 5) - hash + input.charCodeAt(i); + hash |= 0; // to 32bit integer + } + return Math.abs(hash); + }; + + const data = reviews.map(({ state, id, login }) => { + return { state, id, user: { login, id: getHash(login) } }; + }); + // @ts-ignore because the official type and the library type do not match - client.rest.pulls.listReviews.mockResolvedValue({ data: reviews }); + client.rest.pulls.listReviews.mockResolvedValue({data}); }; + const summaryTestFile = "./summary-test.html"; + beforeEach(() => { logger = mock(); client = mockDeep(); @@ -73,6 +107,24 @@ describe("Integration testing", () => { }), }); } + + // @ts-ignore missing more of the required types + client.rest.checks.listForRef.mockResolvedValue({ data: { check_runs: [], total_count: 0 } }); + client.rest.checks.create.mockResolvedValue({ + // @ts-ignore missing types + data: { html_url: "demo", title: "title", output: { text: "output" } }, + }); + + // Create file to upload the summary text (else it will fail) + process.env.GITHUB_STEP_SUMMARY = summaryTestFile; + openSync(summaryTestFile, "w"); + }); + + afterEach(() => { + // delete the summary test file + if (existsSync(summaryTestFile)) { + unlinkSync(summaryTestFile); + } }); describe("Error in config", () => { @@ -124,9 +176,63 @@ describe("Integration testing", () => { ); }); }); - test("should not report problems on empty files", async () => { + + describe("Core developers", () => { + test("should request reviews ", async () => { + // @ts-ignore + client.rest.pulls.listFiles.mockResolvedValue({ data: [{ filename: "README.md" }] }); + const result = await runner.runAction({ configLocation: "abc" }); + expect(result.reports).toHaveLength(1); + expect(result.conclusion).toBe("failure"); + const report = getReport(result.reports, "Core developers"); + expect(report.missingReviews).toBe(2); + }); + + test("should request only one review if author is member", async () => { + // @ts-ignore + client.rest.pulls.listFiles.mockResolvedValue({ data: [{ filename: "README.md" }] }); + pr.user.login = "gavofyork"; + const result = await runner.runAction({ configLocation: "abc" }); + expect(result.reports).toHaveLength(1); + expect(result.conclusion).toBe("failure"); + const report = getReport(result.reports, "Core developers"); + console.log(report); + expect(report.missingReviews).toBe(1); + }); + + test("should approve PR if it has enough approvals", async () => { + // @ts-ignore + client.rest.pulls.listFiles.mockResolvedValue({ data: [{ filename: "README.md" }] }); + mockReviews([ + { login: "core-1", state: "approved", id: 12 }, + { login: "core-2", state: "approved", id: 123 }, + ]); + const result = await runner.runAction({ configLocation: "abc" }); + expect(result.reports).toHaveLength(0); + expect(result.conclusion).toBe("success"); + }); + }); + + test("should request a runtime upgrade review if the file is from runtime upgrades", async () => { + // @ts-ignore + client.rest.pulls.listFiles.mockResolvedValue({ data: [{ filename: "cumulus/parachains/common/src/example.rs" }] }); + + const result = await runner.runAction({ configLocation: "abc" }); + expect(result.reports).toHaveLength(1); + expect(result.conclusion).toBe("failure"); + const report = getReport(result.reports, "Runtime files cumulus"); + expect(report.missingReviews).toBe(2); + }); + + test("should request only one runtime upgrade review if the file is from runtime upgrades and the author belongs to one of the teams", async () => { // @ts-ignore - client.rest.pulls.listFiles.mockResolvedValue({ data: [{ filename: "README.md" }] }); - await runner.runAction({ configLocation: "abc" }); + client.rest.pulls.listFiles.mockResolvedValue({ data: [{ filename: "cumulus/parachains/common/src/example.rs" }] }); + pr.user.login = "gavofyork"; + const result = await runner.runAction({ configLocation: "abc" }); + expect(result.reports).toHaveLength(1); + expect(result.conclusion).toBe("failure"); + const report = getReport(result.reports, "Runtime files cumulus"); + console.log(report); + expect(report.missingReviews).toBe(2); }); });