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(),
19 jest.mock('@proton/docs-shared', () => ({
20 ...jest.requireActual('@proton/docs-shared'),
21 ServerTime: jest.fn().mockImplementation(() => ({})),
26 CreateMarkNode: 'CreateMarkNode',
30 const mockEncryptComment = {
32 } as unknown as jest.Mocked<EncryptComment>
34 const mockDecryptComment = {
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(),
45 deleteThread: jest.fn(),
46 replacePlaceholderThread: jest.fn(),
51 } as unknown as jest.Mocked<LoggerInterface>
53 const mockEventBus = {
57 describe('CreateThread', () => {
58 let createThread: CreateThread
60 const entitlements = {
61 nodeMeta: { volumeId: 'volume-id', linkId: 'link-id' } as NodeMeta,
63 userOwnAddress: 'foo@bar.com',
64 } as unknown as DocumentKeys,
65 } as unknown as DocumentEntitlements
68 decryptedDocumentName: 'Test',
71 commentsState: mockCommentsState as unknown as LocalCommentsState,
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,
83 ;(GenerateUUID as jest.Mock).mockReturnValue('uuid')
84 ServerTime.now = jest.fn()
85 ;(ServerTime.now as jest.Mock).mockReturnValue(new Date())
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(
93 CommentThreadID: 'thread-id',
95 Comments: ['encrypted-response-comment'],
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',
108 expect(mockCommentsApi.createThread).toHaveBeenCalledWith(
111 decryptedDocumentName: 'Test',
112 encryptedMainCommentContent: 'encrypted-comment',
118 expect(mockDecryptComment.execute).toHaveBeenCalledWith('encrypted-response-comment', 'mark-id', {
119 userOwnAddress: 'foo@bar.com',
121 expect(mockCommentsState.replacePlaceholderThread).toHaveBeenCalledWith('uuid', expect.any(CommentThread))
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')
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')