From 45f73243d0546f900785b251a41eea84fd0a5928 Mon Sep 17 00:00:00 2001 From: Leo Amigood Date: Wed, 8 Mar 2017 14:38:46 -0500 Subject: [PATCH 1/2] Initial Puma server setup --- Gemfile | 2 ++ Gemfile.lock | 7 ++++++- config/puma.rb | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 config/puma.rb diff --git a/Gemfile b/Gemfile index cc25094..cb623e3 100644 --- a/Gemfile +++ b/Gemfile @@ -31,6 +31,8 @@ gem 'sdoc', '~> 0.4.0', group: :doc # Use Unicorn as the app server # gem 'unicorn' +gem 'puma' +gem 'puma-heroku' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development diff --git a/Gemfile.lock b/Gemfile.lock index f8972e7..4e435d1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -113,6 +113,9 @@ GEM nokogiri (1.7.0.1) mini_portile2 (~> 2.1.0) pg (0.19.0) + puma (3.7.1) + puma-heroku (1.0.0) + puma (~> 3.0) rack (1.6.5) rack-cors (0.4.1) rack-test (0.6.3) @@ -226,6 +229,8 @@ DEPENDENCIES jquery-rails kaminari pg + puma + puma-heroku rack-cors rails (= 4.2.5) rails-api @@ -240,4 +245,4 @@ DEPENDENCIES web-console (~> 2.0) BUNDLED WITH - 1.14.4 + 1.14.6 diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000..ef9450b --- /dev/null +++ b/config/puma.rb @@ -0,0 +1 @@ +plugin "heroku" From 23da33101246b84cb9dc0c5c0dc3f71ed615ed41 Mon Sep 17 00:00:00 2001 From: Leo Amigood Date: Thu, 9 Mar 2017 10:19:41 -0500 Subject: [PATCH 2/2] Using turnpike as command queue delegate --- .env.backend | 3 +- .env.postgres.test | 2 +- Gemfile | 1 + Gemfile.lock | 20 ++-- .../telegram/{command => action}/best.rb | 4 +- .../{command/action.rb => action/command.rb} | 4 +- .../telegram/{command => action}/create.rb | 25 ++++- .../telegram/{command => action}/guess.rb | 4 +- .../telegram/{command => action}/hint.rb | 4 +- .../telegram/{command => action}/language.rb | 24 ++++- .../telegram/{command => action}/level.rb | 24 ++++- app/services/telegram/action/start.rb | 33 ++++++ .../telegram/{command => action}/stop.rb | 4 +- .../telegram/{command => action}/suggest.rb | 4 +- .../telegram/{command => action}/tries.rb | 4 +- .../telegram/{command => action}/zero.rb | 4 +- app/services/telegram/command/start.rb | 30 ------ app/services/telegram/command_queue.rb | 71 ------------ app/services/telegram/command_queue/exec.rb | 30 ++++++ app/services/telegram/command_queue/queue.rb | 64 +++++++++++ .../command_queue/turnpike_delegate.rb | 53 +++++++++ app/services/telegram/command_route.rb | 34 +++--- app/services/telegram/validator.rb | 2 +- app/services/telegram_dispatcher.rb | 46 ++++---- config/database.yml | 2 +- docker-compose.ci.yml | 9 +- docker-compose.yml | 18 +++- .../hooks/telegram_controller_spec.rb | 53 +++++---- spec/services/telegram/command/create_spec.rb | 13 +-- spec/services/telegram/command/guess_spec.rb | 4 +- spec/services/telegram/command/hint_spec.rb | 6 +- .../telegram/command_queue/queue_spec.rb | 101 ++++++++++++++++++ .../command_queue/turnpike_delegate_spec.rb | 101 ++++++++++++++++++ spec/services/telegram/command_queue_spec.rb | 99 ----------------- spec/services/telegram/validator_spec.rb | 4 +- spec/services/telegram_dispatcher_spec.rb | 34 +++--- 36 files changed, 600 insertions(+), 338 deletions(-) rename app/services/telegram/{command => action}/best.rb (80%) rename app/services/telegram/{command/action.rb => action/command.rb} (90%) rename app/services/telegram/{command => action}/create.rb (64%) rename app/services/telegram/{command => action}/guess.rb (85%) rename app/services/telegram/{command => action}/hint.rb (89%) rename app/services/telegram/{command => action}/language.rb (57%) rename app/services/telegram/{command => action}/level.rb (50%) create mode 100644 app/services/telegram/action/start.rb rename app/services/telegram/{command => action}/stop.rb (84%) rename app/services/telegram/{command => action}/suggest.rb (87%) rename app/services/telegram/{command => action}/tries.rb (82%) rename app/services/telegram/{command => action}/zero.rb (82%) delete mode 100644 app/services/telegram/command/start.rb delete mode 100644 app/services/telegram/command_queue.rb create mode 100644 app/services/telegram/command_queue/exec.rb create mode 100644 app/services/telegram/command_queue/queue.rb create mode 100644 app/services/telegram/command_queue/turnpike_delegate.rb create mode 100644 spec/services/telegram/command_queue/queue_spec.rb create mode 100644 spec/services/telegram/command_queue/turnpike_delegate_spec.rb delete mode 100644 spec/services/telegram/command_queue_spec.rb diff --git a/.env.backend b/.env.backend index ac79e4d..b9fdfc3 100644 --- a/.env.backend +++ b/.env.backend @@ -1,5 +1,6 @@ PORT=3000 -RAILS_ENV=development DB_HOST=postgres DB_PORT=5432 + +REDIS_URL=redis://redis:6379/0 diff --git a/.env.postgres.test b/.env.postgres.test index 79fffce..31a0e5f 100644 --- a/.env.postgres.test +++ b/.env.postgres.test @@ -1,2 +1,2 @@ -POSTGRES_USER=sa +POSTGRES_USER=postgres POSTGRES_PASSWORD=q_7qrNVD5tRMV diff --git a/Gemfile b/Gemfile index cb623e3..f1f69a9 100644 --- a/Gemfile +++ b/Gemfile @@ -44,6 +44,7 @@ gem 'kaminari' gem 'api-pagination' gem 'attr_extras' gem 'aspector' +gem 'turnpike' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console diff --git a/Gemfile.lock b/Gemfile.lock index 4e435d1..1b1fb7d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -59,7 +59,7 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.0.4) + concurrent-ruby (1.0.5) debug_inspector (0.0.2) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) @@ -79,8 +79,8 @@ GEM activesupport (>= 4.1.0) i18n (0.8.1) ice_nine (0.11.2) - jbuilder (2.6.2) - activesupport (>= 3.0.0, < 5.1) + jbuilder (2.6.3) + activesupport (>= 3.0.0, < 5.2) multi_json (~> 1.2) jquery-rails (4.2.2) rails-dom-testing (>= 1, < 3) @@ -108,12 +108,13 @@ GEM mime-types-data (3.2016.0521) mini_portile2 (2.1.0) minitest (5.10.1) + msgpack (1.1.0) multi_json (1.12.1) multipart-post (2.0.0) nokogiri (1.7.0.1) mini_portile2 (~> 2.1.0) pg (0.19.0) - puma (3.7.1) + puma (3.8.0) puma-heroku (1.0.0) puma (~> 3.0) rack (1.6.5) @@ -131,7 +132,7 @@ GEM bundler (>= 1.3.0, < 2.0) railties (= 4.2.5) sprockets-rails - rails-api (0.4.0) + rails-api (0.4.1) actionpack (>= 3.2.11) railties (>= 3.2.11) rails-deprecated_sanitizer (1.0.3) @@ -149,6 +150,7 @@ GEM thor (>= 0.18.1, < 2.0) rake (12.0.0) rdoc (4.3.0) + redis (3.3.3) rspec-core (3.5.4) rspec-support (~> 3.5.0) rspec-expectations (3.5.0) @@ -194,14 +196,17 @@ GEM faraday virtus thor (0.19.4) - thread_safe (0.3.5) + thread_safe (0.3.6) tilt (2.0.6) turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.0) + turnpike (0.8.0) + msgpack (~> 1.0) + redis (~> 3.0) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (3.0.4) + uglifier (3.1.6) execjs (>= 0.3.0, < 3) virtus (1.0.5) axiom-types (~> 0.1) @@ -241,6 +246,7 @@ DEPENDENCIES spring telegram-bot-ruby turbolinks + turnpike uglifier (>= 1.3.0) web-console (~> 2.0) diff --git a/app/services/telegram/command/best.rb b/app/services/telegram/action/best.rb similarity index 80% rename from app/services/telegram/command/best.rb rename to app/services/telegram/action/best.rb index 337ce51..556840a 100644 --- a/app/services/telegram/command/best.rb +++ b/app/services/telegram/action/best.rb @@ -1,7 +1,7 @@ require 'aspector' module Telegram - module Command + module Action class Best class << self @@ -16,7 +16,7 @@ def execute(channel, limit) target do def permit(*args, &block) channel, message = *args - Telegram::Validator.validate!(Telegram::Command::Action::BEST, channel, message) + Telegram::Validator.validate!(Telegram::Action::Command::BEST, channel, message) end end diff --git a/app/services/telegram/command/action.rb b/app/services/telegram/action/command.rb similarity index 90% rename from app/services/telegram/command/action.rb rename to app/services/telegram/action/command.rb index 9fec214..dfe3583 100644 --- a/app/services/telegram/command/action.rb +++ b/app/services/telegram/action/command.rb @@ -1,6 +1,6 @@ module Telegram - module Command - module Action + module Action + module Command BEST = '/best' CREATE = '/create' GUESS = '/guess' diff --git a/app/services/telegram/command/create.rb b/app/services/telegram/action/create.rb similarity index 64% rename from app/services/telegram/command/create.rb rename to app/services/telegram/action/create.rb index 3cf3a4f..9afe630 100644 --- a/app/services/telegram/command/create.rb +++ b/app/services/telegram/action/create.rb @@ -1,7 +1,7 @@ require 'aspector' module Telegram - module Command + module Action class Create class << self @@ -10,9 +10,6 @@ def ask(channel) end def execute(channel, message, options) - return unless Telegram::CommandQueue.assert(self) - - Telegram::CommandQueue.clear case options[:strategy] when :by_word game = GameEngineService.create_by_word(Realm::Telegram.new(channel, message.from.id), options[:word]) @@ -25,18 +22,36 @@ def execute(channel, message, options) end TelegramMessenger.game_created(game) end + + def self? + "proc { |cls| cls == #{self.name} }" + end end end aspector(Create, class_methods: true) do target do + def assert(*args, &block) + channel, message = *args + Telegram::CommandQueue::Queue.new(channel).assert(self) + end + def permit(*args, &block) channel, message = *args - Telegram::Validator.validate!(Action::CREATE, channel, message) + Telegram::Validator.validate!(Command::CREATE, channel, message) + end + + def pop(*args, &block) + msg, channel, message = *args + Telegram::CommandQueue::Queue.new(channel).pop + + msg end end + before_filter :execute, :assert before :ask, :execute, :permit + after :execute, :pop end end end diff --git a/app/services/telegram/command/guess.rb b/app/services/telegram/action/guess.rb similarity index 85% rename from app/services/telegram/command/guess.rb rename to app/services/telegram/action/guess.rb index 40637f8..9b4e192 100644 --- a/app/services/telegram/command/guess.rb +++ b/app/services/telegram/action/guess.rb @@ -1,7 +1,7 @@ require 'aspector' module Telegram - module Command + module Action class Guess class << self @@ -17,7 +17,7 @@ def execute(channel, message, word) target do def permit(*args, &block) channel, message = *args - Telegram::Validator.validate!(Action::GUESS, channel, message) + Telegram::Validator.validate!(Command::GUESS, channel, message) end end diff --git a/app/services/telegram/command/hint.rb b/app/services/telegram/action/hint.rb similarity index 89% rename from app/services/telegram/command/hint.rb rename to app/services/telegram/action/hint.rb index 8b5dd56..e76eb63 100644 --- a/app/services/telegram/command/hint.rb +++ b/app/services/telegram/action/hint.rb @@ -1,7 +1,7 @@ require 'aspector' module Telegram - module Command + module Action class Hint class << self @@ -25,7 +25,7 @@ def execute(channel, options) target do def permit(*args, &block) channel, message = *args - Telegram::Validator.validate!(Action::HINT, channel, message) + Telegram::Validator.validate!(Command::HINT, channel, message) end end diff --git a/app/services/telegram/command/language.rb b/app/services/telegram/action/language.rb similarity index 57% rename from app/services/telegram/command/language.rb rename to app/services/telegram/action/language.rb index b1417cd..ad168db 100644 --- a/app/services/telegram/command/language.rb +++ b/app/services/telegram/action/language.rb @@ -1,7 +1,7 @@ require 'aspector' module Telegram - module Command + module Action class Language class << self @@ -10,26 +10,42 @@ def ask(channel) end def execute(channel, language) - return unless Telegram::CommandQueue.assert(self) - language = GameEngineService.language(language.upcase) dictionary = Dictionary.where(lang: language).order('RANDOM()').first GameEngineService.settings(channel, {dictionary_id: dictionary.id, language: language}) TelegramMessenger.language(language) end + + def self? + "proc { |cls| cls == #{self.name} }" + end end end aspector(Language, class_methods: true) do target do + def assert(*args, &block) + channel, message = *args + Telegram::CommandQueue::Queue.new(channel).assert(self) + end + def permit(*args, &block) channel, message = *args - Telegram::Validator.validate!(Action::LANG, channel, message) + Telegram::Validator.validate!(Command::LANG, channel, message) + end + + def pop(*args, &block) + msg, channel, message = *args + Telegram::CommandQueue::Queue.new(channel).pop + + msg end end + before_filter :execute, :assert before :ask, :execute, :permit + after :execute, :pop end end end diff --git a/app/services/telegram/command/level.rb b/app/services/telegram/action/level.rb similarity index 50% rename from app/services/telegram/command/level.rb rename to app/services/telegram/action/level.rb index 40b63ab..d66b389 100644 --- a/app/services/telegram/command/level.rb +++ b/app/services/telegram/action/level.rb @@ -1,7 +1,7 @@ require 'aspector' module Telegram - module Command + module Action class Level class << self @@ -10,23 +10,39 @@ def ask(channel) end def execute(channel, level) - return unless Telegram::CommandQueue.assert(self) - GameEngineService.settings(channel, { complexity: level }) TelegramMessenger.level(level) end + + def self? + "proc { |cls| cls == #{self.name} }" + end end end aspector(Level, class_methods: true) do target do + def assert(*args, &block) + channel, message = *args + Telegram::CommandQueue::Queue.new(channel).assert(self) + end + def permit(*args, &block) channel, message = *args - Telegram::Validator.validate!(Action::LEVEL, channel, message) + Telegram::Validator.validate!(Command::LEVEL, channel, message) + end + + def pop(*args, &block) + msg, channel, message = *args + Telegram::CommandQueue::Queue.new(channel).pop + + msg end end + before_filter :execute, :assert before :ask, :execute, :permit + after :execute, :pop end end end diff --git a/app/services/telegram/action/start.rb b/app/services/telegram/action/start.rb new file mode 100644 index 0000000..36cb5bb --- /dev/null +++ b/app/services/telegram/action/start.rb @@ -0,0 +1,33 @@ +require 'aspector' + +module Telegram + module Action + + class Start + class << self + def execute(channel) + TelegramMessenger.welcome(channel) + + queue = Telegram::CommandQueue::Queue.new(channel).reset + + ask_language = Telegram::CommandQueue::Exec.new('TelegramMessenger.ask_language', channel, Telegram::Action::Language.self?) + ask_level = Telegram::CommandQueue::Exec.new('TelegramMessenger.ask_level', channel, Telegram::Action::Level.self?) + ask_length = Telegram::CommandQueue::Exec.new('TelegramMessenger.ask_length', channel, Telegram::Action::Create.self?) + + queue.push(ask_language, ask_level, ask_length) + end + end + end + + aspector(Start, class_methods: true) do + target do + def permit(*args, &block) + channel, message = *args + Telegram::Validator.validate!(Command::START, channel, message) + end + end + + before :execute, :permit + end + end +end diff --git a/app/services/telegram/command/stop.rb b/app/services/telegram/action/stop.rb similarity index 84% rename from app/services/telegram/command/stop.rb rename to app/services/telegram/action/stop.rb index f3070c4..352cb42 100644 --- a/app/services/telegram/command/stop.rb +++ b/app/services/telegram/action/stop.rb @@ -1,7 +1,7 @@ require 'aspector' module Telegram - module Command + module Action class Stop class << self @@ -17,7 +17,7 @@ def execute(channel, message) target do def permit(*args, &block) channel, message = *args - Telegram::Validator.validate!(Action::STOP, channel, message) + Telegram::Validator.validate!(Command::STOP, channel, message) end end diff --git a/app/services/telegram/command/suggest.rb b/app/services/telegram/action/suggest.rb similarity index 87% rename from app/services/telegram/command/suggest.rb rename to app/services/telegram/action/suggest.rb index e2612ec..660e828 100644 --- a/app/services/telegram/command/suggest.rb +++ b/app/services/telegram/action/suggest.rb @@ -1,7 +1,7 @@ require 'aspector' module Telegram - module Command + module Action class Suggest class << self @@ -18,7 +18,7 @@ def execute(channel, message, letters) target do def permit(*args, &block) channel, message = *args - Telegram::Validator.validate!(Action::SUGGEST, channel, message) + Telegram::Validator.validate!(Command::SUGGEST, channel, message) end end diff --git a/app/services/telegram/command/tries.rb b/app/services/telegram/action/tries.rb similarity index 82% rename from app/services/telegram/command/tries.rb rename to app/services/telegram/action/tries.rb index b006121..6d018d9 100644 --- a/app/services/telegram/command/tries.rb +++ b/app/services/telegram/action/tries.rb @@ -1,7 +1,7 @@ require 'aspector' module Telegram - module Command + module Action class Tries class << self @@ -16,7 +16,7 @@ def execute(channel) target do def permit(*args, &block) channel, message = *args - Telegram::Validator.validate!(Action::TRIES, channel, message) + Telegram::Validator.validate!(Command::TRIES, channel, message) end end diff --git a/app/services/telegram/command/zero.rb b/app/services/telegram/action/zero.rb similarity index 82% rename from app/services/telegram/command/zero.rb rename to app/services/telegram/action/zero.rb index 8a66831..83c1c06 100644 --- a/app/services/telegram/command/zero.rb +++ b/app/services/telegram/action/zero.rb @@ -1,7 +1,7 @@ require 'aspector' module Telegram - module Command + module Action class Zero class << self @@ -16,7 +16,7 @@ def execute(channel) target do def permit(*args, &block) channel, message = *args - Telegram::Validator.validate!(Action::ZERO, channel, message) + Telegram::Validator.validate!(Command::ZERO, channel, message) end end diff --git a/app/services/telegram/command/start.rb b/app/services/telegram/command/start.rb deleted file mode 100644 index a700044..0000000 --- a/app/services/telegram/command/start.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'aspector' - -module Telegram - module Command - - class Start - class << self - def execute(channel) - Telegram::CommandQueue.clear - TelegramMessenger.welcome(channel) - - Telegram::CommandQueue.push(Proc.new{ |c| c == Telegram::Command::Language }) { TelegramMessenger.ask_language(channel) } - Telegram::CommandQueue.push(Proc.new{ |c| c == Telegram::Command::Level }) { TelegramMessenger.ask_level(channel) } - Telegram::CommandQueue.push(Proc.new{ |c| c == Telegram::Command::Create }) { TelegramMessenger.ask_length(channel) } - end - end - end - - aspector(Start, class_methods: true) do - target do - def permit(*args, &block) - channel, message = *args - Telegram::Validator.validate!(Action::START, channel, message) - end - end - - before :execute, :permit - end - end -end diff --git a/app/services/telegram/command_queue.rb b/app/services/telegram/command_queue.rb deleted file mode 100644 index 483ab8c..0000000 --- a/app/services/telegram/command_queue.rb +++ /dev/null @@ -1,71 +0,0 @@ -module Telegram - - class CommandBlock < Proc - attr_accessor :callback - - def initialize(callback, &command) - @callback = callback - super(&command) - end - end - - class CommandQueue - @queue = [] - @asserted = true - - class << self - def push(callback = nil, &command) - @queue.push CommandBlock.new(callback, &command) - self - end - - def assert(cls) - return true if empty? - - @asserted = !!(peek.callback.try(:call, cls) && shift) - end - - def asserted? - @asserted - end - - def execute - return unless present? && asserted? - - if peek.callback.present? - @asserted = false - peek.call - else - shift.call - end - end - - def size - @queue.size - end - - def clear - @asserted = true - @queue.clear - end - - def empty? - @queue.empty? - end - - def present? - @queue.present? - end - - private - - def peek - @queue.first - end - - def shift - @queue.shift - end - end - end -end diff --git a/app/services/telegram/command_queue/exec.rb b/app/services/telegram/command_queue/exec.rb new file mode 100644 index 0000000..9996d4e --- /dev/null +++ b/app/services/telegram/command_queue/exec.rb @@ -0,0 +1,30 @@ +module Telegram + + module CommandQueue + class Exec + attr_accessor :command, :args, :callback + + def initialize(command, args, callback = nil) + @command = command + @args = args + @callback = callback + end + + def call + eval "#{@command}(#{@args})" + end + + # called upon redis push message serialization + def to_msgpack(packer) + packer.write([self.class.name, @command, *@args, @callback]) + end + + def ==(other) + self.command == other.command && self.args == other.args && self.callback == other.callback + end + end + end + +end + + diff --git a/app/services/telegram/command_queue/queue.rb b/app/services/telegram/command_queue/queue.rb new file mode 100644 index 0000000..7a543b0 --- /dev/null +++ b/app/services/telegram/command_queue/queue.rb @@ -0,0 +1,64 @@ +module Telegram + + module CommandQueue + class Queue + def initialize(channel, delegate = TurnpikeDelegate) + @channel = channel + @delegate = delegate.new(channel) + end + + def push(*commands) + @delegate.push(*commands) + self + end + + def assert(cls) + return true if empty? + + peek.callback.try(:call, cls) + end + + def pop + @delegate.pop + end + + def execute + return if empty? + + if peek.callback.present? + peek.call + else + pop.call + end + end + + def size + @delegate.size + end + + def clear + @delegate.clear + end + + def reset + @delegate.clear + self + end + + def present? + not empty? + end + + def empty? + @delegate.empty? + end + + private + + def peek + @delegate.peek + end + end + end + +end diff --git a/app/services/telegram/command_queue/turnpike_delegate.rb b/app/services/telegram/command_queue/turnpike_delegate.rb new file mode 100644 index 0000000..7a13349 --- /dev/null +++ b/app/services/telegram/command_queue/turnpike_delegate.rb @@ -0,0 +1,53 @@ +require 'turnpike' + +module Telegram + + module CommandQueue + class TurnpikeDelegate + def initialize(channel) + @queue = Turnpike.call(channel) + end + + def push(*elements) + @queue.push(*elements) + end + + def size + @queue.size + end + + def empty? + @queue.size == 0 + end + + def present? + @queue.size > 0 + end + + def pop + unpack(@queue.pop) + end + + def peek + element = @queue.pop + @queue.unshift(element) if element.present? + + unpack(element) + end + + def clear + @queue.pop(@queue.size) + end + + private + + def unpack(msg) + return unless msg.present? + + classname, command, args, callback = *msg + Object::const_get(classname).new(command, args, eval(callback.to_s)) + end + end + end + +end diff --git a/app/services/telegram/command_route.rb b/app/services/telegram/command_route.rb index 2235437..b88b291 100644 --- a/app/services/telegram/command_route.rb +++ b/app/services/telegram/command_route.rb @@ -2,24 +2,24 @@ module Telegram class CommandRoute BOT_REGEXP = '(?:@\w+)?' - BEST = /^#{Telegram::Command::Action::BEST}#{BOT_REGEXP}\s*(?[[:digit:]]+)?$/i - CREATE = /^#{Telegram::Command::Action::CREATE}#{BOT_REGEXP}$/i - CREATE_ALPHA = /^#{Telegram::Command::Action::CREATE}#{BOT_REGEXP}\s+(?[[:alpha:]]+)$/i - CREATE_DIGIT = /^#{Telegram::Command::Action::CREATE}#{BOT_REGEXP}\s+(?[[:digit:]]+)$/i - GUESS = /^#{Telegram::Command::Action::GUESS}#{BOT_REGEXP}\s+(?[[:alpha:]]+)$/i + BEST = /^#{Telegram::Action::Command::BEST}#{BOT_REGEXP}\s*(?[[:digit:]]+)?$/i + CREATE = /^#{Telegram::Action::Command::CREATE}#{BOT_REGEXP}$/i + CREATE_ALPHA = /^#{Telegram::Action::Command::CREATE}#{BOT_REGEXP}\s+(?[[:alpha:]]+)$/i + CREATE_DIGIT = /^#{Telegram::Action::Command::CREATE}#{BOT_REGEXP}\s+(?[[:digit:]]+)$/i + GUESS = /^#{Telegram::Action::Command::GUESS}#{BOT_REGEXP}\s+(?[[:alpha:]]+)$/i WORD = /\A(?[[:alpha:]]+)\z/im - HELP = /^#{Telegram::Command::Action::HELP}#{BOT_REGEXP}$/i - HINT_ALPHA = /^#{Telegram::Command::Action::HINT}#{BOT_REGEXP}\s*(?[[:alpha:]])?$/i - HINT_DIGIT = /^#{Telegram::Command::Action::HINT}#{BOT_REGEXP}\s+(?[[:digit:]])$/i - LANG = /^#{Telegram::Command::Action::LANG}#{BOT_REGEXP}$/i - LANG_ALPHA = /^#{Telegram::Command::Action::LANG}#{BOT_REGEXP}\s+(?[[:alpha:]]+)$/i - LEVEL = /^#{Telegram::Command::Action::LEVEL}#{BOT_REGEXP}$/i - LEVEL_ALPHA = /^#{Telegram::Command::Action::LEVEL}#{BOT_REGEXP}\s+(?[[:alpha:]]+)$/i - START = /^#{Telegram::Command::Action::START}#{BOT_REGEXP}$/i - STOP = /^#{Telegram::Command::Action::STOP}#{BOT_REGEXP}$/i - SUGGEST = /^#{Telegram::Command::Action::SUGGEST}#{BOT_REGEXP}\s*(?[[:alpha:]]+)?$/i - TRIES = /^#{Telegram::Command::Action::TRIES}#{BOT_REGEXP}$/i - ZERO = /^#{Telegram::Command::Action::ZERO}#{BOT_REGEXP}$/i + HELP = /^#{Telegram::Action::Command::HELP}#{BOT_REGEXP}$/i + HINT_ALPHA = /^#{Telegram::Action::Command::HINT}#{BOT_REGEXP}\s*(?[[:alpha:]])?$/i + HINT_DIGIT = /^#{Telegram::Action::Command::HINT}#{BOT_REGEXP}\s+(?[[:digit:]])$/i + LANG = /^#{Telegram::Action::Command::LANG}#{BOT_REGEXP}$/i + LANG_ALPHA = /^#{Telegram::Action::Command::LANG}#{BOT_REGEXP}\s+(?[[:alpha:]]+)$/i + LEVEL = /^#{Telegram::Action::Command::LEVEL}#{BOT_REGEXP}$/i + LEVEL_ALPHA = /^#{Telegram::Action::Command::LEVEL}#{BOT_REGEXP}\s+(?[[:alpha:]]+)$/i + START = /^#{Telegram::Action::Command::START}#{BOT_REGEXP}$/i + STOP = /^#{Telegram::Action::Command::STOP}#{BOT_REGEXP}$/i + SUGGEST = /^#{Telegram::Action::Command::SUGGEST}#{BOT_REGEXP}\s*(?[[:alpha:]]+)?$/i + TRIES = /^#{Telegram::Action::Command::TRIES}#{BOT_REGEXP}$/i + ZERO = /^#{Telegram::Action::Command::ZERO}#{BOT_REGEXP}$/i OTHER = /^\/.*/ end end diff --git a/app/services/telegram/validator.rb b/app/services/telegram/validator.rb index 9b2a4c3..11fb62f 100644 --- a/app/services/telegram/validator.rb +++ b/app/services/telegram/validator.rb @@ -2,7 +2,7 @@ module Telegram class Validator class << self - include Telegram::Command::Action + include Telegram::Action::Command def validate!(action, channel, message) game = GameService.recent_game(channel) diff --git a/app/services/telegram_dispatcher.rb b/app/services/telegram_dispatcher.rb index 706ee72..085e39d 100644 --- a/app/services/telegram_dispatcher.rb +++ b/app/services/telegram_dispatcher.rb @@ -18,7 +18,7 @@ def update(update) def handle(message) begin command = message.text.mb_chars.downcase.to_s - execute(command, message.chat.id, message) + execute(command, channel = message.chat.id, message) rescue => ex ex.message end @@ -26,12 +26,14 @@ def handle(message) def handle_callback_query(callback_query) begin + channel = callback_query.message.chat.id command = callback_query.data.downcase.to_s - response = execute(command, callback_query.message.chat.id, callback_query) + response = execute(command, channel, callback_query) - if Telegram::CommandQueue.present? + queue = Telegram::CommandQueue::Queue.new(channel) + if queue.present? TelegramMessenger.answerCallbackQuery(callback_query.id, response) - Telegram::CommandQueue.execute + queue.execute if response.present? else response end @@ -43,56 +45,56 @@ def handle_callback_query(callback_query) def execute(command, channel, message) case command when Telegram::CommandRoute::START - Telegram::Command::Start.execute(channel) - Telegram::CommandQueue.execute + Telegram::Action::Start.execute(channel) + Telegram::CommandQueue::Queue.new(channel).execute when Telegram::CommandRoute::LANG - Telegram::Command::Language.ask(channel) + Telegram::Action::Language.ask(channel) when Telegram::CommandRoute::LANG_ALPHA - Telegram::Command::Language.execute(channel, $~['language']) + Telegram::Action::Language.execute(channel, $~['language']) when Telegram::CommandRoute::CREATE - Telegram::Command::Create.ask(channel) + Telegram::Action::Create.ask(channel) when Telegram::CommandRoute::CREATE_ALPHA - Telegram::Command::Create.execute(channel, message, word: $~['secret'], strategy: :by_word) + Telegram::Action::Create.execute(channel, message, word: $~['secret'], strategy: :by_word) when Telegram::CommandRoute::CREATE_DIGIT - Telegram::Command::Create.execute(channel, message, length: $~['number'], strategy: :by_number) + Telegram::Action::Create.execute(channel, message, length: $~['number'], strategy: :by_number) when Telegram::CommandRoute::GUESS - Telegram::Command::Guess.execute(channel, message, $~['guess']) + Telegram::Action::Guess.execute(channel, message, $~['guess']) when Telegram::CommandRoute::WORD - Telegram::Command::Guess.execute(channel, message, command) if GameService.in_progress?(channel) + Telegram::Action::Guess.execute(channel, message, command) if GameService.in_progress?(channel) when Telegram::CommandRoute::HINT_ALPHA - Telegram::Command::Hint.execute(channel, letter: $~['letter'], strategy: :by_letter) + Telegram::Action::Hint.execute(channel, letter: $~['letter'], strategy: :by_letter) when Telegram::CommandRoute::HINT_DIGIT - Telegram::Command::Hint.execute(channel, number: $~['number'], strategy: :by_number) + Telegram::Action::Hint.execute(channel, number: $~['number'], strategy: :by_number) when Telegram::CommandRoute::SUGGEST - Telegram::Command::Suggest.execute(channel, message, $~['letters']) + Telegram::Action::Suggest.execute(channel, message, $~['letters']) when Telegram::CommandRoute::TRIES - Telegram::Command::Tries.execute(channel) + Telegram::Action::Tries.execute(channel) when Telegram::CommandRoute::BEST - Telegram::Command::Best.execute(channel, $~['best']) + Telegram::Action::Best.execute(channel, $~['best']) when Telegram::CommandRoute::ZERO - Telegram::Command::Zero.execute(channel) + Telegram::Action::Zero.execute(channel) when Telegram::CommandRoute::LEVEL - Telegram::Command::Level.ask(channel) + Telegram::Action::Level.ask(channel) when Telegram::CommandRoute::LEVEL_ALPHA - Telegram::Command::Level.execute(channel, $~['level']) + Telegram::Action::Level.execute(channel, $~['level']) when Telegram::CommandRoute::STOP - Telegram::Command::Stop.execute(channel, message) + Telegram::Action::Stop.execute(channel, message) when Telegram::CommandRoute::HELP TelegramMessenger.help diff --git a/config/database.yml b/config/database.yml index c723352..5841d64 100644 --- a/config/database.yml +++ b/config/database.yml @@ -13,7 +13,7 @@ default: &default development: <<: *default database: bulls_cows_development - username: sa + username: <%= ENV['POSTGRES_USER'] %> password: <%= ENV['POSTGRES_PASSWORD'] %> pool: 5 host: <%= ENV['DB_HOST'] %> diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 5ecf123..bf589c1 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -6,20 +6,25 @@ services: - "15432:5432" env_file: - '.env.postgres.test' - + redis: + image: redis:3.2 + ports: + - "16379:6379" rspec: build: . volumes: - .:/u/apps/bullsandcows links: - postgres + - redis depends_on: - postgres + - redis working_dir: /u/apps/bullsandcows environment: RAILS_ENV: test env_file: - - '.env.postgres.test' - '.env.backend' - '.env.backend.test' + - '.env.postgres.test' command: bash -c "sleep 5 && rake db:setup && bundle exec rspec -fd" diff --git a/docker-compose.yml b/docker-compose.yml index dcbf45a..65e2da6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,22 +5,30 @@ services: volumes: - .:/u/apps/bullsandcows ports: - - "13000:3000" + - "3000:3000" links: - postgres + - redis depends_on: - postgres + - redis working_dir: /u/apps/bullsandcows + environment: + RAILS_ENV: development env_file: - - '.env.postgres.secret' - '.env.backend' - '.env.backend.secret' + - '.env.postgres.secret' postgres: image: postgres:9.5.3 ports: - - "15432:5432" + - "5432:5432" env_file: - '.env.postgres.secret' + redis: + image: redis:3.2 + ports: + - "6379:6379" rspec: build: . @@ -28,13 +36,15 @@ services: - .:/u/apps/bullsandcows links: - postgres + - redis depends_on: - postgres + - redis working_dir: /u/apps/bullsandcows environment: RAILS_ENV: test env_file: - - '.env.postgres.secret' - '.env.backend' - '.env.backend.test' + - '.env.postgres.secret' command: bundle exec rspec -fd diff --git a/spec/controllers/hooks/telegram_controller_spec.rb b/spec/controllers/hooks/telegram_controller_spec.rb index 0e430c4..a7ef2d2 100644 --- a/spec/controllers/hooks/telegram_controller_spec.rb +++ b/spec/controllers/hooks/telegram_controller_spec.rb @@ -1,11 +1,16 @@ require 'rails_helper' describe Hooks::TelegramController, :type => :request do + let!(:chat_id) { 169778030 } + let!(:queue) { Telegram::CommandQueue::Queue.new(chat_id) } + + after do + queue.clear + end context 'when receives telegram message with /start command' do before do allow(TelegramMessenger).to receive(:send_message) - allow(Telegram::CommandQueue).to receive(:push) end let!(:command) { @@ -14,12 +19,12 @@ 'message' => { 'message_id' => 214, 'from' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0' }, 'chat' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0', 'type' => 'private' @@ -35,12 +40,12 @@ 'message' => { 'message_id' => 214, 'from' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0' }, 'chat' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0', 'type' => 'private' @@ -54,16 +59,16 @@ } it 'responds with game start' do - post "/hooks/telegram/#{ENV['TELEGRAM_WEBHOOK']}", command + expect { + post "/hooks/telegram/#{ENV['TELEGRAM_WEBHOOK']}", command + }.to change(queue, :size).by(3) expect(response).to be_success expect(response).to have_http_status(200) - expect(Telegram::CommandQueue).to have_received(:push).thrice - expect(json).to be expect(json['text']).to eq('') - expect(json['chat_id']).to eq(169778030) + expect(json['chat_id']).to eq(chat_id) expect(json['parse_mode']).to eq('Markdown') expect(json['method']).to eq('sendMessage') end @@ -76,12 +81,12 @@ 'message' => { 'message_id' => 117, 'from' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0' }, 'chat' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0', 'type' => 'private' @@ -97,12 +102,12 @@ 'message' => { 'message_id' => 117, 'from' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0' }, 'chat' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0', 'type' => 'private' @@ -123,15 +128,19 @@ expect(json).to be expect(json['text']).to match /Here is the list of available commands/ - expect(json['chat_id']).to eq(169778030) + expect(json['chat_id']).to eq(chat_id) expect(json['parse_mode']).to eq('Markdown') expect(json['method']).to eq('sendMessage') end end context 'given dictionary with word levels' do - before(:each) do + let!(:ask_level) { Telegram::CommandQueue::Exec.new('TelegramMessenger.ask_level', chat_id, Telegram::Action::Level.self?) } + + before do allow(TelegramMessenger).to receive(:answerCallbackQuery) + + queue.push(ask_level) end let!(:dictionary) { create :dictionary, :english, lang: 'RU'} @@ -143,7 +152,7 @@ 'callback_query' => { 'id' => '729191086489033331', 'from' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0' }, @@ -155,7 +164,7 @@ 'username' => 'bullsandcowswordsbot' }, 'chat' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0', 'type' => 'private' @@ -173,7 +182,7 @@ 'callback_query' => { 'id' => '729191086489033331', 'from' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0' }, @@ -185,7 +194,7 @@ 'username' => 'bullsandcowswordsbot' }, 'chat' => { - 'id' => 169778030, + 'id' => chat_id, 'first_name' => 'Leo', 'username' => 'Amig0', 'type' => 'private' @@ -201,14 +210,16 @@ } it 'creates a game with selected level' do - post "/hooks/telegram/#{ENV['TELEGRAM_WEBHOOK']}", command + expect { + post "/hooks/telegram/#{ENV['TELEGRAM_WEBHOOK']}", command + }.to change(queue, :size).by(-1) expect(response).to be_success expect(response).to have_http_status(200) expect(json).to be expect(json['text']).to eq('Game level was set to easy.') - expect(json['chat_id']).to eq(169778030) + expect(json['chat_id']).to eq(chat_id) expect(json['parse_mode']).to eq('Markdown') expect(json['method']).to eq('sendMessage') end diff --git a/spec/services/telegram/command/create_spec.rb b/spec/services/telegram/command/create_spec.rb index b78046c..784e541 100644 --- a/spec/services/telegram/command/create_spec.rb +++ b/spec/services/telegram/command/create_spec.rb @@ -1,12 +1,14 @@ require 'rails_helper' -describe Telegram::Command::Create, type: :service do +describe Telegram::Action::Create, type: :service do let!(:channel) { Random.rand(@MAX_INT_VALUE) } let!(:user) { User.new(id = Random.rand(@MAX_INT_VALUE), name = '@Amig0') } let!(:message) { Telegram::Bot::Types::Message.new(text: '/create') } before do + allow_any_instance_of(Telegram::CommandQueue::Queue).to receive(:pop) + message.stub_chain(:chat, :id).and_return(channel) message.stub_chain(:from, :id).and_return(user.id) end @@ -15,14 +17,13 @@ let!(:settings) { create :setting, channel: channel, complexity: 'hard', language: 'RU'} before do - allow(Telegram::CommandQueue).to receive(:clear) allow(TelegramMessenger).to receive(:game_created) end it 'verifies create by word execution chain' do - Telegram::Command::Create.execute(channel, message, word: 'secret', strategy: :by_word) + expect_any_instance_of(Telegram::CommandQueue::Queue).to receive(:pop).with(no_args) + Telegram::Action::Create.execute(channel, message, word: 'secret', strategy: :by_word) - expect(Telegram::CommandQueue).to have_received(:clear).with(no_args) expect(TelegramMessenger).to have_received(:game_created).with( have_attributes(status: 'created', secret: 'secret') ) @@ -33,9 +34,9 @@ let!(:hard) { create :dictionary_level, :hard_ru } it 'creates game with specifies word length' do - Telegram::Command::Create.execute(channel, message, length: 8, strategy: :by_number) + expect_any_instance_of(Telegram::CommandQueue::Queue).to receive(:pop).with(no_args) + Telegram::Action::Create.execute(channel, message, length: 8, strategy: :by_number) - expect(Telegram::CommandQueue).to have_received(:clear).with(no_args) expect(TelegramMessenger).to have_received(:game_created).with( have_attributes(status: 'created').and have_attributes(secret: satisfy{ |s| s.length == 8 }) ) diff --git a/spec/services/telegram/command/guess_spec.rb b/spec/services/telegram/command/guess_spec.rb index e6ace5d..3ecb06d 100644 --- a/spec/services/telegram/command/guess_spec.rb +++ b/spec/services/telegram/command/guess_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Telegram::Command::Guess, type: :service do +describe Telegram::Action::Guess, type: :service do let!(:user) { User.new(id = Random.rand, name = '@Amig0') } let!(:channel) { 'telegram-game-channel' } @@ -20,7 +20,7 @@ end it 'verifies guess execution chain' do - Telegram::Command::Guess.execute(channel, message, message.text) + Telegram::Action::Guess.execute(channel, message, message.text) expect(TelegramMessenger).to have_received(:guess).with( have_attributes(game_id: game.id, word: 'hostel', username: '@Amig0') diff --git a/spec/services/telegram/command/hint_spec.rb b/spec/services/telegram/command/hint_spec.rb index bb19a5a..02c423f 100644 --- a/spec/services/telegram/command/hint_spec.rb +++ b/spec/services/telegram/command/hint_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Telegram::Command::Hint, type: :service do +describe Telegram::Action::Hint, type: :service do let!(:channel) { Random.rand(@MAX_INT_VALUE) } let!(:user) { User.new(Random.rand(@MAX_INT_VALUE), '@Amig0') } @@ -16,7 +16,7 @@ let!(:letter) { 'r' } it 'verifies hint by letter execution chain' do - Telegram::Command::Hint.execute(channel, letter: 'r', strategy: :by_letter) + Telegram::Action::Hint.execute(channel, letter: 'r', strategy: :by_letter) expect(GameService).to have_received(:find_by_channel!).with(channel) expect(TelegramMessenger).to have_received(:hint).with(letter) @@ -24,7 +24,7 @@ let!(:number) { 4 } it 'verifies hint by number execution chain' do - Telegram::Command::Hint.execute(channel, number: number, strategy: :by_number) + Telegram::Action::Hint.execute(channel, number: number, strategy: :by_number) expect(GameService).to have_received(:find_by_channel!).with(channel) expect(TelegramMessenger).to have_received(:hint).with(letter) diff --git a/spec/services/telegram/command_queue/queue_spec.rb b/spec/services/telegram/command_queue/queue_spec.rb new file mode 100644 index 0000000..a4da707 --- /dev/null +++ b/spec/services/telegram/command_queue/queue_spec.rb @@ -0,0 +1,101 @@ +require 'rails_helper' + +describe Telegram::CommandQueue::Queue, type: :service do + let!(:channel) { Random.rand(@MAX_INT_VALUE) } + let!(:queue) { Telegram::CommandQueue::Queue.new(channel) } + + before do + allow(TelegramMessenger).to receive(:ask_level) + end + + after do + queue.clear + end + + context 'given empty queue' do + it 'verify queue size' do + expect(queue.size).to eq(0) + end + + it 'verify successful assertion' do + expect(queue.assert(self)).to be true + end + + it 'verify queue empty state' do + expect(queue.empty?).to be true + end + + it 'verify queue present state' do + expect(queue.present?).to be false + end + + it 'verify clear succeeds' do + expect(queue.clear).to eq([]) + end + + it 'verify reset succeeds' do + expect(queue.reset).to eq(queue) + end + + context 'given a command' do + let!(:command) { Telegram::CommandQueue::Exec.new('TelegramMessenger.ask_level', channel) } + + it 'verify push command increments queue size' do + expect{ + queue.push(command) + }.to change(queue, :size).by(1) + end + end + end + + context 'given one command with assertion in a queue' do + let!(:command) { Telegram::CommandQueue::Exec.new('TelegramMessenger.ask_level', channel, Telegram::Action::Level.self?) } + + before do + allow(GameEngineService).to receive(:settings) + allow(TelegramMessenger).to receive(:level) + + queue.push(command) + end + + it 'verify queue size' do + expect(queue.size).to eq(1) + end + + it 'verify queue not empty state' do + expect(queue.empty?).to be false + end + + it 'verify queue present state' do + expect(queue.present?).to be true + end + + context 'when command gets executed' do + before do + expect(queue.execute).to be_nil + expect(TelegramMessenger).to have_received(:ask_level).with(channel) + end + + it 'verify command remains in a queue' do + expect{ + expect(queue.empty?).to be false + }.not_to change(queue, :size) + end + + it 'verify command /language not executed and stays in the queue' do + expect{ + Telegram::Action::Language.execute(channel, 'RU') + expect(GameEngineService).not_to have_received(:settings) + }.not_to change(queue, :size) + end + + it 'verify command /level gets executed and removed from the queue' do + expect{ + Telegram::Action::Level.execute(channel, 'easy') + expect(GameEngineService).to have_received(:settings) + }.to change(queue, :size).by(-1) + end + end + end + +end diff --git a/spec/services/telegram/command_queue/turnpike_delegate_spec.rb b/spec/services/telegram/command_queue/turnpike_delegate_spec.rb new file mode 100644 index 0000000..53c338f --- /dev/null +++ b/spec/services/telegram/command_queue/turnpike_delegate_spec.rb @@ -0,0 +1,101 @@ +require 'rails_helper' + +describe Telegram::CommandQueue::TurnpikeDelegate, type: :service do + let!(:channel) { Random.rand(@MAX_INT_VALUE) } + let!(:queue) { Telegram::CommandQueue::TurnpikeDelegate.new(channel) } + + before do + queue.clear + end + + after do + queue.clear + end + + context 'given empty queue' do + it 'verify queue size' do + expect(queue.size).to eq(0) + end + + it 'verify queue empty state' do + expect(queue.empty?).to be true + end + + it 'verify clear succeeds' do + expect(queue.clear).to eq([]) + end + + it 'verify push element adds one element' do + expect{ + queue.push('data') + }.to change(queue, :size).by(1) + end + + it 'verify push array adds one element' do + expect{ + queue.push(%w(one two)) + }.to change(queue, :size).by(1) + end + + it 'verify push multiple elements adds multiple elements' do + expect{ + queue.push('one', 'two') + }.to change(queue, :size).by(2) + end + end + + context 'given one one item in a queue' do + let!(:command1) { Telegram::CommandQueue::Exec.new('String.new', 'first item') } + + before do + queue.push(command1) + end + + it 'verify queue size' do + expect(queue.size).to eq(1) + end + + it 'verify queue not empty state' do + expect(queue.empty?).to be false + end + + it 'verify first item to be popped next' do + expect(queue.peek).to eq(command1) + end + + it 'verify popped item' do + expect(queue.pop).to eq(command1) + end + + it 'verify queue is empty after item was popped' do + expect(queue.pop).to eq(command1) + expect(queue.size).to eq(0) + expect(queue.empty?).to be true + end + + it 'verify there is only one item can be popped' do + expect(queue.pop).to eq(command1) + expect(queue.pop).to be_nil + end + + it 'verify queue can hold multiple same items' do + expect{ + queue.push(command1) + }.to change(queue, :size).by(1) + end + + let!(:command2) { Telegram::CommandQueue::Exec.new('String.new', 'first item') } + let!(:command3) { Telegram::CommandQueue::Exec.new('String.new', 'first item') } + it 'verify queue order with multiple push and pop' do + expect{ + queue.push(command2) + queue.push(command3) + + expect(queue.pop).to eq(command1) + expect(queue.pop).to eq(command2) + expect(queue.pop).to eq(command3) + }.to change(queue, :size).by(-1) + end + end + +end diff --git a/spec/services/telegram/command_queue_spec.rb b/spec/services/telegram/command_queue_spec.rb deleted file mode 100644 index 892a7bb..0000000 --- a/spec/services/telegram/command_queue_spec.rb +++ /dev/null @@ -1,99 +0,0 @@ -require 'rails_helper' - -describe Telegram::CommandQueue, type: :service do - let!(:channel) { Random.rand(@MAX_INT_VALUE) } - - before do - allow(TelegramMessenger).to receive(:ask_level) - end - - context 'given empty queue' do - after do - Telegram::CommandQueue.clear - end - - it 'verify queue size' do - expect(Telegram::CommandQueue.size).to eq(0) - end - - it 'verify successful assertion' do - expect(Telegram::CommandQueue.assert(self)).to be true - end - - it 'verify queue empty state' do - expect(Telegram::CommandQueue.empty?).to be true - end - - it 'verify clear succeeds' do - expect(Telegram::CommandQueue.clear).to eq([]) - end - - it 'verify not present state' do - expect(Telegram::CommandQueue.present?).to eq false - end - - it 'verify not present state' do - expect(Telegram::CommandQueue.present?).to eq false - end - - it 'verify push increments queue size' do - expect{ - Telegram::CommandQueue.push{ TelegramMessenger.ask_level(channel) } - }.to change(Telegram::CommandQueue, :size).by(1) - end - end - - context 'given one command with assertion in a queue' do - before do - allow(GameEngineService).to receive(:settings) - allow(TelegramMessenger).to receive(:level) - - Telegram::CommandQueue.push(Proc.new{|cls| cls == Telegram::Command::Level }) { 'block' + ' to execute' } - end - - after do - Telegram::CommandQueue.clear - end - - it 'verify queue size' do - expect(Telegram::CommandQueue.size).to eq(1) - end - - it 'verify queue not empty state' do - expect(Telegram::CommandQueue.empty?).to be false - end - - it 'verify queue present state' do - expect(Telegram::CommandQueue.present?).to be true - end - - context 'when command gets executed' do - before do - expect(Telegram::CommandQueue.execute).to eq('block to execute') - end - - it 'verify command to remain in a queue' do - expect{ - expect(Telegram::CommandQueue.present?).to be true - }.not_to change(Telegram::CommandQueue, :size) - end - - it 'verify command /language fails expected callback assertion' do - expect{ - Telegram::Command::Language.execute(channel, 'RU') - expect(Telegram::CommandQueue.asserted?).to eq(false) - expect(GameEngineService).not_to have_received(:settings) - }.not_to change(Telegram::CommandQueue, :size) - end - - it 'verify command /level executes with successful callback and assertion' do - expect{ - Telegram::Command::Level.execute(channel, 'easy') - expect(Telegram::CommandQueue.asserted?).to eq(true) - expect(GameEngineService).to have_received(:settings) - }.to change(Telegram::CommandQueue, :size).by(-1) - end - end - end - -end diff --git a/spec/services/telegram/validator_spec.rb b/spec/services/telegram/validator_spec.rb index d600a7f..475188e 100644 --- a/spec/services/telegram/validator_spec.rb +++ b/spec/services/telegram/validator_spec.rb @@ -1,5 +1,5 @@ require 'rails_helper' -include Telegram::Command::Action +include Telegram::Action::Command describe Telegram::Validator, type: :service do let!(:channel) { Random.rand(@MAX_INT_VALUE) } @@ -75,7 +75,7 @@ it 'allows player permission to stop the game' do expect{ - Telegram::Validator.validate!(Telegram::Command::Stop, realm.channel, message) + Telegram::Validator.validate!(Telegram::Action::Stop, realm.channel, message) }.not_to raise_error end end diff --git a/spec/services/telegram_dispatcher_spec.rb b/spec/services/telegram_dispatcher_spec.rb index 249542d..0a644b9 100644 --- a/spec/services/telegram_dispatcher_spec.rb +++ b/spec/services/telegram_dispatcher_spec.rb @@ -20,10 +20,6 @@ message.stub_chain(:chat, :id).and_return(channel) end - after do - Telegram::CommandQueue.clear - end - let(:markup) { instance_of(Telegram::Bot::Types::InlineKeyboardMarkup) } it 'replies with a welcome text' do @@ -31,7 +27,7 @@ expect(TelegramDispatcher.handle(message)).to be expect(TelegramMessenger).to have_received(:send_message).with(channel, /Welcome to Bulls and Cows!/).once expect(TelegramMessenger).to have_received(:send_message).with(channel, 'Select game language:', markup).once - }.to change(Telegram::CommandQueue, :size).by(3) + }.to change(Telegram::CommandQueue::Queue.new(channel), :size).by(3) end end @@ -155,8 +151,8 @@ context 'NOT being last command in command queue' do before do - allow(Telegram::CommandQueue).to receive(:present?).and_return(true) - allow(Telegram::CommandQueue).to receive(:execute) + allow_any_instance_of(Telegram::CommandQueue::Queue).to receive(:present?).and_return(true) + allow_any_instance_of(Telegram::CommandQueue::Queue).to receive(:execute) end it 'creates setting with selected game level and response with inline message' do @@ -212,8 +208,8 @@ context 'NOT being last command in command queue' do before do - allow(Telegram::CommandQueue).to receive(:present?).and_return(true) - allow(Telegram::CommandQueue).to receive(:execute) + allow_any_instance_of(Telegram::CommandQueue::Queue).to receive(:present?).and_return(true) + allow_any_instance_of(Telegram::CommandQueue::Queue).to receive(:execute) end it 'creates setting with selected game language and response with status message' do @@ -229,14 +225,14 @@ let!(:message) { Telegram::Bot::Types::Message.new(text: '/tries') } before do - allow(Telegram::Command::Tries).to receive(:execute) + allow(Telegram::Action::Tries).to receive(:execute) message.stub_chain(:chat, :id).and_return(channel) end it 'errors out with message about no recent games available' do expect { expect(TelegramDispatcher.handle(message)).to include('No recent game to show _/tries_ guesses on.') - expect(Telegram::Command::Tries).not_to have_received(:execute) + expect(Telegram::Action::Tries).not_to have_received(:execute) }.not_to change(Guess, :count) end end @@ -245,14 +241,14 @@ let!(:message) { Telegram::Bot::Types::Message.new(text: '/best') } before do - allow(Telegram::Command::Best).to receive(:execute) + allow(Telegram::Action::Best).to receive(:execute) message.stub_chain(:chat, :id).and_return(channel) end it 'errors out with message about no recent games available' do expect { expect(TelegramDispatcher.handle(message)).to include('No recent game to show _/best_ guesses on.') - expect(Telegram::Command::Best).not_to have_received(:execute) + expect(Telegram::Action::Best).not_to have_received(:execute) }.not_to change(Guess, :count) end end @@ -261,14 +257,14 @@ let!(:message) { Telegram::Bot::Types::Message.new(text: '/zero') } before do - allow(Telegram::Command::Zero).to receive(:execute) + allow(Telegram::Action::Zero).to receive(:execute) message.stub_chain(:chat, :id).and_return(channel) end it 'errors out with message about no recent games available' do expect { expect(TelegramDispatcher.handle(message)).to include('No recent game to show _/zero_ guesses on.') - expect(Telegram::Command::Zero).not_to have_received(:execute) + expect(Telegram::Action::Zero).not_to have_received(:execute) }.not_to change(Guess, :count) end end @@ -313,14 +309,14 @@ let!(:message) { Telegram::Bot::Types::Message.new(text: '/lang') } before do - allow(Telegram::Command::Language).to receive(:execute) + allow(Telegram::Action::Language).to receive(:execute) message.stub_chain(:chat, :id).and_return(channel) end it 'errors out with message about permissions' do expect { expect(TelegramDispatcher.handle(message)).to include('You are NOT allowed to change game language.') - expect(Telegram::Command::Language).not_to have_received(:execute) + expect(Telegram::Action::Language).not_to have_received(:execute) }.not_to change(Game, :count) end end @@ -329,14 +325,14 @@ let!(:message) { Telegram::Bot::Types::Message.new(text: '/level') } before do - allow(Telegram::Command::Level).to receive(:execute) + allow(Telegram::Action::Level).to receive(:execute) message.stub_chain(:chat, :id).and_return(channel) end it 'errors out with message about permissions' do expect { expect(TelegramDispatcher.handle(message)).to include('You are NOT allowed to change game level.') - expect(Telegram::Command::Level).not_to have_received(:execute) + expect(Telegram::Action::Level).not_to have_received(:execute) }.not_to change(Game, :count) end end