add support heartbeat to couchdb-changes-stream
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import {
|
||||
CouchDBChangesStream,
|
||||
CouchDBChange,
|
||||
} from "../src/CouchDBChangesStream";
|
||||
|
||||
describe("CouchDBChangesStream - Heartbeat", () => {
|
||||
let mockFetch: jest.Mock;
|
||||
const dbUrl = "http://localhost:5984/mydb";
|
||||
|
||||
beforeEach(() => {
|
||||
mockFetch = jest.fn();
|
||||
jest.useFakeTimers(); // Подключаем fake timers
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers(); // Отключаем fake timers после каждого теста
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("should reset heartbeat timer on receiving data", async () => {
|
||||
const mockResponseData = {
|
||||
seq: "1",
|
||||
id: "doc1",
|
||||
changes: [{ rev: "1-abc" }],
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
body: new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode(JSON.stringify(mockResponseData) + "\n"),
|
||||
);
|
||||
controller.close();
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const stream = new CouchDBChangesStream(dbUrl, {
|
||||
feed: "continuous",
|
||||
heartbeat: 30_000, // 30 секунд
|
||||
live: true,
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
||||
const iterator = stream[Symbol.asyncIterator]();
|
||||
|
||||
const heartbeatSpy = jest.spyOn(global, "setTimeout");
|
||||
|
||||
const firstChange = await iterator.next();
|
||||
expect(firstChange.done).toBe(false);
|
||||
expect(firstChange.value.seq).toBe("1");
|
||||
// Таймер heartbeat должен был быть установлен
|
||||
expect(heartbeatSpy).toHaveBeenCalledWith(expect.any(Function), 33_000); // 5000 + 10% буфер,
|
||||
});
|
||||
|
||||
it("should throw an error if no data is received within heartbeat + buffer", async () => {
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
body: new ReadableStream({
|
||||
start(controller) {
|
||||
// do not send any data to test heartbeat timeout
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const stream = new CouchDBChangesStream(dbUrl, {
|
||||
feed: "continuous",
|
||||
heartbeat: 20_000,
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
||||
const iterator = stream[Symbol.asyncIterator]();
|
||||
|
||||
const fetchPromise = iterator.next();
|
||||
|
||||
// Прокручиваем время до истечения heartbeat таймера (20_000 + 10%)
|
||||
jest.advanceTimersByTime(22_000);
|
||||
|
||||
await expect(fetchPromise).rejects.toThrow(
|
||||
"Heartbeat timeout: no data received from server after 22000 ms",
|
||||
);
|
||||
});
|
||||
|
||||
it("should continue working if data is received before heartbeat timeout", async () => {
|
||||
const mockResponseData = {
|
||||
seq: "1",
|
||||
id: "doc1",
|
||||
changes: [{ rev: "1-abc" }],
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
ok: true,
|
||||
body: new ReadableStream({
|
||||
start(controller) {
|
||||
// Отправляем данные перед истечением таймера
|
||||
setTimeout(() => {
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode(JSON.stringify(mockResponseData) + "\n"),
|
||||
);
|
||||
controller.close();
|
||||
}, 2000); // Data arrives before heartbeat timeout (3000)
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const stream = new CouchDBChangesStream(dbUrl, {
|
||||
feed: "continuous",
|
||||
heartbeat: 3_000,
|
||||
fetch: mockFetch,
|
||||
live: true,
|
||||
});
|
||||
|
||||
const iterator = stream[Symbol.asyncIterator]();
|
||||
const nextPromise = iterator.next();
|
||||
|
||||
await Promise.resolve();
|
||||
jest.advanceTimersByTime(2_999);
|
||||
await Promise.resolve();
|
||||
|
||||
const firstChange = await nextPromise;
|
||||
expect(firstChange.done).toBe(false);
|
||||
expect(firstChange.value.seq).toBe("1");
|
||||
|
||||
// Let's make sure that a timeout error does not occur
|
||||
await Promise.resolve();
|
||||
jest.advanceTimersByTime(2_999);
|
||||
await Promise.resolve();
|
||||
|
||||
const secondFetch = iterator.next();
|
||||
await expect(secondFetch).resolves.not.toThrow();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user