feat: 기능 추가 과정중
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
calculateReconnectDelay,
|
||||
defineReconnectPolicy,
|
||||
isReconnectAttemptResetEligible,
|
||||
parseRetryAfterDelay,
|
||||
REALTIME_RECONNECT_CEILINGS,
|
||||
reconnectBudgetRemaining,
|
||||
type ReconnectPolicy,
|
||||
} from "../../../src/adapters/realtime/reconnect-policy.ts";
|
||||
|
||||
const policy = defineReconnectPolicy({
|
||||
baseDelayMs: 1_000,
|
||||
maxDelayMs: 60_000,
|
||||
maxAttempts: 10,
|
||||
maxElapsedMs: 300_000,
|
||||
stableOpenMs: 30_000,
|
||||
});
|
||||
|
||||
describe("realtime reconnect policy", () => {
|
||||
it("uses full jitter and treats a server hint as a not-before floor", () => {
|
||||
expect(
|
||||
calculateReconnectDelay({
|
||||
policy,
|
||||
attemptIndex: 2,
|
||||
remainingElapsedMs: 10_000,
|
||||
random: () => 0.5,
|
||||
}),
|
||||
).toBe(2_000);
|
||||
expect(
|
||||
calculateReconnectDelay({
|
||||
policy,
|
||||
attemptIndex: 2,
|
||||
remainingElapsedMs: 10_000,
|
||||
random: () => 0.5,
|
||||
serverNotBeforeMs: 3_000,
|
||||
}),
|
||||
).toBe(3_000);
|
||||
});
|
||||
|
||||
it("stops instead of clamping a hint past a hard or remaining budget", () => {
|
||||
expect(
|
||||
calculateReconnectDelay({
|
||||
policy,
|
||||
attemptIndex: 0,
|
||||
remainingElapsedMs: 100_000,
|
||||
random: () => 0,
|
||||
serverNotBeforeMs: 60_001,
|
||||
}),
|
||||
).toBeNull();
|
||||
expect(
|
||||
calculateReconnectDelay({
|
||||
policy,
|
||||
attemptIndex: 0,
|
||||
remainingElapsedMs: 3_000,
|
||||
random: () => 0,
|
||||
serverNotBeforeMs: 3_000,
|
||||
}),
|
||||
).toBeNull();
|
||||
expect(
|
||||
calculateReconnectDelay({
|
||||
policy,
|
||||
attemptIndex: 10,
|
||||
remainingElapsedMs: 100_000,
|
||||
random: () => 0,
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("parses only bounded-shape Retry-After syntax for caller validation", () => {
|
||||
expect(parseRetryAfterDelay("12", 0)).toBe(12_000);
|
||||
expect(
|
||||
parseRetryAfterDelay(
|
||||
"Thu, 01 Jan 1970 00:00:20 GMT",
|
||||
5_000,
|
||||
),
|
||||
).toBe(15_000);
|
||||
expect(parseRetryAfterDelay("1.5", 0)).toBeNull();
|
||||
expect(parseRetryAfterDelay("-1", 0)).toBeNull();
|
||||
});
|
||||
|
||||
it("resets attempts only after stable open or a valid signal", () => {
|
||||
expect(
|
||||
isReconnectAttemptResetEligible({
|
||||
policy,
|
||||
openedAtMs: 10,
|
||||
nowMs: 29_000,
|
||||
observedValidHeartbeatOrEvent: false,
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isReconnectAttemptResetEligible({
|
||||
policy,
|
||||
openedAtMs: 10,
|
||||
nowMs: 30_010,
|
||||
observedValidHeartbeatOrEvent: false,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isReconnectAttemptResetEligible({
|
||||
policy,
|
||||
openedAtMs: 10,
|
||||
nowMs: 11,
|
||||
observedValidHeartbeatOrEvent: true,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(reconnectBudgetRemaining(policy, 1_000, 2_000)).toBe(
|
||||
299_000,
|
||||
);
|
||||
expect(reconnectBudgetRemaining(policy, 2_000, 1_000)).toBe(0);
|
||||
});
|
||||
|
||||
it("rejects policies above the implementation ceiling", () => {
|
||||
expect(() =>
|
||||
defineReconnectPolicy({
|
||||
...policy,
|
||||
maxAttempts: 11,
|
||||
}),
|
||||
).toThrow(TypeError);
|
||||
});
|
||||
|
||||
it("keeps aborted-task drain below the absolute implementation ceiling", () => {
|
||||
expect(REALTIME_RECONNECT_CEILINGS.drainTimeoutMs).toBe(2_000);
|
||||
expect(
|
||||
REALTIME_RECONNECT_CEILINGS.drainTimeoutMs,
|
||||
).toBeLessThanOrEqual(
|
||||
REALTIME_RECONNECT_CEILINGS.maxDrainTimeoutMs,
|
||||
);
|
||||
expect(
|
||||
REALTIME_RECONNECT_CEILINGS.maxDrainTimeoutMs,
|
||||
).toBe(30_000);
|
||||
});
|
||||
|
||||
it("snapshots only exact own data properties", () => {
|
||||
const inherited = Object.create(policy) as ReconnectPolicy;
|
||||
const accessor = Object.defineProperties(
|
||||
{},
|
||||
{
|
||||
baseDelayMs: { enumerable: true, value: 1_000 },
|
||||
maxDelayMs: {
|
||||
enumerable: true,
|
||||
get: () => 60_000,
|
||||
},
|
||||
maxAttempts: { enumerable: true, value: 10 },
|
||||
maxElapsedMs: { enumerable: true, value: 300_000 },
|
||||
stableOpenMs: { enumerable: true, value: 30_000 },
|
||||
},
|
||||
) as ReconnectPolicy;
|
||||
|
||||
expect(() => defineReconnectPolicy(inherited)).toThrow(TypeError);
|
||||
expect(() => defineReconnectPolicy(accessor)).toThrow(TypeError);
|
||||
expect(() =>
|
||||
defineReconnectPolicy({
|
||||
...policy,
|
||||
extra: true,
|
||||
} as ReconnectPolicy),
|
||||
).toThrow(TypeError);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user