diff options
author | Drew Blessing <drew@gitlab.com> | 2015-12-18 01:08:14 +0300 |
---|---|---|
committer | Drew Blessing <drew@gitlab.com> | 2015-12-18 23:19:48 +0300 |
commit | f177aaa5fa789654dc440d6ec4ae3546544c1401 (patch) | |
tree | 709b259ea7903da9ac28e5393cc29d0780a6c63a /spec | |
parent | 27859f7ed9e1efe98b8386844d0a7e69fd58277a (diff) |
Backport JIRA service
Diffstat (limited to 'spec')
-rw-r--r-- | spec/helpers/merge_requests_helper_spec.rb | 21 | ||||
-rw-r--r-- | spec/lib/gitlab/reference_extractor_spec.rb | 10 | ||||
-rw-r--r-- | spec/models/concerns/mentionable_spec.rb | 13 | ||||
-rw-r--r-- | spec/models/jira_issue_spec.rb | 30 | ||||
-rw-r--r-- | spec/models/merge_request_spec.rb | 11 | ||||
-rw-r--r-- | spec/models/project_services/jira_service_spec.rb | 118 | ||||
-rw-r--r-- | spec/services/git_push_service_spec.rb | 69 | ||||
-rw-r--r-- | spec/services/system_note_service_spec.rb | 61 | ||||
-rw-r--r-- | spec/support/jira_service_helper.rb | 67 |
9 files changed, 391 insertions, 9 deletions
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index 0ef1efb8bce..28fb3216f60 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -1,24 +1,37 @@ require 'spec_helper' describe MergeRequestsHelper do - describe "#issues_sentence" do + describe '#issues_sentence' do subject { issues_sentence(issues) } let(:issues) do [build(:issue, iid: 1), build(:issue, iid: 2), build(:issue, iid: 3)] end it { is_expected.to eq('#1, #2, and #3') } + + context 'for JIRA issues' do + let(:project) { create(:project) } + let(:issues) do + [ + JiraIssue.new('JIRA-123', project), + JiraIssue.new('JIRA-456', project), + JiraIssue.new('FOOBAR-7890', project) + ] + end + + it { is_expected.to eq('FOOBAR-7890, JIRA-123, and JIRA-456') } + end end - describe "#format_mr_branch_names" do - describe "within the same project" do + describe '#format_mr_branch_names' do + describe 'within the same project' do let(:merge_request) { create(:merge_request) } subject { format_mr_branch_names(merge_request) } it { is_expected.to eq([merge_request.source_branch, merge_request.target_branch]) } end - describe "within different projects" do + describe 'within different projects' do let(:project) { create(:project) } let(:fork_project) { create(:project, forked_from_project: project) } let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) } diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index 66dc5d4911d..7d963795e17 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -97,6 +97,16 @@ describe Gitlab::ReferenceExtractor, lib: true do expect(extracted.first.commit_to).to eq commit end + context 'with an external issue tracker' do + let(:project) { create(:jira_project) } + subject { described_class.new(project, project.creator) } + + it 'returns JIRA issues for a JIRA-integrated project' do + subject.analyze('JIRA-123 and FOOBAR-4567') + expect(subject.issues).to eq [JiraIssue.new('JIRA-123', project), JiraIssue.new('FOOBAR-4567', project)] + end + end + context 'with a project with an underscore' do let(:other_project) { create(:project, path: 'test_project') } let(:issue) { create(:issue, project: other_project) } diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb index 6179882e935..6653621a83e 100644 --- a/spec/models/concerns/mentionable_spec.rb +++ b/spec/models/concerns/mentionable_spec.rb @@ -1,5 +1,18 @@ require 'spec_helper' +describe Mentionable do + include Mentionable + + describe :references do + let(:project) { create(:project) } + + it 'excludes JIRA references' do + allow(project).to receive_messages(jira_tracker?: true) + expect(referenced_mentionables(project, 'JIRA-123')).to be_empty + end + end +end + describe Issue, "Mentionable" do describe '#mentioned_users' do let!(:user) { create(:user, username: 'stranger') } diff --git a/spec/models/jira_issue_spec.rb b/spec/models/jira_issue_spec.rb new file mode 100644 index 00000000000..1634265b439 --- /dev/null +++ b/spec/models/jira_issue_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe JiraIssue do + let(:project) { create(:project) } + subject { JiraIssue.new('JIRA-123', project) } + + describe 'id' do + subject { super().id } + it { is_expected.to eq('JIRA-123') } + end + + describe 'iid' do + subject { super().iid } + it { is_expected.to eq('JIRA-123') } + end + + describe 'to_s' do + subject { super().to_s } + it { is_expected.to eq('JIRA-123') } + end + + describe :== do + specify { expect(subject).to eq(JiraIssue.new('JIRA-123', project)) } + specify { expect(subject).not_to eq(JiraIssue.new('JIRA-124', project)) } + + it 'only compares with JiraIssues' do + expect(subject).not_to eq('JIRA-123') + end + end +end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 1aeba9b2b3b..e0653a8327d 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -164,6 +164,17 @@ describe MergeRequest, models: true do expect(subject.closes_issues).to include(issue2) end + + context 'for a project with JIRA integration' do + let(:issue0) { JiraIssue.new('JIRA-123', subject.project) } + let(:issue1) { JiraIssue.new('FOOBAR-4567', subject.project) } + + it 'returns sorted JiraIssues' do + allow(subject.project).to receive_messages(default_branch: subject.target_branch) + + expect(subject.closes_issues).to eq([issue0, issue1]) + end + end end describe "#work_in_progress?" do diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 7d91ebe9ce6..2f8193170ae 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -26,6 +26,113 @@ describe JiraService, models: true do it { is_expected.to have_one :service_hook } end + describe "Execute" do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:merge_request) { create(:merge_request) } + + before do + @jira_service = JiraService.new + allow(@jira_service).to receive_messages( + project_id: project.id, + project: project, + service_hook: true, + project_url: 'http://jira.example.com', + username: 'gitlab_jira_username', + password: 'gitlab_jira_password' + ) + @jira_service.save # will build API URL, as api_url was not specified above + @sample_data = Gitlab::PushDataBuilder.build_sample(project, user) + # https://github.com/bblimke/webmock#request-with-basic-authentication + @api_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/transitions' + @comment_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/comment' + + WebMock.stub_request(:post, @api_url) + WebMock.stub_request(:post, @comment_url) + end + + it "should call JIRA API" do + @jira_service.execute(merge_request, JiraIssue.new("JIRA-123", project)) + expect(WebMock).to have_requested(:post, @comment_url).with( + body: /Issue solved with/ + ).once + end + + it "calls the api with jira_issue_transition_id" do + @jira_service.jira_issue_transition_id = 'this-is-a-custom-id' + @jira_service.execute(merge_request, JiraIssue.new("JIRA-123", project)) + expect(WebMock).to have_requested(:post, @api_url).with( + body: /this-is-a-custom-id/ + ).once + end + end + + describe "Stored password invalidation" do + let(:project) { create(:project) } + + context "when a password was previously set" do + before do + @jira_service = JiraService.create( + project: create(:project), + properties: { + api_url: 'http://jira.example.com/rest/api/2', + username: 'mic', + password: "password" + } + ) + end + + it "reset password if url changed" do + @jira_service.api_url = 'http://jira_edited.example.com/rest/api/2' + @jira_service.save + expect(@jira_service.password).to be_nil + end + + it "does not reset password if username changed" do + @jira_service.username = "some_name" + @jira_service.save + expect(@jira_service.password).to eq("password") + end + + it "does not reset password if new url is set together with password, even if it's the same password" do + @jira_service.api_url = 'http://jira_edited.example.com/rest/api/2' + @jira_service.password = 'password' + @jira_service.save + expect(@jira_service.password).to eq("password") + expect(@jira_service.api_url).to eq("http://jira_edited.example.com/rest/api/2") + end + + it "should reset password if url changed, even if setter called multiple times" do + @jira_service.api_url = 'http://jira1.example.com/rest/api/2' + @jira_service.api_url = 'http://jira1.example.com/rest/api/2' + @jira_service.save + expect(@jira_service.password).to be_nil + end + end + + context "when no password was previously set" do + before do + @jira_service = JiraService.create( + project: create(:project), + properties: { + api_url: 'http://jira.example.com/rest/api/2', + username: 'mic' + } + ) + end + + it "saves password if new url is set together with password" do + @jira_service.api_url = 'http://jira_edited.example.com/rest/api/2' + @jira_service.password = 'password' + @jira_service.save + expect(@jira_service.password).to eq("password") + expect(@jira_service.api_url).to eq("http://jira_edited.example.com/rest/api/2") + end + + end + end + + describe "Validations" do context "active" do before do @@ -78,11 +185,12 @@ describe JiraService, models: true do context 'when gitlab.yml was initialized' do before do - settings = { "jira" => { - "title" => "Jira", - "project_url" => "http://jira.sample/projects/project_a", - "issues_url" => "http://jira.sample/issues/:id", - "new_issue_url" => "http://jira.sample/projects/project_a/issues/new" + settings = { + "jira" => { + "title" => "Jira", + "project_url" => "http://jira.sample/projects/project_a", + "issues_url" => "http://jira.sample/issues/:id", + "new_issue_url" => "http://jira.sample/projects/project_a/issues/new" } } allow(Gitlab.config).to receive(:issues_tracker).and_return(settings) diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index a04c242cf0e..c1080ef190a 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -265,6 +265,75 @@ describe GitPushService, services: true do expect(Issue.find(issue.id)).to be_opened end end + + # EE-only tests + context "for jira issue tracker" do + include JiraServiceHelper + + let(:jira_tracker) { project.create_jira_service if project.jira_service.nil? } + + before do + jira_service_settings + + WebMock.stub_request(:post, jira_api_transition_url) + WebMock.stub_request(:post, jira_api_comment_url) + WebMock.stub_request(:get, jira_api_comment_url).to_return(body: jira_issue_comments) + WebMock.stub_request(:get, jira_api_test_url) + + 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 + }) + + allow(project.repository).to receive_messages(commits_between: [closing_commit]) + end + + after do + jira_tracker.destroy! + end + + context "mentioning an issue" do + let(:message) { "this is some work.\n\nrelated to JIRA-1" } + + it "should initiate one api call to jira server to mention the issue" do + service.execute(project, user, @oldrev, @newrev, @ref) + + expect(WebMock).to have_requested(:post, jira_api_comment_url).with( + body: /mentioned this issue in/ + ).once + end + end + + context "closing an issue" do + let(:message) { "this is some work.\n\ncloses JIRA-1" } + + it "should initiate one api call to jira server to close the issue" do + transition_body = { + transition: { + id: '2' + } + }.to_json + + service.execute(project, user, @oldrev, @newrev, @ref) + expect(WebMock).to have_requested(:post, jira_api_transition_url).with( + body: transition_body + ).once + end + + it "should initiate one api call to jira server to comment on the issue" do + comment_body = { + body: "Issue solved with [#{closing_commit.id}|http://localhost/#{project.path_with_namespace}/commit/#{closing_commit.id}]." + }.to_json + + service.execute(project, user, @oldrev, @newrev, @ref) + expect(WebMock).to have_requested(:post, jira_api_comment_url).with( + body: comment_body + ).once + end + end + end end describe "empty project" do diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 0a4f9b230e8..c9f828ae2f7 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -425,4 +425,65 @@ describe SystemNoteService, services: true do end end end + + include JiraServiceHelper + + describe 'JIRA integration' do + let(:project) { create(:project) } + let(:author) { create(:user) } + let(:issue) { create(:issue, project: project) } + let(:mergereq) { create(:merge_request, :simple, target_project: project, source_project: project) } + let(:jira_issue) { JiraIssue.new("JIRA-1", project)} + let(:jira_tracker) { project.create_jira_service if project.jira_service.nil? } + let(:commit) { project.commit } + + context 'in JIRA issue tracker' do + before do + jira_service_settings + WebMock.stub_request(:post, jira_api_comment_url) + end + + after do + jira_tracker.destroy! + end + + describe "new reference" do + before do + WebMock.stub_request(:get, jira_api_comment_url).to_return(body: jira_issue_comments) + end + + subject { described_class.cross_reference(jira_issue, commit, author) } + + it { is_expected.to eq(jira_status_message) } + end + + describe "existing reference" do + before do + message = "[#{author.name}|http://localhost/u/#{author.username}] mentioned this issue in [a commit of #{project.path_with_namespace}|http://localhost/#{project.path_with_namespace}/commit/#{commit.id}]." + WebMock.stub_request(:get, jira_api_comment_url).to_return(body: "{\"comments\":[{\"body\":\"#{message}\"}]}") + end + + subject { described_class.cross_reference(jira_issue, commit, author) } + it { is_expected.not_to eq(jira_status_message) } + end + end + + context 'issue from an issue' do + context 'in JIRA issue tracker' do + before do + jira_service_settings + WebMock.stub_request(:post, jira_api_comment_url) + WebMock.stub_request(:get, jira_api_comment_url).to_return(body: jira_issue_comments) + end + + after do + jira_tracker.destroy! + end + + subject { described_class.cross_reference(jira_issue, issue, author) } + + it { is_expected.to eq(jira_status_message) } + end + end + end end diff --git a/spec/support/jira_service_helper.rb b/spec/support/jira_service_helper.rb new file mode 100644 index 00000000000..a3f496359b1 --- /dev/null +++ b/spec/support/jira_service_helper.rb @@ -0,0 +1,67 @@ +module JiraServiceHelper + + def jira_service_settings + properties = { + "title"=>"JIRA tracker", + "project_url"=>"http://jira.example/issues/?jql=project=A", + "issues_url"=>"http://jira.example/browse/JIRA-1", + "new_issue_url"=>"http://jira.example/secure/CreateIssue.jspa", + "api_url"=>"http://jira.example/rest/api/2" + } + + jira_tracker.update_attributes(properties: properties, active: true) + end + + def jira_status_message + "JiraService SUCCESS 200: Successfully posted to #{jira_api_comment_url}." + end + + def jira_issue_comments + "{\"startAt\":0,\"maxResults\":11,\"total\":11, + \"comments\":[{\"self\":\"http://0.0.0.0:4567/rest/api/2/issue/10002/comment/10609\", + \"id\":\"10609\",\"author\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\", + \"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\", + \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\", + \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", + \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\", + \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"}, + \"displayName\":\"GitLab\",\"active\":true}, + \"body\":\"[Administrator|http://localhost:3000/u/root] mentioned JIRA-1 in Merge request of [gitlab-org/gitlab-test|http://localhost:3000/gitlab-org/gitlab-test/merge_requests/2].\", + \"updateAuthor\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",\"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\", + \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\", + \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", + \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\", + \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true}, + \"created\":\"2015-02-12T22:47:07.826+0100\", + \"updated\":\"2015-02-12T22:47:07.826+0100\"}, + {\"self\":\"http://0.0.0.0:4567/rest/api/2/issue/10002/comment/10700\", + \"id\":\"10700\",\"author\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\", + \"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\", + \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\", + \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", + \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\", + \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true}, + \"body\":\"[Administrator|http://localhost:3000/u/root] mentioned this issue in [a commit of h5bp/html5-boilerplate|http://localhost:3000/h5bp/html5-boilerplate/commit/2439f77897122fbeee3bfd9bb692d3608848433e].\", + \"updateAuthor\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",\"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\", + \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\", + \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", + \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\", + \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true}, + \"created\":\"2015-04-01T03:45:55.667+0200\", + \"updated\":\"2015-04-01T03:45:55.667+0200\" + } + ]}" + end + + def jira_api_comment_url + 'http://jira.example/rest/api/2/issue/JIRA-1/comment' + end + + def jira_api_transition_url + 'http://jira.example/rest/api/2/issue/JIRA-1/transitions' + end + + def jira_api_test_url + 'http://jira.example/rest/api/2/myself' + end +end |