mattermost-mobile/app/actions/remote/scheduled_post.test.ts
Rahim Rahman 882097a668
feat(MM-63937): fetch initial team data only on entry (#8894)
* feat(MM-63936): add mark function in performance metrics manager
* feat(MM-63563): fetch initial team data ONLY on entry
* remove all performance mark()
* revert changes to processEntryModels
* using processNextTeam to download team in sequence
* separate restDeferredAppEntryActions into two parts
* remove console.log
* fixed issue with conflict post.id
* regression from fetchPostsForUnreadChannels
* extracted deferred functions into deferred.ts
* missing mock
* extract combineChannelsData to it's own function
* remove isDelete
* use logDebug vs logInfo
* add logDebug and logWarning in the mocking
* add combineChannelsData test
* revert the use of logDebug
* spelling errors
* undo logDebug changes
* moving fetchMissingDirectChannelsInfo into setTimeout saved quite a bit of time when it comes to TTI
* changes to accommodate other recent changes
* keep the channelsMap smaller by moving delete_at.
* using for loop instead of recursive
* making this function more readable
* add jsdoc
* changes based on review
* team will not get omitted from the sidebar if there is an error
* fix test as well
* added a new test
* remove comments on categories
* add categories to fix failing test
* fix failing test
2025-08-18 10:06:15 -06:00

271 lines
11 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {forceLogoutIfNecessary} from '@actions/remote/session';
import {ActionType} from '@constants';
import DatabaseManager from '@database/manager';
import NetworkManager from '@managers/network_manager';
import {getConfigValue, getCurrentTeamId, getLicense} from '@queries/servers/system';
import {logError} from '@utils/log';
import {createScheduledPost, deleteScheduledPost, fetchScheduledPosts, updateScheduledPost} from './scheduled_post';
import type ServerDataOperator from '@database/operator/server_data_operator';
jest.mock('@utils/log', () => ({
logDebug: jest.fn(),
logError: jest.fn(),
logWarning: jest.fn(),
}));
jest.mock('@actions/remote/session', () => ({
forceLogoutIfNecessary: jest.fn(),
}));
const serverUrl = 'baseHandler.test.com';
let operator: ServerDataOperator;
const user1 = {id: 'userid1', username: 'user1', email: 'user1@mattermost.com', roles: ''} as UserProfile;
const scheduledPost: ScheduledPost = {
id: 'scheduledPostId1',
root_id: '',
update_at: 0,
channel_id: 'channelid1',
message: 'Test message',
scheduled_at: Date.now() + 10000,
create_at: Date.now(),
user_id: 'userid1',
error_code: '',
};
const scheduledPostsResponse: FetchScheduledPostsResponse = {
directChannels: [],
bar: [
{
id: 'scheduled_post_id',
channel_id: 'channel_id',
root_id: '',
message: 'test scheduled post',
scheduled_at: 123,
user_id: 'user_id',
processed_at: 0,
update_at: 456,
create_at: 789,
error_code: '',
},
{
id: 'scheduled_post_id_2',
channel_id: 'channel_id',
root_id: '',
message: 'test scheduled post 2',
scheduled_at: 123,
user_id: 'user_id',
processed_at: 0,
update_at: 456,
create_at: 789,
error_code: '',
},
],
};
const throwFunc = () => {
throw Error('error');
};
const mockConnectionId = 'mock-connection-id';
const mockClient = {
createScheduledPost: jest.fn((post, connId) => ({...scheduledPost, connectionId: connId})),
updateScheduledPost: jest.fn(() => Promise.resolve(({...scheduledPost}))),
getScheduledPostsForTeam: jest.fn(() => Promise.resolve({...scheduledPostsResponse})),
deleteScheduledPost: jest.fn((scheduledPostId, connId) => {
const post = scheduledPostsResponse.bar.find((p) => p.id === scheduledPostId);
return Promise.resolve({...post, connectionId: connId});
}),
};
const mockWebSocketClient = {
getConnectionId: jest.fn(() => mockConnectionId),
};
jest.mock('@queries/servers/system', () => ({
getConfigValue: jest.fn(),
getLicense: jest.fn(),
getCurrentTeamId: jest.fn(),
getCurrentUserId: jest.fn(),
}));
jest.mock('@managers/websocket_manager', () => ({
getClient: jest.fn(() => mockWebSocketClient),
}));
jest.mock('@utils/scheduled_post', () => {
return {
isScheduledPostModel: jest.fn(() => false),
};
});
const mockedGetConfigValue = jest.mocked(getConfigValue);
const mockedGetLicense = jest.mocked(getLicense);
beforeAll(() => {
// @ts-ignore
NetworkManager.getClient = () => mockClient;
});
beforeEach(async () => {
await DatabaseManager.init([serverUrl]);
operator = DatabaseManager.getServerDatabaseAndOperator(serverUrl)!.operator;
});
afterEach(async () => {
await DatabaseManager.destroyServerDatabase(serverUrl);
});
describe('createScheduledPost', () => {
it('handle not found database', async () => {
const result = await createScheduledPost('foo', scheduledPost);
expect((result.error)).toBe('foo database not found');
expect(logError).toHaveBeenCalled();
expect(forceLogoutIfNecessary).toHaveBeenCalled();
});
it('base case', async () => {
await operator.handleUsers({users: [user1], prepareRecordsOnly: false});
const result = await createScheduledPost(serverUrl, scheduledPost);
expect(result.error).toBeUndefined();
expect(result.data).toBe(true);
expect(result!.response!.id).toBe(scheduledPost.id);
expect(mockClient.createScheduledPost).toHaveBeenCalledWith(scheduledPost, mockConnectionId);
});
it('request error', async () => {
const error = new Error('custom error');
mockClient.createScheduledPost.mockImplementationOnce(jest.fn(() => {
throw error;
}));
const result = await createScheduledPost(serverUrl, scheduledPost);
expect(result.error).toBe('custom error');
expect(logError).toHaveBeenCalledWith('error on createScheduledPost', error.message);
expect(forceLogoutIfNecessary).toHaveBeenCalledWith(serverUrl, error);
});
it('operator handling error', async () => {
const error = new Error('operator error');
await operator.handleUsers({users: [user1], prepareRecordsOnly: false});
jest.spyOn(operator, 'handleScheduledPosts').mockRejectedValueOnce(error);
const result = await createScheduledPost(serverUrl, scheduledPost);
expect(result.error).toBe('operator error');
expect(logError).toHaveBeenCalledWith('error on createScheduledPost', error.message);
expect(forceLogoutIfNecessary).toHaveBeenCalledWith(serverUrl, error);
});
});
describe('fetchScheduledPosts', () => {
it('handle database not found', async () => {
const {error} = await fetchScheduledPosts('foo', 'bar');
expect((error as Error).message).toEqual('foo database not found');
expect(logError).toHaveBeenCalled();
expect(forceLogoutIfNecessary).toHaveBeenCalled();
});
it('handle client error', async () => {
jest.spyOn(NetworkManager, 'getClient').mockImplementationOnce(throwFunc);
const result = await fetchScheduledPosts(serverUrl, 'bar');
expect(result.error).toBeTruthy();
});
it('handle scheduled post disabled', async () => {
const result = await fetchScheduledPosts(serverUrl, 'bar');
expect(result.scheduledPosts).toEqual([]);
expect(mockClient.getScheduledPostsForTeam).not.toHaveBeenCalled();
});
it('handle scheduled post disabled is no license', async () => {
mockedGetConfigValue.mockResolvedValueOnce('true');
mockedGetLicense.mockResolvedValueOnce({IsLicensed: 'false'} as ClientLicense);
const result = await fetchScheduledPosts(serverUrl, 'bar');
expect(result.scheduledPosts).toEqual([]);
expect(mockClient.getScheduledPostsForTeam).not.toHaveBeenCalled();
});
it('handle scheduled post enabled', async () => {
jest.mocked(getCurrentTeamId).mockResolvedValue('bar');
mockedGetConfigValue.mockResolvedValueOnce('true');
mockedGetLicense.mockResolvedValueOnce({IsLicensed: 'true'} as ClientLicense);
const spyHandleScheduledPosts = jest.spyOn(operator, 'handleScheduledPosts');
const result = await fetchScheduledPosts(serverUrl, 'bar');
expect(result.scheduledPosts).toEqual(scheduledPostsResponse.bar);
expect(spyHandleScheduledPosts).toHaveBeenCalledWith({
actionType: ActionType.SCHEDULED_POSTS.RECEIVED_ALL_SCHEDULED_POSTS,
scheduledPosts: scheduledPostsResponse.bar,
prepareRecordsOnly: false,
includeDirectChannelPosts: false,
});
});
});
describe('updateScheduledPost', () => {
it('handle not found database', async () => {
const result = await updateScheduledPost('foo', scheduledPost, 123) as unknown as {error: Error};
expect(result).toEqual({error: 'foo database not found'});
expect(logError).toHaveBeenCalled();
expect(forceLogoutIfNecessary).toHaveBeenCalled();
});
it('base case', async () => {
await operator.handleUsers({users: [user1], prepareRecordsOnly: false});
const result = await updateScheduledPost(serverUrl, scheduledPost, 123);
expect(result.error).toBeUndefined();
expect(result.scheduledPost).toEqual(scheduledPost);
});
it('request error', async () => {
const error = new Error('custom error');
mockClient.updateScheduledPost = jest.fn().mockRejectedValueOnce(error);
const result = await updateScheduledPost(serverUrl, scheduledPost, 123);
expect(result.error).toBe('custom error');
expect(logError).toHaveBeenCalledWith('error on updateScheduledPost', error.message);
expect(forceLogoutIfNecessary).toHaveBeenCalledWith(serverUrl, error);
});
it('fetch only', async () => {
const spyHandleScheduledPosts = jest.spyOn(operator, 'handleScheduledPosts');
await operator.handleUsers({users: [user1], prepareRecordsOnly: false});
const mockResponse = {...scheduledPost, update_at: Date.now()};
mockClient.updateScheduledPost.mockImplementationOnce(() => Promise.resolve(mockResponse));
const result = await updateScheduledPost(serverUrl, scheduledPost, 123, true);
console.log(result.error);
expect(result.error).toBeUndefined();
expect(result.scheduledPost).toEqual(mockResponse);
expect(spyHandleScheduledPosts).not.toHaveBeenCalled();
});
});
describe('deleteScheduledPost', () => {
it('handle database not found', async () => {
const {error} = await deleteScheduledPost('foo', 'scheduled_post_id');
expect((error as Error).message).toBe('foo database not found');
expect(logError).toHaveBeenCalled();
expect(forceLogoutIfNecessary).toHaveBeenCalled();
});
it('handle client error', async () => {
jest.spyOn(NetworkManager, 'getClient').mockImplementationOnce(throwFunc);
const result = await deleteScheduledPost(serverUrl, 'scheduled_post_id');
expect(result.error).toBeTruthy();
});
it('handle scheduled post enabled', async () => {
const spyHandleScheduledPosts = jest.spyOn(operator, 'handleScheduledPosts');
const result = await deleteScheduledPost(serverUrl, 'scheduled_post_id');
expect(result.scheduledPost).toEqual({...scheduledPostsResponse.bar[0], connectionId: mockConnectionId});
expect(spyHandleScheduledPosts).toHaveBeenCalledWith({
actionType: ActionType.SCHEDULED_POSTS.DELETE_SCHEDULED_POST,
scheduledPosts: [{...scheduledPostsResponse.bar[0], connectionId: mockConnectionId}],
prepareRecordsOnly: false,
});
expect(mockClient.deleteScheduledPost).toHaveBeenCalledWith('scheduled_post_id', mockConnectionId);
});
});