From 1130db29befd81519a3d7314366088fec1ebe09d Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Tue, 29 Oct 2024 17:23:30 -0400 Subject: [PATCH 1/5] Add cud tests to actions/remote/post --- app/actions/remote/post.test.ts | 335 ++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 app/actions/remote/post.test.ts diff --git a/app/actions/remote/post.test.ts b/app/actions/remote/post.test.ts new file mode 100644 index 0000000000..a36983a4aa --- /dev/null +++ b/app/actions/remote/post.test.ts @@ -0,0 +1,335 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +/* eslint-disable max-lines */ + +import {ActionType, Post} from '@app/constants'; +import PostModel from '@app/database/models/server/post'; +import {SYSTEM_IDENTIFIERS} from '@constants/database'; +import DatabaseManager from '@database/manager'; +import NetworkManager from '@managers/network_manager'; +import TestHelper from '@test/test_helper'; + +import { + createPost, + retryFailedPost, + togglePinPost, + deletePost, + markPostAsUnread, + editPost, + acknowledgePost, + unacknowledgePost, +} from './post'; + +import type ServerDataOperator from '@database/operator/server_data_operator'; + +const serverUrl = 'baseHandler.test.com'; +let operator: ServerDataOperator; + +const channelId = 'channelid1'; +const teamId = 'teamid1'; + +const team: Team = { + id: teamId, + name: 'team1', +} as Team; + +const user1 = {id: 'userid1', username: 'user1', email: 'user1@mattermost.com', roles: ''} as UserProfile; + +const post1 = {...TestHelper.fakePost(channelId), id: 'postid1'}; +const reply1 = {...TestHelper.fakePost(channelId), id: 'replyid1', root_id: post1.id}; + +const fileInfo1: FileInfo = { + id: 'fileid', + clientId: 'clientid', + localPath: 'path1', +} as FileInfo; + +const thread1: Thread = {id: 'postid1', + reply_count: 1, + last_reply_at: 1, + last_viewed_at: 1, + participants: [user1], + post: {id: 'postid1', + channel_id: channelId, + create_at: 1, + message: 'post1', + user_id: user1.id} as Post, + unread_replies: 0, + unread_mentions: 0, + delete_at: 0, +}; + +const mockPostModel = (overrides: Partial = {}): PostModel => ({ + id: 'post-id', + channelId: 'channel-id', + createAt: Date.now(), + deleteAt: 0, + type: 'custom_post_type', + userId: 'user-id', + ...overrides, +} as PostModel); + +const threads = [ + thread1, +] as ThreadWithLastFetchedAt[]; + +const throwFunc = () => { + throw Error('error'); +}; + +const acknowledgedTime = Date.now(); + +const mockClient = { + createPost: jest.fn((post: Post) => ({...post, id: 'newid'})), + pinPost: jest.fn(), + unpinPost: jest.fn(), + deletePost: jest.fn(), + getChannel: jest.fn((_channelId: string) => ({id: _channelId, name: 'channel1', creatorId: user1.id, total_msg_count: 100})), + getChannelMember: jest.fn((_channelId: string, userId: string) => ({id: userId + '-' + _channelId, user_id: userId, channel_id: _channelId, roles: '', msg_count: 100, mention_count: 0})), + markPostAsUnread: jest.fn(), + patchPost: jest.fn((message: string, postId: string) => ({...post1, id: postId, message})), + acknowledgePost: jest.fn(() => ({acknowledged_at: acknowledgedTime})), + unacknowledgePost: jest.fn(), +}; + +let mockGetIsCRTEnabled: jest.Mock; +jest.mock('@queries/servers/thread', () => { + const original = jest.requireActual('@queries/servers/thread'); + mockGetIsCRTEnabled = jest.fn(() => true); + return { + ...original, + getIsCRTEnabled: mockGetIsCRTEnabled, + }; +}); + +beforeAll(() => { + // eslint-disable-next-line + // @ts-ignore + NetworkManager.getClient = () => mockClient; +}); + +beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; +}); + +afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); +}); + +describe('create, update & delete posts', () => { + it('createPost - handle database not found', async () => { + const result = await createPost('foo', post1); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + 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}); + + 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}); + + 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}); + + const result = await createPost(serverUrl, reply1, [fileInfo1]); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.data).toBeTruthy(); + }); + + it('retryFailedPost - handle database not found', async () => { + const result = await retryFailedPost('foo', mockPostModel()); + 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}); + + const result = await retryFailedPost(serverUrl, mockPostModel({id: post1.id, prepareUpdate: jest.fn(), toApi: async () => post1})); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + }); + + it('retryFailedPost - handle error', async () => { + mockClient.createPost.mockImplementationOnce(jest.fn(throwFunc)); + const result = await retryFailedPost(serverUrl, mockPostModel({id: post1.id, prepareUpdate: jest.fn(), toApi: async () => post1})); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('togglePinPost - handle database not found', async () => { + const result = await togglePinPost('foo', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('togglePinPost - base case', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [post1], + prepareRecordsOnly: false, + }); + + const result = await togglePinPost(serverUrl, post1.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.post).toBeDefined(); + expect(result.post?.isPinned).toBe(true); + }); + + it('deletePost - handle error', async () => { + mockClient.deletePost.mockImplementationOnce(jest.fn(throwFunc)); + const result = await deletePost('foo', {} as PostModel); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('deletePost - base case', async () => { + const postModels = await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [post1], + prepareRecordsOnly: false, + }); + + const result = await deletePost(serverUrl, postModels[0] as PostModel); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.post).toBeDefined(); + }); + + it('deletePost - system post', async () => { + const postModels = await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [{...post1, id: `user-activity-${post1.id}`, type: Post.POST_TYPES.COMBINED_USER_ACTIVITY as PostType, props: {system_post_ids: [post1.id]}}], + prepareRecordsOnly: false, + }); + + const result = await deletePost(serverUrl, postModels[0] as PostModel); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.post).toBeDefined(); + }); + + it('markPostAsUnread - handle error', async () => { + mockClient.deletePost.mockImplementationOnce(jest.fn(throwFunc)); + const result = await markPostAsUnread('foo', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('markPostAsUnread - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [post1], + prepareRecordsOnly: false, + }); + + const result = await markPostAsUnread(serverUrl, post1.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.post).toBeDefined(); + }); + + it('markPostAsUnread - no current user', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [post1], + prepareRecordsOnly: false, + }); + + const result = await markPostAsUnread(serverUrl, post1.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.post).toBeDefined(); + }); + + it('editPost - handle error', async () => { + mockClient.deletePost.mockImplementationOnce(jest.fn(throwFunc)); + const result = await editPost('foo', '', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('editPost - base case', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [post1], + prepareRecordsOnly: false, + }); + + const result = await editPost(serverUrl, post1.id, 'new message'); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.post).toBeDefined(); + }); + + it('acknowledgePost - handle error', async () => { + mockClient.deletePost.mockImplementationOnce(jest.fn(throwFunc)); + const result = await acknowledgePost('foo', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('acknowledgePost - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [post1], + prepareRecordsOnly: false, + }); + + const result = await acknowledgePost(serverUrl, post1.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.model).toBeDefined(); + expect(result.model?.metadata?.acknowledgements?.[0].acknowledged_at).toBe(acknowledgedTime); + }); + + it('unacknowledgePost - handle error', async () => { + mockClient.deletePost.mockImplementationOnce(jest.fn(throwFunc)); + const result = await unacknowledgePost('foo', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('unacknowledgePost - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post1.id], + posts: [post1], + prepareRecordsOnly: false, + }); + + const result = await unacknowledgePost(serverUrl, post1.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.model).toBeDefined(); + expect(result.model?.metadata?.acknowledgements?.length).toBe(0); + }); +}); From a67f6a7e6a9366b29b1737518b2c1d867535d032 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Wed, 30 Oct 2024 13:42:37 -0400 Subject: [PATCH 2/5] Remove unused fetchPostsForCurrentChannel --- app/actions/remote/post.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/app/actions/remote/post.ts b/app/actions/remote/post.ts index 342478896d..23da027e14 100644 --- a/app/actions/remote/post.ts +++ b/app/actions/remote/post.ts @@ -16,7 +16,7 @@ import NetworkManager from '@managers/network_manager'; import {getMyChannel, prepareMissingChannelsForAllTeams, queryAllMyChannel} from '@queries/servers/channel'; import {queryAllCustomEmojis} from '@queries/servers/custom_emoji'; import {getPostById, getRecentPostsInChannel} from '@queries/servers/post'; -import {getCurrentUserId, getCurrentChannelId} from '@queries/servers/system'; +import {getCurrentUserId} from '@queries/servers/system'; import {getIsCRTEnabled, prepareThreadsFromReceivedPosts} from '@queries/servers/thread'; import {queryAllUsers} from '@queries/servers/user'; import EphemeralStore from '@store/ephemeral_store'; @@ -275,16 +275,6 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => { return {}; }; -export const fetchPostsForCurrentChannel = async (serverUrl: string) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - const currentChannelId = await getCurrentChannelId(database); - return fetchPostsForChannel(serverUrl, currentChannelId); -}; - export async function fetchPostsForChannel(serverUrl: string, channelId: string, fetchOnly = false) { try { if (!fetchOnly) { From 724205a31873ec0b142f5f9dbb071465af458326 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Wed, 30 Oct 2024 15:23:54 -0400 Subject: [PATCH 3/5] Add fetch tests to actions/remote/post --- app/actions/remote/post.test.ts | 300 +++++++++++++++++++++++++++++--- app/actions/remote/post.ts | 3 +- 2 files changed, 276 insertions(+), 27 deletions(-) diff --git a/app/actions/remote/post.test.ts b/app/actions/remote/post.test.ts index a36983a4aa..e87b31756c 100644 --- a/app/actions/remote/post.test.ts +++ b/app/actions/remote/post.test.ts @@ -19,6 +19,18 @@ import { editPost, acknowledgePost, unacknowledgePost, + fetchPostsForChannel, + fetchPostsForUnreadChannels, + fetchPosts, + fetchPostsBefore, + fetchPostsSince, + fetchPostAuthors, + fetchPostThread, + fetchPostsAround, + fetchMissingChannelsFromPosts, + fetchPostById, + fetchSavedPosts, + fetchPinnedPosts, } from './post'; import type ServerDataOperator from '@database/operator/server_data_operator'; @@ -29,15 +41,27 @@ let operator: ServerDataOperator; const channelId = 'channelid1'; const teamId = 'teamid1'; -const team: Team = { - id: teamId, - name: 'team1', -} as Team; - const user1 = {id: 'userid1', username: 'user1', email: 'user1@mattermost.com', roles: ''} as UserProfile; +const user2 = {id: 'userid2', username: 'user2', email: 'user2@mattermost.com', roles: ''} as UserProfile; + +const post1 = {...TestHelper.fakePost(channelId), id: 'postid1', user_id: user1.id}; +const post2 = {...TestHelper.fakePost(channelId), id: 'postid2', user_id: user2.id}; +const reply1 = {...TestHelper.fakePost(channelId), id: 'replyid1', root_id: post1.id, user_id: user2.id}; -const post1 = {...TestHelper.fakePost(channelId), id: 'postid1'}; -const reply1 = {...TestHelper.fakePost(channelId), id: 'replyid1', root_id: post1.id}; +const channel1 = { + id: channelId, + team_id: teamId, + total_msg_count: 12, + creator_id: user1.id, + delete_at: 0, +} as Channel; + +const channelMember1 = { + id: 'memberid1', + channel_id: channelId, + user_id: user1.id, + msg_count: 10, +} as ChannelMembership; const fileInfo1: FileInfo = { id: 'fileid', @@ -45,21 +69,6 @@ const fileInfo1: FileInfo = { localPath: 'path1', } as FileInfo; -const thread1: Thread = {id: 'postid1', - reply_count: 1, - last_reply_at: 1, - last_viewed_at: 1, - participants: [user1], - post: {id: 'postid1', - channel_id: channelId, - create_at: 1, - message: 'post1', - user_id: user1.id} as Post, - unread_replies: 0, - unread_mentions: 0, - delete_at: 0, -}; - const mockPostModel = (overrides: Partial = {}): PostModel => ({ id: 'post-id', channelId: 'channel-id', @@ -70,16 +79,14 @@ const mockPostModel = (overrides: Partial = {}): PostModel => ({ ...overrides, } as PostModel); -const threads = [ - thread1, -] as ThreadWithLastFetchedAt[]; - const throwFunc = () => { throw Error('error'); }; const acknowledgedTime = Date.now(); +const genericGetPostsMock = jest.fn((_channelId: string) => ({posts: {[post1.id]: {...post1, channel_id: _channelId}, [post2.id]: {...post2, channel_id: _channelId}}, order: [post1.id, post2.id]})); + const mockClient = { createPost: jest.fn((post: Post) => ({...post, id: 'newid'})), pinPost: jest.fn(), @@ -87,10 +94,21 @@ const mockClient = { deletePost: jest.fn(), getChannel: jest.fn((_channelId: string) => ({id: _channelId, name: 'channel1', creatorId: user1.id, total_msg_count: 100})), getChannelMember: jest.fn((_channelId: string, userId: string) => ({id: userId + '-' + _channelId, user_id: userId, channel_id: _channelId, roles: '', msg_count: 100, mention_count: 0})), + getMyChannelMember: jest.fn((_channelId: string) => ({id: user1.id + '-' + _channelId, user_id: user1.id, channel_id: _channelId, roles: '', msg_count: 100, mention_count: 0})), markPostAsUnread: jest.fn(), patchPost: jest.fn((message: string, postId: string) => ({...post1, id: postId, message})), acknowledgePost: jest.fn(() => ({acknowledged_at: acknowledgedTime})), unacknowledgePost: jest.fn(), + getPosts: genericGetPostsMock, + getPostsBefore: genericGetPostsMock, + getPostsSince: jest.fn((_channelId: string, since: number) => ({posts: {[post1.id]: {...post1, channel_id: _channelId, create_at: since + 1}, [post2.id]: {...post2, channel_id: _channelId, create_at: since + 2}}, order: [post1.id, post2.id]})), + getProfilesByIds: jest.fn((ids: string[]) => (ids.map((id) => ({...user1, id})))), + getProfilesByUsernames: jest.fn((names: string[]) => (names.map((name) => ({...user1, username: name, id: 'id' + name})))), + getPostThread: jest.fn((_postId: string) => ({posts: {[_postId]: {...post1, id: _postId}, [reply1.id]: {...reply1, root_id: _postId}}, order: [_postId, reply1.id]})), + getPostsAfter: genericGetPostsMock, + getPost: jest.fn((_postId: string) => ({...post2, id: _postId})), + getSavedPosts: genericGetPostsMock, + getPinnedPosts: genericGetPostsMock, }; let mockGetIsCRTEnabled: jest.Mock; @@ -333,3 +351,233 @@ describe('create, update & delete posts', () => { expect(result.model?.metadata?.acknowledgements?.length).toBe(0); }); }); + +describe('get posts', () => { + it('fetchPostsForChannel - handle database not found', async () => { + const result = await fetchPostsForChannel('foo', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchPostsForChannel - base case', 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}); + + const result = await fetchPostsForChannel(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); + + it('fetchPostsForChannel - request error', async () => { + mockClient.getPosts.mockImplementationOnce(jest.fn(throwFunc)); + + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel1], myChannels: [channelMember1], prepareRecordsOnly: false}); + + const result = await fetchPostsForChannel(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result.error).toBeDefined(); + }); + + it('fetchPostsForUnreadChannels - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel1], myChannels: [channelMember1], prepareRecordsOnly: false}); + + const result = await fetchPostsForUnreadChannels(serverUrl, [channel1, {...channel1, id: 'channelid2', total_msg_count: 10}], [{...channelMember1, msg_count: 5}, {...channelMember1, channel_id: 'channelid2', msg_count: 10}], 'testid'); + expect(result).toBeDefined(); + expect(result.data).toBeDefined(); + expect(result.data?.length).toBe(1); // Only returns the response for the channel with unread messages + expect(result.data?.[0].posts?.[0].channel_id).toBe(channel1.id); + }); + + it('fetchPosts - handle database not found', async () => { + const result = await fetchPosts('foo', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchPosts - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: 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('fetchPostsBefore - handle database not found', async () => { + const result = await fetchPostsBefore('foo', '', '') as {error: unknown}; + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchPostsBefore - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: 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('fetchPostsSince - handle database not found', async () => { + const result = await fetchPostsSince('foo', '', 0); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchPostsSince - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await fetchPostsSince(serverUrl, channelId, 123); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); + + it('fetchPostAuthors - handle database not found', async () => { + const result = await fetchPostAuthors('foo', []); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchPostAuthors - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user1], prepareRecordsOnly: false}); + + const result = await fetchPostAuthors(serverUrl, [{...post1, message: 'hi @user3'}, post2]); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.authors).toBeDefined(); + expect(result.authors?.length).toBe(2); // 1 by id for user2, 1 by username for user3 + }); + + it('fetchPostAuthors - no users to fetch', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user1], prepareRecordsOnly: false}); + + const result = await fetchPostAuthors(serverUrl, [post1]); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.authors).toBeDefined(); + expect(result.authors?.length).toBe(0); + }); + + it('fetchPostThread - handle database not found', async () => { + const result = await fetchPostThread('foo', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchPostThread - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: 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('fetchPostsAround - handle database not found', async () => { + const result = await fetchPostsAround('foo', '', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchPostsAround - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: 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('fetchMissingChannelsFromPosts - handle database not found', async () => { + const result = await fetchMissingChannelsFromPosts('foo', []); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchMissingChannelsFromPosts - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await fetchMissingChannelsFromPosts(serverUrl, [post1]); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.channels).toBeDefined(); + expect(result.channels?.[0].id).toBe(post1.channel_id); + }); + + it('fetchPostById - handle database not found', async () => { + const result = await fetchPostById('foo', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchPostById - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: 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('fetchSavedPosts - handle database not found', async () => { + const result = await fetchSavedPosts('foo'); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchSavedPosts - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await fetchSavedPosts(serverUrl); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); + + it('fetchPinnedPosts - handle database not found', async () => { + const result = await fetchPinnedPosts('foo', ''); + expect(result).toBeDefined(); + expect(result.error).toBeTruthy(); + }); + + it('fetchPinnedPosts - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user1.id}], prepareRecordsOnly: false}); + + const result = await fetchPinnedPosts(serverUrl, channel1.id); + expect(result).toBeDefined(); + expect(result.error).toBeUndefined(); + expect(result.posts).toBeTruthy(); + expect(result.posts?.length).toBe(2); + }); +}); diff --git a/app/actions/remote/post.ts b/app/actions/remote/post.ts index 23da027e14..c9e44d069f 100644 --- a/app/actions/remote/post.ts +++ b/app/actions/remote/post.ts @@ -331,7 +331,8 @@ export const fetchPostsForUnreadChannels = async (serverUrl: string, channels: C promises.push(fetchPostsForChannel(serverUrl, channel.id)); } } - await Promise.all(promises); + const responses = await Promise.all(promises); + return {data: responses}; }; export async function fetchPosts(serverUrl: string, channelId: string, page = 0, perPage = General.POST_CHUNK_SIZE, fetchOnly = false): Promise { From ef05d47f4033dd1019a0322ddbe0837e8fb2e193 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 1 Nov 2024 14:07:44 +0800 Subject: [PATCH 4/5] Update app/actions/remote/post.ts --- app/actions/remote/post.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/actions/remote/post.ts b/app/actions/remote/post.ts index c9e44d069f..5ff76826f6 100644 --- a/app/actions/remote/post.ts +++ b/app/actions/remote/post.ts @@ -331,8 +331,7 @@ export const fetchPostsForUnreadChannels = async (serverUrl: string, channels: C promises.push(fetchPostsForChannel(serverUrl, channel.id)); } } - const responses = await Promise.all(promises); - return {data: responses}; + return Promise.all(promises); }; export async function fetchPosts(serverUrl: string, channelId: string, page = 0, perPage = General.POST_CHUNK_SIZE, fetchOnly = false): Promise { From e5e7aab2d9c8f8933de09195c5e9f4d9b5c40245 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 1 Nov 2024 14:21:43 +0800 Subject: [PATCH 5/5] fix tests --- app/actions/remote/post.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/actions/remote/post.test.ts b/app/actions/remote/post.test.ts index e87b31756c..f6a087b8c8 100644 --- a/app/actions/remote/post.test.ts +++ b/app/actions/remote/post.test.ts @@ -399,9 +399,8 @@ describe('get posts', () => { const result = await fetchPostsForUnreadChannels(serverUrl, [channel1, {...channel1, id: 'channelid2', total_msg_count: 10}], [{...channelMember1, msg_count: 5}, {...channelMember1, channel_id: 'channelid2', msg_count: 10}], 'testid'); expect(result).toBeDefined(); - expect(result.data).toBeDefined(); - expect(result.data?.length).toBe(1); // Only returns the response for the channel with unread messages - expect(result.data?.[0].posts?.[0].channel_id).toBe(channel1.id); + expect(result?.length).toBe(1); // Only returns the response for the channel with unread messages + expect(result?.[0].posts?.[0].channel_id).toBe(channel1.id); }); it('fetchPosts - handle database not found', async () => {