mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-10-31 18:02:05 +00:00
feat(cli): .seedpass backup naming (phase 1 -- convention only, zero format change)
A portable backup is an encrypted, checksummed, cross-impl-verified file; its only sin was landing on disk as an anonymous .json. Exports now self-identify: point `vault export` (or the TUI database export) at a directory and the file is named seedpass-<fingerprint>-<date>.seedpass -- tied to the profile it restores and sortable by date. The TUI also appends .seedpass to an extensionless destination, since interactive input has no script-compatibility concerns. The zero-risk boundary, kept deliberately: an explicit file path is used exactly as typed -- a script that exports to backup.json still gets backup.json -- and imports sniff content, never extensions, so any old export restores unchanged. No byte of the format moved, which is what makes this safe to ship ahead of the review gate while format_version 2 (magic header) and the OS icon association stay queued behind it. Tests pin all three behaviours in both surfaces: directory target gets the generated name, extensionless TUI input gets the suffix, explicit paths pass through untouched. 158 CLI tests pass. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tkj4rzKBA3uhfgk7kqjsb8
This commit is contained in:
1 parent
7748d6b5e9
commit
0482bc3dfd
8 files changed
+109
-16
No files matched your search
@@ -528,3 +528,31 @@ describe("unknown entry kinds at the CLI surface", () => {
|
||||
expect(rows.find((row) => row["kind"] === "bitlogin_org")).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(".seedpass export naming", () => {
|
||||
it("a directory target gets the generated seedpass-<fp>-<date>.seedpass name", async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), "seedpass-name-"));
|
||||
const r = await run("--vault", vaultPath, "vault", "export", dir);
|
||||
expect(r.error).toBeUndefined();
|
||||
const dest = String(JSON.parse(r.stdout).exported);
|
||||
expect(dest.startsWith(dir)).toBe(true);
|
||||
expect(dest).toMatch(/seedpass-[0-9A-F]{16}-\d{8}\.seedpass$/);
|
||||
// And the file restores like any other backup: same wrapper shape.
|
||||
const wrapper = JSON.parse(await readFile(dest, "utf8"));
|
||||
expect(wrapper.format_version).toBe(1);
|
||||
});
|
||||
|
||||
it("an explicit file path is used exactly as typed (scripts depend on it)", async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), "seedpass-name-"));
|
||||
const exact = join(dir, "my-backup.json");
|
||||
const r = await run("--vault", vaultPath, "vault", "export", exact);
|
||||
expect(String(JSON.parse(r.stdout).exported)).toBe(exact);
|
||||
});
|
||||
|
||||
it("an extensionless explicit path is also used exactly as typed", async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), "seedpass-name-"));
|
||||
const bare = join(dir, "backup");
|
||||
const r = await run("--vault", vaultPath, "vault", "export", bare);
|
||||
expect(String(JSON.parse(r.stdout).exported)).toBe(bare);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user