diff --git a/app/actions/remote/command.test.ts b/app/actions/remote/command.test.ts index 005a81a37..3e565ea5c 100644 --- a/app/actions/remote/command.test.ts +++ b/app/actions/remote/command.test.ts @@ -107,6 +107,15 @@ describe('executeCommand', () => { expect(result).toEqual({error: 'invalid_url database not found'}); }); + it('handle client error', async () => { + jest.spyOn(NetworkManager, 'getClient').mockImplementationOnce(() => { + throw error; + }); + + const result = await executeCommand(serverUrl, intl, message, channelId, rootId); + expect(result).toEqual({error}); + }); + it('handle apps enabled', async () => { jest.spyOn(AppsManager, 'isAppsEnabled').mockResolvedValue(true); const parser = { @@ -164,6 +173,34 @@ describe('executeCommand', () => { expect(result).toEqual({data: {trigger_id: 'trigger_id'}}); }); + it('handle /code command execution with successful response', async () => { + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + + jest.spyOn(AppsManager, 'isAppsEnabled').mockResolvedValue(false); + const mockSetTriggerId = jest.fn(); + jest.spyOn(IntegrationsManager, 'getManager').mockReturnValue({ + setTriggerId: mockSetTriggerId, + } as any); + + const result = await executeCommand(serverUrl, intl, '/code', channelId, rootId); + + expect(mockClient.executeCommand).toHaveBeenCalledWith('/code ', args); + expect(mockSetTriggerId).toHaveBeenCalledWith('trigger_id'); + expect(result).toEqual({data: {trigger_id: 'trigger_id'}}); + }); + + it('handle command execution with no trigger id', async () => { + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + + jest.spyOn(AppsManager, 'isAppsEnabled').mockResolvedValue(false); + mockClient.executeCommand.mockResolvedValueOnce({} as never); + + const result = await executeCommand(serverUrl, intl, message, channelId, rootId); + + expect(mockClient.executeCommand).toHaveBeenCalledWith(message, args); + expect(result).toEqual({data: {}}); + }); + it('handle command execution with error response', async () => { await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); @@ -181,6 +218,16 @@ describe('executeCommand', () => { describe('executeAppCommand', () => { const msg = 'test message'; + it('should handle a undefined creq', async () => { + const parser = { + composeCommandSubmitCall: jest.fn().mockResolvedValue({errorMessage: 'Error occurred'}), + }; + + const result = await executeAppCommand(serverUrl, intl, parser as any, msg, args); + + expect(result).toEqual({error: {message: 'Error occurred'}}); + }); + it('should handle a successful command execution with OK response', async () => { const parser = { composeCommandSubmitCall: jest.fn().mockResolvedValue({creq: {}, errorMessage: null}), @@ -196,6 +243,20 @@ describe('executeAppCommand', () => { expect(result).toEqual({data: {}}); }); + it('should handle OK response with no text', async () => { + const parser = { + composeCommandSubmitCall: jest.fn().mockResolvedValue({creq: {}, errorMessage: null}), + }; + (AppCommandParser as jest.Mock).mockReturnValue(parser); + (doAppSubmit as jest.Mock).mockResolvedValue({data: {type: AppCallResponseTypes.OK}}); + + const result = await executeAppCommand(serverUrl, intl, parser as any, msg, args); + + expect(parser.composeCommandSubmitCall).toHaveBeenCalledWith(msg); + expect(doAppSubmit).toHaveBeenCalledWith(serverUrl, {}, intl); + expect(result).toEqual({data: {}}); + }); + it('should handle an error response', async () => { const parser = { composeCommandSubmitCall: jest.fn().mockResolvedValue({creq: {}, errorMessage: null}), @@ -210,6 +271,20 @@ describe('executeAppCommand', () => { expect(result).toEqual({error: {message: 'Error occurred'}}); }); + it('should handle an error response with no text', async () => { + const parser = { + composeCommandSubmitCall: jest.fn().mockResolvedValue({creq: {}, errorMessage: null}), + }; + (AppCommandParser as jest.Mock).mockReturnValue(parser); + (doAppSubmit as jest.Mock).mockResolvedValue({error: {}}); + + const result = await executeAppCommand(serverUrl, intl, parser as any, msg, args); + + expect(parser.composeCommandSubmitCall).toHaveBeenCalledWith(msg); + expect(doAppSubmit).toHaveBeenCalledWith(serverUrl, {}, intl); + expect(result).toEqual({error: {message: 'Unknown error.'}}); + }); + it('should handle a form response', async () => { const parser = { composeCommandSubmitCall: jest.fn().mockResolvedValue({creq: {context: {}}, errorMessage: null}), @@ -225,6 +300,20 @@ describe('executeAppCommand', () => { expect(result).toEqual({data: {}}); }); + it('should handle a form response with no form', async () => { + const parser = { + composeCommandSubmitCall: jest.fn().mockResolvedValue({creq: {context: {}}, errorMessage: null}), + }; + (AppCommandParser as jest.Mock).mockReturnValue(parser); + (doAppSubmit as jest.Mock).mockResolvedValue({data: {type: AppCallResponseTypes.FORM}}); + + const result = await executeAppCommand(serverUrl, intl, parser as any, msg, args); + + expect(parser.composeCommandSubmitCall).toHaveBeenCalledWith(msg); + expect(doAppSubmit).toHaveBeenCalledWith(serverUrl, {context: {}}, intl); + expect(result).toEqual({data: {}}); + }); + it('should handle a navigate response', async () => { const parser = { composeCommandSubmitCall: jest.fn().mockResolvedValue({creq: {}, errorMessage: null}), @@ -239,6 +328,20 @@ describe('executeAppCommand', () => { expect(result).toEqual({data: {}}); }); + it('should handle a navigate response with no url', async () => { + const parser = { + composeCommandSubmitCall: jest.fn().mockResolvedValue({creq: {}, errorMessage: null}), + }; + (AppCommandParser as jest.Mock).mockReturnValue(parser); + (doAppSubmit as jest.Mock).mockResolvedValue({data: {type: AppCallResponseTypes.NAVIGATE}}); + + const result = await executeAppCommand(serverUrl, intl, parser as any, msg, args); + + expect(parser.composeCommandSubmitCall).toHaveBeenCalledWith(msg); + expect(doAppSubmit).toHaveBeenCalledWith(serverUrl, {}, intl); + expect(result).toEqual({data: {}}); + }); + it('should handle an unknown response type', async () => { const parser = { composeCommandSubmitCall: jest.fn().mockResolvedValue({creq: {}, errorMessage: null}), diff --git a/app/actions/remote/post.test.ts b/app/actions/remote/post.test.ts index 428381c3f..998d4765d 100644 --- a/app/actions/remote/post.test.ts +++ b/app/actions/remote/post.test.ts @@ -3,7 +3,7 @@ /* eslint-disable max-lines */ -import {ActionType, Post} from '@constants'; +import {ActionType, Post, ServerErrors} from '@constants'; import {SYSTEM_IDENTIFIERS} from '@constants/database'; import DatabaseManager from '@database/manager'; import PostModel from '@database/models/server/post'; @@ -121,6 +121,16 @@ jest.mock('@queries/servers/thread', () => { }; }); +let mockAddRecentReaction: jest.Mock; +jest.mock('@actions/local/reactions', () => { + const original = jest.requireActual('@actions/local/reactions'); + mockAddRecentReaction = jest.fn(() => [{user_id: 'userid1', emoji_name: 'smile'}]); + return { + ...original, + addRecentReaction: mockAddRecentReaction, + }; +}); + beforeAll(() => { // eslint-disable-next-line // @ts-ignore @@ -143,6 +153,28 @@ describe('create, update & delete posts', () => { expect(result.error).toBeTruthy(); }); + it('createPost - handle client error', async () => { + jest.spyOn(NetworkManager, 'getClient').mockImplementationOnce(throwFunc); + + const result = await createPost(serverUrl, post1); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('createPost - handle existing failed post', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [{...post1, props: {failed: false}}], + prepareRecordsOnly: false, + }); + + const result = await createPost(serverUrl, {...post1, pending_post_id: post1.id}); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.data).toBe(false); + }); + it('createPost - fail create', async () => { mockClient.createPost.mockImplementationOnce(jest.fn(throwFunc)); await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); @@ -153,6 +185,45 @@ describe('create, update & delete posts', () => { expect(result.data).toBeTruthy(); }); + it('createPost - fail on deleted root post server error', async () => { + mockClient.createPost.mockImplementationOnce(jest.fn(() => { + // eslint-disable-next-line no-throw-literal + throw {message: 'error', server_error_id: ServerErrors.DELETED_ROOT_POST_ERROR}; + })); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await createPost(serverUrl, post1); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.data).toBeTruthy(); + }); + + it('createPost - fail on town square read only server error', async () => { + mockClient.createPost.mockImplementationOnce(jest.fn(() => { + // eslint-disable-next-line no-throw-literal + throw {message: 'error', server_error_id: ServerErrors.TOWN_SQUARE_READ_ONLY_ERROR}; + })); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await createPost(serverUrl, post1); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.data).toBeTruthy(); + }); + + it('createPost - fail on plugin dismissed post server error', async () => { + mockClient.createPost.mockImplementationOnce(jest.fn(() => { + // eslint-disable-next-line no-throw-literal + throw {message: 'error', server_error_id: ServerErrors.PLUGIN_DISMISSED_POST_ERROR}; + })); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await createPost(serverUrl, post1); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.data).toBeTruthy(); + }); + it('createPost - root', async () => { await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); @@ -162,6 +233,16 @@ describe('create, update & delete posts', () => { expect(result.data).toBeTruthy(); }); + it('createPost - without reactions', async () => { + mockAddRecentReaction.mockImplementationOnce(() => []); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await createPost(serverUrl, post1); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.data).toBeTruthy(); + }); + it('createPost - reply', async () => { await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); @@ -177,6 +258,14 @@ describe('create, update & delete posts', () => { expect(result.error).toBeTruthy(); }); + it('retryFailedPost - handle client error', async () => { + jest.spyOn(NetworkManager, 'getClient').mockImplementationOnce(throwFunc); + + const result = await retryFailedPost(serverUrl, mockPostModel({id: post1.id, prepareUpdate: jest.fn(), toApi: async () => post1})); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + it('retryFailedPost - base case', async () => { await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); @@ -192,6 +281,42 @@ describe('create, update & delete posts', () => { expect(result.error).toBeTruthy(); }); + it('retryFailedPost - fail on deleted root post server error', async () => { + mockClient.createPost.mockImplementationOnce(jest.fn(() => { + // eslint-disable-next-line no-throw-literal + throw {message: 'error', server_error_id: ServerErrors.DELETED_ROOT_POST_ERROR}; + })); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await retryFailedPost(serverUrl, mockPostModel({id: post1.id, prepareUpdate: jest.fn(), toApi: async () => post1})); + expect(result).toBeDefined(); + expect(result.error).toBeDefined(); + }); + + it('retryFailedPost - fail on town square read only server error', async () => { + mockClient.createPost.mockImplementationOnce(jest.fn(() => { + // eslint-disable-next-line no-throw-literal + throw {message: 'error', server_error_id: ServerErrors.TOWN_SQUARE_READ_ONLY_ERROR}; + })); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await retryFailedPost(serverUrl, mockPostModel({id: post1.id, prepareUpdate: jest.fn(), toApi: async () => post1})); + expect(result).toBeDefined(); + expect(result.error).toBeDefined(); + }); + + it('retryFailedPost - fail on plugin dismissed post server error', async () => { + mockClient.createPost.mockImplementationOnce(jest.fn(() => { + // eslint-disable-next-line no-throw-literal + throw {message: 'error', server_error_id: ServerErrors.PLUGIN_DISMISSED_POST_ERROR}; + })); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await retryFailedPost(serverUrl, mockPostModel({id: post1.id, prepareUpdate: jest.fn(), toApi: async () => post1})); + expect(result).toBeDefined(); + expect(result.error).toBeDefined(); + }); + it('togglePinPost - handle database not found', async () => { const result = await togglePinPost('foo', ''); expect(result).toBeDefined(); @@ -382,6 +507,65 @@ describe('get posts', () => { expect(result.posts?.length).toBe(2); }); + it('fetchPostsForChannel - base case with since', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [{ + id: channelId, + team_id: teamId, + total_msg_count: 0, + creator_id: user1.id, + } as Channel], + myChannels: [{ + id: 'id', + channel_id: channelId, + user_id: user1.id, + msg_count: 0, + } as ChannelMembership], + prepareRecordsOnly: false}); + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [post1], + prepareRecordsOnly: false, + }); + + const result = await fetchPostsForChannel(serverUrl, channelId, true); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); + + it('fetchPostsForChannel - no posts with since', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [{ + id: channelId, + team_id: teamId, + total_msg_count: 0, + creator_id: user1.id, + } as Channel], + myChannels: [{ + id: 'id', + channel_id: channelId, + user_id: user1.id, + msg_count: 0, + } as ChannelMembership], + prepareRecordsOnly: false}); + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [post1], + prepareRecordsOnly: false, + }); + + mockClient.getPostsSince.mockImplementationOnce(jest.fn(() => ({posts: {}, order: []}))); + const result = await fetchPostsForChannel(serverUrl, channelId, true); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(0); + }); + it('fetchPostsForChannel - request error', async () => { mockClient.getPosts.mockImplementationOnce(jest.fn(throwFunc)); @@ -419,8 +603,30 @@ describe('get posts', () => { expect(result.posts?.length).toBe(2); }); + it('fetchPosts - no CRT', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockGetIsCRTEnabled.mockImplementationOnce(() => false); + + const result = await fetchPosts(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); + + it('fetchPosts - no authors needed', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockClient.getProfilesByIds.mockImplementationOnce(jest.fn(() => [])); + + const result = await fetchPosts(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); + it('fetchPostsBefore - handle database not found', async () => { - const result = await fetchPostsBefore('foo', '', '') as {error: unknown}; + const result = await fetchPostsBefore('foo', '', '', 50, true) as {error: unknown}; expect(result).toBeDefined(); expect(result.error).toBeTruthy(); }); @@ -438,6 +644,48 @@ describe('get posts', () => { expect(result.posts?.length).toBe(2); }); + it('fetchPostsBefore - no CRT', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockGetIsCRTEnabled.mockImplementationOnce(() => false); + + const result = await fetchPostsBefore(serverUrl, channelId, post1.id) as { + posts: Post[]; + order: string[]; + previousPostId: string | undefined; + }; + expect(result).toBeDefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); + + it('fetchPostsBefore - no authors needed', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockClient.getProfilesByIds.mockImplementationOnce(jest.fn(() => [])); + + const result = await fetchPostsBefore(serverUrl, channelId, post1.id) as { + posts: Post[]; + order: string[]; + previousPostId: string | undefined; + }; + expect(result).toBeDefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); + + it('fetchPostsBefore - no posts', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockClient.getPostsBefore.mockImplementationOnce(jest.fn(() => ({posts: {}, order: []}))); + + const result = await fetchPostsBefore(serverUrl, channelId, post1.id) as { + posts: Post[]; + order: string[]; + previousPostId: string | undefined; + }; + expect(result).toBeDefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(0); + }); + it('fetchPostsSince - handle database not found', async () => { const result = await fetchPostsSince('foo', '', 0); expect(result).toBeDefined(); @@ -500,6 +748,40 @@ describe('get posts', () => { expect(result.posts?.[1].id).toBe(reply1.id); }); + it('fetchPostThread - no CRT', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockGetIsCRTEnabled.mockImplementationOnce(() => false); + + const result = await fetchPostThread(serverUrl, post1.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + expect(result.posts?.[0].id).toBe(post1.id); + expect(result.posts?.[1].id).toBe(reply1.id); + }); + + it('fetchPostThread - no authors needed', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockClient.getProfilesByIds.mockImplementationOnce(jest.fn(() => [])); + + const result = await fetchPostThread(serverUrl, post1.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + }); + + it('fetchPostThread - no posts', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockClient.getPostThread.mockImplementationOnce(jest.fn(() => ({posts: {}, order: []}))); + + const result = await fetchPostThread(serverUrl, post1.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(0); + }); + it('fetchPostsAround - handle database not found', async () => { const result = await fetchPostsAround('foo', '', ''); expect(result).toBeDefined(); @@ -516,6 +798,28 @@ describe('get posts', () => { expect(result.posts?.length).toBe(2); }); + it('fetchPostsAround - no CRT', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockGetIsCRTEnabled.mockImplementationOnce(() => false); + + const result = await fetchPostsAround(serverUrl, channelId, post2.id, 100, true); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); + + it('fetchPostsAround - no authors needed', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockClient.getProfilesByIds.mockImplementationOnce(jest.fn(() => [])); + + const result = await fetchPostsAround(serverUrl, channelId, post2.id, 100, true); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); + it('fetchMissingChannelsFromPosts - handle database not found', async () => { const result = await fetchMissingChannelsFromPosts('foo', []); expect(result).toBeDefined(); @@ -548,6 +852,38 @@ describe('get posts', () => { expect(result.post?.id).toBe(post2.id); }); + it('fetchPostById - no CRT', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockGetIsCRTEnabled.mockImplementationOnce(() => false); + + const result = await fetchPostById(serverUrl, post2.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.post).toBeDefined(); + expect(result.post?.id).toBe(post2.id); + }); + + it('fetchPostById - no authors needed', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + mockClient.getProfilesByIds.mockImplementationOnce(jest.fn(() => [])); + + const result = await fetchPostById(serverUrl, post2.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.post).toBeDefined(); + expect(result.post?.id).toBe(post2.id); + }); + + it('fetchPostById - fetch only', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await fetchPostById(serverUrl, post2.id, true); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.post).toBeDefined(); + expect(result.post?.id).toBe(post2.id); + }); + it('fetchSavedPosts - handle database not found', async () => { const result = await fetchSavedPosts('foo'); expect(result).toBeDefined(); diff --git a/app/actions/remote/session.test.ts b/app/actions/remote/session.test.ts index ae92fb0eb..09051d2b8 100644 --- a/app/actions/remote/session.test.ts +++ b/app/actions/remote/session.test.ts @@ -3,6 +3,8 @@ /* eslint-disable max-lines */ +import {Platform} from 'react-native'; + import {GLOBAL_IDENTIFIERS, SYSTEM_IDENTIFIERS} from '@constants/database'; import DatabaseManager from '@database/manager'; import NetworkManager from '@managers/network_manager'; @@ -12,6 +14,7 @@ import { forceLogoutIfNecessary, fetchSessions, login, + logout, cancelSessionNotification, scheduleSessionNotification, sendPasswordResetEmail, @@ -109,6 +112,13 @@ describe('sessions', () => { expect(result.error).toBeDefined(); }); + it('addPushProxyVerificationStateFromLogin - no verification', async () => { + mockGetPushProxyVerificationState.mockImplementationOnce(() => ''); + const result = await addPushProxyVerificationStateFromLogin(serverUrl); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + }); + it('addPushProxyVerificationStateFromLogin - base case', async () => { const result = await addPushProxyVerificationStateFromLogin(serverUrl); expect(result).toBeDefined(); @@ -146,6 +156,13 @@ describe('sessions', () => { expect(result).toBeUndefined(); }); + it('fetchSessions - handle client error', async () => { + jest.spyOn(NetworkManager, 'getClient').mockImplementationOnce(throwFunc); + + const result = await fetchSessions(serverUrl, user1.id); + expect(result).toBeUndefined(); + }); + it('fetchSessions - base case', async () => { const result = await fetchSessions(serverUrl, user1.id); expect(result).toBeDefined(); @@ -168,6 +185,21 @@ describe('sessions', () => { expect(result.failed).toBe(true); }); + it('login - handle throw after login request', async () => { + jest.spyOn(DatabaseManager, 'setActiveServerDatabase').mockImplementationOnce(throwFunc); + + const result = await login(serverUrl, {config: {DiagnosticId: 'diagnosticid'}} as LoginArgs); + expect(result).toBeDefined(); + expect(result.error).toBeDefined(); + expect(result.failed).toBe(false); + }); + + it('logout - base case', async () => { + const result = await logout(serverUrl, true, true, true); + expect(result).toBeDefined(); + expect(result.data).toBeDefined(); + }); + it('cancelSessionNotification - handle not found database', async () => { const result = await cancelSessionNotification('foo'); expect(result).toBeDefined(); @@ -192,6 +224,12 @@ describe('sessions', () => { expect(result.error).toBeUndefined(); }); + it('cancelSessionNotification - no expired session', async () => { + const result = await cancelSessionNotification(serverUrl); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + }); + it('scheduleSessionNotification - handle not found database', async () => { const result = await scheduleSessionNotification('foo'); expect(result).toBeDefined(); @@ -216,6 +254,20 @@ describe('sessions', () => { expect(result.error).toBeUndefined(); }); + it('scheduleSessionNotification - no session', async () => { + mockClient.getSessions.mockImplementationOnce(() => []); + const result = await scheduleSessionNotification(serverUrl); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + }); + + it('scheduleSessionNotification - null sessions', async () => { + mockClient.getSessions.mockImplementationOnce(() => null as any); + const result = await scheduleSessionNotification(serverUrl); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + }); + it('sendPasswordResetEmail - handle error', async () => { mockClient.sendPasswordResetEmail.mockImplementationOnce(jest.fn(throwFunc)); const result = await sendPasswordResetEmail('foo', ''); @@ -245,6 +297,15 @@ describe('sessions', () => { expect(result.failed).toBe(false); }); + it('ssoLogin - handle throw after login request', async () => { + jest.spyOn(DatabaseManager, 'setActiveServerDatabase').mockImplementationOnce(throwFunc); + + const result = await ssoLogin(serverUrl, 'servername', 'diagnosticid', 'authtoken', 'csrftoken'); + expect(result).toBeDefined(); + expect(result.error).toBeDefined(); + expect(result.failed).toBe(false); + }); + it('findSession - handle not found database', async () => { const result = await findSession('foo', []); expect(result).toBeUndefined(); @@ -277,8 +338,35 @@ describe('sessions', () => { expect(session).toBeDefined(); }); + it('findSession - non-match device token', async () => { + await DatabaseManager.appDatabase?.operator.handleGlobal({ + globals: [{id: GLOBAL_IDENTIFIERS.DEVICE_TOKEN, value: 'diffdeviceid'}], + prepareRecordsOnly: false, + }); + + const session = await findSession(serverUrl, [session1]); + expect(session).toBeDefined(); + }); + it('findSession - by csrf', async () => { const session = await findSession(serverUrl, [session1]); expect(session).toBeDefined(); }); + + it('findSession - no csrf token', async () => { + mockGetCSRFFromCookie.mockResolvedValueOnce(''); + const session = await findSession(serverUrl, [session1]); + expect(session).toBeUndefined(); + }); + + it('findSession - by os', async () => { + const session = await findSession(serverUrl, [{...session1, props: {os: Platform.OS, csrf: 'diffcsrfid'}}]); + expect(session).toBeDefined(); + }); + + it('findSession - handle error', async () => { + jest.spyOn(DatabaseManager, 'getServerDatabaseAndOperator').mockImplementationOnce(throwFunc); + const result = await findSession(serverUrl, []); + expect(result).toBeUndefined(); + }); }); diff --git a/app/actions/remote/session.ts b/app/actions/remote/session.ts index 60c8845ce..fbe42bfb2 100644 --- a/app/actions/remote/session.ts +++ b/app/actions/remote/session.ts @@ -149,6 +149,8 @@ export const logout = async (serverUrl: string, skipServerLogout = false, remove if (!skipEvents) { DeviceEventEmitter.emit(Events.SERVER_LOGOUT, {serverUrl, removeServer}); } + + return {data: true}; }; export const cancelSessionNotification = async (serverUrl: string) => {