mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-18 22:35:51 +00:00
test: 添加一大堆测试文件
This commit is contained in:
@@ -1,201 +1,207 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { describe, expect, test } from 'bun:test'
|
||||
import {
|
||||
buildTool,
|
||||
toolMatchesName,
|
||||
findToolByName,
|
||||
getEmptyToolPermissionContext,
|
||||
filterToolProgressMessages,
|
||||
} from "../Tool";
|
||||
} from '../Tool'
|
||||
|
||||
// Minimal tool definition for testing buildTool
|
||||
function makeMinimalToolDef(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
name: "TestTool",
|
||||
inputSchema: { type: "object" as const } as any,
|
||||
name: 'TestTool',
|
||||
inputSchema: { type: 'object' as const } as any,
|
||||
maxResultSizeChars: 10000,
|
||||
call: async () => ({ data: "ok" }),
|
||||
description: async () => "A test tool",
|
||||
prompt: async () => "test prompt",
|
||||
mapToolResultToToolResultBlockParam: (content: unknown, toolUseID: string) => ({
|
||||
type: "tool_result" as const,
|
||||
call: async () => ({ data: 'ok' }),
|
||||
description: async () => 'A test tool',
|
||||
prompt: async () => 'test prompt',
|
||||
mapToolResultToToolResultBlockParam: (
|
||||
content: unknown,
|
||||
toolUseID: string,
|
||||
) => ({
|
||||
type: 'tool_result' as const,
|
||||
tool_use_id: toolUseID,
|
||||
content: String(content),
|
||||
}),
|
||||
renderToolUseMessage: () => null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
describe("buildTool", () => {
|
||||
test("fills in default isEnabled as true", () => {
|
||||
const tool = buildTool(makeMinimalToolDef());
|
||||
expect(tool.isEnabled()).toBe(true);
|
||||
});
|
||||
describe('buildTool', () => {
|
||||
test('fills in default isEnabled as true', () => {
|
||||
const tool = buildTool(makeMinimalToolDef())
|
||||
expect(tool.isEnabled()).toBe(true)
|
||||
})
|
||||
|
||||
test("fills in default isConcurrencySafe as false", () => {
|
||||
const tool = buildTool(makeMinimalToolDef());
|
||||
expect(tool.isConcurrencySafe({})).toBe(false);
|
||||
});
|
||||
test('fills in default isConcurrencySafe as false', () => {
|
||||
const tool = buildTool(makeMinimalToolDef())
|
||||
expect(tool.isConcurrencySafe({})).toBe(false)
|
||||
})
|
||||
|
||||
test("fills in default isReadOnly as false", () => {
|
||||
const tool = buildTool(makeMinimalToolDef());
|
||||
expect(tool.isReadOnly({})).toBe(false);
|
||||
});
|
||||
test('fills in default isReadOnly as false', () => {
|
||||
const tool = buildTool(makeMinimalToolDef())
|
||||
expect(tool.isReadOnly({})).toBe(false)
|
||||
})
|
||||
|
||||
test("fills in default isDestructive as false", () => {
|
||||
const tool = buildTool(makeMinimalToolDef());
|
||||
expect(tool.isDestructive!({})).toBe(false);
|
||||
});
|
||||
test('fills in default isDestructive as false', () => {
|
||||
const tool = buildTool(makeMinimalToolDef())
|
||||
expect(tool.isDestructive!({})).toBe(false)
|
||||
})
|
||||
|
||||
test("fills in default checkPermissions as allow", async () => {
|
||||
const tool = buildTool(makeMinimalToolDef());
|
||||
const input = { foo: "bar" };
|
||||
const result = await tool.checkPermissions(input, {} as any);
|
||||
expect(result).toEqual({ behavior: "allow", updatedInput: input });
|
||||
});
|
||||
test('fills in default checkPermissions as allow', async () => {
|
||||
const tool = buildTool(makeMinimalToolDef())
|
||||
const input = { foo: 'bar' }
|
||||
const result = await tool.checkPermissions(input, {} as any)
|
||||
expect(result).toEqual({ behavior: 'allow', updatedInput: input })
|
||||
})
|
||||
|
||||
test("fills in default userFacingName from tool name", () => {
|
||||
const tool = buildTool(makeMinimalToolDef());
|
||||
expect(tool.userFacingName(undefined)).toBe("TestTool");
|
||||
});
|
||||
test('fills in default userFacingName from tool name', () => {
|
||||
const tool = buildTool(makeMinimalToolDef())
|
||||
expect(tool.userFacingName(undefined)).toBe('TestTool')
|
||||
})
|
||||
|
||||
test("fills in default toAutoClassifierInput as empty string", () => {
|
||||
const tool = buildTool(makeMinimalToolDef());
|
||||
expect(tool.toAutoClassifierInput({})).toBe("");
|
||||
});
|
||||
test('fills in default toAutoClassifierInput as empty string', () => {
|
||||
const tool = buildTool(makeMinimalToolDef())
|
||||
expect(tool.toAutoClassifierInput({})).toBe('')
|
||||
})
|
||||
|
||||
test("preserves explicitly provided methods", () => {
|
||||
test('preserves explicitly provided methods', () => {
|
||||
const tool = buildTool(
|
||||
makeMinimalToolDef({
|
||||
isEnabled: () => false,
|
||||
isConcurrencySafe: () => true,
|
||||
isReadOnly: () => true,
|
||||
})
|
||||
);
|
||||
expect(tool.isEnabled()).toBe(false);
|
||||
expect(tool.isConcurrencySafe({})).toBe(true);
|
||||
expect(tool.isReadOnly({})).toBe(true);
|
||||
});
|
||||
}),
|
||||
)
|
||||
expect(tool.isEnabled()).toBe(false)
|
||||
expect(tool.isConcurrencySafe({})).toBe(true)
|
||||
expect(tool.isReadOnly({})).toBe(true)
|
||||
})
|
||||
|
||||
test("preserves all non-defaultable properties", () => {
|
||||
const tool = buildTool(makeMinimalToolDef());
|
||||
expect(tool.name).toBe("TestTool");
|
||||
expect(tool.maxResultSizeChars).toBe(10000);
|
||||
expect(typeof tool.call).toBe("function");
|
||||
expect(typeof tool.description).toBe("function");
|
||||
expect(typeof tool.prompt).toBe("function");
|
||||
});
|
||||
});
|
||||
test('preserves all non-defaultable properties', () => {
|
||||
const tool = buildTool(makeMinimalToolDef())
|
||||
expect(tool.name).toBe('TestTool')
|
||||
expect(tool.maxResultSizeChars).toBe(10000)
|
||||
expect(typeof tool.call).toBe('function')
|
||||
expect(typeof tool.description).toBe('function')
|
||||
expect(typeof tool.prompt).toBe('function')
|
||||
})
|
||||
})
|
||||
|
||||
describe("toolMatchesName", () => {
|
||||
test("returns true for exact name match", () => {
|
||||
expect(toolMatchesName({ name: "Bash" }, "Bash")).toBe(true);
|
||||
});
|
||||
describe('toolMatchesName', () => {
|
||||
test('returns true for exact name match', () => {
|
||||
expect(toolMatchesName({ name: 'Bash' }, 'Bash')).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false for non-matching name", () => {
|
||||
expect(toolMatchesName({ name: "Bash" }, "Read")).toBe(false);
|
||||
});
|
||||
test('returns false for non-matching name', () => {
|
||||
expect(toolMatchesName({ name: 'Bash' }, 'Read')).toBe(false)
|
||||
})
|
||||
|
||||
test("returns true when name matches an alias", () => {
|
||||
test('returns true when name matches an alias', () => {
|
||||
expect(
|
||||
toolMatchesName({ name: "Bash", aliases: ["BashTool", "Shell"] }, "BashTool")
|
||||
).toBe(true);
|
||||
});
|
||||
toolMatchesName(
|
||||
{ name: 'Bash', aliases: ['BashTool', 'Shell'] },
|
||||
'BashTool',
|
||||
),
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false when aliases is undefined", () => {
|
||||
expect(toolMatchesName({ name: "Bash" }, "BashTool")).toBe(false);
|
||||
});
|
||||
test('returns false when aliases is undefined', () => {
|
||||
expect(toolMatchesName({ name: 'Bash' }, 'BashTool')).toBe(false)
|
||||
})
|
||||
|
||||
test("returns false when aliases is empty", () => {
|
||||
expect(
|
||||
toolMatchesName({ name: "Bash", aliases: [] }, "BashTool")
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
test('returns false when aliases is empty', () => {
|
||||
expect(toolMatchesName({ name: 'Bash', aliases: [] }, 'BashTool')).toBe(
|
||||
false,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("findToolByName", () => {
|
||||
describe('findToolByName', () => {
|
||||
const mockTools = [
|
||||
buildTool(makeMinimalToolDef({ name: "Bash" })),
|
||||
buildTool(makeMinimalToolDef({ name: "Read", aliases: ["FileRead"] })),
|
||||
buildTool(makeMinimalToolDef({ name: "Edit" })),
|
||||
];
|
||||
buildTool(makeMinimalToolDef({ name: 'Bash' })),
|
||||
buildTool(makeMinimalToolDef({ name: 'Read', aliases: ['FileRead'] })),
|
||||
buildTool(makeMinimalToolDef({ name: 'Edit' })),
|
||||
]
|
||||
|
||||
test("finds tool by primary name", () => {
|
||||
const tool = findToolByName(mockTools, "Bash");
|
||||
expect(tool).toBeDefined();
|
||||
expect(tool!.name).toBe("Bash");
|
||||
});
|
||||
test('finds tool by primary name', () => {
|
||||
const tool = findToolByName(mockTools, 'Bash')
|
||||
expect(tool).toBeDefined()
|
||||
expect(tool!.name).toBe('Bash')
|
||||
})
|
||||
|
||||
test("finds tool by alias", () => {
|
||||
const tool = findToolByName(mockTools, "FileRead");
|
||||
expect(tool).toBeDefined();
|
||||
expect(tool!.name).toBe("Read");
|
||||
});
|
||||
test('finds tool by alias', () => {
|
||||
const tool = findToolByName(mockTools, 'FileRead')
|
||||
expect(tool).toBeDefined()
|
||||
expect(tool!.name).toBe('Read')
|
||||
})
|
||||
|
||||
test("returns undefined when no match", () => {
|
||||
expect(findToolByName(mockTools, "NonExistent")).toBeUndefined();
|
||||
});
|
||||
test('returns undefined when no match', () => {
|
||||
expect(findToolByName(mockTools, 'NonExistent')).toBeUndefined()
|
||||
})
|
||||
|
||||
test("returns first match when duplicates exist", () => {
|
||||
test('returns first match when duplicates exist', () => {
|
||||
const dupeTools = [
|
||||
buildTool(makeMinimalToolDef({ name: "Bash", maxResultSizeChars: 100 })),
|
||||
buildTool(makeMinimalToolDef({ name: "Bash", maxResultSizeChars: 200 })),
|
||||
];
|
||||
const tool = findToolByName(dupeTools, "Bash");
|
||||
expect(tool!.maxResultSizeChars).toBe(100);
|
||||
});
|
||||
});
|
||||
buildTool(makeMinimalToolDef({ name: 'Bash', maxResultSizeChars: 100 })),
|
||||
buildTool(makeMinimalToolDef({ name: 'Bash', maxResultSizeChars: 200 })),
|
||||
]
|
||||
const tool = findToolByName(dupeTools, 'Bash')
|
||||
expect(tool!.maxResultSizeChars).toBe(100)
|
||||
})
|
||||
})
|
||||
|
||||
describe("getEmptyToolPermissionContext", () => {
|
||||
test("returns default permission mode", () => {
|
||||
const ctx = getEmptyToolPermissionContext();
|
||||
expect(ctx.mode).toBe("default");
|
||||
});
|
||||
describe('getEmptyToolPermissionContext', () => {
|
||||
test('returns default permission mode', () => {
|
||||
const ctx = getEmptyToolPermissionContext()
|
||||
expect(ctx.mode).toBe('default')
|
||||
})
|
||||
|
||||
test("returns empty maps and arrays", () => {
|
||||
const ctx = getEmptyToolPermissionContext();
|
||||
expect(ctx.additionalWorkingDirectories.size).toBe(0);
|
||||
expect(ctx.alwaysAllowRules).toEqual({});
|
||||
expect(ctx.alwaysDenyRules).toEqual({});
|
||||
expect(ctx.alwaysAskRules).toEqual({});
|
||||
});
|
||||
test('returns empty maps and arrays', () => {
|
||||
const ctx = getEmptyToolPermissionContext()
|
||||
expect(ctx.additionalWorkingDirectories.size).toBe(0)
|
||||
expect(ctx.alwaysAllowRules).toEqual({})
|
||||
expect(ctx.alwaysDenyRules).toEqual({})
|
||||
expect(ctx.alwaysAskRules).toEqual({})
|
||||
})
|
||||
|
||||
test("returns isBypassPermissionsModeAvailable as false", () => {
|
||||
const ctx = getEmptyToolPermissionContext();
|
||||
expect(ctx.isBypassPermissionsModeAvailable).toBe(false);
|
||||
});
|
||||
});
|
||||
test('returns isBypassPermissionsModeAvailable as false', () => {
|
||||
const ctx = getEmptyToolPermissionContext()
|
||||
expect(ctx.isBypassPermissionsModeAvailable).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("filterToolProgressMessages", () => {
|
||||
test("filters out hook_progress messages", () => {
|
||||
describe('filterToolProgressMessages', () => {
|
||||
test('filters out hook_progress messages', () => {
|
||||
const messages = [
|
||||
{ data: { type: "hook_progress", hookName: "pre" } },
|
||||
{ data: { type: "tool_progress", toolName: "Bash" } },
|
||||
] as any[];
|
||||
const result = filterToolProgressMessages(messages);
|
||||
expect(result).toHaveLength(1);
|
||||
expect((result[0]!.data as any).type).toBe("tool_progress");
|
||||
});
|
||||
{ data: { type: 'hook_progress', hookName: 'pre' } },
|
||||
{ data: { type: 'tool_progress', toolName: 'Bash' } },
|
||||
] as any[]
|
||||
const result = filterToolProgressMessages(messages)
|
||||
expect(result).toHaveLength(1)
|
||||
expect((result[0]!.data as any).type).toBe('tool_progress')
|
||||
})
|
||||
|
||||
test("keeps tool progress messages", () => {
|
||||
test('keeps tool progress messages', () => {
|
||||
const messages = [
|
||||
{ data: { type: "tool_progress", toolName: "Bash" } },
|
||||
{ data: { type: "tool_progress", toolName: "Read" } },
|
||||
] as any[];
|
||||
const result = filterToolProgressMessages(messages);
|
||||
expect(result).toHaveLength(2);
|
||||
});
|
||||
{ data: { type: 'tool_progress', toolName: 'Bash' } },
|
||||
{ data: { type: 'tool_progress', toolName: 'Read' } },
|
||||
] as any[]
|
||||
const result = filterToolProgressMessages(messages)
|
||||
expect(result).toHaveLength(2)
|
||||
})
|
||||
|
||||
test("returns empty array for empty input", () => {
|
||||
expect(filterToolProgressMessages([])).toEqual([]);
|
||||
});
|
||||
test('returns empty array for empty input', () => {
|
||||
expect(filterToolProgressMessages([])).toEqual([])
|
||||
})
|
||||
|
||||
test("handles messages without type field", () => {
|
||||
test('handles messages without type field', () => {
|
||||
const messages = [
|
||||
{ data: { toolName: "Bash" } },
|
||||
{ data: { type: "hook_progress" } },
|
||||
] as any[];
|
||||
const result = filterToolProgressMessages(messages);
|
||||
expect(result).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
{ data: { toolName: 'Bash' } },
|
||||
{ data: { type: 'hook_progress' } },
|
||||
] as any[]
|
||||
const result = filterToolProgressMessages(messages)
|
||||
expect(result).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,167 +1,167 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { describe, expect, test } from 'bun:test'
|
||||
import {
|
||||
getPastedTextRefNumLines,
|
||||
formatPastedTextRef,
|
||||
formatImageRef,
|
||||
parseReferences,
|
||||
expandPastedTextRefs,
|
||||
} from "../history";
|
||||
} from '../history'
|
||||
|
||||
describe("getPastedTextRefNumLines", () => {
|
||||
test("returns 0 for single line (no newline)", () => {
|
||||
expect(getPastedTextRefNumLines("hello")).toBe(0);
|
||||
});
|
||||
describe('getPastedTextRefNumLines', () => {
|
||||
test('returns 0 for single line (no newline)', () => {
|
||||
expect(getPastedTextRefNumLines('hello')).toBe(0)
|
||||
})
|
||||
|
||||
test("counts LF newlines", () => {
|
||||
expect(getPastedTextRefNumLines("a\nb\nc")).toBe(2);
|
||||
});
|
||||
test('counts LF newlines', () => {
|
||||
expect(getPastedTextRefNumLines('a\nb\nc')).toBe(2)
|
||||
})
|
||||
|
||||
test("counts CRLF newlines", () => {
|
||||
expect(getPastedTextRefNumLines("a\r\nb")).toBe(1);
|
||||
});
|
||||
test('counts CRLF newlines', () => {
|
||||
expect(getPastedTextRefNumLines('a\r\nb')).toBe(1)
|
||||
})
|
||||
|
||||
test("counts CR newlines", () => {
|
||||
expect(getPastedTextRefNumLines("a\rb")).toBe(1);
|
||||
});
|
||||
test('counts CR newlines', () => {
|
||||
expect(getPastedTextRefNumLines('a\rb')).toBe(1)
|
||||
})
|
||||
|
||||
test("returns 0 for empty string", () => {
|
||||
expect(getPastedTextRefNumLines("")).toBe(0);
|
||||
});
|
||||
test('returns 0 for empty string', () => {
|
||||
expect(getPastedTextRefNumLines('')).toBe(0)
|
||||
})
|
||||
|
||||
test("trailing newline counts as one", () => {
|
||||
expect(getPastedTextRefNumLines("a\n")).toBe(1);
|
||||
});
|
||||
});
|
||||
test('trailing newline counts as one', () => {
|
||||
expect(getPastedTextRefNumLines('a\n')).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("formatPastedTextRef", () => {
|
||||
test("formats with lines count", () => {
|
||||
expect(formatPastedTextRef(1, 10)).toBe("[Pasted text #1 +10 lines]");
|
||||
});
|
||||
describe('formatPastedTextRef', () => {
|
||||
test('formats with lines count', () => {
|
||||
expect(formatPastedTextRef(1, 10)).toBe('[Pasted text #1 +10 lines]')
|
||||
})
|
||||
|
||||
test("formats without lines when 0", () => {
|
||||
expect(formatPastedTextRef(3, 0)).toBe("[Pasted text #3]");
|
||||
});
|
||||
test('formats without lines when 0', () => {
|
||||
expect(formatPastedTextRef(3, 0)).toBe('[Pasted text #3]')
|
||||
})
|
||||
|
||||
test("formats with large id", () => {
|
||||
expect(formatPastedTextRef(99, 5)).toBe("[Pasted text #99 +5 lines]");
|
||||
});
|
||||
});
|
||||
test('formats with large id', () => {
|
||||
expect(formatPastedTextRef(99, 5)).toBe('[Pasted text #99 +5 lines]')
|
||||
})
|
||||
})
|
||||
|
||||
describe("formatImageRef", () => {
|
||||
test("formats image reference", () => {
|
||||
expect(formatImageRef(1)).toBe("[Image #1]");
|
||||
});
|
||||
describe('formatImageRef', () => {
|
||||
test('formats image reference', () => {
|
||||
expect(formatImageRef(1)).toBe('[Image #1]')
|
||||
})
|
||||
|
||||
test("formats with large id", () => {
|
||||
expect(formatImageRef(42)).toBe("[Image #42]");
|
||||
});
|
||||
});
|
||||
test('formats with large id', () => {
|
||||
expect(formatImageRef(42)).toBe('[Image #42]')
|
||||
})
|
||||
})
|
||||
|
||||
describe("parseReferences", () => {
|
||||
test("parses Pasted text ref", () => {
|
||||
const refs = parseReferences("[Pasted text #1 +5 lines]");
|
||||
expect(refs).toHaveLength(1);
|
||||
describe('parseReferences', () => {
|
||||
test('parses Pasted text ref', () => {
|
||||
const refs = parseReferences('[Pasted text #1 +5 lines]')
|
||||
expect(refs).toHaveLength(1)
|
||||
expect(refs[0]).toEqual({
|
||||
id: 1,
|
||||
match: "[Pasted text #1 +5 lines]",
|
||||
match: '[Pasted text #1 +5 lines]',
|
||||
index: 0,
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
test("parses Image ref", () => {
|
||||
const refs = parseReferences("[Image #2]");
|
||||
expect(refs).toHaveLength(1);
|
||||
expect(refs[0]!.id).toBe(2);
|
||||
});
|
||||
test('parses Image ref', () => {
|
||||
const refs = parseReferences('[Image #2]')
|
||||
expect(refs).toHaveLength(1)
|
||||
expect(refs[0]!.id).toBe(2)
|
||||
})
|
||||
|
||||
test("parses Truncated text ref", () => {
|
||||
const refs = parseReferences("[...Truncated text #3]");
|
||||
expect(refs).toHaveLength(1);
|
||||
expect(refs[0]!.id).toBe(3);
|
||||
});
|
||||
test('parses Truncated text ref', () => {
|
||||
const refs = parseReferences('[...Truncated text #3]')
|
||||
expect(refs).toHaveLength(1)
|
||||
expect(refs[0]!.id).toBe(3)
|
||||
})
|
||||
|
||||
test("parses Pasted text without line count", () => {
|
||||
const refs = parseReferences("[Pasted text #4]");
|
||||
expect(refs).toHaveLength(1);
|
||||
expect(refs[0]!.id).toBe(4);
|
||||
});
|
||||
test('parses Pasted text without line count', () => {
|
||||
const refs = parseReferences('[Pasted text #4]')
|
||||
expect(refs).toHaveLength(1)
|
||||
expect(refs[0]!.id).toBe(4)
|
||||
})
|
||||
|
||||
test("parses multiple refs", () => {
|
||||
const refs = parseReferences("hello [Pasted text #1] world [Image #2]");
|
||||
expect(refs).toHaveLength(2);
|
||||
expect(refs[0]!.id).toBe(1);
|
||||
expect(refs[1]!.id).toBe(2);
|
||||
});
|
||||
test('parses multiple refs', () => {
|
||||
const refs = parseReferences('hello [Pasted text #1] world [Image #2]')
|
||||
expect(refs).toHaveLength(2)
|
||||
expect(refs[0]!.id).toBe(1)
|
||||
expect(refs[1]!.id).toBe(2)
|
||||
})
|
||||
|
||||
test("returns empty for no refs", () => {
|
||||
expect(parseReferences("plain text")).toEqual([]);
|
||||
});
|
||||
test('returns empty for no refs', () => {
|
||||
expect(parseReferences('plain text')).toEqual([])
|
||||
})
|
||||
|
||||
test("filters out id 0", () => {
|
||||
const refs = parseReferences("[Pasted text #0]");
|
||||
expect(refs).toHaveLength(0);
|
||||
});
|
||||
test('filters out id 0', () => {
|
||||
const refs = parseReferences('[Pasted text #0]')
|
||||
expect(refs).toHaveLength(0)
|
||||
})
|
||||
|
||||
test("captures correct index for embedded refs", () => {
|
||||
const input = "prefix [Pasted text #1] suffix";
|
||||
const refs = parseReferences(input);
|
||||
expect(refs[0]!.index).toBe(7);
|
||||
});
|
||||
test('captures correct index for embedded refs', () => {
|
||||
const input = 'prefix [Pasted text #1] suffix'
|
||||
const refs = parseReferences(input)
|
||||
expect(refs[0]!.index).toBe(7)
|
||||
})
|
||||
|
||||
test("handles duplicate refs", () => {
|
||||
const refs = parseReferences("[Pasted text #1] and [Pasted text #1]");
|
||||
expect(refs).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
test('handles duplicate refs', () => {
|
||||
const refs = parseReferences('[Pasted text #1] and [Pasted text #1]')
|
||||
expect(refs).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe("expandPastedTextRefs", () => {
|
||||
test("replaces single text ref", () => {
|
||||
const input = "look at [Pasted text #1 +2 lines]";
|
||||
describe('expandPastedTextRefs', () => {
|
||||
test('replaces single text ref', () => {
|
||||
const input = 'look at [Pasted text #1 +2 lines]'
|
||||
const pastedContents = {
|
||||
1: { id: 1, type: "text" as const, content: "line1\nline2\nline3" },
|
||||
};
|
||||
const result = expandPastedTextRefs(input, pastedContents);
|
||||
expect(result).toBe("look at line1\nline2\nline3");
|
||||
});
|
||||
1: { id: 1, type: 'text' as const, content: 'line1\nline2\nline3' },
|
||||
}
|
||||
const result = expandPastedTextRefs(input, pastedContents)
|
||||
expect(result).toBe('look at line1\nline2\nline3')
|
||||
})
|
||||
|
||||
test("replaces multiple text refs in reverse order", () => {
|
||||
const input = "[Pasted text #1] and [Pasted text #2]";
|
||||
test('replaces multiple text refs in reverse order', () => {
|
||||
const input = '[Pasted text #1] and [Pasted text #2]'
|
||||
const pastedContents = {
|
||||
1: { id: 1, type: "text" as const, content: "AAA" },
|
||||
2: { id: 2, type: "text" as const, content: "BBB" },
|
||||
};
|
||||
const result = expandPastedTextRefs(input, pastedContents);
|
||||
expect(result).toBe("AAA and BBB");
|
||||
});
|
||||
1: { id: 1, type: 'text' as const, content: 'AAA' },
|
||||
2: { id: 2, type: 'text' as const, content: 'BBB' },
|
||||
}
|
||||
const result = expandPastedTextRefs(input, pastedContents)
|
||||
expect(result).toBe('AAA and BBB')
|
||||
})
|
||||
|
||||
test("does not replace image refs", () => {
|
||||
const input = "[Image #1]";
|
||||
test('does not replace image refs', () => {
|
||||
const input = '[Image #1]'
|
||||
const pastedContents = {
|
||||
1: { id: 1, type: "image" as const, content: "data" },
|
||||
};
|
||||
const result = expandPastedTextRefs(input, pastedContents);
|
||||
expect(result).toBe("[Image #1]");
|
||||
});
|
||||
1: { id: 1, type: 'image' as const, content: 'data' },
|
||||
}
|
||||
const result = expandPastedTextRefs(input, pastedContents)
|
||||
expect(result).toBe('[Image #1]')
|
||||
})
|
||||
|
||||
test("returns original when no refs", () => {
|
||||
const input = "no refs here";
|
||||
const result = expandPastedTextRefs(input, {});
|
||||
expect(result).toBe("no refs here");
|
||||
});
|
||||
test('returns original when no refs', () => {
|
||||
const input = 'no refs here'
|
||||
const result = expandPastedTextRefs(input, {})
|
||||
expect(result).toBe('no refs here')
|
||||
})
|
||||
|
||||
test("skips refs with no matching pasted content", () => {
|
||||
const input = "[Pasted text #99 +1 lines]";
|
||||
const result = expandPastedTextRefs(input, {});
|
||||
expect(result).toBe("[Pasted text #99 +1 lines]");
|
||||
});
|
||||
test('skips refs with no matching pasted content', () => {
|
||||
const input = '[Pasted text #99 +1 lines]'
|
||||
const result = expandPastedTextRefs(input, {})
|
||||
expect(result).toBe('[Pasted text #99 +1 lines]')
|
||||
})
|
||||
|
||||
test("handles mixed content", () => {
|
||||
const input = "see [Pasted text #1] and [Image #2]";
|
||||
test('handles mixed content', () => {
|
||||
const input = 'see [Pasted text #1] and [Image #2]'
|
||||
const pastedContents = {
|
||||
1: { id: 1, type: "text" as const, content: "code here" },
|
||||
2: { id: 2, type: "image" as const, content: "img data" },
|
||||
};
|
||||
const result = expandPastedTextRefs(input, pastedContents);
|
||||
expect(result).toBe("see code here and [Image #2]");
|
||||
});
|
||||
});
|
||||
1: { id: 1, type: 'text' as const, content: 'code here' },
|
||||
2: { id: 2, type: 'image' as const, content: 'img data' },
|
||||
}
|
||||
const result = expandPastedTextRefs(input, pastedContents)
|
||||
expect(result).toBe('see code here and [Image #2]')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,82 +1,85 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { parseToolPreset, filterToolsByDenyRules } from "../tools";
|
||||
import { getEmptyToolPermissionContext } from "../Tool";
|
||||
import { describe, expect, test } from 'bun:test'
|
||||
import { parseToolPreset, filterToolsByDenyRules } from '../tools'
|
||||
import { getEmptyToolPermissionContext } from '../Tool'
|
||||
|
||||
describe("parseToolPreset", () => {
|
||||
describe('parseToolPreset', () => {
|
||||
test('returns "default" for "default" input', () => {
|
||||
expect(parseToolPreset("default")).toBe("default");
|
||||
});
|
||||
expect(parseToolPreset('default')).toBe('default')
|
||||
})
|
||||
|
||||
test('returns "default" for "Default" input (case-insensitive)', () => {
|
||||
expect(parseToolPreset("Default")).toBe("default");
|
||||
});
|
||||
expect(parseToolPreset('Default')).toBe('default')
|
||||
})
|
||||
|
||||
test("returns null for unknown preset", () => {
|
||||
expect(parseToolPreset("unknown")).toBeNull();
|
||||
});
|
||||
test('returns null for unknown preset', () => {
|
||||
expect(parseToolPreset('unknown')).toBeNull()
|
||||
})
|
||||
|
||||
test("returns null for empty string", () => {
|
||||
expect(parseToolPreset("")).toBeNull();
|
||||
});
|
||||
test('returns null for empty string', () => {
|
||||
expect(parseToolPreset('')).toBeNull()
|
||||
})
|
||||
|
||||
test("returns null for random string", () => {
|
||||
expect(parseToolPreset("custom-preset")).toBeNull();
|
||||
});
|
||||
});
|
||||
test('returns null for random string', () => {
|
||||
expect(parseToolPreset('custom-preset')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
// ─── filterToolsByDenyRules ─────────────────────────────────────────────
|
||||
|
||||
describe("filterToolsByDenyRules", () => {
|
||||
describe('filterToolsByDenyRules', () => {
|
||||
const mockTools = [
|
||||
{ name: "Bash", mcpInfo: undefined },
|
||||
{ name: "Read", mcpInfo: undefined },
|
||||
{ name: "Write", mcpInfo: undefined },
|
||||
{ name: "mcp__server__tool", mcpInfo: { serverName: "server", toolName: "tool" } },
|
||||
];
|
||||
{ name: 'Bash', mcpInfo: undefined },
|
||||
{ name: 'Read', mcpInfo: undefined },
|
||||
{ name: 'Write', mcpInfo: undefined },
|
||||
{
|
||||
name: 'mcp__server__tool',
|
||||
mcpInfo: { serverName: 'server', toolName: 'tool' },
|
||||
},
|
||||
]
|
||||
|
||||
test("returns all tools when no deny rules", () => {
|
||||
const ctx = getEmptyToolPermissionContext();
|
||||
const result = filterToolsByDenyRules(mockTools, ctx);
|
||||
expect(result).toHaveLength(4);
|
||||
});
|
||||
test('returns all tools when no deny rules', () => {
|
||||
const ctx = getEmptyToolPermissionContext()
|
||||
const result = filterToolsByDenyRules(mockTools, ctx)
|
||||
expect(result).toHaveLength(4)
|
||||
})
|
||||
|
||||
test("filters out denied tool by name", () => {
|
||||
test('filters out denied tool by name', () => {
|
||||
const ctx = {
|
||||
...getEmptyToolPermissionContext(),
|
||||
alwaysDenyRules: {
|
||||
localSettings: ["Bash"],
|
||||
localSettings: ['Bash'],
|
||||
},
|
||||
};
|
||||
const result = filterToolsByDenyRules(mockTools, ctx as any);
|
||||
expect(result.find((t) => t.name === "Bash")).toBeUndefined();
|
||||
expect(result).toHaveLength(3);
|
||||
});
|
||||
}
|
||||
const result = filterToolsByDenyRules(mockTools, ctx as any)
|
||||
expect(result.find(t => t.name === 'Bash')).toBeUndefined()
|
||||
expect(result).toHaveLength(3)
|
||||
})
|
||||
|
||||
test("filters out multiple denied tools", () => {
|
||||
test('filters out multiple denied tools', () => {
|
||||
const ctx = {
|
||||
...getEmptyToolPermissionContext(),
|
||||
alwaysDenyRules: {
|
||||
localSettings: ["Bash", "Write"],
|
||||
localSettings: ['Bash', 'Write'],
|
||||
},
|
||||
};
|
||||
const result = filterToolsByDenyRules(mockTools, ctx as any);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result.map((t) => t.name)).toEqual(["Read", "mcp__server__tool"]);
|
||||
});
|
||||
}
|
||||
const result = filterToolsByDenyRules(mockTools, ctx as any)
|
||||
expect(result).toHaveLength(2)
|
||||
expect(result.map(t => t.name)).toEqual(['Read', 'mcp__server__tool'])
|
||||
})
|
||||
|
||||
test("returns empty array when all tools denied", () => {
|
||||
test('returns empty array when all tools denied', () => {
|
||||
const ctx = {
|
||||
...getEmptyToolPermissionContext(),
|
||||
alwaysDenyRules: {
|
||||
localSettings: mockTools.map((t) => t.name),
|
||||
localSettings: mockTools.map(t => t.name),
|
||||
},
|
||||
};
|
||||
const result = filterToolsByDenyRules(mockTools, ctx as any);
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
}
|
||||
const result = filterToolsByDenyRules(mockTools, ctx as any)
|
||||
expect(result).toHaveLength(0)
|
||||
})
|
||||
|
||||
test("handles empty tools array", () => {
|
||||
const ctx = getEmptyToolPermissionContext();
|
||||
expect(filterToolsByDenyRules([], ctx)).toEqual([]);
|
||||
});
|
||||
});
|
||||
test('handles empty tools array', () => {
|
||||
const ctx = getEmptyToolPermissionContext()
|
||||
expect(filterToolsByDenyRules([], ctx)).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user