/ src / tests / arg-parser.test.ts
arg-parser.test.ts
  1  /**
  2   * Test script for argument parsing enhancement
  3   * Tests both comma-separated and array element formats for multi-value arguments
  4   */
  5  
  6  import { jest } from "@jest/globals";
  7  
  8  // Mock console.error to suppress output during tests
  9  const originalConsoleError = console.error;
 10  beforeAll(() => {
 11    console.error = jest.fn();
 12  });
 13  
 14  afterAll(() => {
 15    console.error = originalConsoleError;
 16  });
 17  
 18  describe("Argument Parser Enhancement", () => {
 19    // Helper function to simulate argument parsing
 20    function parseArguments(args: string[]) {
 21      const approvedFoldersFromArgs: string[] = [];
 22      const ignoredFolders: string[] = [];
 23      const enabledToolCategories: string[] = [];
 24      const enabledTools: string[] = [];
 25      const approvedCommandsFromArgs: string[] = [];
 26  
 27      let parsingIgnoredFolders = false;
 28      let parsingEnabledToolCategories = false;
 29      let parsingEnabledTools = false;
 30      let parsingApprovedFolders = false;
 31      let parsingApprovedCommands = false;
 32  
 33      for (let i = 0; i < args.length; i++) {
 34        const arg = args[i];
 35  
 36        if (arg === "--approved-folders") {
 37          parsingApprovedFolders = true;
 38          parsingIgnoredFolders = false;
 39          parsingEnabledToolCategories = false;
 40          parsingEnabledTools = false;
 41          parsingApprovedCommands = false;
 42  
 43          // Look ahead to collect all non-flag arguments
 44          const folders: string[] = [];
 45          while (i + 1 < args.length && !args[i + 1].startsWith("--")) {
 46            folders.push(args[i + 1]);
 47            i++;
 48          }
 49  
 50          // Also support comma-separated format for backward compatibility
 51          const parsed = folders.flatMap((f) =>
 52            f
 53              .split(",")
 54              .map((dir) => dir.trim())
 55              .filter((dir) => dir.length > 0)
 56          );
 57          approvedFoldersFromArgs.push(...parsed);
 58  
 59          continue;
 60        }
 61  
 62        if (arg === "--ignored-folders") {
 63          parsingIgnoredFolders = true;
 64          parsingApprovedFolders = false;
 65          parsingEnabledToolCategories = false;
 66          parsingEnabledTools = false;
 67          parsingApprovedCommands = false;
 68  
 69          // Look ahead to collect all non-flag arguments
 70          const folders: string[] = [];
 71          while (i + 1 < args.length && !args[i + 1].startsWith("--")) {
 72            folders.push(args[i + 1]);
 73            i++;
 74          }
 75  
 76          // Also support comma-separated format for backward compatibility
 77          const parsed = folders.flatMap((f) =>
 78            f
 79              .split(",")
 80              .map((dir) => dir.trim())
 81              .filter((dir) => dir.length > 0)
 82          );
 83          ignoredFolders.push(...parsed);
 84  
 85          continue;
 86        }
 87  
 88        if (arg === "--enabled-tool-categories") {
 89          parsingEnabledToolCategories = true;
 90          parsingIgnoredFolders = false;
 91          parsingApprovedFolders = false;
 92          parsingEnabledTools = false;
 93          parsingApprovedCommands = false;
 94  
 95          // Look ahead to collect all non-flag arguments
 96          const categories: string[] = [];
 97          while (i + 1 < args.length && !args[i + 1].startsWith("--")) {
 98            categories.push(args[i + 1]);
 99            i++;
100          }
101  
102          // Also support comma-separated format for backward compatibility
103          const parsed = categories.flatMap((c) =>
104            c
105              .split(",")
106              .map((cat) => cat.trim())
107              .filter((cat) => cat.length > 0)
108          );
109          enabledToolCategories.push(...parsed);
110  
111          continue;
112        }
113  
114        if (arg === "--enabled-tools") {
115          parsingEnabledTools = true;
116          parsingIgnoredFolders = false;
117          parsingApprovedFolders = false;
118          parsingEnabledToolCategories = false;
119          parsingApprovedCommands = false;
120  
121          // Look ahead to collect all non-flag arguments
122          const tools: string[] = [];
123          while (i + 1 < args.length && !args[i + 1].startsWith("--")) {
124            tools.push(args[i + 1]);
125            i++;
126          }
127  
128          // Also support comma-separated format for backward compatibility
129          const parsed = tools.flatMap((t) =>
130            t
131              .split(",")
132              .map((tool) => tool.trim())
133              .filter((tool) => tool.length > 0)
134          );
135          enabledTools.push(...parsed);
136  
137          continue;
138        }
139  
140        if (arg === "--approved-commands") {
141          parsingApprovedCommands = true;
142          parsingIgnoredFolders = false;
143          parsingApprovedFolders = false;
144          parsingEnabledToolCategories = false;
145          parsingEnabledTools = false;
146  
147          // Look ahead to collect all non-flag arguments
148          const commands: string[] = [];
149          while (i + 1 < args.length && !args[i + 1].startsWith("--")) {
150            commands.push(args[i + 1]);
151            i++;
152          }
153  
154          // Also support comma-separated format for backward compatibility
155          const parsed = commands.flatMap((c) =>
156            c
157              .split(",")
158              .map((cmd) => cmd.trim())
159              .filter((cmd) => cmd.length > 0)
160          );
161          approvedCommandsFromArgs.push(...parsed);
162  
163          continue;
164        }
165  
166        // Reset parsing flags
167        parsingIgnoredFolders = false;
168        parsingApprovedFolders = false;
169        parsingEnabledToolCategories = false;
170        parsingEnabledTools = false;
171        parsingApprovedCommands = false;
172      }
173  
174      return {
175        approvedFoldersFromArgs,
176        ignoredFolders,
177        enabledToolCategories,
178        enabledTools,
179        approvedCommandsFromArgs,
180      };
181    }
182  
183    describe("--approved-folders", () => {
184      test("should parse comma-separated string (backward compatibility)", () => {
185        const args = ["--approved-folders", "dir1,dir2,dir3"];
186        const result = parseArguments(args);
187        expect(result.approvedFoldersFromArgs).toEqual(["dir1", "dir2", "dir3"]);
188      });
189  
190      test("should parse separate array elements (new format)", () => {
191        const args = ["--approved-folders", "dir1", "dir2", "dir3"];
192        const result = parseArguments(args);
193        expect(result.approvedFoldersFromArgs).toEqual(["dir1", "dir2", "dir3"]);
194      });
195  
196      test("should parse mixed format", () => {
197        const args = ["--approved-folders", "dir1,dir2", "dir3"];
198        const result = parseArguments(args);
199        expect(result.approvedFoldersFromArgs).toEqual(["dir1", "dir2", "dir3"]);
200      });
201  
202      test("should parse single directory", () => {
203        const args = ["--approved-folders", "dir1"];
204        const result = parseArguments(args);
205        expect(result.approvedFoldersFromArgs).toEqual(["dir1"]);
206      });
207  
208      test("should ignore empty strings", () => {
209        const args = ["--approved-folders", "", "dir1", "  ", "dir2"];
210        const result = parseArguments(args);
211        expect(result.approvedFoldersFromArgs).toEqual(["dir1", "dir2"]);
212      });
213  
214      test("should handle paths with spaces (when properly quoted)", () => {
215        const args = [
216          "--approved-folders",
217          "C:/path with spaces/dir1",
218          "C:/path with spaces/dir2",
219        ];
220        const result = parseArguments(args);
221        expect(result.approvedFoldersFromArgs).toEqual([
222          "C:/path with spaces/dir1",
223          "C:/path with spaces/dir2",
224        ]);
225      });
226  
227      test("should stop at next flag", () => {
228        const args = [
229          "--approved-folders",
230          "dir1",
231          "dir2",
232          "--ignored-folders",
233          "node_modules",
234        ];
235        const result = parseArguments(args);
236        expect(result.approvedFoldersFromArgs).toEqual(["dir1", "dir2"]);
237        expect(result.ignoredFolders).toEqual(["node_modules"]);
238      });
239    });
240  
241    describe("--ignored-folders", () => {
242      test("should parse comma-separated string", () => {
243        const args = ["--ignored-folders", "node_modules,dist,.git"];
244        const result = parseArguments(args);
245        expect(result.ignoredFolders).toEqual(["node_modules", "dist", ".git"]);
246      });
247  
248      test("should parse array elements", () => {
249        const args = ["--ignored-folders", "node_modules", "dist", ".git"];
250        const result = parseArguments(args);
251        expect(result.ignoredFolders).toEqual(["node_modules", "dist", ".git"]);
252      });
253  
254      test("should parse mixed format", () => {
255        const args = ["--ignored-folders", "node_modules,dist", ".git"];
256        const result = parseArguments(args);
257        expect(result.ignoredFolders).toEqual(["node_modules", "dist", ".git"]);
258      });
259    });
260  
261    describe("--enabled-tool-categories", () => {
262      test("should parse comma-separated string", () => {
263        const args = ["--enabled-tool-categories", "read,write,filesystem"];
264        const result = parseArguments(args);
265        expect(result.enabledToolCategories).toEqual([
266          "read",
267          "write",
268          "filesystem",
269        ]);
270      });
271  
272      test("should parse array elements", () => {
273        const args = ["--enabled-tool-categories", "read", "write", "filesystem"];
274        const result = parseArguments(args);
275        expect(result.enabledToolCategories).toEqual([
276          "read",
277          "write",
278          "filesystem",
279        ]);
280      });
281  
282      test("should parse mixed format", () => {
283        const args = ["--enabled-tool-categories", "read,write", "filesystem"];
284        const result = parseArguments(args);
285        expect(result.enabledToolCategories).toEqual([
286          "read",
287          "write",
288          "filesystem",
289        ]);
290      });
291    });
292  
293    describe("--enabled-tools", () => {
294      test("should parse comma-separated string", () => {
295        const args = ["--enabled-tools", "read_file,write_file,list_directory"];
296        const result = parseArguments(args);
297        expect(result.enabledTools).toEqual([
298          "read_file",
299          "write_file",
300          "list_directory",
301        ]);
302      });
303  
304      test("should parse array elements", () => {
305        const args = [
306          "--enabled-tools",
307          "read_file",
308          "write_file",
309          "list_directory",
310        ];
311        const result = parseArguments(args);
312        expect(result.enabledTools).toEqual([
313          "read_file",
314          "write_file",
315          "list_directory",
316        ]);
317      });
318  
319      test("should parse mixed format", () => {
320        const args = [
321          "--enabled-tools",
322          "read_file,write_file",
323          "list_directory",
324        ];
325        const result = parseArguments(args);
326        expect(result.enabledTools).toEqual([
327          "read_file",
328          "write_file",
329          "list_directory",
330        ]);
331      });
332    });
333  
334    describe("--approved-commands", () => {
335      test("should parse comma-separated string", () => {
336        const args = ["--approved-commands", "npm,node,git"];
337        const result = parseArguments(args);
338        expect(result.approvedCommandsFromArgs).toEqual(["npm", "node", "git"]);
339      });
340  
341      test("should parse array elements", () => {
342        const args = ["--approved-commands", "npm", "node", "git"];
343        const result = parseArguments(args);
344        expect(result.approvedCommandsFromArgs).toEqual(["npm", "node", "git"]);
345      });
346  
347      test("should parse mixed format", () => {
348        const args = ["--approved-commands", "npm,node", "git"];
349        const result = parseArguments(args);
350        expect(result.approvedCommandsFromArgs).toEqual(["npm", "node", "git"]);
351      });
352    });
353  
354    describe("Multiple flags", () => {
355      test("should handle multiple different flags", () => {
356        const args = [
357          "--approved-folders",
358          "dir1",
359          "dir2",
360          "--ignored-folders",
361          "node_modules",
362          "dist",
363          "--enabled-tools",
364          "read_file",
365          "write_file",
366        ];
367        const result = parseArguments(args);
368        expect(result.approvedFoldersFromArgs).toEqual(["dir1", "dir2"]);
369        expect(result.ignoredFolders).toEqual(["node_modules", "dist"]);
370        expect(result.enabledTools).toEqual(["read_file", "write_file"]);
371      });
372  
373      test("should handle complex real-world configuration", () => {
374        const args = [
375          "--approved-folders",
376          "C:/Development/Projects/Test/filesystem-of-a-down-approved-folders-test",
377          "C:/Development/Projects/MCP-Servers/filesystem-of-a-down/src/tests/fixtures",
378          "--ignored-folders",
379          "node_modules,dist,.git",
380          "--enabled-tools",
381          "read_file",
382          "read_multiple_files",
383          "write_file",
384          "write_multiple_files",
385          "edit_file",
386          "make_directory",
387          "list_directory",
388          "directory_tree",
389          "file_operations",
390          "get_file_info",
391          "register_directory",
392          "grep_files",
393          "glob_files",
394          "delete_files",
395          "execute_shell",
396        ];
397        const result = parseArguments(args);
398        expect(result.approvedFoldersFromArgs).toEqual([
399          "C:/Development/Projects/Test/filesystem-of-a-down-approved-folders-test",
400          "C:/Development/Projects/MCP-Servers/filesystem-of-a-down/src/tests/fixtures",
401        ]);
402        expect(result.ignoredFolders).toEqual(["node_modules", "dist", ".git"]);
403        expect(result.enabledTools).toHaveLength(15);
404        expect(result.enabledTools).toContain("read_file");
405        expect(result.enabledTools).toContain("execute_shell");
406      });
407    });
408  });