mattermost-mobile/app/managers/performance_metrics_manager/index.test.ts
Daniel Espino García 5f01f9e9af
Add performance metrics to the app (#7953)
* Add performance metrics to the app

* add batcher and improve handling

* Add tests

* Fix test

* Address feedback

* Address feedback

* Address feedback

* update podfile
2024-06-12 09:45:27 +02:00

209 lines
7.9 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import performance from 'react-native-performance';
import {mockApiClient} from '@test/mock_api_client';
import TestHelper from '@test/test_helper';
import NetworkManager from '../network_manager';
import {getBaseReportRequest} from './test_utils';
import PerformanceMetricsManager from '.';
const TEST_EPOCH = 1577836800000;
jest.mock('@utils/log', () => ({
logDebug: () => '',
}));
performance.timeOrigin = TEST_EPOCH;
describe('load metrics', () => {
const serverUrl = 'http://www.someserverurl.com/';
const expectedUrl = `${serverUrl}/api/v4/client_perf`;
const measure: PerformanceReportMeasure = {
metric: 'mobile_load',
timestamp: TEST_EPOCH + 61000,
value: 61000,
};
beforeEach(async () => {
NetworkManager.createClient(serverUrl);
const {operator} = await TestHelper.setupServerDatabase(serverUrl);
await operator.handleConfigs({configs: [{id: 'EnableClientMetrics', value: 'true'}], configsToDelete: [], prepareRecordsOnly: false});
jest.useFakeTimers({doNotFake: [
'cancelAnimationFrame',
'cancelIdleCallback',
'clearImmediate',
'clearInterval',
'clearTimeout',
'hrtime',
'nextTick',
'queueMicrotask',
'requestAnimationFrame',
'requestIdleCallback',
'setImmediate',
'setInterval',
]}).setSystemTime(new Date(TEST_EPOCH));
});
afterEach(async () => {
jest.useRealTimers();
NetworkManager.invalidateClient(serverUrl);
await TestHelper.tearDown();
});
it('only load on target', async () => {
performance.mark('nativeLaunchStart');
const expectedRequest = getBaseReportRequest(measure.timestamp, measure.timestamp + 1);
expectedRequest.body.histograms = [measure];
PerformanceMetricsManager.setLoadTarget('CHANNEL');
PerformanceMetricsManager.finishLoad('HOME', serverUrl);
await TestHelper.tick();
jest.advanceTimersByTime(61000);
await TestHelper.tick();
expect(mockApiClient.post).not.toHaveBeenCalled();
PerformanceMetricsManager.finishLoad('CHANNEL', serverUrl);
await TestHelper.tick();
jest.advanceTimersByTime(61000);
await TestHelper.tick();
expect(mockApiClient.post).toHaveBeenCalledWith(expectedUrl, expectedRequest);
});
});
describe('other metrics', () => {
const serverUrl1 = 'http://www.someserverurl.com/';
const expectedUrl1 = `${serverUrl1}/api/v4/client_perf`;
const serverUrl2 = 'http://www.otherserverurl.com/';
const expectedUrl2 = `${serverUrl2}/api/v4/client_perf`;
const measure1: PerformanceReportMeasure = {
metric: 'mobile_channel_switch',
timestamp: TEST_EPOCH + 100,
value: 100,
};
const measure2: PerformanceReportMeasure = {
metric: 'mobile_team_switch',
timestamp: TEST_EPOCH + 150 + 50,
value: 150,
};
beforeEach(async () => {
NetworkManager.createClient(serverUrl1);
NetworkManager.createClient(serverUrl2);
const {operator: operator1} = await TestHelper.setupServerDatabase(serverUrl1);
const {operator: operator2} = await TestHelper.setupServerDatabase(serverUrl2);
await operator1.handleConfigs({configs: [{id: 'EnableClientMetrics', value: 'true'}], configsToDelete: [], prepareRecordsOnly: false});
await operator2.handleConfigs({configs: [{id: 'EnableClientMetrics', value: 'true'}], configsToDelete: [], prepareRecordsOnly: false});
jest.useFakeTimers({doNotFake: [
'cancelAnimationFrame',
'cancelIdleCallback',
'clearImmediate',
'clearInterval',
'clearTimeout',
'hrtime',
'nextTick',
'queueMicrotask',
'requestAnimationFrame',
'requestIdleCallback',
'setImmediate',
'setInterval',
]}).setSystemTime(new Date(TEST_EPOCH));
});
afterEach(async () => {
jest.useRealTimers();
NetworkManager.invalidateClient(serverUrl1);
NetworkManager.invalidateClient(serverUrl2);
await TestHelper.tearDown();
});
it('do not send metrics when we do not start them', async () => {
PerformanceMetricsManager.endMetric('mobile_channel_switch', serverUrl1);
jest.advanceTimersByTime(61000);
await TestHelper.tick();
expect(mockApiClient.post).not.toHaveBeenCalled();
});
it('send metric after it has been started', async () => {
const expectedRequest = getBaseReportRequest(measure1.timestamp, measure1.timestamp + 1);
expectedRequest.body.histograms = [measure1];
PerformanceMetricsManager.startMetric('mobile_channel_switch');
jest.advanceTimersByTime(100);
PerformanceMetricsManager.endMetric('mobile_channel_switch', serverUrl1);
await TestHelper.tick();
jest.advanceTimersByTime(61000);
await TestHelper.tick();
expect(mockApiClient.post).toHaveBeenCalledWith(expectedUrl1, expectedRequest);
});
it('a second end metric does not generate a second measure', async () => {
const expectedRequest = getBaseReportRequest(measure1.timestamp, measure1.timestamp + 1);
expectedRequest.body.histograms = [measure1];
PerformanceMetricsManager.startMetric('mobile_channel_switch');
jest.advanceTimersByTime(100);
PerformanceMetricsManager.endMetric('mobile_channel_switch', serverUrl1);
await TestHelper.tick();
jest.advanceTimersByTime(100);
PerformanceMetricsManager.endMetric('mobile_channel_switch', serverUrl1);
await TestHelper.tick();
jest.advanceTimersByTime(61000);
await TestHelper.tick();
expect(mockApiClient.post).toHaveBeenCalledWith(expectedUrl1, expectedRequest);
});
it('different metrics do not interfere', async () => {
const expectedRequest = getBaseReportRequest(measure1.timestamp, measure2.timestamp);
expectedRequest.body.histograms = [measure1, measure2];
PerformanceMetricsManager.startMetric('mobile_channel_switch');
jest.advanceTimersByTime(50);
PerformanceMetricsManager.startMetric('mobile_team_switch');
jest.advanceTimersByTime(50);
PerformanceMetricsManager.endMetric('mobile_channel_switch', serverUrl1);
await TestHelper.tick();
jest.advanceTimersByTime(100);
PerformanceMetricsManager.endMetric('mobile_team_switch', serverUrl1);
await TestHelper.tick();
jest.advanceTimersByTime(61000);
await TestHelper.tick();
expect(mockApiClient.post).toHaveBeenCalledWith(expectedUrl1, expectedRequest);
});
it('metrics to different servers do not interfere', async () => {
const expectedRequest1 = getBaseReportRequest(measure1.timestamp, measure1.timestamp + 1);
expectedRequest1.body.histograms = [measure1];
const expectedRequest2 = getBaseReportRequest(measure2.timestamp, measure2.timestamp + 1);
expectedRequest2.body.histograms = [measure2];
PerformanceMetricsManager.startMetric('mobile_channel_switch');
jest.advanceTimersByTime(50);
PerformanceMetricsManager.startMetric('mobile_team_switch');
jest.advanceTimersByTime(50);
PerformanceMetricsManager.endMetric('mobile_channel_switch', serverUrl1);
await TestHelper.tick();
jest.advanceTimersByTime(100);
PerformanceMetricsManager.endMetric('mobile_team_switch', serverUrl2);
await TestHelper.tick();
jest.advanceTimersByTime(61000);
await TestHelper.tick();
expect(mockApiClient.post).toHaveBeenCalledWith(expectedUrl1, expectedRequest1);
expect(mockApiClient.post).toHaveBeenCalledWith(expectedUrl2, expectedRequest2);
});
});