debian-mirror-gitlab/spec/services/web_hook_service_spec.rb

229 lines
7.6 KiB
Ruby
Raw Normal View History

2019-07-31 22:56:46 +05:30
# frozen_string_literal: true
2017-09-10 17:25:29 +05:30
require 'spec_helper'
2020-07-28 23:09:34 +05:30
RSpec.describe WebHookService do
2019-06-05 12:25:43 +05:30
include StubRequests
2017-09-10 17:25:29 +05:30
let(:project) { create(:project) }
let(:project_hook) { create(:project_hook) }
let(:headers) do
{
'Content-Type' => 'application/json',
2021-01-29 00:20:46 +05:30
'User-Agent' => "GitLab/#{Gitlab::VERSION}",
2017-09-10 17:25:29 +05:30
'X-Gitlab-Event' => 'Push Hook'
}
end
2020-10-24 23:57:45 +05:30
2017-09-10 17:25:29 +05:30
let(:data) do
{ before: 'oldrev', after: 'newrev', ref: 'ref' }
end
2020-10-24 23:57:45 +05:30
2018-03-17 18:26:18 +05:30
let(:service_instance) { described_class.new(project_hook, data, :push_hooks) }
2017-09-10 17:25:29 +05:30
2018-03-26 14:24:53 +05:30
describe '#initialize' do
2019-10-12 21:52:04 +05:30
before do
stub_application_setting(setting_name => setting)
2018-03-26 14:24:53 +05:30
end
2019-10-12 21:52:04 +05:30
shared_examples_for 'respects outbound network setting' do
context 'when local requests are allowed' do
let(:setting) { true }
it { expect(hook.request_options[:allow_local_requests]).to be_truthy }
end
context 'when local requests are not allowed' do
let(:setting) { false }
it { expect(hook.request_options[:allow_local_requests]).to be_falsey }
2018-03-26 14:24:53 +05:30
end
end
2019-10-12 21:52:04 +05:30
context 'when SystemHook' do
let(:setting_name) { :allow_local_requests_from_system_hooks }
let(:hook) { described_class.new(build(:system_hook), data, :system_hook) }
include_examples 'respects outbound network setting'
end
context 'when ProjectHook' do
let(:setting_name) { :allow_local_requests_from_web_hooks_and_services }
let(:hook) { described_class.new(build(:project_hook), data, :project_hook) }
include_examples 'respects outbound network setting'
end
2018-03-26 14:24:53 +05:30
end
2017-09-10 17:25:29 +05:30
describe '#execute' do
2018-03-17 18:26:18 +05:30
before do
2017-09-10 17:25:29 +05:30
project.hooks << [project_hook]
end
context 'when token is defined' do
let(:project_hook) { create(:project_hook, :token) }
it 'POSTs to the webhook URL' do
2019-09-04 21:01:54 +05:30
stub_full_request(project_hook.url, method: :post)
2017-09-10 17:25:29 +05:30
service_instance.execute
2019-09-04 21:01:54 +05:30
expect(WebMock).to have_requested(:post, stubbed_hostname(project_hook.url)).with(
2017-09-10 17:25:29 +05:30
headers: headers.merge({ 'X-Gitlab-Token' => project_hook.token })
).once
end
end
it 'POSTs the data as JSON' do
2019-09-04 21:01:54 +05:30
stub_full_request(project_hook.url, method: :post)
2017-09-10 17:25:29 +05:30
service_instance.execute
2019-09-04 21:01:54 +05:30
expect(WebMock).to have_requested(:post, stubbed_hostname(project_hook.url)).with(
2017-09-10 17:25:29 +05:30
headers: headers
).once
end
2018-11-08 19:23:39 +05:30
context 'when auth credentials are present' do
2019-03-02 22:35:43 +05:30
let(:url) {'https://example.org'}
2018-11-08 19:23:39 +05:30
let(:project_hook) { create(:project_hook, url: 'https://demo:demo@example.org/') }
it 'uses the credentials' do
2019-06-05 12:25:43 +05:30
stub_full_request(url, method: :post)
2018-11-08 19:23:39 +05:30
service_instance.execute
2019-06-05 12:25:43 +05:30
expect(WebMock).to have_requested(:post, stubbed_hostname(url)).with(
2018-11-08 19:23:39 +05:30
headers: headers.merge('Authorization' => 'Basic ZGVtbzpkZW1v')
).once
end
end
context 'when auth credentials are partial present' do
2019-03-02 22:35:43 +05:30
let(:url) {'https://example.org'}
2018-11-08 19:23:39 +05:30
let(:project_hook) { create(:project_hook, url: 'https://demo@example.org/') }
it 'uses the credentials anyways' do
2019-06-05 12:25:43 +05:30
stub_full_request(url, method: :post)
2018-11-08 19:23:39 +05:30
service_instance.execute
2019-06-05 12:25:43 +05:30
expect(WebMock).to have_requested(:post, stubbed_hostname(url)).with(
2018-11-08 19:23:39 +05:30
headers: headers.merge('Authorization' => 'Basic ZGVtbzo=')
).once
end
end
2017-09-10 17:25:29 +05:30
it 'catches exceptions' do
2019-09-04 21:01:54 +05:30
stub_full_request(project_hook.url, method: :post).to_raise(StandardError.new('Some error'))
2017-09-10 17:25:29 +05:30
expect { service_instance.execute }.to raise_error(StandardError)
end
it 'handles exceptions' do
2018-12-13 13:39:08 +05:30
exceptions = [SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout, Gitlab::HTTP::BlockedUrlError, Gitlab::HTTP::RedirectionTooDeep]
2017-09-10 17:25:29 +05:30
exceptions.each do |exception_class|
exception = exception_class.new('Exception message')
2019-09-04 21:01:54 +05:30
stub_full_request(project_hook.url, method: :post).to_raise(exception)
2019-07-07 11:18:12 +05:30
expect(service_instance.execute).to eq({ status: :error, message: exception.to_s })
2017-09-10 17:25:29 +05:30
expect { service_instance.execute }.not_to raise_error
end
end
2021-03-08 18:12:59 +05:30
context 'when url is not encoded' do
let(:project_hook) { create(:project_hook, url: 'http://server.com/my path/') }
it 'handles exceptions' do
expect(service_instance.execute).to eq(status: :error, message: 'bad URI(is not URI?): "http://server.com/my path/"')
expect { service_instance.execute }.not_to raise_error
end
end
2020-10-24 23:57:45 +05:30
context 'when request body size is too big' do
it 'does not perform the request' do
stub_const("#{described_class}::REQUEST_BODY_SIZE_LIMIT", 10.bytes)
expect(service_instance.execute).to eq({ status: :error, message: "Gitlab::Json::LimitedEncoder::LimitExceeded" })
end
end
2017-09-10 17:25:29 +05:30
it 'handles 200 status code' do
2019-09-04 21:01:54 +05:30
stub_full_request(project_hook.url, method: :post).to_return(status: 200, body: 'Success')
2017-09-10 17:25:29 +05:30
expect(service_instance.execute).to include({ status: :success, http_status: 200, message: 'Success' })
end
it 'handles 2xx status codes' do
2019-09-04 21:01:54 +05:30
stub_full_request(project_hook.url, method: :post).to_return(status: 201, body: 'Success')
2017-09-10 17:25:29 +05:30
expect(service_instance.execute).to include({ status: :success, http_status: 201, message: 'Success' })
end
context 'execution logging' do
let(:hook_log) { project_hook.web_hook_logs.last }
context 'with success' do
before do
2019-09-04 21:01:54 +05:30
stub_full_request(project_hook.url, method: :post).to_return(status: 200, body: 'Success')
2017-09-10 17:25:29 +05:30
service_instance.execute
end
it 'log successful execution' do
expect(hook_log.trigger).to eq('push_hooks')
expect(hook_log.url).to eq(project_hook.url)
expect(hook_log.request_headers).to eq(headers)
expect(hook_log.response_body).to eq('Success')
expect(hook_log.response_status).to eq('200')
expect(hook_log.execution_duration).to be > 0
expect(hook_log.internal_error_message).to be_nil
end
end
context 'with exception' do
before do
2019-09-04 21:01:54 +05:30
stub_full_request(project_hook.url, method: :post).to_raise(SocketError.new('Some HTTP Post error'))
2017-09-10 17:25:29 +05:30
service_instance.execute
end
it 'log failed execution' do
expect(hook_log.trigger).to eq('push_hooks')
expect(hook_log.url).to eq(project_hook.url)
expect(hook_log.request_headers).to eq(headers)
expect(hook_log.response_body).to eq('')
expect(hook_log.response_status).to eq('internal error')
expect(hook_log.execution_duration).to be > 0
expect(hook_log.internal_error_message).to eq('Some HTTP Post error')
end
end
context 'with unsafe response body' do
before do
2019-09-04 21:01:54 +05:30
stub_full_request(project_hook.url, method: :post).to_return(status: 200, body: "\xBB")
2017-09-10 17:25:29 +05:30
service_instance.execute
end
it 'log successful execution' do
expect(hook_log.trigger).to eq('push_hooks')
expect(hook_log.url).to eq(project_hook.url)
expect(hook_log.request_headers).to eq(headers)
expect(hook_log.response_body).to eq('')
expect(hook_log.response_status).to eq('200')
expect(hook_log.execution_duration).to be > 0
expect(hook_log.internal_error_message).to be_nil
end
end
end
end
describe '#async_execute' do
let(:system_hook) { create(:system_hook) }
2018-03-17 18:26:18 +05:30
2017-09-10 17:25:29 +05:30
it 'enqueue WebHookWorker' do
2018-03-17 18:26:18 +05:30
expect(WebHookWorker).to receive(:perform_async).with(project_hook.id, data, 'push_hooks')
2017-09-10 17:25:29 +05:30
described_class.new(project_hook, data, 'push_hooks').async_execute
end
end
end