import { SessionClaims } from '@/lib/feature/user/clerk.model'; import { CreateUserModel, UpdateUserModel, } from '@/lib/feature/user/user.model'; import { getUserByEmail, saveUser, syncUser, updateUser, } from '@/lib/feature/user/user.service'; import { clerkClient } from '@clerk/nextjs/server'; import { AbstractStartedContainer } from 'testcontainers'; import { startTestDB } from '~/tests/setup/setup-db'; jest.mock('@clerk/nextjs/server', () => ({ clerkClient: jest.fn(), })); describe('UserService', () => { let container: AbstractStartedContainer; beforeAll(async () => { container = await startTestDB(); }, 1_000_000); afterAll(async () => { await container.stop(); }, 1_000_000); test('can save user', async () => { const userToSave: CreateUserModel = { name: 'Test User', email: 'test@email.com', role: 'user', }; const savedUser = await saveUser(userToSave); expect(savedUser.id).toBeDefined(); expect(savedUser.name).toBe(userToSave.name); expect(savedUser.email).toBe(userToSave.email); expect(savedUser.role).toBe(userToSave.role); expect(savedUser.role).toBe(userToSave.role); expect(savedUser.externalId).toBeDefined(); // Default to true if not set }); test('cannot save user with existing email', async () => { const userToSave: CreateUserModel = { name: 'Test User', email: 'test@email.com', role: 'user', }; await expect(saveUser(userToSave)).rejects.toThrow( `User with email ${userToSave.email} already exists` ); }); test('can getUserByEmail', async () => { const user = await getUserByEmail('test@email.com'); expect(user).toBeDefined(); expect(user?.email).toBe('test@email.com'); expect(user?.name).toBe('Test User'); expect(user?.role).toBe('user'); expect(user?.externalId).toBeDefined(); // Default to true if not set }); test('cannot getUserByEmail with non-existing email', async () => { await expect(getUserByEmail('missing@email.com')).resolves.toBeNull(); }); test('can update user', async () => { const dataToUpdate: UpdateUserModel = { name: 'Updated User', role: 'admin', }; const user = await getUserByEmail('test@email.com'); expect(user).toBeDefined(); const updatedUser = await updateUser(user!.id, dataToUpdate); expect(updatedUser).toBeDefined(); expect(updatedUser.id).toBe(user!.id); expect(updatedUser.name).toBe(dataToUpdate.name); expect(updatedUser.role).toBe(dataToUpdate.role); expect(updatedUser.email).toBe(user!.email); expect(updatedUser.externalId).toBeDefined(); // Default to true if not set }); test('cannot update non-existing user', async () => { const dataToUpdate: UpdateUserModel = { name: 'Updated User', role: 'admin', }; await expect(updateUser('9999', dataToUpdate)).rejects.toThrow( `User with ID 9999 not found` ); }); test('can sync admin user', async () => { process.env.CLERK_ORG_ID = 'test-org-id'; const mockGetOrganizationMembershipList = jest.fn().mockResolvedValue({ data: [ { organization: { id: process.env.CLERK_ORG_ID, }, role: 'org:admin', }, ], }); (clerkClient as jest.Mock).mockResolvedValue({ users: { getOrganizationMembershipList: mockGetOrganizationMembershipList, }, }); const sessionClaims = { user: { full_name: 'Updated Name', email: 'test@email.com', username: 'test', public_metadata: { role: 'admin', }, }, } as SessionClaims; const syncedUser = await syncUser(sessionClaims); expect(syncedUser).toBeDefined(); expect(syncedUser.name).toBe('Updated Name'); expect(syncedUser.email).toBe('test@email.com'); expect(syncedUser.role).toBe('admin'); }); test('can sync internal user', async () => { process.env.CLERK_ORG_ID = 'test-org-id'; const mockGetOrganizationMembershipList = jest.fn().mockResolvedValue({ data: [ { organization: { id: process.env.CLERK_ORG_ID, }, role: 'org:member', }, ], }); (clerkClient as jest.Mock).mockResolvedValue({ users: { getOrganizationMembershipList: mockGetOrganizationMembershipList, }, }); const sessionClaims = { user: { full_name: 'Updated Name', email: 'test@email.com', username: 'test', public_metadata: { role: 'internal', }, }, } as SessionClaims; const syncedUser = await syncUser(sessionClaims); expect(syncedUser).toBeDefined(); expect(syncedUser.name).toBe('Updated Name'); expect(syncedUser.email).toBe('test@email.com'); expect(syncedUser.role).toBe('internal'); }); test('can sync user', async () => { process.env.CLERK_ORG_ID = 'test-org-id'; const mockGetOrganizationMembershipList = jest.fn().mockResolvedValue({ data: [], }); (clerkClient as jest.Mock).mockResolvedValue({ users: { getOrganizationMembershipList: mockGetOrganizationMembershipList, }, }); const sessionClaims = { user: { full_name: 'Updated Name', email: 'test@email.com', username: 'test', public_metadata: { role: 'user', }, }, } as SessionClaims; const syncedUser = await syncUser(sessionClaims); expect(syncedUser).toBeDefined(); expect(syncedUser.name).toBe('Updated Name'); expect(syncedUser.email).toBe('test@email.com'); expect(syncedUser.role).toBe('user'); }); test('can sync saving new user', async () => { process.env.CLERK_ORG_ID = 'test-org-id'; const mockGetOrganizationMembershipList = jest.fn().mockResolvedValue({ data: [], }); (clerkClient as jest.Mock).mockResolvedValue({ users: { getOrganizationMembershipList: mockGetOrganizationMembershipList, }, }); const sessionClaims = { user: { full_name: 'Updated Name', email: 'new@email.com', username: 'test', public_metadata: { role: 'user', }, }, } as SessionClaims; const syncedUser = await syncUser(sessionClaims); expect(syncedUser).toBeDefined(); expect(syncedUser.name).toBe('Updated Name'); expect(syncedUser.email).toBe('new@email.com'); expect(syncedUser.role).toBe('user'); }); });