Skip to main content
Glama
alexander-zuev

Kollektiv | Your private LLM knowledgebase

cookies.test.ts11.7 kB
import {isValidOAuthRequest} from "@/web/utils/authContext"; import { type AuthCookie, clearAuthCookie, loadAuthCookie, saveAuthCookie, } from "@/web/utils/cookies"; import {deleteCookie, getCookie, setCookie} from "hono/cookie"; import {beforeEach, describe, expect, it, type Mock, vi} from "vitest"; import {createMockContext} from "../../../mocks/hono-mocks"; // Mock the hono/cookie module vi.mock("hono/cookie", () => ({ getCookie: vi.fn(), setCookie: vi.fn(), deleteCookie: vi.fn(), })); // Mock the authContext validation functions vi.mock("@/web/utils/authContext", () => ({ isValidOAuthRequest: vi.fn(), })); // Get the mocked functions const mockedIsValidOAuthRequest = vi.mocked(isValidOAuthRequest); const ENCODED = "signed.b64url.payload"; const TEST_TX = "test-transaction-id"; describe("Cookie Utilities", () => { // Valid test data const validAuthCookie: AuthCookie = { oauthReq: { responseType: "code", clientId: "test-client-id", redirectUri: "https://example.com/callback", scope: [], state: "test-state", }, csrfToken: "test-csrf-token", }; // Reset mocks before each test beforeEach(() => { vi.resetAllMocks(); }); describe("cookie utils (encoding/decoding)", () => { it("sets a signed cookie when given valid data", async () => { const mockContext = createMockContext(); await saveAuthCookie(mockContext, TEST_TX, validAuthCookie); // Assert expect(setCookie).toHaveBeenCalledTimes(1); const [_c, cookieName, cookieValue, options] = (setCookie as Mock).mock.calls[0]; expect(cookieName).toBe(`auth_tx_${TEST_TX}`); expect(typeof cookieValue).toBe("string"); expect(cookieValue.split(".")).toHaveLength(2); // body.signature expect(options).toMatchObject({ path: "/", httpOnly: true, secure: true, sameSite: "lax", maxAge: 60 * 5, }); }); it("returns null if the signature is invalid", async () => { const c = createMockContext(); // Mock a tampered cookie value const tampered = "invalid.signature"; (getCookie as Mock).mockReturnValue(tampered); const result = await loadAuthCookie(c, TEST_TX); expect(result).toBeNull(); }); it("returns null for malformed cookie structure", async () => { const c = createMockContext(); (getCookie as Mock).mockReturnValue("invalid-part-only"); const result = await loadAuthCookie(c, TEST_TX); expect(result).toBeNull(); }); it("returns null for invalid JSON payload", async () => { const c = createMockContext(); // Mock an invalid JSON payload (getCookie as Mock).mockReturnValue("invalid.json.payload"); const result = await loadAuthCookie(c, TEST_TX); expect(result).toBeNull(); }); }); describe("saveAuthCookie", async () => { it("should set a cookie with valid data", async () => { // Arrange const mockContext = createMockContext({ env: {NODE_ENV: "production"}, }); // Mock crypto functions vi.spyOn(global.crypto.subtle, "importKey").mockResolvedValue({} as CryptoKey); vi.spyOn(global.crypto.subtle, "sign").mockResolvedValue(new ArrayBuffer(32)); // Mock Buffer.from for encoding const originalFrom = Buffer.from; vi.spyOn(Buffer, "from").mockImplementation((data: any, encoding?: string) => { if (!encoding && (data instanceof ArrayBuffer || ArrayBuffer.isView(data))) { return { toString: () => "mockedSignature", } as Buffer; } return originalFrom(data, encoding as BufferEncoding); }); // Act await saveAuthCookie(mockContext, TEST_TX, validAuthCookie); // Assert expect(setCookie).toHaveBeenCalledTimes(1); const [ctxArg, nameArg, valueArg, optsArg] = (setCookie as Mock).mock.calls[0]; expect(ctxArg).toBe(mockContext); expect(nameArg).toBe(`auth_tx_${TEST_TX}`); expect(typeof valueArg).toBe("string"); expect(valueArg.split(".")).toHaveLength(2); // body.signature expect(optsArg).toMatchObject({ path: "/", httpOnly: true, secure: true, sameSite: "lax", maxAge: 60 * 5, }); // Restore mocks vi.restoreAllMocks(); }); it("should set cookie with development settings when NODE_ENV is development", async () => { // Arrange const mockContext = createMockContext({ env: {NODE_ENV: "development"}, }); // Mock crypto functions vi.spyOn(global.crypto.subtle, "importKey").mockResolvedValue({} as CryptoKey); vi.spyOn(global.crypto.subtle, "sign").mockResolvedValue(new ArrayBuffer(32)); // Mock Buffer.from for encoding const originalFrom = Buffer.from; vi.spyOn(Buffer, "from").mockImplementation((data: any, encoding?: string) => { if (!encoding && (data instanceof ArrayBuffer || ArrayBuffer.isView(data))) { return { toString: () => "mockedSignature", } as Buffer; } return originalFrom(data, encoding as BufferEncoding); }); // Act await saveAuthCookie(mockContext, TEST_TX, validAuthCookie); // Assert expect(setCookie).toHaveBeenCalledTimes(1); const [_c, _name, _value, options] = (setCookie as Mock).mock.calls[0]; expect(options).toMatchObject({ secure: true, sameSite: "lax", }); // Restore mocks vi.restoreAllMocks(); }); it("should handle errors when setting cookie", async () => { // Arrange const mockContext = createMockContext(); // Mock crypto functions vi.spyOn(global.crypto.subtle, "importKey").mockResolvedValue({} as CryptoKey); vi.spyOn(global.crypto.subtle, "sign").mockResolvedValue(new ArrayBuffer(32)); // Mock Buffer.from for encoding const originalFrom = Buffer.from; vi.spyOn(Buffer, "from").mockImplementation((data: any, encoding?: string) => { if (!encoding && (data instanceof ArrayBuffer || ArrayBuffer.isView(data))) { return { toString: () => "mockedSignature", } as unknown as Buffer; } return originalFrom(data, encoding as BufferEncoding); }); // Mock setCookie to throw an error (setCookie as Mock).mockImplementationOnce(() => { throw new Error("Cookie error"); }); // Act & Assert await expect(saveAuthCookie(mockContext, TEST_TX, validAuthCookie)).rejects.toThrow( "Cookie error", ); expect(setCookie).toHaveBeenCalledTimes(1); // Cleanup vi.restoreAllMocks(); }); }); describe("loadAuthCookie", () => { it("should return null when no cookie data is present", async () => { // Arrange const mockContext = createMockContext(); (getCookie as Mock).mockReturnValue(null); // Act const result = await loadAuthCookie(mockContext, TEST_TX); // Assert expect(result).toBeNull(); expect(getCookie).toHaveBeenCalledWith(mockContext, `auth_tx_${TEST_TX}`); }); it("should return valid data from a signed cookie", async () => { // Arrange const c = createMockContext(); // Mock getCookie to return a valid cookie value const validCookieValue = "validBody.validSignature"; (getCookie as Mock).mockReturnValue(validCookieValue); // Mock hmac to always return the signature part of our cookie vi.spyOn(global.crypto.subtle, "importKey").mockResolvedValue({} as CryptoKey); vi.spyOn(global.crypto.subtle, "sign").mockImplementation(() => { return Promise.resolve(new TextEncoder().encode("validSignature").buffer); }); // Mock Buffer.from for both encoding directions const originalFrom = Buffer.from; vi.spyOn(Buffer, "from").mockImplementation((data: any, encoding?: string) => { // For deb64u function - decode the body if (encoding === "base64url" && data === "validBody") { return Buffer.from( JSON.stringify({ v: 1, oauthReq: validAuthCookie.oauthReq, csrfToken: validAuthCookie.csrfToken, }), ); } // For b64u function - encode the signature if (!encoding && data instanceof ArrayBuffer) { return { toString: () => "validSignature", } as unknown as Buffer; } return originalFrom(data, encoding as BufferEncoding); }); // Act const result = await loadAuthCookie(c, TEST_TX); // Assert expect(result).not.toBeNull(); // Ignore version field while asserting core data expect(result).toMatchObject(validAuthCookie); // Explicitly verify version for completeness expect((result as any).v).toBe(1); // Cleanup vi.restoreAllMocks(); }); it("should log message when cookie is invalid", async () => { // Arrange const mockContext = createMockContext(); (getCookie as Mock).mockReturnValue("invalid.cookie"); // Spy on console.log const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => { }); // Act const result = await loadAuthCookie(mockContext, TEST_TX); // Assert expect(result).toBeNull(); expect(consoleLogSpy).toHaveBeenCalledWith(`[Cookie] No cookie auth_tx_${TEST_TX}`); // Cleanup consoleLogSpy.mockRestore(); }); }); describe("clearAuthCookie", () => { it("should delete the auth cookie for a transaction", () => { // Arrange const mockContext = createMockContext(); // Spy on console.log const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => { }); // Act clearAuthCookie(mockContext, TEST_TX); // Assert expect(deleteCookie).toHaveBeenCalledWith(mockContext, `auth_tx_${TEST_TX}`, {path: "/"}); expect(consoleLogSpy).toHaveBeenCalledWith(`[Cookie] Cookie auth_tx_${TEST_TX} deleted`); // Cleanup consoleLogSpy.mockRestore(); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/alexander-zuev/kollektiv-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server