/** * Tests for error utility functions */ import { createOidcError, validateProvider, OidcErrorCodes } from "../../src/utils/error-utils"; describe("error-utils", () => { describe("OidcErrorCodes", () => { it("should define all expected error codes", () => { expect(OidcErrorCodes.INVALID_STATE).toBe("invalid_state"); expect(OidcErrorCodes.SESSION_NOT_FOUND).toBe("session_not_found"); expect(OidcErrorCodes.SESSION_EXPIRED).toBe("session_expired"); expect(OidcErrorCodes.PROVIDER_NOT_CONFIGURED).toBe("provider_not_configured"); expect(OidcErrorCodes.DISCOVERY_FAILED).toBe("discovery_failed"); expect(OidcErrorCodes.TOKEN_EXCHANGE_FAILED).toBe("token_exchange_failed"); expect(OidcErrorCodes.USERINFO_FAILED).toBe("userinfo_failed"); expect(OidcErrorCodes.INVALID_PROVIDER).toBe("invalid_provider"); expect(OidcErrorCodes.MISSING_CODE).toBe("missing_code"); expect(OidcErrorCodes.MISSING_STATE).toBe("missing_state"); }); }); describe("createOidcError", () => { it("should create an error with code and message", () => { const error = createOidcError(OidcErrorCodes.INVALID_STATE, "State mismatch"); expect(error).toBeInstanceOf(Error); expect(error.message).toBe("State mismatch"); expect(error.code).toBe("invalid_state"); }); it("should include details if provided", () => { const details = { provider: "test", sessionId: "123" }; const error = createOidcError(OidcErrorCodes.SESSION_NOT_FOUND, "Session not found", details); expect(error.details).toEqual(details); }); it("should work without details", () => { const error = createOidcError(OidcErrorCodes.INVALID_STATE, "State mismatch"); expect(error.details).toBeUndefined(); }); it("should be throwable", () => { expect(() => { throw createOidcError(OidcErrorCodes.SESSION_EXPIRED, "Session expired"); }).toThrowError("Session expired"); }); }); describe("validateProvider", () => { it("should accept valid provider names", () => { expect(() => validateProvider("google")).not.toThrow(); expect(() => validateProvider("microsoft")).not.toThrow(); expect(() => validateProvider("okta")).not.toThrow(); expect(() => validateProvider("auth0")).not.toThrow(); expect(() => validateProvider("custom-provider")).not.toThrow(); expect(() => validateProvider("provider_123")).not.toThrow(); }); it("should reject invalid provider names", () => { expect(() => validateProvider("")).toThrow(); expect(() => validateProvider("provider with spaces")).toThrow(); expect(() => validateProvider("provider/slash")).toThrow(); expect(() => validateProvider("provider\\backslash")).toThrow(); expect(() => validateProvider("provider.dot")).toThrow(); }); it("should reject provider names with special characters", () => { const invalidChars = ["@", "#", "$", "%", "^", "&", "*", "(", ")", "+", "=", "[", "]", "{", "}", "|", ";", ":", "'", '"', "<", ">", "?", "/", "\\"]; invalidChars.forEach((char) => { expect(() => validateProvider(`provider${char}name`)).toThrow(); }); }); it("should accept alphanumeric with dash and underscore", () => { expect(() => validateProvider("provider-name-123")).not.toThrow(); expect(() => validateProvider("provider_name_456")).not.toThrow(); expect(() => validateProvider("ProviderName789")).not.toThrow(); }); }); describe("error handling in try-catch", () => { it("should be catchable and inspectable", () => { try { throw createOidcError(OidcErrorCodes.TOKEN_EXCHANGE_FAILED, "Token exchange failed", { originalError: "Network timeout", }); } catch (error: any) { expect(error.code).toBe("token_exchange_failed"); expect(error.message).toBe("Token exchange failed"); expect(error.details.originalError).toBe("Network timeout"); } }); it("should allow error code checking", () => { try { throw createOidcError(OidcErrorCodes.PROVIDER_NOT_CONFIGURED, "Provider not found"); } catch (error: any) { if (error.code === "provider_not_configured") { expect(error.message).toBe("Provider not found"); } else { fail("Should have caught provider_not_configured error"); } } }); }); });