Skip to content
代码片段 群组 项目
未验证 提交 6537f162 编辑于 作者: Sincheol (David) Kim's avatar Sincheol (David) Kim 提交者: GitLab
浏览文件

Avoid creating reviews on the same diff hunk

上级 656a9512
No related branches found
No related tags found
无相关合并请求
...@@ -79,24 +79,37 @@ def build_draft_note_params(response_modifier, diff_file, hunk, diff_refs) ...@@ -79,24 +79,37 @@ def build_draft_note_params(response_modifier, diff_file, hunk, diff_refs)
# create the note on the old line. # create the note on the old line.
old_line = hunk[:removed].last&.old_pos if hunk[:added].empty? old_line = hunk[:removed].last&.old_pos if hunk[:added].empty?
position = {
base_sha: diff_refs.base_sha,
start_sha: diff_refs.start_sha,
head_sha: diff_refs.head_sha,
old_path: diff_file.old_path,
new_path: diff_file.new_path,
position_type: 'text',
old_line: old_line,
new_line: hunk[:added].last&.new_pos,
ignore_whitespace_change: false
}
return if review_note_already_exists?(position)
@draft_notes_params << { @draft_notes_params << {
merge_request: merge_request, merge_request: merge_request,
author: review_bot, author: review_bot,
note: response_modifier.response_body, note: response_modifier.response_body,
position: { position: position
base_sha: diff_refs.base_sha,
start_sha: diff_refs.start_sha,
head_sha: diff_refs.head_sha,
old_path: diff_file.old_path,
new_path: diff_file.new_path,
position_type: 'text',
old_line: old_line,
new_line: hunk[:added].last&.new_pos,
ignore_whitespace_change: false
}
} }
end end
def review_note_already_exists?(position)
merge_request
.notes
.diff_notes
.authored_by(review_bot)
.positions
.any? { |pos| pos.to_h >= position }
end
def create_progress_note def create_progress_note
@progress_note = Notes::CreateService.new( @progress_note = Notes::CreateService.new(
merge_request.project, merge_request.project,
...@@ -108,26 +121,27 @@ def create_progress_note ...@@ -108,26 +121,27 @@ def create_progress_note
end end
def update_progress_note_with_review_summary(draft_notes) def update_progress_note_with_review_summary(draft_notes)
summary_note = if draft_notes.blank?
s_("DuoCodeReview|I finished my review and found nothing to comment on. Nice work! :tada:")
else
response = summary_response_for(user, draft_notes)
if response.errors.any? || response.response_body.blank?
s_("DuoCodeReview|I have encountered some issues while I was reviewing. " \
"Please try again later.")
else
response.response_body
end
end
Notes::UpdateService.new( Notes::UpdateService.new(
merge_request.project, merge_request.project,
review_bot, review_bot,
note: summary_note note: summary_note(draft_notes)
).execute(@progress_note) ).execute(@progress_note)
end end
def summary_note(draft_notes)
if draft_notes.blank?
s_("DuoCodeReview|I finished my review and found nothing to comment on. Nice work! :tada:")
else
response = summary_response_for(user, draft_notes)
if response.errors.any? || response.response_body.blank?
s_("DuoCodeReview|I have encountered some issues while I was reviewing. Please try again later.")
else
response.response_body
end
end
end
def publish_draft_notes def publish_draft_notes
if @draft_notes_params.empty? if @draft_notes_params.empty?
update_progress_note_with_review_summary([]) update_progress_note_with_review_summary([])
......
...@@ -9,59 +9,59 @@ ...@@ -9,59 +9,59 @@
let(:options) { {} } let(:options) { {} }
let(:response_modifier) { double } let(:response_modifier) { double }
let(:create_note_allowed?) { true } let(:create_note_allowed?) { true }
let(:review_start_note) do
s_("DuoCodeReview|Hey :wave: I'm starting to review your merge request and I will let you know when I'm finished.")
end
let(:review_no_comment_note) do
s_("DuoCodeReview|I finished my review and found nothing to comment on. Nice work! :tada:")
end
let(:review_error_note) do
s_("DuoCodeReview|I have encountered some issues while I was reviewing. Please try again later.")
end
let_it_be(:duo_code_review_bot) { create(:user, :duo_code_review_bot) } let_it_be(:duo_code_review_bot) { create(:user, :duo_code_review_bot) }
let_it_be(:merge_request) { create(:merge_request) } let_it_be(:project) do
let_it_be(:user) { create(:user, developer_of: merge_request.project) } create(:project, :custom_repo, files: { 'UPDATED.md' => "existing line 1\nexisting line 2\n" })
let_it_be(:diff_refs) { merge_request.diff_refs } end
let(:ai_reviewable_diff_files) do let_it_be(:user) { create(:user, developer_of: project) }
[ let_it_be(:source_branch) { 'review-merge-request-test' }
instance_double( let_it_be(:merge_request) do
Gitlab::Diff::File, project.repository.create_branch(source_branch, project.default_branch)
old_path: 'NEW.md', project.repository.update_file(
new_path: 'NEW.md', user,
diff_lines_by_hunk: [ 'UPDATED.md',
{ "existing line 1\nnew line\n",
added: [ message: 'Update file',
instance_double(Gitlab::Diff::Line, old_pos: 5, new_pos: 3), branch_name: source_branch)
instance_double(Gitlab::Diff::Line, old_pos: 5, new_pos: 4)
], project.repository.create_file(
removed: [ user,
instance_double(Gitlab::Diff::Line, old_pos: 3, new_pos: 3), 'NEW.md',
instance_double(Gitlab::Diff::Line, old_pos: 4, new_pos: 3) "new line1\nnew line 2\n",
] message: 'Create file',
} branch_name: source_branch)
]
), create(
instance_double( :merge_request,
Gitlab::Diff::File, target_project: project,
old_path: 'UPDATED.md', source_project: project,
new_path: 'UPDATED.md', source_branch: source_branch,
diff_lines_by_hunk: [ target_branch: project.default_branch
{ )
added: [],
removed: [
instance_double(Gitlab::Diff::Line, old_pos: 3, new_pos: 3),
instance_double(Gitlab::Diff::Line, old_pos: 4, new_pos: 3)
]
}
]
)
]
end end
let_it_be(:diff_refs) { merge_request.diff_refs }
let(:review_prompt_message) do let(:review_prompt_message) do
build(:ai_message, :review_merge_request, user: user, resource: merge_request, request_id: 'uuid') build(:ai_message, :review_merge_request, user: user, resource: merge_request, request_id: 'uuid')
end end
subject(:completion) { described_class.new(review_prompt_message, review_prompt_class, options) } subject(:completion) { described_class.new(review_prompt_message, review_prompt_class, options) }
before do
allow(merge_request)
.to receive(:ai_reviewable_diff_files)
.and_return(ai_reviewable_diff_files)
end
describe '#execute' do describe '#execute' do
let(:review_prompt) { 'This is a review prompt' } let(:review_prompt) { 'This is a review prompt' }
let(:summary_prompt) { 'This is a summary prompt' } let(:summary_prompt) { 'This is a summary prompt' }
...@@ -147,56 +147,56 @@ ...@@ -147,56 +147,56 @@
} }
end end
it 'builds draft note and publish to create diff note on new and updated files' do it 'creates diff notes on new and updated files' do
new_file_draft_note_params = { completion.execute
merge_request: merge_request,
author: duo_code_review_bot,
note: review_answer,
position: {
base_sha: diff_refs.base_sha,
start_sha: diff_refs.start_sha,
head_sha: diff_refs.head_sha,
old_path: 'NEW.md',
new_path: 'NEW.md',
position_type: 'text',
old_line: nil,
new_line: 4,
ignore_whitespace_change: false
}
}
updated_file_draft_note_params = { diff_notes = merge_request.notes.diff_notes.authored_by(duo_code_review_bot)
merge_request: merge_request, expect(diff_notes.count).to eq 2
author: duo_code_review_bot,
note: review_answer, new_file_note = diff_notes[0]
position: { expect(new_file_note.note).to eq review_answer
base_sha: diff_refs.base_sha, expect(new_file_note.position.to_h).to eq({
start_sha: diff_refs.start_sha, base_sha: diff_refs.base_sha,
head_sha: diff_refs.head_sha, start_sha: diff_refs.start_sha,
old_path: 'UPDATED.md', head_sha: diff_refs.head_sha,
new_path: 'UPDATED.md', old_path: 'NEW.md',
position_type: 'text', new_path: 'NEW.md',
old_line: 4, position_type: 'text',
new_line: nil, old_line: nil,
ignore_whitespace_change: false new_line: 2,
} line_range: nil,
} ignore_whitespace_change: false
})
updated_file_note = diff_notes[1]
expect(updated_file_note.note).to eq review_answer
expect(updated_file_note.position.to_h).to eq({
base_sha: diff_refs.base_sha,
start_sha: diff_refs.start_sha,
head_sha: diff_refs.head_sha,
old_path: 'UPDATED.md',
new_path: 'UPDATED.md',
position_type: 'text',
old_line: nil,
new_line: 2,
line_range: nil,
ignore_whitespace_change: false
})
end
draft_note_1 = instance_double(DraftNote) context 'when review note alredy exist on the same position' do
draft_note_2 = instance_double(DraftNote) it 'does not add more notes to the same position' do
expect do
expect(DraftNote).to receive(:new).with(new_file_draft_note_params).and_return(draft_note_1) described_class.new(review_prompt_message, review_prompt_class, options).execute
expect(DraftNote).to receive(:new).with(updated_file_draft_note_params).and_return(draft_note_2) end.to change { merge_request.notes.diff_notes.count }.by(2)
expect(DraftNote).to receive(:bulk_insert!).with([draft_note_1, draft_note_2], batch_size: 20) .and change { merge_request.notes.non_diff_notes.count }.by(1) # review summary
expect_next_instance_of(
DraftNotes::PublishService,
merge_request,
duo_code_review_bot
) do |svc|
expect(svc).to receive(:execute).with(executing_user: user)
end
completion.execute expect { completion.execute }
.to not_change { merge_request.notes.diff_notes.count }
.and change { merge_request.notes.non_diff_notes.count }.by(1) # review summary
expect(merge_request.notes.last.note).to eq(review_no_comment_note)
end
end end
context 'when user is not allowed to create notes' do context 'when user is not allowed to create notes' do
...@@ -216,9 +216,8 @@ ...@@ -216,9 +216,8 @@
it 'updates progress note with a success message' do it 'updates progress note with a success message' do
completion.execute completion.execute
expect(Note.count).to eq 1 expect(merge_request.notes.count).to eq 1
expect(Note.last.note).to eq s_("DuoCodeReview|I finished my review and found nothing to comment on. " \ expect(merge_request.notes.last.note).to eq(review_no_comment_note)
"Nice work! :tada:")
end end
end end
...@@ -231,14 +230,13 @@ ...@@ -231,14 +230,13 @@
merge_request.project, merge_request.project,
duo_code_review_bot, duo_code_review_bot,
noteable: merge_request, noteable: merge_request,
note: s_("MergeRequests|Hey :wave: I'm starting to review your merge request and " \ note: review_start_note
"I will let you know when I'm finished.")
).and_call_original ).and_call_original
allow(Notes::CreateService).to receive(:new).and_call_original allow(Notes::CreateService).to receive(:new).and_call_original
completion.execute completion.execute
expect(Note.last.note).to eq s_("Helpful review summary") expect(merge_request.notes.non_diff_notes.last.note).to eq s_("Helpful review summary")
end end
end end
...@@ -255,9 +253,9 @@ ...@@ -255,9 +253,9 @@
it 'updates progress note with an error message' do it 'updates progress note with an error message' do
completion.execute completion.execute
expect(Note.count).to eq 1 expect(merge_request.notes.count).to eq 3
expect(Note.last.note).to eq s_("DuoCodeReview|I have encountered some issues while I was reviewing. " \ expect(merge_request.notes.diff_notes.count).to eq 2
"Please try again later.") expect(merge_request.notes.non_diff_notes.last.note).to eq(review_error_note)
end end
end end
...@@ -267,9 +265,9 @@ ...@@ -267,9 +265,9 @@
it 'updates progress note with an error message' do it 'updates progress note with an error message' do
completion.execute completion.execute
expect(Note.count).to eq 1 expect(merge_request.notes.count).to eq 3
expect(Note.last.note).to eq s_("DuoCodeReview|I have encountered some issues while I was reviewing. " \ expect(merge_request.notes.diff_notes.count).to eq 2
"Please try again later.") expect(merge_request.notes.non_diff_notes.last.note).to eq(review_error_note)
end end
end end
end end
...@@ -279,37 +277,23 @@ ...@@ -279,37 +277,23 @@
stub_const("#{described_class}::DRAFT_NOTES_COUNT_LIMIT", 1) stub_const("#{described_class}::DRAFT_NOTES_COUNT_LIMIT", 1)
end end
it 'builds draft note and publish to create diff note on new and updated files' do it 'creates diff note on the first file only' do
new_file_draft_note_params = {
merge_request: merge_request,
author: duo_code_review_bot,
note: review_answer,
position: {
base_sha: diff_refs.base_sha,
start_sha: diff_refs.start_sha,
head_sha: diff_refs.head_sha,
old_path: 'NEW.md',
new_path: 'NEW.md',
position_type: 'text',
old_line: nil,
new_line: 4,
ignore_whitespace_change: false
}
}
draft_note_1 = instance_double(DraftNote)
expect(DraftNote).to receive(:new).with(new_file_draft_note_params).and_return(draft_note_1)
expect(DraftNote).to receive(:bulk_insert!).with([draft_note_1], batch_size: 20)
expect_next_instance_of(
DraftNotes::PublishService,
merge_request,
duo_code_review_bot
) do |svc|
expect(svc).to receive(:execute).with(executing_user: user)
end
completion.execute completion.execute
diff_notes = merge_request.notes.diff_notes
expect(diff_notes.count).to eq 1
expect(diff_notes[0].position.to_h).to eq({
base_sha: diff_refs.base_sha,
start_sha: diff_refs.start_sha,
head_sha: diff_refs.head_sha,
old_path: 'NEW.md',
new_path: 'NEW.md',
position_type: 'text',
old_line: nil,
new_line: 2,
line_range: nil,
ignore_whitespace_change: false
})
end end
end end
end end
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册