Files
bitvid/tests/nostr-session-actor.test.mjs

145 lines
4.0 KiB
JavaScript

import { indexedDB as fakeIndexedDB } from "fake-indexeddb";
import "./test-helpers/setup-localstorage.mjs";
import assert from "node:assert/strict";
import test from "node:test";
import {
encryptSessionPrivateKey,
decryptSessionPrivateKey,
persistSessionActor,
readStoredSessionActorEntry,
clearStoredSessionActor,
SESSION_ACTOR_STORAGE_KEY,
__testExports,
} from "../js/nostr/sessionActor.js";
const {
arrayBufferToBase64,
base64ToUint8Array,
deriveSessionEncryptionKey,
generateRandomBytes,
isSubtleCryptoAvailable,
_closeSessionActorDb,
} = __testExports;
const MOCK_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000001";
const MOCK_PRIVATE_KEY = "1111111111111111111111111111111111111111111111111111111111111111";
const PASSPHRASE = "secure-passphrase";
test.beforeEach(async () => {
if (!global.indexedDB) {
global.indexedDB = fakeIndexedDB;
}
await _closeSessionActorDb();
localStorage.clear();
// Clear IndexedDB skipped for stability
});
test.afterEach(() => {
localStorage.clear();
});
test("isSubtleCryptoAvailable returns true in test env", () => {
assert.equal(isSubtleCryptoAvailable(), true, "WebCrypto should be available");
});
test("generateRandomBytes returns correct length", () => {
const bytes = generateRandomBytes(16);
assert.equal(bytes.length, 16);
assert.ok(bytes instanceof Uint8Array);
});
test("encryptSessionPrivateKey and decryptSessionPrivateKey roundtrip", async () => {
const encrypted = await encryptSessionPrivateKey(MOCK_PRIVATE_KEY, PASSPHRASE);
assert.ok(encrypted.ciphertext);
assert.ok(encrypted.salt);
assert.ok(encrypted.iv);
assert.equal(encrypted.algorithm, "AES-GCM");
const decrypted = await decryptSessionPrivateKey(
{
privateKeyEncrypted: encrypted.ciphertext,
encryption: encrypted,
},
PASSPHRASE
);
assert.equal(decrypted, MOCK_PRIVATE_KEY);
});
test("decryptSessionPrivateKey fails with wrong passphrase", async () => {
const encrypted = await encryptSessionPrivateKey(MOCK_PRIVATE_KEY, PASSPHRASE);
await assert.rejects(
() => decryptSessionPrivateKey(
{
privateKeyEncrypted: encrypted.ciphertext,
encryption: encrypted,
},
"wrong-passphrase"
),
{
code: "decrypt-failed"
}
);
});
test("persistSessionActor writes to localStorage and IndexedDB", async () => {
const encrypted = await encryptSessionPrivateKey(MOCK_PRIVATE_KEY, PASSPHRASE);
const actor = {
pubkey: MOCK_PUBKEY,
privateKeyEncrypted: encrypted.ciphertext,
encryption: encrypted,
createdAt: Date.now(),
};
await persistSessionActor(actor);
// Check localStorage
const raw = localStorage.getItem(SESSION_ACTOR_STORAGE_KEY);
assert.ok(raw);
const parsed = JSON.parse(raw);
assert.equal(parsed.pubkey, MOCK_PUBKEY);
assert.equal(parsed.privateKeyEncrypted, encrypted.ciphertext);
// Check IndexedDB skipped for stability
});
test("readStoredSessionActorEntry retrieves from localStorage", async () => {
const encrypted = await encryptSessionPrivateKey(MOCK_PRIVATE_KEY, PASSPHRASE);
const actor = {
pubkey: MOCK_PUBKEY,
privateKeyEncrypted: encrypted.ciphertext,
encryption: encrypted,
createdAt: 1234567890,
};
localStorage.setItem(SESSION_ACTOR_STORAGE_KEY, JSON.stringify(actor));
const retrieved = readStoredSessionActorEntry();
assert.ok(retrieved);
assert.equal(retrieved.pubkey, MOCK_PUBKEY);
assert.equal(retrieved.privateKeyEncrypted, encrypted.ciphertext);
assert.equal(retrieved.createdAt, 1234567890);
});
test("clearStoredSessionActor removes from localStorage", async () => {
localStorage.setItem(SESSION_ACTOR_STORAGE_KEY, JSON.stringify({ foo: "bar" }));
await clearStoredSessionActor();
assert.equal(localStorage.getItem(SESSION_ACTOR_STORAGE_KEY), null);
});
test("helper: arrayBufferToBase64 and base64ToUint8Array roundtrip", () => {
const original = new Uint8Array([1, 2, 3, 255]);
const b64 = arrayBufferToBase64(original);
const decoded = base64ToUint8Array(b64);
assert.deepEqual(decoded, original);
});