import { describe, expect, test } from "bun:test"; import { normalizeTitle, stripMarkdown, stripThinkingTags, truncateTitle, } from "../short-title.js"; /** The character and word budgets `short-title.ts` documents and enforces. */ const MAX_TITLE_LENGTH = 40; const MAX_TITLE_WORDS = 7; describe("stripMarkdown", () => { test("unwraps bold, italic, strikethrough, and code spans", () => { expect(stripMarkdown("**Auth** Rewrite")).toBe("Auth Rewrite"); expect(stripMarkdown("__Auth__ Rewrite")).toBe("Auth Rewrite"); expect(stripMarkdown("*Auth* Rewrite")).toBe("Auth Rewrite"); expect(stripMarkdown("_Auth_ Rewrite")).toBe("Auth Rewrite"); expect(stripMarkdown("~~Auth~~ Rewrite")).toBe("Auth Rewrite"); expect(stripMarkdown("`Auth` Rewrite")).toBe("Auth Rewrite"); }); test("keeps link text and drops the target", () => { expect(stripMarkdown("[Auth Rewrite](https://example.com)")).toBe( "Auth Rewrite", ); }); test("removes heading markers", () => { expect(stripMarkdown("## Auth Rewrite")).toBe("Auth Rewrite"); }); test("preserves snake_case identifiers", () => { expect(stripMarkdown("record_conversation_title")).toBe( "record_conversation_title", ); }); }); describe("stripThinkingTags", () => { test("removes paired thinking blocks and their contents", () => { expect(stripThinkingTags("hmmAuth Rewrite")).toBe( "Auth Rewrite", ); expect(stripThinkingTags("hmmAuth Rewrite")).toBe( "Auth Rewrite", ); expect(stripThinkingTags("hmmAuth Rewrite")).toBe( "Auth Rewrite", ); }); test("removes unpaired tags but keeps the surrounding text", () => { expect(stripThinkingTags("Auth Rewrite")).toBe("Auth Rewrite"); expect(stripThinkingTags("Auth Rewrite")).toBe("Auth Rewrite"); }); test("removes colon-prefixed pseudo tags", () => { expect(stripThinkingTags("<:anything>Auth Rewrite")).toBe("Auth Rewrite"); }); test("leaves plain text untouched", () => { expect(stripThinkingTags("Auth Rewrite")).toBe("Auth Rewrite"); }); }); describe("truncateTitle", () => { test("leaves titles within the character limit untouched", () => { const title = "Auth Middleware Rewrite"; expect(title.length).toBeLessThanOrEqual(MAX_TITLE_LENGTH); expect(truncateTitle(title)).toBe(title); }); test("trims at a word boundary when few but long words exceed the char limit", () => { const title = "Supercalifragilistic Authentication Middleware Rewrite"; expect(title.split(" ").length).toBeLessThanOrEqual(MAX_TITLE_WORDS); expect(truncateTitle(title)).toBe("Supercalifragilistic Authentication"); }); test("hard-slices a single word longer than the char limit", () => { const title = "a".repeat(MAX_TITLE_LENGTH + 5); expect(truncateTitle(title)).toBe("a".repeat(MAX_TITLE_LENGTH)); }); test("keeps the first five words when the word limit is exceeded", () => { const title = "alpha bravo charlie delta echo foxtrot golf hotel india juliet"; expect(title.length).toBeGreaterThan(MAX_TITLE_LENGTH); expect(title.split(" ").length).toBeGreaterThan(MAX_TITLE_WORDS); expect(truncateTitle(title)).toBe("alpha bravo charlie delta echo"); }); test("still honors the char limit when the first five words overflow it", () => { const title = "Authentication Infrastructure Modernization Deployment Verification Report Complete Today"; expect(title.length).toBeGreaterThan(MAX_TITLE_LENGTH); expect(title.split(" ").length).toBeGreaterThan(MAX_TITLE_WORDS); const result = truncateTitle(title); expect(result).toBe("Authentication Infrastructure"); expect(result.length).toBeLessThanOrEqual(MAX_TITLE_LENGTH); }); }); describe("normalizeTitle prose rejection", () => { test("rejects multi-line output", () => { expect(normalizeTitle("Auth Rewrite\nand more")).toBe(""); }); test("rejects embedded transcript markers", () => { expect(normalizeTitle("User: how do I log in")).toBe(""); expect(normalizeTitle("Assistant : here you go")).toBe(""); }); test("rejects sentence-shaped clauses ending in terminal punctuation", () => { expect(normalizeTitle("This is a topic about authentication.")).toBe(""); expect(normalizeTitle("What did they want to build here?")).toBe(""); }); test("keeps short phrases that end in punctuation", () => { expect(normalizeTitle("Auth Rewrite?")).toBe("Auth Rewrite?"); }); test("rejects first-person reasoning openers", () => { expect(normalizeTitle("I need to generate a title")).toBe(""); expect(normalizeTitle("I'll work through these files")).toBe(""); expect(normalizeTitle("I cannot title this")).toBe(""); }); test("rejects imperative and deferral openers", () => { expect(normalizeTitle("Let me summarize the request")).toBe(""); expect(normalizeTitle("Looking at the conversation")).toBe(""); expect(normalizeTitle("Based on the messages")).toBe(""); expect(normalizeTitle("Given the context")).toBe(""); }); test("rejects infinitive openers", () => { expect(normalizeTitle("To generate a good title")).toBe(""); expect(normalizeTitle("To summarize the discussion")).toBe(""); expect(normalizeTitle("To title this thread")).toBe(""); }); test("rejects subject-led reasoning openers", () => { expect(normalizeTitle("The user wants a summary")).toBe(""); expect(normalizeTitle("The conversation is about Docker")).toBe(""); expect(normalizeTitle("The assistant should reply")).toBe(""); expect(normalizeTitle("The title should be short")).toBe(""); }); test("rejects preamble openers", () => { expect(normalizeTitle("Here's a title")).toBe(""); expect(normalizeTitle("Here is a title")).toBe(""); expect(normalizeTitle("Here are the options")).toBe(""); expect(normalizeTitle("Sure, Auth Rewrite")).toBe(""); expect(normalizeTitle("Okay, Auth Rewrite")).toBe(""); expect(normalizeTitle("Ok, Auth Rewrite")).toBe(""); }); test("accepts bare noun-phrase titles that share a subject word", () => { expect(normalizeTitle("The Conversation API")).toBe("The Conversation API"); expect(normalizeTitle("The User Interface Redesign")).toBe( "The User Interface Redesign", ); expect(normalizeTitle("Auth Middleware Rewrite")).toBe( "Auth Middleware Rewrite", ); }); }); describe("normalizeTitle", () => { test("strips surrounding quotes", () => { expect(normalizeTitle('"Auth Rewrite"')).toBe("Auth Rewrite"); expect(normalizeTitle("'Auth Rewrite'")).toBe("Auth Rewrite"); }); test("strips markdown and thinking tags together", () => { expect(normalizeTitle("hmm **Auth Rewrite**")).toBe( "Auth Rewrite", ); }); test("returns empty for blank input", () => { expect(normalizeTitle("")).toBe(""); expect(normalizeTitle(" ")).toBe(""); expect(normalizeTitle("hmm")).toBe(""); }); test("returns empty for leaked prose", () => { expect(normalizeTitle("I need to generate a title for this")).toBe(""); expect(normalizeTitle("User: how do I log in")).toBe(""); expect(normalizeTitle("Auth Rewrite\nand more")).toBe(""); }); test("returns empty for meta-failure titles regardless of case", () => { const metaFailures = [ "missing context", "no context", "insufficient context", "unclear context", "empty context", "no topic", "unclear topic", "unclear request", "unclear message", "empty conversation", "empty message", "no content", ]; for (const meta of metaFailures) { expect(normalizeTitle(meta)).toBe(""); } expect(normalizeTitle("Missing Context")).toBe(""); expect(normalizeTitle('"No Topic"')).toBe(""); }); test("truncates an over-long accepted title", () => { expect( normalizeTitle("alpha bravo charlie delta echo foxtrot golf hotel india"), ).toBe("alpha bravo charlie delta echo"); }); test("keeps a wordy title inside the character budget", () => { const result = normalizeTitle( "Authentication Infrastructure Modernization Deployment Verification Report Complete Today", ); expect(result).toBe("Authentication Infrastructure"); expect(result.length).toBeLessThanOrEqual(MAX_TITLE_LENGTH); }); });