summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorDouwe Maan <douwe@selenight.nl>2016-03-21 23:22:21 +0100
committerDouwe Maan <douwe@selenight.nl>2016-03-21 23:22:21 +0100
commitae7b2ef62cdf61c990f914d776a6fdfc2bc49fa2 (patch)
tree6f0022bf04b1b566fa79b979cc9cc373cd0ebaa1 /spec
parent8d544645f0ef114586212835cf011a3e268c9ec1 (diff)
parent0305dd98b32b5a989f2b84e0810cf5ddc14abd7f (diff)
downloadgitlab-ce-ae7b2ef62cdf61c990f914d776a6fdfc2bc49fa2.tar.gz
Merge branch 'master' into issue_12658
# Conflicts: # app/models/issue.rb # app/views/projects/_home_panel.html.haml # app/views/shared/projects/_project.html.haml # db/schema.rb # spec/models/project_spec.rb
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb35
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb23
-rw-r--r--spec/features/issues/move_spec.rb87
-rw-r--r--spec/helpers/labels_helper_spec.rb6
-rw-r--r--spec/helpers/projects_helper_spec.rb19
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/gfm/reference_rewriter_spec.rb89
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb20
-rw-r--r--spec/mailers/notify_spec.rb27
-rw-r--r--spec/models/issue_spec.rb59
-rw-r--r--spec/models/merge_request_spec.rb5
-rw-r--r--spec/models/project_spec.rb41
-rw-r--r--spec/models/project_wiki_spec.rb12
-rw-r--r--spec/models/repository_spec.rb30
-rw-r--r--spec/requests/api/issues_spec.rb34
-rw-r--r--spec/requests/api/merge_requests_spec.rb37
-rw-r--r--spec/services/issues/move_service_spec.rb213
-rw-r--r--spec/services/system_note_service_spec.rb53
-rw-r--r--spec/workers/repository_fork_worker_spec.rb19
19 files changed, 781 insertions, 30 deletions
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 2cd81231144..d6e4cd71ce6 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -1,11 +1,11 @@
require('spec_helper')
describe Projects::IssuesController do
- describe "GET #index" do
- let(:project) { create(:project) }
- let(:user) { create(:user) }
- let(:issue) { create(:issue, project: project) }
+ let(:project) { create(:project_empty_repo) }
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue, project: project) }
+ describe "GET #index" do
before do
sign_in(user)
project.team << [user, :developer]
@@ -41,7 +41,7 @@ describe Projects::IssuesController do
end
describe 'Confidential Issues' do
- let(:project) { create(:empty_project, :public) }
+ let(:project) { create(:project_empty_repo, :public) }
let(:assignee) { create(:assignee) }
let(:author) { create(:user) }
let(:non_member) { create(:user) }
@@ -186,4 +186,29 @@ describe Projects::IssuesController do
end
end
end
+
+ describe "DELETE #destroy" do
+ context "when the user is a developer" do
+ before { sign_in(user) }
+ it "rejects a developer to destroy an issue" do
+ delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context "when the user is owner" do
+ let(:owner) { create(:user) }
+ let(:namespace) { create(:namespace, owner: owner) }
+ let(:project) { create(:project, namespace: namespace) }
+
+ before { sign_in(owner) }
+
+ it "deletes the issue" do
+ delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
+
+ expect(response.status).to eq(302)
+ expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./).now
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index e82fe26c7a6..c5b034dc064 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -157,6 +157,29 @@ describe Projects::MergeRequestsController do
end
end
+ describe "DELETE #destroy" do
+ it "denies access to users unless they're admin or project owner" do
+ delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
+
+ expect(response.status).to eq(404)
+ end
+
+ context "when the user is owner" do
+ let(:owner) { create(:user) }
+ let(:namespace) { create(:namespace, owner: owner) }
+ let(:project) { create(:project, namespace: namespace) }
+
+ before { sign_in owner }
+
+ it "deletes the merge request" do
+ delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
+
+ expect(response.status).to eq(302)
+ expect(controller).to set_flash[:notice].to(/The merge request was successfully deleted\./).now
+ end
+ end
+ end
+
describe 'GET diffs' do
def go(format: 'html')
get :diffs,
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
new file mode 100644
index 00000000000..6fda0c31866
--- /dev/null
+++ b/spec/features/issues/move_spec.rb
@@ -0,0 +1,87 @@
+require 'rails_helper'
+
+feature 'issue move to another project' do
+ let(:user) { create(:user) }
+ let(:old_project) { create(:project) }
+ let(:text) { 'Some issue description' }
+
+ let(:issue) do
+ create(:issue, description: text, project: old_project, author: user)
+ end
+
+ background { login_as(user) }
+
+ context 'user does not have permission to move issue' do
+ background do
+ old_project.team << [user, :guest]
+
+ edit_issue(issue)
+ end
+
+ scenario 'moving issue to another project not allowed' do
+ expect(page).to have_no_select('move_to_project_id')
+ end
+ end
+
+ context 'user has permission to move issue' do
+ let!(:mr) { create(:merge_request, source_project: old_project) }
+ let(:new_project) { create(:project) }
+ let(:text) { 'Text with !1' }
+ let(:cross_reference) { old_project.to_reference }
+
+ background do
+ old_project.team << [user, :reporter]
+ new_project.team << [user, :reporter]
+
+ edit_issue(issue)
+ end
+
+ scenario 'moving issue to another project' do
+ select(new_project.name_with_namespace, from: 'move_to_project_id')
+ click_button('Save changes')
+
+ expect(current_url).to include project_path(new_project)
+
+ page.within('.issue') do
+ expect(page).to have_content("Text with #{cross_reference}!1")
+ expect(page).to have_content("Moved from #{cross_reference}#1")
+ expect(page).to have_content(issue.title)
+ end
+ end
+
+ context 'projects user does not have permission to move issue to exist' do
+ let!(:private_project) { create(:project, :private) }
+ let(:another_project) { create(:project) }
+ background { another_project.team << [user, :guest] }
+
+ scenario 'browsing projects in projects select' do
+ options = [ '', 'No project', new_project.name_with_namespace ]
+ expect(page).to have_select('move_to_project_id', options: options)
+ end
+ end
+
+ context 'issue has been already moved' do
+ let(:new_issue) { create(:issue, project: new_project) }
+ let(:issue) do
+ create(:issue, project: old_project, author: user, moved_to: new_issue)
+ end
+
+ scenario 'user wants to move issue that has already been moved' do
+ expect(page).to have_no_select('move_to_project_id')
+ end
+ end
+ end
+
+ def edit_issue(issue)
+ visit issue_path(issue)
+ page.within('.issuable-header') { click_link 'Edit' }
+ end
+
+ def issue_path(issue)
+ namespace_project_issue_path(issue.project.namespace, issue.project, issue)
+ end
+
+ def project_path(project)
+ namespace_project_path(new_project.namespace, new_project)
+ end
+end
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index eca8bc8ab2d..39042ff7e91 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -11,7 +11,7 @@ describe LabelsHelper do
end
it 'uses the instance variable' do
- expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name=#{label.name}"><span class="[\w\s\-]*has_tooltip".*</span></a>}
+ expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name=#{label.name}"><span class="[\w\s\-]*has-tooltip".*</span></a>}
end
end
@@ -41,8 +41,8 @@ describe LabelsHelper do
context 'with a tooltip argument' do
context 'set to false' do
- it 'does not include the has_tooltip class' do
- expect(link_to_label(label, tooltip: false)).not_to match %r{has_tooltip}
+ it 'does not include the has-tooltip class' do
+ expect(link_to_label(label, tooltip: false)).not_to match %r{has-tooltip}
end
end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index dc324f1e60e..c258cfebd73 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -86,4 +86,23 @@ describe ProjectsHelper do
end
end
end
+
+ describe 'default_clone_protocol' do
+ describe 'using HTTP' do
+ it 'returns HTTP' do
+ expect(helper).to receive(:current_user).and_return(nil)
+
+ expect(helper.send(:default_clone_protocol)).to eq('http')
+ end
+ end
+
+ describe 'using HTTPS' do
+ it 'returns HTTPS' do
+ allow(Gitlab.config.gitlab).to receive(:protocol).and_return('https')
+ expect(helper).to receive(:current_user).and_return(nil)
+
+ expect(helper.send(:default_clone_protocol)).to eq('https')
+ end
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 4c1d4a2d24c..94468abcbb3 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -56,7 +56,7 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
describe 'label span element' do
it 'includes default classes' do
doc = reference_filter("Label #{reference}")
- expect(doc.css('a span').first.attr('class')).to eq 'label color-label has_tooltip'
+ expect(doc.css('a span').first.attr('class')).to eq 'label color-label has-tooltip'
end
it 'includes a style attribute' do
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
new file mode 100644
index 00000000000..0a7ca3ec848
--- /dev/null
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe Gitlab::Gfm::ReferenceRewriter do
+ let(:text) { 'some text' }
+ let(:old_project) { create(:project) }
+ let(:new_project) { create(:project) }
+ let(:user) { create(:user) }
+
+ before { old_project.team << [user, :guest] }
+
+ describe '#rewrite' do
+ subject do
+ described_class.new(text, old_project, user).rewrite(new_project)
+ end
+
+ context 'multiple issues and merge requests referenced' do
+ let!(:issue_first) { create(:issue, project: old_project) }
+ let!(:issue_second) { create(:issue, project: old_project) }
+ let!(:merge_request) { create(:merge_request, source_project: old_project) }
+
+ context 'plain text description' do
+ let(:text) { 'Description that references #1, #2 and !1' }
+
+ it { is_expected.to include issue_first.to_reference(new_project) }
+ it { is_expected.to include issue_second.to_reference(new_project) }
+ it { is_expected.to include merge_request.to_reference(new_project) }
+ end
+
+ context 'description with ignored elements' do
+ let(:text) do
+ "Hi. This references #1, but not `#2`\n" +
+ '<pre>and not !1</pre>'
+ end
+
+ it { is_expected.to include issue_first.to_reference(new_project) }
+ it { is_expected.to_not include issue_second.to_reference(new_project) }
+ it { is_expected.to_not include merge_request.to_reference(new_project) }
+ end
+
+ context 'description ambigous elements' do
+ context 'url' do
+ let(:url) { 'http://gitlab.com/#1' }
+ let(:text) { "This references #1, but not #{url}" }
+
+ it { is_expected.to include url }
+ end
+
+ context 'code' do
+ let(:text) { "#1, but not `[#1]`" }
+ it { is_expected.to eq "#{issue_first.to_reference(new_project)}, but not `[#1]`" }
+ end
+
+ context 'code reverse' do
+ let(:text) { "not `#1`, but #1" }
+ it { is_expected.to eq "not `#1`, but #{issue_first.to_reference(new_project)}" }
+ end
+
+ context 'code in random order' do
+ let(:text) { "#1, `#1`, #1, `#1`" }
+ let(:ref) { issue_first.to_reference(new_project) }
+
+ it { is_expected.to eq "#{ref}, `#1`, #{ref}, `#1`" }
+ end
+
+ context 'description with labels' do
+ let!(:label) { create(:label, id: 123, name: 'test', project: old_project) }
+ let(:project_ref) { old_project.to_reference }
+
+ context 'label referenced by id' do
+ let(:text) { '#1 and ~123' }
+ it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~123} }
+ end
+
+ context 'label referenced by text' do
+ let(:text) { '#1 and ~"test"' }
+ it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~123} }
+ end
+ end
+ end
+
+ context 'reference contains milestone' do
+ let(:milestone) { create(:milestone) }
+ let(:text) { "milestone ref: #{milestone.to_reference}" }
+
+ it { is_expected.to eq text }
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 65af37e24f1..7c617723e6d 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -124,4 +124,24 @@ describe Gitlab::ReferenceExtractor, lib: true do
expect(extracted).to match_array([issue])
end
end
+
+ describe '#all' do
+ let(:issue) { create(:issue, project: project) }
+ let(:label) { create(:label, project: project) }
+ let(:text) { "Ref. #{issue.to_reference} and #{label.to_reference}" }
+
+ before do
+ project.team << [project.creator, :developer]
+ subject.analyze(text)
+ end
+
+ it 'returns all referables' do
+ expect(subject.all).to match_array([issue, label])
+ end
+ end
+
+ describe '.references_pattern' do
+ subject { described_class.references_pattern }
+ it { is_expected.to be_kind_of Regexp }
+ end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index f910424d85b..9b47acfe0cd 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -158,6 +158,33 @@ describe Notify do
is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
end
end
+
+ describe 'moved to another project' do
+ let(:new_issue) { create(:issue) }
+ subject { Notify.issue_moved_email(recipient, issue, new_issue, current_user) }
+
+ it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'it should show Gmail Actions View Issue link'
+ it_behaves_like 'an unsubscribeable thread'
+
+ it 'contains description about action taken' do
+ is_expected.to have_body_text 'Issue was moved to another project'
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/i
+ end
+
+ it 'contains link to new issue' do
+ new_issue_url = namespace_project_issue_path(new_issue.project.namespace,
+ new_issue.project, new_issue)
+ is_expected.to have_body_text new_issue_url
+ end
+
+ it 'contains a link to the original issue' do
+ is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
+ end
+ end
end
context 'for merge requests' do
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 540a62eb1f8..3c34b1d397f 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -37,6 +37,11 @@ describe Issue, models: true do
subject { create(:issue) }
+ describe "act_as_paranoid" do
+ it { is_expected.to have_db_column(:deleted_at) }
+ it { is_expected.to have_db_index(:deleted_at) }
+ end
+
describe '#to_reference' do
it 'returns a String reference to the object' do
expect(subject.to_reference).to eq "##{subject.iid}"
@@ -130,12 +135,62 @@ describe Issue, models: true do
end
end
+ describe '#can_move?' do
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue) }
+ subject { issue.can_move?(user) }
+
+ context 'user is not a member of project issue belongs to' do
+ it { is_expected.to eq false}
+ end
+
+ context 'user is reporter in project issue belongs to' do
+ let(:project) { create(:project) }
+ let(:issue) { create(:issue, project: project) }
+
+ before { project.team << [user, :reporter] }
+
+ it { is_expected.to eq true }
+
+ context 'checking destination project also' do
+ subject { issue.can_move?(user, to_project) }
+ let(:to_project) { create(:project) }
+
+ context 'destination project allowed' do
+ before { to_project.team << [user, :reporter] }
+ it { is_expected.to eq true }
+ end
+
+ context 'destination project not allowed' do
+ before { to_project.team << [user, :guest] }
+ it { is_expected.to eq false }
+ end
+ end
+ end
+ end
+
+ describe '#moved?' do
+ let(:issue) { create(:issue) }
+ subject { issue.moved? }
+
+ context 'issue not moved' do
+ it { is_expected.to eq false }
+ end
+
+ context 'issue already moved' do
+ let(:moved_to_issue) { create(:issue) }
+ let(:issue) { create(:issue, moved_to: moved_to_issue) }
+
+ it { is_expected.to eq true }
+ end
+ end
+
describe '#related_branches' do
it "selects the right branches" do
allow(subject.project.repository).to receive(:branch_names).
- and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name])
+ and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name])
- expect(subject.related_branches).to eq [subject.to_branch_name]
+ expect(subject.related_branches).to eq([subject.to_branch_name])
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index f2f07e4ee17..bd0a4ebe337 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -49,6 +49,11 @@ describe MergeRequest, models: true do
it { is_expected.to include_module(Taskable) }
end
+ describe "act_as_paranoid" do
+ it { is_expected.to have_db_column(:deleted_at) }
+ it { is_expected.to have_db_index(:deleted_at) }
+ end
+
describe 'validation' do
it { is_expected.to validate_presence_of(:target_branch) }
it { is_expected.to validate_presence_of(:source_branch) }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index dba7ffc8565..757324184bd 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -732,4 +732,45 @@ describe Project, models: true do
it { expect(internal_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey }
end
end
+
+ describe '#create_repository' do
+ let(:project) { create(:project) }
+ let(:shell) { Gitlab::Shell.new }
+
+ before do
+ allow(project).to receive(:gitlab_shell).and_return(shell)
+ end
+
+ context 'using a regular repository' do
+ it 'creates the repository' do
+ expect(shell).to receive(:add_repository).
+ with(project.path_with_namespace).
+ and_return(true)
+
+ expect(project.repository).to receive(:after_create)
+
+ expect(project.create_repository).to eq(true)
+ end
+
+ it 'adds an error if the repository could not be created' do
+ expect(shell).to receive(:add_repository).
+ with(project.path_with_namespace).
+ and_return(false)
+
+ expect(project.repository).not_to receive(:after_create)
+
+ expect(project.create_repository).to eq(false)
+ expect(project.errors).not_to be_empty
+ end
+ end
+
+ context 'using a forked repository' do
+ it 'does nothing' do
+ expect(project).to receive(:forked?).and_return(true)
+ expect(shell).not_to receive(:add_repository)
+
+ project.create_repository
+ end
+ end
+ end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index a2085df5bcd..532e3f013fd 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -244,6 +244,18 @@ describe ProjectWiki, models: true do
end
end
+ describe '#create_repo!' do
+ it 'creates a repository' do
+ expect(subject).to receive(:init_repo).
+ with(subject.path_with_namespace).
+ and_return(true)
+
+ expect(subject.repository).to receive(:after_create)
+
+ expect(subject.create_repo!).to be_an_instance_of(Gollum::Wiki)
+ end
+ end
+
private
def create_temp_repo(path)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index a57229a4fdf..7eac70ae948 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -537,6 +537,12 @@ describe Repository, models: true do
repository.before_delete
end
+
+ it 'flushes the exists cache' do
+ expect(repository).to receive(:expire_exists_cache)
+
+ repository.before_delete
+ end
end
describe 'when a repository exists' do
@@ -593,6 +599,12 @@ describe Repository, models: true do
repository.after_import
end
+
+ it 'flushes the exists cache' do
+ expect(repository).to receive(:expire_exists_cache)
+
+ repository.after_import
+ end
end
describe '#after_push_commit' do
@@ -619,6 +631,14 @@ describe Repository, models: true do
end
end
+ describe '#after_create' do
+ it 'flushes the exists cache' do
+ expect(repository).to receive(:expire_exists_cache)
+
+ repository.after_create
+ end
+ end
+
describe "#main_language" do
it 'shows the main language of the project' do
expect(repository.main_language).to eq("Ruby")
@@ -781,6 +801,16 @@ describe Repository, models: true do
end
end
+ describe '#expire_exists_cache' do
+ let(:cache) { repository.send(:cache) }
+
+ it 'expires the cache' do
+ expect(cache).to receive(:expire).with(:exists?)
+
+ repository.expire_exists_cache
+ end
+ end
+
describe '#build_cache' do
let(:cache) { repository.send(:cache) }
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index bb2ab058003..ce55cb7b0ae 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -2,12 +2,12 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
- let(:user) { create(:user) }
- let(:non_member) { create(:user) }
- let(:author) { create(:author) }
- let(:assignee) { create(:assignee) }
- let(:admin) { create(:admin) }
- let!(:project) { create(:project, :public, namespace: user.namespace ) }
+ let(:user) { create(:user) }
+ let(:non_member) { create(:user) }
+ let(:author) { create(:author) }
+ let(:assignee) { create(:assignee) }
+ let(:admin) { create(:user, :admin) }
+ let!(:project) { create(:project, :public, namespace: user.namespace ) }
let!(:closed_issue) do
create :closed_issue,
author: user,
@@ -469,9 +469,25 @@ describe API::API, api: true do
end
describe "DELETE /projects/:id/issues/:issue_id" do
- it "should delete a project issue" do
- delete api("/projects/#{project.id}/issues/#{issue.id}", user)
- expect(response.status).to eq(405)
+ it "rejects a non member from deleting an issue" do
+ delete api("/projects/#{project.id}/issues/#{issue.id}", non_member)
+ expect(response.status).to be(403)
+ end
+
+ it "rejects a developer from deleting an issue" do
+ delete api("/projects/#{project.id}/issues/#{issue.id}", author)
+ expect(response.status).to be(403)
+ end
+
+ context "when the user is project owner" do
+ let(:owner) { create(:user) }
+ let(:project) { create(:project, namespace: owner.namespace) }
+
+ it "deletes the issue if an admin requests it" do
+ delete api("/projects/#{project.id}/issues/#{issue.id}", owner)
+ expect(response.status).to eq(200)
+ expect(json_response['state']).to eq 'opened'
+ end
end
end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 4fd1df25568..c9175a4d6eb 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -2,15 +2,17 @@ require "spec_helper"
describe API::API, api: true do
include ApiHelpers
- let(:base_time) { Time.now }
- let(:user) { create(:user) }
- let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
+ let(:base_time) { Time.now }
+ let(:user) { create(:user) }
+ let(:admin) { create(:user, :admin) }
+ let(:non_member) { create(:user) }
+ let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) }
let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) }
let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds) }
- let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
- let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
- let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
+ let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
+ let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
+ let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
before do
project.team << [user, :reporters]
@@ -315,6 +317,29 @@ describe API::API, api: true do
end
end
+ describe "DELETE /projects/:id/merge_requests/:merge_request_id" do
+ context "when the user is developer" do
+ let(:developer) { create(:user) }
+
+ before do
+ project.team << [developer, :developer]
+ end
+
+ it "denies the deletion of the merge request" do
+ delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", developer)
+ expect(response.status).to be(403)
+ end
+ end
+
+ context "when the user is project owner" do
+ it "destroys the merge request owners can destroy" do
+ delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+
describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do
it "should return merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
new file mode 100644
index 00000000000..14cc20e529a
--- /dev/null
+++ b/spec/services/issues/move_service_spec.rb
@@ -0,0 +1,213 @@
+require 'spec_helper'
+
+describe Issues::MoveService, services: true do
+ let(:user) { create(:user) }
+ let(:author) { create(:user) }
+ let(:title) { 'Some issue' }
+ let(:description) { 'Some issue description' }
+ let(:old_project) { create(:project) }
+ let(:new_project) { create(:project) }
+
+ let(:old_issue) do
+ create(:issue, title: title, description: description,
+ project: old_project, author: author)
+ end
+
+ let(:move_service) do
+ described_class.new(old_project, user)
+ end
+
+ shared_context 'user can move issue' do
+ before do
+ old_project.team << [user, :reporter]
+ new_project.team << [user, :reporter]
+ end
+ end
+
+ describe '#execute' do
+ shared_context 'issue move executed' do
+ let!(:new_issue) { move_service.execute(old_issue, new_project) }
+ end
+
+ context 'issue movable' do
+ include_context 'user can move issue'
+
+ context 'generic issue' do
+ include_context 'issue move executed'
+
+ it 'creates a new issue in a new project' do
+ expect(new_issue.project).to eq new_project
+ end
+
+ it 'rewrites issue title' do
+ expect(new_issue.title).to eq title
+ end
+
+ it 'rewrites issue description' do
+ expect(new_issue.description).to eq description
+ end
+
+ it 'adds system note to old issue at the end' do
+ expect(old_issue.notes.last.note).to match /^Moved to/
+ end
+
+ it 'adds system note to new issue at the end' do
+ expect(new_issue.notes.last.note).to match /^Moved from/
+ end
+
+ it 'closes old issue' do
+ expect(old_issue.closed?).to be true
+ end
+
+ it 'persists new issue' do
+ expect(new_issue.persisted?).to be true
+ end
+
+ it 'persists all changes' do
+ expect(old_issue.changed?).to be false
+ expect(new_issue.changed?).to be false
+ end
+
+ it 'preserves author' do
+ expect(new_issue.author).to eq author
+ end
+
+ it 'removes data that is invalid in new context' do
+ expect(new_issue.milestone).to be_nil
+ expect(new_issue.labels).to be_empty
+ end
+
+ it 'creates a new internal id for issue' do
+ expect(new_issue.iid).to be 1
+ end
+
+ it 'marks issue as moved' do
+ expect(old_issue.moved?).to eq true
+ expect(old_issue.moved_to).to eq new_issue
+ end
+ end
+
+ context 'issue with notes' do
+ context 'notes without references' do
+ let(:notes_params) do
+ [{ system: false, note: 'Some comment 1' },
+ { system: true, note: 'Some system note' },
+ { system: false, note: 'Some comment 2' }]
+ end
+
+ let(:notes_contents) { notes_params.map { |n| n[:note] } }
+
+ before do
+ note_params = { noteable: old_issue, project: old_project, author: author }
+ notes_params.each do |note|
+ create(:note, note_params.merge(note))
+ end
+ end
+
+ include_context 'issue move executed'
+
+ let(:all_notes) { new_issue.notes.order('id ASC') }
+ let(:system_notes) { all_notes.system }
+ let(:user_notes) { all_notes.user }
+
+ it 'rewrites existing notes in valid order' do
+ expect(all_notes.pluck(:note).first(3)).to eq notes_contents
+ end
+
+ it 'adds a system note about move after rewritten notes' do
+ expect(system_notes.last.note).to match /^Moved from/
+ end
+
+ it 'preserves orignal author of comment' do
+ expect(user_notes.pluck(:author_id)).to all(eq(author.id))
+ end
+
+ it 'preserves time when note has been created at' do
+ expect(old_issue.notes.first.created_at)
+ .to eq new_issue.notes.first.created_at
+ end
+ end
+
+ context 'notes with references' do
+ before do
+ create(:merge_request, source_project: old_project)
+ create(:note, noteable: old_issue, project: old_project, author: author,
+ note: 'Note with reference to merge request !1')
+ end
+
+ include_context 'issue move executed'
+ let(:new_note) { new_issue.notes.first }
+
+ it 'rewrites references using a cross reference to old project' do
+ expect(new_note.note)
+ .to eq "Note with reference to merge request #{old_project.to_reference}!1"
+ end
+ end
+ end
+
+ describe 'rewritting references' do
+ include_context 'issue move executed'
+
+ context 'issue reference' do
+ let(:another_issue) { create(:issue, project: old_project) }
+ let(:description) { "Some description #{another_issue.to_reference}" }
+
+ it 'rewrites referenced issues creating cross project reference' do
+ expect(new_issue.description)
+ .to eq "Some description #{old_project.to_reference}#{another_issue.to_reference}"
+ end
+ end
+ end
+
+ context 'moving to same project' do
+ let(:new_project) { old_project }
+
+ it 'raises error' do
+ expect { move_service.execute(old_issue, new_project) }
+ .to raise_error(StandardError, /Cannot move issue/)
+ end
+ end
+ end
+
+ describe 'move permissions' do
+ let(:move) { move_service.execute(old_issue, new_project) }
+
+ context 'user is reporter in both projects' do
+ include_context 'user can move issue'
+ it { expect { move }.to_not raise_error }
+ end
+
+ context 'user is reporter only in new project' do
+ before { new_project.team << [user, :reporter] }
+ it { expect { move }.to raise_error(StandardError, /permissions/) }
+ end
+
+ context 'user is reporter only in old project' do
+ before { old_project.team << [user, :reporter] }
+ it { expect { move }.to raise_error(StandardError, /permissions/) }
+ end
+
+ context 'user is reporter in one project and guest in another' do
+ before do
+ new_project.team << [user, :guest]
+ old_project.team << [user, :reporter]
+ end
+
+ it { expect { move }.to raise_error(StandardError, /permissions/) }
+ end
+
+ context 'issue has already been moved' do
+ include_context 'user can move issue'
+
+ let(:moved_to_issue) { create(:issue) }
+
+ let(:old_issue) do
+ create(:issue, project: old_project, author: author,
+ moved_to: moved_to_issue)
+ end
+
+ it { expect { move }.to raise_error(StandardError, /permissions/) }
+ end
+ end
+ end
+end
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 8e6292014d4..240eae10052 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -453,6 +453,59 @@ describe SystemNoteService, services: true do
end
end
+ describe '.noteable_moved' do
+ let(:new_project) { create(:project) }
+ let(:new_noteable) { create(:issue, project: new_project) }
+
+ subject do
+ described_class.noteable_moved(noteable, project, new_noteable, author, direction: direction)
+ end
+
+ shared_examples 'cross project mentionable' do
+ include GitlabMarkdownHelper
+
+ it 'should contain cross reference to new noteable' do
+ expect(subject.note).to include cross_project_reference(new_project, new_noteable)
+ end
+
+ it 'should mention referenced noteable' do
+ expect(subject.note).to include new_noteable.to_reference
+ end
+
+ it 'should mention referenced project' do
+ expect(subject.note).to include new_project.to_reference
+ end
+ end
+
+ context 'moved to' do
+ let(:direction) { :to }
+
+ it_behaves_like 'cross project mentionable'
+
+ it 'should notify about noteable being moved to' do
+ expect(subject.note).to match /Moved to/
+ end
+ end
+
+ context 'moved from' do
+ let(:direction) { :from }
+
+ it_behaves_like 'cross project mentionable'
+
+ it 'should notify about noteable being moved from' do
+ expect(subject.note).to match /Moved from/
+ end
+ end
+
+ context 'invalid direction' do
+ let(:direction) { :invalid }
+
+ it 'should raise error' do
+ expect { subject }.to raise_error StandardError, /Invalid direction/
+ end
+ end
+ end
+
include JiraServiceHelper
describe 'JIRA integration' do
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index 172537474ee..4ef05eb29d2 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -3,12 +3,17 @@ require 'spec_helper'
describe RepositoryForkWorker do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
+ let(:shell) { Gitlab::Shell.new }
subject { RepositoryForkWorker.new }
+ before do
+ allow(subject).to receive(:gitlab_shell).and_return(shell)
+ end
+
describe "#perform" do
it "creates a new repository from a fork" do
- expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).with(
+ expect(shell).to receive(:fork_repository).with(
project.path_with_namespace,
fork_project.namespace.path
).and_return(true)
@@ -19,20 +24,26 @@ describe RepositoryForkWorker do
fork_project.namespace.path)
end
- it 'flushes the empty caches' do
- expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).
+ it 'flushes various caches' do
+ expect(shell).to receive(:fork_repository).
with(project.path_with_namespace, fork_project.namespace.path).
and_return(true)
expect_any_instance_of(Repository).to receive(:expire_emptiness_caches).
and_call_original
+ expect_any_instance_of(Repository).to receive(:expire_exists_cache).
+ and_call_original
+
subject.perform(project.id, project.path_with_namespace,
fork_project.namespace.path)
end
it "handles bad fork" do
- expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).and_return(false)
+ expect(shell).to receive(:fork_repository).and_return(false)
+
+ expect(subject.logger).to receive(:error)
+
subject.perform(
project.id,
project.path_with_namespace,