UNPKG

ls-json

Version:
898 lines (876 loc) 66.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const index_1 = require("./index"); const vitest_1 = require("vitest"); (0, vitest_1.describe)("ls-json tests", () => { const lsOutput1 = `total 80 drwxr-xr-x 28 root root 4096 2009-01-01 00:00 . drwxr-xr-x 28 root root 4096 2009-01-01 00:00 .. drwxr-xr-x 2 root root 4096 2009-01-01 00:00 acct drwxr-xr-x 70 root root 1460 2025-09-19 01:22 apex lrw-r--r-- 1 root root 11 2009-01-01 00:00 bin -> /system/bin lrw-r--r-- 1 root root 50 2009-01-01 00:00 bugreports -> /data/user_de/0/com.android.shell/files/bugreports drwxrwx--- 2 system cache 4096 2009-01-01 00:00 cache drwxr-xr-x 3 root root 0 1970-01-01 00:00 config lrw-r--r-- 1 root root 17 2009-01-01 00:00 d -> /sys/kernel/debug drwxrwx--x 50 system system 4096 2025-09-19 01:23 data d????????? ? ? ? ? ? data_mirror drwxr-xr-x 2 root root 4096 2009-01-01 00:00 debug_ramdisk drwxr-xr-x 21 root root 2720 2025-09-19 01:22 dev lrw-r--r-- 1 root root 11 2009-01-01 00:00 etc -> /system/etc l????????? ? ? ? ? ? init -> ? -????????? ? ? ? ? ? init.environ.rc d????????? ? ? ? ? ? linkerconfig drwx------ 2 root root 16384 2009-01-01 00:00 lost+found d????????? ? ? ? ? ? metadata drwxr-xr-x 15 root system 320 2025-09-19 01:22 mnt drwxr-xr-x 2 root root 4096 2009-01-01 00:00 odm drwxr-xr-x 2 root root 4096 2009-01-01 00:00 odm_dlkm drwxr-xr-x 2 root root 4096 2009-01-01 00:00 oem d????????? ? ? ? ? ? postinstall dr-xr-xr-x 332 root root 0 2025-09-19 01:22 proc drwxr-xr-x 10 root root 4096 2009-01-01 00:00 product lrw-r--r-- 1 root root 21 2009-01-01 00:00 sdcard -> /storage/self/primary drwxr-xr-x 2 root root 4096 2009-01-01 00:00 second_stage_resources drwx--x--- 4 shell everybody 80 2025-09-19 01:22 storage dr-xr-xr-x 13 root root 0 2025-09-19 01:22 sys drwxr-xr-x 12 root root 4096 2009-01-01 00:00 system d????????? ? ? ? ? ? system_dlkm drwxr-xr-x 8 root root 4096 2009-01-01 00:00 system_ext drwxr-xr-x 13 root shell 4096 2009-01-01 00:00 vendor drwxr-xr-x 2 root root 4096 2009-01-01 00:00 vendor_dlkm`; const lsOutput2 = `total 160 drwxr-xr-x 12 user staff 384 Sep 19 14:51 . drwxr-xr-x 9 user staff 288 Sep 19 14:16 .. drwxr-xr-x 7 user staff 224 Sep 19 14:35 dist -rw-r--r-- 1 user staff 2640 Sep 19 14:51 index.spec.ts -rw-r--r-- 1 user staff 13201 Sep 19 15:10 index.ts -rw-r--r-- 1 user staff 11358 Sep 19 14:40 LICENSE drwxr-xr-x 12 user staff 384 Sep 19 14:51 node_modules -rw-r--r-- 1 user staff 1164 Sep 19 14:51 package.json -rw-r--r-- 1 user staff 31149 Sep 19 14:51 pnpm-lock.yaml -rw-r--r-- 1 user staff 1072 Sep 19 14:40 README.md -rw-r--r-- 1 user staff 909 Sep 19 14:51 tsconfig.json -rw-r--r-- 1 user staff 366 Sep 19 14:52 vitest.config.ts`; const lsOutput3 = `total 160 drwxr-xr-x 7 user staff 224B Sep 19 14:35 dist -rw-r--r-- 1 user staff 2.6K Sep 19 14:51 index.spec.ts -rw-r--r-- 1 user staff 13K Sep 19 15:10 index.ts -rw-r--r-- 1 user staff 11K Sep 19 14:40 LICENSE drwxr-xr-x 12 user staff 384B Sep 19 14:51 node_modules -rw-r--r-- 1 user staff 1.1K Sep 19 14:51 package.json -rw-r--r-- 1 user staff 30K Sep 19 14:51 pnpm-lock.yaml -rw-r--r-- 1 user staff 1.0K Sep 19 14:40 README.md -rw-r--r-- 1 user staff 909B Sep 19 14:51 tsconfig.json -rw-r--r-- 1 user staff 366B Sep 19 14:52 vitest.config.ts`; (0, vitest_1.it)("should parse ls output to json v1", () => { const result = (0, index_1.parse)(lsOutput1); console.log(result); (0, vitest_1.expect)(result).toBeDefined(); }); (0, vitest_1.it)("should parse ls output to json v2", () => { const result = (0, index_1.parse)(lsOutput2); console.log(JSON.stringify(result, null, 2)); (0, vitest_1.expect)(result).toBeDefined(); }); (0, vitest_1.it)("should parse ls output to json v3", () => { const result = (0, index_1.parse)(lsOutput3); console.log(result); (0, vitest_1.expect)(result).toBeDefined(); }); (0, vitest_1.it)("should handle octal permissions correctly including leading zeros", () => { const lsOutputWithSpecialPerms = `total 32 ---------- 1 user user 1024 Sep 19 14:51 no_perms.txt -rw------- 1 user user 2048 Sep 19 14:52 owner_only.txt -rw-rw---- 1 user user 3072 Sep 19 14:53 owner_group.txt -rw-rw-rw- 1 user user 4096 Sep 19 14:54 all_read_write.txt ---x--x--x 1 user user 5120 Sep 19 14:55 execute_only.txt drwx------ 2 user user 4096 Sep 19 14:56 private_dir`; const result = (0, index_1.parse)(lsOutputWithSpecialPerms); console.log("Special permissions:", result); (0, vitest_1.expect)(result).toBeDefined(); (0, vitest_1.expect)(result.length).toBe(6); // Test different permission modes const noPerms = result.find((entry) => entry.filename === "no_perms.txt"); (0, vitest_1.expect)(noPerms?.mode).toBe("000"); // 000 permissions const ownerOnly = result.find((entry) => entry.filename === "owner_only.txt"); (0, vitest_1.expect)(ownerOnly?.mode).toBe("600"); // rw------- const ownerGroup = result.find((entry) => entry.filename === "owner_group.txt"); (0, vitest_1.expect)(ownerGroup?.mode).toBe("660"); // rw-rw---- const allReadWrite = result.find((entry) => entry.filename === "all_read_write.txt"); (0, vitest_1.expect)(allReadWrite?.mode).toBe("666"); // rw-rw-rw- const executeOnly = result.find((entry) => entry.filename === "execute_only.txt"); (0, vitest_1.expect)(executeOnly?.mode).toBe("111"); // --x--x--x const privateDir = result.find((entry) => entry.filename === "private_dir"); (0, vitest_1.expect)(privateDir?.mode).toBe("700"); // drwx------ }); (0, vitest_1.it)("should handle permissions with leading zeros correctly", () => { const lsOutputWithLeadingZeros = `total 16 -------rwx 1 user user 1024 Sep 19 14:51 others_only.txt ----rw-rw- 1 user user 2048 Sep 19 14:52 group_others.txt`; const result = (0, index_1.parse)(lsOutputWithLeadingZeros); console.log("Leading zeros:", result); (0, vitest_1.expect)(result).toBeDefined(); (0, vitest_1.expect)(result.length).toBe(2); // Test modes that would have leading zeros in octal const othersOnly = result.find((entry) => entry.filename === "others_only.txt"); (0, vitest_1.expect)(othersOnly?.mode).toBe("007"); // -------rwx -> 007 const groupOthers = result.find((entry) => entry.filename === "group_others.txt"); (0, vitest_1.expect)(groupOthers?.mode).toBe("066"); // ----rw-rw- -> 066 }); (0, vitest_1.it)("should hide . and .. entries by default", () => { const lsOutputWithDots = `total 16 drwxr-xr-x 3 user user 4096 Sep 19 14:51 . drwxr-xr-x 5 user user 8192 Sep 19 14:50 .. -rw-r--r-- 1 user user 1024 Sep 19 14:51 file.txt drwxr-xr-x 2 user user 4096 Sep 19 14:52 subdir`; const result = (0, index_1.parse)(lsOutputWithDots); // 默认 showDotsDir: false console.log("Default (hide dots):", result.map((e) => e.filename)); (0, vitest_1.expect)(result).toBeDefined(); (0, vitest_1.expect)(result.length).toBe(2); // 只有 file.txt 和 subdir // 验证 . 和 .. 不存在 (0, vitest_1.expect)(result.find((entry) => entry.filename === ".")).toBeUndefined(); (0, vitest_1.expect)(result.find((entry) => entry.filename === "..")).toBeUndefined(); // 验证其他文件存在 (0, vitest_1.expect)(result.find((entry) => entry.filename === "file.txt")).toBeDefined(); (0, vitest_1.expect)(result.find((entry) => entry.filename === "subdir")).toBeDefined(); }); (0, vitest_1.it)("should include . and .. entries when showDotsDir is true", () => { const lsOutputWithDots = `total 16 drwxr-xr-x 3 user user 4096 Sep 19 14:51 . drwxr-xr-x 5 user user 8192 Sep 19 14:50 .. -rw-r--r-- 1 user user 1024 Sep 19 14:51 file.txt drwxr-xr-x 2 user user 4096 Sep 19 14:52 subdir`; const result = (0, index_1.parse)(lsOutputWithDots, { showDotsDir: true }); console.log("Show dots:", result.map((e) => e.filename)); (0, vitest_1.expect)(result).toBeDefined(); (0, vitest_1.expect)(result.length).toBe(4); // . .. file.txt subdir // 验证 . 和 .. 存在 const dotEntry = result.find((entry) => entry.filename === "."); (0, vitest_1.expect)(dotEntry).toBeDefined(); (0, vitest_1.expect)(dotEntry?.type).toBe("directory"); (0, vitest_1.expect)(dotEntry?.mode).toBe("755"); const dotdotEntry = result.find((entry) => entry.filename === ".."); (0, vitest_1.expect)(dotdotEntry).toBeDefined(); (0, vitest_1.expect)(dotdotEntry?.type).toBe("directory"); (0, vitest_1.expect)(dotdotEntry?.mode).toBe("755"); // 验证其他文件也存在 (0, vitest_1.expect)(result.find((entry) => entry.filename === "file.txt")).toBeDefined(); (0, vitest_1.expect)(result.find((entry) => entry.filename === "subdir")).toBeDefined(); }); }); (0, vitest_1.describe)("Unified parse function tests", () => { const lsOutput = `total 160 drwxr-xr-x 12 user staff 384 Sep 19 14:51 . drwxr-xr-x 9 user staff 288 Sep 19 14:16 .. -rw-r--r-- 1 user staff 1164 Sep 19 14:51 package.json -rw-r--r-- 1 user staff 1072 Sep 19 14:40 README.md`; const recursiveOutput = `/home: total 8 -rw-r--r-- 1 user user 1024 2009-01-01 00:00 file.txt drwxr-xr-x 2 user user 4096 2009-01-01 00:00 subdir /home/subdir: total 4 -rw-r--r-- 1 user user 128 2009-01-01 00:00 nested.txt`; (0, vitest_1.it)("should parse standard ls output by default", () => { const result = (0, index_1.parse)(lsOutput); (0, vitest_1.expect)(Array.isArray(result)).toBe(true); (0, vitest_1.expect)(result).toHaveLength(2); // package.json and README.md (. and .. are hidden) }); (0, vitest_1.it)("should parse recursive ls output with recursive option", () => { const result = (0, index_1.parse)(recursiveOutput, { recursive: true }); (0, vitest_1.expect)(result).toHaveProperty("directories"); (0, vitest_1.expect)(result).toHaveProperty("getAllEntries"); (0, vitest_1.expect)(result.directories).toHaveLength(2); }); (0, vitest_1.it)("should support depth limiting in recursive mode", () => { const deepRecursive = `/level0: total 4 drwxr-xr-x 2 user user 4096 2009-01-01 00:00 level1 /level0/level1: total 4 drwxr-xr-x 2 user user 4096 2009-01-01 00:00 level2 /level0/level1/level2: total 4 -rw-r--r-- 1 user user 128 2009-01-01 00:00 deep.txt`; const result = (0, index_1.parse)(deepRecursive, { recursive: true, depth: 1 }); (0, vitest_1.expect)(result.directories).toHaveLength(2); // Should stop at depth 1 }); (0, vitest_1.it)("should provide traversal methods for recursive results", () => { const result = (0, index_1.parse)(recursiveOutput, { recursive: true }); // Test getAllEntries const allEntries = result.getAllEntries(); (0, vitest_1.expect)(allEntries).toHaveLength(3); // file.txt, subdir, and nested.txt (dots are hidden) // Test findEntry const fileEntry = result.findEntry((entry) => entry.filename === "file.txt"); (0, vitest_1.expect)(fileEntry).toBeDefined(); (0, vitest_1.expect)(fileEntry?.parent).toBe("/home"); // Test getEntriesByPath const homeEntries = result.getEntriesByPath("/home"); (0, vitest_1.expect)(homeEntries).toHaveLength(2); // file.txt and subdir (dots are hidden by default) // Test filterEntries const txtFiles = result.filterEntries((entry) => entry.filename.endsWith(".txt")); (0, vitest_1.expect)(txtFiles).toHaveLength(2); // file.txt and nested.txt // Test getDirectoryTree const tree = result.getDirectoryTree(); (0, vitest_1.expect)(Object.keys(tree)).toHaveLength(2); (0, vitest_1.expect)(tree["/home"]).toBeDefined(); (0, vitest_1.expect)(tree["/home/subdir"]).toBeDefined(); }); (0, vitest_1.it)("should support traverse method", () => { const result = (0, index_1.parse)(recursiveOutput, { recursive: true }); const traversedFiles = []; result.traverse((entry, directory) => { traversedFiles.push(`${directory.path}/${entry.filename}`); }); (0, vitest_1.expect)(traversedFiles).toHaveLength(3); // file.txt, subdir, nested.txt (0, vitest_1.expect)(traversedFiles).toContain("/home/file.txt"); (0, vitest_1.expect)(traversedFiles).toContain("/home/subdir"); (0, vitest_1.expect)(traversedFiles).toContain("/home/subdir/nested.txt"); }); }); (0, vitest_1.describe)("Comprehensive LsRecursiveOutput method tests", () => { const complexRecursiveOutput = `/vendor: total 68 drwxr-xr-x 13 root shell 4096 2009-01-01 00:00 . drwxr-xr-x 28 root root 4096 2009-01-01 00:00 .. drwxr-xr-x 2 root shell 4096 2009-01-01 00:00 apex drwxr-x--x 3 root shell 4096 2009-01-01 00:00 bin -????????? ? ? ? ? ? build.prop drwxr-xr-x 10 root shell 4096 2009-01-01 00:00 etc drwxr-xr-x 3 root shell 4096 2009-01-01 00:00 lib drwxr-xr-x 7 root shell 8192 2009-01-01 00:00 lib64 /vendor/apex: total 4108 drwxr-xr-x 2 root shell 4096 2009-01-01 00:00 . drwxr-xr-x 13 root shell 4096 2009-01-01 00:00 .. -rw-r--r-- 1 root root 4227072 2009-01-01 00:00 com.google.android.widevine.nonupdatable.apex /vendor/bin: total 320 drwxr-x--x 3 root shell 4096 2009-01-01 00:00 . drwxr-xr-x 13 root shell 4096 2009-01-01 00:00 .. l????????? ? ? ? ? ? [ -> ? -rwxr-xr-x 1 root shell 340712 2009-01-01 00:00 sh drwxr-x--x 2 root shell 4096 2009-01-01 00:00 hw /vendor/bin/hw: total 8 drwxr-x--x 2 root shell 4096 2009-01-01 00:00 . drwxr-x--x 3 root shell 4096 2009-01-01 00:00 .. -????????? ? ? ? ? ? android.hardware.atrace@1.0-service -????????? ? ? ? ? ? android.hardware.wifi-service /vendor/etc: total 376 drwxr-xr-x 10 root shell 4096 2009-01-01 00:00 . drwxr-xr-x 13 root shell 4096 2009-01-01 00:00 .. -rw-r--r-- 1 root root 190463 2009-01-01 00:00 NOTICE.xml.gz -rw-r--r-- 1 root root 7544 2009-01-01 00:00 audio_effects.xml drwxr-xr-x 2 root shell 4096 2009-01-01 00:00 config drwxr-xr-x 3 root shell 4096 2009-01-01 00:00 init drwxr-xr-x 2 root shell 4096 2009-01-01 00:00 wifi /vendor/etc/config: total 92 drwxr-xr-x 2 root shell 4096 2009-01-01 00:00 . drwxr-xr-x 10 root shell 4096 2009-01-01 00:00 .. -rw-r--r-- 1 root root 12628 2009-01-01 00:00 emu_camera_back.json -rw-r--r-- 1 root root 7600 2009-01-01 00:00 emu_camera_depth.json -rw-r--r-- 1 root root 58438 2009-01-01 00:00 emu_camera_front.json ls: /vendor/restricted: Permission denied /vendor/lib: total 12 drwxr-xr-x 3 root shell 4096 2009-01-01 00:00 . drwxr-xr-x 13 root shell 4096 2009-01-01 00:00 .. drwxr-xr-x 2 root shell 4096 2009-01-01 00:00 modules /vendor/lib/modules: total 8 drwxr-xr-x 2 root shell 4096 2009-01-01 00:00 . drwxr-xr-x 3 root shell 4096 2009-01-01 00:00 .. -????????? ? ? ? ? ? goldfish_pipe.ko -????????? ? ? ? ? ? virtio-gpu.ko`; let result; // Use any to access methods (0, vitest_1.beforeEach)(() => { result = (0, index_1.parse)(complexRecursiveOutput, { recursive: true }); }); (0, vitest_1.describe)("getAllEntries method", () => { (0, vitest_1.it)("should return all entries from all directories", () => { const allEntries = result.getAllEntries(); // Should include all valid entries (excluding dots and unknown permissions) (0, vitest_1.expect)(allEntries.length).toBeGreaterThan(10); // Check for specific entries from different directories const filenames = allEntries.map((entry) => entry.filename); (0, vitest_1.expect)(filenames).toContain("apex"); (0, vitest_1.expect)(filenames).toContain("bin"); (0, vitest_1.expect)(filenames).toContain("com.google.android.widevine.nonupdatable.apex"); (0, vitest_1.expect)(filenames).toContain("sh"); (0, vitest_1.expect)(filenames).toContain("hw"); (0, vitest_1.expect)(filenames).toContain("NOTICE.xml.gz"); (0, vitest_1.expect)(filenames).toContain("config"); (0, vitest_1.expect)(filenames).toContain("emu_camera_back.json"); (0, vitest_1.expect)(filenames).toContain("modules"); }); (0, vitest_1.it)("should include parent path information for all entries", () => { const allEntries = result.getAllEntries(); allEntries.forEach((entry) => { (0, vitest_1.expect)(entry).toHaveProperty('parent'); (0, vitest_1.expect)(typeof entry.parent).toBe('string'); (0, vitest_1.expect)(entry.parent.startsWith('/vendor')).toBe(true); }); }); (0, vitest_1.it)("should return entries with correct types", () => { const allEntries = result.getAllEntries(); const shEntry = allEntries.find((e) => e.filename === 'sh'); (0, vitest_1.expect)(shEntry?.type).toBe('file'); const apexEntry = allEntries.find((e) => e.filename === 'apex'); (0, vitest_1.expect)(apexEntry?.type).toBe('directory'); const configEntry = allEntries.find((e) => e.filename === 'config'); (0, vitest_1.expect)(configEntry?.type).toBe('directory'); }); }); (0, vitest_1.describe)("findEntry method", () => { (0, vitest_1.it)("should find entry by filename", () => { const shEntry = result.findEntry((entry) => entry.filename === 'sh'); (0, vitest_1.expect)(shEntry).toBeDefined(); (0, vitest_1.expect)(shEntry.filename).toBe('sh'); (0, vitest_1.expect)(shEntry.type).toBe('file'); (0, vitest_1.expect)(shEntry.parent).toBe('/vendor/bin'); (0, vitest_1.expect)(shEntry.size).toBe(340712); }); (0, vitest_1.it)("should find entry by multiple conditions", () => { const largeFile = result.findEntry((entry) => entry.type === 'file' && entry.size > 1000000); (0, vitest_1.expect)(largeFile).toBeDefined(); (0, vitest_1.expect)(largeFile.filename).toBe('com.google.android.widevine.nonupdatable.apex'); (0, vitest_1.expect)(largeFile.size).toBe(4227072); (0, vitest_1.expect)(largeFile.parent).toBe('/vendor/apex'); }); (0, vitest_1.it)("should find directory by type", () => { const hwDir = result.findEntry((entry) => entry.filename === 'hw' && entry.type === 'directory'); (0, vitest_1.expect)(hwDir).toBeDefined(); (0, vitest_1.expect)(hwDir.type).toBe('directory'); (0, vitest_1.expect)(hwDir.parent).toBe('/vendor/bin'); }); (0, vitest_1.it)("should return undefined for non-existent entry", () => { const nonExistent = result.findEntry((entry) => entry.filename === 'nonexistent.txt'); (0, vitest_1.expect)(nonExistent).toBeUndefined(); }); (0, vitest_1.it)("should find entry by owner", () => { const rootOwnedFile = result.findEntry((entry) => entry.owner === 'root' && entry.group === 'root' && entry.type === 'file'); (0, vitest_1.expect)(rootOwnedFile).toBeDefined(); (0, vitest_1.expect)(rootOwnedFile.owner).toBe('root'); (0, vitest_1.expect)(rootOwnedFile.group).toBe('root'); }); }); (0, vitest_1.describe)("filterEntries method", () => { (0, vitest_1.it)("should filter files only", () => { const files = result.filterEntries((entry) => entry.type === 'file'); (0, vitest_1.expect)(files.length).toBeGreaterThan(0); files.forEach((file) => { (0, vitest_1.expect)(file.type).toBe('file'); }); const filenames = files.map((f) => f.filename); (0, vitest_1.expect)(filenames).toContain('sh'); (0, vitest_1.expect)(filenames).toContain('com.google.android.widevine.nonupdatable.apex'); (0, vitest_1.expect)(filenames).toContain('NOTICE.xml.gz'); }); (0, vitest_1.it)("should filter directories only", () => { const directories = result.filterEntries((entry) => entry.type === 'directory'); (0, vitest_1.expect)(directories.length).toBeGreaterThan(0); directories.forEach((dir) => { (0, vitest_1.expect)(dir.type).toBe('directory'); }); const dirNames = directories.map((d) => d.filename); (0, vitest_1.expect)(dirNames).toContain('apex'); (0, vitest_1.expect)(dirNames).toContain('bin'); (0, vitest_1.expect)(dirNames).toContain('config'); (0, vitest_1.expect)(dirNames).toContain('hw'); }); (0, vitest_1.it)("should filter by file extension", () => { const xmlFiles = result.filterEntries((entry) => entry.filename.endsWith('.xml')); (0, vitest_1.expect)(xmlFiles.length).toBeGreaterThan(0); xmlFiles.forEach((file) => { (0, vitest_1.expect)(file.filename.endsWith('.xml')).toBe(true); }); }); (0, vitest_1.it)("should filter by size range", () => { const largeFiles = result.filterEntries((entry) => entry.type === 'file' && entry.size > 100000); (0, vitest_1.expect)(largeFiles.length).toBeGreaterThan(0); largeFiles.forEach((file) => { (0, vitest_1.expect)(file.size).toBeGreaterThan(100000); }); }); (0, vitest_1.it)("should filter by specific path", () => { const etcFiles = result.filterEntries((entry) => entry.parent && entry.parent.startsWith('/vendor/etc')); (0, vitest_1.expect)(etcFiles.length).toBeGreaterThan(0); etcFiles.forEach((file) => { (0, vitest_1.expect)(file.parent.startsWith('/vendor/etc')).toBe(true); }); }); (0, vitest_1.it)("should filter by permissions", () => { const executableFiles = result.filterEntries((entry) => entry.flags && entry.flags.includes('x') && entry.type === 'file'); const shFile = executableFiles.find((f) => f.filename === 'sh'); (0, vitest_1.expect)(shFile).toBeDefined(); (0, vitest_1.expect)(shFile.flags).toContain('x'); }); (0, vitest_1.it)("should return empty array when no matches", () => { const noMatches = result.filterEntries((entry) => entry.filename === 'impossible-to-match-filename'); (0, vitest_1.expect)(noMatches).toEqual([]); }); }); (0, vitest_1.describe)("getEntriesByPath method", () => { (0, vitest_1.it)("should get entries for root vendor path", () => { const vendorEntries = result.getEntriesByPath('/vendor'); (0, vitest_1.expect)(vendorEntries).toBeDefined(); (0, vitest_1.expect)(vendorEntries.length).toBeGreaterThan(0); const filenames = vendorEntries.map((e) => e.filename); (0, vitest_1.expect)(filenames).toContain('apex'); (0, vitest_1.expect)(filenames).toContain('bin'); (0, vitest_1.expect)(filenames).toContain('etc'); (0, vitest_1.expect)(filenames).toContain('lib'); }); (0, vitest_1.it)("should get entries for nested paths", () => { const configEntries = result.getEntriesByPath('/vendor/etc/config'); (0, vitest_1.expect)(configEntries).toBeDefined(); (0, vitest_1.expect)(configEntries.length).toBeGreaterThan(0); const filenames = configEntries.map((e) => e.filename); (0, vitest_1.expect)(filenames).toContain('emu_camera_back.json'); (0, vitest_1.expect)(filenames).toContain('emu_camera_depth.json'); (0, vitest_1.expect)(filenames).toContain('emu_camera_front.json'); }); (0, vitest_1.it)("should get entries for bin/hw path", () => { const hwEntries = result.getEntriesByPath('/vendor/bin/hw'); (0, vitest_1.expect)(hwEntries).toBeDefined(); (0, vitest_1.expect)(hwEntries.length).toBe(0); // No valid entries due to unknown permissions }); (0, vitest_1.it)("should return undefined for non-existent path", () => { const nonExistentEntries = result.getEntriesByPath('/vendor/nonexistent'); (0, vitest_1.expect)(nonExistentEntries).toBeUndefined(); }); (0, vitest_1.it)("should return entries with correct parent paths", () => { const apexEntries = result.getEntriesByPath('/vendor/apex'); (0, vitest_1.expect)(apexEntries).toBeDefined(); apexEntries.forEach((entry) => { (0, vitest_1.expect)(entry.parent).toBe('/vendor/apex'); }); }); }); (0, vitest_1.describe)("getDirectoryTree method", () => { (0, vitest_1.it)("should return tree with all directory paths as keys", () => { const tree = result.getDirectoryTree(); (0, vitest_1.expect)(tree).toHaveProperty('/vendor'); (0, vitest_1.expect)(tree).toHaveProperty('/vendor/apex'); (0, vitest_1.expect)(tree).toHaveProperty('/vendor/bin'); (0, vitest_1.expect)(tree).toHaveProperty('/vendor/bin/hw'); (0, vitest_1.expect)(tree).toHaveProperty('/vendor/etc'); (0, vitest_1.expect)(tree).toHaveProperty('/vendor/etc/config'); (0, vitest_1.expect)(tree).toHaveProperty('/vendor/lib'); (0, vitest_1.expect)(tree).toHaveProperty('/vendor/lib/modules'); }); (0, vitest_1.it)("should have entries arrays for each directory", () => { const tree = result.getDirectoryTree(); Object.keys(tree).forEach(path => { (0, vitest_1.expect)(Array.isArray(tree[path])).toBe(true); }); }); (0, vitest_1.it)("should have correct entries for each directory", () => { const tree = result.getDirectoryTree(); const vendorEntries = tree['/vendor']; const vendorFilenames = vendorEntries.map((e) => e.filename); (0, vitest_1.expect)(vendorFilenames).toContain('apex'); (0, vitest_1.expect)(vendorFilenames).toContain('bin'); (0, vitest_1.expect)(vendorFilenames).toContain('etc'); const configEntries = tree['/vendor/etc/config']; const configFilenames = configEntries.map((e) => e.filename); (0, vitest_1.expect)(configFilenames).toContain('emu_camera_back.json'); }); (0, vitest_1.it)("should maintain parent relationship consistency", () => { const tree = result.getDirectoryTree(); Object.keys(tree).forEach(path => { const entries = tree[path]; entries.forEach((entry) => { (0, vitest_1.expect)(entry.parent).toBe(path); }); }); }); }); (0, vitest_1.describe)("traverse method", () => { (0, vitest_1.it)("should traverse all entries with callback", () => { const traversedEntries = []; const traversedPaths = []; result.traverse((entry, directory) => { traversedEntries.push(entry); traversedPaths.push(directory.path); }); (0, vitest_1.expect)(traversedEntries.length).toBeGreaterThan(10); (0, vitest_1.expect)(traversedPaths.length).toBeGreaterThan(10); // Should include entries from different directories const filenames = traversedEntries.map(e => e.filename); (0, vitest_1.expect)(filenames).toContain('apex'); (0, vitest_1.expect)(filenames).toContain('sh'); (0, vitest_1.expect)(filenames).toContain('NOTICE.xml.gz'); (0, vitest_1.expect)(filenames).toContain('emu_camera_back.json'); }); (0, vitest_1.it)("should provide correct directory context in callback", () => { const pathEntryMap = {}; result.traverse((entry, directory) => { if (!pathEntryMap[directory.path]) { pathEntryMap[directory.path] = []; } pathEntryMap[directory.path].push(entry.filename); }); (0, vitest_1.expect)(pathEntryMap['/vendor']).toBeDefined(); (0, vitest_1.expect)(pathEntryMap['/vendor']).toContain('apex'); (0, vitest_1.expect)(pathEntryMap['/vendor']).toContain('bin'); (0, vitest_1.expect)(pathEntryMap['/vendor/apex']).toBeDefined(); (0, vitest_1.expect)(pathEntryMap['/vendor/apex']).toContain('com.google.android.widevine.nonupdatable.apex'); (0, vitest_1.expect)(pathEntryMap['/vendor/bin']).toBeDefined(); (0, vitest_1.expect)(pathEntryMap['/vendor/bin']).toContain('sh'); (0, vitest_1.expect)(pathEntryMap['/vendor/etc/config']).toBeDefined(); (0, vitest_1.expect)(pathEntryMap['/vendor/etc/config']).toContain('emu_camera_back.json'); }); (0, vitest_1.it)("should allow modifying external state during traversal", () => { let fileCount = 0; let dirCount = 0; let totalSize = 0; result.traverse((entry, directory) => { if (entry.type === 'file') { fileCount++; if (typeof entry.size === 'number') { totalSize += entry.size; } } else if (entry.type === 'directory') { dirCount++; } }); (0, vitest_1.expect)(fileCount).toBeGreaterThan(0); (0, vitest_1.expect)(dirCount).toBeGreaterThan(0); (0, vitest_1.expect)(totalSize).toBeGreaterThan(0); }); (0, vitest_1.it)("should visit entries in directory order", () => { const visitOrder = []; result.traverse((entry, directory) => { visitOrder.push(`${directory.path}/${entry.filename}`); }); // Check that /vendor entries come before /vendor/apex entries const vendorApexIndex = visitOrder.findIndex(path => path.startsWith('/vendor/apex/')); const vendorBinIndex = visitOrder.findIndex(path => path.startsWith('/vendor/bin/')); const lastVendorRootIndex = visitOrder.map((path, idx) => path.startsWith('/vendor/') && !path.includes('/', 8) ? idx : -1).filter(idx => idx !== -1).pop() || -1; (0, vitest_1.expect)(vendorApexIndex).toBeGreaterThan(lastVendorRootIndex); (0, vitest_1.expect)(vendorBinIndex).toBeGreaterThan(lastVendorRootIndex); }); }); (0, vitest_1.describe)("Error handling and edge cases", () => { (0, vitest_1.it)("should handle empty recursive output", () => { const emptyResult = (0, index_1.parse)('', { recursive: true }); (0, vitest_1.expect)(emptyResult.directories).toEqual([]); (0, vitest_1.expect)(emptyResult.getAllEntries()).toEqual([]); (0, vitest_1.expect)(emptyResult.findEntry(() => true)).toBeUndefined(); (0, vitest_1.expect)(emptyResult.filterEntries(() => true)).toEqual([]); (0, vitest_1.expect)(emptyResult.getEntriesByPath('/any/path')).toBeUndefined(); (0, vitest_1.expect)(emptyResult.getDirectoryTree()).toEqual({}); let callbackCalled = false; emptyResult.traverse(() => { callbackCalled = true; }); (0, vitest_1.expect)(callbackCalled).toBe(false); }); (0, vitest_1.it)("should handle single directory with no entries", () => { const singleEmptyDir = `/empty: total 0 drwxr-xr-x 2 user user 4096 2009-01-01 00:00 . drwxr-xr-x 3 user user 4096 2009-01-01 00:00 ..`; const singleResult = (0, index_1.parse)(singleEmptyDir, { recursive: true }); (0, vitest_1.expect)(singleResult.directories).toHaveLength(1); (0, vitest_1.expect)(singleResult.getAllEntries()).toEqual([]); (0, vitest_1.expect)(singleResult.getEntriesByPath('/empty')).toEqual([]); let traverseCount = 0; singleResult.traverse(() => { traverseCount++; }); (0, vitest_1.expect)(traverseCount).toBe(0); }); (0, vitest_1.it)("should handle directories with only errors", () => { const errorOnlyOutput = `ls: /restricted: Permission denied`; const errorResult = (0, index_1.parse)(errorOnlyOutput, { recursive: true }); (0, vitest_1.expect)(errorResult.directories).toHaveLength(0); (0, vitest_1.expect)(errorResult.errors).toBeDefined(); (0, vitest_1.expect)(errorResult.errors[0]).toContain('Permission denied'); }); }); (0, vitest_1.describe)("Performance and consistency", () => { (0, vitest_1.it)("should maintain consistency between different access methods", () => { // getAllEntries should equal sum of all getEntriesByPath results const allEntries = result.getAllEntries(); const tree = result.getDirectoryTree(); let treeEntriesCount = 0; Object.keys(tree).forEach(path => { treeEntriesCount += tree[path].length; }); (0, vitest_1.expect)(allEntries.length).toBe(treeEntriesCount); }); (0, vitest_1.it)("should maintain consistency between traverse and other methods", () => { const allEntries = result.getAllEntries(); let traverseCount = 0; result.traverse(() => { traverseCount++; }); (0, vitest_1.expect)(allEntries.length).toBe(traverseCount); }); (0, vitest_1.it)("should return same instances for repeated calls", () => { const firstCall = result.getAllEntries(); const secondCall = result.getAllEntries(); (0, vitest_1.expect)(firstCall.length).toBe(secondCall.length); // Objects should have same data even if not same instances (0, vitest_1.expect)(firstCall[0]).toEqual(secondCall[0]); }); }); (0, vitest_1.describe)("Options and parameter testing", () => { (0, vitest_1.it)("should work with showDotsDir option", () => { const resultWithDots = (0, index_1.parse)(complexRecursiveOutput, { recursive: true, showDotsDir: true }); const tree = resultWithDots.getDirectoryTree(); const vendorEntries = tree['/vendor']; (0, vitest_1.expect)(vendorEntries).toBeDefined(); const filenames = vendorEntries.map((e) => e.filename); (0, vitest_1.expect)(filenames).toContain('.'); (0, vitest_1.expect)(filenames).toContain('..'); (0, vitest_1.expect)(filenames).toContain('apex'); (0, vitest_1.expect)(filenames).toContain('bin'); }); (0, vitest_1.it)("should work with depth limiting", () => { const resultWithDepth = (0, index_1.parse)(complexRecursiveOutput, { recursive: true, depth: 1 }); const tree = resultWithDepth.getDirectoryTree(); const paths = Object.keys(tree); // With depth=1, should include: // - /vendor (depth 0) // - /vendor/apex, /vendor/bin, /vendor/etc, /vendor/lib (depth 1) // But NOT: // - /vendor/bin/hw, /vendor/etc/config, /vendor/lib/modules (depth 2, > depth limit) (0, vitest_1.expect)(paths).toContain('/vendor'); (0, vitest_1.expect)(paths).toContain('/vendor/apex'); (0, vitest_1.expect)(paths).toContain('/vendor/bin'); (0, vitest_1.expect)(paths).toContain('/vendor/etc'); (0, vitest_1.expect)(paths).toContain('/vendor/lib'); (0, vitest_1.expect)(paths).not.toContain('/vendor/bin/hw'); (0, vitest_1.expect)(paths).not.toContain('/vendor/etc/config'); (0, vitest_1.expect)(paths).not.toContain('/vendor/lib/modules'); }); (0, vitest_1.it)("should work with raw option", () => { const resultRaw = (0, index_1.parse)(complexRecursiveOutput, { recursive: true, raw: true }); const allEntries = resultRaw.getAllEntries(); (0, vitest_1.expect)(allEntries.length).toBeGreaterThan(0); // Raw option should still produce valid entries allEntries.forEach((entry) => { (0, vitest_1.expect)(entry).toHaveProperty('filename'); (0, vitest_1.expect)(entry).toHaveProperty('type'); (0, vitest_1.expect)(entry).toHaveProperty('parent'); }); }); (0, vitest_1.it)("should handle combined options", () => { const resultCombined = (0, index_1.parse)(complexRecursiveOutput, { recursive: true, showDotsDir: true, depth: 1, raw: false }); const tree = resultCombined.getDirectoryTree(); // Should have depth-limited directories (depth=1, so only depth 0 and 1) (0, vitest_1.expect)(tree).toHaveProperty('/vendor'); (0, vitest_1.expect)(tree).toHaveProperty('/vendor/etc'); // depth 1 (0, vitest_1.expect)(tree).not.toHaveProperty('/vendor/etc/config'); // depth 2, > depth limit (0, vitest_1.expect)(tree).not.toHaveProperty('/vendor/lib/modules'); // depth 2, > depth limit // Should include dot entries const vendorEntries = tree['/vendor']; (0, vitest_1.expect)(vendorEntries).toBeDefined(); const filenames = vendorEntries.map((e) => e.filename); (0, vitest_1.expect)(filenames).toContain('.'); (0, vitest_1.expect)(filenames).toContain('..'); }); }); (0, vitest_1.describe)("Real-world Android ls output tests", () => { const realWorldOutput = `/product/app: total 108 drwxr-xr-x 27 root root 4096 2009-01-01 00:00 . drwxr-xr-x 10 root root 4096 2009-01-01 00:00 .. drwxr-xr-x 3 root root 4096 2009-01-01 00:00 CalendarGooglePrebuilt drwxr-xr-x 4 root root 4096 2009-01-01 00:00 Camera2 drwxr-xr-x 2 root root 4096 2009-01-01 00:00 Chrome drwxr-xr-x 2 root root 4096 2009-01-01 00:00 Chrome-Stub /product/app/CalendarGooglePrebuilt: total 37888 drwxr-xr-x 3 root root 4096 2009-01-01 00:00 . drwxr-xr-x 27 root root 4096 2009-01-01 00:00 .. -rw-r--r-- 1 root root 38783606 2009-01-01 00:00 CalendarGooglePrebuilt.apk drwxr-xr-x 3 root root 4096 2009-01-01 00:00 oat /product/app/CalendarGooglePrebuilt/oat: total 12 drwxr-xr-x 3 root root 4096 2009-01-01 00:00 . drwxr-xr-x 3 root root 4096 2009-01-01 00:00 .. drwxr-xr-x 2 root root 4096 2009-01-01 00:00 arm64 /product/app/CalendarGooglePrebuilt/oat/arm64: total 1104 drwxr-xr-x 2 root root 4096 2009-01-01 00:00 . drwxr-xr-x 3 root root 4096 2009-01-01 00:00 .. -rw-r--r-- 1 root root 267192 2009-01-01 00:00 CalendarGooglePrebuilt.odex -rw-r--r-- 1 root root 846580 2009-01-01 00:00 CalendarGooglePrebuilt.vdex /product/app/Camera2: total 7880 drwxr-xr-x 4 root root 4096 2009-01-01 00:00 . drwxr-xr-x 27 root root 4096 2009-01-01 00:00 .. -rw-r--r-- 1 root root 8050927 2009-01-01 00:00 Camera2.apk drwxr-xr-x 3 root root 4096 2009-01-01 00:00 lib drwxr-xr-x 3 root root 4096 2009-01-01 00:00 oat /product/app/Camera2/lib: total 12 drwxr-xr-x 3 root root 4096 2009-01-01 00:00 . drwxr-xr-x 4 root root 4096 2009-01-01 00:00 .. drwxr-xr-x 2 root root 4096 2009-01-01 00:00 arm64 /product/app/Camera2/lib/arm64: total 396 drwxr-xr-x 2 root root 4096 2009-01-01 00:00 . drwxr-xr-x 3 root root 4096 2009-01-01 00:00 .. -rw-r--r-- 1 root root 406488 2009-01-01 00:00 libjni_jpegutil.so -rw-r--r-- 1 root root 133384 2009-01-01 00:00 libjni_tinyplanet.so`; (0, vitest_1.it)("should parse complex nested Android directory structure", () => { const androidResult = (0, index_1.parse)(realWorldOutput, { recursive: true }); (0, vitest_1.expect)(androidResult.directories.length).toBe(7); const tree = androidResult.getDirectoryTree(); (0, vitest_1.expect)(tree).toHaveProperty('/product/app'); (0, vitest_1.expect)(tree).toHaveProperty('/product/app/CalendarGooglePrebuilt'); (0, vitest_1.expect)(tree).toHaveProperty('/product/app/CalendarGooglePrebuilt/oat'); (0, vitest_1.expect)(tree).toHaveProperty('/product/app/CalendarGooglePrebuilt/oat/arm64'); (0, vitest_1.expect)(tree).toHaveProperty('/product/app/Camera2'); (0, vitest_1.expect)(tree).toHaveProperty('/product/app/Camera2/lib'); (0, vitest_1.expect)(tree).toHaveProperty('/product/app/Camera2/lib/arm64'); }); (0, vitest_1.it)("should correctly identify APK and native library files", () => { const androidResult = (0, index_1.parse)(realWorldOutput, { recursive: true }); const apkFiles = androidResult.filterEntries((entry) => entry.filename.endsWith('.apk')); (0, vitest_1.expect)(apkFiles.length).toBe(2); (0, vitest_1.expect)(apkFiles.map((f) => f.filename)).toContain('CalendarGooglePrebuilt.apk'); (0, vitest_1.expect)(apkFiles.map((f) => f.filename)).toContain('Camera2.apk'); const soFiles = androidResult.filterEntries((entry) => entry.filename.endsWith('.so')); (0, vitest_1.expect)(soFiles.length).toBe(2); (0, vitest_1.expect)(soFiles.map((f) => f.filename)).toContain('libjni_jpegutil.so'); (0, vitest_1.expect)(soFiles.map((f) => f.filename)).toContain('libjni_tinyplanet.so'); const odexFiles = androidResult.filterEntries((entry) => entry.filename.endsWith('.odex') || entry.filename.endsWith('.vdex')); (0, vitest_1.expect)(odexFiles.length).toBe(2); }); (0, vitest_1.it)("should find largest files correctly", () => { const androidResult = (0, index_1.parse)(realWorldOutput, { recursive: true }); const largestFile = androidResult.findEntry((entry) => entry.type === 'file' && typeof entry.size === 'number'); (0, vitest_1.expect)(largestFile).toBeDefined(); // Find the actual largest file const allFiles = androidResult.filterEntries((entry) => entry.type === 'file' && typeof entry.size === 'number'); const largest = allFiles.reduce((prev, current) => current.size > prev.size ? current : prev); (0, vitest_1.expect)(largest.filename).toBe('CalendarGooglePrebuilt.apk'); (0, vitest_1.expect)(largest.size).toBe(38783606); (0, vitest_1.expect)(largest.parent).toBe('/product/app/CalendarGooglePrebuilt'); }); (0, vitest_1.it)("should traverse Android directory structure in correct order", () => { const androidResult = (0, index_1.parse)(realWorldOutput, { recursive: true }); const visitOrder = []; androidResult.traverse((entry, directory) => { if (entry.type === 'directory') { visitOrder.push(`${directory.path}/${entry.filename}`); } }); // Verify that parent directories are visited before children const calendarIndex = visitOrder.indexOf('/product/app/CalendarGooglePrebuilt'); const oatIndex = visitOrder.indexOf('/product/app/CalendarGooglePrebuilt/oat'); (0, vitest_1.expect)(calendarIndex).toBeGreaterThan(-1); (0, vitest_1.expect)(oatIndex).toBeGreaterThan(-1); (0, vitest_1.expect)(calendarIndex).toBeLessThan(oatIndex); }); (0, vitest_1.it)("should handle different file size formats", () => { const androidResult = (0, index_1.parse)(realWorldOutput, { recursive: true }); const largeFiles = androidResult.filterEntries((entry) => entry.type === 'file' && entry.size > 1000000); (0, vitest_1.expect)(largeFiles.length).toBeGreaterThan(0); largeFiles.forEach((file) => { (0, vitest_1.expect)(typeof file.size).toBe('number'); (0, vitest_1.expect)(file.size).toBeGreaterThan(1000000); }); }); }); }); (0, vitest_1.describe)("Depth parameter comprehensive tests", () => { const deepRecursiveOutput = `/root: total 16 drwxr-xr-x 3 root root 4096 2009-01-01 00:00 . drwxr-xr-x 3 root root 4096 2009-01-01 00:00 .. drwxr-xr-x 2 root root 4096 2009-01-01 00:00 level1-a drwxr-xr-x 2 root root 4096 2009-01-01 00:00 level1-b -rw-r--r-- 1 root root 100 2009-01-01 00:00 root-file.txt /root/level1-a: total 12 drwxr-xr-x 2 root root 4096 2009-01-01 00:00 . drwxr-xr-x 3 root root 4096 2009-01-01 00:00 .. drwxr-xr-x 2 root root 4096 2009-01-01 00:00 level2-a -rw-r--r-- 1 root root 50 2009-01-01 00:00 level1-a-file.txt /root/level1-a/level2-a: total 12 drwxr-xr-x 2 root root 4096 2009-01-01 00:00 . drwxr-xr-x 2 root root 4096 2009-01-01 00:00 .. drwxr-xr-x 2 root root 4096 2009-01-01 00:00 level3-a -rw-r--r-- 1 root root 25 2009-01-01 00:00 level2-a-file.txt /root/level1-a/level2-a/level3-a: total 8 drwxr-xr-x 2 root root 4096 2009-01-01 00:00 . drwxr-xr-x 2 root root 4096 2009-01-01 00:00 .. -rw-r--r-- 1 root root 10 2009-01-01 00:00 level3-a-file.txt /root/level1-b: total 12 drwxr-xr-x 2 root root 4096 2009-01-01 00:00 . drwxr-xr-x 3 root root 4096 2009-01-01 00:00 .. drwxr-xr-x 2 root root 4096 2009-01-01 00:00 level2-b -rw-r--r-- 1 root root 75 2009-01-01 00:00 level1-b-file.txt /root/level1-b/level2-b: total 8 drwxr-xr-x 2 root root 4096 2009-01-01 00:00 . drwxr-xr-x 2 root root 4096 2009-01-01 00:00 .. -rw-r--r-- 1 root root 15 2009-01-01 00:00 level2-b-file.txt`; (0, vitest_1.describe)("depth = 0 (root level only)", () => { (0, vitest_1.it)("should only include root directory", () => { const result = (0, index_1.parse)(deepRecursiveOutput, { recursive: true, depth: 0 }); const paths = Object.keys(result.getDirectoryTree()); (0, vitest_1.expect)(paths).toHaveLength(1); (0, vitest_1.expect)(paths).toContain('/root'); (0, vitest_1.expect)(paths).not.toContain('/root/level1-a'); (0, vitest_1.expect)(paths).not.toContain('/root/level1-b'); }); (0, vitest_1.it)("should exclude all file entries (path depth > 0)", () => { const result = (0, index_1.parse)(deepRecursiveOutput, { recursive: true, depth: 0 }); // With path-based depth limiting, depth=0 means only paths with depth 0 // Since file entries have minimum path depth of 1 (/root/file.txt), // no file entries should be returned const allEntries = result.getAllEntries(); (0, vitest_1.expect)(allEntries).toHaveLength(0); const rootEntries = result.getEntriesByPath('/root'); (0, vitest_1.expect)(rootEntries).toHaveLength(0); }); }); (0, vitest_1.describe)("depth = 1 (path depth ≤ 1)", () => { (0, vitest_1.it)("should include root and level1 directories", () => { const result = (0, index_1.parse)(deepRecursiveOutput, { recursive: true, depth: 1 }); const paths = Object.keys(result.getDirectoryTree()); (0, vitest_1.expect)(paths).toHaveLength(3); (0, vitest_1.expect)(paths).toContain('/root'); (0, vitest_1.expect)(paths).toContain('/root/level1-a'); (0, vitest_1.expect)(paths).toContain('/root/level1-b'); // Should NOT include level2 directories (0, vitest_1.expect)(paths).not.toContain('/root/level1-a/level2-a'); (0, vitest_1.expect)(paths).not.toContain('/root/level1-b/level2-b'); }); (0, vitest_1.it)("should only include entries with path depth ≤ 1", () => { const result = (0, index_1.parse)(deepRecursiveOutput, { recursive: true, depth: 1 }); const allEntries = result.getAllEntries(); const allFilenames = allEntries.map(e => e.filename); // Path depth 1: /root/file.txt, /root/level1-a, /root/level1-b (0, vitest_1.expect)(allFilenames).toContain('root-file.txt'); (0, vitest_1.expect)(allFilenames).toContain('level1-a'); (0, vitest_1.expect)(allFilenames).toContain('level1-b'); // Path depth 2: should NOT be included (0, vitest_1.expect)(allFilenames).not.toContain('level1-a-file.txt'); (0, vitest_1.expect)(allFilenames).not.toContain('level2-a'); (0, vitest_1.expect)(allFilenames).not.toContain('level1-b-file.txt'); (