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: {
12 processAllRequests: jest.fn(),
13 clearAuthHeaders: jest.fn(),
18 describe('MetricSharedWorker', () => {
19 let worker: MetricSharedWorker;
20 let originalDateNow: () => number;
23 worker = new MetricSharedWorker();
24 originalDateNow = Date.now;
25 Date.now = jest.fn(() => 1000000);
30 Date.now = originalDateNow;
33 test('report should be called after 5 minutes', async () => {
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([
54 [UserAvailabilityTypes.coreFeatureError]: 'true',
55 [UserAvailabilityTypes.recoveredError]: 'false',
56 [UserAvailabilityTypes.handledError]: 'false',
57 [UserAvailabilityTypes.unhandledError]: 'false',
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);
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);
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);
93 test('setLocalUser() should update user information', () => {
94 const connectionId = 'test-connection';
95 const uid = 'test-uid';
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);
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();
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);
122 await worker.report([
127 [UserAvailabilityTypes.coreFeatureError]: 'true',
128 [UserAvailabilityTypes.recoveredError]: 'false',
129 [UserAvailabilityTypes.handledError]: 'false',
130 [UserAvailabilityTypes.unhandledError]: 'false',
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({
140 [UserAvailabilityTypes.coreFeatureError]: 'true',
141 [UserAvailabilityTypes.recoveredError]: 'false',
142 [UserAvailabilityTypes.handledError]: 'false',
143 [UserAvailabilityTypes.unhandledError]: 'false',
145 expect(metrics.processAllRequests).toHaveBeenCalled();
146 expect(metrics.clearAuthHeaders).toHaveBeenCalled();
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({
178 [UserAvailabilityTypes.coreFeatureError]: 'true',
179 [UserAvailabilityTypes.recoveredError]: 'false',
180 [UserAvailabilityTypes.handledError]: 'true',
181 [UserAvailabilityTypes.unhandledError]: 'false',
183 expect(metrics.processAllRequests).toHaveBeenCalled();
184 expect(metrics.clearAuthHeaders).toHaveBeenCalled();