Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-21 03:10:44 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-21 03:10:44 +0300
commitc7a46b04196859929e8e4c04fbcbf8490f228edf (patch)
treed378b8cdd9f49903ed6f61810f61fb61217b6e3e /spec/services/issue_links
parent5c42c9355afa2bd5f95000b294ae6053f1d9219f (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.rb176
-rw-r--r--spec/services/issue_links/destroy_service_spec.rb61
-rw-r--r--spec/services/issue_links/list_service_spec.rb194
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