mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-01 02:12:26 +00:00
Merge pull request #1376 from PR0M3TH3AN/ai/tests-core-modules-20250226-2648005617024541031
Add unit tests for core modules (cache, sessionActor, r2Service)
This commit is contained in:
3 files changed
+331
No files matched your search
@@ -0,0 +1,120 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,106 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert";
|
||||
import r2Service from "../../js/services/r2Service.js";
|
||||
|
||||
// Ensure localStorage is mocked
|
||||
import "../../tests/test-helpers/setup-localstorage.mjs";
|
||||
|
||||
test("js/services/r2Service.js", async (t) => {
|
||||
t.beforeEach(async () => {
|
||||
localStorage.clear();
|
||||
await r2Service.clearSettings();
|
||||
});
|
||||
|
||||
await t.test("Default Settings", () => {
|
||||
const settings = r2Service.getSettings();
|
||||
// The actual default settings might contain more fields than we checked.
|
||||
// Let's verify the core fields match.
|
||||
assert.strictEqual(settings.accountId, "");
|
||||
assert.strictEqual(settings.accessKeyId, "");
|
||||
assert.strictEqual(settings.secretAccessKey, "");
|
||||
assert.strictEqual(settings.baseDomain, "");
|
||||
assert.deepStrictEqual(settings.buckets, {});
|
||||
});
|
||||
|
||||
await t.test("handleCloudflareSettingsSubmit - Valid Input", async () => {
|
||||
const input = {
|
||||
accountId: "acc123",
|
||||
accessKeyId: "key123",
|
||||
secretAccessKey: "secret123",
|
||||
baseDomain: "https://pub-xxx.r2.dev",
|
||||
};
|
||||
|
||||
const result = await r2Service.handleCloudflareSettingsSubmit(input);
|
||||
assert.strictEqual(result, true);
|
||||
|
||||
const saved = r2Service.getSettings();
|
||||
assert.strictEqual(saved.accountId, input.accountId);
|
||||
assert.strictEqual(saved.accessKeyId, input.accessKeyId);
|
||||
assert.strictEqual(saved.secretAccessKey, input.secretAccessKey);
|
||||
assert.strictEqual(saved.baseDomain, input.baseDomain);
|
||||
});
|
||||
|
||||
await t.test("handleCloudflareSettingsSubmit - Invalid S3 URL", async () => {
|
||||
const input = {
|
||||
accountId: "acc123",
|
||||
accessKeyId: "key123",
|
||||
secretAccessKey: "secret123",
|
||||
baseDomain: "https://something.r2.cloudflarestorage.com",
|
||||
};
|
||||
|
||||
let status = "";
|
||||
const cleanup = r2Service.on("settingsStatus", (s) => (status = s.message));
|
||||
|
||||
const result = await r2Service.handleCloudflareSettingsSubmit(input);
|
||||
assert.strictEqual(result, false);
|
||||
assert.match(status, /entered the S3 API URL/);
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
await t.test("handleCloudflareSettingsSubmit - Missing Fields", async () => {
|
||||
const input = {
|
||||
accountId: "acc123",
|
||||
// missing accessKeyId
|
||||
secretAccessKey: "secret123",
|
||||
baseDomain: "https://pub-xxx.r2.dev",
|
||||
};
|
||||
|
||||
const result = await r2Service.handleCloudflareSettingsSubmit(input);
|
||||
assert.strictEqual(result, false);
|
||||
});
|
||||
|
||||
await t.test("ensureBucketConfigForNpub - Uses Explicit Credentials", async () => {
|
||||
const npub = "npub1test";
|
||||
const credentials = {
|
||||
accountId: "acc123",
|
||||
accessKeyId: "key123",
|
||||
secretAccessKey: "secret123",
|
||||
baseDomain: "https://pub-xxx.r2.dev",
|
||||
bucket: "custom-bucket",
|
||||
isLegacy: true,
|
||||
};
|
||||
|
||||
const result = await r2Service.ensureBucketConfigForNpub(npub, { credentials });
|
||||
assert.strictEqual(result.entry.bucket, "custom-bucket");
|
||||
assert.strictEqual(result.entry.publicBaseUrl, "https://pub-xxx.r2.dev");
|
||||
assert.strictEqual(result.customDomainStatus, "manual");
|
||||
});
|
||||
|
||||
await t.test("resolveConnection - Returns Legacy Settings if no StorageService", async () => {
|
||||
const input = {
|
||||
accountId: "legacyAcc",
|
||||
accessKeyId: "legacyKey",
|
||||
secretAccessKey: "legacySecret",
|
||||
baseDomain: "https://legacy.com",
|
||||
};
|
||||
await r2Service.handleCloudflareSettingsSubmit(input);
|
||||
|
||||
const npub = "npub1any";
|
||||
const resolved = await r2Service.resolveConnection(npub);
|
||||
|
||||
assert.ok(resolved);
|
||||
assert.strictEqual(resolved.accountId, "legacyAcc");
|
||||
assert.strictEqual(resolved.isLegacy, true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert";
|
||||
import {
|
||||
loadSavedProfilesFromStorage,
|
||||
persistSavedProfiles,
|
||||
setActiveProfilePubkey,
|
||||
getSavedProfiles,
|
||||
getActiveProfilePubkey,
|
||||
setSavedProfiles,
|
||||
storeUrlHealth,
|
||||
getCachedUrlHealth,
|
||||
getModerationSettings,
|
||||
setModerationSettings,
|
||||
urlHealthConstants,
|
||||
} from "../../js/state/cache.js";
|
||||
|
||||
// Ensure localStorage is mocked
|
||||
import "../../tests/test-helpers/setup-localstorage.mjs";
|
||||
|
||||
test("js/state/cache.js", async (t) => {
|
||||
t.beforeEach(() => {
|
||||
localStorage.clear();
|
||||
// Reset internal state if possible, or relying on overwrites
|
||||
setSavedProfiles([]);
|
||||
setActiveProfilePubkey(null, { persist: false });
|
||||
});
|
||||
|
||||
await t.test("Saved Profiles Persistence", async () => {
|
||||
const profiles = [
|
||||
{ pubkey: "0000000000000000000000000000000000000000000000000000000000000001", name: "Alice" },
|
||||
{ pubkey: "0000000000000000000000000000000000000000000000000000000000000002", name: "Bob" },
|
||||
];
|
||||
|
||||
setSavedProfiles(profiles, { persist: true });
|
||||
|
||||
// Simulate reload
|
||||
const { profiles: loadedProfiles } = loadSavedProfilesFromStorage();
|
||||
assert.strictEqual(loadedProfiles.length, 2);
|
||||
assert.strictEqual(loadedProfiles[0].name, "Alice");
|
||||
assert.strictEqual(loadedProfiles[1].name, "Bob");
|
||||
});
|
||||
|
||||
await t.test("Active Profile Pubkey", async () => {
|
||||
const pubkey = "0000000000000000000000000000000000000000000000000000000000000001";
|
||||
// We need to set saved profiles first so the active one is considered valid in some logic paths,
|
||||
// or just rely on setActiveProfilePubkey which should handle it.
|
||||
// However, looking at cache.js `loadSavedProfilesFromStorage` implementation:
|
||||
// It checks if the active candidate exists in `seenPubkeys` (from `savedProfiles`).
|
||||
// If not, it might not set it or treat it as valid if we are loading from storage.
|
||||
|
||||
// Let's add the profile to saved profiles first to be safe for persistence reload check.
|
||||
setSavedProfiles([{ pubkey, name: "Alice" }]);
|
||||
|
||||
setActiveProfilePubkey(pubkey);
|
||||
|
||||
assert.strictEqual(getActiveProfilePubkey(), pubkey);
|
||||
|
||||
// Verify persistence
|
||||
const { activePubkey } = loadSavedProfilesFromStorage();
|
||||
assert.strictEqual(activePubkey, pubkey);
|
||||
});
|
||||
|
||||
await t.test("URL Health Caching", async () => {
|
||||
const eventId = "event1";
|
||||
const url = "https://example.com/video.mp4";
|
||||
const result = { status: "ok", message: "All good" };
|
||||
|
||||
storeUrlHealth(eventId, url, result);
|
||||
|
||||
const cached = getCachedUrlHealth(eventId, url);
|
||||
assert.ok(cached);
|
||||
assert.strictEqual(cached.status, "ok");
|
||||
assert.strictEqual(cached.url, url);
|
||||
});
|
||||
|
||||
await t.test("URL Health Expiration", async (t) => {
|
||||
const eventId = "event2";
|
||||
const url = "https://example.com/expired.mp4";
|
||||
const result = { status: "checking" };
|
||||
|
||||
// Store with very short TTL
|
||||
storeUrlHealth(eventId, url, result, 1); // 1ms TTL
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
const cached = getCachedUrlHealth(eventId, url);
|
||||
assert.strictEqual(cached, null, "Should return null for expired cache");
|
||||
});
|
||||
|
||||
await t.test("Moderation Settings", async () => {
|
||||
const defaults = getModerationSettings();
|
||||
assert.ok(defaults);
|
||||
|
||||
const newSettings = {
|
||||
blurThreshold: 1,
|
||||
autoplayBlockThreshold: 2,
|
||||
};
|
||||
|
||||
setModerationSettings(newSettings);
|
||||
|
||||
const updated = getModerationSettings();
|
||||
assert.strictEqual(updated.blurThreshold, 1);
|
||||
assert.strictEqual(updated.autoplayBlockThreshold, 2);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user