Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/services/web_hook_service_spec.rb')
-rw-r--r--spec/services/web_hook_service_spec.rb201
1 files changed, 153 insertions, 48 deletions
diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb
index b99bc860523..068550ec234 100644
--- a/spec/services/web_hook_service_spec.rb
+++ b/spec/services/web_hook_service_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state do
include StubRequests
+ let(:ellipsis) { '…' }
let_it_be(:project) { create(:project) }
let_it_be_with_reload(:project_hook) { create(:project_hook, project: project) }
@@ -268,6 +269,20 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state
end
context 'execution logging' do
+ let(:default_log_data) do
+ {
+ trigger: 'push_hooks',
+ url: project_hook.url,
+ request_headers: headers,
+ request_data: data,
+ response_body: 'Success',
+ response_headers: {},
+ response_status: 200,
+ execution_duration: be > 0,
+ internal_error_message: nil
+ }
+ end
+
context 'with success' do
before do
stub_full_request(project_hook.url, method: :post).to_return(status: 200, body: 'Success')
@@ -280,7 +295,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state
expect(::WebHooks::LogExecutionWorker).not_to receive(:perform_async)
expect(::WebHooks::LogExecutionService)
.to receive(:new)
- .with(hook: project_hook, log_data: Hash, response_category: :ok)
+ .with(hook: project_hook, log_data: default_log_data, response_category: :ok)
.and_return(double(execute: nil))
service_instance.execute
@@ -291,17 +306,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state
expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
.with(
project_hook.id,
- hash_including(
- trigger: 'push_hooks',
- url: project_hook.url,
- request_headers: headers,
- request_data: data,
- response_body: 'Success',
- response_headers: {},
- response_status: 200,
- execution_duration: be > 0,
- internal_error_message: nil
- ),
+ hash_including(default_log_data),
:ok,
nil
)
@@ -328,15 +333,10 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state
.with(
project_hook.id,
hash_including(
- trigger: 'push_hooks',
- url: project_hook.url,
- request_headers: headers,
- request_data: data,
- response_body: 'Bad request',
- response_headers: {},
- response_status: 400,
- execution_duration: be > 0,
- internal_error_message: nil
+ default_log_data.merge(
+ response_body: 'Bad request',
+ response_status: 400
+ )
),
:failed,
nil
@@ -356,15 +356,11 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state
.with(
project_hook.id,
hash_including(
- trigger: 'push_hooks',
- url: project_hook.url,
- request_headers: headers,
- request_data: data,
- response_body: '',
- response_headers: {},
- response_status: 'internal error',
- execution_duration: be > 0,
- internal_error_message: 'Some HTTP Post error'
+ default_log_data.merge(
+ response_body: '',
+ response_status: 'internal error',
+ internal_error_message: 'Some HTTP Post error'
+ )
),
:error,
nil
@@ -383,23 +379,137 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state
expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
.with(
project_hook.id,
- hash_including(
- trigger: 'push_hooks',
- url: project_hook.url,
- request_headers: headers,
- request_data: data,
- response_body: '',
- response_headers: {},
- response_status: 200,
- execution_duration: be > 0,
- internal_error_message: nil
- ),
+ hash_including(default_log_data.merge(response_body: '')),
+ :ok,
+ nil
+ )
+
+ service_instance.execute
+ end
+ end
+
+ context 'with oversize response body' do
+ let(:oversize_body) { 'a' * (described_class::RESPONSE_BODY_SIZE_LIMIT + 1) }
+ let(:stripped_body) { 'a' * (described_class::RESPONSE_BODY_SIZE_LIMIT - ellipsis.bytesize) + ellipsis }
+
+ before do
+ stub_full_request(project_hook.url, method: :post).to_return(status: 200, body: oversize_body)
+ end
+
+ it 'queues LogExecutionWorker with stripped response_body' do
+ expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
+ .with(
+ project_hook.id,
+ hash_including(default_log_data.merge(response_body: stripped_body)),
+ :ok,
+ nil
+ )
+
+ service_instance.execute
+ end
+ end
+
+ context 'with massive amount of headers' do
+ let(:response_headers) do
+ (1..described_class::RESPONSE_HEADERS_COUNT_LIMIT + 1).to_a.to_h do |num|
+ ["header-#{num}", SecureRandom.hex(num)]
+ end
+ end
+
+ let(:expected_response_headers) do
+ (1..described_class::RESPONSE_HEADERS_COUNT_LIMIT).to_a.to_h do |num|
+ # Capitalized
+ ["Header-#{num}", response_headers["header-#{num}"]]
+ end
+ end
+
+ before do
+ stub_full_request(project_hook.url, method: :post).to_return(
+ status: 200, body: 'Success', headers: response_headers
+ )
+ end
+
+ it 'queues LogExecutionWorker with limited amount of headers' do
+ expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
+ .with(
+ project_hook.id,
+ hash_including(default_log_data.merge(response_headers: expected_response_headers)),
+ :ok,
+ nil
+ )
+
+ service_instance.execute
+ end
+ end
+
+ context 'with oversize header' do
+ let(:oversize_header) { 'a' * (described_class::RESPONSE_HEADERS_SIZE_LIMIT + 1) }
+ let(:stripped_header) { 'a' * (described_class::RESPONSE_HEADERS_SIZE_LIMIT - ellipsis.bytesize) + ellipsis }
+ let(:response_headers) { { 'oversized-header' => oversize_header } }
+ let(:expected_response_headers) { { 'Oversized-Header' => stripped_header } }
+
+ before do
+ stub_full_request(project_hook.url, method: :post).to_return(
+ status: 200, body: 'Success', headers: response_headers
+ )
+ end
+
+ it 'queues LogExecutionWorker with stripped header value' do
+ expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
+ .with(
+ project_hook.id,
+ hash_including(default_log_data.merge(response_headers: expected_response_headers)),
+ :ok,
+ nil
+ )
+
+ service_instance.execute
+ end
+ end
+
+ context 'with log data exceeding Sidekiq limit' do
+ before do
+ stub_full_request(project_hook.url, method: :post).to_return(status: 200, body: 'Success')
+ end
+
+ it 'queues LogExecutionWorker with request_data overrided in the second attempt' do
+ expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
+ .with(
+ project_hook.id,
+ hash_including(default_log_data),
+ :ok,
+ nil
+ )
+ .and_raise(
+ Gitlab::SidekiqMiddleware::SizeLimiter::ExceedLimitError.new(WebHooks::LogExecutionWorker, 100, 50)
+ )
+ .ordered
+ expect(WebHooks::LogExecutionWorker).to receive(:perform_async)
+ .with(
+ project_hook.id,
+ hash_including(default_log_data.merge(request_data: WebHookLog::OVERSIZE_REQUEST_DATA)),
:ok,
nil
)
+ .and_call_original
+ .ordered
service_instance.execute
end
+
+ context 'new log data still exceeds limit' do
+ before do
+ allow(WebHooks::LogExecutionWorker).to receive(:perform_async).and_raise(
+ Gitlab::SidekiqMiddleware::SizeLimiter::ExceedLimitError.new(WebHooks::LogExecutionWorker, 100, 50)
+ )
+ end
+
+ it 'raises an exception' do
+ expect do
+ service_instance.execute
+ end.to raise_error(Gitlab::SidekiqMiddleware::SizeLimiter::ExceedLimitError)
+ end
+ end
end
end
end
@@ -411,7 +521,7 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state
def expect_to_rate_limit(hook, threshold:, throttled: false)
expect(Gitlab::ApplicationRateLimiter).to receive(:throttled?)
- .with(:web_hook_calls, scope: [hook], threshold: threshold)
+ .with(:web_hook_calls, scope: [hook.parent.root_namespace], threshold: threshold)
.and_return(throttled)
end
@@ -460,13 +570,8 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state
end
end
- context 'when the hook is throttled (via Redis)', :clean_gitlab_redis_rate_limiting do
+ context 'when the hook is throttled (via Redis)', :clean_gitlab_redis_rate_limiting, :freeze_time do
before do
- # Set a high interval to avoid intermittent failures in CI
- allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits).and_return(
- web_hook_calls: { interval: 1.day }
- )
-
expect_to_perform_worker(project_hook).exactly(threshold).times
threshold.times { service_instance.async_execute }