From b2e56c6b3eb89034af6623d5c9d23c65ac917ad8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Mar 2026 03:13:47 +0000 Subject: [PATCH] jsweep: clean hide_comment.cjs and add tests - Remove redundant `const item = message` alias, use `message` directly - Inline `reason` variable into single normalized expression - Add 14 comprehensive tests covering happy path, error cases, staged mode, allowed-reasons validation, and max count enforcement Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- actions/setup/js/hide_comment.cjs | 12 +- actions/setup/js/hide_comment.test.cjs | 185 +++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 actions/setup/js/hide_comment.test.cjs diff --git a/actions/setup/js/hide_comment.cjs b/actions/setup/js/hide_comment.cjs index f4f1fa0e619..b98edb077c9 100644 --- a/actions/setup/js/hide_comment.cjs +++ b/actions/setup/js/hide_comment.cjs @@ -80,10 +80,8 @@ async function main(config = {}) { processedCount++; - const item = message; - try { - const commentId = item.comment_id; + const commentId = message.comment_id; if (!commentId || typeof commentId !== "string") { core.warning("comment_id is required and must be a string (GraphQL node ID)"); return { @@ -92,19 +90,17 @@ async function main(config = {}) { }; } - const reason = item.reason || "SPAM"; - // Normalize reason to uppercase for GitHub API - const normalizedReason = reason.toUpperCase(); + const normalizedReason = (message.reason || "SPAM").toUpperCase(); // Validate reason against allowed reasons if specified (case-insensitive) if (allowedReasons.length > 0) { const normalizedAllowedReasons = allowedReasons.map(r => r.toUpperCase()); if (!normalizedAllowedReasons.includes(normalizedReason)) { - core.warning(`Reason "${reason}" is not in allowed-reasons list [${allowedReasons.join(", ")}]. Skipping comment ${commentId}.`); + core.warning(`Reason "${message.reason}" is not in allowed-reasons list [${allowedReasons.join(", ")}]. Skipping comment ${commentId}.`); return { success: false, - error: `Reason "${reason}" is not in allowed-reasons list`, + error: `Reason "${message.reason}" is not in allowed-reasons list`, }; } } diff --git a/actions/setup/js/hide_comment.test.cjs b/actions/setup/js/hide_comment.test.cjs new file mode 100644 index 00000000000..b4dbf3d809d --- /dev/null +++ b/actions/setup/js/hide_comment.test.cjs @@ -0,0 +1,185 @@ +// @ts-check +import { describe, it, expect, beforeEach, vi } from "vitest"; + +const mockCore = { + info: vi.fn(), + warning: vi.fn(), + error: vi.fn(), + setFailed: vi.fn(), + setOutput: vi.fn(), +}; + +const mockGithub = { + graphql: vi.fn(), +}; + +const mockContext = { + eventName: "issue_comment", + repo: { owner: "testowner", repo: "testrepo" }, + payload: { issue: { number: 42 } }, +}; + +global.core = mockCore; +global.github = mockGithub; +global.context = mockContext; + +async function loadModule() { + const { main } = await import("./hide_comment.cjs?" + Date.now()); + return { main }; +} + +describe("hide_comment.cjs", () => { + beforeEach(() => { + vi.resetAllMocks(); + delete process.env.GH_AW_SAFE_OUTPUTS_STAGED; + + // Default successful graphql mock + mockGithub.graphql.mockResolvedValue({ + minimizeComment: { minimizedComment: { isMinimized: true } }, + }); + }); + + describe("main factory", () => { + it("should return a handler function", async () => { + const { main } = await loadModule(); + const handler = await main(); + expect(typeof handler).toBe("function"); + }); + + it("should log configuration on initialization", async () => { + const { main } = await loadModule(); + await main({ max: 3 }); + expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("max=3")); + }); + + it("should log allowed reasons when configured", async () => { + const { main } = await loadModule(); + await main({ allowed_reasons: ["SPAM", "ABUSE"] }); + expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("SPAM, ABUSE")); + }); + }); + + describe("handleHideComment", () => { + it("should hide a comment successfully", async () => { + const { main } = await loadModule(); + const handler = await main(); + + const result = await handler({ comment_id: "IC_kwDOABCD123456", reason: "SPAM" }, {}); + + expect(result.success).toBe(true); + expect(result.comment_id).toBe("IC_kwDOABCD123456"); + expect(result.is_hidden).toBe(true); + }); + + it("should use SPAM as default reason when not provided", async () => { + const { main } = await loadModule(); + const handler = await main(); + + await handler({ comment_id: "IC_kwDOABCD123456" }, {}); + + expect(mockGithub.graphql).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ classifier: "SPAM" })); + }); + + it("should normalize reason to uppercase", async () => { + const { main } = await loadModule(); + const handler = await main(); + + await handler({ comment_id: "IC_kwDOABCD123456", reason: "abuse" }, {}); + + expect(mockGithub.graphql).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ classifier: "ABUSE" })); + }); + + it("should fail when comment_id is missing", async () => { + const { main } = await loadModule(); + const handler = await main(); + + const result = await handler({ reason: "SPAM" }, {}); + + expect(result.success).toBe(false); + expect(result.error).toContain("comment_id is required"); + expect(mockGithub.graphql).not.toHaveBeenCalled(); + }); + + it("should fail when comment_id is not a string", async () => { + const { main } = await loadModule(); + const handler = await main(); + + const result = await handler({ comment_id: 12345, reason: "SPAM" }, {}); + + expect(result.success).toBe(false); + expect(result.error).toContain("comment_id is required and must be a string"); + }); + + it("should enforce max count limit", async () => { + const { main } = await loadModule(); + const handler = await main({ max: 2 }); + + await handler({ comment_id: "IC_1" }, {}); + await handler({ comment_id: "IC_2" }, {}); + const result = await handler({ comment_id: "IC_3" }, {}); + + expect(result.success).toBe(false); + expect(result.error).toContain("Max count"); + expect(mockGithub.graphql).toHaveBeenCalledTimes(2); + }); + + it("should reject reason not in allowed-reasons list", async () => { + const { main } = await loadModule(); + const handler = await main({ allowed_reasons: ["SPAM", "ABUSE"] }); + + const result = await handler({ comment_id: "IC_kwDOABCD123456", reason: "OFF_TOPIC" }, {}); + + expect(result.success).toBe(false); + expect(result.error).toContain("not in allowed-reasons list"); + expect(mockGithub.graphql).not.toHaveBeenCalled(); + }); + + it("should accept allowed reason case-insensitively", async () => { + const { main } = await loadModule(); + const handler = await main({ allowed_reasons: ["spam", "abuse"] }); + + const result = await handler({ comment_id: "IC_kwDOABCD123456", reason: "SPAM" }, {}); + + expect(result.success).toBe(true); + }); + + it("should handle GraphQL API errors gracefully", async () => { + const { main } = await loadModule(); + mockGithub.graphql.mockRejectedValue(new Error("GraphQL error: Forbidden")); + const handler = await main(); + + const result = await handler({ comment_id: "IC_kwDOABCD123456" }, {}); + + expect(result.success).toBe(false); + expect(result.error).toContain("GraphQL error"); + expect(mockCore.error).toHaveBeenCalled(); + }); + + it("should return failure when comment is not minimized", async () => { + const { main } = await loadModule(); + mockGithub.graphql.mockResolvedValue({ + minimizeComment: { minimizedComment: { isMinimized: false } }, + }); + const handler = await main(); + + const result = await handler({ comment_id: "IC_kwDOABCD123456" }, {}); + + expect(result.success).toBe(false); + expect(result.error).toContain("Failed to hide comment"); + }); + + it("should return staged preview in staged mode", async () => { + process.env.GH_AW_SAFE_OUTPUTS_STAGED = "true"; + const { main } = await loadModule(); + const handler = await main(); + + const result = await handler({ comment_id: "IC_kwDOABCD123456", reason: "ABUSE" }, {}); + + expect(result.success).toBe(true); + expect(result.staged).toBe(true); + expect(result.previewInfo?.commentId).toBe("IC_kwDOABCD123456"); + expect(result.previewInfo?.reason).toBe("ABUSE"); + expect(mockGithub.graphql).not.toHaveBeenCalled(); + }); + }); +});