import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; const connect = vi.fn().mockResolvedValue(undefined); const quit = vi.fn().mockResolvedValue(undefined); const setex = vi.fn().mockResolvedValue("OK"); const get = vi.fn().mockResolvedValue(null); vi.mock("ioredis", () => ({ default: vi.fn().mockImplementation(() => ({ connect, quit, setex, get, })), })); import { resolveQuoteSessionStoragePath, saveQuoteSessionFromContext, } from "@/lib/rpa/quote-session-store"; describe("quote-session-store", () => { const prevCwd = process.cwd(); const prevRedisUrl = process.env.REDIS_URL; let tmpDir = ""; afterEach(() => { process.chdir(prevCwd); if (prevRedisUrl === undefined) { delete process.env.REDIS_URL; } else { process.env.REDIS_URL = prevRedisUrl; } if (tmpDir) { fs.rmSync(tmpDir, { recursive: true, force: true }); tmpDir = ""; } vi.clearAllMocks(); }); it("saveQuoteSessionFromContext 写入本地文件并返回 sessionId", async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "quote-session-")); process.chdir(tmpDir); process.env.REDIS_URL = "redis://127.0.0.1:6379"; const storageState = vi.fn().mockImplementation(async ({ path: outPath }: { path: string }) => { fs.mkdirSync(path.dirname(outPath), { recursive: true }); fs.writeFileSync(outPath, "{}", "utf8"); }); const sessionId = await saveQuoteSessionFromContext( { storageState } as never, { pickup: { street: "123 Main", city: "LA", state: "CA", zip: "90001", }, delivery: { street: "456 Oak", city: "Dallas", state: "TX", zip: "75201", }, }, ); expect(sessionId).toMatch( /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, ); expect( fs.existsSync(path.join(tmpDir, ".rpa", "sessions", `${sessionId}.json`)), ).toBe(true); expect(setex).toHaveBeenCalled(); }); it("resolveQuoteSessionStoragePath Redis 失败时回退本地文件", async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "quote-session-")); process.chdir(tmpDir); process.env.REDIS_URL = "redis://127.0.0.1:6379"; connect.mockRejectedValueOnce(new Error("redis down")); const sessionId = "11111111-1111-4111-8111-111111111111"; const sessionsDir = path.join(tmpDir, ".rpa", "sessions"); fs.mkdirSync(sessionsDir, { recursive: true }); const file = path.join(sessionsDir, `${sessionId}.json`); fs.writeFileSync(file, "{}", "utf8"); const resolved = await resolveQuoteSessionStoragePath(sessionId); expect(resolved).toBe(file); }); });