Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import { encryptConcealedFields } from "../encryption/field-encryption.js";
import { applyPatch, generateItemId, rowToItem } from "./helpers.js";
import type { VaultItemRow } from "./migrations/migrations.js";
function vaultKey(): Uint8Array {
const key = new Uint8Array(32);
crypto.getRandomValues(key);
return key;
}
function makeRow(over: Partial<VaultItemRow> = {}): VaultItemRow {
const key = vaultKey();
const id = "i1";
return {
id,
createdAt: 0,
updatedAt: 0,
version: 1,
deleted: false,
title: "GitHub",
category: "login",
fieldsJson: JSON.stringify({ username: "u" }),
concealedFieldsJson: JSON.stringify(
encryptConcealedFields(key, id, { password: "p" })
),
tagsJson: JSON.stringify(["work"]),
favorite: false,
...over,
};
}
describe("items helpers", () => {
it("rowToItem decrypts concealed fields", { timeout: 5000 }, () => {
const key = vaultKey();
const row = makeRow({
concealedFieldsJson: JSON.stringify(
encryptConcealedFields(key, "i1", { password: "p" })
),
});
const item = rowToItem(row, key);
expect(item.title).toBe("GitHub");
expect(item.concealedFields.password).toBe("p");
expect(item.fields.username).toBe("u");
expect(item.tags).toEqual(["work"]);
});
it("applyPatch updates each field independently", { timeout: 5000 }, () => {
const key = vaultKey();
const row = makeRow({
concealedFieldsJson: JSON.stringify(
encryptConcealedFields(key, "i1", { password: "p" })
),
});
const patched = applyPatch(
row,
{
title: "Renamed",
favorite: true,
fields: { url: "x" },
tags: ["new"],
},
key,
undefined
);
expect(patched.title).toBe("Renamed");
expect(patched.favorite).toBe(true);
expect(JSON.parse(patched.fieldsJson)).toEqual({ url: "x" });
expect(JSON.parse(patched.tagsJson)).toEqual(["new"]);
});
it("applyPatch re-encrypts concealed fields on update", {
timeout: 5000,
}, () => {
const key = vaultKey();
const row = makeRow({
concealedFieldsJson: JSON.stringify(
encryptConcealedFields(key, "i1", { password: "old" })
),
});
const patched = applyPatch(
row,
{ concealedFields: { password: "new" } },
key,
undefined
);
const recovered = rowToItem(patched, key);
expect(recovered.concealedFields.password).toBe("new");
});
it("applyPatch updates category when provided", { timeout: 5000 }, () => {
const key = vaultKey();
const row = makeRow();
const patched = applyPatch(
row,
{ category: "secure-note" },
key,
undefined
);
expect(patched.category).toBe("secure-note");
});
it("generateItemId produces 32-character hex strings", {
timeout: 5000,
}, () => {
const id = generateItemId(undefined);
expect(id).toMatch(/^[0-9a-f]{32}$/);
});
it("generateItemId uses the injected RNG when provided", {
timeout: 5000,
}, () => {
const rng = {
nextUint32: () => 1,
nextFloat: () => 0,
nextBytes: (len: number) => new Uint8Array(len).fill(0xab),
};
const id = generateItemId(rng);
expect(id).toBe("ab".repeat(16));
});
});