diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..985493f06 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +# EditorConfig is awesome: https://EditorConfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 120 +tab_width = 2 +trim_trailing_whitespace = true + +[{*.rb,*.rake}] +indent_size = 2 +indent_style = space + +[{*.har,*.json}] +indent_size = 2 +indent_style = space + +[{*.lock,*.yaml,*.yml}] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..d46506784 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +Gemfile.lock linguist-generated diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml new file mode 100644 index 000000000..4acbb9c04 --- /dev/null +++ b/.github/workflows/analysis.yml @@ -0,0 +1,34 @@ +name: Analysis + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + schedule: + - cron: 13 7 * * 6 + +concurrency: + group: ${{ github.ref }}-analysis + cancel-in-progress: true + +jobs: + linting: + env: + BUNDLE_WITHOUT: test + + name: Ruby linter + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v3 + - + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.1' + bundler-cache: true + - + name: Linting + run: bundle exec rubocop + diff --git a/.rubocop.yml b/.rubocop.yml index 8a1afa36f..8168ee689 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,19 @@ -inherit_from: - - http://shopify.github.io/ruby-style-guide/rubocop.yml +require: + - rubocop-minitest + - rubocop-rake + +inherit_from: .rubocop_todo.yml + +inherit_gem: + rubocop-shopify: rubocop.yml AllCops: - TargetRubyVersion: '2.6' + TargetRubyVersion: '2.7' + NewCops: enable + +Naming/InclusiveLanguage: + Enabled: false + +Style/GlobalVars: + Exclude: + - ext/semian/extconf.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 000000000..2797937e3 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,15 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2022-05-19 18:59:22 UTC using RuboCop version 1.29.1. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 4 +# Configuration parameters: AllowComments, AllowNil. +Lint/SuppressedException: + Exclude: + - 'test/helpers/circuit_breaker_helper.rb' + - 'test/net_http_test.rb' + - 'test/protected_resource_test.rb' diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 26353d5fe..7e252d702 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { - "recommendations": [ - "ms-vscode-remote.vscode-remote-extensionpack" - ], + "recommendations": [ + "ms-vscode-remote.vscode-remote-extensionpack", + "EditorConfig.EditorConfig" + ] } diff --git a/Gemfile b/Gemfile index 811a913bc..c8e837afe 100644 --- a/Gemfile +++ b/Gemfile @@ -1,20 +1,30 @@ -source 'https://rubygems.org' +# frozen_string_literal: true -gem 'benchmark-memory' -gem 'grpc' -gem 'hiredis', '~> 0.6' -gem 'memory_profiler' -gem 'minitest' -gem 'mocha' -gem 'mysql2', '~> 0.5', github: 'brianmario/mysql2' -gem 'pry-byebug', require: false -gem 'rake-compiler' -gem 'rake' -gem 'redis-client', '0.2.0' -gem 'redis' -gem 'rubocop' -gem 'timecop' -gem 'toxiproxy' -gem 'webrick' +source "https://rubygems.org" + +group :test do + gem "benchmark-memory" + gem "grpc" + gem "hiredis", "~> 0.6" + gem "memory_profiler" + gem "minitest" + gem "mocha" + gem "mysql2", "~> 0.5", github: "brianmario/mysql2" + gem "pry-byebug", require: false + gem "rake-compiler" + gem "rake" + gem "redis-client", "0.2.0" + gem "redis" + gem "timecop" + gem "toxiproxy" + gem "webrick" +end + +group :lint do + gem "rubocop-minitest", require: false + gem "rubocop-rake", require: false + gem "rubocop-shopify", require: false + gem "rubocop", require: false +end gemspec diff --git a/Gemfile.lock b/Gemfile.lock index b2b51789d..1f4dc5364 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -62,6 +62,12 @@ GEM unicode-display_width (>= 1.4.0, < 3.0) rubocop-ast (1.18.0) parser (>= 3.1.1.0) + rubocop-minitest (0.19.1) + rubocop (>= 0.90, < 2.0) + rubocop-rake (0.6.0) + rubocop (~> 1.0) + rubocop-shopify (2.6.0) + rubocop (~> 1.29) ruby-progressbar (1.11.0) timecop (0.9.5) toxiproxy (2.0.1) @@ -86,6 +92,9 @@ DEPENDENCIES redis redis-client (= 0.2.0) rubocop + rubocop-minitest + rubocop-rake + rubocop-shopify semian! timecop toxiproxy diff --git a/Rakefile b/Rakefile index 2a8e36001..1ce2bd44f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,17 +1,19 @@ -require 'bundler/gem_tasks' -begin - require 'rubocop/rake_task' - RuboCop::RakeTask.new -rescue LoadError +# frozen_string_literal: true + +require "bundler/gem_tasks" +require "rubocop/rake_task" +RuboCop::RakeTask.new do |task| + task.requires << "rubocop-minitest" + task.requires << "rubocop-rake" end # ========================================================== # Packaging # ========================================================== -GEMSPEC = eval(File.read('semian.gemspec')) +GEMSPEC = eval(File.read("semian.gemspec")) # rubocop:disable Security/Eval -require 'rubygems/package_task' +require "rubygems/package_task" Gem::PackageTask.new(GEMSPEC) do |_pkg| end @@ -19,17 +21,19 @@ end # Ruby Extension # ========================================================== -$LOAD_PATH.unshift File.expand_path("../lib", __FILE__) -require 'semian/platform' +$LOAD_PATH.unshift(File.expand_path("../lib", __FILE__)) +require "semian/platform" if Semian.sysv_semaphores_supported? - require 'rake/extensiontask' - Rake::ExtensionTask.new('semian', GEMSPEC) do |ext| - ext.ext_dir = 'ext/semian' - ext.lib_dir = 'lib/semian' + require "rake/extensiontask" + Rake::ExtensionTask.new("semian", GEMSPEC) do |ext| + ext.ext_dir = "ext/semian" + ext.lib_dir = "lib/semian" end + desc "Build gem" task build: :compile else - task :build do + desc "Build gem" + task :build do # rubocop:disable Rake/DuplicateTask end end @@ -37,23 +41,23 @@ end # Testing # ========================================================== -require 'rake/testtask' -Rake::TestTask.new 'test' do |t| - t.libs = %w(lib test) +require "rake/testtask" +Rake::TestTask.new("test") do |t| + t.libs = ["lib", "test"] t.pattern = "test/*_test.rb" t.warning = false if ENV["CI"] || ENV["VERBOSE"] - t.options = '-v' + t.options = "-v" end end # ========================================================== # Documentation # ========================================================== -require 'rdoc/task' +require "rdoc/task" RDoc::Task.new do |rdoc| rdoc.rdoc_files.include("lib/*.rb", "ext/semian/*.c") end task default: :build -task default: :test +task default: :test # rubocop:disable Rake/DuplicateTask diff --git a/ext/semian/extconf.rb b/ext/semian/extconf.rb index ac2c511be..274c2830f 100644 --- a/ext/semian/extconf.rb +++ b/ext/semian/extconf.rb @@ -1,33 +1,35 @@ -$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__) +# frozen_string_literal: true -require 'semian/platform' +$LOAD_PATH.unshift(File.expand_path("../../../lib", __FILE__)) + +require "semian/platform" unless Semian.sysv_semaphores_supported? - File.write "Makefile", <:mysql_shard0 that has 10 tickets and a default timeout of 500 milliseconds. +# This registers a new resource called :mysql_shard0 that has 10 tickets and +# a default timeout of 500 milliseconds. # # After 3 failures in the span of 10 seconds the circuit will be open. # After an additional 10 seconds it will transition to half-open. @@ -70,8 +80,8 @@ # # Perform a MySQL query here # end # -# This acquires a ticket for the :mysql_shard0 resource. If we use the example above, the ticket count would -# be lowered to 9 when block is executed, then raised to 10 when the block completes. +# This acquires a ticket for the :mysql_shard0 resource. If we use the example above, +# the ticket count would be lowered to 9 when block is executed, then raised to 10 when the block completes. # # ===== Overriding the default timeout # @@ -79,7 +89,8 @@ # # Perform a MySQL query here # end # -# This is the same as the previous example, but overrides the timeout from the default value of 500 milliseconds to 1 second. +# This is the same as the previous example, but overrides the timeout +# from the default value of 500 milliseconds to 1 second. module Semian extend self extend Instrumentable @@ -91,12 +102,14 @@ module Semian OpenCircuitError = Class.new(BaseError) attr_accessor :maximum_lru_size, :minimum_lru_time, :default_permissions, :namespace + self.maximum_lru_size = 500 self.minimum_lru_time = 300 self.default_permissions = 0660 def issue_disabled_semaphores_warning return if defined?(@warning_issued) + @warning_issued = true if !sysv_semaphores_supported? logger.info("Semian sysv semaphores are not supported on #{RUBY_PLATFORM} - all operations will no-op") @@ -119,7 +132,7 @@ def to_s attr_accessor :logger - self.logger = Logger.new(STDERR) + self.logger = Logger.new($stderr) # Registers a resource. # @@ -161,7 +174,7 @@ def register(name, **options) bulkhead = create_bulkhead(name, **options) if circuit_breaker.nil? && bulkhead.nil? - raise ArgumentError, 'Both bulkhead and circuitbreaker cannot be disabled.' + raise ArgumentError, "Both bulkhead and circuitbreaker cannot be disabled." end resources[name] = ProtectedResource.new(name, bulkhead, circuit_breaker) @@ -170,11 +183,10 @@ def register(name, **options) def retrieve_or_register(name, **args) # If consumer who retrieved / registered by a Semian::Adapter, keep track # of who the consumer was so that we can clear the resource reference if needed. - if consumer = args.delete(:consumer) - if consumer.class.include?(Semian::Adapter) - consumers[name] ||= [] - consumers[name] << WeakRef.new(consumer) - end + consumer = args.delete(:consumer) + if consumer&.class&.include?(Semian::Adapter) + consumers[name] ||= [] + consumers[name] << WeakRef.new(consumer) end self[name] || register(name, **args) end @@ -185,9 +197,8 @@ def [](name) end def destroy(name) - if resource = resources.delete(name) - resource.destroy - end + resource = resources.delete(name) + resource&.destroy end def destroy_all_resources @@ -204,17 +215,16 @@ def destroy_all_resources # Also clears any semian_resources # in use by any semian adapters if the weak reference is still alive. def unregister(name) - if resource = resources.delete(name) - resource.bulkhead.unregister_worker if resource.bulkhead + resource = resources.delete(name) + if resource + resource.bulkhead&.unregister_worker consumers_for_resource = consumers.delete(name) || [] consumers_for_resource.each do |consumer| - begin - if consumer.weakref_alive? - consumer.clear_semian_resource - end - rescue WeakRef::RefError - next + if consumer.weakref_alive? + consumer.clear_semian_resource end + rescue WeakRef::RefError + next end end end @@ -243,6 +253,7 @@ def reset! def thread_safe? return @thread_safe if defined?(@thread_safe) + @thread_safe = true end @@ -255,6 +266,7 @@ def thread_safe=(thread_safe) def create_circuit_breaker(name, **options) circuit_breaker = options.fetch(:circuit_breaker, true) return unless circuit_breaker + require_keys!([:success_threshold, :error_threshold, :error_timeout], options) exceptions = options[:exceptions] || [] @@ -277,8 +289,8 @@ def implementation(**options) unless options[:thread_safety_disabled].nil? logger.info( "NOTE: thread_safety_disabled will be replaced by a global setting" \ - "Semian is thread safe by default. It is possible" \ - "to modify the value by using Semian.thread_safe=", + "Semian is thread safe by default. It is possible" \ + "to modify the value by using Semian.thread_safe=", ) end @@ -292,7 +304,11 @@ def create_bulkhead(name, **options) permissions = options[:permissions] || default_permissions timeout = options[:timeout] || 0 - ::Semian::Resource.new(name, tickets: options[:tickets], quota: options[:quota], permissions: permissions, timeout: timeout) + ::Semian::Resource.new(name, + tickets: options[:tickets], + quota: options[:quota], + permissions: permissions, + timeout: timeout) end def require_keys!(required, options) @@ -304,13 +320,13 @@ def require_keys!(required, options) end if Semian.semaphores_enabled? - require 'semian/semian' + require "semian/semian" else Semian::MAX_TICKETS = 0 end if defined? ActiveSupport - ActiveSupport.on_load :active_record do - require 'semian/rails' + ActiveSupport.on_load(:active_record) do + require "semian/rails" end end diff --git a/lib/semian/adapter.rb b/lib/semian/adapter.rb index e78a746cd..fb2922266 100644 --- a/lib/semian/adapter.rb +++ b/lib/semian/adapter.rb @@ -1,9 +1,11 @@ -require 'semian' +# frozen_string_literal: true + +require "semian" module Semian module Adapter def semian_identifier - raise NotImplementedError.new("Semian adapters must implement a `semian_identifier` method") + raise NotImplementedError, "Semian adapters must implement a `semian_identifier` method" end def semian_resource @@ -31,6 +33,7 @@ def clear_semian_resource def acquire_semian_resource(scope:, adapter:, &block) return yield if resource_already_acquired? + semian_resource.acquire(scope: scope, adapter: adapter, resource: self) do mark_resource_as_acquired(&block) end @@ -48,16 +51,17 @@ def acquire_semian_resource(scope:, adapter:, &block) def semian_options return @semian_options if defined? @semian_options + options = raw_semian_options @semian_options = options && options.map { |k, v| [k.to_sym, v] }.to_h end def raw_semian_options - raise NotImplementedError.new("Semian adapters must implement a `raw_semian_options` method") + raise NotImplementedError, "Semian adapters must implement a `raw_semian_options` method" end def resource_exceptions - raise NotImplementedError.new("Semian adapters must implement a `resource_exceptions` method") + raise NotImplementedError, "Semian adapters must implement a `resource_exceptions` method" end def resource_already_acquired? diff --git a/lib/semian/circuit_breaker.rb b/lib/semian/circuit_breaker.rb index 90219e97a..643fed15c 100644 --- a/lib/semian/circuit_breaker.rb +++ b/lib/semian/circuit_breaker.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + module Semian - class CircuitBreaker #:nodoc: + class CircuitBreaker # :nodoc: extend Forwardable def_delegators :@state, :closed?, :open?, :half_open? @@ -7,7 +9,9 @@ class CircuitBreaker #:nodoc: attr_reader :name, :half_open_resource_timeout, :error_timeout, :state, :last_error def initialize(name, exceptions:, success_threshold:, error_threshold:, - error_timeout:, implementation:, half_open_resource_timeout: nil, error_threshold_timeout: nil) + error_timeout:, implementation:, half_open_resource_timeout: nil, + error_threshold_timeout: nil) + @name = name.to_sym @success_count_threshold = success_threshold @error_count_threshold = error_threshold @@ -25,6 +29,7 @@ def initialize(name, exceptions:, success_threshold:, error_threshold:, def acquire(resource = nil, &block) return yield if disabled? + transition_to_half_open if transition_to_half_open? raise OpenCircuitError unless request_allowed? @@ -63,6 +68,7 @@ def mark_failed(error) def mark_success return unless half_open? + @successes.increment transition_to_close if success_threshold_reached? end @@ -80,8 +86,7 @@ def destroy end def in_use? - return false if error_timeout_expired? - @errors.size > 0 + !error_timeout_expired? && !@errors.empty? end private @@ -117,6 +122,7 @@ def error_threshold_reached? def error_timeout_expired? last_error_time = @errors.last return false unless last_error_time + Time.at(last_error_time) + @error_timeout < Time.now end @@ -133,12 +139,12 @@ def log_state_transition(new_state) return if @state.nil? || new_state == @state.value str = "[#{self.class.name}] State transition from #{@state.value} to #{new_state}." - str << " success_count=#{@successes.value} error_count=#{@errors.size}" - str << " success_count_threshold=#{@success_count_threshold} error_count_threshold=#{@error_count_threshold}" - str << " error_timeout=#{@error_timeout} error_last_at=\"#{@errors.last}\"" - str << " name=\"#{@name}\"" + str += " success_count=#{@successes.value} error_count=#{@errors.size}" + str += " success_count_threshold=#{@success_count_threshold} error_count_threshold=#{@error_count_threshold}" + str += " error_timeout=#{@error_timeout} error_last_at=\"#{@errors.last}\"" + str += " name=\"#{@name}\"" if new_state == :open && @last_error - str << " last_error_message=#{@last_error.message.inspect}" + str += " last_error_message=#{@last_error.message.inspect}" end Semian.logger.info(str) @@ -149,7 +155,7 @@ def notify_state_transition(new_state) end def disabled? - ENV['SEMIAN_CIRCUIT_BREAKER_DISABLED'] || ENV['SEMIAN_DISABLED'] + ENV["SEMIAN_CIRCUIT_BREAKER_DISABLED"] || ENV["SEMIAN_DISABLED"] end def maybe_with_half_open_resource_timeout(resource, &block) diff --git a/lib/semian/grpc.rb b/lib/semian/grpc.rb index 38acc20dc..d0e08f8b1 100644 --- a/lib/semian/grpc.rb +++ b/lib/semian/grpc.rb @@ -1,5 +1,7 @@ -require 'semian/adapter' -require 'grpc' +# frozen_string_literal: true + +require "semian/adapter" +require "grpc" module GRPC GRPC::Unavailable.include(::Semian::AdapterError) @@ -22,7 +24,6 @@ def initialize(semian_identifier, *args) module Semian module GRPC - attr_reader :raw_semian_options include Semian::Adapter ResourceBusyError = ::GRPC::ResourceBusyError @@ -40,6 +41,7 @@ class << self def semian_configuration=(configuration) raise Semian::GRPC::SemianConfigurationChangedError unless @semian_configuration.nil? + @semian_configuration = configuration end @@ -52,10 +54,10 @@ def raw_semian_options @raw_semian_options ||= begin # If the host is empty, it's possible that the adapter was initialized # with the channel. Therefore, we look into the channel to find the host - if @host.empty? - host = @ch.target + host = if @host.empty? + @ch.target else - host = @host + @host end @raw_semian_options = Semian::GRPC.retrieve_semian_configuration(host) @raw_semian_options = @raw_semian_options.dup unless @raw_semian_options.nil? @@ -81,21 +83,25 @@ def disabled? def request_response(*, **) return super if disabled? + acquire_semian_resource(adapter: :grpc, scope: :request_response) { super } end def client_streamer(*, **) return super if disabled? + acquire_semian_resource(adapter: :grpc, scope: :client_streamer) { super } end def server_streamer(*, **) return super if disabled? + acquire_semian_resource(adapter: :grpc, scope: :server_streamer) { super } end def bidi_streamer(*, **) return super if disabled? + acquire_semian_resource(adapter: :grpc, scope: :bidi_streamer) { super } end end diff --git a/lib/semian/instrumentable.rb b/lib/semian/instrumentable.rb index ed387d756..966be751a 100644 --- a/lib/semian/instrumentable.rb +++ b/lib/semian/instrumentable.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Semian module Instrumentable def subscribe(name = rand, &block) diff --git a/lib/semian/lru_hash.rb b/lib/semian/lru_hash.rb index 0cf1bbaf1..e5f236f69 100644 --- a/lib/semian/lru_hash.rb +++ b/lib/semian/lru_hash.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LRUHash # This LRU (Least Recently Used) hash will allow # the cleaning of resources as time goes on. @@ -120,10 +122,10 @@ def [](key) def clear_unused_resources payload = { - size: @table.size, - examined: 0, - cleared: 0, - elapsed: nil, + size: @table.size, + examined: 0, + cleared: 0, + elapsed: nil, } timer_start = Process.clock_gettime(Process::CLOCK_MONOTONIC) @@ -155,20 +157,19 @@ def clear_unused_resources end end - EXCEPTION_NEVER = {Exception => :never}.freeze - EXCEPTION_IMMEDIATE = {Exception => :immediate}.freeze + EXCEPTION_NEVER = { Exception => :never }.freeze + EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze private_constant :EXCEPTION_NEVER private_constant :EXCEPTION_IMMEDIATE - def try_synchronize + def try_synchronize(&block) Thread.handle_interrupt(EXCEPTION_NEVER) do - begin - return false unless @lock.try_lock - Thread.handle_interrupt(EXCEPTION_IMMEDIATE) { yield } - true - ensure - @lock.unlock if @lock.owned? - end + return false unless @lock.try_lock + + Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block) + true + ensure + @lock.unlock if @lock.owned? end end end diff --git a/lib/semian/mysql2.rb b/lib/semian/mysql2.rb index 1ef1167aa..801b848a7 100644 --- a/lib/semian/mysql2.rb +++ b/lib/semian/mysql2.rb @@ -1,5 +1,7 @@ -require 'semian/adapter' -require 'mysql2' +# frozen_string_literal: true + +require "semian/adapter" +require "mysql2" module Mysql2 Mysql2::Error.include(::Semian::AdapterError) @@ -32,13 +34,13 @@ module Mysql2 CircuitOpenError = ::Mysql2::CircuitOpenError PingFailure = Class.new(::Mysql2::Error) - DEFAULT_HOST = 'localhost' + DEFAULT_HOST = "localhost" DEFAULT_PORT = 3306 QUERY_WHITELIST = Regexp.union( - /\A(?:\/\*.*?\*\/)?\s*ROLLBACK/i, - /\A(?:\/\*.*?\*\/)?\s*COMMIT/i, - /\A(?:\/\*.*?\*\/)?\s*RELEASE\s+SAVEPOINT/i, + %r{\A(?:/\*.*?\*/)?\s*ROLLBACK}i, + %r{\A(?:/\*.*?\*/)?\s*COMMIT}i, + %r{\A(?:/\*.*?\*/)?\s*RELEASE\s+SAVEPOINT}i, ) # The naked methods are exposed as `raw_query` and `raw_connect` for instrumentation purpose @@ -55,7 +57,8 @@ def self.included(base) def semian_identifier @semian_identifier ||= begin - unless name = semian_options && semian_options[:name] + name = semian_options && semian_options[:name] + unless name host = query_options[:host] || DEFAULT_HOST port = query_options[:port] || DEFAULT_PORT name = "#{host}:#{port}" @@ -68,7 +71,7 @@ def ping result = nil acquire_semian_resource(adapter: :mysql, scope: :ping) do result = raw_ping - raise PingFailure.new(result.to_s) unless result + raise PingFailure, result.to_s unless result end result rescue ResourceBusyError, CircuitOpenError, PingFailure @@ -113,6 +116,7 @@ def query_whitelisted?(sql, *) # data that is not recognized as a valid encoding, in which case we just # return false. return false unless sql.valid_encoding? + raise end @@ -132,7 +136,7 @@ def acquire_semian_resource(**) def raw_semian_options return query_options[:semian] if query_options.key?(:semian) - return query_options['semian'.freeze] if query_options.key?('semian'.freeze) + return query_options["semian"] if query_options.key?("semian") end end end diff --git a/lib/semian/net_http.rb b/lib/semian/net_http.rb index 158850ca7..01c621b3d 100644 --- a/lib/semian/net_http.rb +++ b/lib/semian/net_http.rb @@ -1,5 +1,7 @@ -require 'semian/adapter' -require 'net/http' +# frozen_string_literal: true + +require "semian/adapter" +require "net/http" module Net ProtocolError.include(::Semian::AdapterError) @@ -40,10 +42,11 @@ def semian_identifier ::Net::ProtocolError, ::EOFError, ::IOError, - ::SystemCallError, # includes ::Errno::EINVAL, ::Errno::ECONNRESET, ::Errno::ECONNREFUSED, ::Errno::ETIMEDOUT, and more + ::SystemCallError, # includes ::Errno::EINVAL, ::Errno::ECONNRESET, + # ::Errno::ECONNREFUSED, ::Errno::ETIMEDOUT, and more ].freeze # Net::HTTP can throw many different errors, this tries to capture most of them - module ClassMethods + module ClassMethods def new(*args, semian: true) http = super(*args) http.instance_variable_set(:@semian_enabled, semian) @@ -57,6 +60,7 @@ class << self def semian_configuration=(configuration) raise Semian::NetHTTP::SemianConfigurationChangedError unless @semian_configuration.nil? + @semian_configuration = configuration end @@ -88,11 +92,13 @@ def disabled? def connect return super if disabled? + acquire_semian_resource(adapter: :http, scope: :connection) { super } end def transport_request(*) return super if disabled? + acquire_semian_resource(adapter: :http, scope: :query) do handle_error_responses(super) end diff --git a/lib/semian/platform.rb b/lib/semian/platform.rb index 1442c0b34..897434b2a 100644 --- a/lib/semian/platform.rb +++ b/lib/semian/platform.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Semian extend self @@ -11,6 +13,6 @@ def semaphores_enabled? end def disabled? - ENV['SEMIAN_SEMAPHORES_DISABLED'] || ENV['SEMIAN_DISABLED'] + ENV["SEMIAN_SEMAPHORES_DISABLED"] || ENV["SEMIAN_DISABLED"] end end diff --git a/lib/semian/protected_resource.rb b/lib/semian/protected_resource.rb index 73c10fac9..13a5147a1 100644 --- a/lib/semian/protected_resource.rb +++ b/lib/semian/protected_resource.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + module Semian class ProtectedResource extend Forwardable def_delegators :@bulkhead, :destroy, :count, :semid, :tickets, :registered_workers def_delegators :@circuit_breaker, :reset, :mark_failed, :mark_success, :request_allowed?, - :open?, :closed?, :half_open? + :open?, :closed?, :half_open? attr_reader :bulkhead, :circuit_breaker, :name attr_accessor :updated_at @@ -17,8 +19,8 @@ def initialize(name, bulkhead, circuit_breaker) end def destroy - @bulkhead.destroy unless @bulkhead.nil? - @circuit_breaker.destroy unless @circuit_breaker.nil? + @bulkhead&.destroy + @circuit_breaker&.destroy end def acquire(timeout: nil, scope: nil, adapter: nil, resource: nil) diff --git a/lib/semian/rails.rb b/lib/semian/rails.rb index 4c0aa8c5c..47ab3a2ca 100644 --- a/lib/semian/rails.rb +++ b/lib/semian/rails.rb @@ -1,9 +1,15 @@ -require 'active_record/connection_adapters/abstract_adapter' +# frozen_string_literal: true -class ActiveRecord::ConnectionAdapters::AbstractAdapter - def semian_resource - # support for https://github.com/rails/rails/commit/d86fd6415c0dfce6fadb77e74696cf728e5eb76b - connection = instance_variable_defined?(:@raw_connection) ? @raw_connection : @connection - connection.semian_resource +require "active_record/connection_adapters/abstract_adapter" + +module ActiveRecord + module ConnectionAdapters + class AbstractAdapter + def semian_resource + # support for https://github.com/rails/rails/commit/d86fd6415c0dfce6fadb77e74696cf728e5eb76b + connection = instance_variable_defined?(:@raw_connection) ? @raw_connection : @connection + connection.semian_resource + end + end end end diff --git a/lib/semian/redis.rb b/lib/semian/redis.rb index d8da64afa..5b66ba508 100644 --- a/lib/semian/redis.rb +++ b/lib/semian/redis.rb @@ -1,5 +1,7 @@ -require 'semian/adapter' -require 'redis' +# frozen_string_literal: true + +require "semian/adapter" +require "redis" class Redis Redis::BaseConnectionError.include(::Semian::AdapterError) @@ -17,7 +19,7 @@ class OutOfMemoryError < Redis::CommandError end class ConnectionError < Redis::BaseConnectionError - # A Connection Reset is a fast failure and we don't want to track these errors in + # A Connection Reset is a fast failure and we don't want to track these errors in # semian def marks_semian_circuits? message != "Connection lost (ECONNRESET)" @@ -90,12 +92,11 @@ def io(&block) def connect acquire_semian_resource(adapter: :redis, scope: :connection) do - begin - raw_connect - rescue SocketError, RuntimeError => e - raise ResolveError.new(semian_identifier) if dns_resolve_failure?(e.cause || e) - raise - end + raw_connect + rescue SocketError, RuntimeError => e + raise ResolveError, semian_identifier if dns_resolve_failure?(e.cause || e) + + raise end end @@ -108,7 +109,7 @@ def with_resource_timeout(temp_timeout) begin connection.timeout = temp_timeout if connected? options[:timeout] = Float(temp_timeout), - options[:connect_timeout] = Float(temp_timeout) + options[:connect_timeout] = Float(temp_timeout) options[:read_timeout] = Float(temp_timeout) options[:write_timeout] = Float(temp_timeout) yield @@ -133,17 +134,18 @@ def resource_exceptions def raw_semian_options return options[:semian] if options.key?(:semian) - return options['semian'.freeze] if options.key?('semian'.freeze) + return options["semian"] if options.key?("semian") end def raise_if_out_of_memory(reply) return unless reply.is_a?(::Redis::CommandError) return unless reply.message.start_with?("OOM ") - raise ::Redis::OutOfMemoryError.new(reply.message) + + raise ::Redis::OutOfMemoryError, reply.message end def dns_resolve_failure?(e) - e.to_s.match?(/(can't resolve)|(name or service not known)|(nodename nor servname provided, or not known)|(failure in name resolution)/i) + e.to_s.match?(/(can't resolve)|(name or service not known)|(nodename nor servname provided, or not known)|(failure in name resolution)/i) # rubocop:disable Layout/LineLength end end end diff --git a/lib/semian/redis_client.rb b/lib/semian/redis_client.rb index 2e9bb0e3b..257966bcf 100644 --- a/lib/semian/redis_client.rb +++ b/lib/semian/redis_client.rb @@ -1,5 +1,7 @@ -require 'semian/adapter' -require 'redis-client' +# frozen_string_literal: true + +require "semian/adapter" +require "redis-client" class RedisClient ConnectionError.include(::Semian::AdapterError) @@ -104,7 +106,7 @@ module RedisClientPool include RedisClientCommon define_method(:semian_resource, Semian::Adapter.instance_method(:semian_resource)) define_method(:clear_semian_resource, Semian::Adapter.instance_method(:clear_semian_resource)) - end + end end RedisClient.prepend(Semian::RedisClient) diff --git a/lib/semian/resource.rb b/lib/semian/resource.rb index 6a6e3429e..316b20b7f 100644 --- a/lib/semian/resource.rb +++ b/lib/semian/resource.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module Semian - class Resource #:nodoc: - attr_reader :tickets, :name + class Resource # :nodoc: + attr_reader :name class << Semian::Resource # Ensure that there can only be one resource of a given type @@ -55,7 +57,7 @@ def semid end def key - '0x00000000' + "0x00000000" end def in_use? diff --git a/lib/semian/simple_integer.rb b/lib/semian/simple_integer.rb index 51bc498bb..3d8708808 100644 --- a/lib/semian/simple_integer.rb +++ b/lib/semian/simple_integer.rb @@ -1,8 +1,10 @@ -require 'thread' +# frozen_string_literal: true + +require "thread" module Semian module Simple - class Integer #:nodoc: + class Integer # :nodoc: attr_accessor :value def initialize diff --git a/lib/semian/simple_sliding_window.rb b/lib/semian/simple_sliding_window.rb index 55a2bb713..64c530f4f 100644 --- a/lib/semian/simple_sliding_window.rb +++ b/lib/semian/simple_sliding_window.rb @@ -1,11 +1,13 @@ -require 'thread' +# frozen_string_literal: true + +require "thread" module Semian module Simple - class SlidingWindow #:nodoc: + class SlidingWindow # :nodoc: extend Forwardable - def_delegators :@window, :size, :last + def_delegators :@window, :size, :last, :empty? attr_reader :max_size # A sliding window is a structure that stores the most @max_size recent timestamps diff --git a/lib/semian/simple_state.rb b/lib/semian/simple_state.rb index 322d333d1..2b414956f 100644 --- a/lib/semian/simple_state.rb +++ b/lib/semian/simple_state.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module Semian module Simple - class State #:nodoc: + class State # :nodoc: def initialize reset end diff --git a/lib/semian/unprotected_resource.rb b/lib/semian/unprotected_resource.rb index 8a88b8268..a6ee4d45c 100644 --- a/lib/semian/unprotected_resource.rb +++ b/lib/semian/unprotected_resource.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Semian # This class acts as a replacement for `ProtectedResource` when # the semian configuration of an `Adapter` is missing or explicitly disabled diff --git a/lib/semian/version.rb b/lib/semian/version.rb index d7bd8200e..eb22be1ff 100644 --- a/lib/semian/version.rb +++ b/lib/semian/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Semian - VERSION = '0.12.0' + VERSION = "0.12.0" end diff --git a/semian.gemspec b/semian.gemspec index eee8ace0a..f26dc490f 100644 --- a/semian.gemspec +++ b/semian.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |s| } s.files = ::Dir["{lib,ext}/**/**/*.{rb,h,c}"] + - ::Dir.glob('*.md') - + ::Dir.glob("*.md") s.extensions = ["ext/semian/extconf.rb"] end diff --git a/test/adapter_test.rb b/test/adapter_test.rb index fe9198eaf..c10ef25e7 100644 --- a/test/adapter_test.rb +++ b/test/adapter_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class TestSemianAdapter < Minitest::Test def setup @@ -24,14 +26,18 @@ def test_unregister skip if ENV["SKIP_FLAKY_TESTS"] client = Semian::AdapterTestClient.new(quota: 0.5) assert_nil(Semian.resources[:testing_unregister]) - resource = Semian.register(:testing_unregister, tickets: 2, error_threshold: 0, error_timeout: 0, success_threshold: 0) + resource = Semian.register(:testing_unregister, + tickets: 2, + error_threshold: 0, + error_timeout: 0, + success_threshold: 0) assert_equal(Semian.resources[:testing_unregister], resource) - assert_equal 1, resource.registered_workers + assert_equal(1, resource.registered_workers) without_gc do Semian.unregister(:testing_unregister) - assert_equal 0, resource.registered_workers + assert_equal(0, resource.registered_workers) assert_empty(Semian.resources) assert_empty(Semian.consumers) @@ -54,7 +60,7 @@ def test_unregister_all_resources assert_equal(resource, client.semian_resource) Semian.unregister_all_resources - assert Semian.resources.empty? + assert_empty(Semian.resources) assert_empty(Semian.consumers) # The first call to client.semian_resource after unregistering all resources, @@ -70,15 +76,15 @@ def test_consumer_registration_does_not_prevent_gc # Release the only strong reference to the client object # so that it will be cleared on the forced GC run below - client = nil + client = nil # rubocop:disable Lint/UselessAssignment weak_ref = Semian.consumers[identifier].first - assert_equal(true, weak_ref.weakref_alive?) + assert_predicate(weak_ref, :weakref_alive?) GC.start(full_mark: true, immediate_sweep: true) - assert_nil weak_ref.weakref_alive? + assert_nil(weak_ref.weakref_alive?) - assert_raises WeakRef::RefError do + assert_raises(WeakRef::RefError) do weak_ref.any_method_call end end diff --git a/test/benchmark/lru_benchmarker.rb b/test/benchmark/lru_benchmarker.rb index 78d1e2bfc..146ca827e 100644 --- a/test/benchmark/lru_benchmarker.rb +++ b/test/benchmark/lru_benchmarker.rb @@ -1,18 +1,20 @@ +# frozen_string_literal: true + # Benchmarks the usage of an LRUHash during the set operation. # To make sure we are cleaning resources, MINIMUM_TIME_IN_LRU needs # to be set to 0 -$LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__) -$LOAD_PATH.unshift File.expand_path('../../../test', __FILE__) -require 'thin' -require 'benchmark' -require 'benchmark/ips' -require 'benchmark/memory' -require 'semian' -require 'semian/net_http' -require 'toxiproxy' -require 'yaml' -require 'byebug' -require 'minitest' +$LOAD_PATH.unshift(File.expand_path("../../../lib", __FILE__)) +$LOAD_PATH.unshift(File.expand_path("../../../test", __FILE__)) +require "thin" +require "benchmark" +require "benchmark/ips" +require "benchmark/memory" +require "semian" +require "semian/net_http" +require "toxiproxy" +require "yaml" +require "byebug" +require "minitest" class LRUBenchmarker def run_ips_benchmark @@ -45,9 +47,11 @@ def create_request(i) # make it a success or a failure random = rand(1...100) if random >= 0 && random <= 50 - Semian.register("testing_#{i}", bulkhead: true, tickets: 1, error_threshold: 2, error_timeout: 5, success_threshold: 1) + Semian.register("testing_#{i}", bulkhead: true, tickets: 1, error_threshold: 2, error_timeout: 5, + success_threshold: 1) else - Semian.register("testing_#{i}", bulkhead: false, tickets: 1, error_threshold: 2, error_timeout: 5, success_threshold: 1) + Semian.register("testing_#{i}", bulkhead: false, tickets: 1, error_threshold: 2, error_timeout: 5, + success_threshold: 1) end end diff --git a/test/circuit_breaker_test.rb b/test/circuit_breaker_test.rb index 146419f31..0d18253eb 100644 --- a/test/circuit_breaker_test.rb +++ b/test/circuit_breaker_test.rb @@ -1,29 +1,32 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class TestCircuitBreaker < Minitest::Test include CircuitBreakerHelper def setup @strio = StringIO.new - Semian.logger = Logger.new @strio + Semian.logger = Logger.new(@strio) begin Semian.destroy(:testing) rescue nil end - Semian.register(:testing, tickets: 1, exceptions: [SomeError], error_threshold: 2, error_timeout: 5, success_threshold: 1) + Semian.register(:testing, tickets: 1, exceptions: [SomeError], error_threshold: 2, error_timeout: 5, + success_threshold: 1) @resource = Semian[:testing] end def test_acquire_yield_when_the_circuit_is_closed block_called = false @resource.acquire { block_called = true } - assert_equal true, block_called + assert(block_called) end def test_acquire_raises_circuit_open_error_when_the_circuit_is_open open_circuit! - assert_raises Semian::OpenCircuitError do + assert_raises(Semian::OpenCircuitError) do @resource.acquire { 1 + 1 } end assert_match(/State transition from closed to open/, @strio.string) @@ -78,7 +81,8 @@ def test_errors_more_than_duration_apart_doesnt_open_circuit end def test_sparse_errors_dont_open_circuit - resource = Semian.register(:three, tickets: 1, exceptions: [SomeError], error_threshold: 3, error_timeout: 5, success_threshold: 1) + resource = Semian.register(:three, tickets: 1, exceptions: [SomeError], error_threshold: 3, error_timeout: 5, + success_threshold: 1) Timecop.travel(-6) do trigger_error!(resource) @@ -100,16 +104,17 @@ def test_request_allowed_query_doesnt_trigger_transitions Timecop.travel(Time.now - 6) do open_circuit! - refute_predicate @resource, :request_allowed? - assert_predicate @resource, :open? + refute_predicate(@resource, :request_allowed?) + assert_predicate(@resource, :open?) end - assert_predicate @resource, :request_allowed? - assert_predicate @resource, :open? + assert_predicate(@resource, :request_allowed?) + assert_predicate(@resource, :open?) end def test_open_close_open_cycle - resource = Semian.register(:open_close, tickets: 1, exceptions: [SomeError], error_threshold: 2, error_timeout: 5, success_threshold: 2) + resource = Semian.register(:open_close, tickets: 1, exceptions: [SomeError], error_threshold: 2, error_timeout: 5, + success_threshold: 2) open_circuit!(resource) assert_circuit_opened(resource) @@ -117,10 +122,10 @@ def test_open_close_open_cycle Timecop.travel(resource.circuit_breaker.error_timeout + 1) do assert_circuit_closed(resource) - assert resource.half_open? + assert_predicate(resource, :half_open?) assert_circuit_closed(resource) - assert resource.closed? + assert_predicate(resource, :closed?) open_circuit!(resource) assert_circuit_opened(resource) @@ -128,16 +133,17 @@ def test_open_close_open_cycle Timecop.travel(resource.circuit_breaker.error_timeout + 1) do assert_circuit_closed(resource) - assert resource.half_open? + assert_predicate(resource, :half_open?) assert_circuit_closed(resource) - assert resource.closed? + assert_predicate(resource, :closed?) end end end def test_error_error_threshold_timeout_overrides_error_timeout_when_set_for_opening_circuits - resource = Semian.register(:three, tickets: 1, exceptions: [SomeError], error_threshold: 3, error_timeout: 5, success_threshold: 1, error_threshold_timeout: 10) + resource = Semian.register(:three, tickets: 1, exceptions: [SomeError], error_threshold: 3, error_timeout: 5, + success_threshold: 1, error_threshold_timeout: 10) Timecop.travel(-6) do trigger_error!(resource) @@ -156,7 +162,8 @@ def test_error_error_threshold_timeout_overrides_error_timeout_when_set_for_open end def test_error_threshold_timeout_defaults_to_error_timeout_when_not_specified - resource = Semian.register(:three, tickets: 1, exceptions: [SomeError], error_threshold: 3, error_timeout: 5, success_threshold: 1) + resource = Semian.register(:three, tickets: 1, exceptions: [SomeError], error_threshold: 3, error_timeout: 5, + success_threshold: 1) Timecop.travel(-6) do trigger_error!(resource) @@ -175,19 +182,19 @@ def test_error_threshold_timeout_defaults_to_error_timeout_when_not_specified end def test_env_var_disables_circuit_breaker - ENV['SEMIAN_CIRCUIT_BREAKER_DISABLED'] = '1' + ENV["SEMIAN_CIRCUIT_BREAKER_DISABLED"] = "1" open_circuit! assert_circuit_closed ensure - ENV.delete('SEMIAN_CIRCUIT_BREAKER_DISABLED') + ENV.delete("SEMIAN_CIRCUIT_BREAKER_DISABLED") end def test_semian_wide_env_var_disables_circuit_breaker - ENV['SEMIAN_DISABLED'] = '1' + ENV["SEMIAN_DISABLED"] = "1" open_circuit! assert_circuit_closed ensure - ENV.delete('SEMIAN_DISABLED') + ENV.delete("SEMIAN_DISABLED") end class RawResource @@ -206,8 +213,8 @@ def with_resource_timeout(timeout) def test_changes_resource_timeout_when_configured Semian.register(:resource_timeout, tickets: 1, exceptions: [SomeError], - error_threshold: 2, error_timeout: 5, success_threshold: 1, - half_open_resource_timeout: 0.123) + error_threshold: 2, error_timeout: 5, success_threshold: 1, + half_open_resource_timeout: 0.123) resource = Semian[:resource_timeout] half_open_cicuit!(resource) @@ -217,17 +224,17 @@ def test_changes_resource_timeout_when_configured triggered = false resource.acquire(resource: raw_resource) do triggered = true - assert_equal 0.123, raw_resource.timeout + assert_in_delta(0.123, raw_resource.timeout) end - assert triggered - assert_equal 2, raw_resource.timeout + assert(triggered) + assert_equal(2, raw_resource.timeout) end def test_doesnt_change_resource_timeout_when_closed Semian.register(:resource_timeout, tickets: 1, exceptions: [SomeError], - error_threshold: 2, error_timeout: 5, success_threshold: 1, - half_open_resource_timeout: 0.123) + error_threshold: 2, error_timeout: 5, success_threshold: 1, + half_open_resource_timeout: 0.123) resource = Semian[:resource_timeout] raw_resource = RawResource.new @@ -235,17 +242,17 @@ def test_doesnt_change_resource_timeout_when_closed triggered = false resource.acquire(resource: raw_resource) do triggered = true - assert_equal 2, raw_resource.timeout + assert_equal(2, raw_resource.timeout) end - assert triggered - assert_equal 2, raw_resource.timeout + assert(triggered) + assert_equal(2, raw_resource.timeout) end def test_doesnt_blow_up_when_configured_half_open_timeout_but_adapter_doesnt_support Semian.register(:resource_timeout, tickets: 1, exceptions: [SomeError], - error_threshold: 2, error_timeout: 5, success_threshold: 1, - half_open_resource_timeout: 0.123) + error_threshold: 2, error_timeout: 5, success_threshold: 1, + half_open_resource_timeout: 0.123) resource = Semian[:resource_timeout] raw_resource = Object.new @@ -255,7 +262,7 @@ def test_doesnt_blow_up_when_configured_half_open_timeout_but_adapter_doesnt_sup triggered = true end - assert triggered + assert(triggered) end class SomeErrorThatMarksCircuits < SomeError @@ -286,13 +293,13 @@ def test_notify_state_transition events = [] Semian.subscribe(:test_notify_state_transition) do |event, resource, _scope, _adapter, payload| if event == :state_change - events << {name: resource.name, state: payload[:state]} + events << { name: resource.name, state: payload[:state] } end end # Creating a resource should generate a :closed notification. resource = Semian.register(name, tickets: 1, exceptions: [StandardError], - error_threshold: 2, error_timeout: 1, success_threshold: 1) + error_threshold: 2, error_timeout: 1, success_threshold: 1) assert_equal(1, events.length) assert_equal(name, events[0][:name]) assert_equal(:closed, events[0][:state]) diff --git a/test/config/semian_config.rb b/test/config/semian_config.rb index 16c7df44d..eed2caa84 100644 --- a/test/config/semian_config.rb +++ b/test/config/semian_config.rb @@ -1,7 +1,9 @@ -require 'yaml' +# frozen_string_literal: true + +require "yaml" class SemianConfig - CONFIG_FILE = File.expand_path('../hosts.yml', __FILE__) + CONFIG_FILE = File.expand_path("../hosts.yml", __FILE__) class << self def [](service) diff --git a/test/echo_pb.rb b/test/echo_pb.rb index c62adc075..e92388434 100644 --- a/test/echo_pb.rb +++ b/test/echo_pb.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + # Generated by the protocol buffer compiler. DO NOT EDIT! # source: echo.proto -require 'google/protobuf' +require "google/protobuf" Google::Protobuf::DescriptorPool.generated_pool.build do add_message "echo.EchoRequest" do diff --git a/test/echo_service.rb b/test/echo_service.rb index f0d735c74..d5e451f88 100644 --- a/test/echo_service.rb +++ b/test/echo_service.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + # A test service with an echo implementation. class EchoMsg def self.marshal(_o) - '' + "" end def self.unmarshal(_o) @@ -23,7 +25,7 @@ def initialize(**kw) end def an_rpc(req, call) - GRPC.logger.info('echo service received a request') + GRPC.logger.info("echo service received a request") call.output_metadata.update(@trailing_metadata) @received_md << call.metadata unless call.metadata.nil? req diff --git a/test/echo_services_pb.rb b/test/echo_services_pb.rb index 8e23165a9..6fd93fbbb 100644 --- a/test/echo_services_pb.rb +++ b/test/echo_services_pb.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Generated by the protocol buffer compiler. DO NOT EDIT! # Source: echo.proto for package 'echo' # Original file comments: @@ -16,8 +18,8 @@ # limitations under the License. # -require 'grpc' -require 'echo_pb' +require "grpc" +require "echo_pb" module Echo module EchoServer @@ -26,7 +28,7 @@ class Service self.marshal_class_method = :encode self.unmarshal_class_method = :decode - self.service_name = 'echo.EchoServer' + self.service_name = "echo.EchoServer" rpc :Echo, EchoRequest, EchoReply end diff --git a/test/grpc_test.rb b/test/grpc_test.rb index 9350535e4..450f36928 100644 --- a/test/grpc_test.rb +++ b/test/grpc_test.rb @@ -1,9 +1,11 @@ -require 'test_helper' -require 'grpc' -require 'semian/grpc' -require 'minitest' -require 'mocha/minitest' -require 'echo_service' +# frozen_string_literal: true + +require "test_helper" +require "grpc" +require "semian/grpc" +require "minitest" +require "mocha/minitest" +require "echo_service" class TestGRPC < Minitest::Test ERROR_THRESHOLD = 1 @@ -17,7 +19,11 @@ class TestGRPC < Minitest::Test } DEFAULT_SEMIAN_CONFIGURATION = proc do |host| - next nil if host == SemianConfig['toxiproxy_upstream_host'] && port == SemianConfig['toxiproxy_upstream_port'] # disable if toxiproxy + if host == SemianConfig["toxiproxy_upstream_host"] && \ + port == SemianConfig["toxiproxy_upstream_port"] # disable if toxiproxy + next nil + end + SEMIAN_OPTIONS.merge(name: host) end @@ -34,15 +40,15 @@ def teardown end def test_semian_identifier - assert_equal @host, @stub.semian_identifier + assert_equal(@host, @stub.semian_identifier) end def test_errors_are_tagged_with_the_resource_identifier GRPC::ActiveCall.any_instance.stubs(:request_response).raises(::GRPC::Unavailable) - error = assert_raises ::GRPC::Unavailable do + error = assert_raises(::GRPC::Unavailable) do @stub.an_rpc(EchoMsg.new) end - assert_equal @host, error.semian_identifier + assert_equal(@host, error.semian_identifier) end def test_rpc_server @@ -55,29 +61,30 @@ def test_rpc_server def test_unavailable_server_opens_the_circuit GRPC::ActiveCall.any_instance.stubs(:request_response).raises(::GRPC::Unavailable) ERROR_THRESHOLD.times do - assert_raises ::GRPC::Unavailable do + assert_raises(::GRPC::Unavailable) do @stub.an_rpc(EchoMsg.new) end end - assert_raises GRPC::CircuitOpenError do + assert_raises(GRPC::CircuitOpenError) do @stub.an_rpc(EchoMsg.new) end end def test_timeout_opens_the_circuit skip if ENV["SKIP_FLAKY_TESTS"] - stub = build_insecure_stub(EchoStub, host: "#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['grpc_toxiproxy_port']}", opts: {timeout: 0.1}) + stub = build_insecure_stub(EchoStub, + host: "#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["grpc_toxiproxy_port"]}", opts: { timeout: 0.1 }) run_services_on_server(@server, services: [EchoService]) do - Toxiproxy['semian_test_grpc'].downstream(:latency, latency: 1000).apply do + Toxiproxy["semian_test_grpc"].downstream(:latency, latency: 1000).apply do ERROR_THRESHOLD.times do - assert_raises GRPC::DeadlineExceeded do + assert_raises(GRPC::DeadlineExceeded) do stub.an_rpc(EchoMsg.new) end end end - Toxiproxy['semian_test_grpc'].downstream(:latency, latency: 1000).apply do - assert_raises GRPC::CircuitOpenError do + Toxiproxy["semian_test_grpc"].downstream(:latency, latency: 1000).apply do + assert_raises(GRPC::CircuitOpenError) do stub.an_rpc(EchoMsg.new) end end @@ -90,9 +97,9 @@ def test_instrumentation next if event != :success notified = true - assert_equal Semian[@host], resource - assert_equal :request_response, scope - assert_equal :grpc, adapter + assert_equal(Semian[@host], resource) + assert_equal(:request_response, scope) + assert_equal(:grpc, adapter) end run_services_on_server(@server, services: [EchoService]) do @@ -100,7 +107,7 @@ def test_instrumentation @stub.an_rpc(EchoMsg.new) end - assert notified, 'No notifications has been emitted' + assert(notified, "No notifications has been emitted") ensure Semian.unsubscribe(subscriber) end @@ -110,7 +117,7 @@ def test_circuit_breaker_on_client_streamer requests = [EchoMsg.new, EchoMsg.new] open_circuit!(stub, :a_client_streaming_rpc, requests) - assert_raises GRPC::CircuitOpenError do + assert_raises(GRPC::CircuitOpenError) do stub.a_client_streaming_rpc(requests) end end @@ -120,7 +127,7 @@ def test_circuit_breaker_on_server_streamer request = EchoMsg.new open_circuit!(stub, :a_server_streaming_rpc, request) - assert_raises GRPC::CircuitOpenError do + assert_raises(GRPC::CircuitOpenError) do stub.a_server_streaming_rpc(request) end end @@ -130,7 +137,7 @@ def test_circuit_breaker_on_bidi_streamer requests = [EchoMsg.new, EchoMsg.new] open_circuit!(stub, :a_bidi_rpc, requests) - assert_raises GRPC::CircuitOpenError do + assert_raises(GRPC::CircuitOpenError) do stub.a_bidi_rpc(requests) end end @@ -142,7 +149,7 @@ def test_bulkheads_tickets_are_working success_threshold: 1, error_threshold: 3, error_timeout: 10, - name: "#{host}", + name: host.to_s, } end Semian::GRPC.instance_variable_set(:@semian_configuration, nil) @@ -154,7 +161,7 @@ def test_bulkheads_tickets_are_working stub1.semian_resource.acquire do stub2 = build_insecure_stub(EchoStub, host: "0.0.0.1") stub2.semian_resource.acquire do - assert_raises GRPC::ResourceBusyError do + assert_raises(GRPC::ResourceBusyError) do stub2.an_rpc(EchoMsg.new) end end @@ -166,7 +173,7 @@ def test_bulkheads_tickets_are_working def open_circuit!(stub, method, args) ERROR_THRESHOLD.times do - assert_raises GRPC::Unavailable do + assert_raises(GRPC::Unavailable) do stub.send(method, args) end end @@ -179,9 +186,9 @@ def build_insecure_stub(klass, host: nil, opts: nil) end def build_rpc_server(server_opts: {}, client_opts: {}) - @hostname = SemianConfig['grpc_host'] - @server = new_rpc_server_for_testing({poll_period: 1}.merge(server_opts)) - @port = @server.add_http2_port("0.0.0.0:#{SemianConfig['grpc_port']}", :this_port_is_insecure) + @hostname = SemianConfig["grpc_host"] + @server = new_rpc_server_for_testing({ poll_period: 1 }.merge(server_opts)) + @port = @server.add_http2_port("0.0.0.0:#{SemianConfig["grpc_port"]}", :this_port_is_insecure) @host = "#{@hostname}:#{@port}" @client_opts = client_opts @server diff --git a/test/helpers/adapter_helper.rb b/test/helpers/adapter_helper.rb index b55ef4994..597ec7972 100644 --- a/test/helpers/adapter_helper.rb +++ b/test/helpers/adapter_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Semian module AdapterTest include Semian::Adapter @@ -20,8 +22,8 @@ class AdapterTestClient def initialize(**args) @client_options = args.merge(success_threshold: 1, - error_threshold: 1, - error_timeout: 1) + error_threshold: 1, + error_timeout: 1) end def ==(other) diff --git a/test/helpers/background_helper.rb b/test/helpers/background_helper.rb index 13c0d8f8d..83c1dd68c 100644 --- a/test/helpers/background_helper.rb +++ b/test/helpers/background_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BackgroundHelper attr_writer :threads diff --git a/test/helpers/circuit_breaker_helper.rb b/test/helpers/circuit_breaker_helper.rb index 69e29bed7..0e328fc96 100644 --- a/test/helpers/circuit_breaker_helper.rb +++ b/test/helpers/circuit_breaker_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module CircuitBreakerHelper SomeError = Class.new(StandardError) @@ -21,7 +23,7 @@ def trigger_error!(resource = @resource, error = SomeError) def assert_circuit_closed(resource = @resource) block_called = false resource.acquire { block_called = true } - assert block_called, 'Expected the circuit to be closed, but it was open' + assert(block_called, "Expected the circuit to be closed, but it was open") end def assert_circuit_opened(resource = @resource) @@ -31,6 +33,6 @@ def assert_circuit_opened(resource = @resource) rescue Semian::OpenCircuitError open = true end - assert open, 'Expected the circuit to be open, but it was closed' + assert(open, "Expected the circuit to be open, but it was closed") end end diff --git a/test/helpers/mock_server.rb b/test/helpers/mock_server.rb index 7b28942b5..a11b0a4fa 100644 --- a/test/helpers/mock_server.rb +++ b/test/helpers/mock_server.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'webrick' +require "webrick" class MockServer class << self @@ -41,10 +41,10 @@ def start_server response_code = 200 if response_code == "" res.status = response_code - res.content_type = 'text/html' + res.content_type = "text/html" rescue WEBrick::HTTPStatus::EOFError, WEBrick::HTTPStatus::BadRequest res.status = 400 - res.content_type = 'text/html' + res.content_type = "text/html" ensure res.send_response(sock) end diff --git a/test/helpers/resource_helper.rb b/test/helpers/resource_helper.rb index 59658acfb..4d640da81 100644 --- a/test/helpers/resource_helper.rb +++ b/test/helpers/resource_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ResourceHelper private @@ -10,12 +12,11 @@ def create_resource(*args) def destroy_resources return unless @resources + @resources.each do |resource| - begin - resource.destroy - rescue - nil - end + resource.destroy + rescue + nil end @resources = [] end diff --git a/test/instrumentation_test.rb b/test/instrumentation_test.rb index 5b466d4f1..19cef6156 100644 --- a/test/instrumentation_test.rb +++ b/test/instrumentation_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class TestInstrumentation < Minitest::Test def setup @@ -9,7 +11,7 @@ def setup def test_busy_instrumentation assert_notify(:success, :busy, :state_change) do Semian[:testing].acquire do - assert_raises Semian::TimeoutError do + assert_raises(Semian::TimeoutError) do Semian[:testing].acquire {} end end @@ -19,14 +21,14 @@ def test_busy_instrumentation def test_circuit_open_instrumentation assert_notify(:success, :busy, :state_change) do Semian[:testing].acquire do - assert_raises Semian::TimeoutError do + assert_raises(Semian::TimeoutError) do Semian[:testing].acquire {} end end end assert_notify(:circuit_open) do - assert_raises Semian::OpenCircuitError do + assert_raises(Semian::OpenCircuitError) do Semian[:testing].acquire {} end end @@ -59,7 +61,7 @@ def test_success_instrumentation_wait_time def test_success_instrumentation_when_unknown_exceptions_occur assert_notify(:success) do - assert_raises RuntimeError do + assert_raises(RuntimeError) do Semian[:testing].acquire { raise "Some error" } end end @@ -73,7 +75,7 @@ def assert_notify(*expected_events) events << event end yield - assert_equal expected_events, events, "The timeline of events was not as expected" + assert_equal(expected_events, events, "The timeline of events was not as expected") ensure Semian.unsubscribe(subscription) end diff --git a/test/lru_hash_test.rb b/test/lru_hash_test.rb index 8e0d10b6d..97567ddfc 100644 --- a/test/lru_hash_test.rb +++ b/test/lru_hash_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class TestLRUHash < Minitest::Test def setup @@ -7,128 +9,129 @@ def setup end def test_set_get_item - circuit_breaker = create_circuit_breaker('a') - @lru_hash.set('key', circuit_breaker) - assert_equal circuit_breaker, @lru_hash.get('key') + circuit_breaker = create_circuit_breaker("a") + @lru_hash.set("key", circuit_breaker) + assert_equal(circuit_breaker, @lru_hash.get("key")) end def test_set_get_item_with_thread_safe_disabled Semian.thread_safe = false @lru_hash = LRUHash.new - circuit_breaker = create_circuit_breaker('a') - @lru_hash.set('key', circuit_breaker) - assert_equal circuit_breaker, @lru_hash.get('key') + circuit_breaker = create_circuit_breaker("a") + @lru_hash.set("key", circuit_breaker) + assert_equal(circuit_breaker, @lru_hash.get("key")) end def test_set_get_item_with_thread_safe_enabled - circuit_breaker = create_circuit_breaker('a') - @lru_hash.set('key', circuit_breaker) - assert_equal circuit_breaker, @lru_hash.get('key') + circuit_breaker = create_circuit_breaker("a") + @lru_hash.set("key", circuit_breaker) + assert_equal(circuit_breaker, @lru_hash.get("key")) end def test_remove_item - @lru_hash.set('a', create_circuit_breaker('a')) - @lru_hash.set('b', create_circuit_breaker('b')) - @lru_hash.set('c', create_circuit_breaker('c')) + @lru_hash.set("a", create_circuit_breaker("a")) + @lru_hash.set("b", create_circuit_breaker("b")) + @lru_hash.set("c", create_circuit_breaker("c")) - assert_equal 3, @lru_hash.count + assert_equal(3, @lru_hash.count) - @lru_hash.delete('b') - assert_equal 2, @lru_hash.size - assert_equal @lru_hash.values.last, @lru_hash.get('c') - assert_equal @lru_hash.values.first, @lru_hash.get('a') + @lru_hash.delete("b") + assert_equal(2, @lru_hash.size) + assert_equal(@lru_hash.values.last, @lru_hash.get("c")) + assert_equal(@lru_hash.values.first, @lru_hash.get("a")) end def test_get_moves_the_item_at_the_top - @lru_hash.set('a', create_circuit_breaker('a')) - @lru_hash.set('b', create_circuit_breaker('b')) - @lru_hash.set('c', create_circuit_breaker('c')) + @lru_hash.set("a", create_circuit_breaker("a")) + @lru_hash.set("b", create_circuit_breaker("b")) + @lru_hash.set("c", create_circuit_breaker("c")) - assert_equal 3, @lru_hash.size - @lru_hash.get('a') # Reading the value will move the resource at the tail position - assert_equal @lru_hash.values.last, @lru_hash.get('a') - assert_equal @lru_hash.values.first, @lru_hash.get('b') + assert_equal(3, @lru_hash.size) + @lru_hash.get("a") # Reading the value will move the resource at the tail position + assert_equal(@lru_hash.values.last, @lru_hash.get("a")) + assert_equal(@lru_hash.values.first, @lru_hash.get("b")) end def test_set_cleans_resources_if_last_error_has_expired - @lru_hash.set('b', create_circuit_breaker('b', true, false, 1000)) + @lru_hash.set("b", create_circuit_breaker("b", true, false, 1000)) Timecop.travel(2000) do - @lru_hash.set('d', create_circuit_breaker('d')) - assert_equal 1, @lru_hash.size + @lru_hash.set("d", create_circuit_breaker("d")) + assert_equal(1, @lru_hash.size) end end def test_set_does_not_clean_resources_if_last_error_has_not_expired - @lru_hash.set('b', create_circuit_breaker('b', true, false, 1000)) + @lru_hash.set("b", create_circuit_breaker("b", true, false, 1000)) Timecop.travel(600) do - @lru_hash.set('d', create_circuit_breaker('d')) - assert_equal 2, @lru_hash.size + @lru_hash.set("d", create_circuit_breaker("d")) + assert_equal(2, @lru_hash.size) end end def test_set_cleans_resources_if_minimum_time_is_reached - @lru_hash.set('a', create_circuit_breaker('a', true, false, 1000)) - @lru_hash.set('b', create_circuit_breaker('b', false)) - @lru_hash.set('c', create_circuit_breaker('c', false)) + @lru_hash.set("a", create_circuit_breaker("a", true, false, 1000)) + @lru_hash.set("b", create_circuit_breaker("b", false)) + @lru_hash.set("c", create_circuit_breaker("c", false)) Timecop.travel(600) do - @lru_hash.set('d', create_circuit_breaker('d')) - assert_equal 2, @lru_hash.size + @lru_hash.set("d", create_circuit_breaker("d")) + assert_equal(2, @lru_hash.size) end end def test_set_does_not_cleans_resources_if_minimum_time_is_not_reached - @lru_hash.set('a', create_circuit_breaker('a')) - @lru_hash.set('b', create_circuit_breaker('b', false)) - @lru_hash.set('c', create_circuit_breaker('c')) + @lru_hash.set("a", create_circuit_breaker("a")) + @lru_hash.set("b", create_circuit_breaker("b", false)) + @lru_hash.set("c", create_circuit_breaker("c")) - assert_equal 3, @lru_hash.size + assert_equal(3, @lru_hash.size) end def test_keys - @lru_hash.set('a', create_circuit_breaker('a')) - assert_equal ['a'], @lru_hash.keys + @lru_hash.set("a", create_circuit_breaker("a")) + assert_equal(["a"], @lru_hash.keys) end def test_delete - @lru_hash.set('a', create_circuit_breaker('a')) - assert @lru_hash.get('a') + @lru_hash.set("a", create_circuit_breaker("a")) + assert(@lru_hash.get("a")) - @lru_hash.delete('a') - assert_nil @lru_hash.get('a') + @lru_hash.delete("a") + assert_nil(@lru_hash.get("a")) end def test_clear - @lru_hash.set('a', create_circuit_breaker('a')) + @lru_hash.set("a", create_circuit_breaker("a")) @lru_hash.clear - assert @lru_hash.empty? + assert_empty(@lru_hash) end def test_clean_instrumentation - @lru_hash.set('a', create_circuit_breaker('a', true, false, 1000)) - @lru_hash.set('b', create_circuit_breaker('b', true, false, 1000)) - @lru_hash.set('c', create_circuit_breaker('c', true, false, 1000)) + @lru_hash.set("a", create_circuit_breaker("a", true, false, 1000)) + @lru_hash.set("b", create_circuit_breaker("b", true, false, 1000)) + @lru_hash.set("c", create_circuit_breaker("c", true, false, 1000)) notified = false subscriber = Semian.subscribe do |event, resource, scope, adapter, payload| next unless event == :lru_hash_gc + notified = true - assert_equal @lru_hash, resource - assert_nil scope - assert_nil adapter - assert_equal 4, payload[:size] - assert_equal 4, payload[:examined] - assert_equal 3, payload[:cleared] - refute_nil payload[:elapsed] + assert_equal(@lru_hash, resource) + assert_nil(scope) + assert_nil(adapter) + assert_equal(4, payload[:size]) + assert_equal(4, payload[:examined]) + assert_equal(3, payload[:cleared]) + refute_nil(payload[:elapsed]) end Timecop.travel(2000) do - @lru_hash.set('d', create_circuit_breaker('d')) + @lru_hash.set("d", create_circuit_breaker("d")) end - assert notified, 'No notifications has been emitted' + assert(notified, "No notifications has been emitted") ensure Semian.unsubscribe(subscriber) end @@ -142,46 +145,46 @@ def test_monotonically_increasing notification += 1 if notification < 5 - assert_equal notification, payload[:size] - assert_equal 1, payload[:examined] + assert_equal(notification, payload[:size]) + assert_equal(1, payload[:examined]) elsif notification == 5 # At this point, the table looks like: [a, c, b, d, e] - assert_equal notification, payload[:size] - assert_equal 3, payload[:examined] + assert_equal(notification, payload[:size]) + assert_equal(3, payload[:examined]) else - assert_nil true + assert_nil(true) end end assert_monotonic = lambda do previous_timestamp = start_time @lru_hash.keys.zip(@lru_hash.values).each do |key, val| - assert val.updated_at > previous_timestamp, "Timestamp for #{key} was not monotonically increasing" + assert(val.updated_at > previous_timestamp, "Timestamp for #{key} was not monotonically increasing") end end - @lru_hash.set('a', create_circuit_breaker('a')) - @lru_hash.set('b', create_circuit_breaker('b')) - @lru_hash.set('c', create_circuit_breaker('c')) + @lru_hash.set("a", create_circuit_breaker("a")) + @lru_hash.set("b", create_circuit_breaker("b")) + @lru_hash.set("c", create_circuit_breaker("c")) # Before: [a, b, c] # After: [a, c, b] Timecop.travel(Semian.minimum_lru_time - 1) do - @lru_hash.get('b') + @lru_hash.get("b") assert_monotonic.call end # Before: [a, c, b] # After: [a, c, b, d] Timecop.travel(Semian.minimum_lru_time - 1) do - @lru_hash.set('d', create_circuit_breaker('d')) + @lru_hash.set("d", create_circuit_breaker("d")) assert_monotonic.call end # Before: [a, c, b, d] # After: [b, d, e] Timecop.travel(Semian.minimum_lru_time) do - @lru_hash.set('e', create_circuit_breaker('e')) + @lru_hash.set("e", create_circuit_breaker("e")) assert_monotonic.call end ensure @@ -190,35 +193,35 @@ def test_monotonically_increasing def test_max_size lru_hash = LRUHash.new(max_size: 3) - lru_hash.set('a', create_circuit_breaker('a')) - lru_hash.set('b', create_circuit_breaker('b')) - lru_hash.set('c', create_circuit_breaker('c')) - assert_equal 3, lru_hash.size + lru_hash.set("a", create_circuit_breaker("a")) + lru_hash.set("b", create_circuit_breaker("b")) + lru_hash.set("c", create_circuit_breaker("c")) + assert_equal(3, lru_hash.size) Timecop.travel(Semian.minimum_lru_time) do # [a, b, c] are older than the min_time, so they get garbage collected. - lru_hash.set('d', create_circuit_breaker('d')) - assert_equal 1, lru_hash.size + lru_hash.set("d", create_circuit_breaker("d")) + assert_equal(1, lru_hash.size) end end def test_max_size_overflow lru_hash = LRUHash.new(max_size: 3) - lru_hash.set('a', create_circuit_breaker('a')) - lru_hash.set('b', create_circuit_breaker('b')) - assert_equal 2, lru_hash.size + lru_hash.set("a", create_circuit_breaker("a")) + lru_hash.set("b", create_circuit_breaker("b")) + assert_equal(2, lru_hash.size) Timecop.travel(Semian.minimum_lru_time) do # [a, b] are older than the min_time, but the hash isn't full, so # there's no garbage collection. - lru_hash.set('c', create_circuit_breaker('c')) - assert_equal 3, lru_hash.size + lru_hash.set("c", create_circuit_breaker("c")) + assert_equal(3, lru_hash.size) end Timecop.travel(Semian.minimum_lru_time + 1) do # [a, b] are beyond the min_time, but [c] isn't. - lru_hash.set('d', create_circuit_breaker('d')) - assert_equal 2, lru_hash.size + lru_hash.set("d", create_circuit_breaker("d")) + assert_equal(2, lru_hash.size) end end @@ -241,6 +244,7 @@ def create_circuit_breaker(name, exceptions = true, bulkhead = false, error_time def create_bulkhead(name, bulkhead) return nil unless bulkhead + Semian::Resource.new(name, tickets: 1, quota: nil, permissions: 0660, timeout: 0) end end diff --git a/test/mysql2_test.rb b/test/mysql2_test.rb index 1f55be105..7af4440ca 100644 --- a/test/mysql2_test.rb +++ b/test/mysql2_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class TestMysql2 < Minitest::Test ERROR_TIMEOUT = 5 @@ -18,30 +20,31 @@ def setup end def test_semian_identifier - assert_equal :mysql_foo, FakeMysql.new(semian: {name: 'foo'}).semian_identifier - assert_equal :'mysql_localhost:3306', FakeMysql.new.semian_identifier - assert_equal :'mysql_127.0.0.1:3306', FakeMysql.new(host: '127.0.0.1').semian_identifier - assert_equal :'mysql_example.com:42', FakeMysql.new(host: 'example.com', port: 42).semian_identifier + assert_equal(:mysql_foo, FakeMysql.new(semian: { name: "foo" }).semian_identifier) + assert_equal(:"mysql_localhost:3306", FakeMysql.new.semian_identifier) + assert_equal(:"mysql_127.0.0.1:3306", FakeMysql.new(host: "127.0.0.1").semian_identifier) + assert_equal(:"mysql_example.com:42", FakeMysql.new(host: "example.com", port: 42).semian_identifier) end def test_semian_can_be_disabled resource = Mysql2::Client.new( - host: SemianConfig['toxiproxy_upstream_host'], - port: SemianConfig['mysql_toxiproxy_port'], - semian: false).semian_resource + host: SemianConfig["toxiproxy_upstream_host"], + port: SemianConfig["mysql_toxiproxy_port"], + semian: false + ).semian_resource - assert_instance_of Semian::UnprotectedResource, resource + assert_instance_of(Semian::UnprotectedResource, resource) end def test_connection_errors_open_the_circuit @proxy.downstream(:latency, latency: 2200).apply do ERROR_THRESHOLD.times do - assert_raises ::Mysql2::Error do + assert_raises(::Mysql2::Error) do connect_to_mysql! end end - assert_raises ::Mysql2::CircuitOpenError do + assert_raises(::Mysql2::CircuitOpenError) do connect_to_mysql! end end @@ -50,8 +53,8 @@ def test_connection_errors_open_the_circuit def test_query_errors_does_not_open_the_circuit client = connect_to_mysql! (ERROR_THRESHOLD * 2).times do - assert_raises ::Mysql2::Error do - client.query('ERROR!') + assert_raises(::Mysql2::Error) do + client.query("ERROR!") end end end @@ -59,16 +62,17 @@ def test_query_errors_does_not_open_the_circuit def test_read_timeout_error_open_the_circuit client = connect_to_mysql! - (ERROR_THRESHOLD).times do - assert_raises Mysql2::Error do + ERROR_THRESHOLD.times do + assert_raises(Mysql2::Error) do # Should raise an exception like - - # Mysql2::Error Exception: [mysql_testing] Timeout waiting for a response from the last query. (waited 2 seconds) - client.query('SELECT sleep(5)') + # Mysql2::Error Exception: [mysql_testing] Timeout waiting for a response + # from the last query. (waited 2 seconds) + client.query("SELECT sleep(5)") end end - assert_raises Mysql2::CircuitOpenError do - client.query('SELECT sleep(5)') + assert_raises(Mysql2::CircuitOpenError) do + client.query("SELECT sleep(5)") end # After Mysql2::CircuitOpenError check regular queries are working fine. @@ -78,7 +82,7 @@ def test_read_timeout_error_open_the_circuit result = client.query("SELECT #{query_string};") end - assert_equal 2, result.first[query_string] + assert_equal(2, result.first[query_string]) end def test_connect_instrumentation @@ -87,14 +91,14 @@ def test_connect_instrumentation next unless event == :success notified = true - assert_equal Semian[:mysql_testing], resource - assert_equal :connection, scope - assert_equal :mysql, adapter + assert_equal(Semian[:mysql_testing], resource) + assert_equal(:connection, scope) + assert_equal(:mysql, adapter) end connect_to_mysql! - assert notified, 'No notifications has been emitted' + assert(notified, "No notifications has been emitted") ensure Semian.unsubscribe(subscriber) end @@ -103,37 +107,37 @@ def test_resource_acquisition_for_connect connect_to_mysql! Semian[:mysql_testing].acquire do - error = assert_raises Mysql2::ResourceBusyError do + error = assert_raises(Mysql2::ResourceBusyError) do connect_to_mysql! end - assert_equal :mysql_testing, error.semian_identifier + assert_equal(:mysql_testing, error.semian_identifier) end end def test_network_errors_are_tagged_with_the_resource_identifier client = connect_to_mysql! @proxy.down do - error = assert_raises ::Mysql2::Error do - client.query('SELECT 1 + 1;') + error = assert_raises(::Mysql2::Error) do + client.query("SELECT 1 + 1;") end - assert_equal client.semian_identifier, error.semian_identifier + assert_equal(client.semian_identifier, error.semian_identifier) end end def test_other_mysql_errors_are_not_tagged_with_the_resource_identifier client = connect_to_mysql! - error = assert_raises Mysql2::Error do - client.query('SYNTAX ERROR!') + error = assert_raises(Mysql2::Error) do + client.query("SYNTAX ERROR!") end - assert_nil error.semian_identifier + assert_nil(error.semian_identifier) end def test_resource_timeout_on_connect @proxy.downstream(:latency, latency: 500).apply do background { connect_to_mysql! } - assert_raises Mysql2::ResourceBusyError do + assert_raises(Mysql2::ResourceBusyError) do connect_to_mysql! end end @@ -144,7 +148,7 @@ def test_circuit_breaker_on_connect background { connect_to_mysql! } ERROR_THRESHOLD.times do - assert_raises Mysql2::ResourceBusyError do + assert_raises(Mysql2::ResourceBusyError) do connect_to_mysql! end end @@ -152,7 +156,7 @@ def test_circuit_breaker_on_connect yield_to_background - assert_raises Mysql2::CircuitOpenError do + assert_raises(Mysql2::CircuitOpenError) do connect_to_mysql! end @@ -167,15 +171,15 @@ def test_query_instrumentation notified = false subscriber = Semian.subscribe do |event, resource, scope, adapter| notified = true - assert_equal :success, event - assert_equal Semian[:mysql_testing], resource - assert_equal :query, scope - assert_equal :mysql, adapter + assert_equal(:success, event) + assert_equal(Semian[:mysql_testing], resource) + assert_equal(:query, scope) + assert_equal(:mysql, adapter) end - client.query('SELECT 1 + 1;') + client.query("SELECT 1 + 1;") - assert notified, 'No notifications has been emitted' + assert(notified, "No notifications has been emitted") ensure Semian.unsubscribe(subscriber) end @@ -184,8 +188,8 @@ def test_resource_acquisition_for_query client = connect_to_mysql! Semian[:mysql_testing].acquire do - assert_raises Mysql2::ResourceBusyError do - client.query('SELECT 1 + 1;') + assert_raises(Mysql2::ResourceBusyError) do + client.query("SELECT 1 + 1;") end end end @@ -193,63 +197,63 @@ def test_resource_acquisition_for_query def test_semian_allows_rollback client = connect_to_mysql! - client.query('START TRANSACTION;') + client.query("START TRANSACTION;") Semian[:mysql_testing].acquire do - client.query('ROLLBACK;') + client.query("ROLLBACK;") end end def test_semian_allows_rollback_with_marginalia client = connect_to_mysql! - client.query('START TRANSACTION;') + client.query("START TRANSACTION;") Semian[:mysql_testing].acquire do - client.query('/*foo:bar*/ ROLLBACK;') + client.query("/*foo:bar*/ ROLLBACK;") end end def test_semian_allows_commit client = connect_to_mysql! - client.query('START TRANSACTION;') + client.query("START TRANSACTION;") Semian[:mysql_testing].acquire do - client.query('COMMIT;') + client.query("COMMIT;") end end def test_query_whitelisted_returns_false_for_binary_sql - binary_query = File.read(File.expand_path('../fixtures/binary.sql', __FILE__)) + binary_query = File.read(File.expand_path("../fixtures/binary.sql", __FILE__)) client = connect_to_mysql! - refute client.send(:query_whitelisted?, binary_query) + refute(client.send(:query_whitelisted?, binary_query)) end def test_semian_allows_rollback_to_safepoint client = connect_to_mysql! - client.query('START TRANSACTION;') - client.query('SAVEPOINT foobar;') + client.query("START TRANSACTION;") + client.query("SAVEPOINT foobar;") Semian[:mysql_testing].acquire do - client.query('ROLLBACK TO foobar;') + client.query("ROLLBACK TO foobar;") end - client.query('ROLLBACK;') + client.query("ROLLBACK;") end def test_semian_allows_release_savepoint client = connect_to_mysql! - client.query('START TRANSACTION;') - client.query('SAVEPOINT foobar;') + client.query("START TRANSACTION;") + client.query("SAVEPOINT foobar;") Semian[:mysql_testing].acquire do - client.query('RELEASE SAVEPOINT foobar;') + client.query("RELEASE SAVEPOINT foobar;") end - client.query('ROLLBACK;') + client.query("ROLLBACK;") end def test_resource_timeout_on_query @@ -257,10 +261,10 @@ def test_resource_timeout_on_query client2 = connect_to_mysql! @proxy.downstream(:latency, latency: 500).apply do - background { client2.query('SELECT 1 + 1;') } + background { client2.query("SELECT 1 + 1;") } - assert_raises Mysql2::ResourceBusyError do - client.query('SELECT 1 + 1;') + assert_raises(Mysql2::ResourceBusyError) do + client.query("SELECT 1 + 1;") end end end @@ -270,33 +274,33 @@ def test_circuit_breaker_on_query client2 = connect_to_mysql! @proxy.downstream(:latency, latency: 2200).apply do - background { client2.query('SELECT 1 + 1;') } + background { client2.query("SELECT 1 + 1;") } ERROR_THRESHOLD.times do - assert_raises Mysql2::ResourceBusyError do - client.query('SELECT 1 + 1;') + assert_raises(Mysql2::ResourceBusyError) do + client.query("SELECT 1 + 1;") end end end yield_to_background - assert_raises Mysql2::CircuitOpenError do - client.query('SELECT 1 + 1;') + assert_raises(Mysql2::CircuitOpenError) do + client.query("SELECT 1 + 1;") end Timecop.travel(ERROR_TIMEOUT + 1) do - assert_equal 2, client.query('SELECT 1 + 1 as sum;').to_a.first['sum'] + assert_equal(2, client.query("SELECT 1 + 1 as sum;").to_a.first["sum"]) end end def test_unconfigured client = Mysql2::Client.new( - host: SemianConfig['toxiproxy_upstream_host'], - port: SemianConfig['mysql_toxiproxy_port'], + host: SemianConfig["toxiproxy_upstream_host"], + port: SemianConfig["mysql_toxiproxy_port"], ) - assert_equal 2, client.query('SELECT 1 + 1 as sum;').to_a.first['sum'] + assert_equal(2, client.query("SELECT 1 + 1 as sum;").to_a.first["sum"]) end def test_pings_are_circuit_broken @@ -313,10 +317,10 @@ def client.raw_ping client.ping end - assert_equal false, client.ping + refute(client.ping) end - assert_equal ERROR_THRESHOLD, client.instance_variable_get(:@real_pings) + assert_equal(ERROR_THRESHOLD, client.instance_variable_get(:@real_pings)) end def test_changes_timeout_when_half_open_and_configured @@ -324,37 +328,37 @@ def test_changes_timeout_when_half_open_and_configured @proxy.downstream(:latency, latency: 3000).apply do (ERROR_THRESHOLD * 2).times do - assert_raises Mysql2::Error do - client.query('SELECT 1 + 1;') + assert_raises(Mysql2::Error) do + client.query("SELECT 1 + 1;") end end end - assert_raises Mysql2::CircuitOpenError do - client.query('SELECT 1 + 1;') + assert_raises(Mysql2::CircuitOpenError) do + client.query("SELECT 1 + 1;") end Timecop.travel(ERROR_TIMEOUT + 1) do @proxy.downstream(:latency, latency: 1500).apply do - assert_raises Mysql2::Error do - client.query('SELECT 1 + 1;') + assert_raises(Mysql2::Error) do + client.query("SELECT 1 + 1;") end end end Timecop.travel(ERROR_TIMEOUT * 2 + 1) do - client.query('SELECT 1 + 1;') - client.query('SELECT 1 + 1;') + client.query("SELECT 1 + 1;") + client.query("SELECT 1 + 1;") # Timeout has reset to the normal 2 seconds now that Circuit is closed @proxy.downstream(:latency, latency: 1500).apply do - client.query('SELECT 1 + 1;') + client.query("SELECT 1 + 1;") end end - assert_equal 2, client.query_options[:connect_timeout] - assert_equal 2, client.query_options[:read_timeout] - assert_equal 2, client.query_options[:write_timeout] + assert_equal(2, client.query_options[:connect_timeout]) + assert_equal(2, client.query_options[:read_timeout]) + assert_equal(2, client.query_options[:write_timeout]) end def test_circuit_open_errors_do_not_trigger_the_circuit_breaker @@ -364,7 +368,7 @@ def test_circuit_open_errors_do_not_trigger_the_circuit_breaker assert_raises(Mysql2::Error) do connect_to_mysql! end - assert_equal mysql_connection_error, Semian[:mysql_testing].circuit_breaker.last_error.class + assert_equal(mysql_connection_error, Semian[:mysql_testing].circuit_breaker.last_error.class) end end end @@ -377,8 +381,8 @@ def connect_to_mysql!(semian_options = {}) read_timeout: 2, write_timeout: 2, reconnect: true, - host: SemianConfig['toxiproxy_upstream_host'], - port: SemianConfig['mysql_toxiproxy_port'], + host: SemianConfig["toxiproxy_upstream_host"], + port: SemianConfig["mysql_toxiproxy_port"], semian: SEMIAN_OPTIONS.merge(semian_options), ) end diff --git a/test/net_http_test.rb b/test/net_http_test.rb index 9e64baf2f..493980f1a 100644 --- a/test/net_http_test.rb +++ b/test/net_http_test.rb @@ -1,5 +1,7 @@ -require 'test_helper' -require 'semian/net_http' +# frozen_string_literal: true + +require "test_helper" +require "semian/net_http" class TestNetHTTP < Minitest::Test DEFAULT_SEMIAN_OPTIONS = { @@ -9,22 +11,27 @@ class TestNetHTTP < Minitest::Test error_timeout: 10, }.freeze DEFAULT_SEMIAN_CONFIGURATION = proc do |host, port| - next nil if host == SemianConfig['toxiproxy_upstream_host'] && port == SemianConfig['toxiproxy_upstream_port'] # disable if toxiproxy + if host == SemianConfig["toxiproxy_upstream_host"] && \ + port == SemianConfig["toxiproxy_upstream_port"] # disable if toxiproxy + next nil + end + DEFAULT_SEMIAN_OPTIONS.merge(name: "#{host}_#{port}") end def test_semian_identifier with_server do with_semian_configuration do - Net::HTTP.start(SemianConfig['toxiproxy_upstream_host'], SemianConfig['http_toxiproxy_port']) do |http| - assert_equal "nethttp_#{SemianConfig['toxiproxy_upstream_host']}_#{SemianConfig['http_toxiproxy_port']}", http.semian_identifier + Net::HTTP.start(SemianConfig["toxiproxy_upstream_host"], SemianConfig["http_toxiproxy_port"]) do |http| + assert_equal("nethttp_#{SemianConfig["toxiproxy_upstream_host"]}_#{SemianConfig["http_toxiproxy_port"]}", + http.semian_identifier) end end end end def test_changes_timeout_when_half_open_and_configured - http = Net::HTTP.new(SemianConfig['toxiproxy_upstream_host'], SemianConfig['http_toxiproxy_port']) + http = Net::HTTP.new(SemianConfig["toxiproxy_upstream_host"], SemianConfig["http_toxiproxy_port"]) expected_read_timeout = http.read_timeout expected_open_timeout = http.open_timeout options = proc do |host, port| @@ -41,22 +48,22 @@ def test_changes_timeout_when_half_open_and_configured with_semian_configuration(options) do with_server do - Toxiproxy['semian_test_net_http'].downstream(:latency, latency: 2000).apply do - http.get('/200') + Toxiproxy["semian_test_net_http"].downstream(:latency, latency: 2000).apply do + http.get("/200") end half_open_cicuit! - Toxiproxy['semian_test_net_http'].downstream(:latency, latency: 2000).apply do - assert_raises Net::ReadTimeout do - http.get('/200') + Toxiproxy["semian_test_net_http"].downstream(:latency, latency: 2000).apply do + assert_raises(Net::ReadTimeout) do + http.get("/200") end end end end - assert_equal expected_read_timeout, http.read_timeout - assert_equal expected_open_timeout, http.open_timeout + assert_equal(expected_read_timeout, http.read_timeout) + assert_equal(expected_open_timeout, http.open_timeout) end def test_trigger_open @@ -64,11 +71,11 @@ def test_trigger_open with_server do open_circuit! - uri = URI("http://#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}/200") - exception = assert_raises Net::CircuitOpenError do + uri = URI("http://#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["http_toxiproxy_port"]}/200") + exception = assert_raises(Net::CircuitOpenError) do Net::HTTP.get(uri) end - assert_equal "Net::ReadTimeout", exception.cause.to_s + assert_equal("Net::ReadTimeout", exception.cause.to_s) end end end @@ -94,12 +101,12 @@ def test_bulkheads_tickets_are_working end with_semian_configuration(options) do with_server do - http_1 = Net::HTTP.new(SemianConfig['toxiproxy_upstream_host'], SemianConfig['http_toxiproxy_port']) + http_1 = Net::HTTP.new(SemianConfig["toxiproxy_upstream_host"], SemianConfig["http_toxiproxy_port"]) http_1.semian_resource.acquire do - http_2 = Net::HTTP.new(SemianConfig['toxiproxy_upstream_host'], SemianConfig['http_toxiproxy_port']) + http_2 = Net::HTTP.new(SemianConfig["toxiproxy_upstream_host"], SemianConfig["http_toxiproxy_port"]) http_2.semian_resource.acquire do - assert_raises Net::ResourceBusyError do - Net::HTTP.get(URI("http://#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}/")) + assert_raises(Net::ResourceBusyError) do + Net::HTTP.get(URI("http://#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["http_toxiproxy_port"]}/")) end end end @@ -111,8 +118,8 @@ def test_get_is_protected with_semian_configuration do with_server do open_circuit! - assert_raises Net::CircuitOpenError do - Net::HTTP.get(URI("http://#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}/200")) + assert_raises(Net::CircuitOpenError) do + Net::HTTP.get(URI("http://#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["http_toxiproxy_port"]}/200")) end end end @@ -123,8 +130,8 @@ def test_instance_get_is_protected with_server do open_circuit! - assert_raises Net::CircuitOpenError do - http = Net::HTTP.new(SemianConfig['toxiproxy_upstream_host'], SemianConfig['http_toxiproxy_port']) + assert_raises(Net::CircuitOpenError) do + http = Net::HTTP.new(SemianConfig["toxiproxy_upstream_host"], SemianConfig["http_toxiproxy_port"]) http.get("/") end end @@ -136,8 +143,8 @@ def test_get_response_is_protected with_server do open_circuit! - assert_raises Net::CircuitOpenError do - uri = URI("http://#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}/200") + assert_raises(Net::CircuitOpenError) do + uri = URI("http://#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["http_toxiproxy_port"]}/200") Net::HTTP.get_response(uri) end end @@ -149,9 +156,9 @@ def test_post_form_is_protected with_server do open_circuit! - assert_raises Net::CircuitOpenError do - uri = URI("http://#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}/200") - Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50') + assert_raises(Net::CircuitOpenError) do + uri = URI("http://#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["http_toxiproxy_port"]}/200") + Net::HTTP.post_form(uri, "q" => "ruby", "max" => "50") end end end @@ -162,8 +169,8 @@ def test_http_start_method_is_protected with_server do open_circuit! - uri = URI("http://#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}/200") - assert_raises Net::CircuitOpenError do + uri = URI("http://#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["http_toxiproxy_port"]}/200") + assert_raises(Net::CircuitOpenError) do Net::HTTP.start(uri.host, uri.port) {} end close_circuit! @@ -174,12 +181,12 @@ def test_http_start_method_is_protected def test_http_action_request_inside_start_methods_are_protected with_semian_configuration do with_server do - uri = URI("http://#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}/200") + uri = URI("http://#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["http_toxiproxy_port"]}/200") Net::HTTP.start(uri.host, uri.port) do |http| open_circuit! get_subclasses(Net::HTTPRequest).each do |action| assert_raises(Net::CircuitOpenError, "#{action.name} did not raise a Net::CircuitOpenError") do - request = action.new uri + request = action.new(uri) http.request(request) end end @@ -190,8 +197,8 @@ def test_http_action_request_inside_start_methods_are_protected def test_custom_raw_semian_options_work_with_lookup with_server do - toxiproxy_upstream_host = SemianConfig['toxiproxy_upstream_host'] - http_toxiproxy_port = SemianConfig['http_toxiproxy_port'] + toxiproxy_upstream_host = SemianConfig["toxiproxy_upstream_host"] + http_toxiproxy_port = SemianConfig["http_toxiproxy_port"] semian_config = {} semian_config["development"] = {} @@ -205,8 +212,8 @@ def test_custom_raw_semian_options_work_with_lookup with_semian_configuration(semian_configuration_proc) do Net::HTTP.start(toxiproxy_upstream_host, http_toxiproxy_port) do |http| - assert_equal semian_config["development"][http.semian_identifier], - http.raw_semian_options.dup.tap { |o| o.delete(:name) } + assert_equal(semian_config["development"][http.semian_identifier], + http.raw_semian_options.dup.tap { |o| o.delete(:name) }) end end end @@ -235,12 +242,12 @@ def test_custom_raw_semian_options_work_with_default_fallback semian_identifier = "nethttp_default" unless semian_config[sample_env].key?(semian_identifier) semian_config[sample_env][semian_identifier].merge(name: "default") end - Semian["nethttp_default"].reset if Semian["nethttp_default"] + Semian["nethttp_default"]&.reset Semian.destroy("nethttp_default") with_semian_configuration(semian_configuration_proc) do - Net::HTTP.start(SemianConfig['http_host'], SemianConfig['http_port_service_a']) do |http| + Net::HTTP.start(SemianConfig["http_host"], SemianConfig["http_port_service_a"]) do |http| expected_config = semian_config["development"]["nethttp_default"].dup - assert_equal expected_config, http.raw_semian_options.dup.tap { |o| o.delete(:name) } + assert_equal(expected_config, http.raw_semian_options.dup.tap { |o| o.delete(:name) }) end end end @@ -250,16 +257,17 @@ def test_custom_raw_semian_options_can_disable_using_nil with_server do semian_configuration_proc = proc { nil } with_semian_configuration(semian_configuration_proc) do - http = Net::HTTP.new(SemianConfig['toxiproxy_upstream_host'], SemianConfig['http_toxiproxy_port']) - assert_equal true, http.disabled? + http = Net::HTTP.new(SemianConfig["toxiproxy_upstream_host"], SemianConfig["http_toxiproxy_port"]) + assert_predicate(http, :disabled?) end end end + def test_disable_semian_should_be_false with_server do with_semian_configuration do - http = Net::HTTP.new(SemianConfig['toxiproxy_upstream_host'], SemianConfig['http_toxiproxy_port']) - assert_equal false, http.disabled? + http = Net::HTTP.new(SemianConfig["toxiproxy_upstream_host"], SemianConfig["http_toxiproxy_port"]) + refute_predicate(http, :disabled?) end end end @@ -267,8 +275,9 @@ def test_disable_semian_should_be_false def test_disable_semian_for_all_http_requests_with_flag with_server do with_semian_configuration do - http = Net::HTTP.new(SemianConfig['toxiproxy_upstream_host'], SemianConfig['http_toxiproxy_port'], semian: false) - assert_equal true, http.disabled? + http = Net::HTTP.new(SemianConfig["toxiproxy_upstream_host"], SemianConfig["http_toxiproxy_port"], + semian: false) + assert_predicate(http, :disabled?) end end end @@ -280,20 +289,25 @@ def test_use_custom_configuration_to_combine_endpoints_into_one_resource sample_env = "development" semian_configuration_proc = proc do |host, port| - next nil if host == SemianConfig['toxiproxy_upstream_host'] && port == SemianConfig['toxiproxy_upstream_port'] # disable if toxiproxy + if host == SemianConfig["toxiproxy_upstream_host"] && \ + port == SemianConfig["toxiproxy_upstream_port"] # disable if toxiproxy + next nil + end + semian_identifier = "nethttp_default" semian_config[sample_env][semian_identifier].merge(name: "default") end with_semian_configuration(semian_configuration_proc) do - Semian["nethttp_default"].reset if Semian["nethttp_default"] + Semian["nethttp_default"]&.reset Semian.destroy("nethttp_default") with_server do open_circuit! end - with_server(ports: [SemianConfig['http_port_service_a'], SemianConfig['http_port_service_b']], reset_semian_state: false) do - assert_raises Net::CircuitOpenError do - Net::HTTP.get(URI("http://#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}/200")) + with_server(ports: [SemianConfig["http_port_service_a"], SemianConfig["http_port_service_b"]], + reset_semian_state: false) do + assert_raises(Net::CircuitOpenError) do + Net::HTTP.get(URI("http://#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["http_toxiproxy_port"]}/200")) end end end @@ -301,8 +315,8 @@ def test_use_custom_configuration_to_combine_endpoints_into_one_resource def test_custom_raw_semian_options_can_disable_with_invalid_key with_server do - toxiproxy_upstream_host = SemianConfig['toxiproxy_upstream_host'] - http_toxiproxy_port = SemianConfig['http_toxiproxy_port'] + toxiproxy_upstream_host = SemianConfig["toxiproxy_upstream_host"] + http_toxiproxy_port = SemianConfig["http_toxiproxy_port"] semian_config = {} semian_config["development"] = {} @@ -315,10 +329,10 @@ def test_custom_raw_semian_options_can_disable_with_invalid_key end with_semian_configuration(semian_configuration_proc) do http = Net::HTTP.new(toxiproxy_upstream_host, http_toxiproxy_port) - assert_equal false, http.disabled? + refute_predicate(http, :disabled?) http = Net::HTTP.new(toxiproxy_upstream_host, http_toxiproxy_port + 100) - assert_equal true, http.disabled? + assert_predicate(http, :disabled?) end end end @@ -337,14 +351,16 @@ def test_adding_custom_errors_do_trip_circuit with_semian_configuration do with_custom_errors([::OpenSSL::SSL::SSLError]) do with_server do - http = Net::HTTP.new(SemianConfig['toxiproxy_upstream_host'], SemianConfig['http_toxiproxy_port']) + http = Net::HTTP.new(SemianConfig["toxiproxy_upstream_host"], SemianConfig["http_toxiproxy_port"]) http.use_ssl = true http.raw_semian_options[:error_threshold].times do - assert_raises ::OpenSSL::SSL::SSLError, "for HTTP request to #{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}" do + assert_msg = "for HTTP request to #{SemianConfig["toxiproxy_upstream_host"]}:" \ + "#{SemianConfig["http_toxiproxy_port"]}" + assert_raises(::OpenSSL::SSL::SSLError, assert_msg) do http.get("/200") end end - assert_raises Net::CircuitOpenError do + assert_raises(Net::CircuitOpenError) do http.get("/200") end end @@ -366,11 +382,11 @@ def test_5xxs_trip_circuit_when_fatal_server_flag_enabled with_semian_configuration(options) do with_server do - http = Net::HTTP.new(SemianConfig['http_host'], SemianConfig['http_port_service_a']) + http = Net::HTTP.new(SemianConfig["http_host"], SemianConfig["http_port_service_a"]) http.raw_semian_options[:error_threshold].times do http.get("/500") end - assert_raises Net::CircuitOpenError do + assert_raises(Net::CircuitOpenError) do http.get("/500") end end @@ -381,7 +397,7 @@ def test_5xxs_dont_raise_exceptions_unless_fatal_server_flag_enabled skip if ENV["SKIP_FLAKY_TESTS"] with_semian_configuration do with_server do - http = Net::HTTP.new(SemianConfig['http_host'], SemianConfig['http_port_service_a']) + http = Net::HTTP.new(SemianConfig["http_host"], SemianConfig["http_port_service_a"]) http.raw_semian_options[:error_threshold].times do http.get("/500") end @@ -392,38 +408,38 @@ def test_5xxs_dont_raise_exceptions_unless_fatal_server_flag_enabled def test_multiple_different_endpoints_and_ports_are_tracked_differently with_semian_configuration do - ports = [SemianConfig['http_port_service_a'], SemianConfig['http_port_service_b']] + ports = [SemianConfig["http_port_service_a"], SemianConfig["http_port_service_b"]] ports.each do |port| reset_semian_resource(port: port.to_i) end with_server(ports: ports, reset_semian_state: false) do |host, port| - with_toxic(hostname: host, upstream_port: SemianConfig['http_port_service_a'], toxic_port: port + 1) do |name| + with_toxic(hostname: host, upstream_port: SemianConfig["http_port_service_a"], toxic_port: port + 1) do |name| Net::HTTP.get(URI("http://#{host}:#{port + 1}/")) open_circuit!(hostname: host, toxic_port: port + 1, toxic_name: name) - assert_raises Net::CircuitOpenError do + assert_raises(Net::CircuitOpenError) do Net::HTTP.get(URI("http://#{host}:#{port + 1}/")) end end end - with_server(ports: [SemianConfig['http_port_service_a']], reset_semian_state: false) do + with_server(ports: [SemianConfig["http_port_service_a"]], reset_semian_state: false) do # different endpoint, should not raise errors even though localhost == 127.0.0.1 - Net::HTTP.get(URI("http://#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}/")) + Net::HTTP.get(URI("http://#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["http_toxiproxy_port"]}/")) end end end def test_persistent_state_after_server_restart with_semian_configuration do - with_server(ports: [SemianConfig['http_port_service_b']]) do |_, port| - with_toxic(hostname: SemianConfig['http_host'], upstream_port: port, toxic_port: port + 1) do |name| - open_circuit!(hostname: SemianConfig['toxiproxy_upstream_host'], toxic_port: port + 1, toxic_name: name) + with_server(ports: [SemianConfig["http_port_service_b"]]) do |_, port| + with_toxic(hostname: SemianConfig["http_host"], upstream_port: port, toxic_port: port + 1) do |name| + open_circuit!(hostname: SemianConfig["toxiproxy_upstream_host"], toxic_port: port + 1, toxic_name: name) end end - with_server(ports: [SemianConfig['http_port_service_b']], reset_semian_state: false) do |_, port| - with_toxic(hostname: SemianConfig['http_host'], upstream_port: port, toxic_port: port + 1) do |_| - assert_raises Net::CircuitOpenError do - Net::HTTP.get(URI("http://#{SemianConfig['toxiproxy_upstream_host']}:#{port + 1}/200")) + with_server(ports: [SemianConfig["http_port_service_b"]], reset_semian_state: false) do |_, port| + with_toxic(hostname: SemianConfig["http_host"], upstream_port: port, toxic_port: port + 1) do |_| + assert_raises(Net::CircuitOpenError) do + Net::HTTP.get(URI("http://#{SemianConfig["toxiproxy_upstream_host"]}:#{port + 1}/200")) end end end @@ -480,8 +496,8 @@ def get_subclasses(klass) end def open_circuit!(hostname: nil, toxic_port: nil, toxic_name: "semian_test_net_http") - hostname ||= SemianConfig['toxiproxy_upstream_host'] - toxic_port ||= SemianConfig['http_toxiproxy_port'] + hostname ||= SemianConfig["toxiproxy_upstream_host"] + toxic_port ||= SemianConfig["http_toxiproxy_port"] Net::HTTP.start(hostname, toxic_port) do |http| http.read_timeout = 0.1 @@ -490,7 +506,7 @@ def open_circuit!(hostname: nil, toxic_port: nil, toxic_name: "semian_test_net_h # Cause error error_threshold times so circuit opens Toxiproxy[toxic_name].downstream(:latency, latency: 500).apply do request = Net::HTTP::Get.new(uri) - assert_raises Net::ReadTimeout, "for HTTP request to #{uri}" do + assert_raises(Net::ReadTimeout, "for HTTP request to #{uri}") do http.request(request) end end @@ -498,7 +514,7 @@ def open_circuit!(hostname: nil, toxic_port: nil, toxic_name: "semian_test_net_h end end - def close_circuit!(hostname: SemianConfig['toxiproxy_upstream_host'], toxic_port: SemianConfig['http_toxiproxy_port']) + def close_circuit!(hostname: SemianConfig["toxiproxy_upstream_host"], toxic_port: SemianConfig["http_toxiproxy_port"]) http = Net::HTTP.new(hostname, toxic_port) Timecop.travel(http.raw_semian_options[:error_timeout]) # Cause successes success_threshold times so circuit closes @@ -508,7 +524,7 @@ def close_circuit!(hostname: SemianConfig['toxiproxy_upstream_host'], toxic_port end end - def with_server(ports: [SemianConfig['http_port_service_a']], reset_semian_state: true) + def with_server(ports: [SemianConfig["http_port_service_a"]], reset_semian_state: true) ports.each do |port| reset_semian_resource(port: port) if reset_semian_state @proxy = Toxiproxy[:semian_test_net_http] @@ -516,21 +532,25 @@ def with_server(ports: [SemianConfig['http_port_service_a']], reset_semian_state end end - def reset_semian_resource(hostname: SemianConfig['toxiproxy_upstream_host'], port:) - Semian["nethttp_#{hostname}_#{port}"].reset if Semian["nethttp_#{hostname}_#{port}"] - Semian["nethttp_#{hostname}_#{port.to_i + 1}"].reset if Semian["nethttp_#{hostname}_#{port.to_i + 1}"] + def reset_semian_resource(hostname: SemianConfig["toxiproxy_upstream_host"], port:) + Semian["nethttp_#{hostname}_#{port}"]&.reset + Semian["nethttp_#{hostname}_#{port.to_i + 1}"]&.reset Semian.destroy("nethttp_#{hostname}_#{port}") Semian.destroy("nethttp_#{hostname}_#{port.to_i + 1}") end - def with_toxic(hostname: SemianConfig['http_host'], upstream_port: SemianConfig['http_port_service_a'], toxic_port: upstream_port + 1) + def with_toxic( + hostname: SemianConfig["http_host"], + upstream_port: SemianConfig["http_port_service_a"], + toxic_port: upstream_port + 1 + ) old_proxy = @proxy name = "semian_test_net_http_#{hostname}_#{upstream_port}<-#{toxic_port}" Toxiproxy.populate([ { name: name, upstream: "#{hostname}:#{upstream_port}", - listen: "#{SemianConfig['toxiproxy_upstream_host']}:#{toxic_port}", + listen: "#{SemianConfig["toxiproxy_upstream_host"]}:#{toxic_port}", }, ]) @proxy = Toxiproxy[name] diff --git a/test/protected_resource_test.rb b/test/protected_resource_test.rb index c86f32672..2cbb2e5df 100644 --- a/test/protected_resource_test.rb +++ b/test/protected_resource_test.rb @@ -1,5 +1,7 @@ -require 'test_helper' -require 'securerandom' +# frozen_string_literal: true + +require "test_helper" +require "securerandom" class TestProtectedResource < Minitest::Test include CircuitBreakerHelper @@ -32,9 +34,9 @@ def test_acquire_without_bulkhead block_called = false @resource = Semian[:testing] @resource.acquire { block_called = true } - assert_equal true, block_called - assert_instance_of Semian::CircuitBreaker, @resource.circuit_breaker - assert_nil @resource.bulkhead + assert(block_called) + assert_instance_of(Semian::CircuitBreaker, @resource.circuit_breaker) + assert_nil(@resource.bulkhead) end end @@ -52,12 +54,12 @@ def test_acquire_bulkhead_without_circuit_breaker @resource = Semian[:testing] @resource.acquire do acquired = true - assert_equal 1, @resource.count - assert_equal 2, @resource.tickets + assert_equal(1, @resource.count) + assert_equal(2, @resource.tickets) end - assert acquired - assert_nil @resource.circuit_breaker + assert(acquired) + assert_nil(@resource.circuit_breaker) end def test_acquire_bulkhead_with_circuit_breaker @@ -75,17 +77,17 @@ def test_acquire_bulkhead_with_circuit_breaker @resource = Semian[:testing] @resource.acquire do acquired = true - assert_equal 1, @resource.count - assert_equal 2, @resource.tickets + assert_equal(1, @resource.count) + assert_equal(2, @resource.tickets) half_open_cicuit!(@resource) assert_circuit_closed(@resource) end - assert acquired + assert(acquired) end def test_register_without_any_resource_fails - assert_raises ArgumentError do + assert_raises(ArgumentError) do Semian.register( :testing, circuit_breaker: false, @@ -110,8 +112,8 @@ def test_responds_to_name_when_bulkhead_or_circuit_breaker_disabled circuit_breaker: false, ) - assert_equal :no_bulkhead, Semian[:no_bulkhead].name - assert_equal :no_circuit_breaker, Semian[:no_circuit_breaker].name + assert_equal(:no_bulkhead, Semian[:no_bulkhead].name) + assert_equal(:no_circuit_breaker, Semian[:no_circuit_breaker].name) end def test_gracefully_fails_when_unable_to_decrement_ticket_count @@ -148,8 +150,8 @@ def test_gracefully_fails_when_unable_to_decrement_ticket_count Semian.register(name, tickets: 1, **options) ensure - workers.each do |pid| + workers&.each do |pid| Process.kill("INT", pid) - end if workers + end end end diff --git a/test/redis_client_test.rb b/test/redis_client_test.rb index d9f056de2..5b3149b0e 100644 --- a/test/redis_client_test.rb +++ b/test/redis_client_test.rb @@ -1,5 +1,7 @@ -require 'test_helper' -require 'benchmark' +# frozen_string_literal: true + +require "test_helper" +require "benchmark" module RedisClientTests REDIS_TIMEOUT = 0.5 @@ -23,26 +25,28 @@ def setup end def test_semian_identifier - assert_equal :redis_foo, new_config(semian: {name: 'foo'}).semian_identifier - assert_equal :"redis_#{SemianConfig['toxiproxy_upstream_host']}:16379/1", new_config(semian: {name: nil}).semian_identifier - assert_equal :'redis_example.com:42/1', new_config(host: 'example.com', port: 42, semian: {name: nil}).semian_identifier - - config = new_config(semian: {name: 'foo'}) - assert_equal :redis_foo, config.new_client.semian_identifier - assert_equal :redis_foo, config.new_pool.semian_identifier + assert_equal(:redis_foo, new_config(semian: { name: "foo" }).semian_identifier) + assert_equal(:"redis_#{SemianConfig["toxiproxy_upstream_host"]}:16379/1", + new_config(semian: { name: nil }).semian_identifier) + assert_equal(:"redis_example.com:42/1", + new_config(host: "example.com", port: 42, semian: { name: nil }).semian_identifier) + + config = new_config(semian: { name: "foo" }) + assert_equal(:redis_foo, config.new_client.semian_identifier) + assert_equal(:redis_foo, config.new_pool.semian_identifier) end def test_config_alias config = new_config client = config.new_client client2 = config.new_client - assert_equal client.semian_resource, client2.semian_resource - assert_equal client.semian_identifier, client2.semian_identifier + assert_equal(client.semian_resource, client2.semian_resource) + assert_equal(client.semian_identifier, client2.semian_identifier) end def test_semian_can_be_disabled resource = RedisClient.new(semian: false).semian_resource - assert_instance_of Semian::UnprotectedResource, resource + assert_instance_of(Semian::UnprotectedResource, resource) end def test_connection_errors_open_the_circuit @@ -50,13 +54,13 @@ def test_connection_errors_open_the_circuit @proxy.downstream(:latency, latency: 600).apply do ERROR_THRESHOLD.times do - assert_raises RedisClient::ReadTimeoutError do - client.call('GET', 'foo') + assert_raises(RedisClient::ReadTimeoutError) do + client.call("GET", "foo") end end - assert_raises RedisClient::CircuitOpenError do - client.call('GET', 'foo') + assert_raises(RedisClient::CircuitOpenError) do + client.call("GET", "foo") end end end @@ -66,23 +70,23 @@ def test_connection_reset_does_not_open_the_circuit @proxy.downstream(:reset_peer).apply do ERROR_THRESHOLD.times do - assert_raises RedisClient::ConnectionError do - client.call('GET', 'foo') + assert_raises(RedisClient::ConnectionError) do + client.call("GET", "foo") end end - assert_raises RedisClient::ConnectionError do - client.call('GET', 'foo') + assert_raises(RedisClient::ConnectionError) do + client.call("GET", "foo") end end end def test_command_errors_does_not_open_the_circuit client = connect_to_redis! - client.call('HSET', 'my_hash', 'foo', 'bar') + client.call("HSET", "my_hash", "foo", "bar") (ERROR_THRESHOLD * 2).times do - assert_raises RedisClient::CommandError do - client.call('GET', 'my_hash') + assert_raises(RedisClient::CommandError) do + client.call("GET", "my_hash") end end end @@ -94,14 +98,14 @@ def test_connect_instrumentation next if scope == :query notified = true - assert_equal Semian[:redis_testing], resource - assert_equal :connection, scope - assert_equal :redis_client, adapter + assert_equal(Semian[:redis_testing], resource) + assert_equal(:connection, scope) + assert_equal(:redis_client, adapter) end connect_to_redis! - assert notified, 'No notifications has been emitted' + assert(notified, "No notifications has been emitted") ensure Semian.unsubscribe(subscriber) end @@ -110,37 +114,37 @@ def test_resource_acquisition_for_connect connect_to_redis! Semian[:redis_testing].acquire do - error = assert_raises RedisClient::ResourceBusyError do + error = assert_raises(RedisClient::ResourceBusyError) do connect_to_redis! end - assert_equal :redis_testing, error.semian_identifier + assert_equal(:redis_testing, error.semian_identifier) end end def test_redis_connection_errors_are_tagged_with_the_resource_identifier @proxy.downstream(:latency, latency: 600).apply do - error = assert_raises RedisClient::TimeoutError do + error = assert_raises(RedisClient::TimeoutError) do redis = connect_to_redis! - redis.get('foo') + redis.get("foo") end - assert_equal :redis_testing, error.semian_identifier + assert_equal(:redis_testing, error.semian_identifier) end end def test_other_redis_errors_are_not_tagged_with_the_resource_identifier client = connect_to_redis! - client.call('set', 'foo', 'bar') - error = assert_raises RedisClient::CommandError do - client.call('hget', 'foo', 'bar') + client.call("set", "foo", "bar") + error = assert_raises(RedisClient::CommandError) do + client.call("hget", "foo", "bar") end - refute error.respond_to?(:semian_identifier) + refute_respond_to(error, :semian_identifier) end def test_resource_timeout_on_connect @proxy.downstream(:latency, latency: redis_timeout_ms).apply do background { connect_to_redis! } - assert_raises RedisClient::ResourceBusyError do + assert_raises(RedisClient::ResourceBusyError) do connect_to_redis! end end @@ -148,13 +152,13 @@ def test_resource_timeout_on_connect def test_dns_resolution_failures_open_circuit ERROR_THRESHOLD.times do - assert_raises RedisClient::ConnectionError do - connect_to_redis!(host: 'thisdoesnotresolve') + assert_raises(RedisClient::ConnectionError) do + connect_to_redis!(host: "thisdoesnotresolve") end end - assert_raises RedisClient::CircuitOpenError do - connect_to_redis!(host: 'thisdoesnotresolve') + assert_raises(RedisClient::CircuitOpenError) do + connect_to_redis!(host: "thisdoesnotresolve") end Timecop.travel(ERROR_TIMEOUT + 1) do @@ -168,15 +172,15 @@ def test_query_instrumentation notified = false subscriber = Semian.subscribe do |event, resource, scope, adapter| notified = true - assert_equal :success, event - assert_equal Semian[:redis_testing], resource - assert_equal :query, scope - assert_equal :redis_client, adapter + assert_equal(:success, event) + assert_equal(Semian[:redis_testing], resource) + assert_equal(:query, scope) + assert_equal(:redis_client, adapter) end - client.call('get', 'foo') + client.call("get", "foo") - assert notified, 'No notifications has been emitted' + assert(notified, "No notifications has been emitted") ensure Semian.unsubscribe(subscriber) end @@ -188,37 +192,37 @@ def test_timeout_changes_when_half_open_and_configured_with_reads Timecop.freeze(0) do @proxy.downstream(:latency, latency: redis_timeout_ms + 200).apply do ERROR_THRESHOLD.times do - assert_raises RedisClient::TimeoutError do - client.call('get', 'foo') + assert_raises(RedisClient::TimeoutError) do + client.call("get", "foo") end end end - assert_raises RedisClient::CircuitOpenError do - client.call('get', 'foo') + assert_raises(RedisClient::CircuitOpenError) do + client.call("get", "foo") end end time_circuit_half_open = ERROR_TIMEOUT + 1 Timecop.travel(time_circuit_half_open) do assert_redis_timeout_in_delta(expected_timeout: half_open_resource_timeout) do - client.call('get', 'foo') + client.call("get", "foo") end end time_circuit_closed = time_circuit_half_open + ERROR_TIMEOUT + 1 Timecop.travel(time_circuit_closed) do - SUCCESS_THRESHOLD.times { client.call('get', 'foo') } + SUCCESS_THRESHOLD.times { client.call("get", "foo") } # Timeout has reset now that the Circuit is closed assert_redis_timeout_in_delta(expected_timeout: REDIS_TIMEOUT) do - client.call('get', 'foo') + client.call("get", "foo") end end - assert_equal REDIS_TIMEOUT, client.connect_timeout - assert_equal REDIS_TIMEOUT, client.read_timeout - assert_equal REDIS_TIMEOUT, client.write_timeout + assert_equal(REDIS_TIMEOUT, client.connect_timeout) + assert_equal(REDIS_TIMEOUT, client.read_timeout) + assert_equal(REDIS_TIMEOUT, client.write_timeout) end def test_timeout_changes_when_half_open_and_configured_with_writes_and_disconnects @@ -228,14 +232,14 @@ def test_timeout_changes_when_half_open_and_configured_with_writes_and_disconnec Timecop.freeze(0) do @proxy.downstream(:latency, latency: redis_timeout_ms + 200).apply do ERROR_THRESHOLD.times do - assert_raises RedisClient::TimeoutError do - client.call('set', 'foo', 1) + assert_raises(RedisClient::TimeoutError) do + client.call("set", "foo", 1) end end end - assert_raises RedisClient::CircuitOpenError do - client.call('set', 'foo', 1) + assert_raises(RedisClient::CircuitOpenError) do + client.call("set", "foo", 1) end end @@ -244,7 +248,7 @@ def test_timeout_changes_when_half_open_and_configured_with_writes_and_disconnec time_circuit_half_open = ERROR_TIMEOUT + 1 Timecop.travel(time_circuit_half_open) do assert_redis_timeout_in_delta(expected_timeout: half_open_resource_timeout) do - client.call('set', 'foo', 1) + client.call("set", "foo", 1) end end @@ -252,16 +256,16 @@ def test_timeout_changes_when_half_open_and_configured_with_writes_and_disconnec time_circuit_closed = time_circuit_half_open + ERROR_TIMEOUT + 1 Timecop.travel(time_circuit_closed) do - SUCCESS_THRESHOLD.times { client.call('set', 'foo', 1) } + SUCCESS_THRESHOLD.times { client.call("set", "foo", 1) } assert_redis_timeout_in_delta(expected_timeout: REDIS_TIMEOUT) do - client.call('set', 'foo', 1) + client.call("set", "foo", 1) end end - assert_equal REDIS_TIMEOUT, client.connect_timeout - assert_equal REDIS_TIMEOUT, client.read_timeout - assert_equal REDIS_TIMEOUT, client.write_timeout + assert_equal(REDIS_TIMEOUT, client.connect_timeout) + assert_equal(REDIS_TIMEOUT, client.read_timeout) + assert_equal(REDIS_TIMEOUT, client.write_timeout) end def test_timeout_doesnt_change_when_half_open_but_not_configured @@ -270,21 +274,21 @@ def test_timeout_doesnt_change_when_half_open_but_not_configured Timecop.freeze(0) do @proxy.downstream(:latency, latency: redis_timeout_ms + 200).apply do ERROR_THRESHOLD.times do - assert_raises RedisClient::TimeoutError do - client.call('get', 'foo') + assert_raises(RedisClient::TimeoutError) do + client.call("get", "foo") end end end - assert_raises RedisClient::CircuitOpenError do - client.call('get', 'foo') + assert_raises(RedisClient::CircuitOpenError) do + client.call("get", "foo") end end time_circuit_half_open = ERROR_TIMEOUT + 1 Timecop.travel(time_circuit_half_open) do assert_redis_timeout_in_delta(expected_timeout: REDIS_TIMEOUT) do - client.call('get', 'foo') + client.call("get", "foo") end end end @@ -293,8 +297,8 @@ def test_resource_acquisition_for_query client = connect_to_redis! Semian[:redis_testing].acquire do - assert_raises RedisClient::ResourceBusyError do - client.call('get', 'foo') + assert_raises(RedisClient::ResourceBusyError) do + client.call("get", "foo") end end end @@ -304,10 +308,10 @@ def test_resource_timeout_on_query client2 = connect_to_redis! @proxy.downstream(:latency, latency: redis_timeout_ms).apply do - background { client2.call('get', 'foo') } + background { client2.call("get", "foo") } - assert_raises RedisClient::ResourceBusyError do - client.call('get', 'foo') + assert_raises(RedisClient::ResourceBusyError) do + client.call("get", "foo") end end end @@ -316,26 +320,26 @@ def test_circuit_breaker_on_query client = connect_to_redis! client2 = connect_to_redis! - client.call('set', 'foo', 2) + client.call("set", "foo", 2) @proxy.downstream(:latency, latency: 1000).apply do - background { client2.call('get', 'foo') } + background { client2.call("get", "foo") } ERROR_THRESHOLD.times do - assert_raises RedisClient::ResourceBusyError do - client.call('get', 'foo') + assert_raises(RedisClient::ResourceBusyError) do + client.call("get", "foo") end end end yield_to_background - assert_raises RedisClient::CircuitOpenError do - client.call('get', 'foo') + assert_raises(RedisClient::CircuitOpenError) do + client.call("get", "foo") end Timecop.travel(ERROR_TIMEOUT + 1) do - assert_equal '2', client.call('get', 'foo') + assert_equal("2", client.call("get", "foo")) end end @@ -346,15 +350,15 @@ def new_client(**options) end def new_config(**options) - options[:host] = SemianConfig['toxiproxy_upstream_host'] if options[:host].nil? + options[:host] = SemianConfig["toxiproxy_upstream_host"] if options[:host].nil? semian_options = SEMIAN_OPTIONS.merge(options.delete(:semian) || {}) RedisClient.config(**{ - port: SemianConfig['redis_toxiproxy_port'], + port: SemianConfig["redis_toxiproxy_port"], reconnect_attempts: 0, db: 1, timeout: REDIS_TIMEOUT, semian: semian_options, - driver: redis_driver + driver: redis_driver, }.merge(options)) end @@ -369,17 +373,15 @@ def redis_timeout_ms @redis_timeout_ms ||= (REDIS_TIMEOUT * 1000).to_i end - def assert_redis_timeout_in_delta(expected_timeout:, delta: 0.1) + def assert_redis_timeout_in_delta(expected_timeout:, delta: 0.1, &block) latency = ((expected_timeout + 2 * delta) * 1000).to_i bench = Benchmark.measure do - assert_raises RedisClient::TimeoutError do - @proxy.downstream(:latency, latency: latency).apply do - yield - end + assert_raises(RedisClient::TimeoutError) do + @proxy.downstream(:latency, latency: latency).apply(&block) end end - assert_in_delta bench.real, expected_timeout, delta + assert_in_delta(bench.real, expected_timeout, delta) end end diff --git a/test/redis_test.rb b/test/redis_test.rb index dcb419fb5..7a3910595 100644 --- a/test/redis_test.rb +++ b/test/redis_test.rb @@ -1,5 +1,7 @@ -require 'test_helper' -require 'benchmark' +# frozen_string_literal: true + +require "test_helper" +require "benchmark" module RedisTests REDIS_TIMEOUT = 0.5 @@ -16,32 +18,35 @@ module RedisTests } attr_writer :threads + def setup @proxy = Toxiproxy[:semian_test_redis] Semian.destroy(:redis_testing) end def test_semian_identifier - assert_equal :redis_foo, new_redis(semian: {name: 'foo'})._client.semian_identifier - assert_equal :"redis_#{SemianConfig['toxiproxy_upstream_host']}:16379/1", new_redis(semian: {name: nil})._client.semian_identifier - assert_equal :'redis_example.com:42/1', new_redis(host: 'example.com', port: 42, semian: {name: nil})._client.semian_identifier + assert_equal(:redis_foo, new_redis(semian: { name: "foo" })._client.semian_identifier) + assert_equal(:"redis_#{SemianConfig["toxiproxy_upstream_host"]}:16379/1", + new_redis(semian: { name: nil })._client.semian_identifier) + assert_equal(:"redis_example.com:42/1", + new_redis(host: "example.com", port: 42, semian: { name: nil })._client.semian_identifier) end def test_client_alias redis = connect_to_redis! - assert_equal redis._client.semian_resource, redis.semian_resource - assert_equal redis._client.semian_identifier, redis.semian_identifier + assert_equal(redis._client.semian_resource, redis.semian_resource) + assert_equal(redis._client.semian_identifier, redis.semian_identifier) end def test_semian_can_be_disabled resource = Redis.new(semian: false)._client.semian_resource - assert_instance_of Semian::UnprotectedResource, resource + assert_instance_of(Semian::UnprotectedResource, resource) end def test_semian_resource_in_pipeline redis = connect_to_redis! redis.pipelined do |_pipeline| - assert_instance_of Semian::ProtectedResource, redis.semian_resource + assert_instance_of(Semian::ProtectedResource, redis.semian_resource) end end @@ -50,13 +55,13 @@ def test_connection_errors_open_the_circuit @proxy.downstream(:latency, latency: 600).apply do ERROR_THRESHOLD.times do - assert_raises ::Redis::TimeoutError do - client.get('foo') + assert_raises(::Redis::TimeoutError) do + client.get("foo") end end - assert_raises ::Redis::CircuitOpenError do - client.get('foo') + assert_raises(::Redis::CircuitOpenError) do + client.get("foo") end end end @@ -66,23 +71,23 @@ def test_connection_reset_does_not_open_the_circuit @proxy.downstream(:reset_peer).apply do ERROR_THRESHOLD.times do - assert_raises ::Redis::ConnectionError do - client.get('foo') + assert_raises(::Redis::ConnectionError) do + client.get("foo") end end - assert_raises ::Redis::ConnectionError do - client.get('foo') + assert_raises(::Redis::ConnectionError) do + client.get("foo") end end end def test_command_errors_does_not_open_the_circuit client = connect_to_redis! - client.hset('my_hash', 'foo', 'bar') + client.hset("my_hash", "foo", "bar") (ERROR_THRESHOLD * 2).times do - assert_raises Redis::CommandError do - client.get('my_hash') + assert_raises(Redis::CommandError) do + client.get("my_hash") end end end @@ -92,15 +97,15 @@ def test_command_errors_because_of_oom_do_open_the_circuit with_maxmemory(1) do ERROR_THRESHOLD.times do - exception = assert_raises ::Redis::OutOfMemoryError do - client.set('foo', 'bar') + exception = assert_raises(::Redis::OutOfMemoryError) do + client.set("foo", "bar") end - assert_equal :redis_testing, exception.semian_identifier + assert_equal(:redis_testing, exception.semian_identifier) end - assert_raises ::Redis::CircuitOpenError do - client.set('foo', 'bla') + assert_raises(::Redis::CircuitOpenError) do + client.set("foo", "bla") end end end @@ -110,14 +115,14 @@ def test_script_errors_because_of_oom_do_open_the_circuit with_maxmemory(1) do ERROR_THRESHOLD.times do - exception = assert_raises ::Redis::OutOfMemoryError do + exception = assert_raises(::Redis::OutOfMemoryError) do client.eval("return redis.call('set', 'foo', 'bar');") end - assert_equal :redis_testing, exception.semian_identifier + assert_equal(:redis_testing, exception.semian_identifier) end - assert_raises ::Redis::CircuitOpenError do + assert_raises(::Redis::CircuitOpenError) do client.eval("return redis.call('set', 'foo', 'bar');") end end @@ -129,14 +134,14 @@ def test_connect_instrumentation next unless event == :success notified = true - assert_equal Semian[:redis_testing], resource - assert_equal :connection, scope - assert_equal :redis, adapter + assert_equal(Semian[:redis_testing], resource) + assert_equal(:connection, scope) + assert_equal(:redis, adapter) end connect_to_redis! - assert notified, 'No notifications has been emitted' + assert(notified, "No notifications has been emitted") ensure Semian.unsubscribe(subscriber) end @@ -145,37 +150,37 @@ def test_resource_acquisition_for_connect connect_to_redis! Semian[:redis_testing].acquire do - error = assert_raises Redis::ResourceBusyError do + error = assert_raises(Redis::ResourceBusyError) do connect_to_redis! end - assert_equal :redis_testing, error.semian_identifier + assert_equal(:redis_testing, error.semian_identifier) end end def test_redis_connection_errors_are_tagged_with_the_resource_identifier @proxy.downstream(:latency, latency: 600).apply do - error = assert_raises ::Redis::TimeoutError do + error = assert_raises(::Redis::TimeoutError) do redis = connect_to_redis! - redis.get('foo') + redis.get("foo") end - assert_equal :redis_testing, error.semian_identifier + assert_equal(:redis_testing, error.semian_identifier) end end def test_other_redis_errors_are_not_tagged_with_the_resource_identifier client = connect_to_redis! - client.set('foo', 'bar') - error = assert_raises ::Redis::CommandError do - client.hget('foo', 'bar') + client.set("foo", "bar") + error = assert_raises(::Redis::CommandError) do + client.hget("foo", "bar") end - refute error.respond_to?(:semian_identifier) + refute_respond_to(error, :semian_identifier) end def test_resource_timeout_on_connect @proxy.downstream(:latency, latency: redis_timeout_ms).apply do background { connect_to_redis! } - assert_raises Redis::ResourceBusyError do + assert_raises(Redis::ResourceBusyError) do connect_to_redis! end end @@ -183,13 +188,13 @@ def test_resource_timeout_on_connect def test_dns_resolution_failures_open_circuit ERROR_THRESHOLD.times do - assert_raises Redis::ResolveError do - connect_to_redis!(host: 'thisdoesnotresolve') + assert_raises(Redis::ResolveError) do + connect_to_redis!(host: "thisdoesnotresolve") end end - assert_raises Redis::CircuitOpenError do - connect_to_redis!(host: 'thisdoesnotresolve') + assert_raises(Redis::CircuitOpenError) do + connect_to_redis!(host: "thisdoesnotresolve") end Timecop.travel(ERROR_TIMEOUT + 1) do @@ -203,12 +208,12 @@ def test_dns_resolution_failures_open_circuit "name or service not known", "Could not resolve hostname example.com: nodename nor servname provided, or not known", ].each do |message| - test_suffix = message.gsub(/\W/, '_').downcase + test_suffix = message.gsub(/\W/, "_").downcase define_method(:"test_dns_resolution_failure_#{test_suffix}") do Redis::Client.any_instance.expects(:raw_connect).raises(message) assert_raises Redis::ResolveError do - connect_to_redis!(host: 'example.com') + connect_to_redis!(host: "example.com") end end end @@ -218,7 +223,7 @@ def test_circuit_breaker_on_connect background { connect_to_redis! } ERROR_THRESHOLD.times do - assert_raises Redis::ResourceBusyError do + assert_raises(Redis::ResourceBusyError) do connect_to_redis! end end @@ -226,7 +231,7 @@ def test_circuit_breaker_on_connect yield_to_background - assert_raises Redis::CircuitOpenError do + assert_raises(Redis::CircuitOpenError) do connect_to_redis! end @@ -241,15 +246,15 @@ def test_query_instrumentation notified = false subscriber = Semian.subscribe do |event, resource, scope, adapter| notified = true - assert_equal :success, event - assert_equal Semian[:redis_testing], resource - assert_equal :query, scope - assert_equal :redis, adapter + assert_equal(:success, event) + assert_equal(Semian[:redis_testing], resource) + assert_equal(:query, scope) + assert_equal(:redis, adapter) end - client.get('foo') + client.get("foo") - assert notified, 'No notifications has been emitted' + assert(notified, "No notifications has been emitted") ensure Semian.unsubscribe(subscriber) end @@ -261,39 +266,39 @@ def test_timeout_changes_when_half_open_and_configured_with_reads Timecop.freeze(0) do @proxy.downstream(:latency, latency: redis_timeout_ms + 200).apply do ERROR_THRESHOLD.times do - assert_raises ::Redis::TimeoutError do - client.get('foo') + assert_raises(::Redis::TimeoutError) do + client.get("foo") end end end - assert_raises ::Redis::CircuitOpenError do - client.get('foo') + assert_raises(::Redis::CircuitOpenError) do + client.get("foo") end end time_circuit_half_open = ERROR_TIMEOUT + 1 Timecop.travel(time_circuit_half_open) do assert_redis_timeout_in_delta(expected_timeout: half_open_resource_timeout) do - client.get('foo') + client.get("foo") end end time_circuit_closed = time_circuit_half_open + ERROR_TIMEOUT + 1 Timecop.travel(time_circuit_closed) do - SUCCESS_THRESHOLD.times { client.get('foo') } + SUCCESS_THRESHOLD.times { client.get("foo") } # Timeout has reset now that the Circuit is closed assert_redis_timeout_in_delta(expected_timeout: REDIS_TIMEOUT) do - client.get('foo') + client.get("foo") end end - assert_equal REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:timeout] - assert_equal REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:connect_timeout] - assert_equal REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:read_timeout] - assert_equal REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:write_timeout] - assert_equal REDIS_TIMEOUT, client.instance_variable_get(:@client).timeout + assert_equal(REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:timeout]) + assert_equal(REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:connect_timeout]) + assert_equal(REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:read_timeout]) + assert_equal(REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:write_timeout]) + assert_equal(REDIS_TIMEOUT, client.instance_variable_get(:@client).timeout) end def test_timeout_changes_when_half_open_and_configured_with_writes_and_disconnects @@ -303,14 +308,14 @@ def test_timeout_changes_when_half_open_and_configured_with_writes_and_disconnec Timecop.freeze(0) do @proxy.downstream(:latency, latency: redis_timeout_ms + 200).apply do ERROR_THRESHOLD.times do - assert_raises ::Redis::TimeoutError do - client.set('foo', 1) + assert_raises(::Redis::TimeoutError) do + client.set("foo", 1) end end end - assert_raises ::Redis::CircuitOpenError do - client.set('foo', 1) + assert_raises(::Redis::CircuitOpenError) do + client.set("foo", 1) end end @@ -319,7 +324,7 @@ def test_timeout_changes_when_half_open_and_configured_with_writes_and_disconnec time_circuit_half_open = ERROR_TIMEOUT + 1 Timecop.travel(time_circuit_half_open) do assert_redis_timeout_in_delta(expected_timeout: half_open_resource_timeout) do - client.set('foo', 1) + client.set("foo", 1) end end @@ -327,18 +332,18 @@ def test_timeout_changes_when_half_open_and_configured_with_writes_and_disconnec time_circuit_closed = time_circuit_half_open + ERROR_TIMEOUT + 1 Timecop.travel(time_circuit_closed) do - SUCCESS_THRESHOLD.times { client.set('foo', 1) } + SUCCESS_THRESHOLD.times { client.set("foo", 1) } assert_redis_timeout_in_delta(expected_timeout: REDIS_TIMEOUT) do - client.set('foo', 1) + client.set("foo", 1) end end - assert_equal REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:timeout] - assert_equal REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:connect_timeout] - assert_equal REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:read_timeout] - assert_equal REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:write_timeout] - assert_equal REDIS_TIMEOUT, client.instance_variable_get(:@client).timeout + assert_equal(REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:timeout]) + assert_equal(REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:connect_timeout]) + assert_equal(REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:read_timeout]) + assert_equal(REDIS_TIMEOUT, client.instance_variable_get(:@client).options[:write_timeout]) + assert_equal(REDIS_TIMEOUT, client.instance_variable_get(:@client).timeout) end def test_timeout_doesnt_change_when_half_open_but_not_configured @@ -347,21 +352,21 @@ def test_timeout_doesnt_change_when_half_open_but_not_configured Timecop.freeze(0) do @proxy.downstream(:latency, latency: redis_timeout_ms + 200).apply do ERROR_THRESHOLD.times do - assert_raises ::Redis::TimeoutError do - client.get('foo') + assert_raises(::Redis::TimeoutError) do + client.get("foo") end end end - assert_raises ::Redis::CircuitOpenError do - client.get('foo') + assert_raises(::Redis::CircuitOpenError) do + client.get("foo") end end time_circuit_half_open = ERROR_TIMEOUT + 1 Timecop.travel(time_circuit_half_open) do assert_redis_timeout_in_delta(expected_timeout: REDIS_TIMEOUT) do - client.get('foo') + client.get("foo") end end end @@ -370,8 +375,8 @@ def test_resource_acquisition_for_query client = connect_to_redis! Semian[:redis_testing].acquire do - assert_raises Redis::ResourceBusyError do - client.get('foo') + assert_raises(Redis::ResourceBusyError) do + client.get("foo") end end end @@ -381,10 +386,10 @@ def test_resource_timeout_on_query client2 = connect_to_redis! @proxy.downstream(:latency, latency: redis_timeout_ms).apply do - background { client2.get('foo') } + background { client2.get("foo") } - assert_raises Redis::ResourceBusyError do - client.get('foo') + assert_raises(Redis::ResourceBusyError) do + client.get("foo") end end end @@ -393,41 +398,41 @@ def test_circuit_breaker_on_query client = connect_to_redis! client2 = connect_to_redis! - client.set('foo', 2) + client.set("foo", 2) @proxy.downstream(:latency, latency: 1000).apply do - background { client2.get('foo') } + background { client2.get("foo") } ERROR_THRESHOLD.times do - assert_raises Redis::ResourceBusyError do - client.get('foo') + assert_raises(Redis::ResourceBusyError) do + client.get("foo") end end end yield_to_background - assert_raises Redis::CircuitOpenError do - client.get('foo') + assert_raises(Redis::CircuitOpenError) do + client.get("foo") end Timecop.travel(ERROR_TIMEOUT + 1) do - assert_equal '2', client.get('foo') + assert_equal("2", client.get("foo")) end end private def new_redis(options = {}) - options[:host] = SemianConfig['toxiproxy_upstream_host'] if options[:host].nil? + options[:host] = SemianConfig["toxiproxy_upstream_host"] if options[:host].nil? semian_options = SEMIAN_OPTIONS.merge(options.delete(:semian) || {}) Redis.new({ - port: SemianConfig['redis_toxiproxy_port'], + port: SemianConfig["redis_toxiproxy_port"], reconnect_attempts: 0, db: 1, timeout: REDIS_TIMEOUT, semian: semian_options, - driver: redis_driver + driver: redis_driver, }.merge(options)) end @@ -439,14 +444,14 @@ def connect_to_redis!(semian_options = {}) end def with_maxmemory(bytes) - client = connect_to_redis!(name: 'maxmemory') + client = connect_to_redis!(name: "maxmemory") - _, old = client.config('get', 'maxmemory') + _, old = client.config("get", "maxmemory") begin - client.config('set', 'maxmemory', bytes) + client.config("set", "maxmemory", bytes) yield ensure - client.config('set', 'maxmemory', old) + client.config("set", "maxmemory", old) end end @@ -454,17 +459,15 @@ def redis_timeout_ms @redis_timeout_ms ||= (REDIS_TIMEOUT * 1000).to_i end - def assert_redis_timeout_in_delta(expected_timeout:, delta: 0.1) + def assert_redis_timeout_in_delta(expected_timeout:, delta: 0.1, &block) latency = ((expected_timeout + 2 * delta) * 1000).to_i bench = Benchmark.measure do - assert_raises Redis::TimeoutError do - @proxy.downstream(:latency, latency: latency).apply do - yield - end + assert_raises(Redis::TimeoutError) do + @proxy.downstream(:latency, latency: latency).apply(&block) end end - assert_in_delta bench.real, expected_timeout, delta + assert_in_delta(bench.real, expected_timeout, delta) end end diff --git a/test/resource_test.rb b/test/resource_test.rb index b38cc7547..44d198dfc 100644 --- a/test/resource_test.rb +++ b/test/resource_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class TestResource < Minitest::Test include ResourceHelper @@ -14,59 +16,60 @@ def setup def teardown destroy_resources - signal_workers('KILL') + signal_workers("KILL") Process.waitall end def test_initialize_invalid_args - assert_raises TypeError do - create_resource 123, tickets: 2 + assert_raises(TypeError) do + create_resource(123, tickets: 2) end - assert_raises ArgumentError do - create_resource :testing, tickets: -1 + assert_raises(ArgumentError) do + create_resource(:testing, tickets: -1) end - assert_raises ArgumentError do - create_resource :testing, tickets: 1_000_000 + assert_raises(ArgumentError) do + create_resource(:testing, tickets: 1_000_000) end - assert_raises TypeError do - create_resource :testing, tickets: 2, permissions: 'test' + assert_raises(TypeError) do + create_resource(:testing, tickets: 2, permissions: "test") end end def test_initialize_with_float expected_warning = /semian ticket value 1\.000000 is a float, converting to fixnum/ with_fake_std_error(warn_message: expected_warning) do - resource = create_resource :testing, tickets: 1.0 - assert resource - assert_equal 1, resource.tickets + resource = create_resource(:testing, tickets: 1.0) + assert(resource) + assert_equal(1, resource.tickets) end end def test_max_tickets - assert Semian::MAX_TICKETS > 0 + assert(Semian::MAX_TICKETS > 0) end def test_register - create_resource :testing, tickets: 2 + create_resource(:testing, tickets: 2) end def test_register_with_quota - create_resource :testing, quota: 0.5 + create_resource(:testing, quota: 0.5) end def test_unregister_past_0 workers = 10 - resource = Semian.register(:testing, tickets: workers * 2, error_threshold: 0, error_timeout: 0, success_threshold: 0) + resource = Semian.register(:testing, tickets: workers * 2, error_threshold: 0, error_timeout: 0, + success_threshold: 0) fork_workers(count: workers, tickets: 0, timeout: 0.5, wait_for_timeout: true) do Semian.unregister(:testing) end Semian.unregister(:testing) - signal_workers('TERM') + signal_workers("TERM") Process.waitall - assert_equal 0, resource.registered_workers + assert_equal(0, resource.registered_workers) end def test_reset_registered_workers @@ -75,13 +78,13 @@ def test_reset_registered_workers fork_workers(count: workers - 1, tickets: 0, timeout: 0.5, wait_for_timeout: true) - assert_equal workers, resource.registered_workers + assert_equal(workers, resource.registered_workers) resource.bulkhead.reset_registered_workers! - assert_equal 0, resource.registered_workers + assert_equal(0, resource.registered_workers) - signal_workers('TERM') + signal_workers("TERM") Process.waitall - assert_equal 0, resource.registered_workers + assert_equal(0, resource.registered_workers) end def test_exactly_one_register_with_quota @@ -91,78 +94,79 @@ def test_exactly_one_register_with_quota Semian::Resource.instance(:testing, quota: 0.5) end - assert_equal 1, r.tickets + assert_equal(1, r.tickets) r.destroy end def test_register_with_invalid_quota - assert_raises ArgumentError do - create_resource :testing, quota: 2.0 + assert_raises(ArgumentError) do + create_resource(:testing, quota: 2.0) end - assert_raises ArgumentError do - create_resource :testing, quota: 0 + assert_raises(ArgumentError) do + create_resource(:testing, quota: 0) end - assert_raises ArgumentError do - create_resource :testing, quota: -1.0 + assert_raises(ArgumentError) do + create_resource(:testing, quota: -1.0) end end def test_register_with_quota_and_tickets_raises - assert_raises ArgumentError do - create_resource :testing, tickets: 2, quota: 0.5 + assert_raises(ArgumentError) do + create_resource(:testing, tickets: 2, quota: 0.5) end end def test_register_with_neither_quota_nor_tickets_raises - assert_raises ArgumentError do + assert_raises(ArgumentError) do Semian::Resource.new(:testing) end end def test_register_with_no_tickets_raises - assert_raises Semian::SyscallError do - create_resource :test_raises, tickets: 0 + assert_raises(Semian::SyscallError) do + create_resource(:test_raises, tickets: 0) end end def test_acquire acquired = false - resource = create_resource :testing, tickets: 1 + resource = create_resource(:testing, tickets: 1) resource.acquire { acquired = true } - assert acquired + assert(acquired) end def test_acquire_return_val - resource = create_resource :testing, tickets: 1 + resource = create_resource(:testing, tickets: 1) val = resource.acquire { 1234 } - assert_equal 1234, val + assert_equal(1234, val) end def test_acquire_timeout fork_workers(count: 2, tickets: 1, timeout: 1, wait_for_timeout: true) - signal_workers('TERM') + signal_workers("TERM") timeouts = count_worker_timeouts - assert 1, timeouts + assert_equal(1, timeouts) end def test_acquire_timeout_override skip("Never tested correctly") + fork_workers(count: 1, tickets: 1, timeout: 0.5, wait_for_timeout: true) do - sleep 0.6 + sleep(0.6) end fork_workers(count: 1, tickets: 1, timeout: 1, wait_for_timeout: true) - signal_workers('TERM') + signal_workers("TERM") timeouts = count_worker_timeouts - assert_equal 0, timeouts + assert_equal(0, timeouts) end def test_acquire_with_fork - resource = create_resource :testing, tickets: 2, timeout: 0.5 + resource = create_resource(:testing, tickets: 2, timeout: 0.5) resource.acquire do fork do @@ -170,9 +174,9 @@ def test_acquire_with_fork begin resource.acquire {} rescue Semian::TimeoutError - exit! 100 + exit!(100) end - exit! 0 + exit!(0) end end timeouts = count_worker_timeouts @@ -185,26 +189,26 @@ def test_quota_acquire quota = 0.5 workers = 9 - resource = create_resource :testing, quota: quota, timeout: 0.1 + resource = create_resource(:testing, quota: quota, timeout: 0.1) fork_workers(count: workers, quota: quota, wait_for_timeout: true) # Ensure that the number of tickets is correct and none are remaining - assert_equal quota * (workers + 1), resource.tickets - assert_equal 0, resource.count + assert_equal(quota * (workers + 1), resource.tickets) + assert_equal(0, resource.count) # Ensure that no more tickets may be allocated - assert_raises Semian::TimeoutError do + assert_raises(Semian::TimeoutError) do resource.acquire {} end - signal_workers('TERM') + signal_workers("TERM") # Ensure the correct number of processes timed out timeouts = count_worker_timeouts - assert_equal workers - ((1 - quota) * workers).ceil, timeouts + assert_equal(workers - ((1 - quota) * workers).ceil, timeouts) # Ensure that the tickets were released - assert_equal resource.tickets, resource.count + assert_equal(resource.tickets, resource.count) end def test_quota_increase @@ -214,7 +218,7 @@ def test_quota_increase # Spawn some workers with an initial quota fork_workers(count: workers - 1, quota: quota, timeout: 0.5, wait_for_timeout: true) - resource = create_resource :testing, quota: new_quota, timeout: 0.1 + resource = create_resource(:testing, quota: new_quota, timeout: 0.1) assert_equal((workers * new_quota).ceil, resource.tickets) end @@ -226,12 +230,12 @@ def test_quota_decrease # Spawn some workers with an initial quota fork_workers(count: workers - 1, quota: quota, timeout: 0.5, wait_for_timeout: true) do - sleep 1 + sleep(1) end # We need to signal here to be able to enter the critical section - signal_workers('TERM') - resource = create_resource :testing, quota: new_quota, timeout: 0.1 + signal_workers("TERM") + resource = create_resource(:testing, quota: new_quota, timeout: 0.1) assert_equal((workers * new_quota).ceil, resource.tickets) end @@ -240,7 +244,7 @@ def test_quota_sets_tickets_from_workers quota = 0.5 workers = 50 - resource = create_resource :testing, quota: quota, timeout: 0.1 + resource = create_resource(:testing, quota: quota, timeout: 0.1) fork_workers(count: workers - 1, quota: quota, wait_for_timeout: true) assert_equal((workers * quota).ceil, resource.tickets) @@ -250,7 +254,7 @@ def test_quota_adjust_tickets_on_new_workers quota = 0.5 workers = 50 - resource = create_resource :testing, quota: quota, timeout: 0.1 + resource = create_resource(:testing, quota: quota, timeout: 0.1) # Spawn some workers to get a basis for the quota fork_workers(count: workers - 1, quota: quota, wait_for_timeout: true) @@ -265,27 +269,27 @@ def test_quota_adjust_tickets_on_kill quota = 0.5 workers = 50 - resource = create_resource :testing, quota: quota, timeout: 0.1 + resource = create_resource(:testing, quota: quota, timeout: 0.1) # Spawn some workers to get a basis for the quota fork_workers(count: workers - 1, quota: quota, wait_for_timeout: true) assert_equal((workers * quota).ceil, resource.tickets) # Signal and wait for the workers to quit - signal_workers('KILL') + signal_workers("KILL") Process.waitall # Number of tickets should be unchanged until resource is created assert_equal((workers * quota).ceil, resource.tickets) - resource = create_resource :testing, quota: quota, timeout: 0.1 - assert_equal 1, resource.tickets + resource = create_resource(:testing, quota: quota, timeout: 0.1) + assert_equal(1, resource.tickets) end def test_quota_minimum_one_ticket - resource = create_resource :testing, quota: 0.1, timeout: 0.1 + resource = create_resource(:testing, quota: 0.1, timeout: 0.1) - assert_equal 1, resource.tickets + assert_equal(1, resource.tickets) end def test_switch_static_tickets_to_quota @@ -294,50 +298,50 @@ def test_switch_static_tickets_to_quota # Fork a large number of workers using static ticket strategy fork_workers(count: workers - 1, tickets: 5, timeout: 0.5, wait_for_timeout: true) do - sleep 1 + sleep(1) end # Signal static workers to shut down - signal_workers('TERM') + signal_workers("TERM") # Create a quota based worker, and ensure it accounts for the static # workers that haven't shut down yet - resource = create_resource :testing, quota: quota, timeout: 0.1 + resource = create_resource(:testing, quota: quota, timeout: 0.1) assert_equal((quota * workers).ceil, resource.tickets) # Let the static workers shut down - sleep 2 + sleep(2) # Create a new resource, and ensure the static workers are no longer # accounted for - resource = create_resource :testing, quota: quota, timeout: 0.1 + resource = create_resource(:testing, quota: quota, timeout: 0.1) assert_equal((quota * 2).ceil, resource.tickets) end def test_acquire_releases_on_kill - resource = create_resource :testing, tickets: 1, timeout: 0.1 + resource = create_resource(:testing, tickets: 1, timeout: 0.1) acquired = false # Ghetto process synchronization - file = Tempfile.new('semian') + file = Tempfile.new("semian") path = file.path file.close! pid = fork do resource.acquire do FileUtils.touch(path) - sleep 1000 + sleep(1000) end end - sleep 0.1 until File.exist?(path) - assert_raises Semian::TimeoutError do + sleep(0.1) until File.exist?(path) + assert_raises(Semian::TimeoutError) do resource.acquire {} end Process.kill("KILL", pid) resource.acquire { acquired = true } - assert acquired + assert(acquired) Process.wait ensure @@ -347,30 +351,30 @@ def test_acquire_releases_on_kill def test_get_worker_count workers = rand(5..20) fork_workers(count: workers - 1, tickets: 1, timeout: 0.1, wait_for_timeout: true) - resource = create_resource :testing, tickets: 1 + resource = create_resource(:testing, tickets: 1) assert_equal(workers, resource.registered_workers) end def test_get_resource_key - resource = create_resource :testing, tickets: 2 - assert_equal('0x874714f2', resource.key) + resource = create_resource(:testing, tickets: 2) + assert_equal("0x874714f2", resource.key) end def test_count - resource = create_resource :testing, tickets: 2 + resource = create_resource(:testing, tickets: 2) acquired = false resource.acquire do acquired = true - assert_equal 1, resource.count - assert_equal 2, resource.tickets + assert_equal(1, resource.count) + assert_equal(2, resource.tickets) end - assert acquired + assert(acquired) end def test_sem_undo - resource = create_resource :testing, tickets: 1 + resource = create_resource(:testing, tickets: 1) # Ensure we don't hit ERANGE errors caused by lack of SEM_UNDO on semop* calls # by doing an acquire > SEMVMX (32767) times: @@ -384,34 +388,34 @@ def test_sem_undo end def test_destroy - resource = create_resource :testing, tickets: 1 + resource = create_resource(:testing, tickets: 1) resource.destroy - assert_raises Semian::SyscallError do + assert_raises(Semian::SyscallError) do resource.acquire {} end end def test_destroy_already_destroyed - resource = create_resource :testing, tickets: 1 + resource = create_resource(:testing, tickets: 1) 100.times do resource.destroy end end def test_permissions - resource = create_resource :testing, permissions: 0o600, tickets: 1 + resource = create_resource(:testing, permissions: 0600, tickets: 1) semid = resource.semid - `ipcs -s`.lines.each do |line| + %x(ipcs -s).lines.each do |line| if /\s#{semid}\s/.match(line) - assert_equal '600', line.split[3] + assert_equal("600", line.split[3]) end end - resource = create_resource :testing, permissions: 0o660, tickets: 1 + resource = create_resource(:testing, permissions: 0660, tickets: 1) semid = resource.semid - `ipcs -s`.lines.each do |line| + %x(ipcs -s).lines.each do |line| if /\s#{semid}\s/.match(line) - assert_equal '660', line.split[3] + assert_equal("660", line.split[3]) end end end @@ -419,75 +423,75 @@ def test_permissions def test_namespace previous_namespace = Semian.namespace - resource = create_resource :testing, permissions: 0o600, tickets: 1 + resource = create_resource(:testing, permissions: 0600, tickets: 1) key = resource.key Semian.destroy(:testing) - resource = create_resource :testing, permissions: 0o600, tickets: 1 - assert_equal key, resource.key + resource = create_resource(:testing, permissions: 0600, tickets: 1) + assert_equal(key, resource.key) Semian.destroy(:testing) Semian.namespace = "testing" - resource = create_resource :testing, permissions: 0o600, tickets: 1 - refute_equal key, resource.key + resource = create_resource(:testing, permissions: 0600, tickets: 1) + refute_equal(key, resource.key) Semian.destroy(:testing) ensure Semian.namespace = previous_namespace end def test_resize_tickets_increase - resource = create_resource :testing, tickets: 1 + resource = create_resource(:testing, tickets: 1) - assert_equal resource.tickets, resource.count - assert_equal 1, resource.count + assert_equal(resource.tickets, resource.count) + assert_equal(1, resource.count) fork_workers(count: 1, tickets: 5, timeout: 1, wait_for_timeout: true) - signal_workers('TERM') + signal_workers("TERM") Process.waitall - assert_equal resource.tickets, resource.count - assert_equal 5, resource.count + assert_equal(resource.tickets, resource.count) + assert_equal(5, resource.count) end def test_resize_tickets_decrease - resource = create_resource :testing, tickets: 5 + resource = create_resource(:testing, tickets: 5) - assert_equal resource.tickets, resource.count - assert_equal 5, resource.count + assert_equal(resource.tickets, resource.count) + assert_equal(5, resource.count) fork_workers(count: 1, tickets: 1, timeout: 1, wait_for_timeout: true) - signal_workers('TERM') + signal_workers("TERM") Process.waitall - assert_equal resource.tickets, resource.count - assert_equal 1, resource.count + assert_equal(resource.tickets, resource.count) + assert_equal(1, resource.count) end def test_resize_tickets_decrease_with_fork tickets = 10 workers = 50 - resource = create_resource :testing, tickets: tickets + resource = create_resource(:testing, tickets: tickets) # Need to have the processes sleep for a bit after they are signalled to die fork_workers(count: workers, tickets: 0, wait_for_timeout: true) do - sleep 2 + sleep(2) end assert_equal(tickets, resource.tickets) assert_equal(0, resource.count) # Signal the workers to quit - signal_workers('TERM') + signal_workers("TERM") # Request the ticket count be adjusted immediately # This should only return once the ticket count has been adjusted # This is sort of racey, because it's waiting on enough workers to quit # So that it has room to adjust the ticket count to the desired value. # This must happen in less than 5 seconds, or an internal timeout occurs. - resource = create_resource :testing, tickets: (tickets / 2).floor + resource = create_resource(:testing, tickets: (tickets / 2).floor) # Immediately on the above call returning, the tickets should be correct assert_equal((tickets / 2).floor, resource.tickets) @@ -501,14 +505,14 @@ def test_multiple_register_with_fork tickets = 5 fork_workers(resource: :testing, count: count, tickets: tickets, wait_for_timeout: true) - assert_equal 5, create_resource(:testing, tickets: 0).tickets - assert_equal 0, create_resource(:testing, tickets: 0).count + assert_equal(5, create_resource(:testing, tickets: 0).tickets) + assert_equal(0, create_resource(:testing, tickets: 0).count) - signal_workers('TERM') + signal_workers("TERM") timeouts = count_worker_timeouts - assert_equal 5, create_resource(:testing, tickets: 0).count - assert_equal 0, timeouts + assert_equal(5, create_resource(:testing, tickets: 0).count) + assert_equal(0, timeouts) end def create_resource(name, **kwargs) @@ -520,12 +524,11 @@ def create_resource(name, **kwargs) def destroy_resources return unless @resources + @resources.each do |resource| - begin - resource.destroy - rescue - nil - end + resource.destroy + rescue + nil end @resources = [] end @@ -537,39 +540,37 @@ def destroy_resources # and workers must be cleaned up between tests by the teardown script # An exit value of 100 is to keep track of timeouts, 0 for success. def fork_workers(count:, resource: :testing, quota: nil, tickets: nil, timeout: 0.1, wait_for_timeout: false) - fail 'Must provide at least one of tickets or quota' unless tickets || quota + raise("Must provide at least one of tickets or quota") unless tickets || quota @workers ||= [] count.times do @workers << fork do - begin - resource = Semian::Resource.new(resource.to_sym, quota: quota, tickets: tickets, timeout: timeout) + resource = Semian::Resource.new(resource.to_sym, quota: quota, tickets: tickets, timeout: timeout) - Signal.trap('TERM') do - yield if block_given? - exit! 0 - end + Signal.trap("TERM") do + yield if block_given? + exit!(0) + end - # Hold the resource until signalled - resource.acquire do - sleep - end - rescue Semian::TimeoutError - Signal.trap('TERM') do - # Still sleep (in the yield) to avoid SIGCHLD, which makes semtimedop get interrupted - yield if block_given? - exit! 100 - end + # Hold the resource until signalled + resource.acquire do sleep - rescue StandardError => e - puts "[ERROR] Unhandled exception occurred in worker" - puts "Class: #{e.class}" - puts "Message: #{e}" - puts "---Backtrace---" - puts e.backtrace.join("\n") - puts "---------------" - exit! 2 end + rescue Semian::TimeoutError + Signal.trap("TERM") do + # Still sleep (in the yield) to avoid SIGCHLD, which makes semtimedop get interrupted + yield if block_given? + exit!(100) + end + sleep + rescue StandardError => e + puts "[ERROR] Unhandled exception occurred in worker" + puts "Class: #{e.class}" + puts "Message: #{e}" + puts "---Backtrace---" + puts e.backtrace.join("\n") + puts "---------------" + exit!(2) end end sleep((count / 2.0).ceil * timeout + EPSILON) if wait_for_timeout # give time for threads to timeout @@ -582,12 +583,11 @@ def count_worker_timeouts # Signals all workers def signal_workers(signal, delete: true) return unless @workers + @workers.each do |worker| - begin - Process.kill(signal, worker) - rescue - nil - end + Process.kill(signal, worker) + rescue + nil end @workers = [] if delete end @@ -613,9 +613,9 @@ def with_fake_std_error(warn_message: nil) $stderr = fake_std_err yield if warn_message - assert_match warn_message, fake_std_err.messages[0] + assert_match(warn_message, fake_std_err.messages[0]) end ensure - $std_err = original_stderr # rubocop:disable GlobalVars + $std_err = original_stderr # rubocop:disable Style/GlobalVars end end diff --git a/test/semian_test.rb b/test/semian_test.rb index 547467769..316254e03 100644 --- a/test/semian_test.rb +++ b/test/semian_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class TestSemian < Minitest::Test def setup @@ -9,13 +11,13 @@ def setup def test_unsupported_acquire_yields acquired = false - Semian.register :testing, tickets: 1, error_threshold: 1, error_timeout: 2, success_threshold: 1 + Semian.register(:testing, tickets: 1, error_threshold: 1, error_timeout: 2, success_threshold: 1) Semian[:testing].acquire { acquired = true } - assert acquired + assert(acquired) end def test_register_with_circuit_breaker_missing_options - exception = assert_raises ArgumentError do + exception = assert_raises(ArgumentError) do Semian.register( :testing, error_threshold: 2, @@ -23,9 +25,7 @@ def test_register_with_circuit_breaker_missing_options bulkhead: false, ) end - assert_equal \ - exception.message, - "Missing required arguments for Semian: [:success_threshold]" + assert_equal("Missing required arguments for Semian: [:success_threshold]", exception.message) end def test_register_with_thread_safety_enabled @@ -39,8 +39,8 @@ def test_register_with_thread_safety_enabled thread_safety_disabled: false, ) - assert resource, Semian[:testing] - assert resource.circuit_breaker.state.instance_of?(Semian::ThreadSafe::State) + assert_equal(resource, Semian[:testing]) + assert_instance_of(Semian::ThreadSafe::State, resource.circuit_breaker.state) end def test_register_with_thread_safety_disabled @@ -54,22 +54,23 @@ def test_register_with_thread_safety_disabled thread_safety_disabled: true, ) - assert resource, Semian[:testing] - assert resource.circuit_breaker.state.instance_of?(Semian::Simple::State) + assert_equal(resource, Semian[:testing]) + assert_instance_of(Semian::Simple::State, resource.circuit_breaker.state) end def test_register_with_bulkhead_missing_options - exception = assert_raises ArgumentError do + exception = assert_raises(ArgumentError) do Semian.register( :testing, circuit_breaker: false, ) end - assert_equal "Semian configuration require either the :ticket or :quota parameter, you provided neither", exception.message + assert_equal("Semian configuration require either the :ticket or :quota parameter, you provided neither", + exception.message) end def test_register_with_exclusive_options - exception = assert_raises ArgumentError do + exception = assert_raises(ArgumentError) do Semian.register( :testing, tickets: 42, @@ -77,30 +78,31 @@ def test_register_with_exclusive_options circuit_breaker: false, ) end - assert_equal "Semian configuration require either the :ticket or :quota parameter, you provided both", exception.message + assert_equal("Semian configuration require either the :ticket or :quota parameter, you provided both", + exception.message) end def test_unsuported_constants - assert defined?(Semian::BaseError) - assert defined?(Semian::SyscallError) - assert defined?(Semian::TimeoutError) - assert defined?(Semian::InternalError) - assert defined?(Semian::Resource) + assert(defined?(Semian::BaseError)) + assert(defined?(Semian::SyscallError)) + assert(defined?(Semian::TimeoutError)) + assert(defined?(Semian::InternalError)) + assert(defined?(Semian::Resource)) end def test_disabled_via_env_var - ENV['SEMIAN_SEMAPHORES_DISABLED'] = '1' + ENV["SEMIAN_SEMAPHORES_DISABLED"] = "1" - refute Semian.semaphores_enabled? + refute_predicate(Semian, :semaphores_enabled?) ensure - ENV.delete('SEMIAN_SEMAPHORES_DISABLED') + ENV.delete("SEMIAN_SEMAPHORES_DISABLED") end def test_disabled_via_semian_wide_env_var - ENV['SEMIAN_DISABLED'] = '1' + ENV["SEMIAN_DISABLED"] = "1" - refute Semian.semaphores_enabled? + refute_predicate(Semian, :semaphores_enabled?) ensure - ENV.delete('SEMIAN_DISABLED') + ENV.delete("SEMIAN_DISABLED") end end diff --git a/test/simple_integer_test.rb b/test/simple_integer_test.rb index 2df824392..a151ddb8f 100644 --- a/test/simple_integer_test.rb +++ b/test/simple_integer_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class TestSimpleInteger < Minitest::Test def setup @@ -14,7 +16,7 @@ def test_access_value assert_equal(0, @integer.value) @integer.value = 99 assert_equal(99, @integer.value) - time_now = (Time.now).to_i + time_now = Time.now.to_i @integer.value = time_now assert_equal(time_now, @integer.value) @integer.value = 6 diff --git a/test/simple_sliding_window_test.rb b/test/simple_sliding_window_test.rb index 2e10d18e2..bc77bca05 100644 --- a/test/simple_sliding_window_test.rb +++ b/test/simple_sliding_window_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class TestSimpleSlidingWindow < Minitest::Test def setup @@ -25,8 +27,8 @@ def test_sliding_window_edge_falloff end def resize_to_less_than_1_raises - assert_raises ArgumentError do - @sliding_window.resize_to 0 + assert_raises(ArgumentError) do + @sliding_window.resize_to(0) end end diff --git a/test/simple_state_test.rb b/test/simple_state_test.rb index 0da9dd534..4a65b740f 100644 --- a/test/simple_state_test.rb +++ b/test/simple_state_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class TestSimpleEnum < Minitest::Test def setup @@ -11,31 +13,31 @@ def teardown module StateTestCases def test_start_closed? - assert @state.closed? + assert_predicate(@state, :closed?) end def test_open @state.open! - assert @state.open? - assert_equal @state.value, :open + assert_predicate(@state, :open?) + assert_equal(:open, @state.value) end def test_close @state.close! - assert @state.closed? - assert_equal @state.value, :closed + assert_predicate(@state, :closed?) + assert_equal(:closed, @state.value) end def test_half_open @state.half_open! - assert @state.half_open? - assert_equal @state.value, :half_open + assert_predicate(@state, :half_open?) + assert_equal(:half_open, @state.value) end def test_reset @state.reset - assert @state.closed? - assert_equal @state.value, :closed + assert_predicate(@state, :closed?) + assert_equal(:closed, @state.value) end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 2781c4828..9e3181250 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,63 +1,65 @@ -require 'rubygems' -require 'bundler/setup' +# frozen_string_literal: true -require 'minitest/autorun' -require 'mysql2' -require 'semian' -require 'semian/mysql2' -require 'semian/redis' -require 'semian/redis_client' -require 'toxiproxy' -require 'timecop' -require 'tempfile' -require 'fileutils' -require 'byebug' -require 'mocha' -require 'mocha/minitest' +require "rubygems" +require "bundler/setup" -require 'helpers/background_helper' -require 'helpers/circuit_breaker_helper' -require 'helpers/resource_helper' -require 'helpers/adapter_helper' -require 'helpers/mock_server.rb' +require "minitest/autorun" +require "mysql2" +require "semian" +require "semian/mysql2" +require "semian/redis" +require "semian/redis_client" +require "toxiproxy" +require "timecop" +require "tempfile" +require "fileutils" +require "byebug" +require "mocha" +require "mocha/minitest" -require 'config/semian_config' +require "helpers/background_helper" +require "helpers/circuit_breaker_helper" +require "helpers/resource_helper" +require "helpers/adapter_helper" +require "helpers/mock_server.rb" -BIND_ADDRESS = '0.0.0.0' +require "config/semian_config" + +BIND_ADDRESS = "0.0.0.0" Semian.logger = Logger.new(nil, Logger::FATAL) Toxiproxy.host = URI::HTTP.build( - host: SemianConfig['toxiproxy_upstream_host'], - port: SemianConfig['toxiproxy_upstream_port'], + host: SemianConfig["toxiproxy_upstream_host"], + port: SemianConfig["toxiproxy_upstream_port"], ) Toxiproxy.populate([ { - name: 'semian_test_mysql', - upstream: "#{SemianConfig['mysql_host']}:#{SemianConfig['mysql_port']}", - listen: "#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['mysql_toxiproxy_port']}", + name: "semian_test_mysql", + upstream: "#{SemianConfig["mysql_host"]}:#{SemianConfig["mysql_port"]}", + listen: "#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["mysql_toxiproxy_port"]}", }, { - name: 'semian_test_redis', - upstream: "#{SemianConfig['redis_host']}:#{SemianConfig['redis_port']}", - listen: "#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['redis_toxiproxy_port']}", + name: "semian_test_redis", + upstream: "#{SemianConfig["redis_host"]}:#{SemianConfig["redis_port"]}", + listen: "#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["redis_toxiproxy_port"]}", }, { - name: 'semian_test_net_http', - upstream: "#{SemianConfig['http_host']}:#{SemianConfig['http_port_service_a']}", - listen: "#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['http_toxiproxy_port']}", + name: "semian_test_net_http", + upstream: "#{SemianConfig["http_host"]}:#{SemianConfig["http_port_service_a"]}", + listen: "#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["http_toxiproxy_port"]}", }, { - name: 'semian_test_grpc', - upstream: "#{SemianConfig['grpc_host']}:#{SemianConfig['grpc_port']}", - listen: "#{SemianConfig['toxiproxy_upstream_host']}:#{SemianConfig['grpc_toxiproxy_port']}", + name: "semian_test_grpc", + upstream: "#{SemianConfig["grpc_host"]}:#{SemianConfig["grpc_port"]}", + listen: "#{SemianConfig["toxiproxy_upstream_host"]}:#{SemianConfig["grpc_toxiproxy_port"]}", }, ]) servers = [] -servers << MockServer.start(hostname: BIND_ADDRESS, port: SemianConfig['http_port_service_a']) -servers << MockServer.start(hostname: BIND_ADDRESS, port: SemianConfig['http_port_service_b']) +servers << MockServer.start(hostname: BIND_ADDRESS, port: SemianConfig["http_port_service_a"]) +servers << MockServer.start(hostname: BIND_ADDRESS, port: SemianConfig["http_port_service_b"]) Minitest.after_run do servers.each(&:stop) @@ -69,7 +71,9 @@ def setup end end -class Minitest::Test - include CleanupHelper - include BackgroundHelper +module Minitest + class Test + include CleanupHelper + include BackgroundHelper + end end diff --git a/test/unprotected_resource_test.rb b/test/unprotected_resource_test.rb index e8321f300..c85c640d9 100644 --- a/test/unprotected_resource_test.rb +++ b/test/unprotected_resource_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class UnprotectedResourceTest < Minitest::Test def setup @@ -7,11 +9,11 @@ def setup def test_interface_is_the_same diff = Semian::ProtectedResource.public_instance_methods - Semian::UnprotectedResource.public_instance_methods - assert_equal [], diff + assert_empty(diff) end def test_resource_name - assert_equal :foo, @resource.name + assert_equal(:foo, @resource.name) end def test_resource_tickets @@ -19,11 +21,11 @@ def test_resource_tickets end def test_resource_count - assert_equal 0, @resource.count + assert_equal(0, @resource.count) end def test_resource_semid - assert_equal 0, @resource.semid + assert_equal(0, @resource.semid) end def test_resource_reset @@ -39,7 +41,7 @@ def test_resource_acquire @resource.acquire do acquired = true end - assert acquired + assert(acquired) end def test_resource_acquire_with_timeout @@ -47,11 +49,11 @@ def test_resource_acquire_with_timeout @resource.acquire(timeout: 2) do acquired = true end - assert acquired + assert(acquired) end def test_request_is_allowed - assert @resource.request_allowed? + assert_predicate(@resource, :request_allowed?) end def test_mark_failed @@ -59,10 +61,10 @@ def test_mark_failed end def test_bulkhead - assert_nil @resource.bulkhead + assert_nil(@resource.bulkhead) end def test_circuit_breaker - assert_nil @resource.circuit_breaker + assert_nil(@resource.circuit_breaker) end end