CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
close-keywords.test.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| d62fb36 | 1 | /** |
| 2 | * Block J7 — Closing-keyword parser tests. Pure function so the whole spec | |
| 3 | * lives here. | |
| 4 | */ | |
| 5 | ||
| 6 | import { describe, it, expect } from "bun:test"; | |
| 7 | import { | |
| 8 | extractClosingRefs, | |
| 9 | extractClosingRefsMulti, | |
| 10 | } from "../lib/close-keywords"; | |
| 11 | ||
| 12 | describe("close-keywords — extractClosingRefs", () => { | |
| 13 | it("returns [] for null / empty / undefined", () => { | |
| 14 | expect(extractClosingRefs(null)).toEqual([]); | |
| 15 | expect(extractClosingRefs(undefined)).toEqual([]); | |
| 16 | expect(extractClosingRefs("")).toEqual([]); | |
| 17 | }); | |
| 18 | ||
| 19 | it("matches all close/fix/resolve forms", () => { | |
| 20 | expect(extractClosingRefs("closes #1")).toEqual([1]); | |
| 21 | expect(extractClosingRefs("Close #2")).toEqual([2]); | |
| 22 | expect(extractClosingRefs("closed #3")).toEqual([3]); | |
| 23 | expect(extractClosingRefs("fix #4")).toEqual([4]); | |
| 24 | expect(extractClosingRefs("Fixes #5")).toEqual([5]); | |
| 25 | expect(extractClosingRefs("fixed #6")).toEqual([6]); | |
| 26 | expect(extractClosingRefs("resolve #7")).toEqual([7]); | |
| 27 | expect(extractClosingRefs("Resolves #8")).toEqual([8]); | |
| 28 | expect(extractClosingRefs("resolved #9")).toEqual([9]); | |
| 29 | }); | |
| 30 | ||
| 31 | it("handles colon / hyphen / punctuation between verb and ref", () => { | |
| 32 | expect(extractClosingRefs("Fixes: #12")).toEqual([12]); | |
| 33 | expect(extractClosingRefs("Closes:#13")).toEqual([13]); | |
| 34 | expect(extractClosingRefs("Fixes - #14")).toEqual([14]); | |
| 35 | expect(extractClosingRefs("Closes #15.")).toEqual([15]); | |
| 36 | expect(extractClosingRefs("Closes #16, thanks")).toEqual([16]); | |
| 37 | }); | |
| 38 | ||
| 39 | it("de-dupes and sorts", () => { | |
| 40 | expect(extractClosingRefs("closes #2 fixes #1 resolves #2")).toEqual([1, 2]); | |
| 41 | }); | |
| 42 | ||
| 43 | it("picks up multiple refs in a body", () => { | |
| 44 | const body = | |
| 45 | "This PR tidies up the UI.\n\nFixes #10\nCloses #11\nNot related: #99\nResolves #12"; | |
| 46 | expect(extractClosingRefs(body)).toEqual([10, 11, 12]); | |
| 47 | }); | |
| 48 | ||
| 49 | it("ignores bare #N without a closing verb", () => { | |
| 50 | expect(extractClosingRefs("See #5 for context")).toEqual([]); | |
| 51 | expect(extractClosingRefs("#123 is the root cause")).toEqual([]); | |
| 52 | }); | |
| 53 | ||
| 54 | it("ignores cross-repo refs (owner/repo#N)", () => { | |
| 55 | expect(extractClosingRefs("Closes alice/widgets#42")).toEqual([]); | |
| 56 | expect(extractClosingRefs("Fixes foo/bar#99 and fixes #7")).toEqual([7]); | |
| 57 | }); | |
| 58 | ||
| 59 | it("does not match verbs embedded in larger words", () => { | |
| 60 | expect(extractClosingRefs("disclose #1")).toEqual([]); | |
| 61 | expect(extractClosingRefs("prefix #2")).toEqual([]); | |
| 62 | expect(extractClosingRefs("unresolved #3")).toEqual([]); | |
| 63 | }); | |
| 64 | ||
| 65 | it("does not match when # is spaced away from the number", () => { | |
| 66 | expect(extractClosingRefs("Closes # 1")).toEqual([]); | |
| 67 | }); | |
| 68 | ||
| 69 | it("is case insensitive on the verb", () => { | |
| 70 | expect(extractClosingRefs("CLOSES #1 FIXES #2 Resolves #3")).toEqual([ | |
| 71 | 1, 2, 3, | |
| 72 | ]); | |
| 73 | }); | |
| 74 | ||
| 75 | it("tolerates whitespace runs", () => { | |
| 76 | expect(extractClosingRefs("Closes #1")).toEqual([1]); | |
| 77 | expect(extractClosingRefs("fixes\t#2")).toEqual([2]); | |
| 78 | }); | |
| 79 | ||
| 80 | it("rejects non-positive numbers", () => { | |
| 81 | expect(extractClosingRefs("Closes #0")).toEqual([]); | |
| 82 | // "#-1" is not a valid match under the parser either. | |
| 83 | expect(extractClosingRefs("Closes #-1")).toEqual([]); | |
| 84 | }); | |
| 85 | }); | |
| 86 | ||
| 87 | describe("close-keywords — extractClosingRefsMulti", () => { | |
| 88 | it("merges + de-dupes across sources", () => { | |
| 89 | const body = "Fixes #1\nCloses #2"; | |
| 90 | const title = "Resolves #2: cleanup"; | |
| 91 | expect(extractClosingRefsMulti([title, body])).toEqual([1, 2]); | |
| 92 | }); | |
| 93 | ||
| 94 | it("skips nullish sources gracefully", () => { | |
| 95 | expect(extractClosingRefsMulti([null, undefined, "Closes #7"])).toEqual([ | |
| 96 | 7, | |
| 97 | ]); | |
| 98 | expect(extractClosingRefsMulti([])).toEqual([]); | |
| 99 | }); | |
| 100 | }); |