Introduce a new middleware for the test environment that can block requests
The idea is that after each feature spec example, we block all incoming requests at the Rack level, go to the 'about:blank' page, and wait until the current requests reach 0. This should solve the problem where a request would end after database cleaner performed the database truncation. The problem was that a GET request can still lead to records creation (e.g. namespaces or routes). Signed-off-by: Rémy Coutable <remy@rymai.me>
This commit is contained in:
parent
3574963bc0
commit
645a55f19b
6 changed files with 108 additions and 1 deletions
1
Gemfile
1
Gemfile
|
@ -327,6 +327,7 @@ group :test do
|
||||||
gem 'test_after_commit', '~> 1.1'
|
gem 'test_after_commit', '~> 1.1'
|
||||||
gem 'sham_rack', '~> 1.3.6'
|
gem 'sham_rack', '~> 1.3.6'
|
||||||
gem 'timecop', '~> 0.8.0'
|
gem 'timecop', '~> 0.8.0'
|
||||||
|
gem 'concurrent-ruby', '~> 1.0.5'
|
||||||
end
|
end
|
||||||
|
|
||||||
gem 'octokit', '~> 4.6.2'
|
gem 'octokit', '~> 4.6.2'
|
||||||
|
|
|
@ -128,7 +128,7 @@ GEM
|
||||||
execjs
|
execjs
|
||||||
coffee-script-source (1.10.0)
|
coffee-script-source (1.10.0)
|
||||||
colorize (0.7.7)
|
colorize (0.7.7)
|
||||||
concurrent-ruby (1.0.4)
|
concurrent-ruby (1.0.5)
|
||||||
connection_pool (2.2.1)
|
connection_pool (2.2.1)
|
||||||
crack (0.4.3)
|
crack (0.4.3)
|
||||||
safe_yaml (~> 1.0.0)
|
safe_yaml (~> 1.0.0)
|
||||||
|
@ -868,6 +868,7 @@ DEPENDENCIES
|
||||||
chronic (~> 0.10.2)
|
chronic (~> 0.10.2)
|
||||||
chronic_duration (~> 0.10.6)
|
chronic_duration (~> 0.10.6)
|
||||||
coffee-rails (~> 4.1.0)
|
coffee-rails (~> 4.1.0)
|
||||||
|
concurrent-ruby (~> 1.0.5)
|
||||||
connection_pool (~> 2.0)
|
connection_pool (~> 2.0)
|
||||||
creole (~> 0.5.0)
|
creole (~> 0.5.0)
|
||||||
d3_rails (~> 3.5.0)
|
d3_rails (~> 3.5.0)
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
Rails.application.configure do
|
Rails.application.configure do
|
||||||
|
# Make sure the middleware is inserted first in middleware chain
|
||||||
|
config.middleware.insert_before('ActionDispatch::Static', 'Gitlab::Testing::RequestBlockerMiddleware')
|
||||||
|
|
||||||
# Settings specified here will take precedence over those in config/application.rb
|
# Settings specified here will take precedence over those in config/application.rb
|
||||||
|
|
||||||
# The test environment is used exclusively to run your application's
|
# The test environment is used exclusively to run your application's
|
||||||
|
|
61
lib/gitlab/testing/request_blocker_middleware.rb
Normal file
61
lib/gitlab/testing/request_blocker_middleware.rb
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# rubocop:disable Style/ClassVars
|
||||||
|
|
||||||
|
# This is inspired by http://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests
|
||||||
|
# Rack middleware that keeps track of the number of active requests and can block new requests.
|
||||||
|
module Gitlab
|
||||||
|
module Testing
|
||||||
|
class RequestBlockerMiddleware
|
||||||
|
@@num_active_requests = Concurrent::AtomicFixnum.new(0)
|
||||||
|
@@block_requests = Concurrent::AtomicBoolean.new(false)
|
||||||
|
|
||||||
|
# Returns the number of requests the server is currently processing.
|
||||||
|
def self.num_active_requests
|
||||||
|
@@num_active_requests.value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Prevents the server from accepting new requests. Any new requests will return an HTTP
|
||||||
|
# 503 status.
|
||||||
|
def self.block_requests!
|
||||||
|
@@block_requests.value = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Allows the server to accept requests again.
|
||||||
|
def self.allow_requests!
|
||||||
|
@@block_requests.value = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(app)
|
||||||
|
@app = app
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
increment_active_requests
|
||||||
|
if block_requests?
|
||||||
|
block_request(env)
|
||||||
|
else
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
decrement_active_requests
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def block_requests?
|
||||||
|
@@block_requests.true?
|
||||||
|
end
|
||||||
|
|
||||||
|
def block_request(env)
|
||||||
|
[503, {}, []]
|
||||||
|
end
|
||||||
|
|
||||||
|
def increment_active_requests
|
||||||
|
@@num_active_requests.increment
|
||||||
|
end
|
||||||
|
|
||||||
|
def decrement_active_requests
|
||||||
|
@@num_active_requests.decrement
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -35,6 +35,7 @@ RSpec.configure do |config|
|
||||||
config.include Warden::Test::Helpers, type: :request
|
config.include Warden::Test::Helpers, type: :request
|
||||||
config.include LoginHelpers, type: :feature
|
config.include LoginHelpers, type: :feature
|
||||||
config.include SearchHelpers, type: :feature
|
config.include SearchHelpers, type: :feature
|
||||||
|
config.include WaitForRequests, :js
|
||||||
config.include WaitForAjax, type: :feature
|
config.include WaitForAjax, type: :feature
|
||||||
config.include StubConfiguration
|
config.include StubConfiguration
|
||||||
config.include EmailHelpers, type: :mailer
|
config.include EmailHelpers, type: :mailer
|
||||||
|
|
40
spec/support/wait_for_requests.rb
Normal file
40
spec/support/wait_for_requests.rb
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
module WaitForRequests
|
||||||
|
extend self
|
||||||
|
|
||||||
|
# This is inspired by http://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests
|
||||||
|
def wait_for_requests_complete
|
||||||
|
stop_client
|
||||||
|
Gitlab::Testing::RequestBlockerMiddleware.block_requests!
|
||||||
|
wait_for('pending AJAX requests complete') do
|
||||||
|
Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero?
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
Gitlab::Testing::RequestBlockerMiddleware.allow_requests!
|
||||||
|
end
|
||||||
|
|
||||||
|
# Navigate away from the current page which will prevent any new requests from being started
|
||||||
|
def stop_client
|
||||||
|
page.execute_script %Q{
|
||||||
|
window.location = "about:blank";
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Waits until the passed block returns true
|
||||||
|
def wait_for(condition_name, max_wait_time: Capybara.default_max_wait_time, polling_interval: 0.01)
|
||||||
|
wait_until = Time.now + max_wait_time.seconds
|
||||||
|
loop do
|
||||||
|
break if yield
|
||||||
|
if Time.now > wait_until
|
||||||
|
raise "Condition not met: #{condition_name}"
|
||||||
|
else
|
||||||
|
sleep(polling_interval)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.after(:each, :js) do
|
||||||
|
wait_for_requests_complete
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue