diff --git a/app/actions/local/systems.test.ts b/app/actions/local/systems.test.ts new file mode 100644 index 000000000..6ac807916 --- /dev/null +++ b/app/actions/local/systems.test.ts @@ -0,0 +1,174 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import Database from '@nozbe/watermelondb/Database'; + +import {ActionType} from '@app/constants'; +import {SYSTEM_IDENTIFIERS} from '@app/constants/database'; +import DatabaseManager from '@database/manager'; +import TestHelper from '@test/test_helper'; + +import { + storeConfig, + storeConfigAndLicense, + storeDataRetentionPolicies, + updateLastDataRetentionRun, + dataRetentionCleanup, + setLastServerVersionCheck, + setGlobalThreadsTab, + dismissAnnouncement, +} from './systems'; + +import type {DataRetentionPoliciesRequest} from '@actions/remote/systems'; +import type ServerDataOperator from '@database/operator/server_data_operator'; +import type SystemModel from '@typings/database/models/servers/system'; + +const serverUrl = 'baseHandler.test.com'; +let operator: ServerDataOperator; + +jest.mock('@init/credentials', () => { + const original = jest.requireActual('@init/credentials'); + return { + ...original, + getServerCredentials: jest.fn(async (url: string) => ({serverUrl: url})), + }; +}); + +beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; +}); + +afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); +}); + +describe('storeConfigAndLicense', () => { + it('handle not found database - storeConfig', async () => { + const models = await storeConfig('foo', {} 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(); + expect(models.length).toBe(0); + }); + + it('base case', async () => { + const models = await storeConfigAndLicense(serverUrl, {AboutLink: 'link'} as ClientConfig, {Announcement: 'test'} as ClientLicense); + expect(models).toBeDefined(); + expect(models.length).toBe(1); // config + }); +}); + +describe('dataRetention', () => { + it('handle not found database - storeDataRetentionPolicies', async () => { + const models = await storeDataRetentionPolicies('foo', {} as DataRetentionPoliciesRequest); + expect(models).toBeDefined(); + expect(models.length).toBe(0); + }); + + it('base case - storeDataRetentionPolicies', async () => { + const models = await storeDataRetentionPolicies(serverUrl, {globalPolicy: {} as GlobalDataRetentionPolicy, teamPolicies: [], channelPolicies: []} 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(); + }); + + it('base case - updateLastDataRetentionRun', async () => { + const models = await updateLastDataRetentionRun(serverUrl, 10) 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(); + }); + + it('rentention off - dataRetentionCleanup', async () => { + const post = {...TestHelper.fakePost('channelid1'), id: 'postid', create_at: 1}; + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [post], + prepareRecordsOnly: false, + }); + + const spy = jest.spyOn(Database.prototype, 'unsafeVacuum').mockImplementation(jest.fn()); + const {error} = await dataRetentionCleanup(serverUrl); + expect(error).toBeDefined(); // unsafeExecute loki error + spy.mockRestore(); + }); + + it('retention on - 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.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 spy = jest.spyOn(Database.prototype, 'unsafeVacuum').mockImplementation(jest.fn()); + const {error} = await dataRetentionCleanup(serverUrl); + expect(error).toBeDefined(); // LokiJSAdapter doesn't support unsafeSqlQuery + spy.mockRestore(); + }); +}); + +describe('setLastServerVersionCheck', () => { + it('handle not found database', async () => { + const {error} = await setLastServerVersionCheck('foo'); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + const {error} = await setLastServerVersionCheck(serverUrl); + expect(error).toBeUndefined(); + }); +}); + +describe('setGlobalThreadsTab', () => { + it('handle not found database', async () => { + const {error} = await setGlobalThreadsTab('foo', 'all'); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + const {error} = await setGlobalThreadsTab(serverUrl, 'all'); + expect(error).toBeUndefined(); + }); +}); + +describe('dismissAnnouncement', () => { + it('handle not found database', async () => { + const {error} = await dismissAnnouncement('foo', 'text'); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + const {error} = await dismissAnnouncement(serverUrl, 'text'); + expect(error).toBeUndefined(); + }); +}); + diff --git a/app/actions/local/systems.ts b/app/actions/local/systems.ts index 3dd0a20da..ff3707a36 100644 --- a/app/actions/local/systems.ts +++ b/app/actions/local/systems.ts @@ -38,11 +38,12 @@ export async function storeConfigAndLicense(serverUrl: string, config: ClientCon await operator.handleSystem({systems, prepareRecordsOnly: false}); } - await storeConfig(serverUrl, config); + return await storeConfig(serverUrl, config); } } catch (error) { logError('An error occurred while saving config & license', error); } + return []; } export async function storeConfig(serverUrl: string, config: ClientConfig | undefined, prepareRecordsOnly = false) { @@ -274,8 +275,10 @@ export async function setLastServerVersionCheck(serverUrl: string, reset = false }], prepareRecordsOnly: false, }); + return {error: undefined}; } catch (error) { logError('setLastServerVersionCheck', error); + return {error}; } } @@ -289,8 +292,10 @@ export async function setGlobalThreadsTab(serverUrl: string, globalThreadsTab: G }], prepareRecordsOnly: false, }); + return {error: undefined}; } catch (error) { logError('setGlobalThreadsTab', error); + return {error}; } } @@ -298,7 +303,9 @@ export async function dismissAnnouncement(serverUrl: string, announcementText: s try { const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LAST_DISMISSED_BANNER, value: announcementText}], prepareRecordsOnly: false}); + return {error: undefined}; } catch (error) { logError('An error occurred while dismissing an announcement', error); + return {error}; } }