






























@@ -1,4 +1,5 @@
11import fs from "node:fs/promises";
2+import os from "node:os";
23import path from "node:path";
34import * as tar from "tar";
45import { describe, expect, it, vi } from "vitest";
@@ -409,4 +410,77 @@ describe("createBackupArchive", () => {
409410},
410411);
411412});
413+414+it("does not duplicate the root manifest when the system tempdir lives inside the state dir", async () => {
415+await withOpenClawTestState(
416+{
417+layout: "state-only",
418+prefix: "openclaw-backup-tmp-overlap-",
419+scenario: "minimal",
420+},
421+async (state) => {
422+const stateDir = state.stateDir;
423+const outputDir = state.path("backups");
424+const overlappingTmp = path.join(stateDir, "tmp");
425+await fs.mkdir(overlappingTmp, { recursive: true });
426+await fs.mkdir(outputDir, { recursive: true });
427+const tmpdirSpy = vi.spyOn(os, "tmpdir").mockReturnValue(overlappingTmp);
428+429+try {
430+const result = await createBackupArchive({
431+output: outputDir,
432+includeWorkspace: false,
433+nowMs: Date.UTC(2026, 4, 9, 12, 0, 0),
434+});
435+const entries = await listArchiveEntries(result.archivePath);
436+const rootManifestEntries = entries.filter(
437+(entry) => entry.endsWith("/manifest.json") && !entry.includes("/payload/"),
438+);
439+expect(rootManifestEntries).toHaveLength(1);
440+441+const runtime: RuntimeEnv = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
442+await expect(
443+backupVerifyCommand(runtime, { archive: result.archivePath }),
444+).resolves.toMatchObject({ ok: true });
445+} finally {
446+tmpdirSpy.mockRestore();
447+}
448+},
449+);
450+});
451+452+it("does not duplicate the root manifest when the system tempdir is the state dir itself", async () => {
453+await withOpenClawTestState(
454+{
455+layout: "state-only",
456+prefix: "openclaw-backup-tmp-equals-state-",
457+scenario: "minimal",
458+},
459+async (state) => {
460+const outputDir = state.path("backups");
461+await fs.mkdir(outputDir, { recursive: true });
462+const tmpdirSpy = vi.spyOn(os, "tmpdir").mockReturnValue(state.stateDir);
463+464+try {
465+const result = await createBackupArchive({
466+output: outputDir,
467+includeWorkspace: false,
468+nowMs: Date.UTC(2026, 4, 9, 12, 0, 0),
469+});
470+const entries = await listArchiveEntries(result.archivePath);
471+const rootManifestEntries = entries.filter(
472+(entry) => entry.endsWith("/manifest.json") && !entry.includes("/payload/"),
473+);
474+expect(rootManifestEntries).toHaveLength(1);
475+476+const runtime: RuntimeEnv = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
477+await expect(
478+backupVerifyCommand(runtime, { archive: result.archivePath }),
479+).resolves.toMatchObject({ ok: true });
480+} finally {
481+tmpdirSpy.mockRestore();
482+}
483+},
484+);
485+});
412486});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。