feat: add write support for KeePass entries and groups

This commit is contained in:
2026-05-10 00:19:42 +02:00
parent 89ba04d61a
commit da0b396bf8
7 changed files with 221 additions and 39 deletions
+42 -1
View File
@@ -1,5 +1,8 @@
import { expect, test } from "bun:test";
import { readFile } from "node:fs/promises";
import { copyFile, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { randomUUID } from "node:crypto";
import { openKeePassDatabase } from "../../src/keepass";
type FixtureEntry = {
@@ -25,6 +28,16 @@ type FixtureData = {
const FIXTURE_PATH = "tests/fixtures/data.kdbx";
const FIXTURE_DATA_PATH = "tests/fixtures/data.kdbx.json";
async function withTempCopy<T>(filePath: string, fn: (tempPath: string) => Promise<T>): Promise<T> {
const tempPath = join(tmpdir(), `kdbx-lib-${randomUUID()}.kdbx`);
await copyFile(filePath, tempPath);
try {
return await fn(tempPath);
} finally {
await rm(tempPath, { force: true });
}
}
async function ensurePyKeePass(): Promise<boolean> {
const python = process.env.PYTHON_PATH ?? ".venv/bin/python3";
const child = Bun.spawn([python, "-c", "import pykeepass; print('ok')"], {
@@ -125,6 +138,34 @@ test("finds entries in the bundled data fixture", async () => {
expect(entries[0]?.groupPath).toBe("Folder1");
});
test("creates entries in a temporary copy of the bundled fixture and persists them", async () => {
const [{ password }, pykeepassReady] = await Promise.all([
readFile(FIXTURE_DATA_PATH, "utf8").then((raw) => JSON.parse(raw) as FixtureData),
ensurePyKeePass(),
]);
if (!pykeepassReady) {
console.log("Skipping integration test: pykeepass is not installed");
return;
}
await withTempCopy(FIXTURE_PATH, async (tempPath) => {
const db = openKeePassDatabase(tempPath, { password });
const createdEntry = await db.createEntry({
title: "TempEntry",
username: "temp-user",
password: "temp-pass",
});
expect(createdEntry.title).toBe("TempEntry");
expect(createdEntry.username).toBe("temp-user");
const persisted = await db.findEntries({ title: "TempEntry" });
expect(persisted).toHaveLength(1);
expect(persisted[0]?.username).toBe("temp-user");
});
});
test("uses the JSON fixture content as the source of truth for expectations", async () => {
const { content } = JSON.parse(await readFile(FIXTURE_DATA_PATH, "utf8")) as FixtureData;