Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / applications / drive / src / app / store / _crypto / integrityMetrics.test.ts
blob78a9257aea4e8c91f9cd61189b998974d1411abd
1 import { IntegrityMetrics, getAddressMatchingDefaultShare, getFileSize, getFromBefore2024 } from './integrityMetrics';
3 class IntegrityMetricsForTesting extends IntegrityMetrics {
4     setMetricsModule(metricsModule: any) {
5         this.metricsModule = metricsModule;
6     }
8     getShareIds() {
9         return this.reportedShareIds;
10     }
12     getNodeIds() {
13         return this.reportedNodeIds;
14     }
17 describe('IntegrityMetrics::', () => {
18     const mockMetricsDecryptionErrors = jest.fn();
19     const mockMetricsVerificationErrors = jest.fn();
20     const mockMetricsBlockVerificationErrors = jest.fn();
21     const mockMetricsErroringUsers = jest.fn();
23     let integrityMetrics: IntegrityMetricsForTesting;
25     beforeEach(() => {
26         jest.resetAllMocks();
27         integrityMetrics = new IntegrityMetricsForTesting();
28         integrityMetrics.setMetricsModule({
29             drive_integrity_decryption_errors_total: { increment: mockMetricsDecryptionErrors },
30             drive_integrity_verification_errors_total: { increment: mockMetricsVerificationErrors },
31             drive_integrity_block_verification_errors_total: { increment: mockMetricsBlockVerificationErrors },
32             drive_integrity_erroring_users_total: { increment: mockMetricsErroringUsers },
33         });
34     });
36     describe('shareDecryptionError', () => {
37         it('reports each share only once', () => {
38             integrityMetrics.shareDecryptionError('shareId1', 'main', { isPaid: false, createTime: 0 });
39             integrityMetrics.shareDecryptionError('shareId1', 'main', { isPaid: false, createTime: 0 });
40             integrityMetrics.shareDecryptionError('shareId2', 'device', { isPaid: false, createTime: 0 });
41             expect(mockMetricsDecryptionErrors).toHaveBeenCalledTimes(2);
42             expect(mockMetricsDecryptionErrors).toHaveBeenCalledWith({
43                 entity: 'share',
44                 shareType: 'main',
45                 fromBefore2024: 'unknown',
46             });
47             expect(mockMetricsDecryptionErrors).toHaveBeenCalledWith({
48                 entity: 'share',
49                 shareType: 'device',
50                 fromBefore2024: 'unknown',
51             });
52         });
54         it('does not call affected user if year is before 2024', () => {
55             integrityMetrics.shareDecryptionError('shareId1', 'main', { isPaid: false, createTime: 1704060000 });
56             expect(mockMetricsErroringUsers).toHaveBeenCalledTimes(0);
57         });
59         it('calls also affected user if year is 2024 or newer', () => {
60             integrityMetrics.shareDecryptionError('shareId1', 'main', { isPaid: false, createTime: 1725544806 });
61             expect(mockMetricsErroringUsers).toHaveBeenCalledTimes(1);
62         });
63     });
65     describe('nodeDecryptionError', () => {
66         it('reports each node only once', () => {
67             integrityMetrics.nodeDecryptionError('nodeId1', 'main', { isPaid: false, createTime: 0 });
68             integrityMetrics.nodeDecryptionError('nodeId1', 'main', { isPaid: false, createTime: 0 });
69             integrityMetrics.nodeDecryptionError('nodeId2', 'device', { isPaid: false, createTime: 0 });
70             expect(mockMetricsDecryptionErrors).toHaveBeenCalledTimes(2);
71             expect(mockMetricsDecryptionErrors).toHaveBeenCalledWith({
72                 entity: 'node',
73                 shareType: 'main',
74                 fromBefore2024: 'unknown',
75             });
76             expect(mockMetricsDecryptionErrors).toHaveBeenCalledWith({
77                 entity: 'node',
78                 shareType: 'device',
79                 fromBefore2024: 'unknown',
80             });
81         });
83         it('does not call affected user if year is before 2024', () => {
84             integrityMetrics.nodeDecryptionError('nodeId1', 'main', { isPaid: false, createTime: 1704060000 });
85             expect(mockMetricsErroringUsers).toHaveBeenCalledTimes(0);
86         });
88         it('calls also affected user if year is 2024 or newer', () => {
89             integrityMetrics.nodeDecryptionError('nodeId1', 'main', { isPaid: false, createTime: 1725544806 });
90             expect(mockMetricsErroringUsers).toHaveBeenCalledTimes(1);
91         });
92     });
94     describe('signatureVerificationError', () => {
95         it('reports each node only once', () => {
96             integrityMetrics.signatureVerificationError('nodeId1', 'main', 'NodeKey', {
97                 isPaid: false,
98                 createTime: 0,
99                 addressMatchingDefaultShare: undefined,
100             });
101             integrityMetrics.signatureVerificationError('nodeId1', 'main', 'NodeKey', {
102                 isPaid: false,
103                 createTime: 0,
104                 addressMatchingDefaultShare: undefined,
105             });
106             integrityMetrics.signatureVerificationError('nodeId2', 'device', 'SignatureEmail', {
107                 isPaid: false,
108                 createTime: 0,
109                 addressMatchingDefaultShare: undefined,
110             });
111             expect(mockMetricsVerificationErrors).toHaveBeenCalledTimes(2);
112             expect(mockMetricsVerificationErrors).toHaveBeenCalledWith({
113                 shareType: 'main',
114                 verificationKey: 'NodeKey',
115                 fromBefore2024: 'unknown',
116                 addressMatchingDefaultShare: 'unknown',
117             });
118             expect(mockMetricsVerificationErrors).toHaveBeenCalledWith({
119                 shareType: 'device',
120                 verificationKey: 'SignatureEmail',
121                 fromBefore2024: 'unknown',
122                 addressMatchingDefaultShare: 'unknown',
123             });
124         });
126         it('does not call affected user if year is before 2024 or doesnt have matching address with default share', () => {
127             integrityMetrics.signatureVerificationError('nodeId1', 'main', 'NodeKey', {
128                 isPaid: false,
129                 createTime: 1704060000,
130                 addressMatchingDefaultShare: false,
131             });
132             integrityMetrics.signatureVerificationError('nodeId2', 'main', 'NodeKey', {
133                 isPaid: false,
134                 createTime: 1704060000,
135                 addressMatchingDefaultShare: true,
136             });
137             integrityMetrics.signatureVerificationError('nodeId3', 'main', 'NodeKey', {
138                 isPaid: false,
139                 createTime: 1725544806,
140                 addressMatchingDefaultShare: false,
141             });
142             expect(mockMetricsErroringUsers).toHaveBeenCalledTimes(0);
143         });
145         it('calls also affected user if year is 2024 or newer', () => {
146             integrityMetrics.signatureVerificationError('nodeId1', 'main', 'NodeKey', {
147                 isPaid: false,
148                 createTime: 1725544806,
149                 addressMatchingDefaultShare: true,
150             });
151             expect(mockMetricsErroringUsers).toHaveBeenCalledTimes(1);
152         });
153     });
155     describe('nodeBlockVerificationError', () => {
156         it('reports each incident', () => {
157             integrityMetrics.nodeBlockVerificationError('main', 1234, { isPaid: false, retryHelped: true });
158             integrityMetrics.nodeBlockVerificationError('device', 1234, { isPaid: false, retryHelped: true });
159             expect(mockMetricsBlockVerificationErrors).toHaveBeenCalledTimes(2);
160             expect(mockMetricsBlockVerificationErrors).toHaveBeenCalledWith({
161                 shareType: 'main',
162                 retryHelped: 'yes',
163                 fileSize: '2**20',
164             });
165             expect(mockMetricsBlockVerificationErrors).toHaveBeenCalledWith({
166                 shareType: 'device',
167                 retryHelped: 'yes',
168                 fileSize: '2**20',
169             });
170         });
172         it('does not call affected user if retry helped', () => {
173             integrityMetrics.nodeBlockVerificationError('main', 1234, { isPaid: false, retryHelped: true });
174             expect(mockMetricsErroringUsers).toHaveBeenCalledTimes(0);
175         });
177         it('calls also affected user if retry didnt help', () => {
178             integrityMetrics.nodeBlockVerificationError('main', 1234, { isPaid: false, retryHelped: false });
179             expect(mockMetricsErroringUsers).toHaveBeenCalledTimes(1);
180         });
181     });
184 describe('getFromBefore2024', () => {
185     it('returns unknwon for empty time', () => {
186         expect(getFromBefore2024(undefined)).toBe('unknown');
187         expect(getFromBefore2024(0)).toBe('unknown');
188     });
190     it('returns yes for anythign before 2024', () => {
191         expect(getFromBefore2024(1)).toBe('yes');
192         expect(getFromBefore2024(1111111)).toBe('yes');
193         expect(getFromBefore2024(1704060000)).toBe('yes'); // Dec 31, 2023
194     });
196     it('returns no for 2024 and later', () => {
197         expect(getFromBefore2024(1704067200)).toBe('no'); // Jan 1, 2024
198         expect(getFromBefore2024(1710000000)).toBe('no');
199     });
202 describe('getAddressMatchingDefaultShare', () => {
203     it('returns unknwon for undefined', () => {
204         expect(getAddressMatchingDefaultShare(undefined)).toBe('unknown');
205     });
207     it('returns yes or no based on boolean value', () => {
208         expect(getAddressMatchingDefaultShare(true)).toBe('yes');
209         expect(getAddressMatchingDefaultShare(false)).toBe('no');
210     });
213 describe('getFileSize', () => {
214     it('returns proper matching size bucket', () => {
215         expect(getFileSize(0)).toBe('2**10');
216         expect(getFileSize(1234)).toBe('2**20');
217         expect(getFileSize(123456)).toBe('2**20');
218     });