/** * Tests for OidcConnectionRepo */ import OidcConnectionRepo from "../../src/repos/OidcConnectionRepo"; import { createMockDb } from "../helpers/test-helpers"; import OidcConnection from "../../src/schemas/OidcConnection"; describe("OidcConnectionRepo", () => { let repo: OidcConnectionRepo; let db: any; let cleanup: () => Promise; beforeEach(async () => { const mockDb = await createMockDb(); db = mockDb.db; cleanup = mockDb.cleanup; repo = new OidcConnectionRepo("oidc_connections_test", db); }); afterEach(async () => { await cleanup(); }); describe("create", () => { it("should create a new connection", async () => { const connection: Omit = { userId: "user-123", provider: "google", subject: "google-user-456", issuer: "https://accounts.google.com", accessToken: "encrypted-access-token", idToken: "encrypted-id-token", refreshToken: "encrypted-refresh-token", expiresAt: new Date(Date.now() + 3600000), scope: "openid email profile", createdAt: new Date(), updatedAt: new Date(), }; const created = await repo.create(connection); expect(created._id).toBeDefined(); expect(created.userId).toBe("user-123"); expect(created.provider).toBe("google"); expect(created.subject).toBe("google-user-456"); }); }); describe("findByUserAndProvider", () => { it("should find connection by user ID and provider", async () => { const connection: Omit = { userId: "user-456", provider: "microsoft", subject: "ms-user-789", issuer: "https://login.microsoftonline.com", accessToken: "encrypted-token", idToken: "encrypted-id", expiresAt: new Date(Date.now() + 3600000), createdAt: new Date(), updatedAt: new Date(), }; await repo.create(connection); const found = await repo.findByUserAndProvider("user-456", "microsoft"); expect(found).toBeDefined(); expect(found?.userId).toBe("user-456"); expect(found?.provider).toBe("microsoft"); expect(found?.subject).toBe("ms-user-789"); }); it("should return null if connection not found", async () => { const found = await repo.findByUserAndProvider("nonexistent-user", "google"); expect(found).toBeNull(); }); it("should differentiate between providers for same user", async () => { const googleConnection: Omit = { userId: "user-multi", provider: "google", subject: "google-sub", issuer: "https://accounts.google.com", accessToken: "google-token", idToken: "google-id", expiresAt: new Date(Date.now() + 3600000), createdAt: new Date(), updatedAt: new Date(), }; const msConnection: Omit = { userId: "user-multi", provider: "microsoft", subject: "ms-sub", issuer: "https://login.microsoftonline.com", accessToken: "ms-token", idToken: "ms-id", expiresAt: new Date(Date.now() + 3600000), createdAt: new Date(), updatedAt: new Date(), }; await repo.create(googleConnection); await repo.create(msConnection); const foundGoogle = await repo.findByUserAndProvider("user-multi", "google"); const foundMs = await repo.findByUserAndProvider("user-multi", "microsoft"); expect(foundGoogle?.subject).toBe("google-sub"); expect(foundMs?.subject).toBe("ms-sub"); }); }); describe("findByUserId", () => { it("should find all connections for a user", async () => { const connection1: Omit = { userId: "user-multi-conn", provider: "google", subject: "google-sub-1", issuer: "https://accounts.google.com", accessToken: "token-1", idToken: "id-1", expiresAt: new Date(Date.now() + 3600000), createdAt: new Date(), updatedAt: new Date(), }; const connection2: Omit = { userId: "user-multi-conn", provider: "okta", subject: "okta-sub-2", issuer: "https://okta.example.com", accessToken: "token-2", idToken: "id-2", expiresAt: new Date(Date.now() + 3600000), createdAt: new Date(), updatedAt: new Date(), }; await repo.create(connection1); await repo.create(connection2); const connections = await repo.findByUserId("user-multi-conn"); expect(connections.length).toBe(2); expect(connections.map((c) => c.provider).sort()).toEqual(["google", "okta"]); }); it("should return empty array if no connections found", async () => { const connections = await repo.findByUserId("user-no-connections"); expect(connections).toEqual([]); }); }); describe("findBySubjectAndIssuer", () => { it("should find connection by subject and issuer", async () => { const connection: Omit = { userId: "user-unique", provider: "auth0", subject: "auth0-subject-123", issuer: "https://example.auth0.com", accessToken: "token", idToken: "id", expiresAt: new Date(Date.now() + 3600000), createdAt: new Date(), updatedAt: new Date(), }; await repo.create(connection); const found = await repo.findBySubjectAndIssuer("auth0-subject-123", "https://example.auth0.com"); expect(found).toBeDefined(); expect(found?.userId).toBe("user-unique"); expect(found?.provider).toBe("auth0"); }); it("should return null if connection not found", async () => { const found = await repo.findBySubjectAndIssuer("nonexistent-sub", "https://example.com"); expect(found).toBeNull(); }); }); // updateOne is tested via integration tests - ObjectId handling requires special setup describe("deleteByUserAndProvider", () => { it("should delete connection by user and provider", async () => { const connection: Omit = { userId: "user-delete", provider: "okta", subject: "okta-sub", issuer: "https://okta.example.com", accessToken: "token", idToken: "id", expiresAt: new Date(Date.now() + 3600000), createdAt: new Date(), updatedAt: new Date(), }; await repo.create(connection); await repo.deleteByUserAndProvider("user-delete", "okta"); const found = await repo.findByUserAndProvider("user-delete", "okta"); expect(found).toBeNull(); }); it("should not throw if connection not found", async () => { await repo.deleteByUserAndProvider("nonexistent-user", "google"); // Should complete without error expect(true).toBe(true); }); }); describe("deleteByUserId", () => { it("should delete all connections for a user", async () => { const connection1: Omit = { userId: "user-delete-all", provider: "google", subject: "google-sub", issuer: "https://accounts.google.com", accessToken: "token-1", idToken: "id-1", expiresAt: new Date(Date.now() + 3600000), createdAt: new Date(), updatedAt: new Date(), }; const connection2: Omit = { userId: "user-delete-all", provider: "microsoft", subject: "ms-sub", issuer: "https://login.microsoftonline.com", accessToken: "token-2", idToken: "id-2", expiresAt: new Date(Date.now() + 3600000), createdAt: new Date(), updatedAt: new Date(), }; await repo.create(connection1); await repo.create(connection2); const deletedCount = await repo.deleteByUserId("user-delete-all"); expect(deletedCount).toBe(2); const connections = await repo.findByUserId("user-delete-all"); expect(connections.length).toBe(0); }); it("should return 0 if no connections to delete", async () => { const deletedCount = await repo.deleteByUserId("user-no-connections"); expect(deletedCount).toBe(0); }); }); });