diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-21 03:10:44 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-21 03:10:44 +0300 |
commit | c7a46b04196859929e8e4c04fbcbf8490f228edf (patch) | |
tree | d378b8cdd9f49903ed6f61810f61fb61217b6e3e /spec/services/issue_links | |
parent | 5c42c9355afa2bd5f95000b294ae6053f1d9219f (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/services/issue_links')
-rw-r--r-- | spec/services/issue_links/create_service_spec.rb | 176 | ||||
-rw-r--r-- | spec/services/issue_links/destroy_service_spec.rb | 61 | ||||
-rw-r--r-- | spec/services/issue_links/list_service_spec.rb | 194 |
3 files changed, 431 insertions, 0 deletions
diff --git a/spec/services/issue_links/create_service_spec.rb b/spec/services/issue_links/create_service_spec.rb new file mode 100644 index 00000000000..a60950e64cb --- /dev/null +++ b/spec/services/issue_links/create_service_spec.rb @@ -0,0 +1,176 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe IssueLinks::CreateService do + describe '#execute' do + let(:namespace) { create :namespace } + let(:project) { create :project, namespace: namespace } + let(:issue) { create :issue, project: project } + let(:user) { create :user } + let(:params) do + {} + end + + before do + project.add_developer(user) + end + + subject { described_class.new(issue, user, params).execute } + + context 'when the reference list is empty' do + let(:params) do + { issuable_references: [] } + end + + it 'returns error' do + is_expected.to eq(message: 'No Issue found for given params', status: :error, http_status: 404) + end + end + + context 'when Issue not found' do + let(:params) do + { issuable_references: ["##{non_existing_record_iid}"] } + end + + it 'returns error' do + is_expected.to eq(message: 'No Issue found for given params', status: :error, http_status: 404) + end + + it 'no relationship is created' do + expect { subject }.not_to change(IssueLink, :count) + end + end + + context 'when user has no permission to target project Issue' do + let(:target_issuable) { create :issue } + + let(:params) do + { issuable_references: [target_issuable.to_reference(project)] } + end + + it 'returns error' do + target_issuable.project.add_guest(user) + + is_expected.to eq(message: 'No Issue found for given params', status: :error, http_status: 404) + end + + it 'no relationship is created' do + expect { subject }.not_to change(IssueLink, :count) + end + end + + context 'source and target are the same issue' do + let(:params) do + { issuable_references: [issue.to_reference] } + end + + it 'does not create notes' do + expect(SystemNoteService).not_to receive(:relate_issue) + + subject + end + + it 'no relationship is created' do + expect { subject }.not_to change(IssueLink, :count) + end + end + + context 'when there is an issue to relate' do + let(:issue_a) { create :issue, project: project } + let(:another_project) { create :project, namespace: project.namespace } + let(:another_project_issue) { create :issue, project: another_project } + + let(:issue_a_ref) { issue_a.to_reference } + let(:another_project_issue_ref) { another_project_issue.to_reference(project) } + + let(:params) do + { issuable_references: [issue_a_ref, another_project_issue_ref] } + end + + before do + another_project.add_developer(user) + end + + it 'creates relationships' do + expect { subject }.to change(IssueLink, :count).from(0).to(2) + + expect(IssueLink.find_by!(target: issue_a)).to have_attributes(source: issue, link_type: 'relates_to') + expect(IssueLink.find_by!(target: another_project_issue)).to have_attributes(source: issue, link_type: 'relates_to') + end + + it 'returns success status' do + is_expected.to eq(status: :success) + end + + it 'creates notes' do + # First two-way relation notes + expect(SystemNoteService).to receive(:relate_issue) + .with(issue, issue_a, user) + expect(SystemNoteService).to receive(:relate_issue) + .with(issue_a, issue, user) + + # Second two-way relation notes + expect(SystemNoteService).to receive(:relate_issue) + .with(issue, another_project_issue, user) + expect(SystemNoteService).to receive(:relate_issue) + .with(another_project_issue, issue, user) + + subject + end + end + + context 'when reference of any already related issue is present' do + let(:issue_a) { create :issue, project: project } + let(:issue_b) { create :issue, project: project } + let(:issue_c) { create :issue, project: project } + + before do + create :issue_link, source: issue, target: issue_b, link_type: IssueLink::TYPE_RELATES_TO + create :issue_link, source: issue, target: issue_c, link_type: IssueLink::TYPE_RELATES_TO + end + + let(:params) do + { + issuable_references: [ + issue_a.to_reference, + issue_b.to_reference, + issue_c.to_reference + ], + link_type: IssueLink::TYPE_RELATES_TO + } + end + + it 'creates notes only for new relations' do + expect(SystemNoteService).to receive(:relate_issue).with(issue, issue_a, anything) + expect(SystemNoteService).to receive(:relate_issue).with(issue_a, issue, anything) + expect(SystemNoteService).not_to receive(:relate_issue).with(issue, issue_b, anything) + expect(SystemNoteService).not_to receive(:relate_issue).with(issue_b, issue, anything) + expect(SystemNoteService).not_to receive(:relate_issue).with(issue, issue_c, anything) + expect(SystemNoteService).not_to receive(:relate_issue).with(issue_c, issue, anything) + + subject + end + end + + context 'when there are invalid references' do + let(:issue_a) { create :issue, project: project } + + let(:params) do + { issuable_references: [issue.to_reference, issue_a.to_reference] } + end + + it 'creates links only for valid references' do + expect { subject }.to change { IssueLink.count }.by(1) + end + + it 'returns error status' do + expect(subject).to eq( + status: :error, + http_status: 422, + message: "#{issue.to_reference} cannot be added: cannot be related to itself" + ) + end + end + end +end diff --git a/spec/services/issue_links/destroy_service_spec.rb b/spec/services/issue_links/destroy_service_spec.rb new file mode 100644 index 00000000000..3f5b7ccf9ba --- /dev/null +++ b/spec/services/issue_links/destroy_service_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe IssueLinks::DestroyService do + describe '#execute' do + let(:project) { create(:project_empty_repo) } + let(:user) { create(:user) } + + subject { described_class.new(issue_link, user).execute } + + context 'when successfully removes an issue link' do + let(:issue_a) { create(:issue, project: project) } + let(:issue_b) { create(:issue, project: project) } + + let!(:issue_link) { create(:issue_link, source: issue_a, target: issue_b) } + + before do + project.add_reporter(user) + end + + it 'removes related issue' do + expect { subject }.to change(IssueLink, :count).from(1).to(0) + end + + it 'creates notes' do + # Two-way notes creation + expect(SystemNoteService).to receive(:unrelate_issue) + .with(issue_link.source, issue_link.target, user) + expect(SystemNoteService).to receive(:unrelate_issue) + .with(issue_link.target, issue_link.source, user) + + subject + end + + it 'returns success message' do + is_expected.to eq(message: 'Relation was removed', status: :success) + end + end + + context 'when failing to remove an issue link' do + let(:unauthorized_project) { create(:project) } + let(:issue_a) { create(:issue, project: project) } + let(:issue_b) { create(:issue, project: unauthorized_project) } + + let!(:issue_link) { create(:issue_link, source: issue_a, target: issue_b) } + + it 'does not remove relation' do + expect { subject }.not_to change(IssueLink, :count).from(1) + end + + it 'does not create notes' do + expect(SystemNoteService).not_to receive(:unrelate_issue) + end + + it 'returns error message' do + is_expected.to eq(message: 'No Issue Link found', status: :error, http_status: 404) + end + end + end +end diff --git a/spec/services/issue_links/list_service_spec.rb b/spec/services/issue_links/list_service_spec.rb new file mode 100644 index 00000000000..7a3ba845c7c --- /dev/null +++ b/spec/services/issue_links/list_service_spec.rb @@ -0,0 +1,194 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe IssueLinks::ListService do + let(:user) { create :user } + let(:project) { create(:project_empty_repo, :private) } + let(:issue) { create :issue, project: project } + let(:user_role) { :developer } + + before do + project.add_role(user, user_role) + end + + describe '#execute' do + subject { described_class.new(issue, user).execute } + + context 'user can see all issues' do + let(:issue_b) { create :issue, project: project } + let(:issue_c) { create :issue, project: project } + let(:issue_d) { create :issue, project: project } + + let!(:issue_link_c) do + create(:issue_link, source: issue_d, + target: issue) + end + + let!(:issue_link_b) do + create(:issue_link, source: issue, + target: issue_c) + end + + let!(:issue_link_a) do + create(:issue_link, source: issue, + target: issue_b) + end + + it 'ensures no N+1 queries are made' do + control_count = ActiveRecord::QueryRecorder.new { subject }.count + + project = create :project, :public + milestone = create :milestone, project: project + issue_x = create :issue, project: project, milestone: milestone + issue_y = create :issue, project: project, assignees: [user] + issue_z = create :issue, project: project + create :issue_link, source: issue_x, target: issue_y + create :issue_link, source: issue_x, target: issue_z + create :issue_link, source: issue_y, target: issue_z + + expect { subject }.not_to exceed_query_limit(control_count) + end + + it 'returns related issues JSON' do + expect(subject.size).to eq(3) + + expect(subject).to include(include(id: issue_b.id, + title: issue_b.title, + state: issue_b.state, + reference: issue_b.to_reference(project), + path: "/#{project.full_path}/-/issues/#{issue_b.iid}", + relation_path: "/#{project.full_path}/-/issues/#{issue.iid}/links/#{issue_link_a.id}")) + + expect(subject).to include(include(id: issue_c.id, + title: issue_c.title, + state: issue_c.state, + reference: issue_c.to_reference(project), + path: "/#{project.full_path}/-/issues/#{issue_c.iid}", + relation_path: "/#{project.full_path}/-/issues/#{issue.iid}/links/#{issue_link_b.id}")) + + expect(subject).to include(include(id: issue_d.id, + title: issue_d.title, + state: issue_d.state, + reference: issue_d.to_reference(project), + path: "/#{project.full_path}/-/issues/#{issue_d.iid}", + relation_path: "/#{project.full_path}/-/issues/#{issue.iid}/links/#{issue_link_c.id}")) + end + end + + context 'referencing a public project issue' do + let(:public_project) { create :project, :public } + let(:issue_b) { create :issue, project: public_project } + + let!(:issue_link) do + create(:issue_link, source: issue, target: issue_b) + end + + it 'presents issue' do + expect(subject.size).to eq(1) + end + end + + context 'referencing issue with removed relationships' do + context 'when referenced a deleted issue' do + let(:issue_b) { create :issue, project: project } + let!(:issue_link) do + create(:issue_link, source: issue, target: issue_b) + end + + it 'ignores issue' do + issue_b.destroy! + + is_expected.to eq([]) + end + end + + context 'when referenced an issue with deleted project' do + let(:issue_b) { create :issue, project: project } + let!(:issue_link) do + create(:issue_link, source: issue, target: issue_b) + end + + it 'ignores issue' do + project.destroy! + + is_expected.to eq([]) + end + end + + context 'when referenced an issue with deleted namespace' do + let(:issue_b) { create :issue, project: project } + let!(:issue_link) do + create(:issue_link, source: issue, target: issue_b) + end + + it 'ignores issue' do + project.namespace.destroy! + + is_expected.to eq([]) + end + end + end + + context 'user cannot see relations' do + context 'when user cannot see the referenced issue' do + let!(:issue_link) do + create(:issue_link, source: issue) + end + + it 'returns an empty list' do + is_expected.to eq([]) + end + end + + context 'when user cannot see the issue that referenced' do + let!(:issue_link) do + create(:issue_link, target: issue) + end + + it 'returns an empty list' do + is_expected.to eq([]) + end + end + end + + context 'remove relations' do + let!(:issue_link) do + create(:issue_link, source: issue, target: referenced_issue) + end + + context 'user can admin related issues just on target project' do + let(:user_role) { :guest } + let(:target_project) { create :project } + let(:referenced_issue) { create :issue, project: target_project } + + it 'returns no destroy relation path' do + target_project.add_developer(user) + + expect(subject.first[:relation_path]).to be_nil + end + end + + context 'user can admin related issues just on source project' do + let(:user_role) { :developer } + let(:target_project) { create :project } + let(:referenced_issue) { create :issue, project: target_project } + + it 'returns no destroy relation path' do + target_project.add_guest(user) + + expect(subject.first[:relation_path]).to be_nil + end + end + + context 'when user can admin related issues on both projects' do + let(:referenced_issue) { create :issue, project: project } + + it 'returns related issue destroy relation path' do + expect(subject.first[:relation_path]) + .to eq("/#{project.full_path}/-/issues/#{issue.iid}/links/#{issue_link.id}") + end + end + end + end +end |