diff options
Diffstat (limited to 'spec/services/git_push_service_spec.rb')
-rw-r--r-- | spec/services/git_push_service_spec.rb | 283 |
1 files changed, 163 insertions, 120 deletions
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index ca827fc0f39..3fb677b65be 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -3,27 +3,24 @@ require 'spec_helper' describe GitPushService, services: true do include RepoHelpers - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:blankrev) { Gitlab::Git::BLANK_SHA } + let(:oldrev) { sample_commit.parent_id } + let(:newrev) { sample_commit.id } + let(:ref) { 'refs/heads/master' } before do project.team << [user, :master] - @blankrev = Gitlab::Git::BLANK_SHA - @oldrev = sample_commit.parent_id - @newrev = sample_commit.id - @ref = 'refs/heads/master' end describe 'Push branches' do - let(:oldrev) { @oldrev } - let(:newrev) { @newrev } - subject do - execute_service(project, user, oldrev, newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end context 'new branch' do - let(:oldrev) { @blankrev } + let(:oldrev) { blankrev } it { is_expected.to be_truthy } @@ -51,7 +48,7 @@ describe GitPushService, services: true do end context 'rm branch' do - let(:newrev) { @blankrev } + let(:newrev) { blankrev } it { is_expected.to be_truthy } @@ -70,24 +67,20 @@ describe GitPushService, services: true do end describe "Git Push Data" do - before do - service = execute_service(project, user, @oldrev, @newrev, @ref ) - @push_data = service.push_data - @commit = project.commit(@newrev) - end + let(:commit) { project.commit(newrev) } - subject { @push_data } + subject { push_data_from_service(project, user, oldrev, newrev, ref) } it { is_expected.to include(object_kind: 'push') } - it { is_expected.to include(before: @oldrev) } - it { is_expected.to include(after: @newrev) } - it { is_expected.to include(ref: @ref) } + it { is_expected.to include(before: oldrev) } + it { is_expected.to include(after: newrev) } + it { is_expected.to include(ref: ref) } it { is_expected.to include(user_id: user.id) } it { is_expected.to include(user_name: user.name) } it { is_expected.to include(project_id: project.id) } context "with repository data" do - subject { @push_data[:repository] } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:repository] } it { is_expected.to include(name: project.name) } it { is_expected.to include(url: project.url_to_repo) } @@ -96,7 +89,7 @@ describe GitPushService, services: true do end context "with commits" do - subject { @push_data[:commits] } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits] } it { is_expected.to be_an(Array) } it 'has 1 element' do @@ -104,11 +97,11 @@ describe GitPushService, services: true do end context "the commit" do - subject { @push_data[:commits].first } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits].first } - it { is_expected.to include(id: @commit.id) } - it { is_expected.to include(message: @commit.safe_message) } - it { is_expected.to include(timestamp: @commit.date.xmlschema) } + it { is_expected.to include(id: commit.id) } + it { is_expected.to include(message: commit.safe_message) } + it { expect(subject[:timestamp].in_time_zone).to eq(commit.date.in_time_zone) } it do is_expected.to include( url: [ @@ -116,23 +109,23 @@ describe GitPushService, services: true do project.namespace.to_param, project.to_param, 'commit', - @commit.id + commit.id ].join('/') ) end context "with a author" do - subject { @push_data[:commits].first[:author] } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits].first[:author] } - it { is_expected.to include(name: @commit.author_name) } - it { is_expected.to include(email: @commit.author_email) } + it { is_expected.to include(name: commit.author_name) } + it { is_expected.to include(email: commit.author_email) } end end end end describe "Pipelines" do - subject { execute_service(project, user, @oldrev, @newrev, @ref) } + subject { execute_service(project, user, oldrev, newrev, ref) } before do stub_ci_pipeline_to_return_yaml_file @@ -145,29 +138,26 @@ describe GitPushService, services: true do end describe "Push Event" do - before do - service = execute_service(project, user, @oldrev, @newrev, @ref ) - @event = Event.find_by_action(Event::PUSHED) - @push_data = service.push_data - end + let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) } + let(:event) { Event.find_by_action(Event::PUSHED) } - it { expect(@event).not_to be_nil } - it { expect(@event.project).to eq(project) } - it { expect(@event.action).to eq(Event::PUSHED) } - it { expect(@event.data).to eq(@push_data) } + it { expect(event).not_to be_nil } + it { expect(event.project).to eq(project) } + it { expect(event.action).to eq(Event::PUSHED) } + it { expect(event.data).to eq(push_data) } context "Updates merge requests" do it "when pushing a new branch for the first time" do expect(UpdateMergeRequestsWorker).to receive(:perform_async) - .with(project.id, user.id, @blankrev, 'newrev', 'refs/heads/master') - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + .with(project.id, user.id, blankrev, 'newrev', ref) + execute_service(project, user, blankrev, 'newrev', ref ) end end - + context "Sends System Push data" do it "when pushing on a branch" do - expect(SystemHookPushWorker).to receive(:perform_async).with(@push_data, :push_hooks) - execute_service(project, user, @oldrev, @newrev, @ref ) + expect(SystemHookPushWorker).to receive(:perform_async).with(push_data, :push_hooks) + execute_service(project, user, oldrev, newrev, ref) end end end @@ -177,13 +167,13 @@ describe GitPushService, services: true do it "calls the copy attributes method for the first push to the default branch" do expect(project.repository).to receive(:copy_gitattributes).with('master') - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master') + execute_service(project, user, blankrev, 'newrev', ref) end it "calls the copy attributes method for changes to the default branch" do - expect(project.repository).to receive(:copy_gitattributes).with('refs/heads/master') + expect(project.repository).to receive(:copy_gitattributes).with(ref) - execute_service(project, user, 'oldrev', 'newrev', 'refs/heads/master') + execute_service(project, user, 'oldrev', 'newrev', ref) end end @@ -196,7 +186,7 @@ describe GitPushService, services: true do it "does not call copy attributes method" do expect(project.repository).not_to receive(:copy_gitattributes) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end end end @@ -206,7 +196,7 @@ describe GitPushService, services: true do it "when pushing a branch for the first time" do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER]) expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER]) @@ -217,7 +207,7 @@ describe GitPushService, services: true do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).to be_empty end @@ -227,7 +217,7 @@ describe GitPushService, services: true do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER]) @@ -242,7 +232,7 @@ describe GitPushService, services: true do expect(project.default_branch).to eq("master") expect_any_instance_of(ProtectedBranches::CreateService).not_to receive(:execute) - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::NO_ACCESS]) @@ -254,7 +244,7 @@ describe GitPushService, services: true do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER]) expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER]) @@ -262,7 +252,7 @@ describe GitPushService, services: true do it "when pushing new commits to existing branch" do expect(project).to receive(:execute_hooks) - execute_service(project, user, 'oldrev', 'newrev', 'refs/heads/master' ) + execute_service(project, user, 'oldrev', 'newrev', ref) end end end @@ -292,7 +282,7 @@ describe GitPushService, services: true do it "creates a note if a pushed commit mentions an issue" do expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "only creates a cross-reference note if one doesn't already exist" do @@ -300,7 +290,7 @@ describe GitPushService, services: true do expect(SystemNoteService).not_to receive(:cross_reference).with(issue, commit, commit_author) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "defaults to the pushing user if the commit's author is not known" do @@ -310,16 +300,16 @@ describe GitPushService, services: true do ) expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, user) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "finds references in the first push to a non-default branch" do - allow(project.repository).to receive(:commits_between).with(@blankrev, @newrev).and_return([]) - allow(project.repository).to receive(:commits_between).with("master", @newrev).and_return([commit]) + allow(project.repository).to receive(:commits_between).with(blankrev, newrev).and_return([]) + allow(project.repository).to receive(:commits_between).with("master", newrev).and_return([commit]) expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author) - execute_service(project, user, @blankrev, @newrev, 'refs/heads/other' ) + execute_service(project, user, blankrev, newrev, 'refs/heads/other') end end @@ -349,14 +339,14 @@ describe GitPushService, services: true do context "while saving the 'first_mentioned_in_commit_at' metric for an issue" do it 'sets the metric for referenced issues' do - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) expect(issue.reload.metrics.first_mentioned_in_commit_at).to be_like_time(commit_time) end it 'does not set the metric for non-referenced issues' do non_referenced_issue = create(:issue, project: project) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) expect(non_referenced_issue.reload.metrics.first_mentioned_in_commit_at).to be_nil end @@ -388,30 +378,18 @@ describe GitPushService, services: true do context "to default branches" do it "closes issues" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) expect(Issue.find(issue.id)).to be_closed end it "adds a note indicating that the issue is now closed" do expect(SystemNoteService).to receive(:change_status).with(issue, project, commit_author, "closed", closing_commit) - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) end it "doesn't create additional cross-reference notes" do expect(SystemNoteService).not_to receive(:cross_reference) - execute_service(project, commit_author, @oldrev, @newrev, @ref ) - end - - it "doesn't close issues when external issue tracker is in use" do - allow_any_instance_of(Project).to receive(:default_issues_tracker?) - .and_return(false) - external_issue_tracker = double(title: 'My Tracker', issue_path: issue.iid, reference_pattern: project.issue_reference_pattern) - allow_any_instance_of(Project).to receive(:external_issue_tracker).and_return(external_issue_tracker) - - # The push still shouldn't create cross-reference notes. - expect do - execute_service(project, commit_author, @oldrev, @newrev, 'refs/heads/hurf' ) - end.not_to change { Note.where(project_id: project.id, system: true).count } + execute_service(project, commit_author, oldrev, newrev, ref) end end @@ -423,11 +401,11 @@ describe GitPushService, services: true do it "creates cross-reference notes" do expect(SystemNoteService).to receive(:cross_reference).with(issue, closing_commit, commit_author) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "doesn't close issues" do - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) expect(Issue.find(issue.id)).to be_opened end end @@ -444,11 +422,12 @@ describe GitPushService, services: true do stub_jira_urls("JIRA-1") allow(closing_commit).to receive_messages({ - issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern), - safe_message: message, - author_name: commit_author.name, - author_email: commit_author.email - }) + issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern), + safe_message: message, + author_name: commit_author.name, + author_email: commit_author.email + }) + allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) allow(project.repository).to receive_messages(commits_between: [closing_commit]) @@ -462,7 +441,7 @@ describe GitPushService, services: true do let(:message) { "this is some work.\n\nrelated to JIRA-1" } it "initiates one api call to jira server to mention the issue" do - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with( body: /mentioned this issue in/ @@ -472,7 +451,11 @@ describe GitPushService, services: true do context "closing an issue" do let(:message) { "this is some work.\n\ncloses JIRA-1" } - let(:comment_body) { { body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.path_with_namespace}/commit/#{closing_commit.id}]." }.to_json } + let(:comment_body) do + { + body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.path_with_namespace}/commit/#{closing_commit.id}]." + }.to_json + end before do open_issue = JIRA::Resource::Issue.new(jira_tracker.client, attrs: { "id" => "JIRA-1" }) @@ -486,13 +469,13 @@ describe GitPushService, services: true do context "using right markdown" do it "initiates one api call to jira server to close the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once end it "initiates one api call to jira server to comment on the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with( body: comment_body @@ -500,21 +483,57 @@ describe GitPushService, services: true do end end - context "using wrong markdown" do - let(:message) { "this is some work.\n\ncloses #1" } + context "using internal issue reference" do + context 'when internal issues are disabled' do + before do + project.issues_enabled = false + project.save! + end + let(:message) { "this is some work.\n\ncloses #1" } + + it "does not initiates one api call to jira server to close the issue" do + execute_service(project, commit_author, oldrev, newrev, ref) - it "does not initiates one api call to jira server to close the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + expect(WebMock).not_to have_requested(:post, jira_api_transition_url('JIRA-1')) + end - expect(WebMock).not_to have_requested(:post, jira_api_transition_url('JIRA-1')) + it "does not initiates one api call to jira server to comment on the issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + + expect(WebMock).not_to have_requested(:post, jira_api_comment_url('JIRA-1')).with( + body: comment_body + ).once + end end - it "does not initiates one api call to jira server to comment on the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + context 'when internal issues are enabled' do + let(:issue) { create(:issue, project: project) } + let(:message) { "this is some work.\n\ncloses JIRA-1 \n\n closes #{issue.to_reference}" } - expect(WebMock).not_to have_requested(:post, jira_api_comment_url('JIRA-1')).with( - body: comment_body - ).once + it "initiates one api call to jira server to close the jira issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + + expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once + end + + it "initiates one api call to jira server to comment on the jira issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + + expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with( + body: comment_body + ).once + end + + it "closes the internal issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + expect(issue.reload).to be_closed + end + + it "adds a note indicating that the issue is now closed" do + expect(SystemNoteService).to receive(:change_status) + .with(issue, project, commit_author, "closed", closing_commit) + execute_service(project, commit_author, oldrev, newrev, ref) + end end end end @@ -523,7 +542,7 @@ describe GitPushService, services: true do describe "empty project" do let(:project) { create(:project_empty_repo) } - let(:new_ref) { 'refs/heads/feature'} + let(:new_ref) { 'refs/heads/feature' } before do allow(project).to receive(:default_branch).and_return('feature') @@ -531,7 +550,7 @@ describe GitPushService, services: true do end it 'push to first branch updates HEAD' do - execute_service(project, user, @blankrev, @newrev, new_ref ) + execute_service(project, user, blankrev, newrev, new_ref) end end @@ -539,20 +558,24 @@ describe GitPushService, services: true do let(:housekeeping) { Projects::HousekeepingService.new(project) } before do - # Flush any raw Redis data stored by the housekeeping code. - Gitlab::Redis.with { |conn| conn.flushall } + # Flush any raw key-value data stored by the housekeeping code. + Gitlab::Redis::Cache.with { |conn| conn.flushall } + Gitlab::Redis::Queues.with { |conn| conn.flushall } + Gitlab::Redis::SharedState.with { |conn| conn.flushall } allow(Projects::HousekeepingService).to receive(:new).and_return(housekeeping) end after do - Gitlab::Redis.with { |conn| conn.flushall } + Gitlab::Redis::Cache.with { |conn| conn.flushall } + Gitlab::Redis::Queues.with { |conn| conn.flushall } + Gitlab::Redis::SharedState.with { |conn| conn.flushall } end it 'does not perform housekeeping when not needed' do expect(housekeeping).not_to receive(:execute) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end context 'when housekeeping is needed' do @@ -563,20 +586,20 @@ describe GitPushService, services: true do it 'performs housekeeping' do expect(housekeeping).to receive(:execute) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end it 'does not raise an exception' do allow(housekeeping).to receive(:try_obtain_lease).and_return(false) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end end it 'increments the push counter' do expect(housekeeping).to receive(:increment!) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end end @@ -584,9 +607,9 @@ describe GitPushService, services: true do let(:service) do described_class.new(project, user, - oldrev: sample_commit.parent_id, - newrev: sample_commit.id, - ref: 'refs/heads/master') + oldrev: oldrev, + newrev: newrev, + ref: ref) end context 'on the default branch' do @@ -629,14 +652,13 @@ describe GitPushService, services: true do let(:service) do described_class.new(project, user, - oldrev: sample_commit.parent_id, - newrev: sample_commit.id, - ref: 'refs/heads/master') + oldrev: oldrev, + newrev: newrev, + ref: ref) end it 'only schedules a limited number of commits' do - allow(service).to receive(:push_commits) - .and_return(Array.new(1000, double(:commit, to_hash: {}, matches_cross_reference_regex?: true))) + service.push_commits = Array.new(1000, double(:commit, to_hash: {}, matches_cross_reference_regex?: true)) expect(ProcessCommitWorker).to receive(:perform_async).exactly(100).times @@ -644,8 +666,7 @@ describe GitPushService, services: true do end it "skips commits which don't include cross-references" do - allow(service).to receive(:push_commits) - .and_return([double(:commit, to_hash: {}, matches_cross_reference_regex?: false)]) + service.push_commits = [double(:commit, to_hash: {}, matches_cross_reference_regex?: false)] expect(ProcessCommitWorker).not_to receive(:perform_async) @@ -653,9 +674,31 @@ describe GitPushService, services: true do end end + describe '#update_signatures' do + let(:service) do + described_class.new( + project, + user, + oldrev: oldrev, + newrev: newrev, + ref: 'refs/heads/master' + ) + end + + it 'calls CreateGpgSignatureWorker.perform_async for each commit' do + expect(CreateGpgSignatureWorker).to receive(:perform_async).with(sample_commit.id, project.id) + + execute_service(project, user, oldrev, newrev, ref) + end + end + def execute_service(project, user, oldrev, newrev, ref) - service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref ) + service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) service.execute service end + + def push_data_from_service(project, user, oldrev, newrev, ref) + execute_service(project, user, oldrev, newrev, ref).push_data + end end |