Increase branch test coverage to over 80% for actions/local (#8366)

This commit is contained in:
Joram Wilander 2024-11-21 08:16:19 -05:00 committed by GitHub
parent d259fb00b4
commit f803edfbf4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 263 additions and 28 deletions

View file

@ -14,6 +14,7 @@ import {
} from './category';
import type ServerDataOperator from '@database/operator/server_data_operator';
import type ChannelModel from '@typings/database/models/servers/channel';
describe('handleConvertedGMCategories', () => {
const serverUrl = 'baseHandler.test.com';
@ -66,7 +67,7 @@ describe('handleConvertedGMCategories', () => {
};
await operator.handleCategoryChannels({categoryChannels: [dmCategoryChannel, customCategoryChannel], prepareRecordsOnly: false});
const {models, error} = await handleConvertedGMCategories(serverUrl, channelId, teamId1, true);
const {models, error} = await handleConvertedGMCategories(serverUrl, channelId, teamId1);
expect(error).toBeUndefined();
expect(models).toBeDefined();
expect(models!.length).toBe(3); // two for removing channel for a custom and a DM category, and one for adding it to default channels category
@ -83,7 +84,7 @@ describe('handleConvertedGMCategories', () => {
});
it('error - database not prepared', async () => {
const {error} = await handleConvertedGMCategories(serverUrl, channelId, teamId1, true);
const {error} = await handleConvertedGMCategories('', channelId, teamId1, true);
expect(error).toBeDefined();
});
});
@ -124,6 +125,11 @@ describe('category crud', () => {
expect(error2).toBeUndefined();
});
it('deleteCategory - handle database not found', async () => {
const {error} = await deleteCategory('', '');
expect(error).toBeDefined();
});
it('storeCategories', async () => {
const defaultCategory: CategoryWithChannels = {
id: 'default_category_id',
@ -143,6 +149,11 @@ describe('category crud', () => {
expect(models!.length).toBe(3);
});
it('storeCategories - handle database not found', async () => {
const {error} = await storeCategories('', []);
expect(error).toBeDefined();
});
it('toggleCollapseCategory', async () => {
const defaultCategory: Category = {
id: 'default_category_id',
@ -163,6 +174,11 @@ describe('category crud', () => {
expect(categoryResult2).toBeDefined();
expect(categoryResult2?.collapsed).toBe(defaultCategory.collapsed);
});
it('toggleCollapseCategory - handle database not found', async () => {
const {error} = await toggleCollapseCategory('', '');
expect(error).toBeDefined();
});
});
describe('addChannelToDefaultCategory', () => {
@ -221,6 +237,11 @@ describe('addChannelToDefaultCategory', () => {
expect(dmError).toBeUndefined();
expect(dmModels).toBeDefined();
expect(dmModels!.length).toBe(1); // one for the dm channel
const {models: channelModels, error: channelModelError} = await addChannelToDefaultCategory(serverUrl, {teamId: teamId1, id: channelId} as ChannelModel);
expect(channelModelError).toBeUndefined();
expect(channelModels).toBeDefined();
expect(channelModels!.length).toBe(1); // one for the channel
});
it('error - no current user', async () => {
@ -233,7 +254,7 @@ describe('addChannelToDefaultCategory', () => {
});
it('error - database not prepared', async () => {
const {error} = await addChannelToDefaultCategory(serverUrl, channel);
const {error} = await addChannelToDefaultCategory('', channel);
expect(error).toBeDefined();
});
});

View file

@ -70,7 +70,7 @@ describe('updateDraftFile', () => {
it('update draft file', async () => {
await operator.handleDraft({drafts: [{...draft, files: [{...fileInfo, localPath: 'path0'}]}], prepareRecordsOnly: false});
const {draft: draftModel, error} = await updateDraftFile(serverUrl, channelId, '', fileInfo, false);
const {draft: draftModel, error} = await updateDraftFile(serverUrl, channelId, '', fileInfo);
expect(error).toBeUndefined();
expect(draftModel).toBeDefined();
expect(draftModel?.files?.length).toBe(1);
@ -101,7 +101,7 @@ describe('removeDraftFile', () => {
it('remove draft file', async () => {
await operator.handleDraft({drafts: [{...draft, files: [fileInfo]}], prepareRecordsOnly: false});
const {draft: draftModel, error} = await removeDraftFile(serverUrl, channelId, '', 'clientid', false);
const {draft: draftModel, error} = await removeDraftFile(serverUrl, channelId, '', 'clientid');
expect(error).toBeUndefined();
expect(draftModel).toBeDefined();
});
@ -137,7 +137,7 @@ describe('updateDraftMessage', () => {
it('update draft message', async () => {
await operator.handleDraft({drafts: [{...draft, files: [fileInfo]}], prepareRecordsOnly: false});
const result = await updateDraftMessage(serverUrl, channelId, '', 'newmessage', false) as {draft: DraftModel; error: unknown};
const result = await updateDraftMessage(serverUrl, channelId, '', 'newmessage') as {draft: DraftModel; error: unknown};
expect(result.error).toBeUndefined();
expect(result.draft).toBeDefined();
expect(result.draft.message).toBe('newmessage');
@ -178,7 +178,7 @@ describe('addFilesToDraft', () => {
it('add draft files', async () => {
await operator.handleDraft({drafts: [draft], prepareRecordsOnly: false});
const result = await addFilesToDraft(serverUrl, channelId, '', [fileInfo], false) as {draft: DraftModel; error: unknown};
const result = await addFilesToDraft(serverUrl, channelId, '', [fileInfo]) as {draft: DraftModel; error: unknown};
expect(result.error).toBeUndefined();
expect(result.draft).toBeDefined();
expect(result?.draft.files.length).toBe(1);
@ -201,7 +201,15 @@ describe('removeDraft', () => {
it('remove draft', async () => {
await operator.handleDraft({drafts: [draft], prepareRecordsOnly: false});
const result = await removeDraft(serverUrl, channelId, '');
const result = await removeDraft(serverUrl, channelId);
expect(result.error).toBeUndefined();
expect(result.draft).toBeDefined();
});
it('remove draft with root id', async () => {
await operator.handleDraft({drafts: [{...draft, root_id: 'postid'}], prepareRecordsOnly: false});
const result = await removeDraft(serverUrl, channelId, 'postid');
expect(result.error).toBeUndefined();
expect(result.draft).toBeDefined();
});

View file

@ -6,12 +6,13 @@ import DatabaseManager from '@database/manager';
import {
updateLocalFile,
updateLocalFilePath,
getLocalFileInfo,
} from './file';
import type ServerDataOperator from '@database/operator/server_data_operator';
import type FileModel from '@typings/database/models/servers/file';
describe('updateLocalFiles', () => {
describe('files', () => {
let operator: ServerDataOperator;
const serverUrl = 'baseHandler.test.com';
const fileInfo: FileInfo = {
@ -52,4 +53,23 @@ describe('updateLocalFiles', () => {
const {error} = await updateLocalFilePath(serverUrl, fileInfo.id as string, 'newpath');
expect(error).toBeUndefined();
});
it('updateLocalFilePath - no file', async () => {
const {error} = await updateLocalFilePath(serverUrl, fileInfo.id as string, 'newpath');
expect(error).toBeUndefined();
});
it('getLocalFileInfo - handle not found database', async () => {
const {error} = await getLocalFileInfo('foo', '');
expect(error).toBeTruthy();
});
it('getLocalFileInfo', async () => {
await operator.handleFiles({files: [fileInfo], prepareRecordsOnly: false});
const {file, error} = await getLocalFileInfo(serverUrl, fileInfo.id as string);
expect(error).toBeUndefined();
expect(file).toBeDefined();
expect(file!.id).toBe(fileInfo.id);
});
});

View file

@ -66,6 +66,16 @@ describe('searchGroups', () => {
expect(models[0].id).toBe(group.id);
});
it('searchGroupsByName', async () => {
const groupModels = await operator.handleGroups({groups: [group], prepareRecordsOnly: false});
mockedRemoteGroups.fetchGroupsForAutocomplete.mockReturnValueOnce(Promise.resolve(groupModels));
const models = await searchGroupsByName(serverUrl, group.name);
expect(models).toBeDefined();
expect(models.length).toBe(1);
expect(models[0].id).toBe(group.id);
});
it('searchGroupsByNameInTeam - handle not found database', async () => {
const models = await searchGroupsByNameInTeam('foo', 'test', teamId);
expect(models).toBeDefined();
@ -90,6 +100,18 @@ describe('searchGroups', () => {
expect(models[0].id).toBe(group.id);
});
it('searchGroupsByNameInTeam', async () => {
const groupModels = await operator.handleGroups({groups: [group], prepareRecordsOnly: false});
await operator.handleGroupTeamsForTeam({groups: [group], teamId, prepareRecordsOnly: false});
mockedRemoteGroups.fetchFilteredTeamGroups.mockReturnValueOnce(Promise.resolve(groupModels));
const models = await searchGroupsByNameInTeam(serverUrl, group.name, teamId);
expect(models).toBeDefined();
expect(models.length).toBe(1);
expect(models[0].id).toBe(group.id);
});
it('searchGroupsByNameInChannel - handle not found database', async () => {
const models = await searchGroupsByNameInChannel('foo', 'test', channelId);
expect(models).toBeDefined();
@ -113,4 +135,16 @@ describe('searchGroups', () => {
expect(models.length).toBe(1);
expect(models[0].id).toBe(group.id);
});
it('searchGroupsByNameInChannel', async () => {
const groupModels = await operator.handleGroups({groups: [group], prepareRecordsOnly: false});
await operator.handleGroupChannelsForChannel({groups: [group], channelId, prepareRecordsOnly: false});
mockedRemoteGroups.fetchFilteredChannelGroups.mockReturnValueOnce(Promise.resolve(groupModels));
const models = await searchGroupsByNameInChannel(serverUrl, group.name, channelId);
expect(models).toBeDefined();
expect(models.length).toBe(1);
expect(models[0].id).toBe(group.id);
});
});

View file

@ -36,6 +36,16 @@ jest.mock('@utils/general', () => {
};
});
let mockGetIsCRTEnabled: jest.Mock;
jest.mock('@queries/servers/thread', () => {
const original = jest.requireActual('@queries/servers/thread');
mockGetIsCRTEnabled = jest.fn(() => true);
return {
...original,
getIsCRTEnabled: mockGetIsCRTEnabled,
};
});
const channelId = 'channelid1';
const user: UserProfile = {
id: 'userid',
@ -62,7 +72,7 @@ describe('sendAddToChannelEphemeralPost', () => {
it('base case', async () => {
const users = await operator.handleUsers({users: [user], prepareRecordsOnly: false});
const {posts, error} = await sendAddToChannelEphemeralPost(serverUrl, users[0], ['username2'], ['added username2'], channelId, '');
const {posts, error} = await sendAddToChannelEphemeralPost(serverUrl, users[0], ['username2'], ['added username2'], channelId);
expect(error).toBeUndefined();
expect(posts).toBeDefined();
expect(posts?.length).toBe(1);
@ -86,7 +96,7 @@ describe('sendEphemeralPost', () => {
it('handle no user', async () => {
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: 'useridcurrent'}], prepareRecordsOnly: false});
const {post, error} = await sendEphemeralPost(serverUrl, 'newmessage', channelId, '');
const {post, error} = await sendEphemeralPost(serverUrl, 'newmessage', channelId);
expect(error).toBeUndefined();
expect(post).toBeDefined();
expect(post?.user_id).toBe('useridcurrent');
@ -122,6 +132,12 @@ describe('removePost', () => {
expect(rPost).toBeDefined();
});
it('base case - missing post', async () => {
const {post: rPost, error} = await removePost(serverUrl, post);
expect(error).toBeUndefined();
expect(rPost).toBeDefined();
});
it('base case - system message', async () => {
const systemPost = {...TestHelper.fakePost(channelId), id: `${COMBINED_USER_ACTIVITY}id1_id2`, type: Post.POST_TYPES.COMBINED_USER_ACTIVITY as PostType, props: {system_post_ids: ['id1']}};
@ -189,22 +205,24 @@ describe('storePostsForChannel', () => {
expect(error).toBeTruthy();
});
it('base case', async () => {
it('base case - CRT on', async () => {
await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false});
await operator.handleConfigs({
configs: [
{id: 'CollapsedThreads', value: 'default_on'},
{id: 'FeatureFlagCollapsedThreads', value: 'true'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const {models, error} = await storePostsForChannel(serverUrl, channelId, [post], [post.id], '', ActionType.POSTS.RECEIVED_IN_CHANNEL, [user], false);
expect(error).toBeUndefined();
expect(models).toBeDefined();
expect(models?.length).toBe(5); // Post, PostsInChannel, User, MyChannel, Thread
});
it('base case - CRT off', async () => {
await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false});
mockGetIsCRTEnabled.mockImplementationOnce(() => false);
const {models, error} = await storePostsForChannel(serverUrl, channelId, [post], [post.id], '', ActionType.POSTS.RECEIVED_IN_CHANNEL, [user]);
expect(error).toBeUndefined();
expect(models).toBeDefined();
expect(models?.length).toBe(4); // Post, PostsInChannel, User, MyChannel
});
});
describe('getPosts', () => {
@ -267,7 +285,7 @@ describe('addPostAcknowledgement', () => {
prepareRecordsOnly: false,
});
const {model, error} = await addPostAcknowledgement(serverUrl, post.id, user.id, 123, false);
const {model, error} = await addPostAcknowledgement(serverUrl, post.id, user.id, 123);
expect(error).toBeUndefined();
expect(model).toBeDefined();
});
@ -296,7 +314,7 @@ describe('removePostAcknowledgement', () => {
prepareRecordsOnly: false,
});
const {model, error} = await removePostAcknowledgement(serverUrl, post.id, user.id, false);
const {model, error} = await removePostAcknowledgement(serverUrl, post.id, user.id);
expect(error).toBeUndefined();
expect(model).toBeDefined();
});

View file

@ -50,6 +50,41 @@ describe('storeConfigAndLicense', () => {
expect(models.length).toBe(0);
});
it('handle undefined config - storeConfig', async () => {
const models = await storeConfig(serverUrl, undefined);
expect(models).toBeDefined();
expect(models.length).toBe(0);
});
it('base case - storeConfig', async () => {
await operator.handleConfigs({
configs: [
{id: 'DataRetentionEnableMessageDeletion', value: 'true'},
{id: 'AboutLink', value: 'link'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const models = await storeConfig(serverUrl, {AboutLink: 'link'} as ClientConfig);
expect(models).toBeDefined();
expect(models.length).toBe(1); // data retention removed
});
it('nothing to update - storeConfig', async () => {
await operator.handleConfigs({
configs: [
{id: 'AboutLink', value: 'link'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const models = await storeConfig(serverUrl, {AboutLink: 'link'} as ClientConfig);
expect(models).toBeDefined();
expect(models.length).toBe(0);
});
it('handle not found database', async () => {
const models = await storeConfigAndLicense('foo', {} as ClientConfig, {} as ClientLicense);
expect(models).toBeDefined();
@ -76,6 +111,12 @@ describe('dataRetention', () => {
expect(models.length).toBe(2); // data retention and granular data retention policies
});
it('empty case - storeDataRetentionPolicies', async () => {
const models = await storeDataRetentionPolicies(serverUrl, {} as DataRetentionPoliciesRequest);
expect(models).toBeDefined();
expect(models.length).toBe(2); // data retention and granular data retention policies
});
it('handle not found database - updateLastDataRetentionRun', async () => {
const {error} = await updateLastDataRetentionRun('foo', 0) as {error: unknown};
expect(error).toBeDefined();
@ -87,6 +128,12 @@ describe('dataRetention', () => {
expect(models.length).toBe(1); // data retention
});
it('no time provided - updateLastDataRetentionRun', async () => {
const models = await updateLastDataRetentionRun(serverUrl) as SystemModel[];
expect(models).toBeDefined();
expect(models.length).toBe(1); // data retention
});
it('handle not found database - dataRetentionCleanup', async () => {
const {error} = await dataRetentionCleanup('foo');
expect(error).toBeDefined();
@ -134,6 +181,33 @@ describe('dataRetention', () => {
expect(error).toBeDefined(); // LokiJSAdapter doesn't support unsafeSqlQuery
spy.mockRestore();
});
it('already cleaned today - dataRetentionCleanup', async () => {
const channel: Channel = {
id: 'channelid1',
team_id: 'teamid1',
total_msg_count: 0,
} as Channel;
await operator.handleConfigs({
configs: [
{id: 'DataRetentionEnableMessageDeletion', value: 'true'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
await operator.handleSystem({systems:
[
{id: SYSTEM_IDENTIFIERS.LAST_DATA_RETENTION_RUN, value: Date.now()},
{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true', DataRetention: 'true'}},
{id: SYSTEM_IDENTIFIERS.GRANULAR_DATA_RETENTION_POLICIES, value: {team: [{team_id: 'teamid1', post_duration: 100}], channel: [{channel_id: 'channelid1', post_duration: 100}]}},
],
prepareRecordsOnly: false});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
const {error} = await dataRetentionCleanup(serverUrl);
expect(error).toBeUndefined();
});
});
describe('setLastServerVersionCheck', () => {
@ -146,6 +220,11 @@ describe('setLastServerVersionCheck', () => {
const {error} = await setLastServerVersionCheck(serverUrl);
expect(error).toBeUndefined();
});
it('base case - reset', async () => {
const {error} = await setLastServerVersionCheck(serverUrl, true);
expect(error).toBeUndefined();
});
});
describe('setGlobalThreadsTab', () => {

View file

@ -38,6 +38,7 @@ jest.mock('@store/navigation_store', () => {
return {
...original,
waitUntilScreenIsTop: jest.fn(() => Promise.resolve()),
getScreensInStack: jest.fn(() => []),
};
});
@ -99,7 +100,25 @@ describe('switchToGlobalThreads', () => {
await operator.handleTeam({teams: [team], prepareRecordsOnly: false});
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false});
const {models, error} = await switchToGlobalThreads(serverUrl, undefined, false);
const {models, error} = await switchToGlobalThreads(serverUrl, undefined);
expect(error).toBeUndefined();
expect(models).toBeDefined();
expect(models?.length).toBe(1); // history
});
it('base case - provided team id', async () => {
let mockIsTablet: jest.Mock;
jest.mock('@utils/helpers', () => {
const original = jest.requireActual('@utils/helpers');
mockIsTablet = jest.fn(() => true);
return {
...original,
isTablet: mockIsTablet,
};
});
await operator.handleTeam({teams: [team], prepareRecordsOnly: false});
const {models, error} = await switchToGlobalThreads(serverUrl, team.id);
expect(error).toBeUndefined();
expect(models).toBeDefined();
expect(models?.length).toBe(1); // history
@ -121,7 +140,7 @@ describe('switchToThread', () => {
await operator.handleUsers({users: [user], prepareRecordsOnly: false});
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false});
const {error} = await switchToThread(serverUrl, '', false);
const {error} = await switchToThread(serverUrl, '');
expect((error as Error).message).toBe('Post not found');
});
@ -136,7 +155,7 @@ describe('switchToThread', () => {
prepareRecordsOnly: false,
});
const {error} = await switchToThread(serverUrl, post.id, false);
const {error} = await switchToThread(serverUrl, post.id);
expect((error as Error).message).toBe('Channel not found');
});
@ -157,6 +176,42 @@ describe('switchToThread', () => {
const {error} = await switchToThread(serverUrl, post.id, true);
expect(error).toBeUndefined();
});
it('base case not from notification', async () => {
EphemeralStore.theme = Preferences.THEMES.denim;
await operator.handleUsers({users: [user, user2], prepareRecordsOnly: false});
await operator.handleTeam({teams: [team], prepareRecordsOnly: false});
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: 'teamid2'}, {id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
const post = {...TestHelper.fakePost(channelId, user2.id), id: 'postid', create_at: 1};
await operator.handlePosts({
actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL,
order: [post.id],
posts: [post],
prepareRecordsOnly: false,
});
const {error} = await switchToThread(serverUrl, post.id, false);
expect(error).toBeUndefined();
});
it('base case for DM', async () => {
EphemeralStore.theme = Preferences.THEMES.denim;
await operator.handleUsers({users: [user, user2], prepareRecordsOnly: false});
await operator.handleTeam({teams: [team], prepareRecordsOnly: false});
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: 'teamid2'}, {id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false});
await operator.handleChannel({channels: [{...channel, team_id: '', type: 'D', display_name: 'user1-user2'}], prepareRecordsOnly: false});
const post = {...TestHelper.fakePost(channelId, user2.id), id: 'postid', create_at: 1};
await operator.handlePosts({
actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL,
order: [post.id],
posts: [post],
prepareRecordsOnly: false,
});
const {error} = await switchToThread(serverUrl, post.id, true);
expect(error).toBeUndefined();
});
});
describe('createThreadFromNewPost', () => {
@ -181,7 +236,7 @@ describe('createThreadFromNewPost', () => {
await operator.handleUsers({users: [user2], prepareRecordsOnly: false});
const post = {...TestHelper.fakePost(channelId, user2.id), id: 'postid', create_at: 1};
const {models, error} = await createThreadFromNewPost(serverUrl, post, false);
const {models, error} = await createThreadFromNewPost(serverUrl, post);
expect(error).toBeUndefined();
expect(models).toBeDefined();
expect(models?.length).toBe(1); // thread
@ -215,7 +270,7 @@ describe('processReceivedThreads', () => {
},
] as Thread[];
const {models, error} = await processReceivedThreads(serverUrl, thread, team.id, false);
const {models, error} = await processReceivedThreads(serverUrl, thread, team.id);
expect(error).toBeUndefined();
expect(models).toBeDefined();
expect(models?.length).toBe(4); // post, thread, thread participant, thread in team
@ -289,7 +344,7 @@ describe('updateTeamThreadsSync', () => {
});
it('base case', async () => {
const {models, error} = await updateTeamThreadsSync(serverUrl, {id: 'id1', earliest: 1, latest: 2}, false);
const {models, error} = await updateTeamThreadsSync(serverUrl, {id: 'id1', earliest: 1, latest: 2});
expect(error).toBeUndefined();
expect(models).toBeDefined();
});