Skip to content

Commit

Permalink
Add tests (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
timja authored Jul 25, 2022
1 parent 4555d9c commit d13cf9c
Show file tree
Hide file tree
Showing 8 changed files with 7,086 additions and 859 deletions.
9 changes: 9 additions & 0 deletions app/converters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function extractUsersAndTeams(orgName, reviewers) {
const separator = reviewers.includes(",") ? "," : " ";
const split = reviewers.split(separator);

return {
teams: split.filter((reviewer) => reviewer.includes("/")),
users: split.filter((reviewer) => !reviewer.includes("/")),
};
}
47 changes: 47 additions & 0 deletions app/converters.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { extractUsersAndTeams } from "./converters.js";

describe("converters", () => {
describe("extractUsersAndTeams", () => {
test("single user", () => {
const converted = extractUsersAndTeams("test-org", "@reviewer1");

expect(converted).toEqual({
users: ["@reviewer1"],
teams: [],
});
});
test("user and team", () => {
const converted = extractUsersAndTeams(
"test-org",
"@reviewer1,@test-org/team-1"
);

expect(converted).toEqual({
users: ["@reviewer1"],
teams: ["@test-org/team-1"],
});
});
test("multiple users and teams", () => {
const converted = extractUsersAndTeams(
"test-org",
"@reviewer1,@test-org/team-1,@reviewer2,@test-org/team-2"
);

expect(converted).toEqual({
users: ["@reviewer1", "@reviewer2"],
teams: ["@test-org/team-1", "@test-org/team-2"],
});
});
test("space separated users and teams", () => {
const converted = extractUsersAndTeams(
"test-org",
"@reviewer1 @test-org/team-1 @reviewer2 @test-org/team-2"
);

expect(converted).toEqual({
users: ["@reviewer1", "@reviewer2"],
teams: ["@test-org/team-1", "@test-org/team-2"],
});
});
});
});
23 changes: 23 additions & 0 deletions app/matchers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export function transferMatcher(text) {
return text.match(/\/transfer ([a-zA-Z\d-]+)/);
}

export function closeMatcher(text) {
return text.match(/\/close (not-planned)|\/close/);
}

export function reopenMatcher(text) {
return text.match(/\/reopen/);
}

export function labelMatcher(text) {
return text.match(/\/label ([a-zA-Z\d-, ]+)/);
}

export function removeLabelMatcher(text) {
return text.match(/\/remove-label ([a-zA-Z\d-, ]+)/);
}

export function reviewerMatcher(text) {
return text.match(/\/reviewers? ([@a-z/A-Z\d-, ]+)/);
}
183 changes: 183 additions & 0 deletions app/matchers.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import {
closeMatcher,
labelMatcher,
removeLabelMatcher,
reopenMatcher,
reviewerMatcher,
transferMatcher,
} from "./matchers.js";

describe("matchers", () => {
describe("transfer", () => {
test("matches /transfer and extracts the repo name", () => {
const result = transferMatcher("/transfer github-comment-ops");

expect(result).toBeTruthy();
expect(result[1]).toEqual("github-comment-ops");
});
test("does not match input without /transfer", () => {
const result = transferMatcher("transfer github-comment-ops");

expect(result).toBeFalsy();
});
test("does not match without a repository name", () => {
const result = transferMatcher("/transfer");

expect(result).toBeFalsy();
});
});

describe("close", () => {
test("matches /close", () => {
const result = closeMatcher("/close");

expect(result).toBeTruthy();
expect(result[1]).toBeUndefined();
});
test("does not match input without /close", () => {
const result = closeMatcher("close something");

expect(result).toBeFalsy();
});
test("does not match /closenot-planned", () => {
const result = closeMatcher("/closenot-planned");

expect(result).toBeTruthy();
expect(result[1]).toBeUndefined();
});

test("matches /close not-planned", () => {
const result = closeMatcher("/close not-planned");

expect(result).toBeTruthy();
expect(result[1]).toEqual("not-planned");
});
});

describe("reopen", () => {
test("matches /reopen", () => {
const result = reopenMatcher("/reopen");

expect(result).toBeTruthy();
});
test("does not match input without /reopen", () => {
const result = reopenMatcher("reopen blah");

expect(result).toBeFalsy();
});
});

describe("label", () => {
test("matches /label label1", () => {
const result = labelMatcher("/label label1");

expect(result).toBeTruthy();
expect(result[1]).toEqual("label1");
});
test("does not match input without /label", () => {
const result = labelMatcher("label label1");

expect(result).toBeFalsy();
});
test("does not match /labellabel1", () => {
const result = labelMatcher("/labellabel1");

expect(result).toBeFalsy();
});

test("matches /label label1,label2", () => {
const result = labelMatcher("/label label1,label2");

expect(result).toBeTruthy();
expect(result[1]).toEqual("label1,label2");
});
test("matches /label label1,label2 with spaces,label3", () => {
const result = labelMatcher("/label label1,label 2 with spaces,label3");

expect(result).toBeTruthy();
expect(result[1]).toEqual("label1,label 2 with spaces,label3");
});
});

describe("remove-label", () => {
test("matches /remove-label label1", () => {
const result = removeLabelMatcher("/remove-label label1");

expect(result).toBeTruthy();
expect(result[1]).toEqual("label1");
});
test("does not match input without /remove-label", () => {
const result = removeLabelMatcher("remove-label label1");

expect(result).toBeFalsy();
});
test("does not match /remove-labellabel1", () => {
const result = removeLabelMatcher("/remove-labellabel1");

expect(result).toBeFalsy();
});

test("matches /remove-label label1,label2", () => {
const result = removeLabelMatcher("/remove-label label1,label2");

expect(result).toBeTruthy();
expect(result[1]).toEqual("label1,label2");
});
test("matches /remove-label label1,label2 with spaces,label3", () => {
const result = removeLabelMatcher(
"/remove-label label1,label 2 with spaces,label3"
);

expect(result).toBeTruthy();
expect(result[1]).toEqual("label1,label 2 with spaces,label3");
});
});

describe("reviewer", () => {
test("matches /reviewer reviewer1", () => {
const result = reviewerMatcher("/reviewer reviewer1");

expect(result).toBeTruthy();
expect(result[1]).toEqual("reviewer1");
});
test("matches /reviewers reviewer1,reviewer2", () => {
const result = reviewerMatcher("/reviewers reviewer1,reviewer2");

expect(result).toBeTruthy();
expect(result[1]).toEqual("reviewer1,reviewer2");
});
test("does not match input without /reviewer", () => {
const result = reviewerMatcher("reviewer reviewer1");

expect(result).toBeFalsy();
});
test("does not match /reviewerreviewer1", () => {
const result = reviewerMatcher("/reviewerreviewer1");

expect(result).toBeFalsy();
});

test("matches /reviewer reviewer1,reviewer2", () => {
const result = reviewerMatcher("/reviewer reviewer1,reviewer2");

expect(result).toBeTruthy();
expect(result[1]).toEqual("reviewer1,reviewer2");
});
test("matches /reviewer reviewer1,@reviewer2,@org/team", () => {
const result = reviewerMatcher(
"/reviewer reviewer1,@reviewer2,@org/team"
);

expect(result).toBeTruthy();
expect(result[1]).toEqual("reviewer1,@reviewer2,@org/team");
});
test("matches with space separator /reviewer reviewer1 @reviewer2 @org/team", () => {
const result = reviewerMatcher(
"/reviewer reviewer1 @reviewer2 @org/team"
);

expect(result).toBeTruthy();
expect(result[1]).toEqual("reviewer1 @reviewer2 @org/team");
});
});
});
127 changes: 127 additions & 0 deletions app/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import {
closeMatcher,
labelMatcher,
removeLabelMatcher,
reopenMatcher,
reviewerMatcher,
transferMatcher,
} from "./matchers.js";
import {
addLabel,
closeIssue,
removeLabel,
reopenIssue,
requestReviewers,
transferIssue,
} from "./github.js";
import { getAuthToken } from "./auth.js";
import { extractUsersAndTeams } from "./converters.js";

export async function router(auth, id, payload, verbose) {
const sourceRepo = payload.repository.name;
const transferMatches = transferMatcher(payload.comment.body);
const actorRequest = `as requested by ${payload.sender.login}`;
if (transferMatches) {
const targetRepo = transferMatches[1];
console.log(
`${id} Transferring issue ${payload.issue.html_url} to repo ${targetRepo} ${actorRequest}`
);
await transferIssue(
await getAuthToken(auth, payload.installation.id),
payload.repository.owner.login,
sourceRepo,
targetRepo,
payload.issue.node_id
);
return;
}

const closeMatches = closeMatcher(payload.comment.body);
if (closeMatches) {
const reason =
closeMatches.length > 1 && closeMatches[1] === "not-planned"
? "NOT_PLANNED"
: "COMPLETED";
console.log(
`${id} Closing issue ${payload.issue.html_url}, reason: ${reason} ${actorRequest}`
);
await closeIssue(
await getAuthToken(auth, payload.installation.id),
sourceRepo,
payload.issue.node_id,
reason
);
return;
}

const reopenMatches = reopenMatcher(payload.comment.body);
if (reopenMatches) {
console.log(
`${id} Re-opening issue ${payload.issue.html_url} ${actorRequest}`
);
await reopenIssue(
await getAuthToken(auth, payload.installation.id),
sourceRepo,
payload.issue.node_id
);
return;
}

const labelMatches = labelMatcher(payload.comment.body);
if (labelMatches) {
const labels = labelMatches[1].split(",");

console.log(
`${id} Labeling issue ${payload.issue.html_url} with labels ${labels} ${actorRequest}`
);
await addLabel(
await getAuthToken(auth, payload.installation.id),
payload.repository.owner.login,
sourceRepo,
payload.issue.node_id,
labels
);
return;
}

const removeLabelMatches = removeLabelMatcher(payload.comment.body);
if (removeLabelMatches) {
const labels = removeLabelMatches[1].split(",");

console.log(
`${id} Removing label(s) from issue ${payload.issue.html_url}, labels ${labels} ${actorRequest}`
);
await removeLabel(
await getAuthToken(auth, payload.installation.id),
payload.repository.owner.login,
sourceRepo,
payload.issue.node_id,
labels
);
return;
}

const reviewerMatches = reviewerMatcher(payload.comment.body);
if (reviewerMatches) {
console.log(
`${id} Requesting review for ${reviewerMatches[1]} at ${payload.issue.html_url} ${actorRequest}`
);
const reviewers = extractUsersAndTeams(
payload.repository.owner.login,
reviewerMatches[1]
);
await requestReviewers(
await getAuthToken(auth, payload.installation.id),
payload.repository.owner.login,
sourceRepo,
payload.issue.node_id,
reviewers.users,
reviewers.teams
);
return;
}

if (verbose) {
console.log("No match for", payload.comment.body);
}
}
Loading

0 comments on commit d13cf9c

Please sign in to comment.