1 import { ApiResult, DocumentRole, Result } from '@proton/docs-shared'
2 import type { DocumentKeys, NodeMeta } from '@proton/drive-store'
3 import { Comment } from '../Models'
4 import { GenerateUUID } from '../Util/GenerateUuid'
5 import type { EncryptComment } from './EncryptComment'
6 import type { LocalCommentsState } from '../Services/Comments/LocalCommentsState'
7 import { CreateComment } from './CreateComment'
8 import type { DocsApi } from '../Api/DocsApi'
9 import { DocsApiErrorCode } from '@proton/shared/lib/api/docs'
10 import type { DocumentEntitlements } from '../Types/DocumentEntitlements'
12 jest.mock('../Util/GenerateUuid', () => ({
13 GenerateUUID: jest.fn(),
16 const mockEncryptComment = {
18 } as unknown as jest.Mocked<EncryptComment>
20 const mockCommentsApi = {
21 addCommentToThread: jest.fn(),
22 } as unknown as jest.Mocked<DocsApi>
24 const mockCommentsState = {
25 findThreadById: jest.fn(),
26 addComment: jest.fn(),
27 deleteComment: jest.fn(),
28 replacePlaceholderComment: jest.fn(),
29 } as unknown as jest.Mocked<LocalCommentsState>
31 describe('CreateComment', () => {
32 let createComment: CreateComment
34 const entitlements = {
36 userOwnAddress: 'foo@bar.com',
38 role: new DocumentRole('Editor'),
39 nodeMeta: { volumeId: 'volume-id', linkId: 'link-id' } as NodeMeta,
40 } as unknown as DocumentEntitlements
42 const dto: Parameters<CreateComment['execute']>[0] = {
44 threadID: 'thread-id',
46 commentsState: mockCommentsState,
48 decryptedDocumentName: 'Test',
52 createComment = new CreateComment(
53 mockCommentsApi as unknown as DocsApi,
54 mockEncryptComment as unknown as EncryptComment,
56 ;(GenerateUUID as jest.Mock).mockReturnValue('uuid')
59 it('should call encryptComment, api.addCommentToThread, and decryptComment in order', async () => {
60 mockCommentsState.findThreadById.mockReturnValue({ markID: 'mark-id' } as never)
61 mockEncryptComment.execute.mockResolvedValue(Result.ok('encrypted-comment'))
62 mockCommentsApi.addCommentToThread.mockResolvedValue(
64 Comment: { text: 'encrypted-response-comment', AuthorEmail: 'foo@bar.com' } as never,
69 await createComment.execute(dto)
71 expect(mockCommentsState.findThreadById).toHaveBeenCalledWith('thread-id')
72 expect(mockCommentsState.addComment).toHaveBeenCalledWith(expect.any(Comment), 'thread-id')
73 expect(mockEncryptComment.execute).toHaveBeenCalledWith('Test comment', 'mark-id', {
74 userOwnAddress: 'foo@bar.com',
76 expect(mockCommentsApi.addCommentToThread).toHaveBeenCalledWith(
78 threadId: 'thread-id',
79 encryptedContent: 'encrypted-comment',
80 parentCommentId: null,
81 decryptedDocumentName: 'Test',
86 expect(mockCommentsState.replacePlaceholderComment).toHaveBeenCalled()
89 it('should delete comment if encryption fails', async () => {
90 mockCommentsState.findThreadById.mockReturnValue({ markID: 'mark-id' } as never)
91 mockEncryptComment.execute.mockResolvedValue(Result.fail('encryption error'))
93 const result = await createComment.execute(dto)
95 expect(result.isFailed()).toBe(true)
96 expect(mockCommentsState.deleteComment).toHaveBeenCalledWith({ commentID: 'uuid', threadID: 'thread-id' })
99 it('should delete comment if api call fails', async () => {
100 mockCommentsState.findThreadById.mockReturnValue({ markID: 'mark-id' } as never)
101 mockEncryptComment.execute.mockResolvedValue(Result.ok('encrypted-comment'))
102 mockCommentsApi.addCommentToThread.mockResolvedValue(
103 ApiResult.fail({ code: DocsApiErrorCode.Unknown, message: 'api error' }),
106 const result = await createComment.execute(dto)
108 expect(result.isFailed()).toBe(true)
109 expect(mockCommentsState.deleteComment).toHaveBeenCalledWith({ commentID: 'uuid', threadID: 'thread-id' })