From 9cd313c0f4a1bb4a09df2393977c41992c13f5e8 Mon Sep 17 00:00:00 2001 From: Jan Bajena Date: Sat, 5 May 2018 12:47:30 +0200 Subject: [PATCH 1/2] Execute only first found binding and add message_dispatch tests closes https://github.com/progapandist/rubotnik/issues/6 --- lib/rubotnik/message_dispatch.rb | 2 + lib/rubotnik/user_store.rb | 2 +- rubotnik.gemspec | 2 + spec/factories/message.rb | 14 ++ spec/factories/user.rb | 18 +++ spec/lib/rubotnik/message_dispatch_spec.rb | 169 +++++++++++++++++++++ spec/rubotnik_spec.rb | 4 - spec/spec_helper.rb | 8 + 8 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 spec/factories/message.rb create mode 100644 spec/factories/user.rb create mode 100644 spec/lib/rubotnik/message_dispatch_spec.rb diff --git a/lib/rubotnik/message_dispatch.rb b/lib/rubotnik/message_dispatch.rb index bf31027..cb31b41 100644 --- a/lib/rubotnik/message_dispatch.rb +++ b/lib/rubotnik/message_dispatch.rb @@ -38,6 +38,8 @@ def bind_commands(&block) end def bind(*regex_strings, all: false, to: nil, reply_with: {}) + return if @matched + regexps = regex_strings.map { |rs| /\b#{rs}/i } proceed = regexps.any? { |regex| @message.text =~ regex } proceed = regexps.all? { |regex| @message.text =~ regex } if all diff --git a/lib/rubotnik/user_store.rb b/lib/rubotnik/user_store.rb index f3c6834..1f85373 100644 --- a/lib/rubotnik/user_store.rb +++ b/lib/rubotnik/user_store.rb @@ -4,7 +4,7 @@ class UserStore include Singleton attr_reader :users - + def initialize @users = [] end diff --git a/rubotnik.gemspec b/rubotnik.gemspec index 6c3a2f4..f01e003 100644 --- a/rubotnik.gemspec +++ b/rubotnik.gemspec @@ -30,4 +30,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency "bundler", "~> 1.15" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "factory_bot", "~> 4.0" + spec.add_development_dependency "pry" end diff --git a/spec/factories/message.rb b/spec/factories/message.rb new file mode 100644 index 0000000..92bbd0e --- /dev/null +++ b/spec/factories/message.rb @@ -0,0 +1,14 @@ +FactoryBot.define do + factory :message, class: Facebook::Messenger::Incoming::Message do + skip_create + initialize_with { new(attributes.stringify_keys) } + + transient do + sender_id 1 + text "bla" + end + + sender { { "id" => sender_id } } + message { { "text" => text } } + end +end diff --git a/spec/factories/user.rb b/spec/factories/user.rb new file mode 100644 index 0000000..da0a743 --- /dev/null +++ b/spec/factories/user.rb @@ -0,0 +1,18 @@ +FactoryBot.define do + factory :user, class: User do + skip_create + initialize_with { new(attributes[:id].to_s) } + + sequence(:id) { |n| n } + + trait :with_commands do + transient do + commands { [] } + end + + after(:build) do |user, evaluator| + evaluator.commands.each { |c| user.assign_command(c) } + end + end + end +end diff --git a/spec/lib/rubotnik/message_dispatch_spec.rb b/spec/lib/rubotnik/message_dispatch_spec.rb new file mode 100644 index 0000000..8eba5c3 --- /dev/null +++ b/spec/lib/rubotnik/message_dispatch_spec.rb @@ -0,0 +1,169 @@ +require "spec_helper" + +RSpec.describe Rubotnik::MessageDispatch do + let(:sender_id) { 2 } + let(:message) do + FactoryBot.build(:message, sender_id: sender_id, text: message_text) + end + let(:message_text) { "bla" } + let(:user) do + FactoryBot.build(:user, :with_commands, id: sender_id, commands: commands) + end + let(:commands) { [] } + let(:dispatch) { described_class.new(message) } + let(:commands_module) do + Module.new do + def command1 + end + + def command2 + end + end + end + + before do + allow(UserStore.instance).to receive(:find_or_create_user). + with(sender_id) { user } + dispatch.extend(commands_module) + end + + describe "route" do + context "when user has an active command" do + let(:commands) { [:command1, :command2] } + + it "executes the command" do + expect(dispatch).to receive(:command2) + + dispatch.route + end + + context "when a command raises an error" do + let(:debug) { nil } + let(:commands_module) do + Module.new do + def command2 + raise "err" + end + end + end + + before do + allow(ENV).to receive(:[]).and_wrap_original do |m, *args| + if args[0] == "DEBUG" + debug + else + m.call(*args) + end + end + end + + context "and debug is on" do + let(:debug) { "true" } + + it "shows the error" do + expect(dispatch).to receive(:say) { "There was an error: err" } + + dispatch.route + end + end + + context "and debug is off" do + it "reraises the error" do + expect(dispatch).to receive(:command2).and_call_original + + expect { dispatch.route }.to raise_error("err") + end + end + end + end + + context "with user has no active command" do + context "and message matches a binding with 'all: true'" do + let(:message_text) { "good morning" } + + context "when :to option is given" do + it "executes the command" do + expect(dispatch).to receive(:command2) + + dispatch.route do + bind "Good", "morning", all: true, to: :command2 + end + end + end + + context "when block is given" do + it "executes the command" do + expect(dispatch).to receive(:command1) + + dispatch.route do + bind "Good", "morning", all: true do + command1 + end + end + end + end + end + + context "and message matches a binding without 'all: true'" do + let(:message_text) { "Morning, sir" } + + context "when :to option is given" do + it "executes the command" do + expect(dispatch).to receive(:command2) + + dispatch.route do + bind "Good", "morning", to: :command2 + end + end + end + + context "when block is given" do + it "executes the command" do + expect(dispatch).to receive(:command1) + + dispatch.route do + bind "Good", "morning" do + command1 + end + end + end + end + end + + context "and message matches multiple commands" do + let(:message_text) { "do something" } + + it "executes only the first matched command" do + expect(dispatch).to receive(:command1) + expect(dispatch).not_to receive(:command2) + + dispatch.route do + bind "do", "something", all: true do + command1 + end + + bind "something" do + command2 + end + end + end + end + + context "and message matches no binding" do + it "executes 'default' block" do + expect(dispatch).to receive(:command1) + + dispatch.route do + bind "something" do + command2 + end + + default do + command1 + end + end + end + end + end + end +end diff --git a/spec/rubotnik_spec.rb b/spec/rubotnik_spec.rb index d166bf3..701021a 100644 --- a/spec/rubotnik_spec.rb +++ b/spec/rubotnik_spec.rb @@ -4,8 +4,4 @@ it "has a version number" do expect(Rubotnik::VERSION).not_to be nil end - - it "does something useful" do - expect(false).to eq(true) - end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c655e32..62c82ff 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,7 @@ require "bundler/setup" require "rubotnik" +require "factory_bot" +require "pry" RSpec.configure do |config| # Enable flags like --only-failures and --next-failure @@ -8,4 +10,10 @@ config.expect_with :rspec do |c| c.syntax = :expect end + + config.include FactoryBot::Syntax::Methods + + config.before(:suite) do + FactoryBot.find_definitions + end end From 5d1c17af8d90870bc547fee6e48aa67712bf1d78 Mon Sep 17 00:00:00 2001 From: Jan Bajena Date: Sat, 5 May 2018 12:54:11 +0200 Subject: [PATCH 2/2] Fix hound issues --- spec/factories/message.rb | 8 +- spec/factories/user.rb | 2 + spec/lib/rubotnik/message_dispatch_spec.rb | 95 +++++++++++----------- spec/spec_helper.rb | 8 +- 4 files changed, 59 insertions(+), 54 deletions(-) diff --git a/spec/factories/message.rb b/spec/factories/message.rb index 92bbd0e..3a08282 100644 --- a/spec/factories/message.rb +++ b/spec/factories/message.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :message, class: Facebook::Messenger::Incoming::Message do skip_create @@ -5,10 +7,10 @@ transient do sender_id 1 - text "bla" + text 'bla' end - sender { { "id" => sender_id } } - message { { "text" => text } } + sender { { 'id' => sender_id } } + message { { 'text' => text } } end end diff --git a/spec/factories/user.rb b/spec/factories/user.rb index da0a743..77f8a57 100644 --- a/spec/factories/user.rb +++ b/spec/factories/user.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :user, class: User do skip_create diff --git a/spec/lib/rubotnik/message_dispatch_spec.rb b/spec/lib/rubotnik/message_dispatch_spec.rb index 8eba5c3..d1c62e9 100644 --- a/spec/lib/rubotnik/message_dispatch_spec.rb +++ b/spec/lib/rubotnik/message_dispatch_spec.rb @@ -1,11 +1,13 @@ -require "spec_helper" +# frozen_string_literal: true + +require 'spec_helper' RSpec.describe Rubotnik::MessageDispatch do let(:sender_id) { 2 } let(:message) do FactoryBot.build(:message, sender_id: sender_id, text: message_text) end - let(:message_text) { "bla" } + let(:message_text) { 'bla' } let(:user) do FactoryBot.build(:user, :with_commands, id: sender_id, commands: commands) end @@ -13,43 +15,42 @@ let(:dispatch) { described_class.new(message) } let(:commands_module) do Module.new do - def command1 - end + def command1;end - def command2 - end + def command2;end end end before do - allow(UserStore.instance).to receive(:find_or_create_user). - with(sender_id) { user } + allow(UserStore.instance) + .to receive(:find_or_create_user) + .with(sender_id) { user } dispatch.extend(commands_module) end - describe "route" do - context "when user has an active command" do - let(:commands) { [:command1, :command2] } + describe 'route' do + context 'when user has an active command' do + let(:commands) { %i[command1 command2] } - it "executes the command" do + it 'executes the command' do expect(dispatch).to receive(:command2) dispatch.route end - context "when a command raises an error" do + context 'when a command raises an error' do let(:debug) { nil } let(:commands_module) do Module.new do def command2 - raise "err" + raise 'err' end end end before do allow(ENV).to receive(:[]).and_wrap_original do |m, *args| - if args[0] == "DEBUG" + if args[0] == 'DEBUG' debug else m.call(*args) @@ -57,46 +58,46 @@ def command2 end end - context "and debug is on" do - let(:debug) { "true" } + context 'and debug is on' do + let(:debug) { 'true' } - it "shows the error" do - expect(dispatch).to receive(:say) { "There was an error: err" } + it 'shows the error' do + expect(dispatch).to receive(:say) { 'There was an error: err' } dispatch.route end end - context "and debug is off" do - it "reraises the error" do + context 'and debug is off' do + it 'reraises the error' do expect(dispatch).to receive(:command2).and_call_original - expect { dispatch.route }.to raise_error("err") + expect { dispatch.route }.to raise_error('err') end end end end - context "with user has no active command" do - context "and message matches a binding with 'all: true'" do - let(:message_text) { "good morning" } + context 'with user has no active command' do + context 'and message matches a binding with \'all: true\'' do + let(:message_text) { 'good morning' } - context "when :to option is given" do - it "executes the command" do + context 'when :to option is given' do + it 'executes the command' do expect(dispatch).to receive(:command2) dispatch.route do - bind "Good", "morning", all: true, to: :command2 + bind 'Good', 'morning', all: true, to: :command2 end end end - context "when block is given" do - it "executes the command" do + context 'when block is given' do + it 'executes the command' do expect(dispatch).to receive(:command1) dispatch.route do - bind "Good", "morning", all: true do + bind 'Good', 'morning', all: true do command1 end end @@ -104,25 +105,25 @@ def command2 end end - context "and message matches a binding without 'all: true'" do - let(:message_text) { "Morning, sir" } + context 'and message matches a binding without \'all: true\'' do + let(:message_text) { 'Morning, sir' } - context "when :to option is given" do - it "executes the command" do + context 'when :to option is given' do + it 'executes the command' do expect(dispatch).to receive(:command2) dispatch.route do - bind "Good", "morning", to: :command2 + bind 'Good', 'morning', to: :command2 end end end - context "when block is given" do - it "executes the command" do + context 'when block is given' do + it 'executes the command' do expect(dispatch).to receive(:command1) dispatch.route do - bind "Good", "morning" do + bind 'Good', 'morning' do command1 end end @@ -130,31 +131,31 @@ def command2 end end - context "and message matches multiple commands" do - let(:message_text) { "do something" } + context 'and message matches multiple commands' do + let(:message_text) { 'do something' } - it "executes only the first matched command" do + it 'executes only the first matched command' do expect(dispatch).to receive(:command1) expect(dispatch).not_to receive(:command2) dispatch.route do - bind "do", "something", all: true do + bind 'do', 'something', all: true do command1 end - bind "something" do + bind 'something' do command2 end end end end - context "and message matches no binding" do - it "executes 'default' block" do + context 'and message matches no binding' do + it 'executes \'default\' block' do expect(dispatch).to receive(:command1) dispatch.route do - bind "something" do + bind 'something' do command2 end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 62c82ff..944816a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,7 @@ -require "bundler/setup" -require "rubotnik" -require "factory_bot" -require "pry" +require 'bundler/setup' +require 'rubotnik' +require 'factory_bot' +require 'pry' RSpec.configure do |config| # Enable flags like --only-failures and --next-failure