|
|
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
|
|
|
import type { BrowserContext, Page } from "playwright";
|
|
|
|
|
|
vi.mock("@/workers/rpa/page-prep", () => ({
|
|
|
dismissCookieConsent: vi.fn().mockResolvedValue(undefined),
|
|
|
scrollQuoteWidgetIntoView: vi.fn().mockResolvedValue(undefined),
|
|
|
}));
|
|
|
|
|
|
import {
|
|
|
hasParkedQuoteSession,
|
|
|
parkQuoteSession,
|
|
|
preheatParkedQuoteSession,
|
|
|
releaseAllParkedQuoteSessions,
|
|
|
releaseParkedQuoteSession,
|
|
|
takeParkedQuoteSession,
|
|
|
canPersistParkedQuoteSession,
|
|
|
getParkedQuoteSessionTtlMs,
|
|
|
sweepExpiredParkedSessions,
|
|
|
} from "@/workers/rpa/parked-quote-session";
|
|
|
|
|
|
function mockSession(id: string) {
|
|
|
const page = {
|
|
|
close: vi.fn().mockResolvedValue(undefined),
|
|
|
} as unknown as Page;
|
|
|
const context = {
|
|
|
close: vi.fn().mockResolvedValue(undefined),
|
|
|
} as unknown as BrowserContext;
|
|
|
return { id, page, context };
|
|
|
}
|
|
|
|
|
|
describe("parked-quote-session", () => {
|
|
|
beforeEach(async () => {
|
|
|
vi.stubEnv("RPA_WORKER_ID", "test-worker-1");
|
|
|
await releaseAllParkedQuoteSessions();
|
|
|
});
|
|
|
|
|
|
afterEach(async () => {
|
|
|
await releaseAllParkedQuoteSessions();
|
|
|
vi.unstubAllEnvs();
|
|
|
});
|
|
|
|
|
|
it("park → take 接管同一会话", async () => {
|
|
|
const { id, page, context } = mockSession("sess-a");
|
|
|
await parkQuoteSession(id, page, context);
|
|
|
expect(hasParkedQuoteSession(id)).toBe(true);
|
|
|
|
|
|
const taken = await takeParkedQuoteSession(id);
|
|
|
expect(taken?.page).toBe(page);
|
|
|
expect(hasParkedQuoteSession(id)).toBe(false);
|
|
|
});
|
|
|
|
|
|
it("release 关闭 page/context", async () => {
|
|
|
const { id, page, context } = mockSession("sess-b");
|
|
|
await parkQuoteSession(id, page, context);
|
|
|
expect(await releaseParkedQuoteSession(id)).toBe(true);
|
|
|
expect(page.close).toHaveBeenCalled();
|
|
|
expect(context.close).toHaveBeenCalled();
|
|
|
});
|
|
|
|
|
|
it("take 不存在的 session 返回 null", async () => {
|
|
|
expect(await takeParkedQuoteSession("missing")).toBeNull();
|
|
|
});
|
|
|
|
|
|
it("preheat 对驻留 session 返回 true", async () => {
|
|
|
const { id, page, context } = mockSession("sess-c");
|
|
|
await parkQuoteSession(id, page, context);
|
|
|
expect(await preheatParkedQuoteSession(id)).toBe(true);
|
|
|
});
|
|
|
|
|
|
it("canPersistParkedQuoteSession 需 RPA_WORKER_ID", () => {
|
|
|
vi.stubEnv("RPA_WORKER_ID", "");
|
|
|
expect(canPersistParkedQuoteSession()).toBe(false);
|
|
|
vi.stubEnv("RPA_WORKER_ID", "w1");
|
|
|
expect(canPersistParkedQuoteSession()).toBe(true);
|
|
|
});
|
|
|
|
|
|
it("自定义 ttlMs:过期后 sweep 释放", async () => {
|
|
|
vi.useFakeTimers();
|
|
|
const { id, page, context } = mockSession("sess-ttl");
|
|
|
await parkQuoteSession(id, page, context, { ttlMs: 5_000 });
|
|
|
expect(getParkedQuoteSessionTtlMs(id)).toBe(5_000);
|
|
|
expect(hasParkedQuoteSession(id)).toBe(true);
|
|
|
vi.advanceTimersByTime(5_001);
|
|
|
expect(sweepExpiredParkedSessions()).toBe(1);
|
|
|
// release 为 fire-and-forget,等微任务关掉 page
|
|
|
await Promise.resolve();
|
|
|
await Promise.resolve();
|
|
|
expect(hasParkedQuoteSession(id)).toBe(false);
|
|
|
expect(page.close).toHaveBeenCalled();
|
|
|
vi.useRealTimers();
|
|
|
});
|
|
|
});
|