Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve specs for Ears class #37

Merged
merged 6 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@

require 'rake/clean'
require 'bundler/gem_tasks'
require 'rubocop'
require 'rubocop/rake_task'
require 'rspec/core/rake_task'
require 'yard'

CLEAN << '.yardoc'
CLOBBER << 'doc'
CLOBBER << 'coverage'
CLOBBER << 'doc' << 'coverage'

RSpec::Core::RakeTask.new(:spec)
YARD::Rake::YardocTask.new { |t| t.stats_options = %w[--list-undoc] }

task default: :spec
RuboCop::RakeTask.new(:rubocop) do |task|
task.formatters = ['simple']
task.fail_on_error = true
end

desc 'Run Prettier'
task(:prettier) { system('npm run lint') }

task default: %i[spec rubocop prettier]
2 changes: 1 addition & 1 deletion lib/ears.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
module Ears
class << self
# The global configuration for Ears.
#
# @attribute [r] configuration
# @return [Ears::Configuration]
def configuration
@configuration ||= Ears::Configuration.new
Expand Down
243 changes: 95 additions & 148 deletions spec/ears_spec.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
require 'ears'
# frozen_string_literal: true

RSpec.describe Ears do
let(:bunny) { instance_double(Bunny::Session) }
let(:channel) { instance_double(Bunny::Channel) }

before do
Ears.reset!
allow(Bunny).to receive(:new).and_return(bunny)
allow(bunny).to receive(:start)
allow(bunny).to receive(:create_channel).and_return(channel)
allow(channel).to receive(:prefetch).with(1)
allow(channel).to receive(:on_uncaught_exception)
end
before { Ears.reset! }

it 'has a version number' do
expect(Ears::VERSION).not_to be_nil
expect(Ears::VERSION).to match(/\A[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+/)
end

it 'has a configuration' do
Expand All @@ -24,200 +14,157 @@
describe '.configure' do
it 'allows setting the configuration values' do
Ears.configure do |config|
config.rabbitmq_url = 'test'
config.connection_name = 'conn'
config.rabbitmq_url = 'amqp://guest:guest@test.local:5672'
config.connection_name = 'conn_name'
config.recover_from_connection_close = true
config.recovery_attempts = 666
end

expect(Ears.configuration.rabbitmq_url).to eq('test')
expect(Ears.configuration).to have_attributes(
rabbitmq_url: 'amqp://guest:guest@test.local:5672',
connection_name: 'conn_name',
recover_from_connection_close: true,
recovery_attempts: 666,
)
end

it 'throws an error if connection name was not set' do
Ears.reset!
it 'yields the Ears configuration' do
Ears.configure do |config|
config.connection_name = 'conn_name'
expect(config).to be Ears.configuration
end
end

expect {
Ears.configure { |config| config.rabbitmq_url = 'test' }
}.to raise_error(Ears::Configuration::ConnectionNameMissing)
it 'throws an error if connection name was not set' do
expect do
Ears.configure { :empty_block }
end.to raise_error Ears::Configuration::ConnectionNameMissing
end
end

describe '.connection' do
let(:rabbitmq_url) { 'amqp://lol:lol@kek.com:15672' }
let(:connection_name) { 'my connection' }
let(:bunny_session) { instance_double(Bunny::Session, start: nil) }

context 'when only mandatory options are set' do
context 'with mandatory configuration' do
before do
Ears.configure do |config|
config.rabbitmq_url = rabbitmq_url
config.connection_name = connection_name
end
end

it 'connects with default config parameters when it is accessed' do
allow(Bunny).to receive(:new).and_return(bunny_session)
Ears.configure { |config| config.connection_name = 'conn_name' }
Ears.connection
end

it 'connects with config parameters' do
expect(Bunny).to have_received(:new).with(
rabbitmq_url,
connection_name: connection_name,
'amqp://guest:guest@localhost:5672',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be set in an Environment class somehow?

connection_name: 'conn_name',
recovery_attempts: 10,
recovery_attempts_exhausted: anything,
) do |_args, kwargs|
proc = kwargs[:recovery_attempts_exhausted]
expect { proc.call }.to raise_error(
Ears::MaxRecoveryAttemptsExhaustedError,
)
end
expect(bunny).to have_received(:start)
)
end
end

context 'with more options' do
let(:recover_from_connection_close) { false }
let(:recovery_attempts) { nil }
it 'starts the connection' do
expect(bunny_session).to have_received(:start)
end
end

context 'with custom configration' do
before do
allow(Bunny).to receive(:new).and_return(bunny_session)
Ears.configure do |config|
config.rabbitmq_url = rabbitmq_url
config.connection_name = connection_name
config.recover_from_connection_close = recover_from_connection_close
config.recovery_attempts = recovery_attempts
config.rabbitmq_url = 'amqp://user:password@rabbitmq:15672'
config.connection_name = 'conn_name'
config.recover_from_connection_close = false
config.recovery_attempts = 9
end
end

it 'connects with config parameters when it is accessed' do
Ears.connection
end

it 'connects with config parameters' do
expect(Bunny).to have_received(:new).with(
rabbitmq_url,
connection_name: connection_name,
recover_from_connection_close: recover_from_connection_close,
'amqp://user:password@rabbitmq:15672',
connection_name: 'conn_name',
recover_from_connection_close: false,
recovery_attempts: 9,
recovery_attempts_exhausted: anything,
)
expect(bunny).to have_received(:start)
end

it 'starts the connection' do
expect(bunny_session).to have_received(:start)
end
end
end

describe '.channel' do
it 'creates a channel when it is accessed' do
expect(bunny).to receive(:create_channel).with(nil, 1, true).and_return(
channel,
)
expect(channel).to receive(:prefetch).with(1)
expect(channel).to receive(:on_uncaught_exception)

Ears.channel
let(:bunny_session) do
instance_double(Bunny::Session, start: nil, create_channel: bunny_channel)
end

it 'stores the channel on the current thread' do
expect(Ears.channel).to eq(Thread.current[:ears_channel])
let(:bunny_channel) do
instance_double(Bunny::Channel, prefetch: nil, on_uncaught_exception: nil)
end
end

describe '.setup' do
let(:exchange) { instance_double(Bunny::Exchange) }
let(:queue) { instance_double(Bunny::Queue, name: 'queue', options: {}) }
let(:consumer_wrapper) { instance_double(Ears::ConsumerWrapper) }
let(:delivery_info) { instance_double(Bunny::DeliveryInfo) }
let(:metadata) { instance_double(Bunny::MessageProperties) }
let(:payload) { 'my payload' }

before do
allow(Bunny::Exchange).to receive(:new).and_return(exchange)
allow(Bunny::Queue).to receive(:new).and_return(queue)
allow(queue).to receive(:bind)
allow(queue).to receive(:subscribe_with)
allow(queue).to receive(:channel).and_return(channel)
allow(Ears::ConsumerWrapper).to receive(:new).and_return(consumer_wrapper)
allow(consumer_wrapper).to receive(:on_delivery).and_yield(
delivery_info,
metadata,
payload,
)
allow(Thread).to receive(:new).and_yield

stub_const('MyConsumer', Class.new(Ears::Consumer))
allow(Bunny).to receive(:new).and_return(bunny_session)
Ears.channel
end

it 'creates a given exchange' do
expect(Bunny::Exchange).to receive(:new).with(
channel,
:topic,
'my-exchange',
{},
)

Ears.setup { exchange('my-exchange', :topic) }
it 'creates a channel when it is accessed' do
expect(bunny_session).to have_received(:create_channel).with(nil, 1, true)
end

it 'creates a queue' do
expect(Bunny::Queue).to receive(:new).with(channel, 'my-queue', {})

Ears.setup do
exchange('my-exchange', :topic)
queue('my-queue')
end
it 'configures the channel prefetch' do
expect(bunny_channel).to have_received(:prefetch).with(1)
end

it 'binds a queue to an exchange' do
expect(queue).to receive(:bind).with(exchange, routing_key: 'test')
it 'configures the channel exception handler' do
expect(bunny_channel).to have_received(:on_uncaught_exception)
end

Ears.setup do
exchange = exchange('my-exchange', :topic)
queue = queue('my-queue')
queue.bind(exchange, routing_key: 'test')
end
it 'stores the channel on the current thread' do
expect(Ears.channel).to eq(Thread.current[:ears_channel])
end
end

it 'starts a consumer subscribed to a queue' do
expect(consumer_wrapper).to receive(:on_delivery).and_yield(
delivery_info,
metadata,
payload,
).ordered
expect(consumer_wrapper).to receive(:process_delivery).with(
delivery_info,
metadata,
payload,
).ordered
expect(queue).to receive(:subscribe_with).with(consumer_wrapper).ordered

Ears.setup do
exchange = exchange('my-exchange', :topic)
queue = queue('my-queue')
queue.bind(exchange, routing_key: 'test')
consumer(queue, MyConsumer)
end
describe '.setup' do
it 'creates a setup helper and executed the given block on this instance' do
instance = :none
Ears.setup { instance = self }
expect(instance).to be_a Ears::Setup
end
end

describe '.stop!' do
before { allow(bunny).to receive(:close) }

it 'stops the connection' do
Ears.stop!

expect(bunny).to have_received(:close)
let(:bunny_session) do
instance_double(
Bunny::Session,
start: nil,
create_channel: bunny_channel,
close: nil,
)
end

it 'resets the connection' do
Ears.connection
let(:bunny_channel) do
instance_double(Bunny::Channel, prefetch: nil, on_uncaught_exception: nil)
end

before do
allow(Bunny).to receive(:new).and_return(bunny_session)
Ears.channel
Ears.stop!
end

Ears.connection
Ears.connection
it 'stops the connection' do
expect(bunny_session).to have_received(:close)
end

it 'forces to create a new connection afterwards' do
Ears.connection
expect(Bunny).to have_received(:new).twice
end

it 'resets the channel' do
Ears.channel

Ears.stop!

it 'forces to create a new channel afterwards' do
Ears.channel
Ears.channel

expect(bunny).to have_received(:create_channel).twice
expect(bunny_session).to have_received(:create_channel).twice
end
end
end
22 changes: 8 additions & 14 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
require 'bundler/setup'
# frozen_string_literal: true

require 'rspec/core'
require 'simplecov'
require 'ears'

SimpleCov.start do
enable_coverage :branch
primary_coverage :branch
end
if RSpec.configuration.files_to_run.length > 1
# Let's increase this later on
SimpleCov.minimum_coverage line: 97.5, branch: 65.7
end

RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = '.rspec_status'
RSpec.configure(&:disable_monkey_patching!)

# Disable RSpec exposing methods globally on `Module` and `main`
config.disable_monkey_patching!

config.expect_with :rspec do |c|
c.syntax = :expect
end
if RSpec.configuration.files_to_run.length > 1
# Let's increase this later on
SimpleCov.minimum_coverage line: 99.4, branch: 100
end