UNPKG

@lobehub/chat

Version:

Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.

217 lines (180 loc) • 7.45 kB
import { UserJSON } from '@clerk/backend'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { UserModel } from '@/database/models/user'; import { UserItem } from '@/database/schemas'; import { pino } from '@/libs/logger'; import { AgentService } from '@/server/services/agent'; import { UserService } from './index'; // Mock dependencies vi.mock('@/database/models/user', () => { const MockUserModel = vi.fn(); // @ts-ignore MockUserModel.findById = vi.fn(); // @ts-ignore MockUserModel.createUser = vi.fn(); // @ts-ignore MockUserModel.deleteUser = vi.fn(); // Mock instance methods MockUserModel.prototype.updateUser = vi.fn(); return { UserModel: MockUserModel }; }); vi.mock('@/libs/logger', () => ({ pino: { info: vi.fn(), }, })); vi.mock('@/server/services/agent', () => ({ AgentService: vi.fn().mockImplementation(() => ({ createInbox: vi.fn().mockResolvedValue(undefined), })), })); let service: UserService; const mockUserId = 'test-user-id'; // Mock user data const mockUserJSON: UserJSON = { id: mockUserId, email_addresses: [{ id: 'email-1', email_address: 'test@example.com' }], phone_numbers: [{ id: 'phone-1', phone_number: '+1234567890' }], primary_email_address_id: 'email-1', primary_phone_number_id: 'phone-1', image_url: 'https://example.com/avatar.jpg', first_name: 'Test', last_name: 'User', username: 'testuser', created_at: '2023-01-01T00:00:00Z', } as unknown as UserJSON; beforeEach(() => { service = new UserService(); vi.clearAllMocks(); }); describe('UserService', () => { describe('createUser', () => { it('should create a new user when user does not exist', async () => { // Mock user not found vi.mocked(UserModel.findById).mockResolvedValue(null as any); const result = await service.createUser(mockUserId, mockUserJSON); expect(UserModel.findById).toHaveBeenCalledWith(expect.anything(), mockUserId); expect(UserModel.createUser).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ id: mockUserId, email: 'test@example.com', phone: '+1234567890', firstName: 'Test', lastName: 'User', username: 'testuser', avatar: 'https://example.com/avatar.jpg', clerkCreatedAt: new Date('2023-01-01T00:00:00Z'), }), ); expect(AgentService).toHaveBeenCalledWith(expect.anything(), mockUserId); expect(vi.mocked(AgentService).mock.results[0].value.createInbox).toHaveBeenCalled(); expect(result).toEqual({ message: 'user created', success: true, }); }); it('should not create user if already exists', async () => { // Mock user found vi.mocked(UserModel.findById).mockResolvedValue({ id: mockUserId } as UserItem); const result = await service.createUser(mockUserId, mockUserJSON); expect(UserModel.findById).toHaveBeenCalledWith(expect.anything(), mockUserId); expect(UserModel.createUser).not.toHaveBeenCalled(); expect(AgentService).not.toHaveBeenCalled(); expect(result).toEqual({ message: 'user not created due to user already existing in the database', success: false, }); }); it('should handle user without primary phone number', async () => { vi.mocked(UserModel.findById).mockResolvedValue(null as any); const userWithoutPrimaryPhone = { ...mockUserJSON, primary_phone_number_id: null, phone_numbers: [{ id: 'phone-1', phone_number: '+1234567890' }], } as UserJSON; await service.createUser(mockUserId, userWithoutPrimaryPhone); expect(UserModel.createUser).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ phone: '+1234567890', // Should use first phone number }), ); expect(AgentService).toHaveBeenCalledWith(expect.anything(), mockUserId); expect(vi.mocked(AgentService).mock.results[0].value.createInbox).toHaveBeenCalled(); }); }); describe('deleteUser', () => { it('should delete user', async () => { await service.deleteUser(mockUserId); expect(UserModel.deleteUser).toHaveBeenCalledWith(expect.anything(), mockUserId); }); it('should throw error if deletion fails', async () => { const error = new Error('Deletion failed'); vi.mocked(UserModel.deleteUser).mockRejectedValue(error); await expect(service.deleteUser(mockUserId)).rejects.toThrow('Deletion failed'); }); }); describe('updateUser', () => { it('should update user when user exists', async () => { // Mock user found vi.mocked(UserModel.findById).mockResolvedValue({ id: mockUserId } as UserItem); const mockUpdateUser = vi.mocked(UserModel.prototype.updateUser); const result = await service.updateUser(mockUserId, mockUserJSON); expect(UserModel.findById).toHaveBeenCalledWith(expect.anything(), mockUserId); expect(pino.info).toHaveBeenCalledWith('updating user due to clerk webhook'); expect(mockUpdateUser).toHaveBeenCalledWith( expect.objectContaining({ id: mockUserId, email: 'test@example.com', phone: '+1234567890', firstName: 'Test', lastName: 'User', username: 'testuser', avatar: 'https://example.com/avatar.jpg', }), ); expect(result).toEqual({ message: 'user updated', success: true, }); }); it('should not update user when user does not exist', async () => { // Mock user not found vi.mocked(UserModel.findById).mockResolvedValue(null as any); const mockUpdateUser = vi.mocked(UserModel.prototype.updateUser); const result = await service.updateUser(mockUserId, mockUserJSON); expect(UserModel.findById).toHaveBeenCalledWith(expect.anything(), mockUserId); expect(mockUpdateUser).not.toHaveBeenCalled(); expect(result).toEqual({ message: "user not updated due to the user don't existing in the database", success: false, }); }); it('should handle user without primary email and phone', async () => { vi.mocked(UserModel.findById).mockResolvedValue({ id: mockUserId } as UserItem); const mockUpdateUser = vi.mocked(UserModel.prototype.updateUser); const userWithoutPrimaryContacts = { ...mockUserJSON, primary_email_address_id: null, primary_phone_number_id: null, email_addresses: [{ id: 'email-1', email_address: 'test@example.com' }], phone_numbers: [{ id: 'phone-1', phone_number: '+1234567890' }], } as UserJSON; await service.updateUser(mockUserId, userWithoutPrimaryContacts); // Verify that the first email and phone are used when primary is not specified expect(mockUpdateUser).toHaveBeenCalledWith( expect.objectContaining({ phone: '+1234567890', }), ); }); it('should handle update failure', async () => { vi.mocked(UserModel.findById).mockResolvedValue({ id: mockUserId } as UserItem); const mockUpdateUser = vi.mocked(UserModel.prototype.updateUser); const error = new Error('Update failed'); mockUpdateUser.mockRejectedValue(error); await expect(service.updateUser(mockUserId, mockUserJSON)).rejects.toThrow('Update failed'); }); }); });