debian-mirror-gitlab/spec/models/project_services/jira_service_spec.rb

542 lines
18 KiB
Ruby
Raw Normal View History

2019-05-18 00:54:41 +05:30
# frozen_string_literal: true
2015-04-26 12:48:37 +05:30
require 'spec_helper'
2017-09-10 17:25:29 +05:30
describe JiraService do
include Gitlab::Routing
2018-11-08 19:23:39 +05:30
include AssetsHelpers
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
describe '#options' do
let(:service) do
described_class.new(
project: build_stubbed(:project),
active: true,
username: 'username',
password: 'test',
jira_issue_transition_id: 24,
url: 'http://jira.test.com/path/'
)
end
it 'sets the URL properly' do
# jira-ruby gem parses the URI and handles trailing slashes
# fine: https://github.com/sumoheavy/jira-ruby/blob/v1.4.1/lib/jira/http_client.rb#L59
expect(service.options[:site]).to eq('http://jira.test.com/')
end
it 'leaves out trailing slashes in context' do
expect(service.options[:context_path]).to eq('/path')
end
end
2018-11-20 20:47:30 +05:30
describe 'Associations' do
2015-04-26 12:48:37 +05:30
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
2018-11-18 11:00:15 +05:30
it { is_expected.to allow_value(nil).for(:jira_issue_transition_id) }
2018-11-20 20:47:30 +05:30
it { is_expected.to allow_value('1,2,3').for(:jira_issue_transition_id) }
it { is_expected.to allow_value('1;2;3').for(:jira_issue_transition_id) }
it { is_expected.not_to allow_value('a,b,cd').for(:jira_issue_transition_id) }
2015-04-26 12:48:37 +05:30
end
2016-06-02 11:05:42 +05:30
describe 'Validations' do
context 'when service is active' do
2017-09-10 17:25:29 +05:30
before do
subject.active = true
end
2016-06-02 11:05:42 +05:30
2017-08-17 22:00:37 +05:30
it { is_expected.to validate_presence_of(:url) }
it_behaves_like 'issue tracker service URL attribute', :url
2016-06-02 11:05:42 +05:30
end
context 'when service is inactive' do
2017-09-10 17:25:29 +05:30
before do
subject.active = false
end
2016-06-02 11:05:42 +05:30
2017-08-17 22:00:37 +05:30
it { is_expected.not_to validate_presence_of(:url) }
2018-03-17 18:26:18 +05:30
it { is_expected.not_to validate_presence_of(:username) }
it { is_expected.not_to validate_presence_of(:password) }
2017-08-17 22:00:37 +05:30
end
2017-09-10 17:25:29 +05:30
context 'validating urls' do
let(:service) do
described_class.new(
project: create(:project),
active: true,
username: 'username',
password: 'test',
jira_issue_transition_id: 24,
url: 'http://jira.test.com'
)
end
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
it 'is valid when all fields have required values' do
expect(service).to be_valid
end
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
it 'is not valid when url is not a valid url' do
service.url = 'not valid'
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
expect(service).not_to be_valid
end
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
it 'is not valid when api url is not a valid url' do
service.api_url = 'not valid'
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
expect(service).not_to be_valid
end
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
it 'is not valid when username is missing' do
service.username = nil
expect(service).not_to be_valid
end
it 'is not valid when password is missing' do
service.password = nil
expect(service).not_to be_valid
end
2017-09-10 17:25:29 +05:30
it 'is valid when api url is a valid url' do
service.api_url = 'http://jira.test.com/api'
expect(service).to be_valid
end
2016-06-02 11:05:42 +05:30
end
end
2017-08-17 22:00:37 +05:30
describe '.reference_pattern' do
2016-11-03 12:29:30 +05:30
it_behaves_like 'allows project key on reference pattern'
it 'does not allow # on the code' do
2017-08-17 22:00:37 +05:30
expect(described_class.reference_pattern.match('#123')).to be_nil
expect(described_class.reference_pattern.match('1#23#12')).to be_nil
2016-11-03 12:29:30 +05:30
end
end
2017-08-17 22:00:37 +05:30
describe '#close_issue' do
let(:custom_base_url) { 'http://custom_url' }
2015-12-23 02:04:40 +05:30
let(:user) { create(:user) }
2018-11-20 20:47:30 +05:30
let(:project) { create(:project, :repository) }
2015-12-23 02:04:40 +05:30
2018-11-20 20:47:30 +05:30
shared_examples 'close_issue' do
before do
@jira_service = described_class.new
allow(@jira_service).to receive_messages(
project_id: project.id,
project: project,
service_hook: true,
url: 'http://jira.example.com',
username: 'gitlab_jira_username',
password: 'gitlab_jira_password',
jira_issue_transition_id: '999'
)
2015-12-23 02:04:40 +05:30
2018-11-20 20:47:30 +05:30
# These stubs are needed to test JiraService#close_issue.
# We close the issue then do another request to API to check if it got closed.
# Here is stubbed the API return with a closed and an opened issues.
open_issue = JIRA::Resource::Issue.new(@jira_service.client, attrs: { 'id' => 'JIRA-123' })
closed_issue = open_issue.dup
allow(open_issue).to receive(:resolution).and_return(false)
allow(closed_issue).to receive(:resolution).and_return(true)
allow(JIRA::Resource::Issue).to receive(:find).and_return(open_issue, closed_issue)
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
allow_any_instance_of(JIRA::Resource::Issue).to receive(:key).and_return('JIRA-123')
allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])
2015-12-23 02:04:40 +05:30
2018-11-20 20:47:30 +05:30
@jira_service.save
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
project_issues_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123'
@transitions_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123/transitions'
@comment_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123/comment'
@remote_link_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123/remotelink'
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
WebMock.stub_request(:get, project_issues_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
WebMock.stub_request(:post, @transitions_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
WebMock.stub_request(:post, @comment_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
WebMock.stub_request(:post, @remote_link_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password))
end
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
it 'calls JIRA API' do
@jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
expect(WebMock).to have_requested(:post, @comment_url).with(
body: /Issue solved with/
).once
end
2017-09-10 17:25:29 +05:30
2018-11-20 20:47:30 +05:30
# Check https://developer.atlassian.com/jiradev/jira-platform/guides/other/guide-jira-remote-issue-links/fields-in-remote-issue-links
# for more information
it 'creates Remote Link reference in JIRA for comment' do
@jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
favicon_path = "http://localhost/assets/#{find_asset('favicon.png').digest_path}"
# Creates comment
expect(WebMock).to have_requested(:post, @comment_url)
# Creates Remote Link in JIRA issue fields
expect(WebMock).to have_requested(:post, @remote_link_url).with(
body: hash_including(
GlobalID: 'GitLab',
2019-05-18 00:54:41 +05:30
relationship: 'mentioned on',
2018-11-20 20:47:30 +05:30
object: {
url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{commit_id}",
2019-05-18 00:54:41 +05:30
title: "Solved by commit #{commit_id}.",
2018-11-20 20:47:30 +05:30
icon: { title: 'GitLab', url16x16: favicon_path },
status: { resolved: true }
}
)
).once
end
2017-09-10 17:25:29 +05:30
2018-11-20 20:47:30 +05:30
it 'does not send comment or remote links to issues already closed' do
allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(true)
2017-09-10 17:25:29 +05:30
2018-11-20 20:47:30 +05:30
@jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
expect(WebMock).not_to have_requested(:post, @comment_url)
expect(WebMock).not_to have_requested(:post, @remote_link_url)
end
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
it 'does not send comment or remote links to issues with unknown resolution' do
allow_any_instance_of(JIRA::Resource::Issue).to receive(:respond_to?).with(:resolution).and_return(false)
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
@jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
expect(WebMock).not_to have_requested(:post, @comment_url)
expect(WebMock).not_to have_requested(:post, @remote_link_url)
2017-08-17 22:00:37 +05:30
end
2018-11-20 20:47:30 +05:30
it 'references the GitLab commit' do
stub_config_setting(base_url: custom_base_url)
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
@jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
expect(WebMock).to have_requested(:post, @comment_url).with(
body: %r{#{custom_base_url}/#{project.full_path}/commit/#{commit_id}}
).once
end
it 'references the GitLab commit' do
stub_config_setting(relative_url_root: '/gitlab')
stub_config_setting(url: Settings.send(:build_gitlab_url))
allow(described_class).to receive(:default_url_options) do
{ script_name: '/gitlab' }
end
@jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
expect(WebMock).to have_requested(:post, @comment_url).with(
body: %r{#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{commit_id}}
).once
end
it 'logs exception when transition id is not valid' do
allow(@jira_service).to receive(:log_error)
2018-11-18 11:00:15 +05:30
WebMock.stub_request(:post, @transitions_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password)).and_raise("Bad Request")
2017-08-17 22:00:37 +05:30
2018-11-20 20:47:30 +05:30
@jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
2018-11-18 11:00:15 +05:30
2018-11-20 20:47:30 +05:30
expect(@jira_service).to have_received(:log_error).with("Issue transition failed", error: "Bad Request", client_url: "http://jira.example.com")
2018-11-18 11:00:15 +05:30
end
2018-11-20 20:47:30 +05:30
it 'calls the api with jira_issue_transition_id' do
@jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
2018-11-18 11:00:15 +05:30
expect(WebMock).to have_requested(:post, @transitions_url).with(
body: /999/
).once
end
2018-11-20 20:47:30 +05:30
context 'when have multiple transition ids' do
it 'calls the api with transition ids separated by comma' do
allow(@jira_service).to receive_messages(jira_issue_transition_id: '1,2,3')
2018-11-18 11:00:15 +05:30
2018-11-20 20:47:30 +05:30
@jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
2018-11-18 11:00:15 +05:30
1.upto(3) do |transition_id|
expect(WebMock).to have_requested(:post, @transitions_url).with(
body: /#{transition_id}/
).once
end
end
2018-11-20 20:47:30 +05:30
it 'calls the api with transition ids separated by semicolon' do
allow(@jira_service).to receive_messages(jira_issue_transition_id: '1;2;3')
2018-11-18 11:00:15 +05:30
2018-11-20 20:47:30 +05:30
@jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project))
2018-11-18 11:00:15 +05:30
1.upto(3) do |transition_id|
expect(WebMock).to have_requested(:post, @transitions_url).with(
body: /#{transition_id}/
).once
end
end
end
2015-12-23 02:04:40 +05:30
end
2018-11-20 20:47:30 +05:30
context 'when resource is a merge request' do
let(:resource) { create(:merge_request) }
let(:commit_id) { resource.diff_head_sha }
it_behaves_like 'close_issue'
end
context 'when resource is a commit' do
let(:resource) { project.commit('master') }
let(:commit_id) { resource.id }
it_behaves_like 'close_issue'
end
2015-12-23 02:04:40 +05:30
end
2017-08-17 22:00:37 +05:30
describe '#test_settings' do
let(:jira_service) do
described_class.new(
2017-09-10 17:25:29 +05:30
project: create(:project),
2017-08-17 22:00:37 +05:30
url: 'http://jira.example.com',
2017-09-10 17:25:29 +05:30
username: 'jira_username',
password: 'jira_password'
2017-08-17 22:00:37 +05:30
)
end
2017-09-10 17:25:29 +05:30
def test_settings(api_url = nil)
api_url ||= 'jira.example.com'
test_url = "http://#{api_url}/rest/api/2/serverInfo"
WebMock.stub_request(:get, test_url).with(basic_auth: %w(jira_username jira_password)).to_return(body: { url: 'http://url' }.to_json )
jira_service.test(nil)
2017-08-17 22:00:37 +05:30
end
2017-09-10 17:25:29 +05:30
context 'when the test succeeds' do
it 'tries to get JIRA project with URL when API URL not set' do
test_settings('jira.example.com')
end
it 'returns correct result' do
expect(test_settings).to eq( { success: true, result: { 'url' => 'http://url' } })
end
it 'tries to get JIRA project with API URL if set' do
jira_service.update(api_url: 'http://jira.api.com')
test_settings('jira.api.com')
end
end
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
context 'when the test fails' do
it 'returns result with the error' do
test_url = 'http://jira.example.com/rest/api/2/serverInfo'
WebMock.stub_request(:get, test_url).with(basic_auth: %w(jira_username jira_password))
.to_raise(JIRA::HTTPError.new(double(message: 'Some specific failure.')))
expect(jira_service.test(nil)).to eq( { success: false, result: 'Some specific failure.' })
end
2017-08-17 22:00:37 +05:30
end
end
2018-11-20 20:47:30 +05:30
describe 'Stored password invalidation' do
2017-09-10 17:25:29 +05:30
let(:project) { create(:project) }
2015-12-23 02:04:40 +05:30
2018-11-20 20:47:30 +05:30
context 'when a password was previously set' do
2015-12-23 02:04:40 +05:30
before do
2017-09-10 17:25:29 +05:30
@jira_service = described_class.create!(
2017-08-17 22:00:37 +05:30
project: project,
2015-12-23 02:04:40 +05:30
properties: {
2017-09-10 17:25:29 +05:30
url: 'http://jira.example.com/web',
2015-12-23 02:04:40 +05:30
username: 'mic',
2018-11-20 20:47:30 +05:30
password: 'password'
2015-12-23 02:04:40 +05:30
}
)
end
2017-09-10 17:25:29 +05:30
context 'when only web url present' do
it 'reset password if url changed' do
@jira_service.url = 'http://jira_edited.example.com/rest/api/2'
@jira_service.save
expect(@jira_service.password).to be_nil
end
it 'reset password if url not changed but api url added' do
@jira_service.api_url = 'http://jira_edited.example.com/rest/api/2'
@jira_service.save
expect(@jira_service.password).to be_nil
end
end
context 'when both web and api url present' do
before do
@jira_service.api_url = 'http://jira.example.com/rest/api/2'
@jira_service.password = 'password'
@jira_service.save
end
it 'reset password if api 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 url changed' do
@jira_service.url = 'http://jira_edited.example.com/rweb'
@jira_service.save
2018-11-20 20:47:30 +05:30
expect(@jira_service.password).to eq('password')
2017-09-10 17:25:29 +05:30
end
2018-11-20 20:47:30 +05:30
it 'reset password if api url set to empty' do
2017-09-10 17:25:29 +05:30
@jira_service.api_url = ''
@jira_service.save
expect(@jira_service.password).to be_nil
end
2015-12-23 02:04:40 +05:30
end
2017-09-10 17:25:29 +05:30
it 'does not reset password if username changed' do
@jira_service.username = 'some_name'
2015-12-23 02:04:40 +05:30
@jira_service.save
2017-09-10 17:25:29 +05:30
expect(@jira_service.password).to eq('password')
2015-12-23 02:04:40 +05:30
end
2017-09-10 17:25:29 +05:30
it 'does not reset password if new url is set together with password, even if it\'s the same password' do
2017-08-17 22:00:37 +05:30
@jira_service.url = 'http://jira_edited.example.com/rest/api/2'
2015-12-23 02:04:40 +05:30
@jira_service.password = 'password'
@jira_service.save
2017-09-10 17:25:29 +05:30
expect(@jira_service.password).to eq('password')
expect(@jira_service.url).to eq('http://jira_edited.example.com/rest/api/2')
2015-12-23 02:04:40 +05:30
end
2017-09-10 17:25:29 +05:30
it 'resets password if url changed, even if setter called multiple times' do
2017-08-17 22:00:37 +05:30
@jira_service.url = 'http://jira1.example.com/rest/api/2'
@jira_service.url = 'http://jira1.example.com/rest/api/2'
2015-12-23 02:04:40 +05:30
@jira_service.save
expect(@jira_service.password).to be_nil
end
end
2017-09-10 17:25:29 +05:30
context 'when no password was previously set' do
2015-12-23 02:04:40 +05:30
before do
2017-09-10 17:25:29 +05:30
@jira_service = described_class.create(
2017-08-17 22:00:37 +05:30
project: project,
2015-12-23 02:04:40 +05:30
properties: {
2017-08-17 22:00:37 +05:30
url: 'http://jira.example.com/rest/api/2',
2015-12-23 02:04:40 +05:30
username: 'mic'
}
)
end
2017-09-10 17:25:29 +05:30
it 'saves password if new url is set together with password' do
2017-08-17 22:00:37 +05:30
@jira_service.url = 'http://jira_edited.example.com/rest/api/2'
2015-12-23 02:04:40 +05:30
@jira_service.password = 'password'
@jira_service.save
2017-09-10 17:25:29 +05:30
expect(@jira_service.password).to eq('password')
expect(@jira_service.url).to eq('http://jira_edited.example.com/rest/api/2')
2015-04-26 12:48:37 +05:30
end
end
end
describe 'description and title' do
2017-09-10 17:25:29 +05:30
let(:project) { create(:project) }
2015-04-26 12:48:37 +05:30
context 'when it is not set' do
before do
@service = project.create_jira_service(active: true)
end
after do
@service.destroy!
end
2016-09-13 17:45:13 +05:30
it 'is initialized' do
2015-04-26 12:48:37 +05:30
expect(@service.title).to eq('JIRA')
2018-11-20 20:47:30 +05:30
expect(@service.description).to eq('Jira issue tracker')
2015-04-26 12:48:37 +05:30
end
end
context 'when it is set' do
before do
properties = { 'title' => 'Jira One', 'description' => 'Jira One issue tracker' }
@service = project.create_jira_service(active: true, properties: properties)
end
after do
@service.destroy!
end
2018-11-20 20:47:30 +05:30
it 'is correct' do
2015-04-26 12:48:37 +05:30
expect(@service.title).to eq('Jira One')
expect(@service.description).to eq('Jira One issue tracker')
end
end
end
2018-03-17 18:26:18 +05:30
describe 'additional cookies' do
let(:project) { create(:project) }
context 'provides additional cookies to allow basic auth with oracle webgate' do
before do
@service = project.create_jira_service(
active: true, properties: { url: 'http://jira.com' })
end
after do
@service.destroy!
end
it 'is initialized' do
expect(@service.options[:use_cookies]).to eq(true)
2018-11-20 20:47:30 +05:30
expect(@service.options[:additional_cookies]).to eq(['OBBasicAuth=fromDialog'])
2018-03-17 18:26:18 +05:30
end
end
end
2015-04-26 12:48:37 +05:30
describe 'project and issue urls' do
2017-09-10 17:25:29 +05:30
let(:project) { create(:project) }
2015-04-26 12:48:37 +05:30
context 'when gitlab.yml was initialized' do
before do
2015-12-23 02:04:40 +05:30
settings = {
2017-09-10 17:25:29 +05:30
'jira' => {
'title' => 'Jira',
'url' => 'http://jira.sample/projects/project_a',
'api_url' => 'http://jira.sample/api'
2015-04-26 12:48:37 +05:30
}
}
allow(Gitlab.config).to receive(:issues_tracker).and_return(settings)
@service = project.create_jira_service(active: true)
end
after do
@service.destroy!
end
2016-09-13 17:45:13 +05:30
it 'is prepopulated with the settings' do
2017-09-10 17:25:29 +05:30
expect(@service.properties['title']).to eq('Jira')
expect(@service.properties['url']).to eq('http://jira.sample/projects/project_a')
expect(@service.properties['api_url']).to eq('http://jira.sample/api')
2015-04-26 12:48:37 +05:30
end
end
end
2018-11-08 19:23:39 +05:30
describe 'favicon urls', :request_store do
it 'includes the standard favicon' do
props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title')
expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/assets/favicon(?:-\h+).png$}
end
it 'includes returns the custom favicon' do
create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title')
expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/dk.png$}
end
end
2015-04-26 12:48:37 +05:30
end