Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / docs-core / lib / UseCase / CreateThread.spec.ts
blobc54b696e98f07069cfcf484bcb4b9756f2cc7e0b
1 import { ApiResult, Result } from '@proton/docs-shared'
2 import type { DocumentKeys, NodeMeta } from '@proton/drive-store'
3 import { CommentThread } from '../Models'
4 import type { InternalEventBusInterface } from '@proton/docs-shared'
5 import { ServerTime } from '@proton/docs-shared'
6 import { GenerateUUID } from '../Util/GenerateUuid'
7 import type { EncryptComment } from './EncryptComment'
8 import type { DecryptComment } from './DecryptComment'
9 import type { LocalCommentsState } from '../Services/Comments/LocalCommentsState'
10 import { CreateThread } from './CreateThread'
11 import type { DocsApi } from '../Api/DocsApi'
12 import type { LoggerInterface } from '@proton/utils/logs'
13 import type { DocumentEntitlements } from '../Types/DocumentEntitlements'
15 jest.mock('../Util/GenerateUuid', () => ({
16   GenerateUUID: jest.fn(),
17 }))
19 jest.mock('@proton/docs-shared', () => ({
20   ...jest.requireActual('@proton/docs-shared'),
21   ServerTime: jest.fn().mockImplementation(() => ({})),
22   CommentThreadState: {
23     Active: 'Active',
24   },
25   CommentsEvent: {
26     CreateMarkNode: 'CreateMarkNode',
27   },
28 }))
30 const mockEncryptComment = {
31   execute: jest.fn(),
32 } as unknown as jest.Mocked<EncryptComment>
34 const mockDecryptComment = {
35   execute: jest.fn(),
36 } as unknown as jest.Mocked<DecryptComment>
38 const mockCommentsApi = {
39   createThread: jest.fn(),
40 } as unknown as jest.Mocked<DocsApi>
42 const mockCommentsState = {
43   findThreadById: jest.fn(),
44   addThread: jest.fn(),
45   deleteThread: jest.fn(),
46   replacePlaceholderThread: jest.fn(),
49 const logger = {
50   error: jest.fn(),
51 } as unknown as jest.Mocked<LoggerInterface>
53 const mockEventBus = {
54   publish: jest.fn(),
57 describe('CreateThread', () => {
58   let createThread: CreateThread
60   const entitlements = {
61     nodeMeta: { volumeId: 'volume-id', linkId: 'link-id' } as NodeMeta,
62     keys: {
63       userOwnAddress: 'foo@bar.com',
64     } as unknown as DocumentKeys,
65   } as unknown as DocumentEntitlements
67   const dto = {
68     decryptedDocumentName: 'Test',
69     text: 'Test comment',
70     entitlements,
71     commentsState: mockCommentsState as unknown as LocalCommentsState,
72     type: 1,
73   }
75   beforeEach(() => {
76     createThread = new CreateThread(
77       mockCommentsApi as unknown as DocsApi,
78       mockEncryptComment as unknown as EncryptComment,
79       mockDecryptComment as unknown as DecryptComment,
80       mockEventBus as unknown as InternalEventBusInterface,
81       logger,
82     )
83     ;(GenerateUUID as jest.Mock).mockReturnValue('uuid')
84     ServerTime.now = jest.fn()
85     ;(ServerTime.now as jest.Mock).mockReturnValue(new Date())
86   })
88   it('should create mark node, call encryptComment, api.createThread, and decryptComment in order', async () => {
89     mockEncryptComment.execute.mockResolvedValue(Result.ok('encrypted-comment'))
90     mockCommentsApi.createThread.mockResolvedValue(
91       ApiResult.ok({
92         CommentThread: {
93           CommentThreadID: 'thread-id',
94           Mark: 'mark-id',
95           Comments: ['encrypted-response-comment'],
96         } as never,
97         Code: 1000,
98       }),
99     )
100     mockDecryptComment.execute.mockResolvedValue(Result.ok({ id: 'uuid', text: 'decrypted comment' } as never))
102     await createThread.execute(dto)
104     expect(mockCommentsState.addThread).toHaveBeenCalledWith(expect.any(CommentThread))
105     expect(mockEncryptComment.execute).toHaveBeenCalledWith('Test comment', 'uuid', {
106       userOwnAddress: 'foo@bar.com',
107     })
108     expect(mockCommentsApi.createThread).toHaveBeenCalledWith(
109       {
110         commentType: 1,
111         decryptedDocumentName: 'Test',
112         encryptedMainCommentContent: 'encrypted-comment',
113         markId: 'uuid',
114         type: 1,
115       },
116       entitlements,
117     )
118     expect(mockDecryptComment.execute).toHaveBeenCalledWith('encrypted-response-comment', 'mark-id', {
119       userOwnAddress: 'foo@bar.com',
120     })
121     expect(mockCommentsState.replacePlaceholderThread).toHaveBeenCalledWith('uuid', expect.any(CommentThread))
122   })
124   it('should delete comment if encryption fails', async () => {
125     mockCommentsState.findThreadById.mockReturnValue({ markID: 'mark-id' })
126     mockEncryptComment.execute.mockResolvedValue(Result.fail('encryption error'))
128     const result = await createThread.execute(dto)
130     expect(result.isFailed()).toBe(true)
131     expect(mockCommentsState.deleteThread).toHaveBeenCalledWith('uuid')
132   })
134   it('should delete comment if api call fails', async () => {
135     mockCommentsState.findThreadById.mockReturnValue({ markID: 'mark-id' })
136     mockEncryptComment.execute.mockResolvedValue(Result.ok('encrypted-comment'))
137     mockCommentsApi.createThread.mockResolvedValue(ApiResult.fail({ message: 'api error', code: 0 }))
139     const result = await createThread.execute(dto)
141     expect(result.isFailed()).toBe(true)
142     expect(mockCommentsState.deleteThread).toHaveBeenCalledWith('uuid')
143   })