mattermost-mobile/app/queries/servers/channel.test.ts
Daniel Espino García 1db5cd456c
[MM-67253] Add support for DMs and GMs for autotranslations (#9505)
* [MM-67253] Add support for DMs and GMs for autotranslations

* Address copilot comments
2026-02-16 16:32:02 +01:00

1915 lines
80 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable max-lines */
import {Database, Q} from '@nozbe/watermelondb';
import {of as of$} from 'rxjs';
import {General, Permissions} from '@constants';
import {MM_TABLES} from '@constants/database';
import DatabaseManager from '@database/manager';
import ServerDataOperator from '@database/operator/server_data_operator';
import EphemeralStore from '@store/ephemeral_store';
import TestHelper from '@test/test_helper';
import {hasPermission} from '@utils/role';
import {prepareChannels,
prepareMissingChannelsForAllTeams,
prepareMyChannelsForTeam,
prepareDeleteChannel,
prepareDeleteBookmarks,
queryAllChannels,
queryAllChannelsForTeam,
queryAllChannelsInfo,
queryAllChannelsInfoForTeam,
queryAllMyChannel,
queryAllMyChannelsForTeam,
queryAllUnreadDMsAndGMsIds,
queryChannelsById,
queryChannelsByTypes,
queryUserChannelsByTypes,
queryTeamDefaultChannel,
queryMyChannelsByTeam,
queryUsersOnChannel,
getMyChannel,
observeMyChannel,
observeMyChannelRoles,
getChannelById,
observeChannel,
getChannelByName,
getDefaultChannelForTeam,
getCurrentChannel,
getCurrentChannelInfo,
observeCurrentChannel,
getChannelInfo,
deleteChannelMembership,
addChannelMembership,
getMembersCountByChannelsId,
queryAllMyChannelSettings,
queryMyChannelSettingsByIds,
queryMyChannelUnreads,
queryMyRecentChannels,
observeChannelInfo,
observeAllMyChannelNotifyProps,
observeNotifyPropsByChannels,
observeMyChannelMentionCount,
observeArchiveChannelsByTerm,
observeChannelsByLastPostAt,
observeChannelSettings,
observeDirectChannelsByTerm,
observeIsMutedSetting,
observeJoinedChannelsByTerm,
observeNotDirectChannelsByTerm,
queryChannelMembers,
queryChannelsForAutocomplete,
observeChannelMembers,
queryMyChannelsByChannelIds,
queryMyChannelsWithAutotranslation,
observeChannelAutotranslation,
observeIsChannelAutotranslated,
} from './channel';
import {queryRoles} from './role';
import {getCurrentChannelId, observeCurrentChannelId, observeCurrentUserId} from './system';
import {observeTeammateNameDisplay} from './user';
import type ChannelModel from '@typings/database/models/servers/channel';
import type ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark';
import type ChannelInfoModel from '@typings/database/models/servers/channel_info';
import type ChannelMembershipModel from '@typings/database/models/servers/channel_membership';
jest.mock('./role');
jest.mock('./system', () => {
const actual = jest.requireActual<typeof import('./system')>('./system');
return {
...actual,
getCurrentChannelId: jest.fn(),
observeCurrentChannelId: jest.fn(),
observeCurrentUserId: jest.fn(),
};
});
jest.mock('./user');
jest.mock('@utils/role');
describe('prepareChannels', () => {
let operator: ServerDataOperator;
beforeEach(() => {
operator = {
handleChannel: jest.fn(),
handleChannelInfo: jest.fn(),
handleChannelMembership: jest.fn(),
handleMyChannel: jest.fn(),
handleMyChannelSettings: jest.fn(),
} as unknown as ServerDataOperator;
});
it('should prepare channels, channelInfos, channelMemberships, memberships, and myChannelSettings', () => {
const channels = [
TestHelper.fakeChannel({id: 'channel1'}),
TestHelper.fakeChannel({id: 'channel2'}),
];
const channelInfos = [
TestHelper.fakeChannelInfo({id: 'channel1'}),
TestHelper.fakeChannelInfo({id: 'channel2'}),
];
const channelMemberships = [
TestHelper.fakeChannelMember({channel_id: 'channel1', user_id: 'user1'}),
TestHelper.fakeChannelMember({channel_id: 'channel2', user_id: 'user2'}),
];
const memberships = [
TestHelper.fakeChannelMember({channel_id: 'channel1', user_id: 'user1'}),
TestHelper.fakeChannelMember({channel_id: 'channel2', user_id: 'user2'}),
];
const channelRecords = Promise.resolve([]);
const channelInfoRecords = Promise.resolve([]);
const membershipRecords = Promise.resolve([]);
const myChannelRecords = Promise.resolve([]);
const myChannelSettingsRecords = Promise.resolve([]);
jest.mocked(operator.handleChannel).mockReturnValue(channelRecords);
jest.mocked(operator.handleChannelInfo).mockReturnValue(channelInfoRecords);
jest.mocked(operator.handleChannelMembership).mockReturnValue(membershipRecords);
jest.mocked(operator.handleMyChannel).mockReturnValue(myChannelRecords);
jest.mocked(operator.handleMyChannelSettings).mockReturnValue(myChannelSettingsRecords);
const result = prepareChannels(operator, channels, channelInfos, channelMemberships, memberships, true);
expect(result).toEqual([channelRecords, channelInfoRecords, membershipRecords, myChannelRecords, myChannelSettingsRecords]);
expect(operator.handleChannel).toHaveBeenCalledWith({channels, prepareRecordsOnly: true});
expect(operator.handleChannelInfo).toHaveBeenCalledWith({channelInfos, prepareRecordsOnly: true});
expect(operator.handleChannelMembership).toHaveBeenCalledWith({channelMemberships, prepareRecordsOnly: true});
expect(operator.handleMyChannel).toHaveBeenCalledWith({channels, myChannels: memberships, prepareRecordsOnly: true, isCRTEnabled: true});
expect(operator.handleMyChannelSettings).toHaveBeenCalledWith({settings: memberships, prepareRecordsOnly: true});
});
it('should return an empty array if an error occurs', () => {
jest.mocked(operator.handleChannel).mockImplementation(() => {
throw new Error('Test error');
});
const result = prepareChannels(operator);
expect(result).toEqual([]);
});
});
describe('prepareMissingChannelsForAllTeams', () => {
let operator: ServerDataOperator;
beforeEach(() => {
operator = {
handleChannel: jest.fn(),
handleChannelInfo: jest.fn(),
handleChannelMembership: jest.fn(),
handleMyChannel: jest.fn(),
handleMyChannelSettings: jest.fn(),
} as unknown as ServerDataOperator;
});
it('should prepare missing channels for all teams', () => {
const channels = [
TestHelper.fakeChannel({id: 'channel1', header: 'header1', purpose: 'purpose1', last_post_at: 123, last_root_post_at: 456}),
TestHelper.fakeChannel({id: 'channel2', header: 'header2', purpose: 'purpose2', last_post_at: 789, last_root_post_at: 101112}),
];
const channelMembers = [
TestHelper.fakeChannelMember({channel_id: 'channel1', user_id: 'user1'}),
TestHelper.fakeChannelMember({channel_id: 'channel2', user_id: 'user2'}),
];
const channelRecords = Promise.resolve([]);
const channelInfoRecords = Promise.resolve([]);
const membershipRecords = Promise.resolve([]);
const myChannelRecords = Promise.resolve([]);
const myChannelSettingsRecords = Promise.resolve([]);
jest.mocked(operator.handleChannel).mockReturnValue(channelRecords);
jest.mocked(operator.handleChannelInfo).mockReturnValue(channelInfoRecords);
jest.mocked(operator.handleChannelMembership).mockReturnValue(membershipRecords);
jest.mocked(operator.handleMyChannel).mockReturnValue(myChannelRecords);
jest.mocked(operator.handleMyChannelSettings).mockReturnValue(myChannelSettingsRecords);
const result = prepareMissingChannelsForAllTeams(operator, channels, channelMembers, true);
expect(result).toEqual([channelRecords, channelInfoRecords, membershipRecords, myChannelRecords, myChannelSettingsRecords]);
expect(operator.handleChannel).toHaveBeenCalledWith({channels, prepareRecordsOnly: true});
expect(operator.handleChannelInfo).toHaveBeenCalledWith({
channelInfos: [
{id: 'channel1', header: 'header1', purpose: 'purpose1', guest_count: 0, member_count: 0, pinned_post_count: 0, files_count: 0},
{id: 'channel2', header: 'header2', purpose: 'purpose2', guest_count: 0, member_count: 0, pinned_post_count: 0, files_count: 0},
],
prepareRecordsOnly: true,
});
expect(operator.handleChannelMembership).toHaveBeenCalledWith({
channelMemberships: [
expect.objectContaining({channel_id: 'channel1', user_id: 'user1', id: 'channel1', last_post_at: 123, last_root_post_at: 456}),
expect.objectContaining({channel_id: 'channel2', user_id: 'user2', id: 'channel2', last_post_at: 789, last_root_post_at: 101112}),
],
prepareRecordsOnly: true,
});
expect(operator.handleMyChannel).toHaveBeenCalledWith({
channels,
myChannels: [
expect.objectContaining({channel_id: 'channel1', user_id: 'user1', id: 'channel1', last_post_at: 123, last_root_post_at: 456}),
expect.objectContaining({channel_id: 'channel2', user_id: 'user2', id: 'channel2', last_post_at: 789, last_root_post_at: 101112}),
],
prepareRecordsOnly: true,
isCRTEnabled: true,
});
expect(operator.handleMyChannelSettings).toHaveBeenCalledWith({
settings: [
expect.objectContaining({channel_id: 'channel1', user_id: 'user1', id: 'channel1', last_post_at: 123, last_root_post_at: 456}),
expect.objectContaining({channel_id: 'channel2', user_id: 'user2', id: 'channel2', last_post_at: 789, last_root_post_at: 101112}),
],
prepareRecordsOnly: true,
});
});
it('should return an empty array if an error occurs', () => {
jest.mocked(operator.handleChannel).mockImplementation(() => {
throw new Error('Test error');
});
const result = prepareMissingChannelsForAllTeams(operator, [], []);
expect(result).toEqual([]);
});
});
describe('prepareMyChannelsForTeam', () => {
let operator: ServerDataOperator;
let database: Database;
let query: jest.Mock;
beforeEach(() => {
query = jest.fn();
database = {
get: () => ({
query,
}),
} as unknown as Database;
operator = {
handleChannel: jest.fn(),
handleChannelInfo: jest.fn(),
handleChannelMembership: jest.fn(),
handleMyChannel: jest.fn(),
handleMyChannelSettings: jest.fn(),
database,
} as unknown as ServerDataOperator;
});
it('should prepare my channels for a team', async () => {
const teamId = 'team_id';
const channels = [
TestHelper.fakeChannel({id: 'channel1', header: 'header1', purpose: 'purpose1', last_post_at: 123, last_root_post_at: 456}),
TestHelper.fakeChannel({id: 'channel2', header: 'header2', purpose: 'purpose2', last_post_at: 789, last_root_post_at: 101112}),
];
const channelMembers = [
TestHelper.fakeChannelMember({channel_id: 'channel1', user_id: 'user1'}),
TestHelper.fakeChannelMember({channel_id: 'channel2', user_id: 'user2'}),
];
const allChannelsForTeam = {
channel1: {id: 'channel1'},
channel2: {id: 'channel2'},
};
const allChannelsInfoForTeam = {
channel1: {id: 'channel1', memberCount: 10, guestCount: 2, pinnedPostCount: 1, filesCount: 5},
channel2: {id: 'channel2', memberCount: 20, guestCount: 3, pinnedPostCount: 2, filesCount: 10},
};
const channelsQuery = {fetch: jest.fn().mockResolvedValue(Object.values(allChannelsForTeam))};
const channelInfosQuery = {fetch: jest.fn().mockResolvedValue(Object.values(allChannelsInfoForTeam))};
query.mockReturnValueOnce(channelsQuery);
query.mockReturnValueOnce(channelInfosQuery);
const channelRecords = Promise.resolve([]);
const channelInfoRecords = Promise.resolve([]);
const membershipRecords = Promise.resolve([]);
const myChannelRecords = Promise.resolve([]);
const myChannelSettingsRecords = Promise.resolve([]);
jest.mocked(operator.handleChannel).mockReturnValue(channelRecords);
jest.mocked(operator.handleChannelInfo).mockReturnValue(channelInfoRecords);
jest.mocked(operator.handleChannelMembership).mockReturnValue(membershipRecords);
jest.mocked(operator.handleMyChannel).mockReturnValue(myChannelRecords);
jest.mocked(operator.handleMyChannelSettings).mockReturnValue(myChannelSettingsRecords);
query = jest.fn().mockResolvedValueOnce(Object.entries(allChannelsForTeam).map((c) => c[1])).mockResolvedValueOnce(Object.entries(allChannelsInfoForTeam).map((i) => i[1]));
const result = await prepareMyChannelsForTeam(operator, teamId, channels, channelMembers, true);
query = jest.fn();
expect(result).toEqual([channelRecords, channelInfoRecords, membershipRecords, myChannelRecords, myChannelSettingsRecords]);
expect(operator.handleChannel).toHaveBeenCalledWith({channels, prepareRecordsOnly: true});
expect(operator.handleChannelInfo).toHaveBeenCalledWith({
channelInfos: [
expect.objectContaining({id: 'channel1', header: 'header1', purpose: 'purpose1', guest_count: 2, member_count: 10, pinned_post_count: 1, files_count: 5}),
expect.objectContaining({id: 'channel2', header: 'header2', purpose: 'purpose2', guest_count: 3, member_count: 20, pinned_post_count: 2, files_count: 10}),
],
prepareRecordsOnly: true,
});
expect(operator.handleChannelMembership).toHaveBeenCalledWith({
channelMemberships: [
expect.objectContaining({channel_id: 'channel1', user_id: 'user1'}),
expect.objectContaining({channel_id: 'channel2', user_id: 'user2'}),
],
prepareRecordsOnly: true,
});
expect(operator.handleMyChannel).toHaveBeenCalledWith({
channels,
myChannels: [
expect.objectContaining({channel_id: 'channel1', user_id: 'user1', id: 'channel1'}),
expect.objectContaining({channel_id: 'channel2', user_id: 'user2', id: 'channel2'}),
],
prepareRecordsOnly: true,
isCRTEnabled: true,
});
expect(operator.handleMyChannelSettings).toHaveBeenCalledWith({
settings: [
expect.objectContaining({channel_id: 'channel1', user_id: 'user1', id: 'channel1'}),
expect.objectContaining({channel_id: 'channel2', user_id: 'user2', id: 'channel2'}),
],
prepareRecordsOnly: true,
});
});
});
describe('prepareDeleteChannel', () => {
let channel: ChannelModel;
const serverUrl = 'prepareDeleteChannel.test.com';
beforeEach(() => {
channel = TestHelper.fakeChannelModel({
prepareDestroyPermanently: jest.fn().mockReturnValue({}),
});
});
it('should prepare models for deletion', async () => {
const unsetSpy = jest.spyOn(EphemeralStore, 'unsetChannelPlaybooksSynced');
const membershipModel = TestHelper.fakeMyChannelModel({prepareDestroyPermanently: jest.fn().mockReturnValue({id: 'membership'})});
const infoModel = TestHelper.fakeChannelInfoModel({prepareDestroyPermanently: jest.fn().mockReturnValue({id: 'info'})});
const categoryChannelModel = TestHelper.fakeCategoryChannelModel({prepareDestroyPermanently: jest.fn().mockReturnValue({id: 'category'})});
const memberModels = [TestHelper.fakeChannelMembershipModel({prepareDestroyPermanently: jest.fn().mockReturnValue({id: 'member'})})];
const draftModels = [TestHelper.fakeDraftModel({prepareDestroyPermanently: jest.fn().mockReturnValue({id: 'draft'})})];
const postsInChannelModels = [
TestHelper.fakePostsInChannelModel({prepareDestroyPermanently: jest.fn().mockReturnValue({id: 'postsInChannel'})}),
];
const postModels = [
TestHelper.fakePostModel({id: 'post1', prepareDestroyPermanently: jest.fn().mockReturnValue({id: 'post'})}),
];
const bookmarkModels = [
TestHelper.fakeChannelBookmarkModel({id: 'bookmark1', prepareDestroyPermanently: jest.fn().mockReturnValue({id: 'bookmark'})}),
];
const playbookRunModels = [
TestHelper.fakePlaybookRunModel({
id: 'playbookRun',
prepareDestroyWithRelations: jest.fn().mockResolvedValue([
TestHelper.fakePlaybookRunModel({
id: 'playbookRun',
}),
]),
}),
];
jest.mocked(channel.membership.fetch).mockResolvedValue(membershipModel);
jest.mocked(channel.info.fetch).mockResolvedValue(infoModel);
jest.mocked(channel.categoryChannel.fetch).mockResolvedValue(categoryChannelModel);
jest.mocked(channel.members.fetch).mockResolvedValue(memberModels);
jest.mocked(channel.drafts.fetch).mockResolvedValue(draftModels);
jest.mocked(channel.postsInChannel.fetch).mockResolvedValue(postsInChannelModels);
jest.mocked(channel.posts.fetch).mockResolvedValue(postModels);
jest.mocked(channel.bookmarks.fetch).mockResolvedValue(bookmarkModels);
jest.mocked(channel.playbookRuns.fetch).mockResolvedValue(playbookRunModels);
const result = await prepareDeleteChannel(serverUrl, channel);
expect(result).toEqual([
{},
{id: 'membership'},
{id: 'info'},
{id: 'category'},
{id: 'member'},
{id: 'draft'},
{id: 'postsInChannel'},
{id: 'post'},
{id: 'bookmark'},
expect.objectContaining({id: 'playbookRun'}),
]);
expect(channel.prepareDestroyPermanently).toHaveBeenCalled();
expect(channel.membership.fetch).toHaveBeenCalled();
expect(channel.info.fetch).toHaveBeenCalled();
expect(channel.categoryChannel.fetch).toHaveBeenCalled();
expect(channel.members.fetch).toHaveBeenCalled();
expect(channel.drafts.fetch).toHaveBeenCalled();
expect(channel.postsInChannel.fetch).toHaveBeenCalled();
expect(channel.posts.fetch).toHaveBeenCalled();
expect(channel.bookmarks.fetch).toHaveBeenCalled();
expect(channel.playbookRuns.fetch).toHaveBeenCalled();
expect(playbookRunModels[0].prepareDestroyWithRelations).toHaveBeenCalled();
// Should have cleared the playbooks synced
expect(unsetSpy).toHaveBeenCalledWith(serverUrl, channel.id);
});
it('should handle errors gracefully', async () => {
jest.mocked(channel.membership.fetch).mockRejectedValue(new Error('Test error'));
jest.mocked(channel.info.fetch).mockRejectedValue(new Error('Test error'));
jest.mocked(channel.categoryChannel.fetch).mockRejectedValue(new Error('Test error'));
const result = await prepareDeleteChannel(serverUrl, channel);
expect(result).toEqual([{}]);
expect(channel.prepareDestroyPermanently).toHaveBeenCalled();
});
});
describe('prepareDeleteBookmarks', () => {
let bookmark: ChannelBookmarkModel;
beforeEach(() => {
bookmark = TestHelper.fakeChannelBookmarkModel({
prepareDestroyPermanently: jest.fn().mockReturnValue({}),
fileId: 'file_id',
});
});
it('should prepare bookmark and associated file for deletion', async () => {
const fileModel = TestHelper.fakeFileModel({prepareDestroyPermanently: jest.fn().mockReturnValue({})});
jest.mocked(bookmark.file.fetch).mockResolvedValue(fileModel);
const result = await prepareDeleteBookmarks(bookmark);
expect(result).toEqual([{}, {}]);
expect(bookmark.prepareDestroyPermanently).toHaveBeenCalled();
expect(bookmark.file.fetch).toHaveBeenCalled();
expect(fileModel.prepareDestroyPermanently).toHaveBeenCalled();
});
it('should handle errors gracefully when fetching associated file', async () => {
jest.mocked(bookmark.file.fetch).mockRejectedValue(new Error('Test error'));
const result = await prepareDeleteBookmarks(bookmark);
expect(result).toEqual([{}]);
expect(bookmark.prepareDestroyPermanently).toHaveBeenCalled();
expect(bookmark.file.fetch).toHaveBeenCalled();
});
it('should prepare only the bookmark for deletion if no associated file', async () => {
bookmark.fileId = undefined;
const result = await prepareDeleteBookmarks(bookmark);
expect(result).toEqual([{}]);
expect(bookmark.prepareDestroyPermanently).toHaveBeenCalled();
expect(bookmark.file.fetch).not.toHaveBeenCalled();
});
});
describe('Channel Queries', () => {
let database: Database;
let mockQuery: jest.Mock;
let mockCollection: { query: jest.Mock};
beforeEach(() => {
mockQuery = jest.fn(() => ({extend: jest.fn()}));
mockCollection = {query: mockQuery};
database = {
get: jest.fn().mockReturnValue(mockCollection),
} as unknown as Database;
});
it('should query all channels', () => {
queryAllChannels(database);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalled();
});
it('should query all channels for a team', () => {
const teamId = 'team_id';
queryAllChannelsForTeam(database, teamId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(Q.where('team_id', teamId));
});
it('should query all channel info', () => {
queryAllChannelsInfo(database);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_INFO);
expect(mockQuery).toHaveBeenCalled();
});
it('should query all channel info for a team', () => {
const teamId = 'team_id';
queryAllChannelsInfoForTeam(database, teamId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_INFO);
expect(mockQuery).toHaveBeenCalledWith(Q.on(MM_TABLES.SERVER.CHANNEL, Q.where('team_id', teamId)));
});
it('should query all my channels', () => {
queryAllMyChannel(database);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(mockQuery).toHaveBeenCalled();
});
it('should query all my channels for a team', () => {
const teamId = 'team_id';
queryAllMyChannelsForTeam(database, teamId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(Q.on(MM_TABLES.SERVER.CHANNEL, Q.where('team_id', Q.oneOf([teamId, '']))));
});
it('should query all unread DMs and GMs ids', () => {
queryAllUnreadDMsAndGMsIds(database);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(
Q.on(MM_TABLES.SERVER.MY_CHANNEL, Q.or(
Q.where('mentions_count', Q.gt(0)),
Q.where('message_count', Q.gt(0)),
)),
Q.where('type', Q.oneOf([General.GM_CHANNEL, General.DM_CHANNEL])),
);
});
it('should query channels by their IDs', () => {
const channelIds = ['channel1', 'channel2'];
queryChannelsById(database, channelIds);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(Q.where('id', Q.oneOf(channelIds)));
});
it('should query channels by their types', () => {
const channelTypes: ChannelType[] = ['O', 'P'];
queryChannelsByTypes(database, channelTypes);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(Q.where('type', Q.oneOf(channelTypes)));
});
it('should query user channels by their types', () => {
const userId = 'user_id';
const channelTypes: ChannelType[] = ['O', 'P'];
queryUserChannelsByTypes(database, userId, channelTypes);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(
Q.where('type', Q.oneOf(channelTypes)),
Q.on(MM_TABLES.SERVER.CHANNEL_MEMBERSHIP, Q.where('user_id', userId)),
);
});
it('should query the default channel for a team', () => {
const teamId = 'team_id';
queryTeamDefaultChannel(database, teamId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(
Q.where('team_id', teamId),
Q.where('name', General.DEFAULT_CHANNEL),
);
});
it('should query my channels by team', () => {
const teamId = 'team_id';
queryMyChannelsByTeam(database, teamId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(
Q.on(MM_TABLES.SERVER.CHANNEL, Q.and(
Q.where('team_id', Q.eq(teamId)),
Q.where('delete_at', Q.eq(0)),
)),
);
});
it('should include deleted channels if includeDeleted is true', () => {
const teamId = 'team_id';
queryMyChannelsByTeam(database, teamId, true);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(
Q.on(MM_TABLES.SERVER.CHANNEL, Q.and(
Q.where('team_id', Q.eq(teamId)),
)),
);
});
it('should query users on a channel', () => {
const channelId = 'channel_id';
queryUsersOnChannel(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.USER);
expect(mockQuery).toHaveBeenCalledWith(Q.on(MM_TABLES.SERVER.CHANNEL_MEMBERSHIP, Q.where('channel_id', channelId)));
});
it('should query all my channel settings', () => {
queryAllMyChannelSettings(database);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL_SETTINGS);
expect(mockQuery).toHaveBeenCalled();
});
it('should query my channel settings by IDs', () => {
const ids = ['id1', 'id2'];
queryMyChannelSettingsByIds(database, ids);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL_SETTINGS);
expect(mockQuery).toHaveBeenCalledWith(Q.where('id', Q.oneOf(ids)));
});
it('should query my channel unreads', () => {
const currentTeamId = 'team_id';
queryMyChannelUnreads(database, currentTeamId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(
Q.on(
MM_TABLES.SERVER.CHANNEL,
Q.and(
Q.or(
Q.where('team_id', Q.eq(currentTeamId)),
Q.where('team_id', Q.eq('')),
),
Q.where('delete_at', Q.eq(0)),
),
),
Q.or(
Q.where('is_unread', Q.eq(true)),
Q.where('mentions_count', Q.gte(0)),
),
Q.sortBy('last_post_at', Q.desc),
);
});
it('should query my recent channels', () => {
const take = 10;
queryMyRecentChannels(database, take);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(mockQuery).toHaveBeenCalled();
});
it('should query my recent channels without take', () => {
const take = 0;
queryMyRecentChannels(database, take);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(mockQuery).toHaveBeenCalled();
});
it('should query channels for autocomplete', () => {
const matchTerm = 'test';
const teamId = 'team_id';
queryChannelsForAutocomplete(database, matchTerm, true, teamId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalled();
});
it('should query channels for autocomplete with no search', () => {
const matchTerm = 'test';
const teamId = 'team_id';
queryChannelsForAutocomplete(database, matchTerm, false, teamId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalled();
});
it('should query channel members', () => {
const channelId = 'channel_id';
queryChannelMembers(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_MEMBERSHIP);
expect(mockQuery).toHaveBeenCalledWith(
Q.where('channel_id', channelId),
);
});
});
describe('Channel Functions', () => {
let database: Database;
beforeEach(() => {
database = {
get: jest.fn(),
} as unknown as Database;
});
describe('getMyChannel', () => {
it('should return the channel member if found', async () => {
const channelId = 'channel_id';
const mockMember = TestHelper.fakeMyChannelModel({id: channelId});
jest.mocked(database.get).mockReturnValue({
find: jest.fn().mockResolvedValue(mockMember),
} as any);
const result = await getMyChannel(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(result).toEqual(mockMember);
});
it('should return undefined if the channel member is not found', async () => {
const channelId = 'channel_id';
jest.mocked(database.get).mockReturnValue({
find: jest.fn().mockRejectedValue(new Error('Not found')),
} as any);
const result = await getMyChannel(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(result).toBeUndefined();
});
});
describe('observeMyChannel', () => {
it('should observe the channel member', () => {
const channelId = 'channel_id';
const mockQuery = jest.fn().mockReturnValue({
observe: jest.fn().mockReturnValue(of$([{id: channelId, observe: jest.fn().mockReturnValue(of$({id: channelId}))}])),
});
jest.mocked(database.get).mockReturnValue({
query: mockQuery,
} as any);
const result = observeMyChannel(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(Q.where('id', channelId), Q.take(1));
result.subscribe((value) => {
expect(value).toEqual({id: channelId});
});
});
it('should return undefined if the channel member is not found', () => {
const channelId = 'channel_id';
const mockQuery = jest.fn().mockReturnValue({
observe: jest.fn().mockReturnValue(of$([])),
});
jest.mocked(database.get).mockReturnValue({
query: mockQuery,
} as any);
const result = observeMyChannel(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(Q.where('id', channelId), Q.take(1));
result.subscribe((value) => {
expect(value).toBeUndefined();
});
});
});
describe('observeMyChannelRoles', () => {
it('should observe the channel member roles', () => {
const channelId = 'channel_id';
const roles = 'role1 role2';
const mockQuery = jest.fn().mockReturnValue({
observe: jest.fn().mockReturnValue(of$([{id: channelId, roles, observe: jest.fn().mockReturnValue(of$({roles}))}])),
});
jest.mocked(database.get).mockReturnValue({
query: mockQuery,
} as any);
const result = observeMyChannelRoles(database, channelId);
result.subscribe((value) => {
expect(value).toEqual(roles);
});
});
it('should return undefined if the channel member is not found', () => {
const channelId = 'channel_id';
const mockQuery = jest.fn().mockReturnValue({
observe: jest.fn().mockReturnValue(of$([])),
});
jest.mocked(database.get).mockReturnValue({
query: mockQuery,
} as any);
const result = observeMyChannelRoles(database, channelId);
result.subscribe((value) => {
expect(value).toBeUndefined();
});
});
});
describe('getChannelById', () => {
it('should return the channel if found', async () => {
const channelId = 'channel_id';
const mockedChannel = TestHelper.fakeChannelModel({id: channelId});
jest.mocked(database.get).mockReturnValue({
find: jest.fn().mockResolvedValue(mockedChannel),
} as any);
const result = await getChannelById(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(result).toEqual(mockedChannel);
});
it('should return undefined if the channel is not found', async () => {
const channelId = 'channel_id';
jest.mocked(database.get).mockReturnValue({
find: jest.fn().mockRejectedValue(new Error('Not found')),
} as any);
const result = await getChannelById(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(result).toBeUndefined();
});
});
describe('observeChannel', () => {
it('should observe the channel', () => {
const channelId = 'channel_id';
const mockQuery = jest.fn().mockReturnValue({
observe: jest.fn().mockReturnValue(of$([{id: channelId, observe: jest.fn().mockReturnValue(of$({id: channelId}))}])),
});
jest.mocked(database.get).mockReturnValue({
query: mockQuery,
} as any);
const result = observeChannel(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(Q.where('id', channelId), Q.take(1));
result.subscribe((value) => {
expect(value).toEqual({id: channelId});
});
});
it('should return undefined if the channel is not found', () => {
const channelId = 'channel_id';
const mockQuery = jest.fn().mockReturnValue({
observe: jest.fn().mockReturnValue(of$([])),
});
jest.mocked(database.get).mockReturnValue({
query: mockQuery,
} as any);
const result = observeChannel(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(Q.where('id', channelId), Q.take(1));
result.subscribe((value) => {
expect(value).toBeUndefined();
});
});
});
describe('getChannelByName', () => {
it('should return the channel if found', async () => {
const teamId = 'team_id';
const channelName = 'channel_name';
const mockedChannel = TestHelper.fakeChannelModel({id: 'channel_id', name: channelName});
jest.mocked(database.get).mockReturnValue({
query: jest.fn().mockReturnValue({
fetch: jest.fn().mockResolvedValue([mockedChannel]),
}),
} as any);
const result = await getChannelByName(database, teamId, channelName);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(result).toEqual(mockedChannel);
});
it('should return undefined if the channel is not found', async () => {
const teamId = 'team_id';
const channelName = 'channel_name';
jest.mocked(database.get).mockReturnValue({
query: jest.fn().mockReturnValue({
fetch: jest.fn().mockResolvedValue([]),
}),
} as any);
const result = await getChannelByName(database, teamId, channelName);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(result).toBeUndefined();
});
});
describe('getChannelInfo', () => {
it('should return the channel info if found', async () => {
const channelId = 'channel_id';
const mockChannelInfo = TestHelper.fakeChannelInfoModel({id: channelId});
jest.mocked(database.get).mockReturnValue({
find: jest.fn().mockResolvedValue(mockChannelInfo),
} as any);
const result = await getChannelInfo(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_INFO);
expect(result).toEqual(mockChannelInfo);
});
it('should return undefined if the channel info is not found', async () => {
const channelId = 'channel_id';
jest.mocked(database.get).mockReturnValue({
find: jest.fn().mockRejectedValue(new Error('Not found')),
} as any);
const result = await getChannelInfo(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_INFO);
expect(result).toBeUndefined();
});
});
describe('getDefaultChannelForTeam', () => {
let mockQuery: jest.Mock;
let mockCollection: { query: jest.Mock };
beforeEach(() => {
mockQuery = jest.fn();
mockCollection = {query: mockQuery};
database = {
get: jest.fn().mockReturnValue(mockCollection),
} as unknown as Database;
});
it('should return the default channel for the team', async () => {
const teamId = 'team_id';
const defaultChannel = TestHelper.fakeChannelModel({id: 'default_channel', name: General.DEFAULT_CHANNEL});
const myFirstTeamChannel = TestHelper.fakeChannelModel({id: 'first_team_channel'});
const roles = [TestHelper.fakeRoleModel({permissions: [Permissions.JOIN_PUBLIC_CHANNELS]})];
jest.mocked(queryRoles).mockReturnValue(TestHelper.fakeQuery(roles));
jest.mocked(hasPermission).mockReturnValue(true);
jest.mocked(mockQuery).mockReturnValue({
fetch: jest.fn().mockResolvedValue([defaultChannel, myFirstTeamChannel]),
});
const result = await getDefaultChannelForTeam(database, teamId);
expect(queryRoles).toHaveBeenCalledWith(database);
expect(hasPermission).toHaveBeenCalledWith(roles, Permissions.JOIN_PUBLIC_CHANNELS);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(
Q.on(MM_TABLES.SERVER.MY_CHANNEL, 'id', Q.notEq('')),
Q.and(
Q.where('team_id', teamId),
Q.where('delete_at', Q.eq(0)),
Q.where('type', General.OPEN_CHANNEL),
),
Q.sortBy('display_name', Q.asc),
);
expect(result).toEqual(defaultChannel);
});
it('should return the default channel for the team with ignore id', async () => {
const teamId = 'team_id';
const defaultChannel = TestHelper.fakeChannelModel({id: 'default_channel', name: General.DEFAULT_CHANNEL});
const myFirstTeamChannel = TestHelper.fakeChannelModel({id: 'first_team_channel'});
jest.mocked(queryRoles).mockReturnValue(TestHelper.fakeQuery([]));
jest.mocked(hasPermission).mockReturnValue(true);
jest.mocked(mockQuery).mockReturnValue({
fetch: jest.fn().mockResolvedValue([defaultChannel, myFirstTeamChannel]),
});
const result = await getDefaultChannelForTeam(database, teamId, 'ignoreid');
expect(queryRoles).toHaveBeenCalledWith(database);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(
Q.on(MM_TABLES.SERVER.MY_CHANNEL, 'id', Q.notEq('')),
Q.and(
Q.where('team_id', teamId),
Q.where('delete_at', Q.eq(0)),
Q.where('type', General.OPEN_CHANNEL),
Q.where('id', Q.notEq('ignoreid')),
),
Q.sortBy('display_name', Q.asc),
);
expect(result).toEqual(defaultChannel);
});
it('should return the first team channel if no default channel is found', async () => {
const teamId = 'team_id';
const myFirstTeamChannel = TestHelper.fakeChannelModel({id: 'first_team_channel'});
const roles = [TestHelper.fakeRoleModel({permissions: []})];
jest.mocked(queryRoles).mockReturnValue(TestHelper.fakeQuery(roles));
jest.mocked(hasPermission).mockReturnValue(false);
jest.mocked(mockQuery).mockReturnValue({
fetch: jest.fn().mockResolvedValue([myFirstTeamChannel]),
});
const result = await getDefaultChannelForTeam(database, teamId);
expect(queryRoles).toHaveBeenCalledWith(database);
expect(hasPermission).toHaveBeenCalledWith(roles, Permissions.JOIN_PUBLIC_CHANNELS);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(
Q.on(MM_TABLES.SERVER.MY_CHANNEL, 'id', Q.notEq('')),
Q.and(
Q.where('team_id', teamId),
Q.where('delete_at', Q.eq(0)),
Q.where('type', General.OPEN_CHANNEL),
),
Q.sortBy('display_name', Q.asc),
);
expect(result).toEqual(myFirstTeamChannel);
});
it('should return undefined if no channels are found', async () => {
const teamId = 'team_id';
const roles = [TestHelper.fakeRoleModel({permissions: [Permissions.JOIN_PUBLIC_CHANNELS]})];
jest.mocked(queryRoles).mockReturnValue(TestHelper.fakeQuery(roles));
jest.mocked(hasPermission).mockReturnValue(true);
jest.mocked(mockQuery).mockReturnValue({
fetch: jest.fn().mockResolvedValue([]),
});
const result = await getDefaultChannelForTeam(database, teamId);
expect(queryRoles).toHaveBeenCalledWith(database);
expect(hasPermission).toHaveBeenCalledWith(roles, Permissions.JOIN_PUBLIC_CHANNELS);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(mockQuery).toHaveBeenCalledWith(
Q.on(MM_TABLES.SERVER.MY_CHANNEL, 'id', Q.notEq('')),
Q.and(
Q.where('team_id', teamId),
Q.where('delete_at', Q.eq(0)),
Q.where('type', General.OPEN_CHANNEL),
),
Q.sortBy('display_name', Q.asc),
);
expect(result).toBeUndefined();
});
});
describe('getCurrentChannel', () => {
it('should return the current channel if found', async () => {
const currentChannelId = 'current_channel_id';
const mockedChannel = TestHelper.fakeChannelModel({id: currentChannelId});
jest.mocked(getCurrentChannelId).mockResolvedValue(currentChannelId);
jest.mocked(database.get).mockReturnValue({
find: jest.fn().mockResolvedValue(mockedChannel),
} as any);
const result = await getCurrentChannel(database);
expect(getCurrentChannelId).toHaveBeenCalledWith(database);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
expect(result).toEqual(mockedChannel);
});
it('should return undefined if no current channel is found', async () => {
jest.mocked(getCurrentChannelId).mockResolvedValue('');
const result = await getCurrentChannel(database);
expect(getCurrentChannelId).toHaveBeenCalledWith(database);
expect(result).toBeUndefined();
});
});
describe('getCurrentChannelInfo', () => {
it('should return the current channel info if found', async () => {
const currentChannelId = 'current_channel_id';
const mockChannelInfo = TestHelper.fakeChannelInfoModel({id: currentChannelId});
jest.mocked(getCurrentChannelId).mockResolvedValue(currentChannelId);
jest.mocked(database.get).mockReturnValue({
find: jest.fn().mockResolvedValue(mockChannelInfo),
} as any);
const result = await getCurrentChannelInfo(database);
expect(getCurrentChannelId).toHaveBeenCalledWith(database);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_INFO);
expect(result).toEqual(mockChannelInfo);
});
it('should return undefined if no current channel info is found', async () => {
jest.mocked(getCurrentChannelId).mockResolvedValue('');
const result = await getCurrentChannelInfo(database);
expect(getCurrentChannelId).toHaveBeenCalledWith(database);
expect(result).toBeUndefined();
});
});
describe('observeCurrentChannel', () => {
it('should observe the current channel', () => {
const currentChannelId = 'current_channel_id';
const mockedChannel = TestHelper.fakeChannelModel({id: currentChannelId, observe: jest.fn().mockReturnValue(of$({id: currentChannelId}))});
const mockQuery = jest.fn().mockReturnValue({
observe: jest.fn().mockReturnValue(of$([mockedChannel])),
});
jest.mocked(database.get).mockReturnValue({
query: mockQuery,
} as any);
jest.mocked(observeCurrentChannelId).mockReturnValue(of$(currentChannelId));
const result = observeCurrentChannel(database);
expect(observeCurrentChannelId).toHaveBeenCalledWith(database);
result.subscribe((value) => {
expect(value).toEqual({id: currentChannelId});
});
});
it('should return undefined if no current channel is found', () => {
const currentChannelId = 'current_channel_id';
const mockQuery = jest.fn().mockReturnValue({
observe: jest.fn().mockReturnValue(of$([])),
});
jest.mocked(database.get).mockReturnValue({
query: mockQuery,
} as any);
jest.mocked(observeCurrentChannelId).mockReturnValue(of$(currentChannelId));
const result = observeCurrentChannel(database);
expect(observeCurrentChannelId).toHaveBeenCalledWith(database);
result.subscribe((value) => {
expect(value).toBeUndefined();
});
});
});
describe('getChannelInfo', () => {
it('should return the channel info if found', async () => {
const channelId = 'channel_id';
const mockChannelInfo = TestHelper.fakeChannelInfoModel({id: channelId});
jest.mocked(database.get).mockReturnValue({
find: jest.fn().mockResolvedValue(mockChannelInfo),
} as any);
const result = await getChannelInfo(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_INFO);
expect(result).toEqual(mockChannelInfo);
});
it('should return undefined if the channel info is not found', async () => {
const channelId = 'channel_id';
jest.mocked(database.get).mockReturnValue({
find: jest.fn().mockRejectedValue(new Error('Not found')),
} as any);
const result = await getChannelInfo(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_INFO);
expect(result).toBeUndefined();
});
});
});
describe('Channel Membership Functions', () => {
let operator: ServerDataOperator;
let database: Database;
beforeEach(() => {
database = {
get: jest.fn(),
} as unknown as Database;
operator = {
database,
handleChannelMembership: jest.fn(),
batchRecords: jest.fn(),
} as unknown as ServerDataOperator;
});
describe('deleteChannelMembership', () => {
it('should delete channel membership and return models', async () => {
const userId = 'user_id';
const channelId = 'channel_id';
const mockMembership = TestHelper.fakeChannelMembershipModel({prepareDestroyPermanently: jest.fn().mockReturnValue({})});
jest.mocked(database.get).mockReturnValue({
query: jest.fn().mockReturnValue({
fetch: jest.fn().mockResolvedValue([mockMembership]),
}),
} as any);
const result = await deleteChannelMembership(operator, userId, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_MEMBERSHIP);
expect(result.models).toEqual([{}]);
expect(operator.batchRecords).toHaveBeenCalledWith([{}], 'deleteChannelMembership');
});
it('should return models without batching if prepareRecordsOnly is true', async () => {
const userId = 'user_id';
const channelId = 'channel_id';
const mockMembership = TestHelper.fakeChannelMembershipModel({prepareDestroyPermanently: jest.fn().mockReturnValue({})});
jest.mocked(database.get).mockReturnValue({
query: jest.fn().mockReturnValue({
fetch: jest.fn().mockResolvedValue([mockMembership]),
}),
} as any);
const result = await deleteChannelMembership(operator, userId, channelId, true);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_MEMBERSHIP);
expect(result.models).toEqual([{}]);
expect(operator.batchRecords).not.toHaveBeenCalled();
});
it('should return an error if an exception occurs', async () => {
const userId = 'user_id';
const channelId = 'channel_id';
const error = new Error('Test error');
jest.mocked(database.get).mockReturnValue({
query: jest.fn().mockReturnValue({
fetch: jest.fn().mockRejectedValue(error),
}),
} as any);
const result = await deleteChannelMembership(operator, userId, channelId);
expect(result.error).toEqual(error);
});
});
describe('addChannelMembership', () => {
it('should add channel membership and return an empty object', async () => {
const userId = 'user_id';
const channelId = 'channel_id';
const result = await addChannelMembership(operator, userId, channelId);
expect(operator.handleChannelMembership).toHaveBeenCalledWith({
channelMemberships: [{channel_id: channelId, user_id: userId}],
prepareRecordsOnly: false,
});
expect(result).toEqual({});
});
it('should return an error if an exception occurs', async () => {
const userId = 'user_id';
const channelId = 'channel_id';
const error = new Error('Test error');
jest.mocked(operator.handleChannelMembership).mockRejectedValue(error);
const result = await addChannelMembership(operator, userId, channelId);
expect(result.error).toEqual(error);
});
});
describe('getMembersCountByChannelsId', () => {
let mockQuery: jest.Mock;
let mockCollection: { query: jest.Mock };
beforeEach(() => {
mockQuery = jest.fn();
mockCollection = {query: mockQuery};
database = {
get: jest.fn().mockReturnValue(mockCollection),
} as unknown as Database;
});
it('should return the members count by channel IDs', async () => {
const channelsId = ['channel1', 'channel2'];
const mockMemberships = [
TestHelper.fakeChannelMembershipModel({channelId: 'channel1'}),
TestHelper.fakeChannelMembershipModel({channelId: 'channel1'}),
TestHelper.fakeChannelMembershipModel({channelId: 'channel2'}),
];
jest.mocked(mockQuery).mockReturnValue({
fetch: jest.fn().mockResolvedValue(mockMemberships),
});
const result = await getMembersCountByChannelsId(database, channelsId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_MEMBERSHIP);
expect(mockQuery).toHaveBeenCalledWith(Q.where('channel_id', Q.oneOf(channelsId)));
expect(result).toEqual({channel1: 2, channel2: 1});
});
it('should return zero counts for channels with no members', async () => {
const channelsId = ['channel1', 'channel2'];
const mockMemberships: ChannelMembershipModel[] = [];
jest.mocked(mockQuery).mockReturnValue({
fetch: jest.fn().mockResolvedValue(mockMemberships),
});
const result = await getMembersCountByChannelsId(database, channelsId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_MEMBERSHIP);
expect(mockQuery).toHaveBeenCalledWith(Q.where('channel_id', Q.oneOf(channelsId)));
expect(result).toEqual({channel1: 0, channel2: 0});
});
it('should handle errors gracefully', async () => {
const channelsId = ['channel1', 'channel2'];
const error = new Error('Test error');
jest.mocked(mockQuery).mockReturnValue({
fetch: jest.fn().mockRejectedValue(error),
});
await expect(getMembersCountByChannelsId(database, channelsId)).rejects.toThrow(error);
});
});
});
describe('Channel Observations', () => {
let database: Database;
let mockQuery: jest.Mock;
let mockCollection: { query: jest.Mock };
beforeEach(() => {
mockQuery = jest.fn();
mockCollection = {query: mockQuery};
database = {
get: jest.fn().mockReturnValue(mockCollection),
} as unknown as Database;
});
it('should observe channel info', () => {
const channelId = 'channel_id';
const mockChannelInfo = {id: channelId, observe: jest.fn().mockReturnValue(of$({id: channelId}))} as unknown as ChannelInfoModel;
mockQuery.mockReturnValue({
observe: jest.fn().mockReturnValue(of$([mockChannelInfo])),
});
const result = observeChannelInfo(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_INFO);
expect(mockQuery).toHaveBeenCalledWith(Q.where('id', channelId), Q.take(1));
result.subscribe((value) => {
expect(value).toEqual({id: channelId});
});
});
it('should observe all my channel notify props', () => {
const mockSettings = [
TestHelper.fakeMyChannelSettingsModel({id: 'id1', notifyProps: {mark_unread: 'all'}}),
TestHelper.fakeMyChannelSettingsModel({id: 'id2', notifyProps: {mark_unread: 'mention'}}),
];
mockQuery.mockReturnValue({
observeWithColumns: jest.fn().mockReturnValue(of$(mockSettings)),
});
const result = observeAllMyChannelNotifyProps(database);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL_SETTINGS);
result.subscribe((value) => {
expect(value).toEqual({
id1: {mark_unread: 'all'},
id2: {mark_unread: 'mention'},
});
});
});
it('should observe notify props by channels', () => {
const channels = [
TestHelper.fakeChannelModel({id: 'channel1'}),
TestHelper.fakeChannelModel({id: 'channel2'}),
];
const mockSettings = [
TestHelper.fakeMyChannelSettingsModel({id: 'channel1', notifyProps: {mark_unread: 'all'}}),
TestHelper.fakeMyChannelSettingsModel({id: 'channel2', notifyProps: {mark_unread: 'mention'}}),
];
mockQuery.mockReturnValue({
observeWithColumns: jest.fn().mockReturnValue(of$(mockSettings)),
});
const result = observeNotifyPropsByChannels(database, channels);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL_SETTINGS);
result.subscribe((value) => {
expect(value).toEqual({
channel1: {mark_unread: 'all'},
channel2: {mark_unread: 'mention'},
});
});
});
it('should observe my channel mention count', () => {
const teamId = 'team_id';
const mockChannels = [
TestHelper.fakeMyChannelModel({mentionsCount: 2, isUnread: true}),
TestHelper.fakeMyChannelModel({mentionsCount: 3, isUnread: false}),
];
mockQuery.mockReturnValue({
observeWithColumns: jest.fn().mockReturnValue(of$(mockChannels)),
});
const result = observeMyChannelMentionCount(database, teamId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
result.subscribe((value) => {
expect(value).toEqual(5);
});
});
it('should observe my channel mention count with no team id', () => {
const mockChannels = [
TestHelper.fakeMyChannelModel({mentionsCount: 2, isUnread: true}),
TestHelper.fakeMyChannelModel({mentionsCount: 3, isUnread: false}),
];
mockQuery.mockReturnValue({
observeWithColumns: jest.fn().mockReturnValue(of$(mockChannels)),
});
const result = observeMyChannelMentionCount(database);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
result.subscribe((value) => {
expect(value).toEqual(5);
});
});
it('should observe direct channels by term', () => {
const term = '@test';
const mockCurrentUserId = 'user_id';
const mockChannels = [TestHelper.fakeMyChannelModel({id: 'channel1'})];
jest.mocked(observeCurrentUserId).mockReturnValue(of$(mockCurrentUserId));
mockQuery.mockReturnValue({
observe: jest.fn().mockReturnValue(of$(mockChannels)),
});
const result = observeDirectChannelsByTerm(database, term);
result.subscribe((value) => {
expect(value).toEqual(mockChannels);
});
});
it('should observe not direct channels by term', () => {
const term = 'test';
const mockTeammateNameSetting = General.TEAMMATE_NAME_DISPLAY.SHOW_USERNAME;
const mockUsers = [TestHelper.fakeUserModel({id: 'user1'})];
jest.mocked(observeTeammateNameDisplay).mockReturnValue(of$(mockTeammateNameSetting));
mockQuery.mockReturnValue({
observe: jest.fn().mockReturnValue(of$(mockUsers)),
});
const result = observeNotDirectChannelsByTerm(database, term);
result.subscribe((value) => {
expect(value).toEqual(mockUsers);
});
});
it('should observe joined channels by term', () => {
const term = 'test';
const mockChannels = [TestHelper.fakeMyChannelModel({id: 'channel1'})];
mockQuery.mockReturnValue({
observe: jest.fn().mockReturnValue(of$(mockChannels)),
});
const result = observeJoinedChannelsByTerm(database, term);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
result.subscribe((value) => {
expect(value).toEqual(mockChannels);
});
});
it('should observe joined channels by term with @ mention', () => {
const term = '@test';
const result = observeJoinedChannelsByTerm(database, term);
result.subscribe((value) => {
expect(value).toEqual([]);
});
});
it('should observe archive channels by term', () => {
const term = 'test';
const mockChannels = [TestHelper.fakeMyChannelModel({id: 'channel1'})];
mockQuery.mockReturnValue({
observe: jest.fn().mockReturnValue(of$(mockChannels)),
});
const result = observeArchiveChannelsByTerm(database, term);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL);
result.subscribe((value) => {
expect(value).toEqual(mockChannels);
});
});
it('should observe joined channels by term with @ mention', () => {
const term = '@test';
const result = observeArchiveChannelsByTerm(database, term);
result.subscribe((value) => {
expect(value).toEqual([]);
});
});
it('should observe channel settings', () => {
const channelId = 'channel_id';
const mockSettings = TestHelper.fakeMyChannelSettingsModel({id: channelId});
jest.mocked(mockSettings.observe).mockReturnValue(of$(mockSettings));
mockQuery.mockReturnValue({
observe: jest.fn().mockReturnValue(of$([mockSettings])),
});
const result = observeChannelSettings(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL_SETTINGS);
result.subscribe((value) => {
expect(value).toMatchObject({id: channelId});
});
});
it('should observe is muted setting', () => {
const channelId = 'channel_id';
const mockSettings = TestHelper.fakeMyChannelSettingsModel({id: channelId, notifyProps: {mark_unread: General.MENTION}});
jest.mocked(mockSettings.observe).mockReturnValue(of$(mockSettings));
mockQuery.mockReturnValue({
observe: jest.fn().mockReturnValue(of$([mockSettings])),
});
const result = observeIsMutedSetting(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.MY_CHANNEL_SETTINGS);
result.subscribe((value) => {
expect(value).toEqual(true);
});
});
it('should observe channels by last post at', () => {
const myChannels = [TestHelper.fakeMyChannelModel({id: 'channel1'})];
const mockChannels = [TestHelper.fakeChannelModel({id: 'channel1'})];
mockQuery.mockReturnValue({
observe: jest.fn().mockReturnValue(of$(mockChannels)),
});
const result = observeChannelsByLastPostAt(database, myChannels);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL);
result.subscribe((value) => {
expect(value).toEqual(mockChannels);
});
});
it('should observe channel members', () => {
const channelId = 'channel_id';
const mockMembers = [
TestHelper.fakeChannelMembershipModel({id: 'member1'}),
TestHelper.fakeChannelMembershipModel({id: 'member2'}),
];
mockQuery.mockReturnValue({
observe: jest.fn().mockReturnValue(of$(mockMembers)),
});
const result = observeChannelMembers(database, channelId);
expect(database.get).toHaveBeenCalledWith(MM_TABLES.SERVER.CHANNEL_MEMBERSHIP);
expect(mockQuery).toHaveBeenCalledWith(Q.where('channel_id', channelId));
result.subscribe((value) => {
expect(value).toEqual(mockMembers);
});
});
});
describe('queryMyChannelsByChannelIds and queryMyChannelsWithAutotranslation', () => {
const serverUrl = 'channelQueries.test.com';
let database: Database;
let operator: ServerDataOperator;
beforeEach(async () => {
await DatabaseManager.init([serverUrl]);
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
database = serverDatabaseAndOperator.database;
operator = serverDatabaseAndOperator.operator;
});
afterEach(async () => {
await DatabaseManager.destroyServerDatabase(serverUrl);
});
describe('queryMyChannelsByChannelIds', () => {
it('should return myChannels for given ids', async () => {
const channel1 = TestHelper.fakeChannel({id: 'ch1', team_id: 'team1'});
const channel2 = TestHelper.fakeChannel({id: 'ch2', team_id: 'team1'});
const myCh1 = TestHelper.fakeChannelMember({id: 'ch1', channel_id: 'ch1'});
const myCh2 = TestHelper.fakeChannelMember({id: 'ch2', channel_id: 'ch2'});
await operator.handleChannel({channels: [channel1, channel2], prepareRecordsOnly: false});
await operator.handleMyChannel({channels: [channel1, channel2], myChannels: [myCh1, myCh2], prepareRecordsOnly: false});
const result = queryMyChannelsByChannelIds(database, ['ch1', 'ch2']);
const fetched = await result.fetch();
expect(fetched.length).toBe(2);
expect(fetched.map((c) => c.id).sort()).toEqual(['ch1', 'ch2']);
});
it('should return empty when no matching ids', async () => {
const result = queryMyChannelsByChannelIds(database, ['nonexistent']);
const fetched = await result.fetch();
expect(fetched.length).toBe(0);
});
});
describe('queryMyChannelsWithAutotranslation', () => {
it('should return only myChannels with channel autotranslation enabled and user autotranslation not disabled', async () => {
const channel1 = TestHelper.fakeChannel({id: 'ch1', team_id: 'team1', autotranslation: true});
const channel2 = TestHelper.fakeChannel({id: 'ch2', team_id: 'team1', autotranslation: true});
const myCh1 = TestHelper.fakeChannelMember({id: 'ch1', channel_id: 'ch1', autotranslation_disabled: false});
const myCh2 = TestHelper.fakeChannelMember({id: 'ch2', channel_id: 'ch2', autotranslation_disabled: true});
await operator.handleChannel({channels: [channel1, channel2], prepareRecordsOnly: false});
await operator.handleMyChannel({channels: [channel1, channel2], myChannels: [myCh1, myCh2], prepareRecordsOnly: false});
const result = queryMyChannelsWithAutotranslation(database);
const fetched = await result.fetch();
expect(fetched.length).toBe(1);
expect(fetched[0].id).toBe('ch1');
expect(fetched[0].autotranslationDisabled).toBe(false);
});
it('should exclude channels that have autotranslation disabled at channel level', async () => {
const channel1 = TestHelper.fakeChannel({id: 'ch1', team_id: 'team1', autotranslation: false});
const myCh1 = TestHelper.fakeChannelMember({id: 'ch1', channel_id: 'ch1', autotranslation_disabled: false});
await operator.handleChannel({channels: [channel1], prepareRecordsOnly: false});
await operator.handleMyChannel({channels: [channel1], myChannels: [myCh1], prepareRecordsOnly: false});
const result = queryMyChannelsWithAutotranslation(database);
const fetched = await result.fetch();
expect(fetched.length).toBe(0);
});
it('should return empty when no myChannels have autotranslation disabled false', async () => {
const channel1 = TestHelper.fakeChannel({id: 'ch1', team_id: 'team1', autotranslation: true});
const myCh1 = TestHelper.fakeChannelMember({id: 'ch1', channel_id: 'ch1', autotranslation_disabled: true});
await operator.handleChannel({channels: [channel1], prepareRecordsOnly: false});
await operator.handleMyChannel({channels: [channel1], myChannels: [myCh1], prepareRecordsOnly: false});
const result = queryMyChannelsWithAutotranslation(database);
const fetched = await result.fetch();
expect(fetched.length).toBe(0);
});
});
describe('observeChannelAutotranslation', () => {
const channelId = 'ch_observe';
it('should emit true when EnableAutoTranslation is true and channel has autotranslation', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'true'},
{id: 'RestrictDMAndGMAutotranslation', value: 'false'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const channel = TestHelper.fakeChannel({id: channelId, team_id: 'team1', autotranslation: true});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
const subscriptionNext = jest.fn();
const result = observeChannelAutotranslation(database, channelId);
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(true);
});
it('should emit false when EnableAutoTranslation is false', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'false'},
{id: 'RestrictDMAndGMAutotranslation', value: 'false'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const channel = TestHelper.fakeChannel({id: channelId, team_id: 'team1', autotranslation: true});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
const subscriptionNext = jest.fn();
const result = observeChannelAutotranslation(database, channelId);
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(false);
});
it('should emit false when channel has autotranslation false', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'true'},
{id: 'RestrictDMAndGMAutotranslation', value: 'false'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const channel = TestHelper.fakeChannel({id: channelId, team_id: 'team1', autotranslation: false});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
const subscriptionNext = jest.fn();
const result = observeChannelAutotranslation(database, channelId);
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(false);
});
it('should emit false when channel is not found', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'true'},
{id: 'RestrictDMAndGMAutotranslation', value: 'false'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const subscriptionNext = jest.fn();
const result = observeChannelAutotranslation(database, 'nonexistent');
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(false);
});
it('should emit false when channel is DM and RestrictDMAndGMAutotranslation is true', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'true'},
{id: 'RestrictDMAndGMAutotranslation', value: 'true'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const channel = TestHelper.fakeChannel({
id: channelId,
type: General.DM_CHANNEL,
autotranslation: true,
});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
const subscriptionNext = jest.fn();
const result = observeChannelAutotranslation(database, channelId);
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(false);
});
it('should emit true when channel is DM and RestrictDMAndGMAutotranslation is false', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'true'},
{id: 'RestrictDMAndGMAutotranslation', value: 'false'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const channel = TestHelper.fakeChannel({
id: channelId,
type: General.DM_CHANNEL,
autotranslation: true,
});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
const subscriptionNext = jest.fn();
const result = observeChannelAutotranslation(database, channelId);
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(true);
});
});
describe('observeIsChannelAutotranslated', () => {
const channelId = 'ch_is_autotranslated';
it('should emit true when config and channel has autotranslation true and myChannel has autotranslation disabled false', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'true'},
{id: 'RestrictDMAndGMAutotranslation', value: 'false'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const channel = TestHelper.fakeChannel({id: channelId, team_id: 'team1', autotranslation: true});
const myChannel = TestHelper.fakeChannelMember({id: channelId, channel_id: channelId, autotranslation_disabled: false});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
await operator.handleMyChannel({channels: [channel], myChannels: [myChannel], prepareRecordsOnly: false});
const subscriptionNext = jest.fn();
const result = observeIsChannelAutotranslated(database, channelId);
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(true);
});
it('should emit false when channel autotranslation is false', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'true'},
{id: 'RestrictDMAndGMAutotranslation', value: 'false'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const channel = TestHelper.fakeChannel({id: channelId, team_id: 'team1', autotranslation: false});
const myChannel = TestHelper.fakeChannelMember({id: channelId, channel_id: channelId, autotranslation_disabled: false});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
await operator.handleMyChannel({channels: [channel], myChannels: [myChannel], prepareRecordsOnly: false});
const subscriptionNext = jest.fn();
const result = observeIsChannelAutotranslated(database, channelId);
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(false);
});
it('should emit false when myChannel autotranslation disabled is true', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'true'},
{id: 'RestrictDMAndGMAutotranslation', value: 'false'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const channel = TestHelper.fakeChannel({id: channelId, team_id: 'team1', autotranslation: true});
const myChannel = TestHelper.fakeChannelMember({id: channelId, channel_id: channelId, autotranslation_disabled: true});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
await operator.handleMyChannel({channels: [channel], myChannels: [myChannel], prepareRecordsOnly: false});
const subscriptionNext = jest.fn();
const result = observeIsChannelAutotranslated(database, channelId);
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(false);
});
it('should emit false when channel is DM and RestrictDMAndGMAutotranslation is true', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'true'},
{id: 'RestrictDMAndGMAutotranslation', value: 'true'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const channel = TestHelper.fakeChannel({
id: channelId,
type: General.DM_CHANNEL,
autotranslation: true,
});
const myChannel = TestHelper.fakeChannelMember({id: channelId, channel_id: channelId, autotranslation_disabled: false});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
await operator.handleMyChannel({channels: [channel], myChannels: [myChannel], prepareRecordsOnly: false});
const subscriptionNext = jest.fn();
const result = observeIsChannelAutotranslated(database, channelId);
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(false);
});
it('should emit true when channel is DM and RestrictDMAndGMAutotranslation is false', async () => {
await operator.handleConfigs({
configs: [
{id: 'EnableAutoTranslation', value: 'true'},
{id: 'RestrictDMAndGMAutotranslation', value: 'false'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});
const channel = TestHelper.fakeChannel({
id: channelId,
type: General.DM_CHANNEL,
autotranslation: true,
});
const myChannel = TestHelper.fakeChannelMember({id: channelId, channel_id: channelId, autotranslation_disabled: false});
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
await operator.handleMyChannel({channels: [channel], myChannels: [myChannel], prepareRecordsOnly: false});
const subscriptionNext = jest.fn();
const result = observeIsChannelAutotranslated(database, channelId);
result.subscribe({next: subscriptionNext});
expect(subscriptionNext).toHaveBeenCalledWith(true);
});
});
});