diff --git a/backend/api/jest.config.js b/backend/api/jest.config.js index 02a37b3..b443f00 100644 --- a/backend/api/jest.config.js +++ b/backend/api/jest.config.js @@ -15,7 +15,7 @@ module.exports = { "^email/(.*)$": "/../email/emails/$1" }, - moduleFileExtensions: ["ts", "js", "json"], + moduleFileExtensions: ["tsx","ts", "js", "json"], clearMocks: true, globals: { diff --git a/backend/api/tests/unit/create-comment.unit.test.ts b/backend/api/tests/unit/create-comment.unit.test.ts index bed6cac..b7e7e1d 100644 --- a/backend/api/tests/unit/create-comment.unit.test.ts +++ b/backend/api/tests/unit/create-comment.unit.test.ts @@ -1,7 +1,20 @@ jest.mock('shared/supabase/init'); +jest.mock('shared/supabase/notifications'); +jest.mock('email/functions/helpers'); +jest.mock('common/supabase/comment'); +jest.mock('shared/utils'); +jest.mock('common/user-notification-preferences'); +jest.mock('shared/websockets/helpers'); import * as supabaseInit from "shared/supabase/init"; import { AuthedUser } from "api/helpers/endpoint"; +import * as sharedUtils from "shared/utils"; +import { createComment } from "api/create-comment"; +import * as notificationPrefs from "common/user-notification-preferences"; +import * as supabaseNotifications from "shared/supabase/notifications"; +import * as emailHelpers from "email/functions/helpers"; +import * as websocketHelpers from "shared/websockets/helpers"; +import { convertComment } from "common/supabase/comment"; describe('createComment', () => { let mockPg: any; @@ -14,6 +27,12 @@ describe('createComment', () => { (supabaseInit.createSupabaseDirectClient as jest.Mock) .mockReturnValue(mockPg); + (supabaseNotifications.insertNotificationToSupabase as jest.Mock) + .mockResolvedValue(null); + (emailHelpers.sendNewEndorsementEmail as jest.Mock) + .mockResolvedValue(null); + (convertComment as jest.Mock) + .mockResolvedValue(null); }); afterEach(() => { @@ -22,13 +41,17 @@ describe('createComment', () => { describe('should', () => { it('successfully create a comment with information provided', async () => { - const mockUserId = {userId: '123'} + const mockUserId = { + userId: '123', + blockedUserIds: ['111'] + } const mockOnUser = {id: '123'} const mockCreator = { - id: '123', + id: '1234', name: 'Mock Creator', username: 'mock.creator.username', - avatarUrl: 'mock.creator.avatarurl' + avatarUrl: 'mock.creator.avatarurl', + isBannedFromPosting: false } const mockContent = { content: { @@ -48,9 +71,307 @@ describe('createComment', () => { userId: '123' }; const mockAuth = { uid: '321' } as AuthedUser; + const mockReq = {} as any; + const mockReplyToCommentId = {} as any; + const mockComment = {id: 12}; + const mockNotificationDestination = {} as any; + const mockProps = { + userId: mockUserId.userId, + content: mockContent.content, + replyToCommentId: mockReplyToCommentId + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValueOnce(mockCreator) + .mockResolvedValueOnce(mockOnUser); + (sharedUtils.getPrivateUser as jest.Mock) + .mockResolvedValueOnce(mockUserId) + .mockResolvedValueOnce(mockOnUser); + (mockPg.one as jest.Mock).mockResolvedValue(mockComment); + (notificationPrefs.getNotificationDestinationsForUser as jest.Mock) + .mockReturnValue(mockNotificationDestination); + + const results = await createComment(mockProps, mockAuth, mockReq); + + expect(results.status).toBe('success'); + expect(sharedUtils.getUser).toBeCalledTimes(2); + expect(sharedUtils.getUser).toBeCalledWith(mockUserId.userId); + expect(sharedUtils.getUser).toBeCalledWith(mockAuth.uid); + expect(sharedUtils.getPrivateUser).toBeCalledTimes(2); + expect(mockPg.one).toBeCalledTimes(1); + expect(mockPg.one).toBeCalledWith( + expect.stringContaining('insert into profile_comments'), + expect.arrayContaining([mockCreator.id]) + ); + expect(websocketHelpers.broadcastUpdatedComment).toBeCalledTimes(1) + + }); + + it('throw an error if there is no user matching the userId', async () => { + const mockAuth = { uid: '321' } as AuthedUser; + const mockReq = {} as any; + const mockReplyToCommentId = {} as any; + const mockUserId = { + userId: '123', + blockedUserIds: ['111'] + }; + const mockCreator = { + id: '1234', + name: 'Mock Creator', + username: 'mock.creator.username', + avatarUrl: 'mock.creator.avatarurl', + isBannedFromPosting: false + }; + const mockContent = { + content: { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'This is the comment text' + } + ] + } + ] + }, + userId: '123' + }; + const mockProps = { + userId: mockUserId.userId, + content: mockContent.content, + replyToCommentId: mockReplyToCommentId + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValueOnce(mockCreator) + .mockResolvedValueOnce(null); + (sharedUtils.getPrivateUser as jest.Mock) + .mockResolvedValue(mockUserId); + + expect(createComment( mockProps, mockAuth, mockReq )).rejects.toThrowError('User not found'); + }); + + it('throw an error if there is no account associated with the authId', async () => { + const mockAuth = { uid: '321' } as AuthedUser; + const mockReq = {} as any; const mockReplyToCommentId = {} as any; + const mockUserId = { + userId: '123', + blockedUserIds: ['111'] + }; + const mockContent = { + content: { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'This is the comment text' + } + ] + } + ] + }, + userId: '123' + }; + const mockProps = { + userId: mockUserId.userId, + content: mockContent.content, + replyToCommentId: mockReplyToCommentId + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValueOnce(null); + + expect(createComment( mockProps, mockAuth, mockReq )).rejects.toThrowError('Your account was not found'); + }); + + it('throw an error if the account is banned from posting', async () => { + const mockAuth = { uid: '321' } as AuthedUser; + const mockReq = {} as any; + const mockReplyToCommentId = {} as any; + const mockUserId = { + userId: '123', + blockedUserIds: ['111'] + }; + const mockCreator = { + id: '1234', + name: 'Mock Creator', + username: 'mock.creator.username', + avatarUrl: 'mock.creator.avatarurl', + isBannedFromPosting: true + }; + const mockContent = { + content: { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'This is the comment text' + } + ] + } + ] + }, + userId: '123' + }; + const mockProps = { + userId: mockUserId.userId, + content: mockContent.content, + replyToCommentId: mockReplyToCommentId + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValueOnce(mockCreator); + + expect(createComment( mockProps, mockAuth, mockReq )).rejects.toThrowError('You are banned'); + }); + + it('throw an error if the other user is not found', async () => { + const mockAuth = { uid: '321' } as AuthedUser; + const mockReq = {} as any; + const mockReplyToCommentId = {} as any; + const mockUserId = { + userId: '123', + blockedUserIds: ['111'] + }; + const mockCreator = { + id: '1234', + name: 'Mock Creator', + username: 'mock.creator.username', + avatarUrl: 'mock.creator.avatarurl', + isBannedFromPosting: false + }; + const mockContent = { + content: { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'This is the comment text' + } + ] + } + ] + }, + userId: '123' + }; + const mockProps = { + userId: mockUserId.userId, + content: mockContent.content, + replyToCommentId: mockReplyToCommentId + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValueOnce(mockCreator); + (sharedUtils.getPrivateUser as jest.Mock) + .mockResolvedValue(null); + + expect(createComment( mockProps, mockAuth, mockReq )).rejects.toThrowError('Other user not found'); + }); + + it('throw an error if the user has blocked you', async () => { + const mockAuth = { uid: '321' } as AuthedUser; + const mockReq = {} as any; + const mockReplyToCommentId = {} as any; + const mockUserId = { + userId: '123', + blockedUserIds: ['321'] + }; + const mockCreator = { + id: '1234', + name: 'Mock Creator', + username: 'mock.creator.username', + avatarUrl: 'mock.creator.avatarurl', + isBannedFromPosting: false + }; + const mockContent = { + content: { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'This is the comment text' + } + ] + } + ] + }, + userId: '123' + }; + const mockProps = { + userId: mockUserId.userId, + content: mockContent.content, + replyToCommentId: mockReplyToCommentId + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValueOnce(mockCreator); + (sharedUtils.getPrivateUser as jest.Mock) + .mockResolvedValue(mockUserId); + + expect(createComment( mockProps, mockAuth, mockReq )).rejects.toThrowError('User has blocked you'); + }); + + it('throw an error if the comment is too long', async () => { + const mockAuth = { uid: '321' } as AuthedUser; + const mockReq = {} as any; + const mockReplyToCommentId = {} as any; + const mockUserId = { + userId: '123', + blockedUserIds: ['111'] + }; + const mockCreator = { + id: '1234', + name: 'Mock Creator', + username: 'mock.creator.username', + avatarUrl: 'mock.creator.avatarurl', + isBannedFromPosting: false + }; + const mockContent = { + content: { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'This '.repeat(30000), + } + ] + } + ] + }, + userId: '123' + }; + const mockProps = { + userId: mockUserId.userId, + content: mockContent.content, + replyToCommentId: mockReplyToCommentId + }; + (sharedUtils.getUser as jest.Mock) + .mockResolvedValueOnce(mockCreator); + (sharedUtils.getPrivateUser as jest.Mock) + .mockResolvedValue(mockUserId); + console.log(JSON.stringify(mockContent.content).length); + expect(createComment( mockProps, mockAuth, mockReq )).rejects.toThrowError('Comment is too long'); }); }); }); \ No newline at end of file diff --git a/backend/api/tests/unit/create-compatibility-question.unit.test.ts b/backend/api/tests/unit/create-compatibility-question.unit.test.ts new file mode 100644 index 0000000..3276d3d --- /dev/null +++ b/backend/api/tests/unit/create-compatibility-question.unit.test.ts @@ -0,0 +1,96 @@ +jest.mock('shared/supabase/init'); +jest.mock('shared/utils'); +jest.mock('shared/supabase/utils'); +jest.mock('common/util/try-catch'); + +import { createCompatibilityQuestion } from "api/create-compatibility-question"; +import * as supabaseInit from "shared/supabase/init"; +import * as shareUtils from "shared/utils"; +import { tryCatch } from "common/util/try-catch"; +import * as supabaseUtils from "shared/supabase/utils"; +import { AuthedUser } from "api/helpers/endpoint"; + +describe('createCompatibilityQuestion', () => { + const mockPg = {} as any; + beforeEach(() => { + jest.resetAllMocks(); + + (supabaseInit.createSupabaseDirectClient as jest.Mock) + .mockReturnValue(mockPg); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + describe('should', () => { + it('successfully create compatibility questions', async () => { + const mockQuestion = {} as any; + const mockOptions = {} as any; + const mockProps = {options:mockOptions, question:mockQuestion}; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockCreator = { + id: '123', + }; + const mockData = { + answer_type: "mockAnswerType", + category: "mockCategory", + created_time: "mockCreatedTime", + id: 1, + importance_score: 1, + multiple_choice_options: {"first_choice":"first_answer"}, + question: "mockQuestion" + }; + (shareUtils.getUser as jest.Mock).mockResolvedValue(mockCreator); + (supabaseUtils.insert as jest.Mock).mockResolvedValue(mockData); + (tryCatch as jest.Mock).mockResolvedValue({data:mockData, error: null}); + + const results = await createCompatibilityQuestion(mockProps, mockAuth, mockReq); + + expect(results.question).toEqual(mockData); + + }); + + it('throws an error if the account does not exist', async () => { + const mockQuestion = {} as any; + const mockOptions = {} as any; + const mockProps = {options:mockOptions, question:mockQuestion}; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + (shareUtils.getUser as jest.Mock).mockResolvedValue(null); + + expect(createCompatibilityQuestion(mockProps, mockAuth, mockReq)) + .rejects + .toThrowError('Your account was not found') + + }); + + it('throws an error if unable to create the question', async () => { + const mockQuestion = {} as any; + const mockOptions = {} as any; + const mockProps = {options:mockOptions, question:mockQuestion}; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockCreator = { + id: '123', + }; + const mockData = { + answer_type: "mockAnswerType", + category: "mockCategory", + created_time: "mockCreatedTime", + id: 1, + importance_score: 1, + multiple_choice_options: {"first_choice":"first_answer"}, + question: "mockQuestion" + }; + (shareUtils.getUser as jest.Mock).mockResolvedValue(mockCreator); + (supabaseUtils.insert as jest.Mock).mockResolvedValue(mockData); + (tryCatch as jest.Mock).mockResolvedValue({data:null, error: Error}); + + expect(createCompatibilityQuestion(mockProps, mockAuth, mockReq)) + .rejects + .toThrowError('Error creating question') + + }); + }); +}); \ No newline at end of file diff --git a/backend/api/tests/unit/create-notification.unit.test.ts b/backend/api/tests/unit/create-notification.unit.test.ts new file mode 100644 index 0000000..e979151 --- /dev/null +++ b/backend/api/tests/unit/create-notification.unit.test.ts @@ -0,0 +1,98 @@ +jest.mock('common/util/try-catch'); +jest.mock('shared/supabase/init'); +jest.mock('shared/supabase/notifications'); + +import * as supabaseInit from "shared/supabase/init"; +import * as createNotificationModules from "api/create-notification"; +import { tryCatch } from "common/util/try-catch"; +import * as supabaseNotifications from "shared/supabase/notifications"; +import { Notification } from "common/notifications"; + +type MockNotificationUser = Pick; + +describe('createNotifications', () => { + beforeEach(() => { + jest.resetAllMocks(); + const mockPg = { + many: jest.fn().mockReturnValue(null) + } as any; + + (supabaseInit.createSupabaseDirectClient as jest.Mock) + .mockReturnValue(mockPg); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe('should', () => { + it('sucessfully create a notification', async () => { + const mockUsers = [ + { + created_time: "mockCreatedTime", + data: {"mockData": "mockDataJson"}, + id: "mockId", + name: "mockName", + name_user_vector: "mockNUV", + username: "mockUsername" + }, + ]; + const mockNotification = { + userId: "mockUserId" + } as MockNotificationUser; + + (tryCatch as jest.Mock).mockResolvedValue({data: mockUsers, error:null}); + (supabaseNotifications.insertNotificationToSupabase as jest.Mock) + .mockResolvedValue(null); + + const results = await createNotificationModules.createNotifications(mockNotification as Notification); + expect(results?.success).toBeTruthy; + }); + + it('throws an error if its unable to fetch users', async () => { + const mockUsers = [ + { + created_time: "mockCreatedTime", + data: {"mockData": "mockDataJson"}, + id: "mockId", + name: "mockName", + name_user_vector: "mockNUV", + username: "mockUsername" + }, + ]; + const mockNotification = { + userId: "mockUserId" + } as MockNotificationUser; + + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + (tryCatch as jest.Mock).mockResolvedValue({data: mockUsers, error:Error}); + + await createNotificationModules.createNotifications(mockNotification as Notification) + expect(errorSpy).toHaveBeenCalledWith('Error fetching users', expect.objectContaining({name: 'Error'})) + }); + + it('throws an error if there are no users', async () => { + const mockUsers = [ + { + created_time: "mockCreatedTime", + data: {"mockData": "mockDataJson"}, + id: "mockId", + name: "mockName", + name_user_vector: "mockNUV", + username: "mockUsername" + }, + ]; + const mockNotification = { + userId: "mockUserId" + } as MockNotificationUser; + + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + (tryCatch as jest.Mock).mockResolvedValue({data: null, error:null}); + + await createNotificationModules.createNotifications(mockNotification as Notification) + expect(errorSpy).toHaveBeenCalledWith('No users found') + }); + }); +}); \ No newline at end of file diff --git a/backend/api/tests/unit/create-private-user-message-channel.unit.test.ts b/backend/api/tests/unit/create-private-user-message-channel.unit.test.ts new file mode 100644 index 0000000..5c3d42f --- /dev/null +++ b/backend/api/tests/unit/create-private-user-message-channel.unit.test.ts @@ -0,0 +1,229 @@ +jest.mock('shared/supabase/init'); +jest.mock('common/util/array'); +jest.mock('api/helpers/private-messages'); +jest.mock('shared/utils'); + +import { createPrivateUserMessageChannel } from "api/create-private-user-message-channel"; +import * as supabaseInit from "shared/supabase/init"; +import * as sharedUtils from "shared/utils"; +import * as utilArrayModules from "common/util/array"; +import * as privateMessageModules from "api/helpers/private-messages"; +import { AuthedUser } from "api/helpers/endpoint"; + +describe('createPrivateUserMessageChannel', () => { + let mockPg = {} as any; + beforeEach(() => { + jest.resetAllMocks(); + mockPg = { + oneOrNone: jest.fn(), + one: jest.fn(), + none: jest.fn() + }; + + (supabaseInit.createSupabaseDirectClient as jest.Mock) + .mockReturnValue(mockPg) + }); + + afterEach(() => { + jest.restoreAllMocks() + }); + + describe('should', () => { + it('successfully create a private user message channel (currentChannel)', async () => { + const mockBody = { + userIds: ["123"] + }; + const mockUserIds = ['123', '321']; + const mockPrivateUsers = [ + { + id: '123', + blockedUserIds: ['111'], + blockedByUserIds: [], + }, + { + id: '321', + blockedUserIds: ['111'], + blockedByUserIds: [], + }, + ]; + const mockCurrentChannel = { + channel_id: "444" + }; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockCreator = { + isBannedFromPosting: false + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValue(mockCreator); + (sharedUtils.getPrivateUser as jest.Mock) + .mockResolvedValue(mockUserIds); + (utilArrayModules.filterDefined as jest.Mock) + .mockReturnValue(mockPrivateUsers); + (mockPg.oneOrNone as jest.Mock) + .mockResolvedValue(mockCurrentChannel); + (privateMessageModules.addUsersToPrivateMessageChannel as jest.Mock) + .mockResolvedValue(null); + + const results = await createPrivateUserMessageChannel(mockBody, mockAuth, mockReq); + expect(sharedUtils.getUser).toBeCalledTimes(1); + expect(sharedUtils.getUser).toBeCalledWith(mockAuth.uid); + expect(sharedUtils.getPrivateUser).toBeCalledTimes(2); + expect(sharedUtils.getPrivateUser).toBeCalledWith(mockUserIds[0]); + expect(sharedUtils.getPrivateUser).toBeCalledWith(mockUserIds[1]); + expect(results.status).toBe('success'); + expect(results.channelId).toBe(444) + + }); + + it('successfully create a private user message channel (channel)', async () => { + const mockBody = { + userIds: ["123"] + }; + const mockUserIds = ['123', '321']; + const mockPrivateUsers = [ + { + id: '123', + blockedUserIds: ['111'], + blockedByUserIds: [], + }, + { + id: '321', + blockedUserIds: ['111'], + blockedByUserIds: [], + }, + ]; + const mockChannel = { + id: "333" + }; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockCreator = { + isBannedFromPosting: false + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValue(mockCreator); + (sharedUtils.getPrivateUser as jest.Mock) + .mockResolvedValue(mockUserIds); + (utilArrayModules.filterDefined as jest.Mock) + .mockReturnValue(mockPrivateUsers); + (mockPg.oneOrNone as jest.Mock) + .mockResolvedValue(null); + (mockPg.one as jest.Mock) + .mockResolvedValue(mockChannel); + (privateMessageModules.addUsersToPrivateMessageChannel as jest.Mock) + .mockResolvedValue(null); + + const results = await createPrivateUserMessageChannel(mockBody, mockAuth, mockReq); + expect(sharedUtils.getUser).toBeCalledTimes(1); + expect(sharedUtils.getUser).toBeCalledWith(mockAuth.uid); + expect(sharedUtils.getPrivateUser).toBeCalledTimes(2); + expect(sharedUtils.getPrivateUser).toBeCalledWith(mockUserIds[0]); + expect(sharedUtils.getPrivateUser).toBeCalledWith(mockUserIds[1]); + expect(results.status).toBe('success'); + expect(results.channelId).toBe(333) + + }); + + it('throw an error if the user account doesnt exist', async () => { + const mockBody = { + userIds: ["123"] + }; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + (sharedUtils.getUser as jest.Mock) + .mockResolvedValue(null); + + expect(createPrivateUserMessageChannel(mockBody, mockAuth, mockReq)) + .rejects + .toThrowError('Your account was not found'); + }); + + it('throw an error if the authId is banned from posting', async () => { + const mockBody = { + userIds: ["123"] + }; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockCreator = { + isBannedFromPosting: true + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValue(mockCreator); + + expect(createPrivateUserMessageChannel(mockBody, mockAuth, mockReq)) + .rejects + .toThrowError('You are banned'); + expect(sharedUtils.getUser).toBeCalledTimes(1); + expect(sharedUtils.getUser).toBeCalledWith(mockAuth.uid); + }); + + it('throw an error if the array lengths dont match (privateUsers, userIds)', async () => { + const mockBody = { + userIds: ["123"] + }; + const mockUserIds = ['123']; + const mockPrivateUsers = [ + { + id: '123', + blockedUserIds: ['111'], + blockedByUserIds: [], + }, + ]; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockCreator = { + isBannedFromPosting: false + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValue(mockCreator); + (sharedUtils.getPrivateUser as jest.Mock) + .mockResolvedValue(mockUserIds); + (utilArrayModules.filterDefined as jest.Mock) + .mockReturnValue(mockPrivateUsers); + + expect(createPrivateUserMessageChannel(mockBody, mockAuth, mockReq)) + .rejects + .toThrowError(`Private user ${mockAuth.uid} not found`); + }); + + it('throw an error if there is a blocked user in the userId list', async () => { + const mockBody = { + userIds: ["123"] + }; + const mockUserIds = ['321']; + const mockPrivateUsers = [ + { + id: '123', + blockedUserIds: ['111'], + blockedByUserIds: [], + }, + { + id: '321', + blockedUserIds: ['123'], + blockedByUserIds: [], + }, + ]; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockCreator = { + isBannedFromPosting: false + }; + + (sharedUtils.getUser as jest.Mock) + .mockResolvedValue(mockCreator); + (sharedUtils.getPrivateUser as jest.Mock) + .mockResolvedValue(mockUserIds); + (utilArrayModules.filterDefined as jest.Mock) + .mockReturnValue(mockPrivateUsers); + + expect(createPrivateUserMessageChannel(mockBody, mockAuth, mockReq)) + .rejects + .toThrowError(`One of the users has blocked another user in the list`); + }); + }); +}); \ No newline at end of file diff --git a/backend/api/tests/unit/create-private-user-message.unit.test.ts b/backend/api/tests/unit/create-private-user-message.unit.test.ts new file mode 100644 index 0000000..bbea013 --- /dev/null +++ b/backend/api/tests/unit/create-private-user-message.unit.test.ts @@ -0,0 +1,99 @@ +jest.mock('shared/utils'); +jest.mock('shared/supabase/init'); +jest.mock('api/helpers/private-messages'); + +import { createPrivateUserMessage } from "api/create-private-user-message"; +import * as sharedUtils from "shared/utils"; +import * as supabaseInit from "shared/supabase/init"; +import * as helpersPrivateMessagesModules from "api/helpers/private-messages"; +import { AuthedUser } from "api/helpers/endpoint"; +import { MAX_COMMENT_JSON_LENGTH } from "api/create-comment"; + +describe('createPrivateUserMessage', () => { + beforeEach(() => { + jest.resetAllMocks(); + + const mockPg = {} as any; + + (supabaseInit.createSupabaseDirectClient as jest.Mock) + .mockReturnValue(mockPg); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe('should', () => { + it('successfully create a private user message', async () => { + const mockBody = { + content: {"": "x".repeat((MAX_COMMENT_JSON_LENGTH-8))}, + channelId: 123 + }; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockCreator = { + isBannedFromPosting: false + }; + + (sharedUtils.getUser as jest.Mock).mockResolvedValue(mockCreator); + (helpersPrivateMessagesModules.createPrivateUserMessageMain as jest.Mock) + .mockResolvedValue(null); + + await createPrivateUserMessage(mockBody, mockAuth, mockReq); + expect(helpersPrivateMessagesModules.createPrivateUserMessageMain).toBeCalledWith( + mockCreator, + mockBody.channelId, + mockBody.content, + expect.any(Object), + 'private' + ); + }); + + it('throw an error if the content is too long', async () => { + const mockBody = { + content: {"": "x".repeat((MAX_COMMENT_JSON_LENGTH))}, + channelId: 123 + } + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + + expect(createPrivateUserMessage(mockBody, mockAuth, mockReq)) + .rejects + .toThrowError(`Message JSON should be less than ${MAX_COMMENT_JSON_LENGTH}`); + }); + + it('throw an error if the user does not exist', async () => { + const mockBody = { + content: {"mockJson": "mockJsonContent"}, + channelId: 123 + } + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + + (sharedUtils.getUser as jest.Mock).mockResolvedValue(null); + + expect(createPrivateUserMessage(mockBody, mockAuth, mockReq)) + .rejects + .toThrowError(`Your account was not found`); + }); + + it('throw an error if the user does not exist', async () => { + const mockBody = { + content: {"mockJson": "mockJsonContent"}, + channelId: 123 + } + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockCreator = { + isBannedFromPosting: true + }; + + (sharedUtils.getUser as jest.Mock).mockResolvedValue(mockCreator); + + expect(createPrivateUserMessage(mockBody, mockAuth, mockReq)) + .rejects + .toThrowError(`You are banned`); + }); + }); +}); + diff --git a/backend/api/tests/unit/create-profile.unit.test.ts b/backend/api/tests/unit/create-profile.unit.test.ts new file mode 100644 index 0000000..50842fe --- /dev/null +++ b/backend/api/tests/unit/create-profile.unit.test.ts @@ -0,0 +1,160 @@ +jest.mock('shared/supabase/init'); +jest.mock('shared/utils'); +jest.mock('shared/profiles/parse-photos'); +jest.mock('shared/supabase/users'); +jest.mock('shared/supabase/utils'); +jest.mock('common/util/try-catch'); +jest.mock('shared/analytics'); +jest.mock('common/discord/core'); + +import { createProfile } from "api/create-profile"; +import * as supabaseInit from "shared/supabase/init"; +import * as sharedUtils from "shared/utils"; +import * as supabaseUsers from "shared/supabase/users"; +import * as supabaseUtils from "shared/supabase/utils"; +import { tryCatch } from "common/util/try-catch"; +import { removePinnedUrlFromPhotoUrls } from "shared/profiles/parse-photos"; +import * as sharedAnalytics from "shared/analytics"; +import { sendDiscordMessage } from "common/discord/core"; +import { AuthedUser } from "api/helpers/endpoint"; + +describe('createProfile', () => { + let mockPg = {} as any; + + beforeEach(() => { + jest.resetAllMocks(); + + mockPg = { + oneOrNone: jest.fn().mockReturnValue(null), + one: jest.fn() + }; + + (supabaseInit.createSupabaseDirectClient as jest.Mock) + .mockReturnValue(mockPg); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe('should', () => { + it('successfully create a profile', async () => { + const mockBody = { + city: "mockCity", + gender: "mockGender", + looking_for_matches: true, + photo_urls: ["mockPhotoUrl1"], + pinned_url: "mockPinnedUrl", + pref_gender: ["mockPrefGender"], + pref_relation_styles: ["mockPrefRelationStyles"], + visibility: 'public' as "public" | "member", + wants_kids_strength: 2, + }; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockExistingUser = {id: "mockExistingUserId"}; + const mockData = { + age: 30, + city: "mockCity" + }; + const mockUser = { + createdTime: Date.now() - 2 * 60 * 60 * 1000, //2 hours ago + name: "mockName", + username: "mockUserName" + }; + + (tryCatch as jest.Mock).mockResolvedValueOnce({data: null, error: null}); + (sharedUtils.getUser as jest.Mock).mockResolvedValue(mockUser); + (supabaseUsers.updateUser as jest.Mock).mockReturnValue(null); + (supabaseUtils.insert as jest.Mock).mockReturnValue(null); + (tryCatch as jest.Mock).mockResolvedValueOnce({data: mockData, error: null}); + (sharedAnalytics.track as jest.Mock).mockResolvedValue(null); + (sendDiscordMessage as jest.Mock).mockResolvedValueOnce(null); + (mockPg.one as jest.Mock).mockReturnValue(10); + + const results: any = await createProfile(mockBody, mockAuth, mockReq); + expect(results.result).toEqual(mockData) + }); + + it('throws an error if the profile already exists', async () => { + const mockBody = { + city: "mockCity", + gender: "mockGender", + looking_for_matches: true, + photo_urls: ["mockPhotoUrl1"], + pinned_url: "mockPinnedUrl", + pref_gender: ["mockPrefGender"], + pref_relation_styles: ["mockPrefRelationStyles"], + visibility: 'public' as "public" | "member", + wants_kids_strength: 2, + }; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockExistingUser = {id: "mockExistingUserId"}; + + (tryCatch as jest.Mock).mockResolvedValueOnce({data: mockExistingUser, error: null}); + + await expect(createProfile(mockBody, mockAuth, mockReq)) + .rejects + .toThrowError('User already exists'); + }); + + it('throws an error if the user already exists', async () => { + const mockBody = { + city: "mockCity", + gender: "mockGender", + looking_for_matches: true, + photo_urls: ["mockPhotoUrl1"], + pinned_url: "mockPinnedUrl", + pref_gender: ["mockPrefGender"], + pref_relation_styles: ["mockPrefRelationStyles"], + visibility: 'public' as "public" | "member", + wants_kids_strength: 2, + }; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + + (tryCatch as jest.Mock).mockResolvedValueOnce({data: null, error: null}); + (sharedUtils.getUser as jest.Mock).mockResolvedValue(null); + + await expect(createProfile(mockBody, mockAuth, mockReq)) + .rejects + .toThrowError('Your account was not found'); + expect(sharedUtils.getUser).toBeCalledTimes(1); + expect(sharedUtils.getUser).toBeCalledWith(mockAuth.uid); + }); + + it('throw an error if anything unexpected happens when creating the user', async () => { + const mockBody = { + city: "mockCity", + gender: "mockGender", + looking_for_matches: true, + photo_urls: ["mockPhotoUrl1"], + pinned_url: "mockPinnedUrl", + pref_gender: ["mockPrefGender"], + pref_relation_styles: ["mockPrefRelationStyles"], + visibility: 'public' as "public" | "member", + wants_kids_strength: 2, + }; + const mockAuth = {uid: '321'} as AuthedUser; + const mockReq = {} as any; + const mockUser = { + createdTime: Date.now() - 2 * 60 * 60 * 1000, //2 hours ago + name: "mockName", + username: "mockUserName" + }; + + (tryCatch as jest.Mock).mockResolvedValueOnce({data: null, error: null}); + (sharedUtils.getUser as jest.Mock).mockResolvedValue(mockUser); + (supabaseUsers.updateUser as jest.Mock).mockReturnValue(null); + (supabaseUtils.insert as jest.Mock).mockReturnValue(null); + (tryCatch as jest.Mock).mockResolvedValueOnce({data: null, error: Error}); + + await expect(createProfile(mockBody, mockAuth, mockReq)) + .rejects + .toThrowError('Error creating user'); + expect(sharedUtils.getUser).toBeCalledTimes(1); + expect(sharedUtils.getUser).toBeCalledWith(mockAuth.uid); + }); + }); +}); \ No newline at end of file diff --git a/backend/api/tests/unit/create-user.unit.test.ts b/backend/api/tests/unit/create-user.unit.test.ts new file mode 100644 index 0000000..afc32b7 --- /dev/null +++ b/backend/api/tests/unit/create-user.unit.test.ts @@ -0,0 +1,7 @@ +describe('createUser', () => { + describe('should', () => { + it('', async () => { + + }); + }); +}); \ No newline at end of file diff --git a/backend/api/tsconfig.test.json b/backend/api/tsconfig.test.json index 58048e7..6c128f3 100644 --- a/backend/api/tsconfig.test.json +++ b/backend/api/tsconfig.test.json @@ -13,5 +13,10 @@ "email/*": ["../email/emails/*"] } }, - "include": ["tests/**/*.ts", "src/**/*.ts"] + "include": [ + "tests/**/*.ts", + "src/**/*.ts", + "../shared/src/**/*.ts", + "../../common/src/**/*.ts" + ] } diff --git a/backend/shared/jest.config.js b/backend/shared/jest.config.js index 02a37b3..35eecc5 100644 --- a/backend/shared/jest.config.js +++ b/backend/shared/jest.config.js @@ -9,8 +9,8 @@ module.exports = { ], moduleNameMapper: { - "^api/(.*)$": "/src/$1", - "^shared/(.*)$": "/../shared/src/$1", + "^api/(.*)$": "/../api/src/$1", + "^shared/(.*)$": "/src/$1", "^common/(.*)$": "/../../common/src/$1", "^email/(.*)$": "/../email/emails/$1" }, diff --git a/backend/shared/tests/unit/compute-score.unit.test.ts b/backend/shared/tests/unit/compute-score.unit.test.ts index 0befbc3..7d88af7 100644 --- a/backend/shared/tests/unit/compute-score.unit.test.ts +++ b/backend/shared/tests/unit/compute-score.unit.test.ts @@ -1,4 +1,4 @@ -import {recomputeCompatibilityScoresForUser} from "api/compatibility/compute-scores"; +import {recomputeCompatibilityScoresForUser} from "shared/compatibility/compute-scores"; import * as supabaseInit from "shared/supabase/init"; import * as profilesSupabaseModules from "shared/profiles/supabase"; import * as compatibilityScoreModules from "common/profiles/compatibility-score"; diff --git a/backend/shared/tsconfig.json b/backend/shared/tsconfig.json index bf38a58..f2fe40b 100644 --- a/backend/shared/tsconfig.json +++ b/backend/shared/tsconfig.json @@ -15,10 +15,23 @@ "lib": ["esnext"], "skipLibCheck": true, "paths": { - "common/*": ["../../common/src/*", "../../../common/lib/*"], - "shared/*": ["./src/*"] + "common/*": ["../../common/src/*", + "../../../common/lib/*" + ], + "shared/*": [ + "./src/*" + ], } }, - "references": [{ "path": "../../common" }], + "ts-node": { + "require": [ + "tsconfig-paths/register" + ] + }, + "references": [ + { + "path": "../../common" + }, + ], "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/backend/shared/tsconfig.test.json b/backend/shared/tsconfig.test.json index 58048e7..55c25e9 100644 --- a/backend/shared/tsconfig.test.json +++ b/backend/shared/tsconfig.test.json @@ -7,8 +7,8 @@ "rootDir": "../..", "baseUrl": ".", "paths": { - "api/*": ["src/*"], - "shared/*": ["../shared/src/*"], + "api/*": ["../api/src/*"], + "shared/*": ["src/*"], "common/*": ["../../common/src/*"], "email/*": ["../email/emails/*"] } diff --git a/tsconfig.json b/tsconfig.json index fb7654a..44a4488 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,8 @@ "files": [], "references": [ { "path": "./backend/api" }, - { "path": "./backend/api/tsconfig.test.json" } + { "path": "./backend/api/tsconfig.test.json" }, + { "path": "./backend/shared" }, + { "path": "./backend/shared/tsconfig.test.json" } ] }