gitlab-org--gitlab-foss/spec/services/web_hook_service_spec.rb
Alex Lossent a94e91a45b Log web hook execution timeout events
If a web hook HTTP request is sent but no response comes
within a certain time (10s by default), the hook execution fails
and will be retried. This commit makes such timeouts visible
in the web hook log, like connection timeouts already are.

Also log "no route to host" errors.
2017-07-27 15:02:25 +02:00

137 lines
4.6 KiB
Ruby

require 'spec_helper'
describe WebHookService, services: true do
let(:project) { create(:empty_project) }
let(:project_hook) { create(:project_hook) }
let(:headers) do
{
'Content-Type' => 'application/json',
'X-Gitlab-Event' => 'Push Hook'
}
end
let(:data) do
{ before: 'oldrev', after: 'newrev', ref: 'ref' }
end
let(:service_instance) { WebHookService.new(project_hook, data, 'push_hooks') }
describe '#execute' do
before(:each) do
project.hooks << [project_hook]
WebMock.stub_request(:post, project_hook.url)
end
context 'when token is defined' do
let(:project_hook) { create(:project_hook, :token) }
it 'POSTs to the webhook URL' do
service_instance.execute
expect(WebMock).to have_requested(:post, project_hook.url).with(
headers: headers.merge({ 'X-Gitlab-Token' => project_hook.token })
).once
end
end
it 'POSTs to the webhook URL' do
service_instance.execute
expect(WebMock).to have_requested(:post, project_hook.url).with(
headers: headers
).once
end
it 'POSTs the data as JSON' do
service_instance.execute
expect(WebMock).to have_requested(:post, project_hook.url).with(
headers: headers
).once
end
it 'catches exceptions' do
WebMock.stub_request(:post, project_hook.url).to_raise(StandardError.new('Some error'))
expect { service_instance.execute }.to raise_error(StandardError)
end
it 'handles exceptions' do
exceptions = [SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout]
exceptions.each do |exception_class|
exception = exception_class.new('Exception message')
WebMock.stub_request(:post, project_hook.url).to_raise(exception)
expect(service_instance.execute).to eq({ status: :error, message: exception.message })
expect { service_instance.execute }.not_to raise_error
end
end
it 'handles 200 status code' do
WebMock.stub_request(:post, project_hook.url).to_return(status: 200, body: 'Success')
expect(service_instance.execute).to include({ status: :success, http_status: 200, message: 'Success' })
end
it 'handles 2xx status codes' do
WebMock.stub_request(:post, project_hook.url).to_return(status: 201, body: 'Success')
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
WebMock.stub_request(:post, project_hook.url).to_return(status: 200, body: 'Success')
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
WebMock.stub_request(:post, project_hook.url).to_raise(SocketError.new('Some HTTP Post error'))
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 'should not log ServiceHooks' do
let(:service_hook) { create(:service_hook) }
let(:service_instance) { WebHookService.new(service_hook, data, 'service_hook') }
before do
WebMock.stub_request(:post, service_hook.url).to_return(status: 200, body: 'Success')
end
it { expect { service_instance.execute }.not_to change(WebHookLog, :count) }
end
end
end
describe '#async_execute' do
let(:system_hook) { create(:system_hook) }
it 'enqueue WebHookWorker' do
expect(Sidekiq::Client).to receive(:enqueue).with(WebHookWorker, project_hook.id, data, 'push_hooks')
WebHookService.new(project_hook, data, 'push_hooks').async_execute
end
end
end