perf(fs): 1MiB stream chunks, full sha256 via sha256sum subprocess, walk cap
- copy streams use 1MiB highWaterMark - forced full sha256 uses Deno.Command(sha256sum), falls back to in-process stream; method recorded as hashVia and counted - per-file organize.transfer timing log - ORGANIZE_WALK_MAX_FILES caps manual path walks; dryRun returns truncated Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
473c5e94a5
commit
f9c20c67dc
6 files changed
+223
-27
No files matched your search
@@ -10,8 +10,13 @@ import {
|
||||
diskUsage,
|
||||
FsError,
|
||||
ffprobe,
|
||||
filesMatchSha256,
|
||||
moveSafe,
|
||||
resetSha256Probe,
|
||||
scanIncremental,
|
||||
sha256File,
|
||||
sha256Full,
|
||||
sha256Stats,
|
||||
statSafe,
|
||||
sweepStalePartials,
|
||||
} from "./index.ts";
|
||||
@@ -43,7 +48,10 @@ describe("copyWithVerify", () => {
|
||||
const r = await copyWithVerify(t.path("src/a.mkv"), t.path("dst/a.mkv"), { hash, sampleChunkBytes: 4096 });
|
||||
expect(r.bytes).toBe(3 * 1024 * 1024 + 17);
|
||||
expect(r.verifiedBy).toBe(hash);
|
||||
if (hash === "sha256") expect(r.sha256).toMatch(/^[0-9a-f]{64}$/);
|
||||
if (hash === "sha256") {
|
||||
expect(r.sha256).toMatch(/^[0-9a-f]{64}$/);
|
||||
expect(["sha256sum", "stream"]).toContain(r.sha256Via);
|
||||
}
|
||||
expect((await readFile(t.path("dst/a.mkv"))).equals(await readFile(t.path("src/a.mkv")))).toBe(true);
|
||||
expect(Math.round((await stat(t.path("dst/a.mkv"))).mtimeMs)).toBe(
|
||||
Math.round((await stat(t.path("src/a.mkv"))).mtimeMs),
|
||||
@@ -79,6 +87,36 @@ describe("copyWithVerify", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("全量 sha256(子进程优先,回退进程内)", () => {
|
||||
const hasSha256sum = (() => {
|
||||
try {
|
||||
execFileSync("sha256sum", ["--version"], { stdio: "ignore" });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
it("与进程内流式结果一致;记录所用方式与计数", async () => {
|
||||
resetSha256Probe();
|
||||
const t = await tree({ "a.bin": 2 * 1024 * 1024 + 5, "b.bin": 2 * 1024 * 1024 + 5 });
|
||||
const before = sha256Stats().counts;
|
||||
const r = await sha256Full(t.path("a.bin"));
|
||||
expect(r.digest).toBe(await sha256File(t.path("a.bin")));
|
||||
expect(r.via).toBe(hasSha256sum ? "sha256sum" : "stream");
|
||||
expect(sha256Stats().counts[r.via]).toBe(before[r.via] + 1);
|
||||
// 同尺寸不同内容:不一致
|
||||
const m = await filesMatchSha256(t.path("a.bin"), t.path("b.bin"));
|
||||
expect(m.match).toBe(false);
|
||||
expect(m.via).toBe(r.via);
|
||||
});
|
||||
|
||||
it("文件不存在 → FsError", async () => {
|
||||
resetSha256Probe();
|
||||
await expect(sha256Full("/nonexistent/umt-x.bin")).rejects.toBeInstanceOf(Error);
|
||||
});
|
||||
});
|
||||
|
||||
describe("moveSafe / crossDeviceMove", () => {
|
||||
it("同设备移动;目标存在返回 exists 且不覆盖", async () => {
|
||||
const t = await tree({ "a.mkv": "A", "b.mkv": "B", "c.mkv": "C" });
|
||||
|
||||
Reference in new issue
Block a user