From 28e0aae400d5a4c5b39a60b43be109dc3590d5be Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 04:57:35 +0000 Subject: [PATCH] [jsweep] Clean issue_title_dedup.cjs - Replace parameter reassignment (value = Number.parseInt(...)) with a local const variable for cleaner, more idiomatic code - Fix imprecise `@type {any}` annotation on bestMatch to use the correct type `{{ title: string, distance: number } | null}` - Add comprehensive test file (issue_title_dedup.test.cjs) with 26 test cases covering parseDeduplicateByTitle, normalizeTitleForDedup, and findDuplicateByTitle (previously had no tests) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- actions/setup/js/issue_title_dedup.cjs | 10 +- actions/setup/js/issue_title_dedup.test.cjs | 131 ++++++++++++++++++++ 2 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 actions/setup/js/issue_title_dedup.test.cjs diff --git a/actions/setup/js/issue_title_dedup.cjs b/actions/setup/js/issue_title_dedup.cjs index f16219e2f3c..e24e73b7b2e 100644 --- a/actions/setup/js/issue_title_dedup.cjs +++ b/actions/setup/js/issue_title_dedup.cjs @@ -25,11 +25,9 @@ function parseDeduplicateByTitle(value) { if (value === "true") { return { enabled: true, maxDistance: 0 }; } - if (typeof value === "string" && /^\d+$/.test(value)) { - value = Number.parseInt(value, 10); - } - if (typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0 && value <= MAX_DEDUPLICATE_BY_TITLE_DISTANCE) { - return { enabled: true, maxDistance: value }; + const numeric = typeof value === "string" && /^\d+$/.test(value) ? Number.parseInt(value, 10) : value; + if (typeof numeric === "number" && Number.isFinite(numeric) && Number.isInteger(numeric) && numeric >= 0 && numeric <= MAX_DEDUPLICATE_BY_TITLE_DISTANCE) { + return { enabled: true, maxDistance: numeric }; } throw new Error(`deduplicate-by-title must be a boolean, a boolean-like string, or a non-negative integer (0-${MAX_DEDUPLICATE_BY_TITLE_DISTANCE})`); } @@ -59,7 +57,7 @@ function normalizeTitleForDedup(title) { * @returns {{ title: string, distance: number } | null} */ function findDuplicateByTitle(normalizedTitle, candidates, maxDistance) { - /** @type {any} */ + /** @type {{ title: string, distance: number } | null} */ let bestMatch = null; for (const candidate of candidates) { diff --git a/actions/setup/js/issue_title_dedup.test.cjs b/actions/setup/js/issue_title_dedup.test.cjs new file mode 100644 index 00000000000..5a8c5e5cf3f --- /dev/null +++ b/actions/setup/js/issue_title_dedup.test.cjs @@ -0,0 +1,131 @@ +// @ts-check +import { describe, it, expect } from "vitest"; +import { createRequire } from "module"; + +const req = createRequire(import.meta.url); +const { parseDeduplicateByTitle, normalizeTitleForDedup, findDuplicateByTitle } = req("./issue_title_dedup.cjs"); + +describe("parseDeduplicateByTitle", () => { + it("returns disabled for undefined", () => { + expect(parseDeduplicateByTitle(undefined)).toEqual({ enabled: false, maxDistance: 0 }); + }); + + it("returns disabled for null", () => { + expect(parseDeduplicateByTitle(null)).toEqual({ enabled: false, maxDistance: 0 }); + }); + + it("returns disabled for false", () => { + expect(parseDeduplicateByTitle(false)).toEqual({ enabled: false, maxDistance: 0 }); + }); + + it("returns disabled for string 'false'", () => { + expect(parseDeduplicateByTitle("false")).toEqual({ enabled: false, maxDistance: 0 }); + }); + + it("returns enabled with distance 0 for true", () => { + expect(parseDeduplicateByTitle(true)).toEqual({ enabled: true, maxDistance: 0 }); + }); + + it("returns enabled with distance 0 for string 'true'", () => { + expect(parseDeduplicateByTitle("true")).toEqual({ enabled: true, maxDistance: 0 }); + }); + + it("returns enabled with numeric distance for integer", () => { + expect(parseDeduplicateByTitle(5)).toEqual({ enabled: true, maxDistance: 5 }); + }); + + it("returns enabled with numeric distance for string integer", () => { + expect(parseDeduplicateByTitle("10")).toEqual({ enabled: true, maxDistance: 10 }); + }); + + it("returns enabled with distance 0 for integer 0", () => { + expect(parseDeduplicateByTitle(0)).toEqual({ enabled: true, maxDistance: 0 }); + }); + + it("throws for out-of-range integer (above 100)", () => { + expect(() => parseDeduplicateByTitle(101)).toThrow("deduplicate-by-title"); + }); + + it("throws for negative integer", () => { + expect(() => parseDeduplicateByTitle(-1)).toThrow("deduplicate-by-title"); + }); + + it("throws for float", () => { + expect(() => parseDeduplicateByTitle(1.5)).toThrow("deduplicate-by-title"); + }); + + it("throws for non-numeric string", () => { + expect(() => parseDeduplicateByTitle("abc")).toThrow("deduplicate-by-title"); + }); + + it("accepts max distance 100", () => { + expect(parseDeduplicateByTitle(100)).toEqual({ enabled: true, maxDistance: 100 }); + }); +}); + +describe("normalizeTitleForDedup", () => { + it("lowercases title", () => { + expect(normalizeTitleForDedup("Hello World")).toBe("hello world"); + }); + + it("collapses multiple spaces", () => { + expect(normalizeTitleForDedup("hello world")).toBe("hello world"); + }); + + it("trims leading/trailing whitespace", () => { + expect(normalizeTitleForDedup(" hello ")).toBe("hello"); + }); + + it("handles empty string", () => { + expect(normalizeTitleForDedup("")).toBe(""); + }); + + it("handles mixed case and extra whitespace together", () => { + expect(normalizeTitleForDedup(" Fix BUG ")).toBe("fix bug"); + }); +}); + +describe("findDuplicateByTitle", () => { + it("returns null when candidates is empty", () => { + expect(findDuplicateByTitle("foo", [], 0)).toBeNull(); + }); + + it("finds exact match with maxDistance 0", () => { + const result = findDuplicateByTitle("fix bug", [{ title: "Fix Bug" }], 0); + expect(result).not.toBeNull(); + expect(result?.distance).toBe(0); + expect(result?.title).toBe("Fix Bug"); + }); + + it("returns null when no match within maxDistance", () => { + const result = findDuplicateByTitle("fix bug", [{ title: "add feature" }], 2); + expect(result).toBeNull(); + }); + + it("finds closest match within maxDistance", () => { + const candidates = [{ title: "Fix Bug in Module A" }, { title: "fix bug" }]; + const result = findDuplicateByTitle("fix bug", candidates, 5); + expect(result?.title).toBe("fix bug"); + expect(result?.distance).toBe(0); + }); + + it("uses normalizedTitle when provided", () => { + const candidates = [{ title: "Original Title", normalizedTitle: "fix bug" }]; + const result = findDuplicateByTitle("fix bug", candidates, 0); + expect(result?.title).toBe("Original Title"); + expect(result?.distance).toBe(0); + }); + + it("returns best match (lowest distance) among multiple candidates", () => { + const candidates = [{ title: "fix bugs" }, { title: "fix bug" }]; + const result = findDuplicateByTitle("fix bug", candidates, 5); + expect(result?.title).toBe("fix bug"); + expect(result?.distance).toBe(0); + }); + + it("returns null if best match exceeds maxDistance", () => { + const candidates = [{ title: "completely different title entirely" }]; + const result = findDuplicateByTitle("fix bug", candidates, 2); + expect(result).toBeNull(); + }); +});