[DRVWEB-4345] Flush reports on disconnect
[ProtonMail-WebClient.git] / applications / drive / src / app / utils / metrics / worker / shared-metrics.worker.test.ts
blob62b3684ef0a63ee612246d3de20fded49527fca3
1 import metrics from '@proton/metrics';
3 import { UserAvailabilityTypes } from '../types/userSuccessMetricsTypes';
4 import { MetricSharedWorker } from './shared-metrics.worker';
6 jest.mock('@proton/metrics', () => ({
7     setVersionHeaders: jest.fn(),
8     setAuthHeaders: jest.fn(),
9     drive_users_success_rate_total: {
10         increment: jest.fn(),
11     },
12     processAllRequests: jest.fn(),
13     clearAuthHeaders: jest.fn(),
14 }));
16 jest.useFakeTimers();
18 describe('MetricSharedWorker', () => {
19     let worker: MetricSharedWorker;
20     let originalDateNow: () => number;
22     beforeEach(() => {
23         worker = new MetricSharedWorker();
24         originalDateNow = Date.now;
25         Date.now = jest.fn(() => 1000000);
26     });
28     afterEach(() => {
29         jest.resetAllMocks();
30         Date.now = originalDateNow;
31     });
33     test('report should be called after 5 minutes', async () => {
34         worker.init();
36         const connectionId = 'test-connection';
37         worker.setAuthHeaders(connectionId, 'test-uid', 'test-token');
38         worker.setVersionHeaders(connectionId, 'test-client', '1.0.0');
39         worker.setLocalUser(connectionId, 'test-uid', 'paid');
40         worker.mark(connectionId, UserAvailabilityTypes.coreFeatureError);
42         const expectedUser = worker._getUsers().get(connectionId);
43         const reportSpy = jest.spyOn(worker, 'report').mockResolvedValue(undefined);
45         jest.advanceTimersByTime(5 * 60 * 1000);
46         await Promise.resolve();
48         expect(reportSpy).toHaveBeenCalled();
49         expect(reportSpy).toHaveBeenCalledWith([
50             {
51                 user: expectedUser,
52                 mark: {
53                     plan: 'paid',
54                     [UserAvailabilityTypes.coreFeatureError]: 'true',
55                     [UserAvailabilityTypes.recoveredError]: 'false',
56                     [UserAvailabilityTypes.handledError]: 'false',
57                     [UserAvailabilityTypes.unhandledError]: 'false',
58                 },
59             },
60         ]);
61     });
63     test('mark() should update status for a connection', () => {
64         const connectionId = 'test-connection';
65         worker.mark(connectionId, UserAvailabilityTypes.coreFeatureError);
66         expect(worker._getStatus().get(connectionId)?.get(UserAvailabilityTypes.coreFeatureError)).toBe(1000000);
67     });
69     test('setAuthHeaders() should update user information', () => {
70         const connectionId = 'test-connection';
71         const uid = 'test-uid';
72         const accessToken = 'test-token';
74         worker.setAuthHeaders(connectionId, uid, accessToken);
76         const userInfo = worker._getUsers().get(connectionId);
77         expect(userInfo?.get('uid')).toBe(uid);
78         expect(userInfo?.get('accessToken')).toBe(accessToken);
79     });
81     test('setVersionHeaders() should update user information', () => {
82         const connectionId = 'test-connection';
83         const clientID = 'test-client';
84         const appVersion = '1.0.0';
86         worker.setVersionHeaders(connectionId, clientID, appVersion);
88         const userInfo = worker._getUsers().get(connectionId);
89         expect(userInfo?.get('clientID')).toBe(clientID);
90         expect(userInfo?.get('appVersion')).toBe(appVersion);
91     });
93     test('setLocalUser() should update user information', () => {
94         const connectionId = 'test-connection';
95         const uid = 'test-uid';
96         const plan = 'paid';
98         worker.setLocalUser(connectionId, uid, plan);
100         const userInfo = worker._getUsers().get(connectionId);
101         expect(userInfo?.get('uid')).toBe(uid);
102         expect(userInfo?.get('plan')).toBe(plan);
103     });
105     test('disconnect() should remove user information', async () => {
106         const connectionId = 'test-connection';
107         worker.setLocalUser(connectionId, 'test-uid', 'paid');
108         await worker.disconnect(connectionId);
110         expect(worker._getUsers().has(connectionId)).toBeFalsy();
111     });
113     test('report() should call metrics functions with correct data', async () => {
114         const connectionId = 'test-connection';
115         worker.setAuthHeaders(connectionId, 'test-uid', 'test-token');
116         worker.setVersionHeaders(connectionId, 'test-client', '1.0.0');
117         worker.setLocalUser(connectionId, 'test-uid', 'paid');
118         worker.mark(connectionId, UserAvailabilityTypes.coreFeatureError);
120         const user = worker._getUsers().get(connectionId);
121         if (user) {
122             await worker.report([
123                 {
124                     user,
125                     mark: {
126                         plan: 'paid',
127                         [UserAvailabilityTypes.coreFeatureError]: 'true',
128                         [UserAvailabilityTypes.recoveredError]: 'false',
129                         [UserAvailabilityTypes.handledError]: 'false',
130                         [UserAvailabilityTypes.unhandledError]: 'false',
131                     },
132                 },
133             ]);
134         }
136         expect(metrics.setVersionHeaders).toHaveBeenCalledWith('test-client', '1.0.0');
137         expect(metrics.setAuthHeaders).toHaveBeenCalledWith('test-uid', 'test-token');
138         expect(metrics.drive_users_success_rate_total.increment).toHaveBeenCalledWith({
139             plan: 'paid',
140             [UserAvailabilityTypes.coreFeatureError]: 'true',
141             [UserAvailabilityTypes.recoveredError]: 'false',
142             [UserAvailabilityTypes.handledError]: 'false',
143             [UserAvailabilityTypes.unhandledError]: 'false',
144         });
145         expect(metrics.processAllRequests).toHaveBeenCalled();
146         expect(metrics.clearAuthHeaders).toHaveBeenCalled();
147     });
149     test('reports() should call report() with the metrics & correct data when special cases with same user in multiple connections', async () => {
150         // user in private app
151         const connectionId = 'test-connection';
152         worker.setAuthHeaders(connectionId, 'test-uid', 'test-token');
153         worker.setVersionHeaders(connectionId, 'test-client', '1.0.0');
154         worker.setLocalUser(connectionId, 'test-uid', 'free');
155         worker.mark(connectionId, UserAvailabilityTypes.coreFeatureError);
157         // same user (uid) in public app (error marked)
158         const connectionIdPublic = 'test-connection-public';
159         worker.setAuthHeaders(connectionIdPublic, 'test-uid', 'test-token');
160         worker.setVersionHeaders(connectionIdPublic, 'test-client', '1.0.0');
161         worker.setLocalUser(connectionIdPublic, 'test-uid', 'unknown');
162         worker.mark(connectionIdPublic, UserAvailabilityTypes.handledError);
164         // same user (uid) in public app (no error)
165         const connectionIdPublicAgain = 'test-connection-public-again';
166         worker.setAuthHeaders(connectionIdPublicAgain, 'test-uid');
167         worker.setVersionHeaders(connectionIdPublicAgain, 'test-client', '1.0.0');
168         worker.setLocalUser(connectionIdPublicAgain, 'test-uid', 'unknown');
170         await worker.reports();
172         expect(metrics.setVersionHeaders).toHaveBeenCalledWith('test-client', '1.0.0');
173         expect(metrics.setAuthHeaders).toHaveBeenCalledWith('test-uid', 'test-token');
174         // one single call for one user
175         expect(metrics.drive_users_success_rate_total.increment).toHaveBeenCalledTimes(1);
176         expect(metrics.drive_users_success_rate_total.increment).toHaveBeenCalledWith({
177             plan: 'free',
178             [UserAvailabilityTypes.coreFeatureError]: 'true',
179             [UserAvailabilityTypes.recoveredError]: 'false',
180             [UserAvailabilityTypes.handledError]: 'true',
181             [UserAvailabilityTypes.unhandledError]: 'false',
182         });
183         expect(metrics.processAllRequests).toHaveBeenCalled();
184         expect(metrics.clearAuthHeaders).toHaveBeenCalled();
185     });