mirror of
https://github.com/YuzuZensai/Termix.git
synced 2026-11-12 15:01:57 +00:00
121 lines
3.3 KiB
TypeScript
121 lines
3.3 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|||
|
|
import {
|
||
|
|
computeAdaptivePollDelay,
|
||
|
|
getPollingEnvironmentMultiplier,
|
||
|
|
runAdaptivePolling,
|
||
|
|
} from "../../lib/adaptive-polling.ts";
|
||
|
|
|
||
|
|
describe("adaptive polling", () => {
|
||
|
|
afterEach(() => vi.useRealTimers());
|
||
|
|
|
||
|
|
it("slows down after stable results and backs off after failures", () => {
|
||
|
|
const policy = {
|
||
|
|
minIntervalMs: 1000,
|
||
|
|
maxIntervalMs: 16000,
|
||
|
|
stablePollsPerStep: 2,
|
||
|
|
jitterRatio: 0,
|
||
|
|
};
|
||
|
|
expect(
|
||
|
|
computeAdaptivePollDelay(policy, {
|
||
|
|
stablePolls: 4,
|
||
|
|
consecutiveFailures: 0,
|
||
|
|
}),
|
||
|
|
).toBe(4000);
|
||
|
|
expect(
|
||
|
|
computeAdaptivePollDelay(policy, {
|
||
|
|
stablePolls: 0,
|
||
|
|
consecutiveFailures: 3,
|
||
|
|
}),
|
||
|
|
).toBe(8000);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("adds bounded jitter", () => {
|
||
|
|
const policy = {
|
||
|
|
minIntervalMs: 1000,
|
||
|
|
maxIntervalMs: 10000,
|
||
|
|
jitterRatio: 0.1,
|
||
|
|
};
|
||
|
|
const state = { stablePolls: 0, consecutiveFailures: 2 };
|
||
|
|
expect(computeAdaptivePollDelay(policy, state, () => 0)).toBe(3600);
|
||
|
|
expect(computeAdaptivePollDelay(policy, state, () => 1)).toBe(4400);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("limits request duty cycle when polling is expensive", () => {
|
||
|
|
expect(
|
||
|
|
computeAdaptivePollDelay(
|
||
|
|
{
|
||
|
|
minIntervalMs: 1000,
|
||
|
|
maxIntervalMs: 30000,
|
||
|
|
maxRequestDutyCycle: 0.2,
|
||
|
|
jitterRatio: 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
stablePolls: 0,
|
||
|
|
consecutiveFailures: 0,
|
||
|
|
lastPollDurationMs: 2000,
|
||
|
|
},
|
||
|
|
),
|
||
|
|
).toBe(8000);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("slows non-critical polling on constrained connections", () => {
|
||
|
|
expect(getPollingEnvironmentMultiplier({ saveData: true })).toBe(2);
|
||
|
|
expect(getPollingEnvironmentMultiplier({ effectiveType: "2g" })).toBe(1.75);
|
||
|
|
expect(getPollingEnvironmentMultiplier({ effectiveType: "4g" })).toBe(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("never overlaps requests", async () => {
|
||
|
|
vi.useFakeTimers();
|
||
|
|
let resolvePoll!: () => void;
|
||
|
|
const poll = vi.fn(
|
||
|
|
() =>
|
||
|
|
new Promise<void>((resolve) => {
|
||
|
|
resolvePoll = resolve;
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
const stop = runAdaptivePolling(
|
||
|
|
poll,
|
||
|
|
{ minIntervalMs: 1000, maxIntervalMs: 4000, jitterRatio: 0 },
|
||
|
|
{ runImmediately: true },
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(poll).toHaveBeenCalledTimes(1);
|
||
|
|
await vi.advanceTimersByTimeAsync(5000);
|
||
|
|
expect(poll).toHaveBeenCalledTimes(1);
|
||
|
|
resolvePoll();
|
||
|
|
await Promise.resolve();
|
||
|
|
await vi.advanceTimersByTimeAsync(1000);
|
||
|
|
expect(poll).toHaveBeenCalledTimes(2);
|
||
|
|
stop();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("slows stable data and resets when data changes", async () => {
|
||
|
|
vi.useFakeTimers();
|
||
|
|
const poll = vi
|
||
|
|
.fn<() => Promise<boolean>>()
|
||
|
|
.mockResolvedValueOnce(false)
|
||
|
|
.mockResolvedValueOnce(false)
|
||
|
|
.mockResolvedValueOnce(true)
|
||
|
|
.mockResolvedValue(false);
|
||
|
|
const stop = runAdaptivePolling(
|
||
|
|
poll,
|
||
|
|
{
|
||
|
|
minIntervalMs: 1000,
|
||
|
|
maxIntervalMs: 8000,
|
||
|
|
stablePollsPerStep: 1,
|
||
|
|
jitterRatio: 0,
|
||
|
|
},
|
||
|
|
{ runImmediately: true },
|
||
|
|
);
|
||
|
|
|
||
|
|
await Promise.resolve();
|
||
|
|
await vi.advanceTimersByTimeAsync(2000);
|
||
|
|
expect(poll).toHaveBeenCalledTimes(2);
|
||
|
|
await vi.advanceTimersByTimeAsync(4000);
|
||
|
|
expect(poll).toHaveBeenCalledTimes(3);
|
||
|
|
await vi.advanceTimersByTimeAsync(1000);
|
||
|
|
expect(poll).toHaveBeenCalledTimes(4);
|
||
|
|
stop();
|
||
|
|
});
|
||
|
|
});
|