fix(sdk): 超预算组丢最旧闭环+4xx丢弃计数(I1/M1)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-18 14:27:26 +08:00
parent 6432435c93
commit 08495bc365
2 changed files with 94 additions and 27 deletions
+44 -3
View File
@@ -110,20 +110,22 @@ describe("transport", () => {
expect(fetchMock.mock.calls[1][1].body).toBe(fetchMock.mock.calls[0][1].body);
});
it("400 不重试(参数错,重试无意义)", async () => {
it("400 不重试(参数错,重试无意义),事件落 dropped 桶(四桶对账无幽灵丢失)", async () => {
fetchMock.mockResolvedValueOnce(res(400));
const t = new Transport(mkCfg({ batchSize: 1 }));
t.push({ event_id: "e1" });
await vi.advanceTimersByTimeAsync(10000);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(t.dropped).toBe(1); // M1:4xx 不重试但必须计丢弃
});
it("403 不重试(权限错,重试只会掩盖配置问题)", async () => {
it("403 不重试(权限错,重试只会掩盖配置问题),事件落 dropped 桶", async () => {
fetchMock.mockResolvedValueOnce(res(403));
const t = new Transport(mkCfg({ batchSize: 1 }));
t.push({ event_id: "e1" });
await vi.advanceTimersByTimeAsync(10000);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(t.dropped).toBe(1);
});
it("429 要重试(限流是暂态)", async () => {
@@ -313,7 +315,8 @@ describe("transport", () => {
visitor_id: "v1",
session_id: "s1",
page_id: "p1",
properties: { blob: "x".repeat(65_000) }, // 信封+事件 > 60KB
// 须落在 (60_000, 64_000] 窗口:超信标预算触发降级,又不触发 fetch 64KB 裁剪守卫
properties: { blob: "x".repeat(62_000) },
});
t.flush(true);
expect(beacon).not.toHaveBeenCalled(); // 超预算不走信标
@@ -405,6 +408,44 @@ describe("transport", () => {
{ event_id: "safe", visitor_id: "v1", session_id: "s1", page_id: "p1" },
]);
});
// ---- I1/M1:超预算组送达闭环 + 4xx 丢弃计数 ----
it("超 64KB 组丢最旧闭环:裁剪至预算内送达,最旧事件计 dropped", () => {
const t = new Transport(mkCfg({ batchSize: 10 }));
const big = (id: string, n: number) => ({
event_id: id,
visitor_id: "v1",
session_id: "s1",
page_id: "p1",
properties: { blob: "x".repeat(n) },
});
t.push(big("e1", 33_000));
t.push(big("e2", 33_000));
t.push(big("e3", 2_000));
t.flush();
expect(fetchMock).toHaveBeenCalledTimes(1);
const body = JSON.parse(fetchMock.mock.calls[0][1].body);
expect(body.events.map((e: { event_id: string }) => e.event_id)).toEqual(["e2", "e3"]); // e1(最旧)被裁
expect(t.dropped).toBe(1); // 被裁的最旧事件逐条计 dropped
});
it("单事件超 64KB:整组裁空不发送不空转重试,全量计 dropped", async () => {
fetchMock.mockResolvedValue(res(500)); // 若误发送会进入重试空转——用调用数守卫
const t = new Transport(mkCfg({ batchSize: 10, maxRetry: 3 }));
t.push({
event_id: "huge",
visitor_id: "v1",
session_id: "s1",
page_id: "p1",
properties: { blob: "x".repeat(70_000) },
});
t.flush();
expect(fetchMock).not.toHaveBeenCalled(); // keepalive 64KiB 硬限确定性失败——不空转
await vi.advanceTimersByTimeAsync(60_000);
expect(fetchMock).not.toHaveBeenCalled();
expect(t.dropped).toBe(1); // 整组丢弃完毕
});
});
describe("mergeConfig", () => {