# frozen_string_literal: true require 'spec_helper' describe BambooService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers include StubRequests let(:bamboo_url) { 'http://gitlab.com/bamboo' } subject(:service) do described_class.create( project: create(:project), properties: { bamboo_url: bamboo_url, username: 'mic', password: 'password', build_key: 'foo' } ) end describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } end describe 'Validations' do context 'when service is active' do before do subject.active = true end it { is_expected.to validate_presence_of(:build_key) } it { is_expected.to validate_presence_of(:bamboo_url) } it_behaves_like 'issue tracker service URL attribute', :bamboo_url describe '#username' do it 'does not validate the presence of username if password is nil' do subject.password = nil expect(subject).not_to validate_presence_of(:username) end it 'validates the presence of username if password is present' do subject.password = 'secret' expect(subject).to validate_presence_of(:username) end end describe '#password' do it 'does not validate the presence of password if username is nil' do subject.username = nil expect(subject).not_to validate_presence_of(:password) end it 'validates the presence of password if username is present' do subject.username = 'john' expect(subject).to validate_presence_of(:password) end end end context 'when service is inactive' do before do subject.active = false end it { is_expected.not_to validate_presence_of(:build_key) } it { is_expected.not_to validate_presence_of(:bamboo_url) } it { is_expected.not_to validate_presence_of(:username) } it { is_expected.not_to validate_presence_of(:password) } end end describe 'Callbacks' do describe 'before_update :reset_password' do context 'when a password was previously set' do it 'resets password if url changed' do bamboo_service = service bamboo_service.bamboo_url = 'http://gitlab1.com' bamboo_service.save expect(bamboo_service.password).to be_nil end it 'does not reset password if username changed' do bamboo_service = service bamboo_service.username = 'some_name' bamboo_service.save expect(bamboo_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 bamboo_service = service bamboo_service.bamboo_url = 'http://gitlab_edited.com' bamboo_service.password = 'password' bamboo_service.save expect(bamboo_service.password).to eq('password') expect(bamboo_service.bamboo_url).to eq('http://gitlab_edited.com') end end it 'saves password if new url is set together with password when no password was previously set' do bamboo_service = service bamboo_service.password = nil bamboo_service.bamboo_url = 'http://gitlab_edited.com' bamboo_service.password = 'password' bamboo_service.save expect(bamboo_service.password).to eq('password') expect(bamboo_service.bamboo_url).to eq('http://gitlab_edited.com') end end end describe '#execute' do it 'runs update and build action' do stub_update_and_build_request subject.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA) end end describe '#build_page' do it 'returns the contents of the reactive cache' do stub_reactive_cache(service, { build_page: 'foo' }, 'sha', 'ref') expect(service.build_page('sha', 'ref')).to eq('foo') end end describe '#commit_status' do it 'returns the contents of the reactive cache' do stub_reactive_cache(service, { commit_status: 'foo' }, 'sha', 'ref') expect(service.commit_status('sha', 'ref')).to eq('foo') end end shared_examples 'reactive cache calculation' do describe '#build_page' do subject { service.calculate_reactive_cache('123', 'unused')[:build_page] } it 'returns a specific URL when status is 500' do stub_request(status: 500) is_expected.to eq('http://gitlab.com/bamboo/browse/foo') end it 'returns a specific URL when response has no results' do stub_request(body: %q({"results":{"results":{"size":"0"}}})) is_expected.to eq('http://gitlab.com/bamboo/browse/foo') end it 'returns a build URL when bamboo_url has no trailing slash' do stub_request(body: bamboo_response) is_expected.to eq('http://gitlab.com/bamboo/browse/42') end context 'bamboo_url has trailing slash' do let(:bamboo_url) { 'http://gitlab.com/bamboo/' } it 'returns a build URL' do stub_request(body: bamboo_response) is_expected.to eq('http://gitlab.com/bamboo/browse/42') end end end describe '#commit_status' do subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] } it 'sets commit status to :error when status is 500' do stub_request(status: 500) is_expected.to eq(:error) end it 'sets commit status to "pending" when status is 404' do stub_request(status: 404) is_expected.to eq('pending') end it 'sets commit status to "pending" when response has no results' do stub_request(body: %q({"results":{"results":{"size":"0"}}})) is_expected.to eq('pending') end it 'sets commit status to "success" when build state contains Success' do stub_request(body: bamboo_response(build_state: 'YAY Success!')) is_expected.to eq('success') end it 'sets commit status to "failed" when build state contains Failed' do stub_request(body: bamboo_response(build_state: 'NO Failed!')) is_expected.to eq('failed') end it 'sets commit status to "pending" when build state contains Pending' do stub_request(body: bamboo_response(build_state: 'NO Pending!')) is_expected.to eq('pending') end it 'sets commit status to :error when build state is unknown' do stub_request(body: bamboo_response(build_state: 'FOO BAR!')) is_expected.to eq(:error) end end end describe '#calculate_reactive_cache' do context 'when Bamboo API returns single result' do let(:bamboo_response_template) do %q({"results":{"results":{"size":"1","result":{"buildState":"%{build_state}","planResultKey":{"key":"42"}}}}}) end it_behaves_like 'reactive cache calculation' end context 'when Bamboo API returns an array of results and we only consider the last one' do let(:bamboo_response_template) do %q({"results":{"results":{"size":"2","result":[{"buildState":"%{build_state}","planResultKey":{"key":"41"}},{"buildState":"%{build_state}","planResultKey":{"key":"42"}}]}}}) end it_behaves_like 'reactive cache calculation' end end def stub_update_and_build_request(status: 200, body: nil) bamboo_full_url = 'http://gitlab.com/bamboo/updateAndBuild.action?buildKey=foo&os_authType=basic' stub_bamboo_request(bamboo_full_url, status, body) end def stub_request(status: 200, body: nil) bamboo_full_url = 'http://gitlab.com/bamboo/rest/api/latest/result/byChangeset/123?os_authType=basic' stub_bamboo_request(bamboo_full_url, status, body) end def stub_bamboo_request(url, status, body) stub_full_request(url).to_return( status: status, headers: { 'Content-Type' => 'application/json' }, body: body ).with(basic_auth: %w(mic password)) end def bamboo_response(build_state: 'success') # reference: https://docs.atlassian.com/atlassian-bamboo/REST/6.2.5/#d2e786 bamboo_response_template % { build_state: build_state } end end