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:
rootandClaude Opus 5.5 committed 2026-09-27 22:12:16 +08:00
1 parent 473c5e94a5
commit f9c20c67dc
6 files changed
+223 -27

No files matched your search

+39 -1
View File
@@ -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" });