| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- import { isToolAllowedForMode, FileRestrictionError, ModeConfig } from "../modes"
- describe("isToolAllowedForMode", () => {
- const customModes: ModeConfig[] = [
- {
- slug: "markdown-editor",
- name: "Markdown Editor",
- roleDefinition: "You are a markdown editor",
- groups: ["read", ["edit", { fileRegex: "\\.md$" }], "browser"],
- },
- {
- slug: "css-editor",
- name: "CSS Editor",
- roleDefinition: "You are a CSS editor",
- groups: ["read", ["edit", { fileRegex: "\\.css$" }], "browser"],
- },
- {
- slug: "test-exp-mode",
- name: "Test Exp Mode",
- roleDefinition: "You are an experimental tester",
- groups: ["read", "edit", "browser"],
- },
- ]
- it("allows always available tools", () => {
- expect(isToolAllowedForMode("ask_followup_question", "markdown-editor", customModes)).toBe(true)
- expect(isToolAllowedForMode("attempt_completion", "markdown-editor", customModes)).toBe(true)
- })
- it("allows unrestricted tools", () => {
- expect(isToolAllowedForMode("read_file", "markdown-editor", customModes)).toBe(true)
- expect(isToolAllowedForMode("browser_action", "markdown-editor", customModes)).toBe(true)
- })
- describe("file restrictions", () => {
- it("allows editing matching files", () => {
- // Test markdown editor mode
- const mdResult = isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, {
- path: "test.md",
- content: "# Test",
- })
- expect(mdResult).toBe(true)
- // Test CSS editor mode
- const cssResult = isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, {
- path: "styles.css",
- content: ".test { color: red; }",
- })
- expect(cssResult).toBe(true)
- })
- it("rejects editing non-matching files", () => {
- // Test markdown editor mode with non-markdown file
- expect(() =>
- isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, {
- path: "test.js",
- content: "console.log('test')",
- }),
- ).toThrow(FileRestrictionError)
- expect(() =>
- isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, {
- path: "test.js",
- content: "console.log('test')",
- }),
- ).toThrow(/\\.md\$/)
- // Test CSS editor mode with non-CSS file
- expect(() =>
- isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, {
- path: "test.js",
- content: "console.log('test')",
- }),
- ).toThrow(FileRestrictionError)
- expect(() =>
- isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, {
- path: "test.js",
- content: "console.log('test')",
- }),
- ).toThrow(/\\.css\$/)
- })
- it("handles partial streaming cases (path only, no content/diff)", () => {
- // Should allow path-only for matching files (no validation yet since content/diff not provided)
- expect(
- isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, {
- path: "test.js",
- }),
- ).toBe(true)
- expect(
- isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, {
- path: "test.js",
- }),
- ).toBe(true)
- // Should allow path-only for ask mode too
- expect(
- isToolAllowedForMode("write_to_file", "ask", [], undefined, {
- path: "test.js",
- }),
- ).toBe(true)
- })
- it("applies restrictions to both write_to_file and apply_diff", () => {
- // Test write_to_file
- const writeResult = isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, {
- path: "test.md",
- content: "# Test",
- })
- expect(writeResult).toBe(true)
- // Test apply_diff
- const diffResult = isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, {
- path: "test.md",
- diff: "- old\n+ new",
- })
- expect(diffResult).toBe(true)
- // Test both with non-matching file
- expect(() =>
- isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, {
- path: "test.js",
- content: "console.log('test')",
- }),
- ).toThrow(FileRestrictionError)
- expect(() =>
- isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, {
- path: "test.js",
- diff: "- old\n+ new",
- }),
- ).toThrow(FileRestrictionError)
- })
- it("uses description in file restriction error for custom modes", () => {
- const customModesWithDescription: ModeConfig[] = [
- {
- slug: "docs-editor",
- name: "Documentation Editor",
- roleDefinition: "You are a documentation editor",
- groups: [
- "read",
- ["edit", { fileRegex: "\\.(md|txt)$", description: "Documentation files only" }],
- "browser",
- ],
- },
- ]
- // Test write_to_file with non-matching file
- expect(() =>
- isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, {
- path: "test.js",
- content: "console.log('test')",
- }),
- ).toThrow(FileRestrictionError)
- expect(() =>
- isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, {
- path: "test.js",
- content: "console.log('test')",
- }),
- ).toThrow(/Documentation files only/)
- // Test apply_diff with non-matching file
- expect(() =>
- isToolAllowedForMode("apply_diff", "docs-editor", customModesWithDescription, undefined, {
- path: "test.js",
- diff: "- old\n+ new",
- }),
- ).toThrow(FileRestrictionError)
- expect(() =>
- isToolAllowedForMode("apply_diff", "docs-editor", customModesWithDescription, undefined, {
- path: "test.js",
- diff: "- old\n+ new",
- }),
- ).toThrow(/Documentation files only/)
- // Test that matching files are allowed
- expect(
- isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, {
- path: "test.md",
- content: "# Test",
- }),
- ).toBe(true)
- expect(
- isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, {
- path: "test.txt",
- content: "Test content",
- }),
- ).toBe(true)
- // Test partial streaming cases
- expect(
- isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, {
- path: "test.js",
- }),
- ).toBe(true)
- })
- it("allows ask mode to edit markdown files only", () => {
- // Should allow editing markdown files
- expect(
- isToolAllowedForMode("write_to_file", "ask", [], undefined, {
- path: "test.md",
- content: "# Test",
- }),
- ).toBe(true)
- // Should allow applying diffs to markdown files
- expect(
- isToolAllowedForMode("apply_diff", "ask", [], undefined, {
- path: "readme.md",
- diff: "- old\n+ new",
- }),
- ).toBe(true)
- // Should reject non-markdown files
- expect(() =>
- isToolAllowedForMode("write_to_file", "ask", [], undefined, {
- path: "test.js",
- content: "console.log('test')",
- }),
- ).toThrow(FileRestrictionError)
- expect(() =>
- isToolAllowedForMode("write_to_file", "ask", [], undefined, {
- path: "test.js",
- content: "console.log('test')",
- }),
- ).toThrow(/Markdown files only/)
- // Should maintain read capabilities
- expect(isToolAllowedForMode("read_file", "ask", [])).toBe(true)
- expect(isToolAllowedForMode("browser_action", "ask", [])).toBe(true)
- expect(isToolAllowedForMode("use_mcp_tool", "ask", [])).toBe(true)
- })
- })
- it("handles non-existent modes", () => {
- expect(isToolAllowedForMode("write_to_file", "non-existent", customModes)).toBe(false)
- })
- it("respects tool requirements", () => {
- const toolRequirements = {
- write_to_file: false,
- }
- expect(isToolAllowedForMode("write_to_file", "markdown-editor", customModes, toolRequirements)).toBe(false)
- })
- describe("experimental tools", () => {
- it("disables tools when experiment is disabled", () => {
- const experiments = {
- search_and_replace: false,
- insert_code_block: false,
- }
- expect(
- isToolAllowedForMode(
- "search_and_replace",
- "test-exp-mode",
- customModes,
- undefined,
- undefined,
- experiments,
- ),
- ).toBe(false)
- expect(
- isToolAllowedForMode(
- "insert_code_block",
- "test-exp-mode",
- customModes,
- undefined,
- undefined,
- experiments,
- ),
- ).toBe(false)
- })
- it("allows tools when experiment is enabled", () => {
- const experiments = {
- search_and_replace: true,
- insert_code_block: true,
- }
- expect(
- isToolAllowedForMode(
- "search_and_replace",
- "test-exp-mode",
- customModes,
- undefined,
- undefined,
- experiments,
- ),
- ).toBe(true)
- expect(
- isToolAllowedForMode(
- "insert_code_block",
- "test-exp-mode",
- customModes,
- undefined,
- undefined,
- experiments,
- ),
- ).toBe(true)
- })
- it("allows non-experimental tools when experiments are disabled", () => {
- const experiments = {
- search_and_replace: false,
- insert_code_block: false,
- }
- expect(
- isToolAllowedForMode("read_file", "markdown-editor", customModes, undefined, undefined, experiments),
- ).toBe(true)
- expect(
- isToolAllowedForMode(
- "write_to_file",
- "markdown-editor",
- customModes,
- undefined,
- { path: "test.md" },
- experiments,
- ),
- ).toBe(true)
- })
- })
- })
- describe("FileRestrictionError", () => {
- it("formats error message with pattern when no description provided", () => {
- const error = new FileRestrictionError("Markdown Editor", "\\.md$", undefined, "test.js")
- expect(error.message).toBe(
- "This mode (Markdown Editor) can only edit files matching pattern: \\.md$. Got: test.js",
- )
- expect(error.name).toBe("FileRestrictionError")
- })
- it("formats error message with description when provided", () => {
- const error = new FileRestrictionError("Markdown Editor", "\\.md$", "Markdown files only", "test.js")
- expect(error.message).toBe(
- "This mode (Markdown Editor) can only edit files matching pattern: \\.md$ (Markdown files only). Got: test.js",
- )
- expect(error.name).toBe("FileRestrictionError")
- })
- })
|