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 result = await saveUser(userToSave); expect(result.ok).toBe(true); if (!result.ok) return; expect(result.value.id).toBeDefined(); expect(result.value.name).toBe(userToSave.name); expect(result.value.email).toBe(userToSave.email); expect(result.value.role).toBe(userToSave.role); expect(result.value.externalId).toBeDefined(); }); test('cannot save user with existing email', async () => { const userToSave: CreateUserModel = { name: 'Test User', email: 'test@email.com', role: 'user', }; const result = await saveUser(userToSave); expect(result.ok).toBe(false); if (result.ok) return; expect(result.error.message).toContain(userToSave.email); }); test('can getUserByEmail', async () => { const result = await getUserByEmail('test@email.com'); expect(result.ok).toBe(true); if (!result.ok) return; expect(result.value?.email).toBe('test@email.com'); expect(result.value?.name).toBe('Test User'); expect(result.value?.role).toBe('user'); expect(result.value?.externalId).toBeDefined(); }); test('cannot getUserByEmail with non-existing email', async () => { const result = await getUserByEmail('missing@email.com'); expect(result.ok).toBe(true); if (!result.ok) return; expect(result.value).toBeNull(); }); test('can update user', async () => { const dataToUpdate: UpdateUserModel = { name: 'Updated User', role: 'admin', }; const userResult = await getUserByEmail('test@email.com'); expect(userResult.ok).toBe(true); if (!userResult.ok) return; const user = userResult.value; expect(user).toBeDefined(); const result = await updateUser(user!.id, dataToUpdate); expect(result.ok).toBe(true); if (!result.ok) return; expect(result.value.id).toBe(user!.id); expect(result.value.name).toBe(dataToUpdate.name); expect(result.value.role).toBe(dataToUpdate.role); expect(result.value.email).toBe(user!.email); expect(result.value.externalId).toBeDefined(); }); test('cannot update non-existing user', async () => { const dataToUpdate: UpdateUserModel = { name: 'Updated User', role: 'admin', }; const result = await updateUser('9999', dataToUpdate); expect(result.ok).toBe(false); if (result.ok) return; expect(result.error.message).toContain('9999'); }); 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 result = await syncUser(sessionClaims); expect(result.ok).toBe(true); if (!result.ok) return; expect(result.value.name).toBe('Updated Name'); expect(result.value.email).toBe('test@email.com'); expect(result.value.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 result = await syncUser(sessionClaims); expect(result.ok).toBe(true); if (!result.ok) return; expect(result.value.name).toBe('Updated Name'); expect(result.value.email).toBe('test@email.com'); expect(result.value.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 result = await syncUser(sessionClaims); expect(result.ok).toBe(true); if (!result.ok) return; expect(result.value.name).toBe('Updated Name'); expect(result.value.email).toBe('test@email.com'); expect(result.value.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 result = await syncUser(sessionClaims); expect(result.ok).toBe(true); if (!result.ok) return; expect(result.value.name).toBe('Updated Name'); expect(result.value.email).toBe('new@email.com'); expect(result.value.role).toBe('user'); }); });