feat(linking): explicit [#42] card references
This commit is contained in:
parent
d06d1e99ec
commit
7970edf7c2
2 changed files with 77 additions and 0 deletions
50
src/lib/linking/reference.test.ts
Normal file
50
src/lib/linking/reference.test.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { extractCardReference } from "@/lib/linking/reference";
|
||||
|
||||
describe("extractCardReference", () => {
|
||||
it("finds a bracketed reference at the end of the subject", () => {
|
||||
expect(extractCardReference("feat(stock): inline qty editing [#42]")).toBe(42);
|
||||
});
|
||||
|
||||
it("finds one anywhere in the message, including the body", () => {
|
||||
expect(extractCardReference("[#7] fix: thing")).toBe(7);
|
||||
expect(extractCardReference("fix: thing\n\nRefs [#7]\n")).toBe(7);
|
||||
});
|
||||
|
||||
it("returns null when there is none", () => {
|
||||
expect(extractCardReference("feat(stock): inline qty editing")).toBeNull();
|
||||
expect(extractCardReference("")).toBeNull();
|
||||
});
|
||||
|
||||
it("ignores a bare #42 without brackets", () => {
|
||||
// Bare hashes appear constantly in prose — colours, issue numbers from
|
||||
// other systems, ordinals. Requiring brackets makes the reference a
|
||||
// deliberate act rather than a guess.
|
||||
expect(extractCardReference("fix: matches #42 in the old tracker")).toBeNull();
|
||||
expect(extractCardReference("style: use #ff8a5c")).toBeNull();
|
||||
});
|
||||
|
||||
it("takes the first reference when several appear", () => {
|
||||
// One commit links to at most one card, so a second reference cannot be
|
||||
// honoured. First wins, deterministically.
|
||||
expect(extractCardReference("fix: thing [#3] and also [#9]")).toBe(3);
|
||||
});
|
||||
|
||||
it("rejects zero and negative numbers", () => {
|
||||
expect(extractCardReference("fix: thing [#0]")).toBeNull();
|
||||
expect(extractCardReference("fix: thing [#-4]")).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects a non-numeric reference", () => {
|
||||
expect(extractCardReference("fix: thing [#abc]")).toBeNull();
|
||||
expect(extractCardReference("fix: thing [#]")).toBeNull();
|
||||
});
|
||||
|
||||
it("tolerates whitespace inside the brackets", () => {
|
||||
expect(extractCardReference("fix: thing [# 42 ]")).toBe(42);
|
||||
});
|
||||
|
||||
it("rejects an absurdly large number rather than overflowing", () => {
|
||||
expect(extractCardReference(`fix: thing [#${"9".repeat(20)}]`)).toBeNull();
|
||||
});
|
||||
});
|
||||
27
src/lib/linking/reference.ts
Normal file
27
src/lib/linking/reference.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* Explicit card references in commit messages: `[#42]`.
|
||||
*
|
||||
* Brackets are required. A bare `#42` appears far too often in ordinary prose —
|
||||
* hex colours, issue numbers from other trackers, ordinals — and a linker that
|
||||
* guesses at those attaches work to the wrong card, which is worse than not
|
||||
* linking it at all.
|
||||
*
|
||||
* This is the override, not the daily habit: scope matching carries most
|
||||
* commits. It exists for the times auto-linking gets it wrong.
|
||||
*/
|
||||
|
||||
/** Above this, the value is not a card number; it is a typo or an overflow. */
|
||||
const MAX_CARD_NUMBER = 1_000_000;
|
||||
|
||||
const REFERENCE = /\[#\s*(\d+)\s*\]/;
|
||||
|
||||
export function extractCardReference(message: string): number | null {
|
||||
const match = REFERENCE.exec(message ?? "");
|
||||
if (!match) return null;
|
||||
|
||||
const value = Number(match[1]);
|
||||
if (!Number.isInteger(value)) return null;
|
||||
if (value < 1 || value > MAX_CARD_NUMBER) return null;
|
||||
|
||||
return value;
|
||||
}
|
||||
Loading…
Reference in a new issue