diff options
26 files changed, 408 insertions, 186 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 77c234814a..2fdd898943 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -96,52 +96,55 @@ PATH GEM remote: https://rubygems.org/ specs: - activerecord-jdbc-adapter (1.3.24) - activerecord (>= 2.2, < 5.0) - activerecord-jdbcmysql-adapter (1.3.24) - activerecord-jdbc-adapter (~> 1.3.24) - jdbc-mysql (>= 5.1.22) - activerecord-jdbcpostgresql-adapter (1.3.24) - activerecord-jdbc-adapter (~> 1.3.24) - jdbc-postgres (~> 9.1, <= 9.4.1206) - activerecord-jdbcsqlite3-adapter (1.3.24) - activerecord-jdbc-adapter (~> 1.3.24) - jdbc-sqlite3 (>= 3.7.2, < 3.9) + activerecord-jdbc-adapter (51.1-java) + activerecord (~> 5.1.0) + activerecord-jdbcmysql-adapter (51.1-java) + activerecord-jdbc-adapter (= 51.1) + jdbc-mysql (~> 5.1.36) + activerecord-jdbcpostgresql-adapter (51.1-java) + activerecord-jdbc-adapter (= 51.1) + jdbc-postgres (>= 9.4, < 43) + activerecord-jdbcsqlite3-adapter (51.1-java) + activerecord-jdbc-adapter (= 51.1) + jdbc-sqlite3 (~> 3.8, < 3.30) addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) - amq-protocol (2.2.0) - archive-zip (0.7.0) + amq-protocol (2.3.0) + archive-zip (0.11.0) io-like (~> 0.3.0) ast (2.4.0) - aws-partitions (1.20.0) - aws-sdk-core (3.3.0) + aws-eventstream (1.0.0) + aws-partitions (1.88.0) + aws-sdk-core (3.21.2) + aws-eventstream (~> 1.0) aws-partitions (~> 1.0) aws-sigv4 (~> 1.0) jmespath (~> 1.0) - aws-sdk-kms (1.1.0) + aws-sdk-kms (1.5.0) aws-sdk-core (~> 3) aws-sigv4 (~> 1.0) - aws-sdk-s3 (1.2.0) - aws-sdk-core (~> 3) + aws-sdk-s3 (1.13.0) + aws-sdk-core (~> 3, >= 3.21.2) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.0) - aws-sigv4 (1.0.1) - azure-core (0.1.11) + aws-sigv4 (1.0.2) + azure-core (0.1.14) faraday (~> 0.9) faraday_middleware (~> 0.10) nokogiri (~> 1.6) - azure-storage (0.12.3.preview) + azure-storage (0.15.0.preview) azure-core (~> 0.1) faraday (~> 0.9) faraday_middleware (~> 0.10) + nokogiri (~> 1.6, >= 1.6.8) backburner (1.4.1) beaneater (~> 1.0) concurrent-ruby (~> 1.0.1) dante (> 0.1.5) - bcrypt (3.1.11) - bcrypt (3.1.11-java) - bcrypt (3.1.11-x64-mingw32) - bcrypt (3.1.11-x86-mingw32) + bcrypt (3.1.12) + bcrypt (3.1.12-java) + bcrypt (3.1.12-x64-mingw32) + bcrypt (3.1.12-x86-mingw32) beaneater (1.0.0) benchmark-ips (2.7.2) blade (0.7.1) @@ -157,30 +160,30 @@ GEM thor (>= 0.19.1) useragent (~> 0.16.7) blade-qunit_adapter (2.0.1) - blade-sauce_labs_plugin (0.7.2) + blade-sauce_labs_plugin (0.7.3) childprocess faraday selenium-webdriver - bootsnap (1.2.1) + bootsnap (1.3.0) msgpack (~> 1.0) - bootsnap (1.2.1-java) + bootsnap (1.3.0-java) msgpack (~> 1.0) builder (3.2.3) - bunny (2.6.6) - amq-protocol (>= 2.1.0) - byebug (9.0.6) - capybara (3.0.1) + bunny (2.9.2) + amq-protocol (~> 2.3.0) + byebug (10.0.2) + capybara (3.1.1) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) xpath (~> 3.0) - childprocess (0.7.1) + childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) - chromedriver-helper (1.1.0) - archive-zip (~> 0.7.0) - nokogiri (~> 1.6) + chromedriver-helper (1.2.0) + archive-zip (~> 0.10) + nokogiri (~> 1.8) coffee-rails (4.2.2) coffee-script (>= 2.2.0) railties (>= 4.0.0) @@ -190,19 +193,19 @@ GEM coffee-script-source (1.12.2) concurrent-ruby (1.0.5) concurrent-ruby (1.0.5-java) - connection_pool (2.2.1) + connection_pool (2.2.2) cookiejar (0.3.3) - crass (1.0.3) + crass (1.0.4) curses (1.0.2) - daemons (1.2.4) + daemons (1.2.6) dalli (2.7.8) dante (0.2.0) declarative (0.0.10) declarative-option (0.1.0) - delayed_job (4.1.4) - activesupport (>= 3.0, < 5.2) - delayed_job_active_record (4.1.2) - activerecord (>= 3.0, < 5.2) + delayed_job (4.1.5) + activesupport (>= 3.0, < 5.3) + delayed_job_active_record (4.1.3) + activerecord (>= 3.0, < 5.3) delayed_job (>= 3.0, < 5) digest-crc (0.4.1) em-http-request (1.1.5) @@ -213,13 +216,13 @@ GEM http_parser.rb (>= 0.6.0) em-socksify (0.3.2) eventmachine (>= 1.0.0.beta.4) - erubi (1.7.0) + erubi (1.7.1) et-orbi (1.1.2) tzinfo event_emitter (0.2.6) - eventmachine (1.2.5) + eventmachine (1.2.7) execjs (2.7.0) - faraday (0.13.1) + faraday (0.15.2) multipart-post (>= 1.2, < 3) faraday_middleware (0.12.2) faraday (>= 0.7.4, < 1.0) @@ -238,6 +241,9 @@ GEM ffi (1.9.21-java) ffi (1.9.21-x64-mingw32) ffi (1.9.21-x86-mingw32) + fugit (1.1.1) + et-orbi (~> 1.1, >= 1.1.1) + raabro (~> 1.1) globalid (0.4.1) activesupport (>= 4.2.0) google-api-client (0.19.8) @@ -251,7 +257,7 @@ GEM google-cloud-env (~> 1.0) google-cloud-env (1.0.1) faraday (~> 0.11) - google-cloud-storage (1.11.0) + google-cloud-storage (1.12.0) digest-crc (~> 0.4) google-api-client (~> 0.19.0) google-cloud-core (~> 1.2) @@ -268,23 +274,23 @@ GEM hiredis (0.6.1-java) http_parser.rb (0.6.0) httpclient (2.8.3) - i18n (1.0.0) + i18n (1.0.1) concurrent-ruby (~> 1.0) image_processing (1.2.0) mini_magick (~> 4.0) ruby-vips (>= 2.0.10, < 3) io-like (0.3.0) - jdbc-mysql (5.1.44) - jdbc-postgres (9.4.1206) - jdbc-sqlite3 (3.8.11.2) - jmespath (1.3.1) + jdbc-mysql (5.1.46) + jdbc-postgres (42.1.4) + jdbc-sqlite3 (3.20.1) + jmespath (1.4.0) json (2.1.0) json (2.1.0-java) jwt (2.1.0) kindlerb (1.2.0) mustache nokogiri - libxml-ruby (3.0.0) + libxml-ruby (3.1.0) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -293,12 +299,12 @@ GEM logging (2.2.2) little-plugger (~> 1.1) multi_json (~> 1.10) - loofah (2.2.1) + loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.0) mini_mime (>= 0.1.1) - marcel (0.3.1) + marcel (0.3.2) mimemagic (~> 0.3.2) memoist (0.16.0) metaclass (0.0.4) @@ -323,15 +329,15 @@ GEM msgpack (1.2.4-java) msgpack (1.2.4-x64-mingw32) msgpack (1.2.4-x86-mingw32) - multi_json (1.12.2) + multi_json (1.13.1) multipart-post (2.0.0) mustache (1.0.5) mustermann (1.0.2) - mysql2 (0.5.0) - mysql2 (0.5.0-x64-mingw32) - mysql2 (0.5.0-x86-mingw32) - nio4r (2.2.0) - nio4r (2.2.0-java) + mysql2 (0.5.1) + mysql2 (0.5.1-x64-mingw32) + mysql2 (0.5.1-x86-mingw32) + nio4r (2.3.1) + nio4r (2.3.1-java) nokogiri (1.8.2) mini_portile2 (~> 2.3.0) nokogiri (1.8.2-java) @@ -343,22 +349,23 @@ GEM parallel (1.12.1) parser (2.5.1.0) ast (~> 2.4.0) - path_expander (1.0.2) + path_expander (1.0.3) pg (1.0.0) pg (1.0.0-x64-mingw32) pg (1.0.0-x86-mingw32) powerpack (0.1.1) psych (3.0.2) public_suffix (3.0.2) - puma (3.9.1) - puma (3.9.1-java) - que (0.14.0) + puma (3.11.4) + puma (3.11.4-java) + que (0.14.3) qunit-selenium (0.0.4) selenium-webdriver thor + raabro (1.1.5) racc (1.4.14) - rack (2.0.4) - rack-cache (1.7.0) + rack (2.0.5) + rack-cache (1.7.2) rack (>= 0.4) rack-protection (2.0.1) rack @@ -367,12 +374,12 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) rainbow (3.0.0) - rake (12.3.0) - rb-fsevent (0.10.2) - rdoc (6.0.1) + rake (12.3.1) + rb-fsevent (0.10.3) + rdoc (6.0.4) redcarpet (3.2.3) redis (4.0.1) redis-namespace (1.6.0) @@ -393,7 +400,7 @@ GEM resque (~> 1.26) rufus-scheduler (~> 3.2) retriable (3.1.1) - rubocop (0.54.0) + rubocop (0.56.0) parallel (~> 1.10) parser (>= 2.5) powerpack (~> 0.1) @@ -401,13 +408,13 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.9.0) - ruby-vips (2.0.10) + ruby-vips (2.0.12) ffi (~> 1.9) ruby_dep (1.5.0) rubyzip (1.2.1) - rufus-scheduler (3.4.2) - et-orbi (~> 1.0) - sass (3.5.3) + rufus-scheduler (3.5.0) + fugit (~> 1.1, >= 1.1.1) + sass (3.5.6) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) @@ -420,17 +427,17 @@ GEM tilt (>= 1.1, < 3) sdoc (1.0.0) rdoc (>= 5.0) - selenium-webdriver (3.5.1) + selenium-webdriver (3.12.0) childprocess (~> 0.5) - rubyzip (~> 1.0) - sequel (4.49.0) - serverengine (1.5.11) + rubyzip (~> 1.2) + sequel (5.8.0) + serverengine (2.0.6) sigdump (~> 0.2.2) - sidekiq (5.0.5) + sidekiq (5.1.3) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) - redis (>= 3.3.4, < 5) + redis (>= 3.3.5, < 5) sigdump (0.2.4) signet (0.8.1) addressable (~> 2.3) @@ -442,30 +449,29 @@ GEM rack (~> 2.0) rack-protection (= 2.0.1) tilt (~> 2.0) - sneakers (2.5.0) - bunny (~> 2.6.4) - serverengine (~> 1.5.11) + sneakers (2.7.0) + bunny (~> 2.9.2) + concurrent-ruby (~> 1.0) + serverengine (~> 2.0.5) thor - thread (~> 0.1.7) sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-export (1.0.0) - sprockets-rails (3.2.0) + sprockets-rails (3.2.1) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.3.13) sqlite3 (1.3.13-x64-mingw32) sqlite3 (1.3.13-x86-mingw32) - stackprof (0.2.10) - sucker_punch (2.0.2) + stackprof (0.2.11) + sucker_punch (2.0.4) concurrent-ruby (~> 1.0.0) thin (1.7.2) daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) - thread (0.1.7) thread_safe (0.3.6) thread_safe (0.3.6-java) tilt (2.0.8) @@ -474,26 +480,26 @@ GEM turbolinks-source (5.1.0) tzinfo (1.2.5) thread_safe (~> 0.1) - tzinfo-data (1.2017.2) + tzinfo-data (1.2018.5) tzinfo (>= 1.0.0) uber (0.1.0) - uglifier (3.2.0) + uglifier (4.1.10) execjs (>= 0.3.0, < 3) - unicode-display_width (1.3.2) - useragent (0.16.8) + unicode-display_width (1.3.3) + useragent (0.16.10) vegas (0.1.11) rack (>= 1.0.0) w3c_validators (1.3.3) json (>= 1.8) nokogiri (~> 1.6) wdm (0.1.1) - websocket (1.2.4) - websocket-driver (0.6.5) + websocket (1.2.8) + websocket-driver (0.7.0) websocket-extensions (>= 0.1.0) - websocket-driver (0.6.5-java) + websocket-driver (0.7.0-java) websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.2) - xpath (3.0.0) + websocket-extensions (0.1.3) + xpath (3.1.0) nokogiri (~> 1.8) PLATFORMS diff --git a/actioncable/test/channel/base_test.rb b/actioncable/test/channel/base_test.rb index 3b8eb63975..d368794f73 100644 --- a/actioncable/test/channel/base_test.rb +++ b/actioncable/test/channel/base_test.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "test_helper" +require "minitest/mock" require "stubs/test_connection" require "stubs/room" @@ -226,12 +227,13 @@ class ActionCable::Channel::BaseTest < ActiveSupport::TestCase events << ActiveSupport::Notifications::Event.new(*args) end - @channel.stubs(:subscription_confirmation_sent?).returns(false) - @channel.send(:transmit_subscription_confirmation) + @channel.stub(:subscription_confirmation_sent?, false) do + @channel.send(:transmit_subscription_confirmation) - assert_equal 1, events.length - assert_equal "transmit_subscription_confirmation.action_cable", events[0].name - assert_equal "ActionCable::Channel::BaseTest::ChatChannel", events[0].payload[:channel_class] + assert_equal 1, events.length + assert_equal "transmit_subscription_confirmation.action_cable", events[0].name + assert_equal "ActionCable::Channel::BaseTest::ChatChannel", events[0].payload[:channel_class] + end ensure ActiveSupport::Notifications.unsubscribe "transmit_subscription_confirmation.action_cable" end diff --git a/actioncable/test/channel/broadcasting_test.rb b/actioncable/test/channel/broadcasting_test.rb index ab58f33511..f184147c51 100644 --- a/actioncable/test/channel/broadcasting_test.rb +++ b/actioncable/test/channel/broadcasting_test.rb @@ -1,10 +1,13 @@ # frozen_string_literal: true require "test_helper" +require "active_support/testing/method_call_assertions" require "stubs/test_connection" require "stubs/room" class ActionCable::Channel::BroadcastingTest < ActiveSupport::TestCase + include ActiveSupport::Testing::MethodCallAssertions + class ChatChannel < ActionCable::Channel::Base end @@ -13,8 +16,16 @@ class ActionCable::Channel::BroadcastingTest < ActiveSupport::TestCase end test "broadcasts_to" do - ActionCable.stubs(:server).returns mock().tap { |m| m.expects(:broadcast).with("action_cable:channel:broadcasting_test:chat:Room#1-Campfire", "Hello World") } - ChatChannel.broadcast_to(Room.new(1), "Hello World") + assert_called_with( + ActionCable.server, + :broadcast, + [ + "action_cable:channel:broadcasting_test:chat:Room#1-Campfire", + "Hello World" + ] + ) do + ChatChannel.broadcast_to(Room.new(1), "Hello World") + end end test "broadcasting_for with an object" do diff --git a/actioncable/test/channel/periodic_timers_test.rb b/actioncable/test/channel/periodic_timers_test.rb index 500b984ca6..8d9482577c 100644 --- a/actioncable/test/channel/periodic_timers_test.rb +++ b/actioncable/test/channel/periodic_timers_test.rb @@ -4,8 +4,11 @@ require "test_helper" require "stubs/test_connection" require "stubs/room" require "active_support/time" +require "active_support/testing/method_call_assertions" class ActionCable::Channel::PeriodicTimersTest < ActiveSupport::TestCase + include ActiveSupport::Testing::MethodCallAssertions + class ChatChannel < ActionCable::Channel::Base # Method name arg periodically :send_updates, every: 1 @@ -64,11 +67,22 @@ class ActionCable::Channel::PeriodicTimersTest < ActiveSupport::TestCase end test "timer start and stop" do - @connection.server.event_loop.expects(:timer).times(3).returns(stub(shutdown: nil)) - channel = ChatChannel.new @connection, "{id: 1}", id: 1 + mock = Minitest::Mock.new + 3.times { mock.expect(:shutdown, nil) } + + assert_called( + @connection.server.event_loop, + :timer, + times: 3, + returns: mock + ) do + channel = ChatChannel.new @connection, "{id: 1}", id: 1 + + channel.subscribe_to_channel + channel.unsubscribe_from_channel + assert_equal [], channel.send(:active_periodic_timers) + end - channel.subscribe_to_channel - channel.unsubscribe_from_channel - assert_equal [], channel.send(:active_periodic_timers) + assert mock.verify end end diff --git a/actioncable/test/channel/rejection_test.rb b/actioncable/test/channel/rejection_test.rb index a6da014a21..897efeb65a 100644 --- a/actioncable/test/channel/rejection_test.rb +++ b/actioncable/test/channel/rejection_test.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "test_helper" +require "minitest/mock" require "stubs/test_connection" require "stubs/room" @@ -20,24 +21,36 @@ class ActionCable::Channel::RejectionTest < ActiveSupport::TestCase end test "subscription rejection" do - @connection.expects(:subscriptions).returns mock().tap { |m| m.expects(:remove_subscription).with instance_of(SecretChannel) } - @channel = SecretChannel.new @connection, "{id: 1}", id: 1 - @channel.subscribe_to_channel + subscriptions = Minitest::Mock.new + subscriptions.expect(:remove_subscription, SecretChannel, [SecretChannel]) - expected = { "identifier" => "{id: 1}", "type" => "reject_subscription" } - assert_equal expected, @connection.last_transmission + @connection.stub(:subscriptions, subscriptions) do + @channel = SecretChannel.new @connection, "{id: 1}", id: 1 + @channel.subscribe_to_channel + + expected = { "identifier" => "{id: 1}", "type" => "reject_subscription" } + assert_equal expected, @connection.last_transmission + end + + assert subscriptions.verify end test "does not execute action if subscription is rejected" do - @connection.expects(:subscriptions).returns mock().tap { |m| m.expects(:remove_subscription).with instance_of(SecretChannel) } - @channel = SecretChannel.new @connection, "{id: 1}", id: 1 - @channel.subscribe_to_channel + subscriptions = Minitest::Mock.new + subscriptions.expect(:remove_subscription, SecretChannel, [SecretChannel]) - expected = { "identifier" => "{id: 1}", "type" => "reject_subscription" } - assert_equal expected, @connection.last_transmission - assert_equal 1, @connection.transmissions.size + @connection.stub(:subscriptions, subscriptions) do + @channel = SecretChannel.new @connection, "{id: 1}", id: 1 + @channel.subscribe_to_channel + + expected = { "identifier" => "{id: 1}", "type" => "reject_subscription" } + assert_equal expected, @connection.last_transmission + assert_equal 1, @connection.transmissions.size + + @channel.perform_action("action" => :secret_action) + assert_equal 1, @connection.transmissions.size + end - @channel.perform_action("action" => :secret_action) - assert_equal 1, @connection.transmissions.size + assert subscriptions.verify end end diff --git a/actioncable/test/channel/stream_test.rb b/actioncable/test/channel/stream_test.rb index b4b118846d..ed42f1acd4 100644 --- a/actioncable/test/channel/stream_test.rb +++ b/actioncable/test/channel/stream_test.rb @@ -2,6 +2,7 @@ require "test_helper" require "active_support/testing/method_call_assertions" +require "minitest/mock" require "stubs/test_connection" require "stubs/room" @@ -54,40 +55,58 @@ module ActionCable::StreamTests test "streaming start and stop" do run_in_eventmachine do connection = TestConnection.new - connection.pubsub.expects(:subscribe).with("test_room_1", kind_of(Proc), kind_of(Proc)) - channel = ChatChannel.new connection, "{id: 1}", id: 1 - channel.subscribe_to_channel + pubsub = Minitest::Mock.new connection.pubsub - wait_for_async + pubsub.expect(:subscribe, nil, ["test_room_1", Proc, Proc]) + pubsub.expect(:unsubscribe, nil, ["test_room_1", Proc]) + + connection.stub(:pubsub, pubsub) do + channel = ChatChannel.new connection, "{id: 1}", id: 1 + channel.subscribe_to_channel - connection.pubsub.expects(:unsubscribe) - channel.unsubscribe_from_channel + wait_for_async + channel.unsubscribe_from_channel + end + + assert pubsub.verify end end test "stream from non-string channel" do run_in_eventmachine do connection = TestConnection.new - connection.pubsub.expects(:subscribe).with("channel", kind_of(Proc), kind_of(Proc)) + pubsub = Minitest::Mock.new connection.pubsub - channel = SymbolChannel.new connection, "" - channel.subscribe_to_channel + pubsub.expect(:subscribe, nil, ["channel", Proc, Proc]) + pubsub.expect(:unsubscribe, nil, ["channel", Proc]) - wait_for_async + connection.stub(:pubsub, pubsub) do + channel = SymbolChannel.new connection, "" + channel.subscribe_to_channel + + wait_for_async + + channel.unsubscribe_from_channel + end - connection.pubsub.expects(:unsubscribe) - channel.unsubscribe_from_channel + assert pubsub.verify end end test "stream_for" do run_in_eventmachine do connection = TestConnection.new - connection.pubsub.expects(:subscribe).with("action_cable:stream_tests:chat:Room#1-Campfire", kind_of(Proc), kind_of(Proc)) channel = ChatChannel.new connection, "" channel.subscribe_to_channel channel.stream_for Room.new(1) + wait_for_async + + pubsub_call = channel.pubsub.class.class_variable_get "@@subscribe_called" + + assert_equal "action_cable:stream_tests:chat:Room#1-Campfire", pubsub_call[:channel] + assert_instance_of Proc, pubsub_call[:callback] + assert_instance_of Proc, pubsub_call[:success_callback] end end diff --git a/actioncable/test/connection/client_socket_test.rb b/actioncable/test/connection/client_socket_test.rb index da72501c8e..07bdc7c52a 100644 --- a/actioncable/test/connection/client_socket_test.rb +++ b/actioncable/test/connection/client_socket_test.rb @@ -43,10 +43,11 @@ class ActionCable::Connection::ClientSocketTest < ActionCable::TestCase # Internal hax = :( client = connection.websocket.send(:websocket) - client.instance_variable_get("@stream").expects(:write).raises("foo") + client.instance_variable_get("@stream").stub(:write, proc { raise "foo" }) do - assert_not_called(client, :client_gone) do - client.write("boo") + assert_not_called(client, :client_gone) do + client.write("boo") + end end assert_equal %w[ foo ], connection.errors end diff --git a/actioncable/test/connection/identifier_test.rb b/actioncable/test/connection/identifier_test.rb index 204197c2a7..a7e23b4cd8 100644 --- a/actioncable/test/connection/identifier_test.rb +++ b/actioncable/test/connection/identifier_test.rb @@ -30,13 +30,16 @@ class ActionCable::Connection::IdentifierTest < ActionCable::TestCase run_in_eventmachine do server = TestServer.new - server.pubsub.expects(:subscribe) - .with("action_cable/User#lifo", kind_of(Proc)) - server.pubsub.expects(:unsubscribe) - .with("action_cable/User#lifo", kind_of(Proc)) - open_connection(server) close_connection + wait_for_async + + %w[subscribe unsubscribe].each do |method| + pubsub_call = server.pubsub.class.class_variable_get "@@#{method}_called" + + assert_equal "action_cable/User#lifo", pubsub_call[:channel] + assert_instance_of Proc, pubsub_call[:callback] + end end end diff --git a/actioncable/test/connection/stream_test.rb b/actioncable/test/connection/stream_test.rb index 1e1466af31..daf7c37c79 100644 --- a/actioncable/test/connection/stream_test.rb +++ b/actioncable/test/connection/stream_test.rb @@ -2,6 +2,7 @@ require "test_helper" require "active_support/testing/method_call_assertions" +require "minitest/mock" require "stubs/test_server" class ActionCable::Connection::StreamTest < ActionCable::TestCase @@ -44,10 +45,11 @@ class ActionCable::Connection::StreamTest < ActionCable::TestCase # Internal hax = :( client = connection.websocket.send(:websocket) - client.instance_variable_get("@stream").instance_variable_get("@rack_hijack_io").expects(:write).raises(closed_exception, "foo") - - assert_called(client, :client_gone) do - client.write("boo") + rack_hijack_io = client.instance_variable_get("@stream").instance_variable_get("@rack_hijack_io") + rack_hijack_io.stub(:write, proc { raise(closed_exception, "foo") }) do + assert_called(client, :client_gone) do + client.write("boo") + end end assert_equal [], connection.errors end diff --git a/actioncable/test/stubs/test_adapter.rb b/actioncable/test/stubs/test_adapter.rb index c481046973..5f23a137ea 100644 --- a/actioncable/test/stubs/test_adapter.rb +++ b/actioncable/test/stubs/test_adapter.rb @@ -1,12 +1,16 @@ # frozen_string_literal: true class SuccessAdapter < ActionCable::SubscriptionAdapter::Base + class << self; attr_accessor :subscribe_called, :unsubscribe_called end + def broadcast(channel, payload) end def subscribe(channel, callback, success_callback = nil) + @@subscribe_called = { channel: channel, callback: callback, success_callback: success_callback } end def unsubscribe(channel, callback) + @@unsubscribe_called = { channel: channel, callback: callback } end end diff --git a/actioncable/test/stubs/test_connection.rb b/actioncable/test/stubs/test_connection.rb index fdddd1159e..155c68e38c 100644 --- a/actioncable/test/stubs/test_connection.rb +++ b/actioncable/test/stubs/test_connection.rb @@ -3,7 +3,7 @@ require "stubs/user" class TestConnection - attr_reader :identifiers, :logger, :current_user, :server, :transmissions + attr_reader :identifiers, :logger, :current_user, :server, :subscriptions, :transmissions delegate :pubsub, to: :server diff --git a/actioncable/test/test_helper.rb b/actioncable/test/test_helper.rb index 2f186b7193..755f7b71b4 100644 --- a/actioncable/test/test_helper.rb +++ b/actioncable/test/test_helper.rb @@ -4,7 +4,6 @@ require "action_cable" require "active_support/testing/autorun" require "puma" -require "mocha/minitest" require "rack/mock" begin diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index 6d91d4fbd6..88e8a36eaa 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -6,6 +6,14 @@ *Gannon McGibbon* +* Add `Base.unregister_observer`, `Base.unregister_observers`, + `Base.unregister_interceptor`, `Base.unregister_interceptors`, + `Base.unregister_preview_interceptor` and `Base.unregister_preview_interceptors`. + This makes it possible to dynamically add and remove email observers and + interceptors at runtime in the same way they're registered. + + *Claudio Ortolina*, *Kota Miyake* + * Rails 6 requires Ruby 2.4.1 or newer. *Jeremy Daer* diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 3af95081ee..7f22af83b0 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -475,11 +475,21 @@ module ActionMailer observers.flatten.compact.each { |observer| register_observer(observer) } end + # Unregister one or more previously registered Observers. + def unregister_observers(*observers) + observers.flatten.compact.each { |observer| unregister_observer(observer) } + end + # Register one or more Interceptors which will be called before mail is sent. def register_interceptors(*interceptors) interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) } end + # Unregister one or more previously registered Interceptors. + def unregister_interceptors(*interceptors) + interceptors.flatten.compact.each { |interceptor| unregister_interceptor(interceptor) } + end + # Register an Observer which will be notified when mail is delivered. # Either a class, string or symbol can be passed in as the Observer. # If a string or symbol is passed in it will be camelized and constantized. @@ -487,6 +497,13 @@ module ActionMailer Mail.register_observer(observer_class_for(observer)) end + # Unregister a previously registered Observer. + # Either a class, string or symbol can be passed in as the Observer. + # If a string or symbol is passed in it will be camelized and constantized. + def unregister_observer(observer) + Mail.unregister_observer(observer_class_for(observer)) + end + # Register an Interceptor which will be called before mail is sent. # Either a class, string or symbol can be passed in as the Interceptor. # If a string or symbol is passed in it will be camelized and constantized. @@ -494,6 +511,13 @@ module ActionMailer Mail.register_interceptor(observer_class_for(interceptor)) end + # Unregister a previously registered Interceptor. + # Either a class, string or symbol can be passed in as the Interceptor. + # If a string or symbol is passed in it will be camelized and constantized. + def unregister_interceptor(interceptor) + Mail.unregister_interceptor(observer_class_for(interceptor)) + end + def observer_class_for(value) # :nodoc: case value when String, Symbol diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb index 0aea84fd2b..500b3bede0 100644 --- a/actionmailer/lib/action_mailer/preview.rb +++ b/actionmailer/lib/action_mailer/preview.rb @@ -31,22 +31,39 @@ module ActionMailer interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) } end + # Unregister one or more previously registered Interceptors. + def unregister_preview_interceptors(*interceptors) + interceptors.flatten.compact.each { |interceptor| unregister_preview_interceptor(interceptor) } + end + # Register an Interceptor which will be called before mail is previewed. # Either a class or a string can be passed in as the Interceptor. If a # string is passed in it will be constantized. def register_preview_interceptor(interceptor) - preview_interceptor = \ + preview_interceptor = interceptor_class_for(interceptor) + + unless preview_interceptors.include?(preview_interceptor) + preview_interceptors << preview_interceptor + end + end + + # Unregister a previously registered Interceptor. + # Either a class or a string can be passed in as the Interceptor. If a + # string is passed in it will be constantized. + def unregister_preview_interceptor(interceptor) + preview_interceptors.delete(interceptor_class_for(interceptor)) + end + + private + + def interceptor_class_for(interceptor) case interceptor when String, Symbol interceptor.to_s.camelize.constantize else interceptor end - - unless preview_interceptors.include?(preview_interceptor) - preview_interceptors << preview_interceptor end - end end end diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 4124aa00bd..7898996c30 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -618,37 +618,52 @@ class BaseTest < ActiveSupport::TestCase end end - test "you can register an observer to the mail object that gets informed on email delivery" do + test "you can register and unregister an observer to the mail object that gets informed on email delivery" do mail_side_effects do ActionMailer::Base.register_observer(MyObserver) mail = BaseMailer.welcome assert_called_with(MyObserver, :delivered_email, [mail]) do mail.deliver_now end + + ActionMailer::Base.unregister_observer(MyObserver) + assert_not_called(MyObserver, :delivered_email, returns: mail) do + mail.deliver_now + end end end - test "you can register an observer using its stringified name to the mail object that gets informed on email delivery" do + test "you can register and unregister an observer using its stringified name to the mail object that gets informed on email delivery" do mail_side_effects do ActionMailer::Base.register_observer("BaseTest::MyObserver") mail = BaseMailer.welcome assert_called_with(MyObserver, :delivered_email, [mail]) do mail.deliver_now end + + ActionMailer::Base.unregister_observer("BaseTest::MyObserver") + assert_not_called(MyObserver, :delivered_email, returns: mail) do + mail.deliver_now + end end end - test "you can register an observer using its symbolized underscored name to the mail object that gets informed on email delivery" do + test "you can register and unregister an observer using its symbolized underscored name to the mail object that gets informed on email delivery" do mail_side_effects do ActionMailer::Base.register_observer(:"base_test/my_observer") mail = BaseMailer.welcome assert_called_with(MyObserver, :delivered_email, [mail]) do mail.deliver_now end + + ActionMailer::Base.unregister_observer(:"base_test/my_observer") + assert_not_called(MyObserver, :delivered_email, returns: mail) do + mail.deliver_now + end end end - test "you can register multiple observers to the mail object that both get informed on email delivery" do + test "you can register and unregister multiple observers to the mail object that both get informed on email delivery" do mail_side_effects do ActionMailer::Base.register_observers("BaseTest::MyObserver", MySecondObserver) mail = BaseMailer.welcome @@ -657,6 +672,14 @@ class BaseTest < ActiveSupport::TestCase mail.deliver_now end end + + ActionMailer::Base.unregister_observers("BaseTest::MyObserver", MySecondObserver) + assert_not_called(MyObserver, :delivered_email, returns: mail) do + mail.deliver_now + end + assert_not_called(MySecondObserver, :delivered_email, returns: mail) do + mail.deliver_now + end end end @@ -670,37 +693,52 @@ class BaseTest < ActiveSupport::TestCase def self.previewing_email(mail); end end - test "you can register an interceptor to the mail object that gets passed the mail object before delivery" do + test "you can register and unregister an interceptor to the mail object that gets passed the mail object before delivery" do mail_side_effects do ActionMailer::Base.register_interceptor(MyInterceptor) mail = BaseMailer.welcome assert_called_with(MyInterceptor, :delivering_email, [mail]) do mail.deliver_now end + + ActionMailer::Base.unregister_interceptor(MyInterceptor) + assert_not_called(MyInterceptor, :delivering_email, returns: mail) do + mail.deliver_now + end end end - test "you can register an interceptor using its stringified name to the mail object that gets passed the mail object before delivery" do + test "you can register and unregister an interceptor using its stringified name to the mail object that gets passed the mail object before delivery" do mail_side_effects do ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor") mail = BaseMailer.welcome assert_called_with(MyInterceptor, :delivering_email, [mail]) do mail.deliver_now end + + ActionMailer::Base.unregister_interceptor("BaseTest::MyInterceptor") + assert_not_called(MyInterceptor, :delivering_email, returns: mail) do + mail.deliver_now + end end end - test "you can register an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before delivery" do + test "you can register and unregister an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before delivery" do mail_side_effects do ActionMailer::Base.register_interceptor(:"base_test/my_interceptor") mail = BaseMailer.welcome assert_called_with(MyInterceptor, :delivering_email, [mail]) do mail.deliver_now end + + ActionMailer::Base.unregister_interceptor(:"base_test/my_interceptor") + assert_not_called(MyInterceptor, :delivering_email, returns: mail) do + mail.deliver_now + end end end - test "you can register multiple interceptors to the mail object that both get passed the mail object before delivery" do + test "you can register and unregister multiple interceptors to the mail object that both get passed the mail object before delivery" do mail_side_effects do ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor) mail = BaseMailer.welcome @@ -709,6 +747,14 @@ class BaseTest < ActiveSupport::TestCase mail.deliver_now end end + + ActionMailer::Base.unregister_interceptors("BaseTest::MyInterceptor", MySecondInterceptor) + assert_not_called(MyInterceptor, :delivering_email, returns: mail) do + mail.deliver_now + end + assert_not_called(MySecondInterceptor, :delivering_email, returns: mail) do + mail.deliver_now + end end end @@ -888,8 +934,6 @@ class BaseTest < ActiveSupport::TestCase klass.default_params = old end - # A simple hack to restore the observers and interceptors for Mail, as it - # does not have an unregister API yet. def mail_side_effects old_observers = Mail.class_variable_get(:@@delivery_notification_observers) old_delivery_interceptors = Mail.class_variable_get(:@@delivery_interceptors) @@ -928,7 +972,7 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase def self.previewing_email(mail); end end - test "you can register a preview interceptor to the mail object that gets passed the mail object before previewing" do + test "you can register and unregister a preview interceptor to the mail object that gets passed the mail object before previewing" do ActionMailer::Base.register_preview_interceptor(MyInterceptor) mail = BaseMailer.welcome stub_any_instance(BaseMailerPreview) do |instance| @@ -938,9 +982,14 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase end end end + + ActionMailer::Base.unregister_preview_interceptor(MyInterceptor) + assert_not_called(MyInterceptor, :previewing_email, returns: mail) do + BaseMailerPreview.call(:welcome) + end end - test "you can register a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do + test "you can register and unregister a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do ActionMailer::Base.register_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor") mail = BaseMailer.welcome stub_any_instance(BaseMailerPreview) do |instance| @@ -950,9 +999,14 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase end end end + + ActionMailer::Base.unregister_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor") + assert_not_called(MyInterceptor, :previewing_email, returns: mail) do + BaseMailerPreview.call(:welcome) + end end - test "you can register an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do + test "you can register and unregister a preview interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do ActionMailer::Base.register_preview_interceptor(:"base_preview_interceptors_test/my_interceptor") mail = BaseMailer.welcome stub_any_instance(BaseMailerPreview) do |instance| @@ -962,9 +1016,14 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase end end end + + ActionMailer::Base.unregister_preview_interceptor(:"base_preview_interceptors_test/my_interceptor") + assert_not_called(MyInterceptor, :previewing_email, returns: mail) do + BaseMailerPreview.call(:welcome) + end end - test "you can register multiple preview interceptors to the mail object that both get passed the mail object before previewing" do + test "you can register and unregister multiple preview interceptors to the mail object that both get passed the mail object before previewing" do ActionMailer::Base.register_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor) mail = BaseMailer.welcome stub_any_instance(BaseMailerPreview) do |instance| @@ -976,6 +1035,14 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase end end end + + ActionMailer::Base.unregister_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor) + assert_not_called(MyInterceptor, :previewing_email, returns: mail) do + BaseMailerPreview.call(:welcome) + end + assert_not_called(MySecondInterceptor, :previewing_email, returns: mail) do + BaseMailerPreview.call(:welcome) + end end end diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 953f3c47ed..4bae795438 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -275,7 +275,7 @@ module ActionController #:nodoc: # Check for cross-origin JavaScript responses. def non_xhr_javascript_response? # :doc: - content_type =~ %r(\Atext/javascript) && !request.xhr? + content_type =~ %r(\A(?:text|application)/javascript) && !request.xhr? end AUTHENTICITY_TOKEN_LENGTH = 32 diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 7a02c27c99..ea94a3e048 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -521,6 +521,11 @@ module RequestForgeryProtectionTests get :negotiate_same_origin end + assert_cross_origin_blocked do + @request.accept = "application/javascript" + get :negotiate_same_origin + end + assert_cross_origin_not_blocked { get :same_origin_js, xhr: true } assert_cross_origin_not_blocked { get :same_origin_js, xhr: true, format: "js" } assert_cross_origin_not_blocked do diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 5cefb5dfd7..d101b81e28 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Bump minimum SQLite version to 3.8 + + *Yasuo Honda* + * Fix parent record should not get saved with duplicate children records. Fixes #32940. diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index d36c951292..203e4118c5 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -168,6 +168,16 @@ class ActiveStorage::Blob < ActiveRecord::Base # Downloads the blob to a tempfile on disk. Yields the tempfile. # + # The tempfile's name is prefixed with +ActiveStorage-+ and the blob's ID. Its extension matches that of the blob. + # + # By default, the tempfile is created in <tt>Dir.tmpdir</tt>. Pass +tempdir:+ to create it in a different directory: + # + # blob.open(tempdir: "/path/to/tmp") do |file| + # # ... + # end + # + # The tempfile is automatically closed and unlinked after the given block is executed. + # # Raises ActiveStorage::IntegrityError if the downloaded data does not match the blob's checksum. def open(tempdir: nil, &block) ActiveStorage::Downloader.new(self, tempdir: tempdir).download_blob_to_tempfile(&block) diff --git a/activestorage/lib/active_storage/downloader.rb b/activestorage/lib/active_storage/downloader.rb index 2aa56a729a..87be6efb05 100644 --- a/activestorage/lib/active_storage/downloader.rb +++ b/activestorage/lib/active_storage/downloader.rb @@ -19,7 +19,7 @@ module ActiveStorage attr_reader :blob, :tempdir def open_tempfile - file = Tempfile.open([ "ActiveStorage", tempfile_extension_with_delimiter ], tempdir) + file = Tempfile.open([ "ActiveStorage-#{blob.id}-", blob.filename.extension_with_delimiter ], tempdir) begin yield file @@ -40,9 +40,5 @@ module ActiveStorage raise ActiveStorage::IntegrityError end end - - def tempfile_extension_with_delimiter - blob.filename.extension_with_delimiter - end end end diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb index 2d1857041d..88ce0f868a 100644 --- a/activestorage/test/models/blob_test.rb +++ b/activestorage/test/models/blob_test.rb @@ -85,11 +85,14 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase end test "open with integrity" do - create_file_blob(filename: "racecar.jpg").open do |file| - assert file.binmode? - assert_equal 0, file.pos - assert_match(/\.jpg\z/, file.path) - assert_equal file_fixture("racecar.jpg").binread, file.read, "Expected downloaded file to match fixture file" + create_file_blob(filename: "racecar.jpg").tap do |blob| + blob.open do |file| + assert file.binmode? + assert_equal 0, file.pos + assert File.basename(file.path).starts_with?("ActiveStorage-#{blob.id}-") + assert file.path.ends_with?(".jpg") + assert_equal file_fixture("racecar.jpg").binread, file.read, "Expected downloaded file to match fixture file" + end end end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 91818c3112..100d57aa16 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -20,7 +20,7 @@ %i( title body ).index_with(nil) # => { title: nil, body: nil } - Closely linked with its brethen `index_by`. + Closely linked with `index_by`, which creates a hash where the keys are extracted from a block. *Kasper Timm Hansen* diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb index ce8bfbfd8c..93bde57f6a 100644 --- a/activesupport/lib/active_support/i18n_railtie.rb +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "active_support" -require "active_support/file_update_checker" require "active_support/core_ext/array/wrap" # :enddoc: diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 5f709c5fd9..792c88415c 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -354,8 +354,13 @@ module ActiveSupport # Time.zone = 'Hawaii' # => "Hawaii" # Time.utc(2000).to_f # => 946684800.0 # Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00 - def at(secs) - Time.at(secs).utc.in_time_zone(self) + # + # A second argument can be supplied to specify sub-second precision. + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.at(946684800, 123456.789).nsec # => 123456789 + def at(*args) + Time.at(*args).utc.in_time_zone(self) end # Method for creating new ActiveSupport::TimeWithZone instance in time zone diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index b59f3e9405..6d45a6726d 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -225,6 +225,16 @@ class TimeZoneTest < ActiveSupport::TestCase assert_equal secs, twz.to_f end + def test_at_with_microseconds + zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] + secs = 946684800.0 + microsecs = 123456.789 + twz = zone.at(secs, microsecs) + assert_equal zone, twz.time_zone + assert_equal secs, twz.to_i + assert_equal 123456789, twz.nsec + end + def test_iso8601 zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] twz = zone.iso8601("1999-12-31T19:00:00") |