Files
bitvid/tests/nostr/sessionActor.test.mjs
T
google-labs-jules[bot] 41da0a8361 test(ai): add unit tests for cache, sessionActor, and r2Service (agent)
- Added `tests/state/cache.test.mjs` to test profile persistence and URL health caching.
- Added `tests/nostr/sessionActor.test.mjs` to test session key encryption/decryption and storage.
- Added `tests/services/r2Service.test.mjs` to test R2 settings management and bucket configuration.
- Exposed `__testExports` in `js/nostr/sessionActor.js` to allow testing of private helper functions.
- Used `tests/test-helpers/setup-localstorage.mjs` to mock browser environments in all new tests.
2026-01-19 11:31:46 +00:00

121 lines
3.5 KiB
JavaScript

import test from "node:test";
import assert from "node:assert";
import {
encryptSessionPrivateKey,
decryptSessionPrivateKey,
persistSessionActor,
readStoredSessionActorEntry,
clearStoredSessionActor,
SESSION_ACTOR_STORAGE_KEY,
__testExports
} from "../../js/nostr/sessionActor.js";
const { generateRandomBytes, isSubtleCryptoAvailable } = __testExports;
// Ensure localStorage is mocked
import "../../tests/test-helpers/setup-localstorage.mjs";
test("js/nostr/sessionActor.js", async (t) => {
if (!isSubtleCryptoAvailable()) {
t.skip("WebCrypto not available in this environment");
return;
}
t.beforeEach(async () => {
localStorage.clear();
await clearStoredSessionActor();
});
await t.test("Encryption and Decryption Roundtrip", async () => {
const privateKey = "nsec1testkey..."; // In reality this would be hex or bech32, but for encryption it is just a string payload
const passphrase = "secure-password";
const encrypted = await encryptSessionPrivateKey(privateKey, passphrase);
assert.ok(encrypted.ciphertext);
assert.ok(encrypted.salt);
assert.ok(encrypted.iv);
assert.strictEqual(encrypted.algorithm, "AES-GCM");
const payload = {
privateKeyEncrypted: encrypted.ciphertext,
encryption: encrypted,
};
const decrypted = await decryptSessionPrivateKey(payload, passphrase);
assert.strictEqual(decrypted, privateKey);
});
await t.test("Decryption fails with wrong passphrase", async () => {
const privateKey = "secret";
const passphrase = "correct";
const wrongPassphrase = "wrong";
const encrypted = await encryptSessionPrivateKey(privateKey, passphrase);
const payload = {
privateKeyEncrypted: encrypted.ciphertext,
encryption: encrypted,
};
await assert.rejects(
async () => {
await decryptSessionPrivateKey(payload, wrongPassphrase);
},
(err) => err.code === "decrypt-failed" || err.message.includes("decrypt-failed")
);
});
await t.test("Persistence and Retrieval", async () => {
const pubkey = "0000000000000000000000000000000000000000000000000000000000000001";
const privateKey = "my-secret-key";
const passphrase = "my-password";
const encrypted = await encryptSessionPrivateKey(privateKey, passphrase);
const actor = {
pubkey,
privateKeyEncrypted: encrypted.ciphertext,
encryption: encrypted,
createdAt: Date.now(),
};
persistSessionActor(actor);
// Verify localStorage
const storedRaw = localStorage.getItem(SESSION_ACTOR_STORAGE_KEY);
assert.ok(storedRaw);
const stored = readStoredSessionActorEntry();
assert.ok(stored);
assert.strictEqual(stored.pubkey, pubkey);
assert.strictEqual(stored.privateKeyEncrypted, encrypted.ciphertext);
// Verify we can decrypt what we retrieved
const decrypted = await decryptSessionPrivateKey(stored, passphrase);
assert.strictEqual(decrypted, privateKey);
});
await t.test("Clear Stored Session Actor", async () => {
const pubkey = "0000000000000000000000000000000000000000000000000000000000000001";
const actor = {
pubkey,
privateKeyEncrypted: "fake-encrypted-data",
encryption: {
salt: "fake-salt",
iv: "fake-iv",
iterations: 1000,
hash: "SHA-256",
algorithm: "AES-GCM",
version: 1
},
createdAt: Date.now(),
};
persistSessionActor(actor);
assert.ok(readStoredSessionActorEntry());
clearStoredSessionActor();
assert.strictEqual(readStoredSessionActorEntry(), null);
});
});