mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-02 19:02:28 +00:00
fix(test): resolve DM flow test timeouts and hangs
Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
8e6257881d
commit
ddcc777561
2 files changed
+84
-51
No files matched your search
+39
-27
@@ -192,16 +192,20 @@ export async function loadDirectMessageSnapshot(pubkey) {
|
||||
try {
|
||||
const db = await openSettingsDb();
|
||||
if (db) {
|
||||
const stored = await new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(STORE_NAME, "readonly");
|
||||
const store = tx.objectStore(STORE_NAME);
|
||||
const request = store.get(dbKey);
|
||||
request.onsuccess = () => resolve(request.result || []);
|
||||
request.onerror = () =>
|
||||
reject(request.error || new Error("Failed to load snapshot"));
|
||||
});
|
||||
try {
|
||||
const stored = await new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(STORE_NAME, "readonly");
|
||||
const store = tx.objectStore(STORE_NAME);
|
||||
const request = store.get(dbKey);
|
||||
request.onsuccess = () => resolve(request.result || []);
|
||||
request.onerror = () =>
|
||||
reject(request.error || new Error("Failed to load snapshot"));
|
||||
});
|
||||
|
||||
return normalizeSnapshotList(stored);
|
||||
return normalizeSnapshotList(stored);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
userLogger.warn(
|
||||
@@ -244,15 +248,19 @@ export async function saveDirectMessageSnapshot(pubkey, snapshot) {
|
||||
try {
|
||||
const db = await openSettingsDb();
|
||||
if (db) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(STORE_NAME, "readwrite");
|
||||
const store = tx.objectStore(STORE_NAME);
|
||||
const request = store.put(normalizedSnapshot, dbKey);
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () =>
|
||||
reject(request.error || new Error("Failed to write snapshot"));
|
||||
});
|
||||
return normalizedSnapshot;
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(STORE_NAME, "readwrite");
|
||||
const store = tx.objectStore(STORE_NAME);
|
||||
const request = store.put(normalizedSnapshot, dbKey);
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () =>
|
||||
reject(request.error || new Error("Failed to write snapshot"));
|
||||
});
|
||||
return normalizedSnapshot;
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
userLogger.warn(
|
||||
@@ -290,15 +298,19 @@ export async function clearDirectMessageSnapshot(pubkey) {
|
||||
try {
|
||||
const db = await openSettingsDb();
|
||||
if (db) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(STORE_NAME, "readwrite");
|
||||
const store = tx.objectStore(STORE_NAME);
|
||||
const request = store.delete(dbKey);
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () =>
|
||||
reject(request.error || new Error("Failed to clear snapshot"));
|
||||
});
|
||||
cleared = true;
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(STORE_NAME, "readwrite");
|
||||
const store = tx.objectStore(STORE_NAME);
|
||||
const request = store.delete(dbKey);
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () =>
|
||||
reject(request.error || new Error("Failed to clear snapshot"));
|
||||
});
|
||||
cleared = true;
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
userLogger.warn(
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import "../../scripts/agent/setup-test-env.js";
|
||||
import assert from "node:assert/strict";
|
||||
import { test, afterEach } from "node:test";
|
||||
import { indexedDB, IDBKeyRange } from "fake-indexeddb";
|
||||
|
||||
if (!globalThis.indexedDB) {
|
||||
globalThis.indexedDB = indexedDB;
|
||||
globalThis.IDBKeyRange = IDBKeyRange;
|
||||
}
|
||||
|
||||
import { NostrClient } from "../../js/nostr/client.js";
|
||||
import { NostrService } from "../../js/services/nostrService.js";
|
||||
@@ -11,13 +18,6 @@ import {
|
||||
listMessagesByConversation,
|
||||
} from "../../js/storage/dmDb.js";
|
||||
|
||||
const { indexedDB, IDBKeyRange } = await import("fake-indexeddb");
|
||||
|
||||
if (!globalThis.indexedDB) {
|
||||
globalThis.indexedDB = indexedDB;
|
||||
globalThis.IDBKeyRange = IDBKeyRange;
|
||||
}
|
||||
|
||||
async function deleteDatabase(name) {
|
||||
await new Promise((resolve) => {
|
||||
const request = indexedDB.deleteDatabase(name);
|
||||
@@ -27,6 +27,15 @@ async function deleteDatabase(name) {
|
||||
});
|
||||
}
|
||||
|
||||
async function waitFor(predicate, timeoutMs = 2000, intervalMs = 50, errorMessage = "Timed out") {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
if (predicate()) return;
|
||||
await new Promise((r) => setTimeout(r, intervalMs));
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await deleteDatabase("bitvidDm");
|
||||
await deleteDatabase("bitvidSettings");
|
||||
@@ -46,7 +55,12 @@ class FakeRelayPool {
|
||||
return {
|
||||
on(eventName, handler) {
|
||||
if (eventName === "ok") {
|
||||
setTimeout(() => handler(), 0);
|
||||
// Call synchronously so tests don't depend on scheduling/timers.
|
||||
try {
|
||||
handler();
|
||||
} catch (err) {
|
||||
// Swallow to avoid breaking tests that don't expect thrown errors here.
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@@ -231,10 +245,12 @@ test("DM relay duplication is deduped", async () => {
|
||||
relayPool.flushQueue({ duplicate: true });
|
||||
|
||||
// Poll for the expected state
|
||||
for (let i = 0; i < 20; i++) {
|
||||
if (received.length === 1 && notifications.length === 1) break;
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
await waitFor(
|
||||
() => received.length === 1 && notifications.length === 1,
|
||||
2000,
|
||||
50,
|
||||
`received=${received.length}, notifications=${notifications.length}`
|
||||
);
|
||||
|
||||
assert.equal(received.length, 1, "duplicate relay events should be deduped");
|
||||
assert.equal(notifications.length, 1, "incoming messages should notify once");
|
||||
@@ -291,11 +307,15 @@ test("out-of-order DM delivery keeps newest messages first", async () => {
|
||||
relayPool.queue = [];
|
||||
|
||||
let messages = [];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await waitFor(
|
||||
() => {
|
||||
messages = receiverService.getDirectMessages();
|
||||
if (messages.length === 2) break;
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
return messages.length === 2;
|
||||
},
|
||||
2000,
|
||||
50,
|
||||
"two DM messages should be present"
|
||||
);
|
||||
|
||||
assert.equal(messages.length, 2, "two DM messages should be present");
|
||||
assert.equal(messages[0].plaintext, "Second message");
|
||||
@@ -354,10 +374,12 @@ test("DM reconnect replays do not duplicate messages or reset seen state", async
|
||||
relayPool.flushQueue();
|
||||
|
||||
let conversationId = `dm:${[senderPubkey, receiverPubkey].sort().join(":")}`;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
if (receiverService.getDirectMessageUnseenCount(conversationId) === 1) break;
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
await waitFor(
|
||||
() => receiverService.getDirectMessageUnseenCount(conversationId) === 1,
|
||||
2000,
|
||||
50,
|
||||
"Waiting for unseen count 1"
|
||||
);
|
||||
|
||||
assert.equal(receiverService.getDirectMessageUnseenCount(conversationId), 1);
|
||||
|
||||
@@ -374,6 +396,9 @@ test("DM reconnect replays do not duplicate messages or reset seen state", async
|
||||
subscription.unsub();
|
||||
subscribe();
|
||||
|
||||
if (!relayPool.published.length) {
|
||||
throw new Error("No published events to replay (relayPool.published is empty)");
|
||||
}
|
||||
relayPool.deliver(relayPool.published[0]);
|
||||
|
||||
// Ensure no duplication happens
|
||||
@@ -394,7 +419,3 @@ test("DM reconnect replays do not duplicate messages or reset seen state", async
|
||||
restore();
|
||||
}
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
setTimeout(() => process.exit(0), 100);
|
||||
});
|
||||
Reference in new issue
Block a user