Files
plainleaf/client/components/plainleaf_backup.test.ts
T
wushenghua 9e43ae824a
CI / config (push) Canceled after 0s
CI / test-frontend (push) Canceled after 0s
CI / test-rust (push) Canceled after 0s
CI / test-e2e (push) Canceled after 0s
CI / test-e2e-release (push) Canceled after 0s
CI / build (push) Canceled after 0s
CI / release (push) Canceled after 0s
CI / docker (push) Canceled after 0s
CI / docker-website (push) Canceled after 0s
feat: complete task planning and account management
Add Markdown-native planning, repeat rules, subtasks, reminders, transactional backup restore, Chinese account management, and the account-managed NAS migration configuration.\n\nCo-Authored-By: Codex <noreply@anthropic.com>
2026-08-12 20:22:33 +08:00

121 lines
3.3 KiB
TypeScript

import { expect, test } from "vitest";
import {
createPlainleafBackup,
parsePlainleafBackup,
plainleafBackupFormat,
restorePlainleafBackup,
} from "./plainleaf_backup.ts";
test("creates and parses a Markdown-only backup", () => {
const backup = createPlainleafBackup(
[{ path: "任务/收件箱.md", text: "- [ ] 测试\n" }],
"2026-08-12T12:00:00.000Z",
);
expect(parsePlainleafBackup(JSON.stringify(backup))).toEqual(backup);
expect(backup.format).toBe(plainleafBackupFormat);
});
test("rejects malformed backup payloads and unsafe paths", () => {
expect(() => parsePlainleafBackup("not json")).toThrow("有效的 JSON");
expect(() => parsePlainleafBackup('{"format":"wrong","files":[]}')).toThrow(
"Plainleaf Markdown 备份包",
);
for (const path of [
"../secret.md",
"/absolute.md",
"notes.txt",
"a\\b.md",
"a//b.md",
"a/./b.md",
]) {
expect(() =>
parsePlainleafBackup(
JSON.stringify({
format: plainleafBackupFormat,
exportedAt: "",
files: [{ path, text: "x" }],
}),
),
).toThrow("无效的 Markdown 路径");
}
});
test("allows dots in names but rejects duplicate backup destinations", () => {
expect(
parsePlainleafBackup(
JSON.stringify({
format: plainleafBackupFormat,
files: [{ path: "版本/1..2.md", text: "ok" }],
}),
).files[0].path,
).toBe("版本/1..2.md");
expect(() =>
parsePlainleafBackup(
JSON.stringify({
format: plainleafBackupFormat,
files: [
{ path: "重复.md", text: "first" },
{ path: "重复.md", text: "second" },
],
}),
),
).toThrow("重复路径");
});
test("restores listed Markdown without deleting unrelated files", async () => {
const files = new Map([
["已有.md", "旧内容"],
["保留.md", "不要删除"],
]);
const backup = createPlainleafBackup([
{ path: "已有.md", text: "新内容" },
{ path: "新增.md", text: "新增内容" },
]);
await expect(
restorePlainleafBackup(backup, {
listPaths: async () => [...files.keys()],
read: async (path) => files.get(path)!,
write: async (path, text) => {
files.set(path, text);
},
remove: async (path) => {
files.delete(path);
},
}),
).resolves.toBe(2);
expect(files).toEqual(
new Map([
["已有.md", "新内容"],
["保留.md", "不要删除"],
["新增.md", "新增内容"],
]),
);
});
test("rolls back overwritten and newly created pages when restore fails", async () => {
const files = new Map([["已有.md", "旧内容"]]);
let failing = true;
const backup = createPlainleafBackup([
{ path: "已有.md", text: "新内容" },
{ path: "新增.md", text: "新增内容" },
{ path: "失败.md", text: "不会写入" },
]);
await expect(
restorePlainleafBackup(backup, {
listPaths: async () => [...files.keys()],
read: async (path) => files.get(path)!,
write: async (path, text) => {
if (path === "失败.md" && failing) {
failing = false;
throw new Error("磁盘已满");
}
files.set(path, text);
},
remove: async (path) => {
files.delete(path);
},
}),
).rejects.toThrow("磁盘已满");
expect(files).toEqual(new Map([["已有.md", "旧内容"]]));
});