fix(sdk): transport 测试注释修正(sendBeacon 真实网络警示/退避区间)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,8 @@ export type TransportConfig = Required<CtkConfig>;
|
|||||||
// 批量传输:满 batchSize 或 flushMs 到期即发;4xx(除 429)不重试;
|
// 批量传输:满 batchSize 或 flushMs 到期即发;4xx(除 429)不重试;
|
||||||
// 指数退避 + 抖动重试;重试耗尽按本批事件数计 dropped;queueMax 溢出丢最旧。
|
// 指数退避 + 抖动重试;重试耗尽按本批事件数计 dropped;queueMax 溢出丢最旧。
|
||||||
// 红线:任何异常不得冒泡到宿主页(定时器/事件回调内的异常无人捕获)。
|
// 红线:任何异常不得冒泡到宿主页(定时器/事件回调内的异常无人捕获)。
|
||||||
|
// 审查留档:events 目前均为 SDK 自产可序列化对象;若未来引入受控外的 events
|
||||||
|
// 来源,需处理 stringify 抛错时已换出批次的计数(当前静默丢弃不计 dropped)。
|
||||||
export class Transport {
|
export class Transport {
|
||||||
private q: object[] = [];
|
private q: object[] = [];
|
||||||
private timer: ReturnType<typeof setTimeout> | null = null;
|
private timer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ describe("transport", () => {
|
|||||||
t.push({ event_id: "e2" });
|
t.push({ event_id: "e2" });
|
||||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
await vi.advanceTimersByTimeAsync(2000); // 第 1 次退避 500*2^1*jitter ∈ [1000,1400]
|
await vi.advanceTimersByTimeAsync(2000); // 第 1 次退避 500*2^1*jitter ∈ [800,1200]
|
||||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||||
expect(fetchMock.mock.calls[1][1].body).toBe(fetchMock.mock.calls[0][1].body);
|
expect(fetchMock.mock.calls[1][1].body).toBe(fetchMock.mock.calls[0][1].body);
|
||||||
expect((t as unknown as { retry: number }).retry).toBe(0);
|
expect((t as unknown as { retry: number }).retry).toBe(0);
|
||||||
@@ -127,7 +127,7 @@ describe("transport", () => {
|
|||||||
t.push({ event_id: "e2" });
|
t.push({ event_id: "e2" });
|
||||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
await vi.advanceTimersByTimeAsync(2000); // 第 1 次重试(∈[1000,1400])后再失败
|
await vi.advanceTimersByTimeAsync(2000); // 第 1 次重试(∈[800,1200])后再失败
|
||||||
expect(fetchMock).toHaveBeenCalledTimes(2); // 初发 1 + 重试 1,共 2 次
|
expect(fetchMock).toHaveBeenCalledTimes(2); // 初发 1 + 重试 1,共 2 次
|
||||||
expect(t.dropped).toBe(2); // 按入批事件数计,而非按批计
|
expect(t.dropped).toBe(2); // 按入批事件数计,而非按批计
|
||||||
expect((t as unknown as { retry: number }).retry).toBe(0); // 归零供下批复用
|
expect((t as unknown as { retry: number }).retry).toBe(0); // 归零供下批复用
|
||||||
@@ -148,7 +148,7 @@ describe("transport", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("flush(true) 走 sendBeacon(Blob/text/plain),不调 fetch", async () => {
|
it("flush(true) 走 sendBeacon(Blob/text/plain),不调 fetch", async () => {
|
||||||
// happy-dom 无 navigator.sendBeacon——整对象替换并测后还原(unstubAllGlobals)
|
// happy-dom 的 sendBeacon 会发真实网络请求,必须 stubGlobal 替换 navigator(不能只 spy)
|
||||||
const beacon = vi.fn().mockReturnValue(true);
|
const beacon = vi.fn().mockReturnValue(true);
|
||||||
vi.stubGlobal("navigator", { sendBeacon: beacon });
|
vi.stubGlobal("navigator", { sendBeacon: beacon });
|
||||||
const t = new Transport(mkCfg({ batchSize: 10 }));
|
const t = new Transport(mkCfg({ batchSize: 10 }));
|
||||||
|
|||||||
Reference in New Issue
Block a user