From 1a0cd63f09334d0226cfd215445765347e5a66cb Mon Sep 17 00:00:00 2001 From: James Jeffers Date: Sat, 9 Nov 2024 07:58:45 -0500 Subject: [PATCH 1/2] Rails, rspec, et al, gem version update, plus additional app:upgrade files. Repairs specs for upgrade failures. --- Gemfile | 28 +- Gemfile.lock | 224 ++++---- app/controllers/web_requests_controller.rb | 30 +- bin/rails | 7 +- bin/rake | 5 - bin/setup | 33 ++ config/application.rb | 40 +- config/environment.rb | 2 +- config/environments/test.rb | 5 +- spec/concerns/long_runnable_spec.rb | 66 +-- .../google_calendar_publish_agent_spec.rb | 141 ++--- spec/models/agents/human_task_agent_spec.rb | 491 ++++++++++-------- spec/models/agents/imap_folder_agent_spec.rb | 139 ++--- spec/rails_helper.rb | 19 +- spec/spec_helper.rb | 94 ++++ 15 files changed, 724 insertions(+), 600 deletions(-) create mode 100755 bin/setup create mode 100644 spec/spec_helper.rb diff --git a/Gemfile b/Gemfile index 641ea567c6..a3684f0b07 100644 --- a/Gemfile +++ b/Gemfile @@ -29,13 +29,13 @@ end # Optional libraries. To conserve RAM, comment out any that you don't need, # then run `bundle` and commit the updated Gemfile and Gemfile.lock. gem 'erector', github: 'dsander/erector', branch: 'rails6' -gem 'pirate_weather_forecast_ruby' # WeatherAgent -gem 'hipchat', '~> 1.6.0' # HipchatAgent +gem 'hipchat', '~> 1.6.0' # HipchatAgent gem 'hypdf', bitbucket: 'knu/hypdf_gem', branch: 'uploadio_namespace' # PDFInfoAgent -gem 'mini_racer' # JavaScriptAgent -gem 'mqtt' # MQTTAgent +gem 'mini_racer' # JavaScriptAgent +gem 'mqtt' # MQTTAgent gem 'net-ftp' -gem 'net-ftp-list' # FtpsiteAgent +gem 'net-ftp-list' # FtpsiteAgent +gem 'pirate_weather_forecast_ruby' # WeatherAgent gem 'rturk', '~> 2.12.1' # HumanTaskAgent gem 'slack-notifier', '~> 2.4.0' # SlackAgent gem 'twilio-ruby', '~> 7.3.6' # TwilioAgent @@ -119,11 +119,11 @@ gem 'kramdown' gem 'liquid', '~> 5.5', '>= 5.5.1' gem 'loofah', '~> 2.23', '>= 2.23.1' gem 'mail', '>= 2.8.1' -gem 'mini_magick', ">= 5.0.1" +gem 'mini_magick', '>= 5.0.1' gem 'multi_xml' -gem "nokogiri", ">= 1.16.7" +gem 'nokogiri', '>= 1.16.7' gem 'omniauth' -gem 'rails', '~> 6.1.7', '>= 6.1.7.10' +gem 'rails', '7.0.1' gem 'rails-html-sanitizer', '~> 1.6' gem 'rufus-scheduler', '~> 3.9', '>= 3.9.2', require: false gem 'sassc-rails' @@ -192,14 +192,14 @@ require 'rbconfig' gem 'ffi', '>= 1.17.0' # required by typhoeus; 1.9.4 has fixes for *BSD. gem 'tzinfo', '>= 2.0.6' # required by rails; 1.2.0 has support for *BSD and Solaris. # Windows does not have zoneinfo files, so bundle the tzinfo-data gem. -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw] +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw] # BSD systems require rb-kqueue for "listen" to avoid polling for changes. gem 'rb-kqueue', '>= 0.2.8', require: /bsd|dragonfly/i === RbConfig::CONFIG['target_os'] on_heroku = ENV['ON_HEROKU'] || - ENV['HEROKU_POSTGRESQL_ROSE_URL'] || - ENV['HEROKU_POSTGRESQL_GOLD_URL'] || - File.read(File.join(File.dirname(__FILE__), 'Procfile')) =~ /intended for Heroku/ + ENV['HEROKU_POSTGRESQL_ROSE_URL'] || + ENV['HEROKU_POSTGRESQL_GOLD_URL'] || + File.read(File.join(File.dirname(__FILE__), 'Procfile')) =~ /intended for Heroku/ ENV['DATABASE_ADAPTER'] ||= if on_heroku @@ -213,9 +213,9 @@ if_true(ENV['DATABASE_ADAPTER'].strip == 'postgresql') do end if_true(ENV['DATABASE_ADAPTER'].strip == 'mysql2') do - gem 'mysql2', "~> 0.5", ">= 0.5.6" + gem 'mysql2', '~> 0.5', '>= 0.5.6' end GemfileHelper.parse_each_agent_gem(ENV['ADDITIONAL_GEMS']) do |args| - gem *args + gem(*args) end diff --git a/Gemfile.lock b/Gemfile.lock index b3039eaaeb..39c18e96ba 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -101,65 +101,71 @@ GEM remote: https://rubygems.org/ specs: ace-rails-ap (4.5) - actioncable (6.1.7.10) - actionpack (= 6.1.7.10) - activesupport (= 6.1.7.10) + actioncable (7.0.1) + actionpack (= 7.0.1) + activesupport (= 7.0.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.1.7.10) - actionpack (= 6.1.7.10) - activejob (= 6.1.7.10) - activerecord (= 6.1.7.10) - activestorage (= 6.1.7.10) - activesupport (= 6.1.7.10) + actionmailbox (7.0.1) + actionpack (= 7.0.1) + activejob (= 7.0.1) + activerecord (= 7.0.1) + activestorage (= 7.0.1) + activesupport (= 7.0.1) mail (>= 2.7.1) - actionmailer (6.1.7.10) - actionpack (= 6.1.7.10) - actionview (= 6.1.7.10) - activejob (= 6.1.7.10) - activesupport (= 6.1.7.10) + net-imap + net-pop + net-smtp + actionmailer (7.0.1) + actionpack (= 7.0.1) + actionview (= 7.0.1) + activejob (= 7.0.1) + activesupport (= 7.0.1) mail (~> 2.5, >= 2.5.4) + net-imap + net-pop + net-smtp rails-dom-testing (~> 2.0) - actionpack (6.1.7.10) - actionview (= 6.1.7.10) - activesupport (= 6.1.7.10) - rack (~> 2.0, >= 2.0.9) + actionpack (7.0.1) + actionview (= 7.0.1) + activesupport (= 7.0.1) + rack (~> 2.0, >= 2.2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.1.7.10) - actionpack (= 6.1.7.10) - activerecord (= 6.1.7.10) - activestorage (= 6.1.7.10) - activesupport (= 6.1.7.10) + actiontext (7.0.1) + actionpack (= 7.0.1) + activerecord (= 7.0.1) + activestorage (= 7.0.1) + activesupport (= 7.0.1) + globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (6.1.7.10) - activesupport (= 6.1.7.10) + actionview (7.0.1) + activesupport (= 7.0.1) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.1.7.10) - activesupport (= 6.1.7.10) + activejob (7.0.1) + activesupport (= 7.0.1) globalid (>= 0.3.6) - activemodel (6.1.7.10) - activesupport (= 6.1.7.10) - activerecord (6.1.7.10) - activemodel (= 6.1.7.10) - activesupport (= 6.1.7.10) - activestorage (6.1.7.10) - actionpack (= 6.1.7.10) - activejob (= 6.1.7.10) - activerecord (= 6.1.7.10) - activesupport (= 6.1.7.10) + activemodel (7.0.1) + activesupport (= 7.0.1) + activerecord (7.0.1) + activemodel (= 7.0.1) + activesupport (= 7.0.1) + activestorage (7.0.1) + actionpack (= 7.0.1) + activejob (= 7.0.1) + activerecord (= 7.0.1) + activesupport (= 7.0.1) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (6.1.7.10) + activesupport (7.0.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - zeitwerk (~> 2.3) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) airbrussh (1.4.1) @@ -240,8 +246,8 @@ GEM reline (>= 0.3.8) debug_inspector (1.1.0) declarative (0.0.20) - delayed_job (4.1.11) - activesupport (>= 3.0, < 8.0) + delayed_job (4.1.13) + activesupport (>= 3.0, < 9.0) delayed_job_active_record (4.1.7) activerecord (>= 3.0, < 8.0) delayed_job (>= 3.0, < 5) @@ -251,7 +257,7 @@ GEM railties (>= 4.1.0) responders warden (~> 1.2.3) - diff-lcs (1.5.0) + diff-lcs (1.5.1) docile (1.4.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) @@ -297,10 +303,6 @@ GEM feedjira (3.2.3) loofah (>= 2.3.1, < 3) sax-machine (>= 1.0, < 2) - ffi (1.17.0) - ffi (1.17.0-aarch64-linux-gnu) - ffi (1.17.0-arm64-darwin) - ffi (1.17.0-x86_64-darwin) ffi (1.17.0-x86_64-linux-gnu) ffi-compiler (1.0.1) ffi (>= 1.0.0) @@ -351,18 +353,6 @@ GEM googleapis-common-protos (>= 1.3.10, < 2.a) googleapis-common-protos-types (>= 1.0.5, < 2.a) googleauth (>= 0.16.2, < 2.a) - google-protobuf (4.28.3) - bigdecimal - rake (>= 13) - google-protobuf (4.28.3-aarch64-linux) - bigdecimal - rake (>= 13) - google-protobuf (4.28.3-arm64-darwin) - bigdecimal - rake (>= 13) - google-protobuf (4.28.3-x86_64-darwin) - bigdecimal - rake (>= 13) google-protobuf (4.28.3-x86_64-linux) bigdecimal rake (>= 13) @@ -379,18 +369,6 @@ GEM multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) - grpc (1.67.0) - google-protobuf (>= 3.25, < 5.0) - googleapis-common-protos-types (~> 1.0) - grpc (1.67.0-aarch64-linux) - google-protobuf (>= 3.25, < 5.0) - googleapis-common-protos-types (~> 1.0) - grpc (1.67.0-arm64-darwin) - google-protobuf (>= 3.25, < 5.0) - googleapis-common-protos-types (~> 1.0) - grpc (1.67.0-x86_64-darwin) - google-protobuf (>= 3.25, < 5.0) - googleapis-common-protos-types (~> 1.0) grpc (1.67.0-x86_64-linux) google-protobuf (>= 3.25, < 5.0) googleapis-common-protos-types (~> 1.0) @@ -481,10 +459,6 @@ GEM letter_opener (~> 1.9) railties (>= 6.1) rexml - libv8-node (16.10.0.0) - libv8-node (16.10.0.0-aarch64-linux) - libv8-node (16.10.0.0-arm64-darwin) - libv8-node (16.10.0.0-x86_64-darwin) libv8-node (16.10.0.0-x86_64-linux) liquid (5.5.1) listen (3.9.0) @@ -516,7 +490,6 @@ GEM rake mini_magick (5.0.1) mini_mime (1.1.5) - mini_portile2 (2.8.8) mini_racer (0.6.3) libv8-node (~> 16.10.0.0) minitest (5.25.1) @@ -535,7 +508,7 @@ GEM net-ftp-list (3.3.0) net-http (0.5.0) uri - net-imap (0.5.0) + net-imap (0.5.1) date net-protocol net-pop (0.1.2) @@ -548,16 +521,7 @@ GEM net-protocol net-ssh (7.1.0) netrc (0.11.0) - nio4r (2.7.3) - nokogiri (1.16.7) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) - nokogiri (1.16.7-aarch64-linux) - racc (~> 1.4) - nokogiri (1.16.7-arm64-darwin) - racc (~> 1.4) - nokogiri (1.16.7-x86_64-darwin) - racc (~> 1.4) + nio4r (2.7.4) nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) notiffany (0.1.3) @@ -629,21 +593,20 @@ GEM rack (~> 2.2, >= 2.2.4) rack-test (2.1.0) rack (>= 1.3) - rails (6.1.7.10) - actioncable (= 6.1.7.10) - actionmailbox (= 6.1.7.10) - actionmailer (= 6.1.7.10) - actionpack (= 6.1.7.10) - actiontext (= 6.1.7.10) - actionview (= 6.1.7.10) - activejob (= 6.1.7.10) - activemodel (= 6.1.7.10) - activerecord (= 6.1.7.10) - activestorage (= 6.1.7.10) - activesupport (= 6.1.7.10) + rails (7.0.1) + actioncable (= 7.0.1) + actionmailbox (= 7.0.1) + actionmailer (= 7.0.1) + actionpack (= 7.0.1) + actiontext (= 7.0.1) + actionview (= 7.0.1) + activejob (= 7.0.1) + activemodel (= 7.0.1) + activerecord (= 7.0.1) + activestorage (= 7.0.1) + activesupport (= 7.0.1) bundler (>= 1.15.0) - railties (= 6.1.7.10) - sprockets-rails (>= 2.0.0) + railties (= 7.0.1) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -655,12 +618,13 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (6.1.7.10) - actionpack (= 6.1.7.10) - activesupport (= 6.1.7.10) + railties (7.0.1) + actionpack (= 7.0.1) + activesupport (= 7.0.1) method_source rake (>= 12.2) thor (~> 1.0) + zeitwerk (~> 2.5) rainbow (3.1.1) raindrops (0.20.1) rake (13.2.1) @@ -690,32 +654,32 @@ GEM rexml (3.3.9) rouge (4.1.1) rr (3.1.0) - rspec (3.12.0) - rspec-core (~> 3.12.0) - rspec-expectations (~> 3.12.0) - rspec-mocks (~> 3.12.0) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) rspec-collection_matchers (1.2.0) rspec-expectations (>= 2.99.0.beta1) - rspec-core (3.12.2) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.3) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) + rspec-support (~> 3.13.0) rspec-html-matchers (0.10.0) nokogiri (~> 1) rspec (>= 3.0.0.a) - rspec-mocks (3.12.5) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-rails (6.0.2) - actionpack (>= 6.1) - activesupport (>= 6.1) - railties (>= 6.1) - rspec-core (~> 3.12) - rspec-expectations (~> 3.12) - rspec-mocks (~> 3.12) - rspec-support (~> 3.12) - rspec-support (3.12.0) + rspec-support (~> 3.13.0) + rspec-rails (7.1.0) + actionpack (>= 7.0) + activesupport (>= 7.0) + railties (>= 7.0) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.1) rturk (2.12.1) erector nokogiri @@ -783,7 +747,7 @@ GEM version_gem (~> 1.1, >= 1.1.1) spectrum-rails (1.8.1) railties (>= 3.1) - spring (4.1.1) + spring (4.2.1) spring-commands-rspec (1.0.4) spring (>= 0.9.1) spring-watcher-listen (2.1.0) @@ -792,9 +756,9 @@ GEM sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) sshkit (1.21.4) net-scp (>= 1.1.2) @@ -807,7 +771,7 @@ GEM tilt (2.1.0) time (0.2.2) date - timeout (0.4.1) + timeout (0.4.2) trailblazer-option (0.1.2) treetop (1.6.12) polyglot (~> 0.3) @@ -857,10 +821,6 @@ GEM zlib (3.2.0) PLATFORMS - aarch64-linux - arm64-darwin - ruby - x86_64-darwin x86_64-linux DEPENDENCIES @@ -938,7 +898,7 @@ DEPENDENCIES pirate_weather_forecast_ruby puma rack-livereload - rails (~> 6.1.7, >= 6.1.7.10) + rails (= 7.0.1) rails-controller-testing rails-html-sanitizer (~> 1.6) rb-kqueue (>= 0.2.8) diff --git a/app/controllers/web_requests_controller.rb b/app/controllers/web_requests_controller.rb index 9d5b560228..4e72b52f95 100644 --- a/app/controllers/web_requests_controller.rb +++ b/app/controllers/web_requests_controller.rb @@ -9,7 +9,7 @@ # #receive_web_request is called. For example, one of your Agent's options could be :secret and you could compare this # value to params[:secret] whenever #receive_web_request is called on your Agent, rejecting invalid requests. # -# Your Agent's #receive_web_request method should return an Array of json_or_string_response, status_code, +# Your Agent's #receive_web_request method should return an Array of json_or_string_response, status_code, # optional mime type, and optional hash of custom response headers. For example: # [{status: "success"}, 200] # or @@ -30,27 +30,27 @@ def handle_request content, status, content_type, headers = agent.trigger_web_request(request) if headers.present? - headers.each do |k,v| + headers.each do |k, v| response.headers[k] = v end end - status = status || 200 + status ||= 200 - if status.to_s.in?(["301", "302"]) - redirect_to content, status: status + if status.to_s.in?(%w[301 302]) + redirect_to(content, allow_other_host: true, status:) elsif content.is_a?(String) - render plain: content, :status => status, :content_type => content_type || 'text/plain' + render plain: content, status:, content_type: content_type || 'text/plain' elsif content.is_a?(Hash) - render :json => content, :status => status + render(json: content, status:) else head(status) end else - render plain: "agent not found", :status => 404 + render plain: 'agent not found', status: 404 end else - render plain: "user not found", :status => 404 + render plain: 'user not found', status: 404 end end @@ -58,14 +58,12 @@ def handle_request def update_location if user = User.find_by_id(params[:user_id]) secret = params[:secret] - user.agents.of_type(Agents::UserLocationAgent).each { |agent| - if agent.options[:secret] == secret - agent.trigger_web_request(request) - end - } - render plain: "ok" + user.agents.of_type(Agents::UserLocationAgent).each do |agent| + agent.trigger_web_request(request) if agent.options[:secret] == secret + end + render plain: 'ok' else - render plain: "user not found", :status => :not_found + render plain: 'user not found', status: :not_found end end end diff --git a/bin/rails b/bin/rails index 1cf9077616..efc0377492 100755 --- a/bin/rails +++ b/bin/rails @@ -1,9 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -APP_PATH = File.expand_path('../config/application', __dir__) +APP_PATH = File.expand_path("../config/application", __dir__) require_relative "../config/boot" require "rails/commands" diff --git a/bin/rake b/bin/rake index 53da8ced07..4fbf10b960 100755 --- a/bin/rake +++ b/bin/rake @@ -1,9 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end require_relative "../config/boot" require "rake" Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000000..ec47b79b3b --- /dev/null +++ b/bin/setup @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby +require "fileutils" + +# path to your application root. +APP_ROOT = File.expand_path("..", __dir__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. + # Add necessary setup steps to this file. + + puts "== Installing dependencies ==" + system! "gem install bundler --conservative" + system("bundle check") || system!("bundle install") + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" + # end + + puts "\n== Preparing database ==" + system! "bin/rails db:prepare" + + puts "\n== Removing old logs and tempfiles ==" + system! "bin/rails log:clear tmp:clear" + + puts "\n== Restarting application server ==" + system! "bin/rails restart" +end diff --git a/config/application.rb b/config/application.rb index 3e13029b26..d74d3c1af8 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,17 +1,17 @@ -require_relative "boot" +require_relative 'boot' -require "rails" +require 'rails' -require "active_model/railtie" -require "active_job/railtie" -require "active_record/railtie" +require 'active_model/railtie' +require 'active_job/railtie' +require 'active_record/railtie' # require "active_storage/engine" -require "action_controller/railtie" -require "action_mailer/railtie" -require "action_view/railtie" +require 'action_controller/railtie' +require 'action_mailer/railtie' +require 'action_view/railtie' # require "action_cable/engine" -require "sprockets/railtie" -require "rails/test_unit/railtie" +require 'sprockets/railtie' +require 'rails/test_unit/railtie' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. @@ -19,10 +19,10 @@ module Huginn class Application < Rails::Application - Dotenv.overload File.expand_path('../../spec/env.test', __FILE__) if Rails.env.test? + Dotenv.overload File.expand_path('../spec/env.test', __dir__) if Rails.env.test? # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 6.1 + config.load_defaults 7.0 # Configuration for the application, engines, and railties goes here. # @@ -30,21 +30,21 @@ class Application < Rails::Application # in config/environments, which are processed later. # Custom directories with classes and modules you want to be autoloadable. - config.autoload_paths += %W(#{config.root}/lib #{config.root}/app/presenters #{config.root}/app/jobs) + config.autoload_paths += %W[#{config.root}/lib #{config.root}/app/presenters #{config.root}/app/jobs] # Activate observers that should always be running. # config.active_record.observers = :cacher, :garbage_collector, :forum_observer # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. - config.time_zone = ENV['TIMEZONE'].presence || "Pacific Time (US & Canada)" + config.time_zone = ENV['TIMEZONE'].presence || 'Pacific Time (US & Canada)' # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de # Configure the default encoding used in templates for Ruby 1.9. - config.encoding = "utf-8" + config.encoding = 'utf-8' # Enable escaping HTML in JSON. config.active_support.escape_html_entities_in_json = true @@ -56,10 +56,14 @@ class Application < Rails::Application config.active_job.queue_adapter = :delayed_job - config.action_view.sanitized_allowed_tags = %w[strong em b i p code pre tt samp kbd var sub sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr acronym a img blockquote del ins style table thead tbody tr th td] - config.action_view.sanitized_allowed_attributes = %w[href src width height alt cite datetime title class name xml:lang abbr border cellspacing cellpadding valign style] + config.action_view.sanitized_allowed_tags = %w[strong em b i p code pre tt samp kbd var sub sup dfn cite big small + address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr acronym a img blockquote del ins style table thead tbody tr th td] + config.action_view.sanitized_allowed_attributes = %w[href src width height alt cite datetime title class name + xml:lang abbr border cellspacing cellpadding valign style] - config.active_record.yaml_column_permitted_classes = [Symbol, Date, Time] + config.after_initialize do + config.active_record.yaml_column_permitted_classes = [Symbol, Date, Time] + end ActiveSupport::XmlMini.backend = 'Nokogiri' end diff --git a/config/environment.rb b/config/environment.rb index cac5315775..426333bb46 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,5 @@ # Load the Rails application. -require_relative "application" +require_relative 'application' # Initialize the Rails application. Rails.application.initialize! diff --git a/config/environments/test.rb b/config/environments/test.rb index 71fccb8726..2338de0541 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,4 +1,4 @@ -require "active_support/core_ext/integer/time" +require 'active_support/core_ext/integer/time' # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that @@ -7,10 +7,9 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - config.cache_classes = false - # Do not eager load code on boot. This avoids loading your whole application + # do not eager load code on boot. This avoids loading your whole application # just for the purpose of running a single test. If you are using a tool that # preloads Rails for running tests, you may have to set it to true. config.eager_load = false diff --git a/spec/concerns/long_runnable_spec.rb b/spec/concerns/long_runnable_spec.rb index 423a4033db..2916cd9ba2 100644 --- a/spec/concerns/long_runnable_spec.rb +++ b/spec/concerns/long_runnable_spec.rb @@ -1,11 +1,17 @@ require 'rails_helper' +class TestWorker < LongRunnable::Worker + def stop; end + def terminate; end + def runl; end +end + describe LongRunnable do class LongRunnableAgent < Agent include LongRunnable def default_options - {test: 'test'} + { test: 'test' } end end @@ -13,16 +19,16 @@ def default_options @agent = LongRunnableAgent.new end - it "start_worker? defaults to true" do + it 'start_worker? defaults to true' do expect(@agent.start_worker?).to be_truthy end - it "should build the worker_id" do + it 'should build the worker_id' do expect(@agent.worker_id).to eq('LongRunnableAgent--bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f') end - context "#setup_worker" do - it "returns active agent workers" do + context '#setup_worker' do + it 'returns active agent workers' do expect(LongRunnableAgent).to receive(:active) { [@agent] } workers = LongRunnableAgent.setup_worker expect(workers.length).to eq(1) @@ -30,7 +36,7 @@ def default_options expect(workers.first.agent).to eq(@agent) end - it "returns an empty array when no agent is active" do + it 'returns an empty array when no agent is active' do expect(LongRunnableAgent).to receive(:active) { [] } workers = LongRunnableAgent.setup_worker expect(workers.length).to eq(0) @@ -39,10 +45,12 @@ def default_options describe LongRunnable::Worker do before(:each) do - @agent = Object.new + @agent = double('agent') + @stoppable_worker = TestWorker.new(agent: @agent, id: 'test1234') @worker = LongRunnable::Worker.new(agent: @agent, id: 'test1234') @scheduler = Rufus::Scheduler.new @worker.setup!(@scheduler, Mutex.new) + @stoppable_worker.setup!(@scheduler, Mutex.new) end after(:each) do @@ -50,62 +58,62 @@ def default_options @scheduler.shutdown(:wait) end - it "calls boolify of the agent" do + it 'calls boolify of the agent' do expect(@agent).to receive(:boolify).with('true') { true } expect(@worker.boolify('true')).to be_truthy end - it "expects run to be overriden" do + it 'expects run to be overriden' do expect { @worker.run }.to raise_error(StandardError) end - context "#run!" do - it "runs the agent worker" do + context '#run!' do + it 'runs the agent worker' do expect(@worker).to receive(:run) @worker.run!.join end - it "stops when rescueing a SystemExit" do + it 'stops when rescueing a SystemExit' do expect(@worker).to receive(:run) { raise SystemExit } expect(@worker).to receive(:stop!) @worker.run!.join end - it "creates an agent log entry for a generic exception" do + it 'creates an agent log entry for a generic exception' do allow(STDERR).to receive(:puts) - expect(@worker).to receive(:run) { raise "woups" } + expect(@worker).to receive(:run) { raise 'woups' } expect(@agent).to receive(:error).with(/woups/) @worker.run!.join end end - context "#stop!" do - it "terminates the thread" do + context '#stop!' do + it 'terminates the thread' do expect(@worker).to receive(:terminate_thread!) @worker.stop! end - it "gracefully stops the worker" do - expect(@worker).to receive(:stop) - @worker.stop! + it 'gracefully stops the worker' do + expect(@stoppable_worker).to receive(:stop) + @stoppable_worker.stop! end end - context "#terminate_thread!" do + context '#terminate_thread!' do before do @skip_thread_terminate = true - mock_thread = Object.new + mock_thread = double('mock_thread') allow(@worker).to receive(:thread) { mock_thread } end - it "terminates the thread" do + it 'terminates the thread' do expect(@worker.thread).to receive(:terminate) expect(@worker.thread).not_to receive(:wakeup) expect(@worker.thread).to receive(:status) { 'run' } @worker.terminate_thread! end - it "wakes up sleeping threads after termination" do + it 'wakes up sleeping threads after termination' do expect(@worker.thread).to receive(:terminate) expect(@worker.thread).to receive(:wakeup) expect(@worker.thread).to receive(:status) { 'sleep' } @@ -113,8 +121,8 @@ def default_options end end - context "#restart!" do - it "stops, setups and starts the worker" do + context '#restart!' do + it 'stops, setups and starts the worker' do expect(@worker).to receive(:stop!) expect(@worker).to receive(:setup!).with(@worker.scheduler, @worker.mutex) expect(@worker).to receive(:run!) @@ -123,18 +131,18 @@ def default_options end end - context "scheduling" do - it "schedules tasks once" do + context 'scheduling' do + it 'schedules tasks once' do expect(@worker.scheduler).to receive(:send).with(:schedule_in, 1.hour, tag: 'test1234') @worker.schedule_in 1.hour do noop; end end - it "schedules repeating tasks" do + it 'schedules repeating tasks' do expect(@worker.scheduler).to receive(:send).with(:every, 1.hour, tag: 'test1234') @worker.every 1.hour do noop; end end - it "allows the cron syntax" do + it 'allows the cron syntax' do expect(@worker.scheduler).to receive(:send).with(:cron, '0 * * * *', tag: 'test1234') @worker.cron '0 * * * *' do noop; end end diff --git a/spec/models/agents/google_calendar_publish_agent_spec.rb b/spec/models/agents/google_calendar_publish_agent_spec.rb index 6f69b3ec0f..e5f0082fe2 100644 --- a/spec/models/agents/google_calendar_publish_agent_spec.rb +++ b/spec/models/agents/google_calendar_publish_agent_spec.rb @@ -3,7 +3,7 @@ describe Agents::GoogleCalendarPublishAgent do let(:valid_params) do { - 'expected_update_period_in_days' => "10", + 'expected_update_period_in_days' => '10', 'calendar_id' => calendar_id, 'google' => { 'key_file' => File.dirname(__FILE__) + '/../../data_fixtures/private.key', @@ -14,7 +14,7 @@ end let(:agent) do - _agent = Agents::GoogleCalendarPublishAgent.new(name: "somename", options: valid_params) + _agent = Agents::GoogleCalendarPublishAgent.new(name: 'somename', options: valid_params) _agent.user = users(:jane) _agent.save! _agent @@ -24,8 +24,8 @@ let(:message) do { 'visibility' => 'default', - 'summary' => "Awesome event", - 'description' => "An example event with text. Pro tip: DateTimes are in RFC3339", + 'summary' => 'Awesome event', + 'description' => 'An example event with text. Pro tip: DateTimes are in RFC3339', 'end' => { 'date_time' => '2014-10-02T11:00:00-05:00' }, @@ -46,35 +46,36 @@ let(:calendar_id) { 'sqv39gj35tc837gdns1g4d81cg@group.calendar.google.com' } let(:response_hash) do - {"kind"=>"calendar#event", - "etag"=>"\"2908684044040000\"", - "id"=>"baz", - "status"=>"confirmed", - "html_link"=> - "https://calendar.google.com/calendar/event?eid=foobar", - "created"=>"2016-02-01T15:53:41.000Z", - "updated"=>"2016-02-01T15:53:42.020Z", - "summary"=>"Awesome event", - "description"=> - "An example event with text. Pro tip: DateTimes are in RFC3339", - "creator"=> - {"email"=> - "blah-foobar@developer.gserviceaccount.com"}, - "organizer"=> - {"email"=>calendar_id, - "display_name"=>"Huginn Location Log", - "self"=>true}, - "start"=>{"date_time"=>"2014-10-03T00:30:00+09:30"}, - "end"=>{"date_time"=>"2014-10-03T01:30:00+09:30"}, - "i_cal_uid"=>"blah@google.com", - "sequence"=>0, - "reminders"=>{"use_default"=>true} - } + { 'kind' => 'calendar#event', + 'etag' => '"2908684044040000"', + 'id' => 'baz', + 'status' => 'confirmed', + 'html_link' => + 'https://calendar.google.com/calendar/event?eid=foobar', + 'created' => '2016-02-01T15:53:41.000Z', + 'updated' => '2016-02-01T15:53:42.020Z', + 'summary' => 'Awesome event', + 'description' => + 'An example event with text. Pro tip: DateTimes are in RFC3339', + 'creator' => + { 'email' => + 'blah-foobar@developer.gserviceaccount.com' }, + 'organizer' => + { 'email' => calendar_id, + 'display_name' => 'Huginn Location Log', + 'self' => true }, + 'start' => { 'date_time' => '2014-10-03T00:30:00+09:30' }, + 'end' => { 'date_time' => '2014-10-03T01:30:00+09:30' }, + 'i_cal_uid' => 'blah@google.com', + 'sequence' => 0, + 'reminders' => { 'use_default' => true } } end def setup_mock! - fake_interface = Object.new - expect(GoogleCalendar).to receive(:new).with(agent.interpolate_options(agent.options), Rails.logger) { fake_interface } + fake_interface = double('fake_interface') + expect(GoogleCalendar).to receive(:new).with(agent.interpolate_options(agent.options), Rails.logger) { + fake_interface + } expect(fake_interface).to receive(:publish_as).with(calendar_id, message) { response_hash } expect(fake_interface).to receive(:cleanup!) end @@ -83,11 +84,12 @@ def setup_mock! it 'should publish any payload it receives' do setup_mock! - expect { + expect do agent.receive([event]) - }.to change { agent.events.count }.by(1) + end.to change { agent.events.count }.by(1) - expect(agent.events.last.payload).to eq({ "success" => true, "published_calendar_event" => response_hash, "agent_id" => event.agent_id, "event_id" => event.id }) + expect(agent.events.last.payload).to eq({ 'success' => true, 'published_calendar_event' => response_hash, + 'agent_id' => event.agent_id, 'event_id' => event.id }) end end @@ -104,7 +106,8 @@ def setup_mock! agent.receive([event]) expect(agent.events.count).to eq(1) - expect(agent.events.last.payload).to eq({ "success" => true, "published_calendar_event" => response_hash, "agent_id" => event.agent_id, "event_id" => event.id }) + expect(agent.events.last.payload).to eq({ 'success' => true, 'published_calendar_event' => response_hash, + 'agent_id' => event.agent_id, 'event_id' => event.id }) end it 'should allow Liquid in the key' do @@ -131,8 +134,8 @@ def setup_mock! _event.agent = agents(:bob_manual_event_agent) _event.payload = { 'message' => { 'visibility' => 'default', - 'summary' => "Awesome event", - 'description' => "An example event with text. Pro tip: DateTimes are in RFC3339", + 'summary' => 'Awesome event', + 'description' => 'An example event with text. Pro tip: DateTimes are in RFC3339', 'end' => { 'dateTime' => '2014-10-02T11:00:00-05:00' }, @@ -148,8 +151,8 @@ def setup_mock! let(:message) do { 'visibility' => 'default', - 'summary' => "Awesome event", - 'description' => "An example event with text. Pro tip: DateTimes are in RFC3339", + 'summary' => 'Awesome event', + 'description' => 'An example event with text. Pro tip: DateTimes are in RFC3339', 'end' => { 'date_time' => '2014-10-02T11:00:00-05:00' }, @@ -160,36 +163,37 @@ def setup_mock! end let(:response_hash) do - {"kind"=>"calendar#event", - "etag"=>"\"2908684044040000\"", - "id"=>"baz", - "status"=>"confirmed", - "html_link"=> - "https://calendar.google.com/calendar/event?eid=foobar", - "created"=>"2016-02-01T15:53:41.000Z", - "updated"=>"2016-02-01T15:53:42.020Z", - "summary"=>"Awesome event", - "description"=> - "An example event with text. Pro tip: DateTimes are in RFC3339", - "creator"=> - {"email"=> - "blah-foobar@developer.gserviceaccount.com"}, - "organizer"=> - {"email"=>calendar_id, - "display_name"=>"Huginn Location Log", - "self"=>true}, - "start"=>{"date_time"=>"2014-10-03T00:30:00+09:30"}, - "end"=>{"date_time"=>"2014-10-03T01:30:00+09:30"}, - "i_cal_uid"=>"blah@google.com", - "sequence"=>0, - "reminders"=>{"use_default"=>true} - } + { 'kind' => 'calendar#event', + 'etag' => '"2908684044040000"', + 'id' => 'baz', + 'status' => 'confirmed', + 'html_link' => + 'https://calendar.google.com/calendar/event?eid=foobar', + 'created' => '2016-02-01T15:53:41.000Z', + 'updated' => '2016-02-01T15:53:42.020Z', + 'summary' => 'Awesome event', + 'description' => + 'An example event with text. Pro tip: DateTimes are in RFC3339', + 'creator' => + { 'email' => + 'blah-foobar@developer.gserviceaccount.com' }, + 'organizer' => + { 'email' => calendar_id, + 'display_name' => 'Huginn Location Log', + 'self' => true }, + 'start' => { 'date_time' => '2014-10-03T00:30:00+09:30' }, + 'end' => { 'date_time' => '2014-10-03T01:30:00+09:30' }, + 'i_cal_uid' => 'blah@google.com', + 'sequence' => 0, + 'reminders' => { 'use_default' => true } } end def setup_mock! - fake_interface = Object.new - expect(GoogleCalendar).to receive(:new).with(agent.interpolate_options(agent.options), Rails.logger) { fake_interface } - expect(fake_interface).to receive(:publish_as).with(calendar_id, message) { response_hash } + fake_interface = double('fake_interface') + expect(GoogleCalendar).to receive(:new).with(agent.interpolate_options(agent.options), Rails.logger) { + fake_interface + } + allow(fake_interface).to receive(:publish_as).with(calendar_id, message) { response_hash } expect(fake_interface).to receive(:cleanup!) end @@ -197,11 +201,12 @@ def setup_mock! it 'should publish old style payload it receives' do setup_mock! - expect { + expect do agent.receive([event]) - }.to change { agent.events.count }.by(1) + end.to change { agent.events.count }.by(1) - expect(agent.events.last.payload).to eq({ "success" => true, "published_calendar_event" => response_hash, "agent_id" => event.agent_id, "event_id" => event.id }) + expect(agent.events.last.payload).to eq({ 'success' => true, 'published_calendar_event' => response_hash, + 'agent_id' => event.agent_id, 'event_id' => event.id }) end end end diff --git a/spec/models/agents/human_task_agent_spec.rb b/spec/models/agents/human_task_agent_spec.rb index afa7207018..3b6c9b434d 100644 --- a/spec/models/agents/human_task_agent_spec.rb +++ b/spec/models/agents/human_task_agent_spec.rb @@ -2,28 +2,28 @@ describe Agents::HumanTaskAgent do before do - @checker = Agents::HumanTaskAgent.new(:name => "my human task agent") + @checker = Agents::HumanTaskAgent.new(name: 'my human task agent') @checker.options = @checker.default_options @checker.user = users(:bob) @checker.save! @event = Event.new @event.agent = agents(:bob_rain_notifier_agent) - @event.payload = { 'foo' => { "bar" => { 'baz' => "a2b" } }, - 'name' => "Joe" } + @event.payload = { 'foo' => { 'bar' => { 'baz' => 'a2b' } }, + 'name' => 'Joe' } @event.id = 345 expect(@checker).to be_valid end - describe "validations" do + describe 'validations' do it "validates that trigger_on is 'schedule' or 'event'" do - @checker.options['trigger_on'] = "foo" + @checker.options['trigger_on'] = 'foo' expect(@checker).not_to be_valid end it "requires expected_receive_period_in_days when trigger_on is set to 'event'" do - @checker.options['trigger_on'] = "event" + @checker.options['trigger_on'] = 'event' @checker.options['expected_receive_period_in_days'] = nil expect(@checker).not_to be_valid @checker.options['expected_receive_period_in_days'] = 2 @@ -31,35 +31,35 @@ end it "requires a positive submission_period when trigger_on is set to 'schedule'" do - @checker.options['trigger_on'] = "schedule" + @checker.options['trigger_on'] = 'schedule' @checker.options['submission_period'] = nil expect(@checker).not_to be_valid @checker.options['submission_period'] = 2 expect(@checker).to be_valid end - it "requires a hit.title" do - @checker.options['hit']['title'] = "" + it 'requires a hit.title' do + @checker.options['hit']['title'] = '' expect(@checker).not_to be_valid end - it "requires a hit.description" do - @checker.options['hit']['description'] = "" + it 'requires a hit.description' do + @checker.options['hit']['description'] = '' expect(@checker).not_to be_valid end - it "requires hit.assignments" do - @checker.options['hit']['assignments'] = "" + it 'requires hit.assignments' do + @checker.options['hit']['assignments'] = '' expect(@checker).not_to be_valid @checker.options['hit']['assignments'] = 0 expect(@checker).not_to be_valid - @checker.options['hit']['assignments'] = "moose" + @checker.options['hit']['assignments'] = 'moose' expect(@checker).not_to be_valid - @checker.options['hit']['assignments'] = "2" + @checker.options['hit']['assignments'] = '2' expect(@checker).to be_valid end - it "requires hit.questions" do + it 'requires hit.questions' do old_questions = @checker.options['hit']['questions'] @checker.options['hit']['questions'] = nil expect(@checker).not_to be_valid @@ -69,13 +69,13 @@ expect(@checker).to be_valid end - it "requires that all questions have key, name, required, type, and question" do + it 'requires that all questions have key, name, required, type, and question' do old_questions = @checker.options['hit']['questions'] - @checker.options['hit']['questions'].first['key'] = "" + @checker.options['hit']['questions'].first['key'] = '' expect(@checker).not_to be_valid @checker.options['hit']['questions'] = old_questions - @checker.options['hit']['questions'].first['name'] = "" + @checker.options['hit']['questions'].first['name'] = '' expect(@checker).not_to be_valid @checker.options['hit']['questions'] = old_questions @@ -83,11 +83,11 @@ expect(@checker).not_to be_valid @checker.options['hit']['questions'] = old_questions - @checker.options['hit']['questions'].first['type'] = "" + @checker.options['hit']['questions'].first['type'] = '' expect(@checker).not_to be_valid @checker.options['hit']['questions'] = old_questions - @checker.options['hit']['questions'].first['question'] = "" + @checker.options['hit']['questions'].first['question'] = '' expect(@checker).not_to be_valid end @@ -96,58 +96,58 @@ expect(@checker).not_to be_valid @checker.options['hit']['questions'][0]['selections'] = [{}] expect(@checker).not_to be_valid - @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => "", 'text' => "" }] + @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => '', 'text' => '' }] expect(@checker).not_to be_valid - @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => "", 'text' => "hi" }] + @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => '', 'text' => 'hi' }] expect(@checker).not_to be_valid - @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => "hi", 'text' => "" }] + @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => 'hi', 'text' => '' }] expect(@checker).not_to be_valid - @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => "hi", 'text' => "hi" }] + @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => 'hi', 'text' => 'hi' }] expect(@checker).to be_valid - @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => "hi", 'text' => "hi" }, {}] + @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => 'hi', 'text' => 'hi' }, {}] expect(@checker).not_to be_valid end it "requires that 'poll_options' be present and populated when 'combination_mode' is set to 'poll'" do - @checker.options['combination_mode'] = "poll" + @checker.options['combination_mode'] = 'poll' expect(@checker).not_to be_valid @checker.options['poll_options'] = {} expect(@checker).not_to be_valid - @checker.options['poll_options'] = { 'title' => "Take a poll about jokes", - 'instructions' => "Rank these by how funny they are", + @checker.options['poll_options'] = { 'title' => 'Take a poll about jokes', + 'instructions' => 'Rank these by how funny they are', 'assignments' => 3, - 'row_template' => "{{joke}}" } + 'row_template' => '{{joke}}' } expect(@checker).to be_valid - @checker.options['poll_options'] = { 'instructions' => "Rank these by how funny they are", + @checker.options['poll_options'] = { 'instructions' => 'Rank these by how funny they are', 'assignments' => 3, - 'row_template' => "{{joke}}" } + 'row_template' => '{{joke}}' } expect(@checker).not_to be_valid - @checker.options['poll_options'] = { 'title' => "Take a poll about jokes", + @checker.options['poll_options'] = { 'title' => 'Take a poll about jokes', 'assignments' => 3, - 'row_template' => "{{joke}}" } + 'row_template' => '{{joke}}' } expect(@checker).not_to be_valid - @checker.options['poll_options'] = { 'title' => "Take a poll about jokes", - 'instructions' => "Rank these by how funny they are", - 'row_template' => "{{joke}}" } + @checker.options['poll_options'] = { 'title' => 'Take a poll about jokes', + 'instructions' => 'Rank these by how funny they are', + 'row_template' => '{{joke}}' } expect(@checker).not_to be_valid - @checker.options['poll_options'] = { 'title' => "Take a poll about jokes", - 'instructions' => "Rank these by how funny they are", - 'assignments' => 3} + @checker.options['poll_options'] = { 'title' => 'Take a poll about jokes', + 'instructions' => 'Rank these by how funny they are', + 'assignments' => 3 } expect(@checker).not_to be_valid end it "requires that all questions be of type 'selection' when 'combination_mode' is 'take_majority'" do - @checker.options['combination_mode'] = "take_majority" + @checker.options['combination_mode'] = 'take_majority' expect(@checker).not_to be_valid - @checker.options['hit']['questions'][1]['type'] = "selection" + @checker.options['hit']['questions'][1]['type'] = 'selection' @checker.options['hit']['questions'][1]['selections'] = @checker.options['hit']['questions'][0]['selections'] expect(@checker).to be_valid end it "accepts 'take_majority': 'true' for legacy support" do - @checker.options['take_majority'] = "true" + @checker.options['take_majority'] = 'true' expect(@checker).not_to be_valid - @checker.options['hit']['questions'][1]['type'] = "selection" + @checker.options['hit']['questions'][1]['type'] = 'selection' @checker.options['hit']['questions'][1]['selections'] = @checker.options['hit']['questions'][0]['selections'] expect(@checker).to be_valid end @@ -155,12 +155,12 @@ describe "when 'trigger_on' is set to 'schedule'" do before do - @checker.options['trigger_on'] = "schedule" - @checker.options['submission_period'] = "2" + @checker.options['trigger_on'] = 'schedule' + @checker.options['submission_period'] = '2' @checker.options.delete('expected_receive_period_in_days') end - it "should check for reviewable HITs frequently" do + it 'should check for reviewable HITs frequently' do expect(@checker).to receive(:review_hits).twice expect(@checker).to receive(:create_basic_hit).once @checker.check @@ -179,15 +179,15 @@ @checker.check end - it "should ignore events" do + it 'should ignore events' do expect(@checker).not_to receive(:create_basic_hit).with(anything) @checker.receive([events(:bob_website_agent_event)]) end end describe "when 'trigger_on' is set to 'event'" do - it "should not create HITs during check but should check for reviewable HITs" do - @checker.options['submission_period'] = "2" + it 'should not create HITs during check but should check for reviewable HITs' do + @checker.options['submission_period'] = '2' now = Time.now allow(Time).to receive(:now) { now } expect(@checker).to receive(:review_hits).exactly(3).times @@ -199,51 +199,54 @@ @checker.check end - it "should create HITs based on events" do + it 'should create HITs based on events' do expect(@checker).to receive(:create_basic_hit).with(events(:bob_website_agent_event)).once @checker.receive([events(:bob_website_agent_event)]) end end - describe "creating hits" do - it "can create HITs based on events, interpolating their values" do - @checker.options['hit']['title'] = "Hi {{name}}" - @checker.options['hit']['description'] = "Make something for {{name}}" - @checker.options['hit']['questions'][0]['name'] = "{{name}} Question 1" + describe 'creating hits' do + it 'can create HITs based on events, interpolating their values' do + @checker.options['hit']['title'] = 'Hi {{name}}' + @checker.options['hit']['description'] = 'Make something for {{name}}' + @checker.options['hit']['questions'][0]['name'] = '{{name}} Question 1' question_form = nil - hitInterface = OpenStruct.new - hitInterface.id = 123 - expect(hitInterface).to receive(:question_form).with(instance_of Agents::HumanTaskAgent::AgentQuestionForm) { |agent_question_form_instance| question_form = agent_question_form_instance } - expect(RTurk::Hit).to receive(:create).with(title: "Hi Joe").and_yield(hitInterface).and_return(hitInterface) - @checker.send :create_basic_hit, @event + hit_interface = double('hit_interface', id: 123, url: 'https://') + allow(hit_interface).to receive(:question_form).with(instance_of(Agents::HumanTaskAgent::AgentQuestionForm)) { |agent_question_form_instance| + question_form = agent_question_form_instance + } + allow(hit_interface).to receive(:max_assignments=).with(@checker.options['hit']['assignments']) + allow(hit_interface).to receive(:description=).with('Make something for Joe') + allow(hit_interface).to receive(:lifetime=) + allow(hit_interface).to receive(:reward=).with(@checker.options['hit']['reward']) + expect(RTurk::Hit).to receive(:create).with(title: 'Hi Joe').and_yield(hit_interface).and_return(hit_interface) - expect(hitInterface.max_assignments).to eq(@checker.options['hit']['assignments']) - expect(hitInterface.reward).to eq(@checker.options['hit']['reward']) - expect(hitInterface.description).to eq("Make something for Joe") + @checker.send :create_basic_hit, @event xml = question_form.to_xml - expect(xml).to include("Hi Joe") - expect(xml).to include("Make something for Joe") - expect(xml).to include("Joe Question 1") + expect(xml).to include('Hi Joe') + expect(xml).to include('Make something for Joe') + expect(xml).to include('Joe Question 1') expect(@checker.memory['hits'][123]['event_id']).to eq(@event.id) end - it "works without an event too" do - @checker.options['hit']['title'] = "Hi {{name}}" - hitInterface = OpenStruct.new - hitInterface.id = 123 - expect(hitInterface).to receive(:question_form).with(instance_of Agents::HumanTaskAgent::AgentQuestionForm) - expect(RTurk::Hit).to receive(:create).with(title: "Hi").and_yield(hitInterface).and_return(hitInterface) + it 'works without an event too' do + @checker.options['hit']['title'] = 'Hi {{name}}' + hit_interface = double('hit_interface', id: 123, url: 'https://') + allow(hit_interface).to receive(:question_form).with(instance_of(Agents::HumanTaskAgent::AgentQuestionForm)) + allow(hit_interface).to receive(:max_assignments=).with(@checker.options['hit']['assignments']) + allow(hit_interface).to receive(:description=) + allow(hit_interface).to receive(:lifetime=) + allow(hit_interface).to receive(:reward=).with(@checker.options['hit']['reward']) + expect(RTurk::Hit).to receive(:create).with(title: 'Hi').and_yield(hit_interface).and_return(hit_interface) @checker.send :create_basic_hit - expect(hitInterface.max_assignments).to eq(@checker.options['hit']['assignments']) - expect(hitInterface.reward).to eq(@checker.options['hit']['reward']) end end - describe "reviewing HITs" do + describe 'reviewing HITs' do class FakeHit def initialize(options = {}) @options = options @@ -278,7 +281,7 @@ def answers end def status - @options[:status] || "" + @options[:status] || '' end def approve! @@ -286,298 +289,322 @@ def approve! end end - it "should work on multiple HITs" do + it 'should work on multiple HITs' do event2 = Event.new event2.agent = agents(:bob_rain_notifier_agent) - event2.payload = { 'foo2' => { "bar2" => { 'baz2' => "a2b2" } }, - 'name2' => "Joe2" } + event2.payload = { 'foo2' => { 'bar2' => { 'baz2' => 'a2b2' } }, + 'name2' => 'Joe2' } event2.id = 3452 # It knows about two HITs from two different events. @checker.memory['hits'] = {} - @checker.memory['hits']["JH3132836336DHG"] = { 'event_id' => @event.id } - @checker.memory['hits']["JH39AA63836DHG"] = { 'event_id' => event2.id } + @checker.memory['hits']['JH3132836336DHG'] = { 'event_id' => @event.id } + @checker.memory['hits']['JH39AA63836DHG'] = { 'event_id' => event2.id } hit_ids = %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345] - expect(RTurk::GetReviewableHITs).to receive(:create) { double(hit_ids: hit_ids) } # It sees 3 HITs. + expect(RTurk::GetReviewableHITs).to receive(:create) { double(hit_ids:) } # It sees 3 HITs. # It looksup the two HITs that it owns. Neither are ready yet. - expect(RTurk::Hit).to receive(:new).with("JH3132836336DHG") { FakeHit.new } - expect(RTurk::Hit).to receive(:new).with("JH39AA63836DHG") { FakeHit.new } + expect(RTurk::Hit).to receive(:new).with('JH3132836336DHG') { FakeHit.new } + expect(RTurk::Hit).to receive(:new).with('JH39AA63836DHG') { FakeHit.new } @checker.send :review_hits end it "shouldn't do anything if an assignment isn't ready" do - @checker.memory['hits'] = { "JH3132836336DHG" => { 'event_id' => @event.id } } - expect(RTurk::GetReviewableHITs).to receive(:create) { double(hit_ids: %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345]) } + @checker.memory['hits'] = { 'JH3132836336DHG' => { 'event_id' => @event.id } } + expect(RTurk::GetReviewableHITs).to receive(:create) { + double(hit_ids: %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345]) + } assignments = [ - FakeAssignment.new(:status => "Accepted", :answers => {}), - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"happy", "feedback"=>"Take 2"}) + FakeAssignment.new(status: 'Accepted', answers: {}), + FakeAssignment.new(status: 'Submitted', answers: { 'sentiment' => 'happy', 'feedback' => 'Take 2' }) ] - hit = FakeHit.new(:max_assignments => 2, :assignments => assignments) - expect(RTurk::Hit).to receive(:new).with("JH3132836336DHG") { hit } + hit = FakeHit.new(max_assignments: 2, assignments:) + expect(RTurk::Hit).to receive(:new).with('JH3132836336DHG') { hit } # One of the assignments isn't set to "Submitted", so this should get skipped for now. expect_any_instance_of(FakeAssignment).not_to receive(:answers) @checker.send :review_hits - expect(assignments.all? {|a| a.approved == true }).to be_falsey - expect(@checker.memory['hits']).to eq({ "JH3132836336DHG" => { 'event_id' => @event.id } }) + expect(assignments.all? { |a| a.approved == true }).to be_falsey + expect(@checker.memory['hits']).to eq({ 'JH3132836336DHG' => { 'event_id' => @event.id } }) end it "shouldn't do anything if an assignment is missing" do - @checker.memory['hits'] = { "JH3132836336DHG" => { 'event_id' => @event.id } } - expect(RTurk::GetReviewableHITs).to receive(:create) { double(hit_ids: %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345]) } + @checker.memory['hits'] = { 'JH3132836336DHG' => { 'event_id' => @event.id } } + expect(RTurk::GetReviewableHITs).to receive(:create) { + double(hit_ids: %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345]) + } assignments = [ - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"happy", "feedback"=>"Take 2"}) + FakeAssignment.new(status: 'Submitted', answers: { 'sentiment' => 'happy', 'feedback' => 'Take 2' }) ] - hit = FakeHit.new(:max_assignments => 2, :assignments => assignments) - expect(RTurk::Hit).to receive(:new).with("JH3132836336DHG") { hit } + hit = FakeHit.new(max_assignments: 2, assignments:) + expect(RTurk::Hit).to receive(:new).with('JH3132836336DHG') { hit } # One of the assignments hasn't shown up yet, so this should get skipped for now. expect_any_instance_of(FakeAssignment).not_to receive(:answers) @checker.send :review_hits - expect(assignments.all? {|a| a.approved == true }).to be_falsey - expect(@checker.memory['hits']).to eq({ "JH3132836336DHG" => { 'event_id' => @event.id } }) + expect(assignments.all? { |a| a.approved == true }).to be_falsey + expect(@checker.memory['hits']).to eq({ 'JH3132836336DHG' => { 'event_id' => @event.id } }) end - context "emitting events" do + context 'emitting events' do before do - @checker.memory['hits'] = { "JH3132836336DHG" => { 'event_id' => @event.id } } - expect(RTurk::GetReviewableHITs).to receive(:create) { double(hit_ids: %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345]) } + @checker.memory['hits'] = { 'JH3132836336DHG' => { 'event_id' => @event.id } } + expect(RTurk::GetReviewableHITs).to receive(:create) { + double(hit_ids: %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345]) + } @assignments = [ - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"neutral", "feedback"=>""}), - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"happy", "feedback"=>"Take 2"}) + FakeAssignment.new(status: 'Submitted', answers: { 'sentiment' => 'neutral', 'feedback' => '' }), + FakeAssignment.new(status: 'Submitted', answers: { 'sentiment' => 'happy', 'feedback' => 'Take 2' }) ] - @hit = FakeHit.new(:max_assignments => 2, :assignments => @assignments) + @hit = FakeHit.new(max_assignments: 2, assignments: @assignments) expect(@hit).not_to be_disposed - expect(RTurk::Hit).to receive(:new).with("JH3132836336DHG") { @hit } + expect(RTurk::Hit).to receive(:new).with('JH3132836336DHG') { @hit } end - it "should create events when all assignments are ready" do - expect { + it 'should create events when all assignments are ready' do + expect do @checker.send :review_hits - }.to change { Event.count }.by(1) + end.to change { Event.count }.by(1) - expect(@assignments.all? {|a| a.approved == true }).to be_truthy + expect(@assignments.all? { |a| a.approved == true }).to be_truthy expect(@hit).to be_disposed expect(@checker.events.last.payload['answers']).to eq([ - {'sentiment' => "neutral", 'feedback' => ""}, - {'sentiment' => "happy", 'feedback' => "Take 2"} - ]) + { 'sentiment' => 'neutral', 'feedback' => '' }, + { 'sentiment' => 'happy', 'feedback' => 'Take 2' } + ]) expect(@checker.memory['hits']).to eq({}) end - it "should emit separate answers when options[:separate_answers] is true" do + it 'should emit separate answers when options[:separate_answers] is true' do @checker.options[:separate_answers] = true - expect { + expect do @checker.send :review_hits - }.to change { Event.count }.by(2) + end.to change { Event.count }.by(2) - expect(@assignments.all? {|a| a.approved == true }).to be_truthy + expect(@assignments.all? { |a| a.approved == true }).to be_truthy expect(@hit).to be_disposed event1, event2 = @checker.events.last(2) expect(event1.payload).not_to have_key('answers') expect(event2.payload).not_to have_key('answers') - expect(event1.payload['answer']).to eq({ 'sentiment' => "happy", 'feedback' => "Take 2" }) - expect(event2.payload['answer']).to eq({ 'sentiment' => "neutral", 'feedback' => "" }) + expect(event1.payload['answer']).to eq({ 'sentiment' => 'happy', 'feedback' => 'Take 2' }) + expect(event2.payload['answer']).to eq({ 'sentiment' => 'neutral', 'feedback' => '' }) expect(@checker.memory['hits']).to eq({}) end end - describe "taking majority votes" do + describe 'taking majority votes' do before do - @checker.options['combination_mode'] = "take_majority" - @checker.memory['hits'] = { "JH3132836336DHG" => { 'event_id' => @event.id } } - expect(RTurk::GetReviewableHITs).to receive(:create) { double(hit_ids: %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345]) } + @checker.options['combination_mode'] = 'take_majority' + @checker.memory['hits'] = { 'JH3132836336DHG' => { 'event_id' => @event.id } } + expect(RTurk::GetReviewableHITs).to receive(:create) { + double(hit_ids: %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345]) + } end - it "should take the majority votes of all questions" do + it 'should take the majority votes of all questions' do @checker.options['hit']['questions'][1] = { - 'type' => "selection", - 'key' => "age_range", - 'name' => "Age Range", - 'required' => "true", - 'question' => "Please select your age range:", + 'type' => 'selection', + 'key' => 'age_range', + 'name' => 'Age Range', + 'required' => 'true', + 'question' => 'Please select your age range:', 'selections' => [ - { 'key' => "<50", 'text' => "50 years old or younger" }, - { 'key' => ">50", 'text' => "Over 50 years old" } + { 'key' => '<50', 'text' => '50 years old or younger' }, + { 'key' => '>50', 'text' => 'Over 50 years old' } ] } assignments = [ - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"sad", "age_range"=>"<50"}), - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"neutral", "age_range"=>">50"}), - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"happy", "age_range"=>">50"}), - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"happy", "age_range"=>">50"}) + FakeAssignment.new(status: 'Submitted', answers: { 'sentiment' => 'sad', 'age_range' => '<50' }), + FakeAssignment.new(status: 'Submitted', answers: { 'sentiment' => 'neutral', 'age_range' => '>50' }), + FakeAssignment.new(status: 'Submitted', answers: { 'sentiment' => 'happy', 'age_range' => '>50' }), + FakeAssignment.new(status: 'Submitted', answers: { 'sentiment' => 'happy', 'age_range' => '>50' }) ] - hit = FakeHit.new(:max_assignments => 4, :assignments => assignments) - expect(RTurk::Hit).to receive(:new).with("JH3132836336DHG") { hit } + hit = FakeHit.new(max_assignments: 4, assignments:) + expect(RTurk::Hit).to receive(:new).with('JH3132836336DHG') { hit } - expect { + expect do @checker.send :review_hits - }.to change { Event.count }.by(1) + end.to change { Event.count }.by(1) - expect(assignments.all? {|a| a.approved == true }).to be_truthy + expect(assignments.all? { |a| a.approved == true }).to be_truthy expect(@checker.events.last.payload['answers']).to eq([ - { 'sentiment' => "sad", 'age_range' => "<50" }, - { 'sentiment' => "neutral", 'age_range' => ">50" }, - { 'sentiment' => "happy", 'age_range' => ">50" }, - { 'sentiment' => "happy", 'age_range' => ">50" } - ]) - - expect(@checker.events.last.payload['counts']).to eq({ 'sentiment' => { 'happy' => 2, 'sad' => 1, 'neutral' => 1 }, 'age_range' => { ">50" => 3, "<50" => 1 } }) - expect(@checker.events.last.payload['majority_answer']).to eq({ 'sentiment' => "happy", 'age_range' => ">50" }) + { 'sentiment' => 'sad', 'age_range' => '<50' }, + { 'sentiment' => 'neutral', 'age_range' => '>50' }, + { 'sentiment' => 'happy', 'age_range' => '>50' }, + { 'sentiment' => 'happy', 'age_range' => '>50' } + ]) + + expect(@checker.events.last.payload['counts']).to eq({ + 'sentiment' => { 'happy' => 2, 'sad' => 1, + 'neutral' => 1 }, 'age_range' => { '>50' => 3, '<50' => 1 } + }) + expect(@checker.events.last.payload['majority_answer']).to eq({ 'sentiment' => 'happy', 'age_range' => '>50' }) expect(@checker.events.last.payload).not_to have_key('average_answer') expect(@checker.memory['hits']).to eq({}) end - it "should also provide an average answer when all questions are numeric" do + it 'should also provide an average answer when all questions are numeric' do # it should accept 'take_majority': 'true' as well for legacy support. Demonstrating that here. @checker.options.delete :combination_mode - @checker.options['take_majority'] = "true" + @checker.options['take_majority'] = 'true' @checker.options['hit']['questions'] = [ { - 'type' => "selection", - 'key' => "rating", - 'name' => "Rating", - 'required' => "true", - 'question' => "Please select a rating:", + 'type' => 'selection', + 'key' => 'rating', + 'name' => 'Rating', + 'required' => 'true', + 'question' => 'Please select a rating:', 'selections' => [ - { 'key' => "1", 'text' => "One" }, - { 'key' => "2", 'text' => "Two" }, - { 'key' => "3", 'text' => "Three" }, - { 'key' => "4", 'text' => "Four" }, - { 'key' => "5.1", 'text' => "Five Point One" } + { 'key' => '1', 'text' => 'One' }, + { 'key' => '2', 'text' => 'Two' }, + { 'key' => '3', 'text' => 'Three' }, + { 'key' => '4', 'text' => 'Four' }, + { 'key' => '5.1', 'text' => 'Five Point One' } ] } ] assignments = [ - FakeAssignment.new(:status => "Submitted", :answers => { "rating"=>"1" }), - FakeAssignment.new(:status => "Submitted", :answers => { "rating"=>"3" }), - FakeAssignment.new(:status => "Submitted", :answers => { "rating"=>"5.1" }), - FakeAssignment.new(:status => "Submitted", :answers => { "rating"=>"2" }), - FakeAssignment.new(:status => "Submitted", :answers => { "rating"=>"2" }) + FakeAssignment.new(status: 'Submitted', answers: { 'rating' => '1' }), + FakeAssignment.new(status: 'Submitted', answers: { 'rating' => '3' }), + FakeAssignment.new(status: 'Submitted', answers: { 'rating' => '5.1' }), + FakeAssignment.new(status: 'Submitted', answers: { 'rating' => '2' }), + FakeAssignment.new(status: 'Submitted', answers: { 'rating' => '2' }) ] - hit = FakeHit.new(:max_assignments => 5, :assignments => assignments) - expect(RTurk::Hit).to receive(:new).with("JH3132836336DHG") { hit } + hit = FakeHit.new(max_assignments: 5, assignments:) + expect(RTurk::Hit).to receive(:new).with('JH3132836336DHG') { hit } - expect { + expect do @checker.send :review_hits - }.to change { Event.count }.by(1) + end.to change { Event.count }.by(1) - expect(assignments.all? {|a| a.approved == true }).to be_truthy + expect(assignments.all? { |a| a.approved == true }).to be_truthy expect(@checker.events.last.payload['answers']).to eq([ - { 'rating' => "1" }, - { 'rating' => "3" }, - { 'rating' => "5.1" }, - { 'rating' => "2" }, - { 'rating' => "2" } - ]) - - expect(@checker.events.last.payload['counts']).to eq({ 'rating' => { "1" => 1, "2" => 2, "3" => 1, "4" => 0, "5.1" => 1 } }) - expect(@checker.events.last.payload['majority_answer']).to eq({ 'rating' => "2" }) + { 'rating' => '1' }, + { 'rating' => '3' }, + { 'rating' => '5.1' }, + { 'rating' => '2' }, + { 'rating' => '2' } + ]) + + expect(@checker.events.last.payload['counts']).to eq({ 'rating' => { '1' => 1, '2' => 2, '3' => 1, '4' => 0, + '5.1' => 1 } }) + expect(@checker.events.last.payload['majority_answer']).to eq({ 'rating' => '2' }) expect(@checker.events.last.payload['average_answer']).to eq({ 'rating' => (1 + 2 + 2 + 3 + 5.1) / 5.0 }) expect(@checker.memory['hits']).to eq({}) end end - describe "creating and reviewing polls" do + describe 'creating and reviewing polls' do before do - @checker.options['combination_mode'] = "poll" + @checker.options['combination_mode'] = 'poll' @checker.options['poll_options'] = { - 'title' => "Hi!", - 'instructions' => "hello!", + 'title' => 'Hi!', + 'instructions' => 'hello!', 'assignments' => 2, - 'row_template' => "This is {{sentiment}}" + 'row_template' => 'This is {{sentiment}}' } @event.save! - expect(RTurk::GetReviewableHITs).to receive(:create) { double(hit_ids: %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345]) } + expect(RTurk::GetReviewableHITs).to receive(:create) { + double(hit_ids: %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345]) + } end - it "creates a poll using the row_template, message, and correct number of assignments" do - @checker.memory['hits'] = { "JH3132836336DHG" => { 'event_id' => @event.id } } + it 'creates a poll using the row_template, message, and correct number of assignments' do + @checker.memory['hits'] = { 'JH3132836336DHG' => { 'event_id' => @event.id } } # Mock out the HIT's submitted assignments. assignments = [ - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"sad", "feedback"=>"This is my feedback 1"}), - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"neutral", "feedback"=>"This is my feedback 2"}), - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"happy", "feedback"=>"This is my feedback 3"}), - FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"happy", "feedback"=>"This is my feedback 4"}) + FakeAssignment.new(status: 'Submitted', + answers: { 'sentiment' => 'sad', + 'feedback' => 'This is my feedback 1' }), + FakeAssignment.new(status: 'Submitted', + answers: { 'sentiment' => 'neutral', + 'feedback' => 'This is my feedback 2' }), + FakeAssignment.new(status: 'Submitted', + answers: { 'sentiment' => 'happy', + 'feedback' => 'This is my feedback 3' }), + FakeAssignment.new(status: 'Submitted', + answers: { 'sentiment' => 'happy', + 'feedback' => 'This is my feedback 4' }) ] - hit = FakeHit.new(:max_assignments => 4, :assignments => assignments) - expect(RTurk::Hit).to receive(:new).with("JH3132836336DHG") { hit } + hit = FakeHit.new(max_assignments: 4, assignments:) + expect(RTurk::Hit).to receive(:new).with('JH3132836336DHG') { hit } - expect(@checker.memory['hits']["JH3132836336DHG"]).to be_present + expect(@checker.memory['hits']['JH3132836336DHG']).to be_present # Setup mocks for HIT creation question_form = nil - hitInterface = OpenStruct.new - hitInterface.id = "JH39AA63836DH12345" - expect(hitInterface).to receive(:question_form).with(instance_of Agents::HumanTaskAgent::AgentQuestionForm) { |agent_question_form_instance| question_form = agent_question_form_instance } - expect(RTurk::Hit).to receive(:create).with(title: "Hi!").and_yield(hitInterface).and_return(hitInterface) + hit_interface = double('hit_interface', id: 'JH39AA63836DH12345', url: 'https://') + allow(hit_interface).to receive(:question_form).with(instance_of(Agents::HumanTaskAgent::AgentQuestionForm)) { |agent_question_form_instance| + question_form = agent_question_form_instance + } + allow(hit_interface).to receive(:max_assignments=).with(@checker.options['poll_options']['assignments']) + allow(hit_interface).to receive(:description=).with(@checker.options['poll_options']['instructions']) + allow(hit_interface).to receive(:lifetime=) + allow(hit_interface).to receive(:reward=).with(@checker.options['hit']['reward']) + expect(RTurk::Hit).to receive(:create).with(title: 'Hi!').and_yield(hit_interface).and_return(hit_interface) # And finally, the test. - expect { + # it does not emit an event until all poll results are in + expect do @checker.send :review_hits - }.to change { Event.count }.by(0) # it does not emit an event until all poll results are in - + end.to change { Event.count }.by(0) # it approves the existing assignments - expect(assignments.all? {|a| a.approved == true }).to be_truthy + expect(assignments.all? { |a| a.approved == true }).to be_truthy expect(hit).to be_disposed # it creates a new HIT for the poll - expect(hitInterface.max_assignments).to eq(@checker.options['poll_options']['assignments']) - expect(hitInterface.description).to eq(@checker.options['poll_options']['instructions']) - xml = question_form.to_xml - expect(xml).to include("This is happy") - expect(xml).to include("This is neutral") - expect(xml).to include("This is sad") + expect(xml).to include('This is happy') + expect(xml).to include('This is neutral') + expect(xml).to include('This is sad') @checker.save @checker.reload - expect(@checker.memory['hits']["JH3132836336DHG"]).not_to be_present - expect(@checker.memory['hits']["JH39AA63836DH12345"]).to be_present - expect(@checker.memory['hits']["JH39AA63836DH12345"]['event_id']).to eq(@event.id) - expect(@checker.memory['hits']["JH39AA63836DH12345"]['type']).to eq("poll") - expect(@checker.memory['hits']["JH39AA63836DH12345"]['original_hit']).to eq("JH3132836336DHG") - expect(@checker.memory['hits']["JH39AA63836DH12345"]['answers'].length).to eq(4) + expect(@checker.memory['hits']['JH3132836336DHG']).not_to be_present + expect(@checker.memory['hits']['JH39AA63836DH12345']).to be_present + expect(@checker.memory['hits']['JH39AA63836DH12345']['event_id']).to eq(@event.id) + expect(@checker.memory['hits']['JH39AA63836DH12345']['type']).to eq('poll') + expect(@checker.memory['hits']['JH39AA63836DH12345']['original_hit']).to eq('JH3132836336DHG') + expect(@checker.memory['hits']['JH39AA63836DH12345']['answers'].length).to eq(4) end - it "emits an event when all poll results are in, containing the data from the best answer, plus all others" do + it 'emits an event when all poll results are in, containing the data from the best answer, plus all others' do original_answers = [ - { 'sentiment' => "sad", 'feedback' => "This is my feedback 1"}, - { 'sentiment' => "neutral", 'feedback' => "This is my feedback 2"}, - { 'sentiment' => "happy", 'feedback' => "This is my feedback 3"}, - { 'sentiment' => "happy", 'feedback' => "This is my feedback 4"} + { 'sentiment' => 'sad', 'feedback' => 'This is my feedback 1' }, + { 'sentiment' => 'neutral', 'feedback' => 'This is my feedback 2' }, + { 'sentiment' => 'happy', 'feedback' => 'This is my feedback 3' }, + { 'sentiment' => 'happy', 'feedback' => 'This is my feedback 4' } ] @checker.memory['hits'] = { 'JH39AA63836DH12345' => { 'type' => 'poll', - 'original_hit' => "JH3132836336DHG", + 'original_hit' => 'JH3132836336DHG', 'answers' => original_answers, 'event_id' => 345 } @@ -585,27 +612,29 @@ def approve! # Mock out the HIT's submitted assignments. assignments = [ - FakeAssignment.new(:status => "Submitted", :answers => {"1" => "2", "2" => "5", "3" => "3", "4" => "2"}), - FakeAssignment.new(:status => "Submitted", :answers => {"1" => "3", "2" => "4", "3" => "1", "4" => "4"}) + FakeAssignment.new(status: 'Submitted', answers: { '1' => '2', '2' => '5', '3' => '3', '4' => '2' }), + FakeAssignment.new(status: 'Submitted', answers: { '1' => '3', '2' => '4', '3' => '1', '4' => '4' }) ] - hit = FakeHit.new(:max_assignments => 2, :assignments => assignments) - expect(RTurk::Hit).to receive(:new).with("JH39AA63836DH12345") { hit } + hit = FakeHit.new(max_assignments: 2, assignments:) + expect(RTurk::Hit).to receive(:new).with('JH39AA63836DH12345') { hit } - expect(@checker.memory['hits']["JH39AA63836DH12345"]).to be_present + expect(@checker.memory['hits']['JH39AA63836DH12345']).to be_present - expect { + expect do @checker.send :review_hits - }.to change { Event.count }.by(1) + end.to change { Event.count }.by(1) # It emits an event expect(@checker.events.last.payload['answers']).to eq(original_answers) - expect(@checker.events.last.payload['poll']).to eq([{"1" => "2", "2" => "5", "3" => "3", "4" => "2"}, {"1" => "3", "2" => "4", "3" => "1", "4" => "4"}]) - expect(@checker.events.last.payload['best_answer']).to eq({'sentiment' => "neutral", 'feedback' => "This is my feedback 2"}) + expect(@checker.events.last.payload['poll']).to eq([{ '1' => '2', '2' => '5', '3' => '3', '4' => '2' }, + { '1' => '3', '2' => '4', '3' => '1', '4' => '4' }]) + expect(@checker.events.last.payload['best_answer']).to eq({ 'sentiment' => 'neutral', + 'feedback' => 'This is my feedback 2' }) # it approves the existing assignments - expect(assignments.all? {|a| a.approved == true }).to be_truthy + expect(assignments.all? { |a| a.approved == true }).to be_truthy expect(hit).to be_disposed expect(@checker.memory['hits']).to be_empty diff --git a/spec/models/agents/imap_folder_agent_spec.rb b/spec/models/agents/imap_folder_agent_spec.rb index 3e5bead9b3..27033c6c2c 100644 --- a/spec/models/agents/imap_folder_agent_spec.rb +++ b/spec/models/agents/imap_folder_agent_spec.rb @@ -15,21 +15,29 @@ def has_attachment? false end - def body_parts(mime_types = %[text/plain text/enriched text/html]) - mime_types.map { |type| - all_parts.find { |part| + def body_parts(mime_types = %(text/plain text/enriched text/html)) + mime_types.map do |type| + all_parts.find do |part| part.mime_type == type - } - }.compact.map! { |part| + end + end.compact.map! do |part| part.extend(Agents::ImapFolderAgent::Message::Scrubbed) - } + end end + def uid; end + + def raw_mail; end + + def delete; end + + def mark_as_read; end + include Agents::ImapFolderAgent::Message::Scrubbed end describe 'checking IMAP' do - let(:valid_options) { + let(:valid_options) do { 'expected_update_period_in_days' => 1, 'host' => 'mail.example.net', @@ -37,28 +45,27 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) 'username' => 'foo', 'password' => 'bar', 'folders' => ['INBOX'], - 'conditions' => { - } + 'conditions' => {} } - } + end - let(:mails) { + let(:mails) do [ - Mail.read(Rails.root.join('spec/data_fixtures/imap1.eml')).tap { |mail| + Mail.read(Rails.root.join('spec/data_fixtures/imap1.eml')).tap do |mail| mail.extend(MessageMixin) allow(mail).to receive(:uid).and_return(1) allow(mail).to receive(:raw_mail).and_return(mail.encoded) - }, - Mail.read(Rails.root.join('spec/data_fixtures/imap2.eml')).tap { |mail| + end, + Mail.read(Rails.root.join('spec/data_fixtures/imap2.eml')).tap do |mail| mail.extend(MessageMixin) allow(mail).to receive(:uid).and_return(2) allow(mail).to receive(:has_attachment?).and_return(true) allow(mail).to receive(:raw_mail).and_return(mail.encoded) - }, + end ] - } + end - let(:expected_payloads) { + let(:expected_payloads) do [ { 'message_id' => 'foo.123@mail.example.jp', @@ -71,7 +78,7 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) 'body' => "Some plain text\nSome second line\n", 'has_attachment' => false, 'matches' => {}, - 'mime_type' => 'text/plain', + 'mime_type' => 'text/plain' }, { 'message_id' => 'bar.456@mail.example.com', @@ -84,10 +91,10 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) 'date' => '2014-05-09T17:00:00+09:00', 'has_attachment' => true, 'matches' => {}, - 'mime_type' => 'text/plain', + 'mime_type' => 'text/plain' } ] - } + end before do @checker = Agents::ImapFolderAgent.new(name: 'Example', options: valid_options, keep_events_for: 2.days) @@ -97,10 +104,10 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) allow(@checker).to receive(:each_unread_mail) { |&yielder| seen = @checker.lastseen notified = @checker.notified - mails.each_with_object(notified) { |mail| + mails.each do |mail| yielder[mail, notified] seen[mail.uidvalidity] = mail.uid - } + end @checker.lastseen = seen @checker.notified = notified nil @@ -171,13 +178,13 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) it 'should check for mails and save memory' do expect { @checker.check }.to change { Event.count }.by(2) expect(@checker.notified.sort).to eq(mails.map(&:message_id).sort) - expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) { |mail, seen| + expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) do |mail, seen| seen[mail.uidvalidity] = mail.uid - }) + end) expect(Event.last(2).map(&:payload)).to eq expected_payloads - expect { @checker.check }.not_to change { Event.count } + expect { @checker.check }.not_to(change { Event.count }) end it 'should narrow mails by To' do @@ -185,42 +192,42 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) expect { @checker.check }.to change { Event.count }.by(1) expect(@checker.notified.sort).to eq([mails.first.message_id]) - expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) { |mail, seen| + expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) do |mail, seen| seen[mail.uidvalidity] = mail.uid - }) + end) expect(Event.last.payload).to eq(expected_payloads.first) - expect { @checker.check }.not_to change { Event.count } + expect { @checker.check }.not_to(change { Event.count }) end it 'should not fail when a condition on Cc is given and a mail does not have the field' do @checker.options['conditions']['cc'] = 'John.Doe@*' - expect { - expect { @checker.check }.not_to change { Event.count } - }.not_to raise_exception + expect do + expect { @checker.check }.not_to(change { Event.count }) + end.not_to raise_exception end it 'should perform regexp matching and save named captures' do @checker.options['conditions'].update( 'subject' => '\ARe: (?.+)', - 'body' => 'Some (?.+) reply', + 'body' => 'Some (?.+) reply' ) expect { @checker.check }.to change { Event.count }.by(1) expect(@checker.notified.sort).to eq([mails.last.message_id]) - expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) { |mail, seen| + expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) do |mail, seen| seen[mail.uidvalidity] = mail.uid - }) + end) expect(Event.last.payload).to eq(expected_payloads.last.update( - 'body' => "
Some HTML reply
\n", - 'matches' => { 'a' => 'some subject', 'b' => 'HTML' }, - 'mime_type' => 'text/html', - )) + 'body' => "
Some HTML reply
\n", + 'matches' => { 'a' => 'some subject', 'b' => 'HTML' }, + 'mime_type' => 'text/html' + )) - expect { @checker.check }.not_to change { Event.count } + expect { @checker.check }.not_to(change { Event.count }) end it 'should narrow mails by has_attachment (true)' do @@ -243,45 +250,45 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) @checker.options['mime_types'] = %w[text/plain] @checker.options['conditions'].update( 'subject' => '\ARe: (?
.+)', - 'body' => 'Some (?.+) reply', + 'body' => 'Some (?.+) reply' ) - expect { @checker.check }.not_to change { Event.count } + expect { @checker.check }.not_to(change { Event.count }) expect(@checker.notified.sort).to eq([]) - expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) { |mail, seen| + expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) do |mail, seen| seen[mail.uidvalidity] = mail.uid - }) + end) end it 'should never mark mails as read unless mark_as_read is true' do - mails.each { |mail| + mails.each do |mail| allow(mail).to receive(:mark_as_read).never - } + end expect { @checker.check }.to change { Event.count }.by(2) end it 'should mark mails as read if mark_as_read is true' do @checker.options['mark_as_read'] = true - mails.each { |mail| + mails.each do |mail| allow(mail).to receive(:mark_as_read).once - } + end expect { @checker.check }.to change { Event.count }.by(2) end it 'should create just one event for multiple mails with the same Message-Id' do mails.first.message_id = mails.last.message_id @checker.options['mark_as_read'] = true - mails.each { |mail| + mails.each do |mail| allow(mail).to receive(:mark_as_read).once - } + end expect { @checker.check }.to change { Event.count }.by(1) end it 'should delete mails if delete is true' do @checker.options['delete'] = true - mails.each { |mail| + mails.each do |mail| allow(mail).to receive(:delete).once - } + end expect { @checker.check }.to change { Event.count }.by(2) end @@ -299,12 +306,12 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) it 'should ignore them without failing if a "from" condition is given' do @checker.options['conditions']['from'] = '*' - expect { @checker.check }.not_to change { Event.count } + expect { @checker.check }.not_to(change { Event.count }) end end describe 'with event_headers' do - let(:expected_headers) { + let(:expected_headers) do [ { 'mime_version' => '1.0', @@ -315,7 +322,7 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) 'x_foo' => "test2-1\ntest2-2" } ] - } + end before do expected_payloads.zip(expected_headers) do |payload, headers| payload['headers'] = headers @@ -329,13 +336,13 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) it 'should check for mails and emit events with headers' do expect { @checker.check }.to change { Event.count }.by(2) expect(@checker.notified.sort).to eq(mails.map(&:message_id).sort) - expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) { |mail, seen| - seen[mail.uidvalidity] = mail.uid - }) + expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) do |mail, seen| + seen[mail.uidvalidity] = mail.uid + end) expect(Event.last(2).map(&:payload)).to match expected_payloads - expect { @checker.check }.not_to change { Event.count } + expect { @checker.check }.not_to(change { Event.count }) end end @@ -348,17 +355,17 @@ def body_parts(mime_types = %[text/plain text/enriched text/html]) it 'should check for mails and emit events with raw_mail' do expect { @checker.check }.to change { Event.count }.by(2) expect(@checker.notified.sort).to eq(mails.map(&:message_id).sort) - expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) { |mail, seen| - seen[mail.uidvalidity] = mail.uid - }) + expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) do |mail, seen| + seen[mail.uidvalidity] = mail.uid + end) - expect(Event.last(2).map(&:payload)).to match expected_payloads.map.with_index { |payload, i| + expect(Event.last(2).map(&:payload)).to match(expected_payloads.map.with_index do |payload, i| payload.merge( 'raw_mail' => satisfy { |d| Base64.decode64(d) == mails[i].encoded } ) - } + end) - expect { @checker.check }.not_to change { Event.count } + expect { @checker.check }.not_to(change { Event.count }) end end end @@ -383,8 +390,8 @@ def body describe '#scrubbed' do it 'should return a scrubbed string' do - expect(@object.scrubbed(:subject)).to eq("brokensubject") - expect(@object.scrubbed(:body)).to eq("brokenbody") + expect(@object.scrubbed(:subject)).to eq('brokensubject') + expect(@object.scrubbed(:body)).to eq('brokenbody') end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index f821104eb2..1eb7053f9f 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,4 +1,4 @@ -ENV["RAILS_ENV"] ||= 'test' +ENV['RAILS_ENV'] ||= 'test' if ENV['COVERAGE'] require 'simplecov' @@ -16,7 +16,8 @@ end end -require File.expand_path('../config/environment', __dir__) +# require File.expand_path('../config/environment', __dir__) +require_relative '../config/environment' require 'rspec/rails' require 'webmock/rspec' @@ -24,7 +25,7 @@ # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. -Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } +Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } ActiveRecord::Migration.maintain_test_schema! @@ -61,27 +62,23 @@ # to individual examples or groups you care about by tagging them with # `:focus` metadata. When nothing is tagged with `:focus`, all examples # get run. - if ENV['CI'] != 'true' - config.filter_run :focus - end + config.filter_run :focus if ENV['CI'] != 'true' config.run_all_when_everything_filtered = true # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 - config.order = "random" + config.order = 'random' config.global_fixtures = :all config.render_views - config.example_status_persistence_file_path = "./spec/examples.txt" + config.example_status_persistence_file_path = './spec/examples.txt' config.include Devise::Test::ControllerHelpers, type: :controller config.include SpecHelpers config.include ActiveSupport::Testing::TimeHelpers end -if ENV['RSPEC_TASK'] != 'spec:nofeatures' - require 'capybara_helper' -end +require 'capybara_helper' if ENV['RSPEC_TASK'] != 'spec:nofeatures' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000000..327b58ea1f --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,94 @@ +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end From 050c6806c997c1ff0a030a30c1bcba65b56901c2 Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Sat, 28 Dec 2024 13:23:24 +0900 Subject: [PATCH 2/2] Update Rails to 7.0.8.7 --- Gemfile | 2 +- Gemfile.lock | 122 +++++++++++++++++++++++++-------------------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/Gemfile b/Gemfile index e7d40f6332..fe4f00ae0f 100644 --- a/Gemfile +++ b/Gemfile @@ -123,7 +123,7 @@ gem 'mini_magick', '>= 5.0.1' gem 'multi_xml' gem 'nokogiri', '>= 1.16.7' gem 'omniauth' -gem 'rails', '7.0.1' +gem 'rails', '~> 7.0.1' gem 'rails-html-sanitizer', '~> 1.6', '>= 1.6.2' gem 'rufus-scheduler', '~> 3.9', '>= 3.9.2', require: false gem 'sassc-rails' diff --git a/Gemfile.lock b/Gemfile.lock index fca0c4a6e7..0ac9bf1623 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -101,67 +101,67 @@ GEM remote: https://rubygems.org/ specs: ace-rails-ap (4.5) - actioncable (7.0.1) - actionpack (= 7.0.1) - activesupport (= 7.0.1) + actioncable (7.0.8.7) + actionpack (= 7.0.8.7) + activesupport (= 7.0.8.7) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.1) - actionpack (= 7.0.1) - activejob (= 7.0.1) - activerecord (= 7.0.1) - activestorage (= 7.0.1) - activesupport (= 7.0.1) + actionmailbox (7.0.8.7) + actionpack (= 7.0.8.7) + activejob (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.1) - actionpack (= 7.0.1) - actionview (= 7.0.1) - activejob (= 7.0.1) - activesupport (= 7.0.1) + actionmailer (7.0.8.7) + actionpack (= 7.0.8.7) + actionview (= 7.0.8.7) + activejob (= 7.0.8.7) + activesupport (= 7.0.8.7) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.1) - actionview (= 7.0.1) - activesupport (= 7.0.1) - rack (~> 2.0, >= 2.2.0) + actionpack (7.0.8.7) + actionview (= 7.0.8.7) + activesupport (= 7.0.8.7) + rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.1) - actionpack (= 7.0.1) - activerecord (= 7.0.1) - activestorage (= 7.0.1) - activesupport (= 7.0.1) + actiontext (7.0.8.7) + actionpack (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.1) - activesupport (= 7.0.1) + actionview (7.0.8.7) + activesupport (= 7.0.8.7) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.1) - activesupport (= 7.0.1) + activejob (7.0.8.7) + activesupport (= 7.0.8.7) globalid (>= 0.3.6) - activemodel (7.0.1) - activesupport (= 7.0.1) - activerecord (7.0.1) - activemodel (= 7.0.1) - activesupport (= 7.0.1) - activestorage (7.0.1) - actionpack (= 7.0.1) - activejob (= 7.0.1) - activerecord (= 7.0.1) - activesupport (= 7.0.1) + activemodel (7.0.8.7) + activesupport (= 7.0.8.7) + activerecord (7.0.8.7) + activemodel (= 7.0.8.7) + activesupport (= 7.0.8.7) + activestorage (7.0.8.7) + actionpack (= 7.0.8.7) + activejob (= 7.0.8.7) + activerecord (= 7.0.8.7) + activesupport (= 7.0.8.7) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.1) + activesupport (7.0.8.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -240,7 +240,7 @@ GEM crass (1.0.6) csv (3.3.0) daemons (1.4.1) - date (3.4.0) + date (3.4.1) debug (1.9.2) irb (~> 1.10) reline (>= 0.3.8) @@ -273,7 +273,7 @@ GEM eventmachine (>= 0.12.9) http_parser.rb (~> 0) equalizer (0.0.11) - erubi (1.13.0) + erubi (1.13.1) et-orbi (1.2.11) tzinfo ethon (0.16.0) @@ -500,7 +500,7 @@ GEM mini_mime (1.1.5) mini_racer (0.6.3) libv8-node (~> 16.10.0.0) - minitest (5.25.1) + minitest (5.25.4) mqtt (0.6.0) msgpack (1.7.2) multi_json (1.15.0) @@ -516,7 +516,7 @@ GEM net-ftp-list (3.3.0) net-http (0.5.0) uri - net-imap (0.5.1) + net-imap (0.5.4) date net-protocol net-pop (0.1.2) @@ -601,22 +601,22 @@ GEM rack-protection (3.2.0) base64 (>= 0.1.0) rack (~> 2.2, >= 2.2.4) - rack-test (2.1.0) + rack-test (2.2.0) rack (>= 1.3) - rails (7.0.1) - actioncable (= 7.0.1) - actionmailbox (= 7.0.1) - actionmailer (= 7.0.1) - actionpack (= 7.0.1) - actiontext (= 7.0.1) - actionview (= 7.0.1) - activejob (= 7.0.1) - activemodel (= 7.0.1) - activerecord (= 7.0.1) - activestorage (= 7.0.1) - activesupport (= 7.0.1) + rails (7.0.8.7) + actioncable (= 7.0.8.7) + actionmailbox (= 7.0.8.7) + actionmailer (= 7.0.8.7) + actionpack (= 7.0.8.7) + actiontext (= 7.0.8.7) + actionview (= 7.0.8.7) + activejob (= 7.0.8.7) + activemodel (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) bundler (>= 1.15.0) - railties (= 7.0.1) + railties (= 7.0.8.7) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -628,9 +628,9 @@ GEM rails-html-sanitizer (1.6.2) loofah (~> 2.21) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (7.0.1) - actionpack (= 7.0.1) - activesupport (= 7.0.1) + railties (7.0.8.7) + actionpack (= 7.0.8.7) + activesupport (= 7.0.8.7) method_source rake (>= 12.2) thor (~> 1.0) @@ -781,7 +781,7 @@ GEM tilt (2.1.0) time (0.2.2) date - timeout (0.4.2) + timeout (0.4.3) trailblazer-option (0.1.2) treetop (1.6.12) polyglot (~> 0.3) @@ -909,7 +909,7 @@ DEPENDENCIES pirate_weather_forecast_ruby puma rack-livereload - rails (= 7.0.1) + rails (~> 7.0.1) rails-controller-testing rails-html-sanitizer (~> 1.6, >= 1.6.2) rb-kqueue (>= 0.2.8)