mattermost-mobile/app/actions/local/team.test.ts
Daniel Espino García 2455ecf39f
Remove playbooks on delete channel (#8994)
Co-authored-by: Mattermost Build <build@mattermost.com>
2025-07-17 11:54:14 +02:00

400 lines
17 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import DatabaseManager from '@database/manager';
import ServerDataOperator from '@database/operator/server_data_operator';
import {getMyTeamById, getTeamById, getTeamSearchHistoryById, prepareDeleteTeam, removeTeamFromTeamHistory} from '@queries/servers/team';
import {logError} from '@utils/log';
import {queryTeamSearchHistoryByTeamId} from '../../queries/servers/team';
import {addSearchToTeamSearchHistory, removeSearchFromTeamSearchHistory, removeUserFromTeam, MAX_TEAM_SEARCHES} from './team';
jest.mock('@database/manager');
jest.mock('@queries/servers/team');
jest.mock('@utils/log');
let operator: ServerDataOperator;
const serverUrl = 'baseHandler.test.com';
const teamId = 'teamId123';
const database = {
write: jest.fn(async (callback) => callback()),
};
describe('removeSearchFromTeamSearchHistory', () => {
const searchId = 'searchId123';
beforeEach(async () => {
jest.clearAllMocks();
await DatabaseManager.init([serverUrl]);
operator = DatabaseManager.serverDatabases[serverUrl]!.operator;
DatabaseManager.getServerDatabaseAndOperator = jest.fn();
(DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockReturnValue({
database,
operator,
});
});
afterEach(async () => {
await DatabaseManager.destroyServerDatabase(serverUrl);
});
it('should remove search from team search history successfully', async () => {
const teamSearch = {
destroyPermanently: jest.fn(),
};
(getTeamSearchHistoryById as jest.Mock).mockResolvedValue(teamSearch);
const result = await removeSearchFromTeamSearchHistory(serverUrl, searchId);
expect(getTeamSearchHistoryById).toHaveBeenCalledWith(database, searchId);
expect(database.write).toHaveBeenCalledWith(expect.any(Function));
expect(database.write).toHaveBeenCalledTimes(1);
expect(teamSearch.destroyPermanently).toHaveBeenCalledTimes(1);
expect(result).toEqual({teamSearch});
});
it('should handle case when team search history is not found', async () => {
(getTeamSearchHistoryById as jest.Mock).mockResolvedValue(undefined);
const result = await removeSearchFromTeamSearchHistory(serverUrl, searchId);
expect(getTeamSearchHistoryById).toHaveBeenCalledWith(database, searchId);
expect(database.write).not.toHaveBeenCalled();
expect(result).toEqual({teamSearch: undefined});
});
it('should handle errors and log them', async () => {
const error = new Error('Test error');
(getTeamSearchHistoryById as jest.Mock).mockRejectedValue(error);
const result = await removeSearchFromTeamSearchHistory(serverUrl, searchId);
expect(getTeamSearchHistoryById).toHaveBeenCalledWith(database, searchId);
expect(logError).toHaveBeenCalledWith('Failed removeSearchFromTeamSearchHistory', error);
expect(result).toEqual({error});
});
it('should handle database write errors', async () => {
const teamSearch = {
destroyPermanently: jest.fn(),
};
const writeError = new Error('Database write error');
(getTeamSearchHistoryById as jest.Mock).mockResolvedValue(teamSearch);
(database.write as jest.Mock).mockRejectedValue(writeError);
const result = await removeSearchFromTeamSearchHistory(serverUrl, searchId);
expect(getTeamSearchHistoryById).toHaveBeenCalledWith(database, searchId);
expect(database.write).toHaveBeenCalledWith(expect.any(Function));
expect(logError).toHaveBeenCalledWith('Failed removeSearchFromTeamSearchHistory', writeError);
expect(result).toEqual({error: writeError});
});
});
describe('removeUserFromTeam', () => {
beforeEach(async () => {
jest.clearAllMocks();
await DatabaseManager.init([serverUrl]);
operator = DatabaseManager.serverDatabases[serverUrl]!.operator;
DatabaseManager.getServerDatabaseAndOperator = jest.fn();
(DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockReturnValue({
database,
operator,
});
operator.batchRecords = jest.fn();
});
afterEach(async () => {
await DatabaseManager.destroyServerDatabase(serverUrl);
});
it('should remove user from team successfully', async () => {
const myTeam = {id: 'myTeamId'};
const team = {id: 'myTeamId'};
const preparedModels = [
{id: 'model1', _preparedState: 'markAsDeleted'},
{id: 'model2', _preparedState: 'markAsDeleted'},
];
const systemModels = [
{id: 'systemModel1', _preparedState: 'update', value: []},
];
(getMyTeamById as jest.Mock).mockResolvedValue(myTeam);
(getTeamById as jest.Mock).mockResolvedValue(team);
(prepareDeleteTeam as jest.Mock).mockResolvedValue(preparedModels);
(removeTeamFromTeamHistory as jest.Mock).mockResolvedValue(systemModels);
const result = await removeUserFromTeam(serverUrl, team.id);
expect(getMyTeamById).toHaveBeenCalledWith(database, team.id);
expect(getTeamById).toHaveBeenCalledWith(database, myTeam.id);
expect(prepareDeleteTeam).toHaveBeenCalledWith(serverUrl, team);
expect(removeTeamFromTeamHistory).toHaveBeenCalledWith(operator, team.id, true);
expect(operator.batchRecords).toHaveBeenCalledWith(preparedModels, 'removeUserFromTeam');
// Verify team id is not present
systemModels.forEach((model) => {
expect(model.value).not.toContain(team.id);
});
// Check if preparedModels contain systemModels
expect(preparedModels).toEqual(expect.arrayContaining(systemModels));
expect(result).toEqual({error: undefined});
});
it('should handle errors during database write', async () => {
const myTeam = {id: 'myTeamId'};
const team = {id: 'teamId'};
const preparedModels = [
{id: 'model1', _preparedState: 'markAsDeleted'},
{id: 'model2', _preparedState: 'markAsDeleted'},
];
const systemModels = [
{id: 'systemModel1', _preparedState: 'update', value: [team.id]},
];
const writeError = new Error('Database write error');
(getMyTeamById as jest.Mock).mockResolvedValue(myTeam);
(getTeamById as jest.Mock).mockResolvedValue(team);
(prepareDeleteTeam as jest.Mock).mockResolvedValue(preparedModels);
(removeTeamFromTeamHistory as jest.Mock).mockResolvedValue(systemModels);
(operator.batchRecords as jest.Mock).mockRejectedValue(writeError);
const result = await removeUserFromTeam(serverUrl, teamId);
expect(getMyTeamById).toHaveBeenCalledWith(database, teamId);
expect(getTeamById).toHaveBeenCalledWith(database, myTeam.id);
expect(prepareDeleteTeam).toHaveBeenCalledWith(serverUrl, team);
expect(removeTeamFromTeamHistory).toHaveBeenCalledWith(operator, team.id, true);
expect(operator.batchRecords).toHaveBeenCalledWith(preparedModels, 'removeUserFromTeam');
expect(logError).toHaveBeenCalledWith('Failed removeUserFromTeam', writeError);
// Verify team id is present
systemModels.forEach((model) => {
expect(model.value).toContain(team.id);
});
// Check if preparedModels contain systemModels
expect(preparedModels).toEqual(expect.arrayContaining(systemModels));
expect(result).toEqual({error: writeError});
});
it('should handle case when myTeam is not found', async () => {
(getMyTeamById as jest.Mock).mockResolvedValue(null);
const result = await removeUserFromTeam(serverUrl, teamId);
expect(getMyTeamById).toHaveBeenCalledWith(database, teamId);
expect(getTeamById).not.toHaveBeenCalled();
expect(prepareDeleteTeam).not.toHaveBeenCalledWith(serverUrl, null as any);
expect(removeTeamFromTeamHistory).not.toHaveBeenCalled();
expect(operator.batchRecords).not.toHaveBeenCalled();
expect(result).toEqual({error: undefined});
});
it('should handle case when team is not found', async () => {
const myTeam = {id: 'myTeamId'};
(getMyTeamById as jest.Mock).mockResolvedValue(myTeam);
(getTeamById as jest.Mock).mockResolvedValue(null);
const result = await removeUserFromTeam(serverUrl, teamId);
expect(getMyTeamById).toHaveBeenCalledWith(database, teamId);
expect(getTeamById).toHaveBeenCalledWith(database, myTeam.id);
expect(result.error).toEqual(new Error('Team not found'));
});
it('should handle errors and log them', async () => {
const error = new Error('Test error');
(DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockImplementation(() => {
throw error;
});
const result = await removeUserFromTeam(serverUrl, teamId);
expect(DatabaseManager.getServerDatabaseAndOperator).toHaveBeenCalled();
expect(logError).toHaveBeenCalledWith('Failed removeUserFromTeam', error);
expect(result).toEqual({error});
});
it('should handle when removeTeamFromTeamHistory returns undefined or empty array', async () => {
const myTeam = {id: 'myTeamId'};
const team = {id: 'teamId'};
const preparedModels = [
{id: 'model1', _preparedState: 'markAsDeleted'},
{id: 'model2', _preparedState: 'markAsDeleted'},
];
(getMyTeamById as jest.Mock).mockResolvedValue(myTeam);
(getTeamById as jest.Mock).mockResolvedValue(team);
(prepareDeleteTeam as jest.Mock).mockResolvedValue(preparedModels);
(removeTeamFromTeamHistory as jest.Mock).mockResolvedValue(undefined);
const result = await removeUserFromTeam(serverUrl, teamId);
expect(getMyTeamById).toHaveBeenCalledWith(database, teamId);
expect(getTeamById).toHaveBeenCalledWith(database, myTeam.id);
expect(prepareDeleteTeam).toHaveBeenCalledWith(serverUrl, team);
expect(removeTeamFromTeamHistory).toHaveBeenCalledWith(operator, team.id, true);
expect(operator.batchRecords).toHaveBeenCalledWith(preparedModels, 'removeUserFromTeam');
expect(result).toEqual({error: undefined});
});
it('should handle when models length is 0', async () => {
const myTeam = {id: 'myTeamId'};
const team = {id: 'teamId'};
const models: never[] = [];
(getMyTeamById as jest.Mock).mockResolvedValue(myTeam);
(getTeamById as jest.Mock).mockResolvedValue(team);
(prepareDeleteTeam as jest.Mock).mockResolvedValue([]);
(removeTeamFromTeamHistory as jest.Mock).mockResolvedValue(models);
const result = await removeUserFromTeam(serverUrl, teamId);
expect(getMyTeamById).toHaveBeenCalledWith(database, teamId);
expect(getTeamById).toHaveBeenCalledWith(database, myTeam.id);
expect(prepareDeleteTeam).toHaveBeenCalledWith(serverUrl, team);
expect(removeTeamFromTeamHistory).toHaveBeenCalledWith(operator, team.id, true);
expect(operator.batchRecords).not.toHaveBeenCalled();
expect(result).toEqual({error: undefined});
});
});
describe('addSearchToTeamSearchHistory', () => {
const terms = 'search terms';
beforeEach(async () => {
jest.clearAllMocks();
await DatabaseManager.init([serverUrl]);
operator = DatabaseManager.serverDatabases[serverUrl]!.operator;
DatabaseManager.getServerDatabaseAndOperator = jest.fn();
(DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockReturnValue({
database,
operator,
});
operator.handleTeamSearchHistory = jest.fn();
operator.batchRecords = jest.fn();
jest.spyOn(Date, 'now').mockImplementation(() => 1724657520366);
});
afterEach(async () => {
await DatabaseManager.destroyServerDatabase(serverUrl);
});
it('should add search to team search history successfully', async () => {
const newSearch = {
created_at: Date.now(),
display_term: terms,
term: terms,
team_id: teamId,
};
const searchModel = {id: 'searchModelId', _raw: {_changed: 'created_at'}};
(operator.handleTeamSearchHistory as jest.Mock).mockResolvedValue([searchModel]);
const result = await addSearchToTeamSearchHistory(serverUrl, teamId, terms);
expect(operator.handleTeamSearchHistory).toHaveBeenCalledWith({
teamSearchHistories: [newSearch],
prepareRecordsOnly: true,
});
expect(operator.batchRecords).toHaveBeenCalledWith([searchModel], 'addSearchToTeamHistory');
expect(result).toEqual({searchModel});
});
it('should delete the oldest entry if team search history exceeds the limit', async () => {
const newSearch = {
created_at: Date.now(),
display_term: terms,
term: terms,
team_id: teamId,
};
const searchModel = {id: 'searchModelId', _raw: {_changed: ''}};
const oldSearchModel = {id: 'oldSearchModelId', prepareDestroyPermanently: jest.fn().mockReturnValue(undefined)};
const teamSearchHistory = new Array(MAX_TEAM_SEARCHES + 1).fill(oldSearchModel);
(operator.handleTeamSearchHistory as jest.Mock).mockResolvedValue([searchModel]);
(queryTeamSearchHistoryByTeamId as jest.Mock).mockReturnValue({
fetch: jest.fn().mockResolvedValue(teamSearchHistory),
});
const result = await addSearchToTeamSearchHistory(serverUrl, teamId, terms);
expect(operator.handleTeamSearchHistory).toHaveBeenCalledWith({
teamSearchHistories: [newSearch],
prepareRecordsOnly: true,
});
expect(queryTeamSearchHistoryByTeamId).toHaveBeenCalledWith(database, teamId);
expect(oldSearchModel.prepareDestroyPermanently).toHaveBeenCalledTimes(1);
expect(operator.batchRecords).toHaveBeenCalledWith([searchModel], 'addSearchToTeamHistory');
expect(result).toEqual({searchModel});
});
it('should handle case when searchModel._raw._changed is "created_at"', async () => {
const newSearch = {
created_at: Date.now(),
display_term: terms,
term: terms,
team_id: teamId,
};
const searchModel = {id: 'searchModelId', _raw: {_changed: 'created_at'}};
(operator.handleTeamSearchHistory as jest.Mock).mockResolvedValue([searchModel]);
const result = await addSearchToTeamSearchHistory(serverUrl, teamId, terms);
expect(operator.handleTeamSearchHistory).toHaveBeenCalledWith({
teamSearchHistories: [newSearch],
prepareRecordsOnly: true,
});
expect(queryTeamSearchHistoryByTeamId).not.toHaveBeenCalled();
expect(operator.batchRecords).toHaveBeenCalledWith([searchModel], 'addSearchToTeamHistory');
expect(result).toEqual({searchModel});
});
it('should handle case when teamSearchHistory length is less than or equal to MAX_TEAM_SEARCHES', async () => {
const newSearch = {
created_at: Date.now(),
display_term: terms,
term: terms,
team_id: teamId,
};
const searchModel = {id: 'searchModelId', _raw: {_changed: ''}};
const oldSearchModel = {id: 'oldSearchModelId', prepareDestroyPermanently: jest.fn().mockReturnValue(undefined)};
const teamSearchHistory = new Array(MAX_TEAM_SEARCHES).fill(searchModel);
(operator.handleTeamSearchHistory as jest.Mock).mockResolvedValue([searchModel]);
(queryTeamSearchHistoryByTeamId as jest.Mock).mockReturnValue({
fetch: jest.fn().mockResolvedValue(teamSearchHistory),
});
const result = await addSearchToTeamSearchHistory(serverUrl, teamId, terms);
expect(operator.handleTeamSearchHistory).toHaveBeenCalledWith({
teamSearchHistories: [newSearch],
prepareRecordsOnly: true,
});
expect(queryTeamSearchHistoryByTeamId).toHaveBeenCalledWith(database, teamId);
expect(operator.batchRecords).toHaveBeenCalledWith([searchModel], 'addSearchToTeamHistory');
expect(result).toEqual({searchModel});
expect(oldSearchModel.prepareDestroyPermanently).not.toHaveBeenCalled();
});
it('should handle errors and log them', async () => {
const error = new Error('Test error');
(operator.handleTeamSearchHistory as jest.Mock).mockRejectedValue(error);
const result = await addSearchToTeamSearchHistory(serverUrl, teamId, terms);
expect(operator.handleTeamSearchHistory).toHaveBeenCalledWith({
teamSearchHistories: [{created_at: expect.any(Number), display_term: terms, term: terms, team_id: teamId}],
prepareRecordsOnly: true,
});
expect(logError).toHaveBeenCalledWith('Failed addSearchToTeamSearchHistory', error);
expect(result).toEqual({error});
});
});