chore: initialize qiming workspace repository
This commit is contained in:
@@ -0,0 +1,558 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import {
|
||||
buildRedirectUrl,
|
||||
buildNewSessionUrl,
|
||||
buildChatSessionUrl,
|
||||
syncSessionCookie,
|
||||
syncCookieAndGetRedirectUrl,
|
||||
syncCookieAndGetNewSessionUrl,
|
||||
syncCookieAndGetChatUrl,
|
||||
persistTicketCookie,
|
||||
} from "./sessionUrl";
|
||||
|
||||
const { mockSettings, mockSession, mockLogger } = vi.hoisted(() => ({
|
||||
mockSettings: { get: vi.fn(), set: vi.fn() },
|
||||
mockSession: { setCookie: vi.fn(), getCookie: vi.fn() },
|
||||
mockLogger: {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.stubGlobal("window", {
|
||||
electronAPI: { settings: mockSettings, session: mockSession },
|
||||
});
|
||||
|
||||
const mockGetCurrentAuth = vi.fn();
|
||||
vi.mock("../core/auth", () => ({
|
||||
getCurrentAuth: (...args: unknown[]) => mockGetCurrentAuth(...args),
|
||||
}));
|
||||
vi.mock("./logService", () => ({
|
||||
logger: mockLogger,
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockSession.setCookie.mockResolvedValue({ success: true });
|
||||
mockSession.getCookie.mockResolvedValue({ success: true, found: false });
|
||||
});
|
||||
|
||||
describe("buildRedirectUrl", () => {
|
||||
it("strips trailing slashes and builds correct URL", () => {
|
||||
expect(buildRedirectUrl("https://example.com///", "user1")).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/user1?hideMenu=true",
|
||||
);
|
||||
expect(buildRedirectUrl("https://example.com/", "user1")).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/user1?hideMenu=true",
|
||||
);
|
||||
expect(buildRedirectUrl("https://example.com", "user1")).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/user1?hideMenu=true",
|
||||
);
|
||||
});
|
||||
|
||||
it("works with numeric configId", () => {
|
||||
expect(buildRedirectUrl("https://example.com", 42)).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/42?hideMenu=true",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildNewSessionUrl", () => {
|
||||
it("builds correct new-session URL", () => {
|
||||
expect(buildNewSessionUrl("https://example.com", 42)).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/new/42?hideMenu=true",
|
||||
);
|
||||
});
|
||||
|
||||
it("strips trailing slashes", () => {
|
||||
expect(buildNewSessionUrl("https://example.com///", 42)).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/new/42?hideMenu=true",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildChatSessionUrl", () => {
|
||||
it("builds correct URL with session id", () => {
|
||||
expect(buildChatSessionUrl("https://example.com", "sess-abc-123")).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/chat/sess-abc-123?hideMenu=true",
|
||||
);
|
||||
});
|
||||
|
||||
it("strips trailing slashes", () => {
|
||||
expect(buildChatSessionUrl("https://example.com///", "s1")).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/chat/s1?hideMenu=true",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("syncSessionCookie", () => {
|
||||
it("不设 domain 和 secure,由主进程根据 URL scheme 判断", async () => {
|
||||
await syncSessionCookie("https://app.example.com:8080/path", "tok123");
|
||||
|
||||
const payload = mockSession.setCookie.mock.calls[0][0];
|
||||
expect(payload).toEqual({
|
||||
url: "https://app.example.com:8080/path",
|
||||
name: "ticket",
|
||||
value: "tok123",
|
||||
httpOnly: true,
|
||||
});
|
||||
expect(payload).not.toHaveProperty("domain");
|
||||
expect(payload).not.toHaveProperty("secure");
|
||||
});
|
||||
|
||||
it("invalid URL 也不设 domain 和 secure", async () => {
|
||||
await syncSessionCookie("not-a-valid-url", "tok123");
|
||||
|
||||
const payload = mockSession.setCookie.mock.calls[0][0];
|
||||
expect(payload).toEqual({
|
||||
url: "not-a-valid-url",
|
||||
name: "ticket",
|
||||
value: "tok123",
|
||||
httpOnly: true,
|
||||
});
|
||||
expect(payload).not.toHaveProperty("domain");
|
||||
expect(payload).not.toHaveProperty("secure");
|
||||
});
|
||||
|
||||
it("host-only cookie — 不设 domain,与 webview Set-Cookie 行为一致", async () => {
|
||||
await syncSessionCookie("https://example.com", "tok");
|
||||
|
||||
const payload = mockSession.setCookie.mock.calls[0][0];
|
||||
expect(payload).not.toHaveProperty("domain");
|
||||
});
|
||||
|
||||
it("IPv4 host 也不设 domain", async () => {
|
||||
await syncSessionCookie("http://127.0.0.1:8080", "tok-ip");
|
||||
|
||||
const payload = mockSession.setCookie.mock.calls[0][0];
|
||||
expect(payload).toEqual({
|
||||
url: "http://127.0.0.1:8080",
|
||||
name: "ticket",
|
||||
value: "tok-ip",
|
||||
httpOnly: true,
|
||||
});
|
||||
expect(payload).not.toHaveProperty("domain");
|
||||
});
|
||||
|
||||
it("localhost 也不设 domain", async () => {
|
||||
await syncSessionCookie("http://localhost:3000", "tok-local");
|
||||
|
||||
const payload = mockSession.setCookie.mock.calls[0][0];
|
||||
expect(payload).toEqual({
|
||||
url: "http://localhost:3000",
|
||||
name: "ticket",
|
||||
value: "tok-local",
|
||||
httpOnly: true,
|
||||
});
|
||||
expect(payload).not.toHaveProperty("domain");
|
||||
});
|
||||
});
|
||||
|
||||
describe("syncCookieAndGetRedirectUrl", () => {
|
||||
it("returns null when not logged in", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: false,
|
||||
userInfo: null,
|
||||
});
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
expect(result).toBeNull();
|
||||
expect(mockSession.setCookie).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns null when domain is missing", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 1, username: "u" },
|
||||
});
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
expect(result).toBeNull();
|
||||
expect(mockSession.setCookie).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns null when userId is missing", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
expect(result).toBeNull();
|
||||
expect(mockSession.setCookie).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns redirect URL and syncs cookie when all present", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockResolvedValue("my-token");
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/7?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: "https://example.com",
|
||||
name: "ticket",
|
||||
value: "my-token",
|
||||
}),
|
||||
);
|
||||
// host-only cookie,不设 domain
|
||||
const payload = mockSession.setCookie.mock.calls[0][0];
|
||||
expect(payload).not.toHaveProperty("domain");
|
||||
});
|
||||
|
||||
it("clears local auth token after syncing to webview cookie", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockResolvedValue("my-token");
|
||||
|
||||
await syncCookieAndGetRedirectUrl();
|
||||
|
||||
expect(mockSession.setCookie).toHaveBeenCalled();
|
||||
expect(mockSettings.set).toHaveBeenCalledWith("auth.token", null);
|
||||
});
|
||||
|
||||
it("keeps local auth token when cookie sync fails", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockResolvedValue("my-token");
|
||||
mockSession.setCookie.mockResolvedValue({
|
||||
success: false,
|
||||
error: "cookie write failed",
|
||||
});
|
||||
|
||||
await expect(syncCookieAndGetRedirectUrl()).rejects.toThrow(
|
||||
"cookie write failed",
|
||||
);
|
||||
expect(mockSettings.set).not.toHaveBeenCalledWith("auth.token", null);
|
||||
});
|
||||
|
||||
it("returns URL without syncing cookie when token is null", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockResolvedValue(null);
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/7?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("overwrites cookie when domain cache token exists (regardless of existing cookie)", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockImplementation((key: string) => {
|
||||
if (key === "auth.token") return Promise.resolve(null);
|
||||
if (key === "auth.tokens.example.com") return Promise.resolve("my-token");
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/7?hideMenu=true",
|
||||
);
|
||||
// 有 token 时无条件覆盖,不管现有 cookie 状态
|
||||
expect(mockSession.setCookie).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: "https://example.com",
|
||||
name: "ticket",
|
||||
value: "my-token",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("overwrites existing ticket when one-shot auth token is present", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockResolvedValue("fresh-token");
|
||||
mockSession.getCookie.mockResolvedValue({ success: true, found: true });
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/7?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).toHaveBeenCalled();
|
||||
expect(mockSettings.set).toHaveBeenCalledWith("auth.token", null);
|
||||
});
|
||||
|
||||
it("prints diagnostic logs regardless of NODE_ENV", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockResolvedValue("my-token");
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/7?hideMenu=true",
|
||||
);
|
||||
expect(mockLogger.debug).toHaveBeenCalledWith(
|
||||
"[SessionUrl] Pre-session state",
|
||||
"SessionUrl",
|
||||
expect.objectContaining({
|
||||
domain: "https://example.com",
|
||||
hasToken: true,
|
||||
}),
|
||||
);
|
||||
expect(mockLogger.debug).toHaveBeenCalledWith(
|
||||
"[SessionUrl] ticket cookie synced successfully",
|
||||
"SessionUrl",
|
||||
expect.objectContaining({ domain: "https://example.com" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("syncCookieAndGetNewSessionUrl", () => {
|
||||
it("returns null when not logged in", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: false,
|
||||
userInfo: null,
|
||||
});
|
||||
|
||||
const result = await syncCookieAndGetNewSessionUrl();
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("returns new-session URL and syncs cookie", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockResolvedValue("my-token");
|
||||
|
||||
const result = await syncCookieAndGetNewSessionUrl();
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/new/7?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("syncCookieAndGetChatUrl", () => {
|
||||
it("returns null when not logged in", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: false,
|
||||
userInfo: null,
|
||||
});
|
||||
|
||||
const result = await syncCookieAndGetChatUrl("sess-1");
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null when domain is missing", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 1, username: "u" },
|
||||
});
|
||||
|
||||
const result = await syncCookieAndGetChatUrl("sess-1");
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("returns chat URL and syncs cookie", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockResolvedValue("my-token");
|
||||
|
||||
const result = await syncCookieAndGetChatUrl("sess-abc");
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/chat/sess-abc?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns chat URL even when user id is missing", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockResolvedValue("my-token");
|
||||
|
||||
const result = await syncCookieAndGetChatUrl("sess-no-id");
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/chat/sess-no-id?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns chat URL without cookie when token is null", async () => {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: "https://example.com", username: "u" },
|
||||
});
|
||||
mockSettings.get.mockResolvedValue(null);
|
||||
|
||||
const result = await syncCookieAndGetChatUrl("sess-abc");
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/chat/sess-abc?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
// --- JWT expiry & token cache clearing ---
|
||||
|
||||
/** Helper: build a minimal JWT-like string with a given exp (epoch seconds). */
|
||||
function makeJwt(exp: number): string {
|
||||
const header = btoa(JSON.stringify({ alg: "HS256", typ: "JWT" }));
|
||||
const payload = btoa(JSON.stringify({ exp }));
|
||||
return `${header}.${payload}.sig`;
|
||||
}
|
||||
|
||||
function setupAuthWithDomain(domain = "https://example.com") {
|
||||
mockGetCurrentAuth.mockResolvedValue({
|
||||
isLoggedIn: true,
|
||||
userInfo: { id: 7, currentDomain: domain, username: "u" },
|
||||
});
|
||||
}
|
||||
|
||||
describe("syncCookieAndBuildUrl — JWT expiry guard", () => {
|
||||
it("skips cookie sync when one-shot token is expired, clears AUTH_TOKEN only", async () => {
|
||||
setupAuthWithDomain();
|
||||
const expiredJwt = makeJwt(Math.floor(Date.now() / 1000) - 60); // expired 60s ago
|
||||
mockSettings.get.mockResolvedValue(expiredJwt);
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/7?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).not.toHaveBeenCalled();
|
||||
// Only AUTH_TOKEN cleared, not domain cache
|
||||
expect(mockSettings.set).toHaveBeenCalledWith("auth.token", null);
|
||||
expect(mockSettings.set).not.toHaveBeenCalledWith(
|
||||
"auth.tokens.example.com",
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
it("skips cookie sync when domain cache token is expired, clears domain key only", async () => {
|
||||
setupAuthWithDomain();
|
||||
const expiredJwt = makeJwt(Math.floor(Date.now() / 1000) - 60);
|
||||
mockSettings.get.mockImplementation((key: string) => {
|
||||
if (key === "auth.token") return Promise.resolve(null);
|
||||
if (key === "auth.tokens.example.com") return Promise.resolve(expiredJwt);
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/7?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).not.toHaveBeenCalled();
|
||||
// Only domain key cleared, not global AUTH_TOKEN
|
||||
expect(mockSettings.set).toHaveBeenCalledWith(
|
||||
"auth.tokens.example.com",
|
||||
null,
|
||||
);
|
||||
expect(mockSettings.set).not.toHaveBeenCalledWith("auth.token", null);
|
||||
});
|
||||
|
||||
it("proceeds with cookie sync when token is not expired", async () => {
|
||||
setupAuthWithDomain();
|
||||
const validJwt = makeJwt(Math.floor(Date.now() / 1000) + 3600); // expires in 1h
|
||||
mockSettings.get.mockResolvedValue(validJwt);
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/7?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ name: "ticket", value: validJwt }),
|
||||
);
|
||||
});
|
||||
|
||||
it("proceeds with cookie sync for non-JWT token (no exp field)", async () => {
|
||||
setupAuthWithDomain();
|
||||
mockSettings.get.mockResolvedValue("opaque-token-not-jwt");
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
|
||||
expect(result).toBe(
|
||||
"https://example.com/api/sandbox/config/redirect/7?hideMenu=true",
|
||||
);
|
||||
expect(mockSession.setCookie).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ value: "opaque-token-not-jwt" }),
|
||||
);
|
||||
});
|
||||
|
||||
it("proceeds with cookie sync for malformed JWT", async () => {
|
||||
setupAuthWithDomain();
|
||||
mockSettings.get.mockResolvedValue("not-even..a..jwt");
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
|
||||
expect(mockSession.setCookie).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("treats token as valid if expired within clock-skew buffer (30s)", async () => {
|
||||
setupAuthWithDomain();
|
||||
// Expired only 10 seconds ago — within the 30s grace period
|
||||
const barelyExpiredJwt = makeJwt(Math.floor(Date.now() / 1000) - 10);
|
||||
mockSettings.get.mockResolvedValue(barelyExpiredJwt);
|
||||
|
||||
const result = await syncCookieAndGetRedirectUrl();
|
||||
|
||||
// Should still sync — not treated as expired
|
||||
expect(mockSession.setCookie).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ value: barelyExpiredJwt }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("persistTicketCookie", () => {
|
||||
it("clears both AUTH_TOKEN and domain token cache after webview login", async () => {
|
||||
mockSession.getCookie.mockResolvedValue({ found: true });
|
||||
mockSession.flushStore = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
await persistTicketCookie("https://example.com");
|
||||
|
||||
expect(mockSettings.set).toHaveBeenCalledWith("auth.token", null);
|
||||
expect(mockSettings.set).toHaveBeenCalledWith(
|
||||
"auth.tokens.example.com",
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
it("skips clearing when no ticket cookie is present", async () => {
|
||||
mockSession.getCookie.mockResolvedValue({ found: false });
|
||||
|
||||
await persistTicketCookie("https://example.com");
|
||||
|
||||
expect(mockSettings.set).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not throw on failure, logs warning instead", async () => {
|
||||
mockSession.getCookie.mockRejectedValue(new Error("cookie read failed"));
|
||||
|
||||
await expect(
|
||||
persistTicketCookie("https://example.com"),
|
||||
).resolves.toBeUndefined();
|
||||
expect(mockLogger.warn).toHaveBeenCalledWith(
|
||||
"[SessionUrl] persistTicketCookie failed",
|
||||
"SessionUrl",
|
||||
expect.objectContaining({ domain: "https://example.com" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user