ls-json
Version:
parse ls output to json
898 lines (876 loc) • 66.7 kB
JavaScript
"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');
(