/** * Tests for ProviderRegistry */ import { ProviderRegistry } from "../../src/providers/ProviderRegistry"; import { OidcProviderConfig } from "../../src/OidcProviderConfig"; import { createTestProviderConfig } from "../helpers/test-helpers"; describe("ProviderRegistry", () => { let staticProviders: Record; beforeEach(() => { staticProviders = { google: createTestProviderConfig({ issuer: "https://accounts.google.com", discoveryUrl: "https://accounts.google.com/.well-known/openid-configuration", }), microsoft: createTestProviderConfig({ issuer: "https://login.microsoftonline.com/common/v2.0", discoveryUrl: "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration", }), }; }); describe("constructor", () => { it("should create registry with static providers", () => { const registry = new ProviderRegistry(staticProviders); expect(registry).toBeDefined(); }); it("should create registry with provider loader", () => { const loader = async (name: string) => null; const registry = new ProviderRegistry(staticProviders, loader); expect(registry).toBeDefined(); }); }); describe("hasProvider", () => { it("should return true for configured static providers", () => { const registry = new ProviderRegistry(staticProviders); expect(registry.hasProvider("google")).toBe(true); expect(registry.hasProvider("microsoft")).toBe(true); }); it("should return false for non-configured providers", () => { const registry = new ProviderRegistry(staticProviders); expect(registry.hasProvider("okta")).toBe(false); expect(registry.hasProvider("auth0")).toBe(false); }); it("should return true if provider loader is configured", () => { const loader = async (name: string) => null; const registry = new ProviderRegistry(staticProviders, loader); // hasProvider returns true if loader exists, even for unknown providers expect(registry.hasProvider("unknown")).toBe(true); }); }); describe("getProvider", () => { it("should throw error for non-existent provider without loader", async () => { const registry = new ProviderRegistry(staticProviders); try { await registry.getProvider("nonexistent"); fail("Should have thrown error"); } catch (error: any) { expect(error.code).toBe("provider_not_configured"); expect(error.message).toContain("nonexistent"); } }); it("should include available providers in error details", async () => { const registry = new ProviderRegistry(staticProviders); try { await registry.getProvider("okta"); fail("Should have thrown error"); } catch (error: any) { expect(error.details.availableProviders).toEqual(["google", "microsoft"]); } }); it("should use provider loader for non-static providers", async () => { const dynamicConfig = createTestProviderConfig({ issuer: "https://okta.example.com", discoveryUrl: "https://okta.example.com/.well-known/openid-configuration", }); const loader = jasmine.createSpy("loader").and.returnValue(Promise.resolve(dynamicConfig)); const registry = new ProviderRegistry(staticProviders, loader); // Note: This will attempt real OIDC discovery, which will fail in tests // We're primarily testing that the loader is called try { await registry.getProvider("okta"); } catch (error) { // Discovery will fail, but loader should have been called expect(loader).toHaveBeenCalledWith("okta"); } }); it("should prioritize static config over loader", async () => { const loader = jasmine.createSpy("loader").and.returnValue(Promise.resolve(null)); const registry = new ProviderRegistry(staticProviders, loader); // Note: This will attempt real OIDC discovery try { await registry.getProvider("google"); } catch (error) { // Loader should NOT have been called for static provider expect(loader).not.toHaveBeenCalled(); } }); it("should throw error if loader returns null", async () => { const loader = async (name: string) => null; const registry = new ProviderRegistry({}, loader); try { await registry.getProvider("unknown"); fail("Should have thrown error"); } catch (error: any) { expect(error.code).toBe("provider_not_configured"); } }); }); describe("getProviderNames", () => { it("should return list of static provider names", () => { const registry = new ProviderRegistry(staticProviders); const names = registry.getProviderNames(); expect(names).toEqual(["google", "microsoft"]); }); it("should return empty array if no static providers", () => { const registry = new ProviderRegistry({}); const names = registry.getProviderNames(); expect(names).toEqual([]); }); }); describe("clearCache", () => { it("should clear specific provider from cache", () => { const registry = new ProviderRegistry(staticProviders); registry.clearCache("google"); // Cache is cleared, next getProvider will re-initialize expect(registry).toBeDefined(); // Basic assertion }); it("should clear all providers from cache", () => { const registry = new ProviderRegistry(staticProviders); registry.clearCache(); expect(registry).toBeDefined(); }); it("should not throw error for non-existent provider", () => { const registry = new ProviderRegistry(staticProviders); expect(() => registry.clearCache("nonexistent")).not.toThrow(); }); }); describe("provider caching", () => { it("should cache provider instances", async () => { const loader = jasmine.createSpy("loader").and.returnValue( Promise.resolve( createTestProviderConfig({ issuer: "https://test.example.com", discoveryUrl: "https://test.example.com/.well-known/openid-configuration", }) ) ); const registry = new ProviderRegistry({}, loader); // First call - will attempt to initialize (and fail on discovery) try { await registry.getProvider("test"); } catch (error) { // Expected to fail on discovery } // Loader should have been called once expect(loader).toHaveBeenCalledTimes(1); // Clear cache and try again registry.clearCache("test"); try { await registry.getProvider("test"); } catch (error) { // Expected to fail on discovery } // Loader should have been called again after cache clear expect(loader).toHaveBeenCalledTimes(2); }); }); });