Files
UHD_Media_Tool/apps/server/test/sources-identity/snapshot-deadline.test.ts
T
rootandClaude Opus 5.5 5c74666148 fix(sources): 站点快照加总截止时间;定时任务兜住收尾写库错误
OurBits 一次定时快照卡在过验证 / 代抓的等待里 10 小时,任务一直停在运行中。
快照整体 5 分钟截止,到点按失败收尾;调度器执行的拒绝不再变成未处理错误。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
2026-10-08 03:15:53 +08:00

63 lines
2.1 KiB
TypeScript

import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { snapshotSite } from "../../src/modules/sources-identity/service/snapshot.ts";
import { createHarness, type Harness, pgAvailable } from "./harness.ts";
// 快照总截止时间:站点请求卡住且不理会取消信号(过人机验证、浏览器代抓这类等待)时,
// 快照到点按失败收尾、写完 site_snapshot,任务不会一直停在运行中(2026-10-07 OurBits 卡了 10 小时)。
const up = await pgAvailable();
// biome-ignore lint/suspicious/noExplicitAny: 响应体断言
const json = async (r: Response): Promise<any> => {
const body = await r.json();
if (!r.ok) throw new Error(`${r.status} ${JSON.stringify(body)}`);
return body.data;
};
describe.skipIf(!up)("快照截止时间", () => {
let h: Harness;
let siteId: string;
// 永不返回,也不理会 signal
const hang = (() => new Promise<Response>(() => {})) as typeof fetch;
beforeAll(async () => {
h = await createHarness("snapdeadline", { fetch: hang });
const s = await json(
await h.req("/sites", {
body: {
key: "audiences",
name: "观众",
adapter: "nexusphp",
baseUrl: "https://aud.test/",
rateLimit: { minIntervalMs: 0, concurrency: 10 },
},
}),
);
siteId = s.id;
await json(
await h.req(`/sites/${siteId}/credentials`, {
method: "PUT",
body: { kind: "cookie", cookie: "c_secure_uid=1; c_secure_pass=abc" },
}),
);
});
afterAll(async () => {
await h?.close();
});
it("请求卡住 → 到点失败,快照记录收尾", async () => {
const t0 = Date.now();
const r = await snapshotSite(h.rt, { siteId, kind: "periodic" }, { deadlineMs: 300 });
expect(Date.now() - t0).toBeLessThan(5_000);
expect(r.status).toBe("failed");
expect(r.error).toContain("没完成");
const snap = await h.db
.selectFrom("site_snapshot")
.selectAll()
.where("id", "=", r.snapshotId!)
.executeTakeFirstOrThrow();
expect(snap.status).toBe("failed");
expect(snap.finished_at).not.toBeNull();
});
});