From 486d73ecde6c33b331795186f7f7cabe75edb89f Mon Sep 17 00:00:00 2001 From: Arno V Date: Sun, 7 Jul 2024 18:39:22 -0400 Subject: [PATCH] chore: adding tests + coverage for common (#83) --- packages/auth-common/package.json | 7 +-- .../src/components/__tests__/getToken.test.ts | 50 +++++++++++++++ .../components/__tests__/verifyToken.test.ts | 63 +++++++++++++++++++ .../auth-common/src/components/getToken.ts | 2 +- packages/auth-common/src/components/pkce.ts | 2 + 5 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 packages/auth-common/src/components/__tests__/getToken.test.ts create mode 100644 packages/auth-common/src/components/__tests__/verifyToken.test.ts diff --git a/packages/auth-common/package.json b/packages/auth-common/package.json index b378dd7..6f20898 100644 --- a/packages/auth-common/package.json +++ b/packages/auth-common/package.json @@ -14,9 +14,7 @@ "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", - "files": [ - "dist" - ], + "files": ["dist"], "scripts": { "build:check": "tsc", "build:js": "vite build", @@ -29,7 +27,8 @@ "lint": "biome lint src", "start": "static-server dist --port 5173", "test:watch": "vitest", - "test": "vitest run" + "test": "vitest run", + "test:coverage": "vitest run --coverage" }, "dependencies": { "jose": "5.6.3", diff --git a/packages/auth-common/src/components/__tests__/getToken.test.ts b/packages/auth-common/src/components/__tests__/getToken.test.ts new file mode 100644 index 0000000..4923247 --- /dev/null +++ b/packages/auth-common/src/components/__tests__/getToken.test.ts @@ -0,0 +1,50 @@ +import { type HeadersLike, getToken } from ".."; + +describe("getToken", () => { + const clientId = "testClient"; + + it("should return empty string if no token is found", () => { + const headers: HeadersLike = {}; + expect(getToken(headers, clientId)).toBe(""); + }); + + it("should extract token from authorization header", () => { + const token = "someToken"; + const headers: HeadersLike = { + authorization: `Bearer ${token}`, + }; + expect(getToken(headers, clientId)).toBe(token); + }); + + it("should extract token from cookie", () => { + const token = "anotherToken"; + const headers: HeadersLike = { + cookie: `auth.${clientId}=${token};`, + }; + expect(getToken(headers, clientId)).toBe(token); + }); + + it("should prioritize token from cookie over authorization header", () => { + const headerToken = "headerToken"; + const cookieToken = "cookieToken"; + const headers: HeadersLike = { + authorization: `Bearer ${headerToken}`, + cookie: `auth.${clientId}=${cookieToken};`, + }; + expect(getToken(headers, clientId)).toBe(cookieToken); + }); + + it("should return empty string if authorization header is not in correct format", () => { + const headers: HeadersLike = { + authorization: "InvalidHeader", + }; + expect(getToken(headers, clientId)).toBe(""); + }); + + it("should return empty string if cookie does not contain the token", () => { + const headers: HeadersLike = { + cookie: "someOtherCookie=value;", + }; + expect(getToken(headers, clientId)).toBe(""); + }); +}); diff --git a/packages/auth-common/src/components/__tests__/verifyToken.test.ts b/packages/auth-common/src/components/__tests__/verifyToken.test.ts new file mode 100644 index 0000000..63747e1 --- /dev/null +++ b/packages/auth-common/src/components/__tests__/verifyToken.test.ts @@ -0,0 +1,63 @@ +import { decodeJwt, importSPKI, jwtVerify } from "jose"; +import { decodeToken, verifyAndExtractToken } from ".."; +import { JWT, JWT_PUBLIC_KEY } from "../constants"; +vi.mock("jose"); + +describe("verifyAndExtractToken", () => { + const token = "testToken"; + + it("should verify and extract token successfully", async () => { + const mockPublicKey = {}; + const mockJwtVerifyResult = { + payload: "testPayload", + protectedHeader: "testHeader", + }; + // @ts-expect-error + (importSPKI as vi.Mock).mockResolvedValue(mockPublicKey); + // @ts-expect-error + (jwtVerify as vi.Mock).mockResolvedValue(mockJwtVerifyResult); + + const result = await verifyAndExtractToken(token); + + expect(importSPKI).toHaveBeenCalledWith(JWT_PUBLIC_KEY, JWT.ALG); + expect(jwtVerify).toHaveBeenCalledWith(token, mockPublicKey, { + issuer: JWT.ISSUER, + }); + expect(result).toEqual(mockJwtVerifyResult); + }); + + it("should return undefined on error", async () => { + // @ts-expect-error + (importSPKI as vi.Mock).mockRejectedValue(new Error("test error")); + + const result = await verifyAndExtractToken(token); + + expect(result).toBeUndefined(); + }); +}); + +describe("decodeToken", () => { + const token = "testToken"; + + it("should decode token successfully", () => { + const mockDecodeResult = { payload: "testPayload" }; + // @ts-expect-error + (decodeJwt as vi.Mock).mockReturnValue(mockDecodeResult); + + const result = decodeToken(token); + + expect(decodeJwt).toHaveBeenCalledWith(token); + expect(result).toEqual(mockDecodeResult); + }); + + it("should return undefined on error", () => { + // @ts-expect-error + (decodeJwt as vi.Mock).mockImplementation(() => { + throw new Error("test error"); + }); + + const result = decodeToken(token); + + expect(result).toBeUndefined(); + }); +}); diff --git a/packages/auth-common/src/components/getToken.ts b/packages/auth-common/src/components/getToken.ts index f4a855b..e411d0c 100644 --- a/packages/auth-common/src/components/getToken.ts +++ b/packages/auth-common/src/components/getToken.ts @@ -1,4 +1,4 @@ -type HeadersLike = Record & { +export type HeadersLike = Record & { authorization?: string; "content-type"?: string; cookie?: string; diff --git a/packages/auth-common/src/components/pkce.ts b/packages/auth-common/src/components/pkce.ts index c73c612..0d04ff8 100644 --- a/packages/auth-common/src/components/pkce.ts +++ b/packages/auth-common/src/components/pkce.ts @@ -30,11 +30,13 @@ const toBase64 = (val: ArrayBuffer): string => * @returns The base64 url encoded code challenge. */ export async function generateCodeChallenge(code_verifier: string) { + /* c8 ignore start */ if (!crypto.subtle) { throw new Error( "crypto.subtle is available only in secure contexts (HTTPS).", ); } + /* c8 ignore end */ const data = new TextEncoder().encode(code_verifier); const hashed = await crypto.subtle.digest("SHA-256", data); return toBase64(hashed)