|
|
@@ -0,0 +1,123 @@
|
|
|
+import { describe, expect, test } from "bun:test";
|
|
|
+import {
|
|
|
+ stripHtmlComments,
|
|
|
+ isMemoryFilePath,
|
|
|
+ getLargeMemoryFiles,
|
|
|
+ MAX_MEMORY_CHARACTER_COUNT,
|
|
|
+ type MemoryFileInfo,
|
|
|
+} from "../claudemd";
|
|
|
+
|
|
|
+function mockMemoryFile(overrides: Partial<MemoryFileInfo> = {}): MemoryFileInfo {
|
|
|
+ return {
|
|
|
+ path: "/project/CLAUDE.md",
|
|
|
+ type: "Project",
|
|
|
+ content: "test content",
|
|
|
+ ...overrides,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+describe("stripHtmlComments", () => {
|
|
|
+ test("strips block-level HTML comments (own line)", () => {
|
|
|
+ // CommonMark type-2 HTML blocks: comment must start at beginning of line
|
|
|
+ const result = stripHtmlComments("text\n<!-- block comment -->\nmore");
|
|
|
+ expect(result.content).not.toContain("block comment");
|
|
|
+ expect(result.stripped).toBe(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ test("returns stripped: false when no comments", () => {
|
|
|
+ const result = stripHtmlComments("no comments here");
|
|
|
+ expect(result.stripped).toBe(false);
|
|
|
+ expect(result.content).toBe("no comments here");
|
|
|
+ });
|
|
|
+
|
|
|
+ test("returns stripped: true when block comments exist", () => {
|
|
|
+ const result = stripHtmlComments("hello\n<!-- world -->\nend");
|
|
|
+ expect(result.stripped).toBe(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ test("handles empty string", () => {
|
|
|
+ const result = stripHtmlComments("");
|
|
|
+ expect(result.content).toBe("");
|
|
|
+ expect(result.stripped).toBe(false);
|
|
|
+ });
|
|
|
+
|
|
|
+ test("handles multiple block comments", () => {
|
|
|
+ const result = stripHtmlComments(
|
|
|
+ "a\n<!-- c1 -->\nb\n<!-- c2 -->\nc"
|
|
|
+ );
|
|
|
+ expect(result.content).not.toContain("c1");
|
|
|
+ expect(result.content).not.toContain("c2");
|
|
|
+ expect(result.stripped).toBe(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ test("preserves code block content", () => {
|
|
|
+ const input = "text\n```html\n<!-- not stripped -->\n```\nmore";
|
|
|
+ const result = stripHtmlComments(input);
|
|
|
+ expect(result.content).toContain("<!-- not stripped -->");
|
|
|
+ });
|
|
|
+
|
|
|
+ test("preserves inline comments within paragraphs", () => {
|
|
|
+ // Inline comments are NOT stripped (CommonMark paragraph semantics)
|
|
|
+ const result = stripHtmlComments("text <!-- inline --> more");
|
|
|
+ expect(result.content).toContain("<!-- inline -->");
|
|
|
+ expect(result.stripped).toBe(false);
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+describe("isMemoryFilePath", () => {
|
|
|
+ test("returns true for CLAUDE.md path", () => {
|
|
|
+ expect(isMemoryFilePath("/project/CLAUDE.md")).toBe(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ test("returns true for CLAUDE.local.md path", () => {
|
|
|
+ expect(isMemoryFilePath("/project/CLAUDE.local.md")).toBe(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ test("returns true for .claude/rules/ path", () => {
|
|
|
+ expect(isMemoryFilePath("/project/.claude/rules/foo.md")).toBe(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ test("returns false for regular file", () => {
|
|
|
+ expect(isMemoryFilePath("/project/src/main.ts")).toBe(false);
|
|
|
+ });
|
|
|
+
|
|
|
+ test("returns false for unrelated .md file", () => {
|
|
|
+ expect(isMemoryFilePath("/project/README.md")).toBe(false);
|
|
|
+ });
|
|
|
+
|
|
|
+ test("returns false for .claude directory non-rules file", () => {
|
|
|
+ expect(isMemoryFilePath("/project/.claude/settings.json")).toBe(false);
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+describe("getLargeMemoryFiles", () => {
|
|
|
+ test("returns files exceeding threshold", () => {
|
|
|
+ const largeContent = "x".repeat(MAX_MEMORY_CHARACTER_COUNT + 1);
|
|
|
+ const files = [
|
|
|
+ mockMemoryFile({ content: "small" }),
|
|
|
+ mockMemoryFile({ content: largeContent, path: "/big.md" }),
|
|
|
+ ];
|
|
|
+ const result = getLargeMemoryFiles(files);
|
|
|
+ expect(result).toHaveLength(1);
|
|
|
+ expect(result[0].path).toBe("/big.md");
|
|
|
+ });
|
|
|
+
|
|
|
+ test("returns empty array when all files are small", () => {
|
|
|
+ const files = [
|
|
|
+ mockMemoryFile({ content: "small" }),
|
|
|
+ mockMemoryFile({ content: "also small" }),
|
|
|
+ ];
|
|
|
+ expect(getLargeMemoryFiles(files)).toEqual([]);
|
|
|
+ });
|
|
|
+
|
|
|
+ test("correctly identifies threshold boundary", () => {
|
|
|
+ const atThreshold = "x".repeat(MAX_MEMORY_CHARACTER_COUNT);
|
|
|
+ const overThreshold = "x".repeat(MAX_MEMORY_CHARACTER_COUNT + 1);
|
|
|
+ const files = [
|
|
|
+ mockMemoryFile({ content: atThreshold }),
|
|
|
+ mockMemoryFile({ content: overThreshold }),
|
|
|
+ ];
|
|
|
+ const result = getLargeMemoryFiles(files);
|
|
|
+ expect(result).toHaveLength(1);
|
|
|
+ });
|
|
|
+});
|