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 });