| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- import { mock, describe, expect, test } from "bun:test";
- // Mock log.ts to cut the heavy dependency chain
- mock.module("src/utils/log.ts", () => ({
- logError: () => {},
- logToFile: () => {},
- getLogDisplayTitle: () => "",
- logEvent: () => {},
- logMCPError: () => {},
- logMCPDebug: () => {},
- dateToFilename: (d: Date) => d.toISOString().replace(/[:.]/g, "-"),
- getLogFilePath: () => "/tmp/mock-log",
- attachErrorLogSink: () => {},
- getInMemoryErrors: () => [],
- loadErrorLogs: async () => [],
- getErrorLogByIndex: async () => null,
- captureAPIRequest: () => {},
- _resetErrorLogForTesting: () => {},
- }));
- const {
- normalizeQuotes,
- stripTrailingWhitespace,
- findActualString,
- preserveQuoteStyle,
- applyEditToFile,
- LEFT_SINGLE_CURLY_QUOTE,
- RIGHT_SINGLE_CURLY_QUOTE,
- LEFT_DOUBLE_CURLY_QUOTE,
- RIGHT_DOUBLE_CURLY_QUOTE,
- } = await import("../utils");
- // ─── normalizeQuotes ────────────────────────────────────────────────────
- describe("normalizeQuotes", () => {
- test("converts left single curly to straight", () => {
- expect(normalizeQuotes(`${LEFT_SINGLE_CURLY_QUOTE}hello`)).toBe("'hello");
- });
- test("converts right single curly to straight", () => {
- expect(normalizeQuotes(`hello${RIGHT_SINGLE_CURLY_QUOTE}`)).toBe("hello'");
- });
- test("converts left double curly to straight", () => {
- expect(normalizeQuotes(`${LEFT_DOUBLE_CURLY_QUOTE}hello`)).toBe('"hello');
- });
- test("converts right double curly to straight", () => {
- expect(normalizeQuotes(`hello${RIGHT_DOUBLE_CURLY_QUOTE}`)).toBe('hello"');
- });
- test("leaves straight quotes unchanged", () => {
- expect(normalizeQuotes("'hello' \"world\"")).toBe("'hello' \"world\"");
- });
- test("handles empty string", () => {
- expect(normalizeQuotes("")).toBe("");
- });
- });
- // ─── stripTrailingWhitespace ────────────────────────────────────────────
- describe("stripTrailingWhitespace", () => {
- test("strips trailing spaces from lines", () => {
- expect(stripTrailingWhitespace("hello \nworld ")).toBe("hello\nworld");
- });
- test("strips trailing tabs", () => {
- expect(stripTrailingWhitespace("hello\t\nworld\t")).toBe("hello\nworld");
- });
- test("preserves leading whitespace", () => {
- expect(stripTrailingWhitespace(" hello \n world ")).toBe(
- " hello\n world"
- );
- });
- test("handles empty string", () => {
- expect(stripTrailingWhitespace("")).toBe("");
- });
- test("handles CRLF line endings", () => {
- expect(stripTrailingWhitespace("hello \r\nworld ")).toBe(
- "hello\r\nworld"
- );
- });
- test("handles no trailing whitespace", () => {
- expect(stripTrailingWhitespace("hello\nworld")).toBe("hello\nworld");
- });
- });
- // ─── findActualString ───────────────────────────────────────────────────
- describe("findActualString", () => {
- test("finds exact match", () => {
- expect(findActualString("hello world", "hello")).toBe("hello");
- });
- test("finds match with curly quotes normalized", () => {
- const fileContent = `${LEFT_DOUBLE_CURLY_QUOTE}hello${RIGHT_DOUBLE_CURLY_QUOTE}`;
- const result = findActualString(fileContent, '"hello"');
- expect(result).not.toBeNull();
- });
- test("returns null when not found", () => {
- expect(findActualString("hello world", "xyz")).toBeNull();
- });
- test("returns null for empty search in non-empty content", () => {
- // Empty string is always found at index 0 via includes()
- const result = findActualString("hello", "");
- expect(result).toBe("");
- });
- });
- // ─── preserveQuoteStyle ─────────────────────────────────────────────────
- describe("preserveQuoteStyle", () => {
- test("returns newString unchanged when no normalization happened", () => {
- expect(preserveQuoteStyle("hello", "hello", "world")).toBe("world");
- });
- test("converts straight double quotes to curly in replacement", () => {
- const oldString = '"hello"';
- const actualOldString = `${LEFT_DOUBLE_CURLY_QUOTE}hello${RIGHT_DOUBLE_CURLY_QUOTE}`;
- const newString = '"world"';
- const result = preserveQuoteStyle(oldString, actualOldString, newString);
- expect(result).toContain(LEFT_DOUBLE_CURLY_QUOTE);
- expect(result).toContain(RIGHT_DOUBLE_CURLY_QUOTE);
- });
- });
- // ─── applyEditToFile ────────────────────────────────────────────────────
- describe("applyEditToFile", () => {
- test("replaces first occurrence by default", () => {
- expect(applyEditToFile("foo bar foo", "foo", "baz")).toBe("baz bar foo");
- });
- test("replaces all occurrences with replaceAll=true", () => {
- expect(applyEditToFile("foo bar foo", "foo", "baz", true)).toBe(
- "baz bar baz"
- );
- });
- test("handles deletion (empty newString) with trailing newline", () => {
- const result = applyEditToFile("line1\nline2\nline3\n", "line2", "");
- expect(result).toBe("line1\nline3\n");
- });
- test("handles deletion without trailing newline", () => {
- const result = applyEditToFile("foobar", "foo", "");
- expect(result).toBe("bar");
- });
- test("handles no match (returns original)", () => {
- expect(applyEditToFile("hello world", "xyz", "abc")).toBe("hello world");
- });
- test("handles empty original content with insertion", () => {
- expect(applyEditToFile("", "", "new content")).toBe("new content");
- });
- });
|