1 import { renderHook } from '@testing-library/react-hooks';
3 import { VERIFICATION_STATUS } from '@proton/crypto';
4 import { getIsConnectionIssue } from '@proton/shared/lib/api/helpers/apiErrorHelper';
6 import { sendErrorReport } from '../../utils/errorHandling';
7 import { useDriveCrypto } from '../_crypto';
8 import { useDownload } from '../_downloads';
9 import { useLink } from '../_links';
10 import type { ParsedExtendedAttributes } from '../_links/extendedAttributes';
11 import { decryptExtendedAttributes } from '../_links/extendedAttributes';
12 import useRevisions from './useRevisions';
14 jest.mock('../_crypto');
15 jest.mock('../_downloads', () => ({
16 useDownload: jest.fn(),
18 jest.mock('../_links', () => ({
22 jest.mock('../_links/extendedAttributes');
23 jest.mock('../../utils/errorHandling');
24 jest.mock('@proton/shared/lib/api/helpers/apiErrorHelper');
26 const mockedGetVerificationKey = jest.fn();
27 const mockedGetLinkPrivateKey = jest.fn();
28 const mockedDecryptExtendedAttributes = jest.mocked(decryptExtendedAttributes);
29 const mockedSendErrorReport = jest.mocked(sendErrorReport);
30 const mockedGetIsConnectionIssue = jest.mocked(getIsConnectionIssue);
32 jest.mocked(useDriveCrypto).mockImplementation(() => ({
33 ...jest.requireMock('../_crypto').useDriveCrypto,
34 getVerificationKey: mockedGetVerificationKey,
37 jest.mocked(useLink).mockImplementation(() => ({
38 ...jest.requireMock('../_links').useDriveCrypto,
39 getLinkPrivateKey: mockedGetLinkPrivateKey,
42 const mockedCheckFirstBlockSignature = jest.fn();
43 jest.mocked(useDownload).mockImplementation(() => ({
44 ...jest.requireMock('../_downloads').useDownload,
45 checkFirstBlockSignature: mockedCheckFirstBlockSignature,
48 jest.mocked(decryptExtendedAttributes);
50 const revisionXattrs: ParsedExtendedAttributes = {
52 ModificationTime: 1681715947,
55 const revisionEncryptedXattrs = 'encryptedXattrs';
56 const revisionSignatureAddress = 'revisionSignatureAddress';
57 const shareId = 'shareId';
58 const linkId = 'linkId';
60 describe('useRevision', () => {
61 let abortSignal: AbortSignal;
64 abortSignal = new AbortController().signal;
67 it('getRevisionDecryptedXattrs', async () => {
68 mockedGetVerificationKey.mockResolvedValue(['key']);
69 mockedGetLinkPrivateKey.mockResolvedValue('privateKey');
70 mockedDecryptExtendedAttributes.mockResolvedValue({
71 xattrs: revisionXattrs,
72 verified: VERIFICATION_STATUS.SIGNED_AND_VALID,
76 current: { getRevisionDecryptedXattrs },
78 } = renderHook(() => useRevisions(shareId, linkId));
79 const result = await getRevisionDecryptedXattrs(abortSignal, revisionEncryptedXattrs, revisionSignatureAddress);
81 expect(mockedGetVerificationKey).toHaveBeenCalledWith(revisionSignatureAddress);
82 expect(mockedGetLinkPrivateKey).toHaveBeenCalledWith(abortSignal, shareId, linkId);
83 expect(mockedDecryptExtendedAttributes).toHaveBeenCalledWith(revisionEncryptedXattrs, 'privateKey', ['key']);
84 expect(result).toStrictEqual({
85 xattrs: revisionXattrs,
87 xattrs: VERIFICATION_STATUS.SIGNED_AND_VALID,
92 it('getRevisionDecryptedXattrs should sendErrorReport if a promise failed', async () => {
93 mockedDecryptExtendedAttributes.mockResolvedValue({
94 xattrs: revisionXattrs,
95 verified: VERIFICATION_STATUS.SIGNED_AND_VALID,
97 mockedGetVerificationKey.mockResolvedValue(['key']);
98 const error = new Error('getLinkPrivateKey error');
99 mockedGetLinkPrivateKey.mockRejectedValue(error);
102 current: { getRevisionDecryptedXattrs },
104 } = renderHook(() => useRevisions(shareId, linkId));
105 const result = await getRevisionDecryptedXattrs(abortSignal, revisionEncryptedXattrs, revisionSignatureAddress);
106 expect(result).toBeUndefined();
107 expect(mockedSendErrorReport).toHaveBeenCalledWith(error);
110 it('checkRevisionSignature result should be undefined if no issues', async () => {
111 const revisionId = 'revisionId';
112 mockedCheckFirstBlockSignature.mockResolvedValueOnce(undefined);
115 current: { checkRevisionSignature },
117 } = renderHook(() => useRevisions(shareId, linkId));
118 const result = await checkRevisionSignature(abortSignal, revisionId);
119 expect(mockedCheckFirstBlockSignature).toHaveBeenCalledWith(abortSignal, shareId, linkId, revisionId);
120 expect(result).toBeUndefined();
123 it('checkRevisionSignature should throw an error if there is connection issues', async () => {
124 const revisionId = 'revisionId';
125 const error = new Error('Network error');
126 mockedCheckFirstBlockSignature.mockRejectedValue(error);
127 mockedGetIsConnectionIssue.mockReturnValue(true);
130 current: { checkRevisionSignature },
132 } = renderHook(() => useRevisions(shareId, linkId));
133 const errorResult = checkRevisionSignature(abortSignal, revisionId);
134 await expect(errorResult).rejects.toThrowError(error);
135 expect(mockedGetIsConnectionIssue).toHaveBeenCalledWith(error);
138 it('checkRevisionSignature should sendErrorReport and return signatureIssues', async () => {
139 const revisionId = 'revisionId';
140 const error = new Error('checkFirstBlockSignature error');
141 mockedCheckFirstBlockSignature.mockRejectedValue(error);
142 mockedGetIsConnectionIssue.mockReturnValue(false);
145 current: { checkRevisionSignature },
147 } = renderHook(() => useRevisions(shareId, linkId));
148 const result = await checkRevisionSignature(abortSignal, revisionId);
149 expect(mockedSendErrorReport).toHaveBeenCalledWith(error);
150 expect(result).toStrictEqual({
151 contentKeyPacket: VERIFICATION_STATUS.NOT_SIGNED,
152 blocks: VERIFICATION_STATUS.NOT_SIGNED,
153 thumbnail: VERIFICATION_STATUS.NOT_SIGNED,