diff --git a/lib/platform/azure/index.ts b/lib/platform/azure/index.ts index 1e068eac6a1fc49818f885aced723df985b18a6c..ef3709235c3ed29f15179a03938c5b78c97b3327 100644 --- a/lib/platform/azure/index.ts +++ b/lib/platform/azure/index.ts @@ -501,16 +501,59 @@ export async function ensureComment({ content, }: EnsureCommentConfig): Promise<void> { logger.debug(`ensureComment(${number}, ${topic}, content)`); - const body = `### ${topic}\n\n${sanitize(content)}`; + const header = topic ? `### ${topic}\n\n` : ''; + const body = `${header}${sanitize(content)}`; const azureApiGit = await azureApi.gitApi(); - await azureApiGit.createThread( - { - comments: [{ content: body, commentType: 1, parentCommentId: 0 }], - status: 1, - }, - config.repoId, - number - ); + + const threads = await azureApiGit.getThreads(config.repoId, number); + let threadIdFound = null; + let commentIdFound = null; + let commentNeedsUpdating = false; + threads.forEach(thread => { + const firstCommentContent = thread.comments[0].content; + if ( + (topic && firstCommentContent.startsWith(header)) || + (!topic && firstCommentContent === body) + ) { + threadIdFound = thread.id; + commentIdFound = thread.comments[0].id; + commentNeedsUpdating = firstCommentContent !== body; + } + }); + + if (!threadIdFound) { + await azureApiGit.createThread( + { + comments: [{ content: body, commentType: 1, parentCommentId: 0 }], + status: 1, + }, + config.repoId, + number + ); + logger.debug( + { repository: config.repository, issueNo: number, topic }, + 'Comment added' + ); + } else if (commentNeedsUpdating) { + await azureApiGit.updateComment( + { + content: body, + }, + config.repoId, + number, + threadIdFound, + commentIdFound + ); + logger.debug( + { repository: config.repository, issueNo: number, topic }, + 'Comment updated' + ); + } else { + logger.debug( + { repository: config.repository, issueNo: number, topic }, + 'Comment is already update-to-date' + ); + } } export async function ensureCommentRemoval( diff --git a/test/platform/azure/__snapshots__/index.spec.ts.snap b/test/platform/azure/__snapshots__/index.spec.ts.snap index ea8e65413bb2f3ecc71bbf05dabed2db7c0b389d..c7d29e87faef6cd586400271b04a2d3ae134b536 100644 --- a/test/platform/azure/__snapshots__/index.spec.ts.snap +++ b/test/platform/azure/__snapshots__/index.spec.ts.snap @@ -42,11 +42,55 @@ Array [ ] `; -exports[`platform/azure ensureComment add comment 1`] = ` +exports[`platform/azure ensureComment adds comment if missing 1`] = ` Array [ - Array [], - Array [], - Array [], + Array [ + Object { + "comments": Array [ + Object { + "commentType": 1, + "content": "### some-subject + +some +content", + "parentCommentId": 0, + }, + ], + "status": 1, + }, + "1", + 42, + ], +] +`; + +exports[`platform/azure ensureComment adds comment if missing 2`] = `Array []`; + +exports[`platform/azure ensureComment does nothing if comment exists and is the same 1`] = `Array []`; + +exports[`platform/azure ensureComment does nothing if comment exists and is the same 2`] = `Array []`; + +exports[`platform/azure ensureComment does nothing if comment exists and is the same when there is no topic 1`] = `Array []`; + +exports[`platform/azure ensureComment does nothing if comment exists and is the same when there is no topic 2`] = `Array []`; + +exports[`platform/azure ensureComment updates comment if missing 1`] = `Array []`; + +exports[`platform/azure ensureComment updates comment if missing 2`] = ` +Array [ + Array [ + Object { + "content": "### some-subject + +some +new +content", + }, + "1", + 42, + 4, + 2, + ], ] `; @@ -143,16 +187,25 @@ Object { exports[`platform/azure updatePr(prNo, title, body) should update the PR 1`] = ` Array [ - Array [], - Array [], - Array [], + Array [ + Object { + "description": undefined, + "title": "The New Title", + }, + "1", + 1234, + ], ] `; exports[`platform/azure updatePr(prNo, title, body) should update the PR without description 1`] = ` Array [ - Array [], - Array [], - Array [], + Array [ + Object { + "title": "The New Title - autoclose", + }, + "1", + 1234, + ], ] `; diff --git a/test/platform/azure/index.spec.ts b/test/platform/azure/index.spec.ts index 7a91f30dc3346980db8def7c5171a0304e11768d..5ea357bb3851d88a93bdb899660cfd7b7d4ad68c 100644 --- a/test/platform/azure/index.spec.ts +++ b/test/platform/azure/index.spec.ts @@ -642,44 +642,123 @@ describe('platform/azure', () => { describe('updatePr(prNo, title, body)', () => { it('should update the PR', async () => { await initRepo({ repository: 'some/repo' }); + const updatePullRequest = jest.fn(); azureApi.gitApi.mockImplementationOnce( () => ({ - updatePullRequest: jest.fn(), + updatePullRequest, } as any) ); await azure.updatePr(1234, 'The New Title', 'Hello world again'); - expect(azureApi.gitApi.mock.calls).toMatchSnapshot(); + expect(updatePullRequest.mock.calls).toMatchSnapshot(); }); it('should update the PR without description', async () => { await initRepo({ repository: 'some/repo' }); + const updatePullRequest = jest.fn(); azureApi.gitApi.mockImplementationOnce( () => ({ - updatePullRequest: jest.fn(), + updatePullRequest, } as any) ); await azure.updatePr(1234, 'The New Title - autoclose'); - expect(azureApi.gitApi.mock.calls).toMatchSnapshot(); + expect(updatePullRequest.mock.calls).toMatchSnapshot(); }); }); describe('ensureComment', () => { - it('add comment', async () => { + it('adds comment if missing', async () => { await initRepo({ repository: 'some/repo' }); - azureApi.gitApi.mockImplementation( - () => - ({ - createThread: jest.fn(() => [{ id: 123 }]), - } as any) - ); + const gitApiMock = { + createThread: jest.fn(() => [{ id: 123 }]), + getThreads: jest.fn().mockReturnValue([ + { + comments: [{ content: 'end-user comment', id: 1 }], + id: 2, + }, + ]), + updateComment: jest.fn(() => ({ id: 123 })), + }; + azureApi.gitApi.mockImplementation(() => gitApiMock as any); await azure.ensureComment({ number: 42, topic: 'some-subject', content: 'some\ncontent', }); - expect(azureApi.gitApi.mock.calls).toMatchSnapshot(); + expect(gitApiMock.createThread.mock.calls).toMatchSnapshot(); + expect(gitApiMock.updateComment.mock.calls).toMatchSnapshot(); + }); + it('updates comment if missing', async () => { + await initRepo({ repository: 'some/repo' }); + const gitApiMock = { + createThread: jest.fn(() => [{ id: 123 }]), + getThreads: jest.fn().mockReturnValue([ + { + comments: [{ content: 'end-user comment', id: 1 }], + id: 3, + }, + { + comments: [{ content: '### some-subject\n\nsome\ncontent', id: 2 }], + id: 4, + }, + ]), + updateComment: jest.fn(() => ({ id: 123 })), + }; + azureApi.gitApi.mockImplementation(() => gitApiMock as any); + await azure.ensureComment({ + number: 42, + topic: 'some-subject', + content: 'some\nnew\ncontent', + }); + expect(gitApiMock.createThread.mock.calls).toMatchSnapshot(); + expect(gitApiMock.updateComment.mock.calls).toMatchSnapshot(); + }); + it('does nothing if comment exists and is the same', async () => { + await initRepo({ repository: 'some/repo' }); + const gitApiMock = { + createThread: jest.fn(() => [{ id: 123 }]), + getThreads: jest.fn().mockReturnValue([ + { + comments: [{ content: 'end-user comment', id: 1 }], + id: 3, + }, + { + comments: [{ content: '### some-subject\n\nsome\ncontent', id: 2 }], + id: 4, + }, + ]), + updateComment: jest.fn(() => ({ id: 123 })), + }; + azureApi.gitApi.mockImplementation(() => gitApiMock as any); + await azure.ensureComment({ + number: 42, + topic: 'some-subject', + content: 'some\ncontent', + }); + expect(gitApiMock.createThread.mock.calls).toMatchSnapshot(); + expect(gitApiMock.updateComment.mock.calls).toMatchSnapshot(); + }); + it('does nothing if comment exists and is the same when there is no topic', async () => { + await initRepo({ repository: 'some/repo' }); + const gitApiMock = { + createThread: jest.fn(() => [{ id: 123 }]), + getThreads: jest.fn().mockReturnValue([ + { + comments: [{ content: 'some\ncontent', id: 2 }], + id: 4, + }, + ]), + updateComment: jest.fn(() => ({ id: 123 })), + }; + azureApi.gitApi.mockImplementation(() => gitApiMock as any); + await azure.ensureComment({ + number: 42, + topic: null, + content: 'some\ncontent', + }); + expect(gitApiMock.createThread.mock.calls).toMatchSnapshot(); + expect(gitApiMock.updateComment.mock.calls).toMatchSnapshot(); }); }); @@ -728,6 +807,7 @@ describe('platform/azure', () => { () => ({ createThread: jest.fn(() => [{ id: 123 }]), + getThreads: jest.fn(() => []), } as any) ); await azure.addAssignees(123, ['test@bonjour.fr']);