Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
86 lines
3.1 KiB
TypeScript
86 lines
3.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();
|
|
});
|
|
|
|
it("旧的「运行中」记录在下一次快照开始时按中断收尾,刚开始的不动", async () => {
|
|
const mk = (ago: number) =>
|
|
h.db
|
|
.insertInto("site_snapshot")
|
|
.values({
|
|
site_id: siteId,
|
|
kind: "periodic",
|
|
filters: "{}",
|
|
definition_version: "t",
|
|
started_at: new Date(Date.now() - ago),
|
|
})
|
|
.returning("id")
|
|
.executeTakeFirstOrThrow();
|
|
const stale = await mk(3_600_000);
|
|
const fresh = await mk(100);
|
|
await snapshotSite(h.rt, { siteId, kind: "periodic" }, { deadlineMs: 300 });
|
|
const get = (id: string) =>
|
|
h.db.selectFrom("site_snapshot").selectAll().where("id", "=", id).executeTakeFirstOrThrow();
|
|
expect(await get(stale.id)).toMatchObject({ status: "failed" });
|
|
expect((await get(stale.id)).error).toMatchObject({ message: "上次快照中断,没有收尾" });
|
|
expect((await get(fresh.id)).status).toBe("running");
|
|
});
|
|
});
|