diff --git a/extensions/bluebubbles/src/monitor.test.ts b/extensions/bluebubbles/src/monitor.test.ts index 0dcccbef8..96e85e84b 100644 --- a/extensions/bluebubbles/src/monitor.test.ts +++ b/extensions/bluebubbles/src/monitor.test.ts @@ -220,6 +220,12 @@ function createMockResponse(): ServerResponse & { body: string; statusCode: numb return res; } +const flushAsync = async () => { + for (let i = 0; i < 2; i += 1) { + await new Promise((resolve) => setImmediate(resolve)); + } +}; + describe("BlueBubbles webhook monitor", () => { let unregister: () => void; @@ -506,7 +512,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(resolveChatGuidForTarget).toHaveBeenCalledWith( expect.objectContaining({ @@ -554,7 +560,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(resolveChatGuidForTarget).not.toHaveBeenCalled(); expect(sendMessageBlueBubbles).toHaveBeenCalledWith( @@ -601,7 +607,7 @@ describe("BlueBubbles webhook monitor", () => { await handleBlueBubblesWebhookRequest(req, res); // Wait for async processing - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(res.statusCode).toBe(200); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); @@ -640,7 +646,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(res.statusCode).toBe(200); expect(mockDispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); @@ -681,7 +687,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockUpsertPairingRequest).toHaveBeenCalled(); expect(mockDispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); @@ -724,7 +730,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockUpsertPairingRequest).toHaveBeenCalled(); // Should not send pairing reply since created=false @@ -765,7 +771,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); }); @@ -802,7 +808,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); @@ -842,7 +848,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); }); @@ -880,7 +886,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); @@ -919,7 +925,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); @@ -958,7 +964,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); }); @@ -999,7 +1005,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); const callArgs = mockDispatchReplyWithBufferedBlockDispatcher.mock.calls[0][0]; @@ -1040,7 +1046,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); @@ -1078,7 +1084,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); }); @@ -1121,7 +1127,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); const callArgs = mockDispatchReplyWithBufferedBlockDispatcher.mock.calls[0][0]; @@ -1167,7 +1173,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); const callArgs = mockDispatchReplyWithBufferedBlockDispatcher.mock.calls[0][0]; @@ -1213,7 +1219,7 @@ describe("BlueBubbles webhook monitor", () => { const originalRes = createMockResponse(); await handleBlueBubblesWebhookRequest(originalReq, originalRes); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); // Only assert the reply message behavior below. mockDispatchReplyWithBufferedBlockDispatcher.mockClear(); @@ -1237,7 +1243,7 @@ describe("BlueBubbles webhook monitor", () => { const replyRes = createMockResponse(); await handleBlueBubblesWebhookRequest(replyReq, replyRes); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); const callArgs = mockDispatchReplyWithBufferedBlockDispatcher.mock.calls[0][0]; @@ -1283,7 +1289,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); const callArgs = mockDispatchReplyWithBufferedBlockDispatcher.mock.calls[0][0]; @@ -1331,7 +1337,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(sendBlueBubblesReaction).toHaveBeenCalledWith( expect.objectContaining({ @@ -1384,7 +1390,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); // Should process even without mention because it's an authorized control command expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); @@ -1427,7 +1433,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); @@ -1470,7 +1476,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(markBlueBubblesChatRead).toHaveBeenCalled(); }); @@ -1511,7 +1517,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(markBlueBubblesChatRead).not.toHaveBeenCalled(); }); @@ -1554,7 +1560,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); // Should call typing start when reply flow triggers it. expect(sendBlueBubblesTyping).toHaveBeenCalledWith( @@ -1604,7 +1610,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(sendBlueBubblesTyping).toHaveBeenCalledWith( expect.any(String), @@ -1649,7 +1655,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(sendBlueBubblesTyping).toHaveBeenCalledWith( expect.any(String), @@ -1697,7 +1703,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); // Outbound message ID uses short ID "2" (inbound msg-1 is "1", outbound msg-123 is "2") expect(mockEnqueueSystemEvent).toHaveBeenCalledWith( @@ -1742,7 +1748,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockEnqueueSystemEvent).toHaveBeenCalledWith( expect.stringContaining("reaction added"), @@ -1782,7 +1788,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockEnqueueSystemEvent).toHaveBeenCalledWith( expect.stringContaining("reaction removed"), @@ -1822,7 +1828,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockEnqueueSystemEvent).not.toHaveBeenCalled(); }); @@ -1860,7 +1866,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockEnqueueSystemEvent).toHaveBeenCalledWith( expect.stringContaining("👍"), @@ -1901,7 +1907,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled(); const callArgs = mockDispatchReplyWithBufferedBlockDispatcher.mock.calls[0][0]; @@ -1941,7 +1947,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); // The short ID "1" should resolve back to the full UUID expect(resolveBlueBubblesMessageId("1")).toBe("msg-uuid-12345"); @@ -1993,7 +1999,7 @@ describe("BlueBubbles webhook monitor", () => { const res = createMockResponse(); await handleBlueBubblesWebhookRequest(req, res); - await new Promise((resolve) => setTimeout(resolve, 50)); + await flushAsync(); expect(mockDispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); diff --git a/src/media/server.test.ts b/src/media/server.test.ts index 875088cbf..693ba5940 100644 --- a/src/media/server.test.ts +++ b/src/media/server.test.ts @@ -14,6 +14,19 @@ vi.mock("./store.js", () => ({ const { startMediaServer } = await import("./server.js"); +const waitForFileRemoval = async (file: string, timeoutMs = 200) => { + const start = Date.now(); + while (Date.now() - start < timeoutMs) { + try { + await fs.stat(file); + } catch { + return; + } + await new Promise((resolve) => setTimeout(resolve, 5)); + } + throw new Error(`timed out waiting for ${file} removal`); +}; + describe("media server", () => { beforeAll(async () => { await fs.rm(MEDIA_DIR, { recursive: true, force: true }); @@ -32,8 +45,7 @@ describe("media server", () => { const res = await fetch(`http://localhost:${port}/media/file1`); expect(res.status).toBe(200); expect(await res.text()).toBe("hello"); - await new Promise((r) => setTimeout(r, 600)); - await expect(fs.stat(file)).rejects.toThrow(); + await waitForFileRemoval(file); await new Promise((r) => server.close(r)); });