From bab09a2bad6133f1d089a9562c5c0dfa6e424b13 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:58:08 +0000 Subject: [PATCH] refactor: extract validation logic into src/validate.ts Co-authored-by: marocchino <128431+marocchino@users.noreply.github.com> --- __tests__/validate.test.ts | 85 ++++++++++++++++++++++++++++++++++++++ src/main.ts | 29 +++++-------- src/validate.ts | 35 ++++++++++++++++ 3 files changed, 130 insertions(+), 19 deletions(-) create mode 100644 __tests__/validate.test.ts create mode 100644 src/validate.ts diff --git a/__tests__/validate.test.ts b/__tests__/validate.test.ts new file mode 100644 index 0000000..0ece200 --- /dev/null +++ b/__tests__/validate.test.ts @@ -0,0 +1,85 @@ +import {describe, expect, test} from "vitest" +import {validateBody, validateExclusiveModes} from "../src/validate" + +describe("validateBody", () => { + test("throws when body is empty and neither delete nor hide is set", () => { + expect(() => validateBody("", false, false)).toThrow( + "Either message or path input is required", + ) + }) + + test("does not throw when body is provided", () => { + expect(() => validateBody("some body", false, false)).not.toThrow() + }) + + test("does not throw when body is empty but deleteOldComment is true", () => { + expect(() => validateBody("", true, false)).not.toThrow() + }) + + test("does not throw when body is empty but hideOldComment is true", () => { + expect(() => validateBody("", false, true)).not.toThrow() + }) +}) + +describe("validateExclusiveModes", () => { + test("does not throw when no modes are enabled", () => { + expect(() => validateExclusiveModes(false, false, false, false, false, false)).not.toThrow() + }) + + test("does not throw when exactly one mode is enabled", () => { + expect(() => validateExclusiveModes(true, false, false, false, false, false)).not.toThrow() + expect(() => validateExclusiveModes(false, true, false, false, false, false)).not.toThrow() + expect(() => validateExclusiveModes(false, false, true, false, false, false)).not.toThrow() + expect(() => validateExclusiveModes(false, false, false, true, false, false)).not.toThrow() + expect(() => validateExclusiveModes(false, false, false, false, true, false)).not.toThrow() + expect(() => validateExclusiveModes(false, false, false, false, false, true)).not.toThrow() + }) + + test("throws when delete and recreate are both true", () => { + expect(() => validateExclusiveModes(true, true, false, false, false, false)).toThrow( + "delete and recreate cannot be set to true simultaneously", + ) + }) + + test("throws when delete and only_create are both true", () => { + expect(() => validateExclusiveModes(true, false, true, false, false, false)).toThrow( + "delete and only_create cannot be set to true simultaneously", + ) + }) + + test("throws when delete and only_update are both true", () => { + expect(() => validateExclusiveModes(true, false, false, true, false, false)).toThrow( + "delete and only_update cannot be set to true simultaneously", + ) + }) + + test("throws when delete and hide are both true", () => { + expect(() => validateExclusiveModes(true, false, false, false, true, false)).toThrow( + "delete and hide cannot be set to true simultaneously", + ) + }) + + test("throws when delete and hide_and_recreate are both true", () => { + expect(() => validateExclusiveModes(true, false, false, false, false, true)).toThrow( + "delete and hide_and_recreate cannot be set to true simultaneously", + ) + }) + + test("throws when only_create and only_update are both true", () => { + expect(() => validateExclusiveModes(false, false, true, true, false, false)).toThrow( + "only_create and only_update cannot be set to true simultaneously", + ) + }) + + test("throws when hide and hide_and_recreate are both true", () => { + expect(() => validateExclusiveModes(false, false, false, false, true, true)).toThrow( + "hide and hide_and_recreate cannot be set to true simultaneously", + ) + }) + + test("uses Oxford comma when three or more modes are enabled", () => { + expect(() => validateExclusiveModes(true, true, true, false, false, false)).toThrow( + "delete, recreate, and only_create cannot be set to true simultaneously", + ) + }) +}) diff --git a/src/main.ts b/src/main.ts index 37ccdd0..5a83f81 100644 --- a/src/main.ts +++ b/src/main.ts @@ -27,6 +27,7 @@ import { repo, skipUnchanged, } from "./config" +import {validateBody, validateExclusiveModes} from "./validate" async function run(): Promise { if (Number.isNaN(pullRequestNumber) || pullRequestNumber < 1) { @@ -42,25 +43,15 @@ async function run(): Promise { return } - if (!deleteOldComment && !hideOldComment && !body) { - throw new Error("Either message or path input is required") - } - - const exclusiveModes: [string, boolean][] = [ - ["delete", deleteOldComment], - ["recreate", recreate], - ["only_create", onlyCreateComment], - ["only_update", onlyUpdateComment], - ["hide", hideOldComment], - ["hide_and_recreate", hideAndRecreate], - ] - const enabledModes = exclusiveModes.filter(([, flag]) => flag).map(([name]) => name) - if (enabledModes.length > 1) { - const last = enabledModes[enabledModes.length - 1] - const rest = enabledModes.slice(0, -1) - const joined = enabledModes.length === 2 ? `${rest[0]} and ${last}` : `${rest.join(", ")}, and ${last}` - throw new Error(`${joined} cannot be set to true simultaneously`) - } + validateBody(body, deleteOldComment, hideOldComment) + validateExclusiveModes( + deleteOldComment, + recreate, + onlyCreateComment, + onlyUpdateComment, + hideOldComment, + hideAndRecreate, + ) const octokit = github.getOctokit(githubToken) const previous = await findPreviousComment(octokit, repo, pullRequestNumber, header) diff --git a/src/validate.ts b/src/validate.ts new file mode 100644 index 0000000..9db2372 --- /dev/null +++ b/src/validate.ts @@ -0,0 +1,35 @@ +export function validateBody( + body: string, + deleteOldComment: boolean, + hideOldComment: boolean, +): void { + if (!deleteOldComment && !hideOldComment && !body) { + throw new Error("Either message or path input is required") + } +} + +export function validateExclusiveModes( + deleteOldComment: boolean, + recreate: boolean, + onlyCreateComment: boolean, + onlyUpdateComment: boolean, + hideOldComment: boolean, + hideAndRecreate: boolean, +): void { + const exclusiveModes: [string, boolean][] = [ + ["delete", deleteOldComment], + ["recreate", recreate], + ["only_create", onlyCreateComment], + ["only_update", onlyUpdateComment], + ["hide", hideOldComment], + ["hide_and_recreate", hideAndRecreate], + ] + const enabledModes = exclusiveModes.filter(([, flag]) => flag).map(([name]) => name) + if (enabledModes.length > 1) { + const last = enabledModes[enabledModes.length - 1] + const rest = enabledModes.slice(0, -1) + const joined = + enabledModes.length === 2 ? `${rest[0]} and ${last}` : `${rest.join(", ")}, and ${last}` + throw new Error(`${joined} cannot be set to true simultaneously`) + } +}