From fcc0e7ff75b6be201b263cc99bbe3335e3c16379 Mon Sep 17 00:00:00 2001 From: Jacopo Date: Wed, 15 Nov 2023 15:52:05 +0100 Subject: [PATCH 1/5] Remove extra space --- lib/solid_cache/maglev_hash.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solid_cache/maglev_hash.rb b/lib/solid_cache/maglev_hash.rb index ee9189d..6fc4a98 100644 --- a/lib/solid_cache/maglev_hash.rb +++ b/lib/solid_cache/maglev_hash.rb @@ -4,7 +4,7 @@ module SolidCache class MaglevHash attr_reader :nodes - #  Must be prime + # Must be prime TABLE_SIZE = 2053 def initialize(nodes) From 2c3046ed7f1ec10c6d240eb8d5e46d20955173dd Mon Sep 17 00:00:00 2001 From: Jacopo Date: Thu, 16 Nov 2023 08:56:56 +0100 Subject: [PATCH 2/5] Handle outdated Marshal payloads in Cache::Entry with 6.1 cache_format This is the same fix of https://github.com/rails/rails/pull/49716 but applied to SolidCache Store. --- lib/solid_cache/store/api.rb | 9 ++++- test/unit/behaviors.rb | 1 + .../cache_store_format_version_behavior.rb | 33 +++++++++++++++++++ test/unit/solid_cache_test.rb | 1 + 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 test/unit/behaviors/cache_store_format_version_behavior.rb diff --git a/lib/solid_cache/store/api.rb b/lib/solid_cache/store/api.rb index abde5a4..6da663e 100644 --- a/lib/solid_cache/store/api.rb +++ b/lib/solid_cache/store/api.rb @@ -88,7 +88,14 @@ def read_multi_entries(names, **options) if entry.expired? delete_entry(key, **options) elsif !entry.mismatched?(version) - results[name] = entry.value + if defined? ActiveSupport::Cache::DeserializationError + begin + results[name] = entry.value + rescue ActiveSupport::Cache::DeserializationError + end + else + results[name] = entry.value + end end end end diff --git a/test/unit/behaviors.rb b/test/unit/behaviors.rb index d7bf62e..0bcd67f 100644 --- a/test/unit/behaviors.rb +++ b/test/unit/behaviors.rb @@ -5,6 +5,7 @@ require_relative "behaviors/cache_instrumentation_behavior" require_relative "behaviors/cache_store_behavior" require_relative "behaviors/cache_store_version_behavior" +require_relative "behaviors/cache_store_format_version_behavior" require_relative "behaviors/cache_store_coder_behavior" require_relative "behaviors/connection_pool_behavior" require_relative "behaviors/encoded_key_cache_behavior" diff --git a/test/unit/behaviors/cache_store_format_version_behavior.rb b/test/unit/behaviors/cache_store_format_version_behavior.rb new file mode 100644 index 0000000..b7abbbb --- /dev/null +++ b/test/unit/behaviors/cache_store_format_version_behavior.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module CacheStoreFormatVersionBehavior + FORMAT_VERSIONS = [ 6.1, 7.0, 7.1 ] + + FORMAT_VERSIONS.each do |format_version| + define_method "test_marshal_undefined_class_deserialization_error_with_format_#{format_version}" do + with_format(format_version) do |cache| + + key = "marshal-#{rand}" + self.class.const_set(:RemovedConstant, Class.new) + + value_to_compress = [ self.class::RemovedConstant.new, "0" * 100 ] + cache.write(key, value_to_compress, compress: true, compress_threshold: 1) + self.class.send(:remove_const, :RemovedConstant) + assert_equal({}, cache.read_multi(key)) + end + ensure + self.class.send(:remove_const, :RemovedConstant) rescue nil + end + end + + private + def with_format(format_version) + if format_version == 6.1 + ActiveSupport.deprecator.silence do + ActiveSupport::Cache.with(format_version: format_version) { yield lookup_store } + end + else + ActiveSupport::Cache.with(format_version: format_version) { yield lookup_store } + end + end +end diff --git a/test/unit/solid_cache_test.rb b/test/unit/solid_cache_test.rb index c1e2872..bf7346b 100644 --- a/test/unit/solid_cache_test.rb +++ b/test/unit/solid_cache_test.rb @@ -6,6 +6,7 @@ class SolidCacheTest < ActiveSupport::TestCase include ActiveSupport::Testing::MethodCallAssertions include CacheStoreBehavior include CacheStoreVersionBehavior + include CacheStoreFormatVersionBehavior include CacheStoreCoderBehavior include LocalCacheBehavior include CacheIncrementDecrementBehavior From c69ab540680f6eac10c691dcda5b3337f40a4ea2 Mon Sep 17 00:00:00 2001 From: Jacopo Date: Thu, 16 Nov 2023 08:59:10 +0100 Subject: [PATCH 3/5] Rails 7.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Query cache: Rely on the new private method ´internal_exec_query´ to not clear the query cache after 7.1. - Instrumentation test: Rails 7.1 need a non-nil key to instrument a query --- Gemfile.lock | 201 ++++++++++-------- app/models/solid_cache/entry.rb | 8 +- .../cache_instrumentation_behavior.rb | 8 +- test/unit/execution_test.rb | 2 +- test/unit/solid_cache_test.rb | 8 +- 5 files changed, 124 insertions(+), 103 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8300997..fcfe247 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,83 +17,96 @@ PATH GEM remote: https://rubygems.org/ specs: - actioncable (7.0.7.2) - actionpack (= 7.0.7.2) - activesupport (= 7.0.7.2) + actioncable (7.1.2) + actionpack (= 7.1.2) + activesupport (= 7.1.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.7.2) - actionpack (= 7.0.7.2) - activejob (= 7.0.7.2) - activerecord (= 7.0.7.2) - activestorage (= 7.0.7.2) - activesupport (= 7.0.7.2) + zeitwerk (~> 2.6) + actionmailbox (7.1.2) + actionpack (= 7.1.2) + activejob (= 7.1.2) + activerecord (= 7.1.2) + activestorage (= 7.1.2) + activesupport (= 7.1.2) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.7.2) - actionpack (= 7.0.7.2) - actionview (= 7.0.7.2) - activejob (= 7.0.7.2) - activesupport (= 7.0.7.2) + actionmailer (7.1.2) + actionpack (= 7.1.2) + actionview (= 7.1.2) + activejob (= 7.1.2) + activesupport (= 7.1.2) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp - rails-dom-testing (~> 2.0) - actionpack (7.0.7.2) - actionview (= 7.0.7.2) - activesupport (= 7.0.7.2) - rack (~> 2.0, >= 2.2.4) + rails-dom-testing (~> 2.2) + actionpack (7.1.2) + actionview (= 7.1.2) + activesupport (= 7.1.2) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.7.2) - actionpack (= 7.0.7.2) - activerecord (= 7.0.7.2) - activestorage (= 7.0.7.2) - activesupport (= 7.0.7.2) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actiontext (7.1.2) + actionpack (= 7.1.2) + activerecord (= 7.1.2) + activestorage (= 7.1.2) + activesupport (= 7.1.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.7.2) - activesupport (= 7.0.7.2) + actionview (7.1.2) + activesupport (= 7.1.2) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.7.2) - activesupport (= 7.0.7.2) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.1.2) + activesupport (= 7.1.2) globalid (>= 0.3.6) - activemodel (7.0.7.2) - activesupport (= 7.0.7.2) - activerecord (7.0.7.2) - activemodel (= 7.0.7.2) - activesupport (= 7.0.7.2) - activestorage (7.0.7.2) - actionpack (= 7.0.7.2) - activejob (= 7.0.7.2) - activerecord (= 7.0.7.2) - activesupport (= 7.0.7.2) + activemodel (7.1.2) + activesupport (= 7.1.2) + activerecord (7.1.2) + activemodel (= 7.1.2) + activesupport (= 7.1.2) + timeout (>= 0.4.0) + activestorage (7.1.2) + actionpack (= 7.1.2) + activejob (= 7.1.2) + activerecord (= 7.1.2) + activesupport (= 7.1.2) marcel (~> 1.0) - mini_mime (>= 1.1.0) - activesupport (7.0.7.2) + activesupport (7.1.2) + base64 + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) minitest (>= 5.1) + mutex_m tzinfo (~> 2.0) ast (2.4.2) base64 (0.1.1) + bigdecimal (3.1.4) builder (3.2.4) concurrent-ruby (1.2.2) + connection_pool (2.4.1) crass (1.0.6) - date (3.3.3) + date (3.3.4) debug (1.7.0) irb (>= 1.5.0) reline (>= 0.3.1) + drb (2.2.0) + ruby2_keywords erubi (1.12.0) - globalid (1.1.0) - activesupport (>= 5.0) + globalid (1.2.1) + activesupport (>= 6.1) i18n (1.14.1) concurrent-ruby (~> 1.0) io-console (0.5.11) @@ -101,76 +114,80 @@ GEM reline (>= 0.3.0) json (2.6.3) language_server-protocol (3.17.0.3) - loofah (2.21.3) + loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) - mail (2.8.0.1) + mail (2.8.1) mini_mime (>= 0.1.1) net-imap net-pop net-smtp marcel (1.0.2) - method_source (1.0.0) - mini_mime (1.1.2) - minitest (5.19.0) + mini_mime (1.1.5) + mini_portile2 (2.8.5) + minitest (5.20.0) mocha (2.1.0) ruby2_keywords (>= 0.0.5) + mutex_m (0.2.0) mysql2 (0.5.4) - net-imap (0.3.4) + net-imap (0.4.5) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout - net-smtp (0.3.3) + net-smtp (0.4.0) net-protocol - nio4r (2.5.8) - nokogiri (1.15.3-arm64-darwin) - racc (~> 1.4) - nokogiri (1.15.3-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.15.3-x86_64-linux) + nio4r (2.5.9) + nokogiri (1.15.4) + mini_portile2 (~> 2.8.2) racc (~> 1.4) parallel (1.23.0) parser (3.2.2.3) ast (~> 2.4.1) racc pg (1.4.5) - racc (1.7.1) - rack (2.2.8) + racc (1.7.3) + rack (3.0.8) + rack-session (2.0.0) + rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.7.2) - actioncable (= 7.0.7.2) - actionmailbox (= 7.0.7.2) - actionmailer (= 7.0.7.2) - actionpack (= 7.0.7.2) - actiontext (= 7.0.7.2) - actionview (= 7.0.7.2) - activejob (= 7.0.7.2) - activemodel (= 7.0.7.2) - activerecord (= 7.0.7.2) - activestorage (= 7.0.7.2) - activesupport (= 7.0.7.2) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + rails (7.1.2) + actioncable (= 7.1.2) + actionmailbox (= 7.1.2) + actionmailer (= 7.1.2) + actionpack (= 7.1.2) + actiontext (= 7.1.2) + actionview (= 7.1.2) + activejob (= 7.1.2) + activemodel (= 7.1.2) + activerecord (= 7.1.2) + activestorage (= 7.1.2) + activesupport (= 7.1.2) bundler (>= 1.15.0) - railties (= 7.0.7.2) - rails-dom-testing (2.1.1) + railties (= 7.1.2) + rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.0.7.2) - actionpack (= 7.0.7.2) - activesupport (= 7.0.7.2) - method_source + railties (7.1.2) + actionpack (= 7.1.2) + activesupport (= 7.1.2) + irb + rackup (>= 1.0.0) rake (>= 12.2) - thor (~> 1.0) - zeitwerk (~> 2.5) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.0.6) + rake (13.1.0) regexp_parser (2.8.1) reline (0.3.1) io-console (~> 0.5) @@ -207,18 +224,18 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - sqlite3 (1.6.6-arm64-darwin) - sqlite3 (1.6.6-x86_64-darwin) - sqlite3 (1.6.6-x86_64-linux) - thor (1.2.1) - timeout (0.3.1) + sqlite3 (1.6.6) + mini_portile2 (~> 2.8.0) + thor (1.3.0) + timeout (0.4.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) - websocket-driver (0.7.5) + webrick (1.8.1) + websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - zeitwerk (2.6.6) + zeitwerk (2.6.12) PLATFORMS arm64-darwin-21 diff --git a/app/models/solid_cache/entry.rb b/app/models/solid_cache/entry.rb index 9bc4f6b..11686d8 100644 --- a/app/models/solid_cache/entry.rb +++ b/app/models/solid_cache/entry.rb @@ -74,8 +74,12 @@ def upsert_all_no_query_cache(attributes) message = +"#{self} " message << "Bulk " if attributes.many? message << "Upsert" - # exec_query does not clear the query cache, exec_insert_all does - connection.exec_query sql, message + # exec_query_method does not clear the query cache, exec_insert_all does + connection.send exec_query_method, sql, message + end + + def exec_query_method + connection.respond_to?(:internal_exec_query) ? :internal_exec_query : :exec_query end def upsert_unique_by diff --git a/test/unit/behaviors/cache_instrumentation_behavior.rb b/test/unit/behaviors/cache_instrumentation_behavior.rb index 7a3237f..1c57796 100644 --- a/test/unit/behaviors/cache_instrumentation_behavior.rb +++ b/test/unit/behaviors/cache_instrumentation_behavior.rb @@ -44,12 +44,12 @@ def test_instrumentation_with_fetch_multi_as_super_operation def test_instrumentation_empty_fetch_multi events = with_instrumentation "read_multi" do - @cache.fetch_multi() { |key| key * 2 } + @cache.fetch_multi("key1", "key2") { |key| key * 2 } end assert_equal %w[ cache_read_multi.active_support ], events.map(&:name) assert_equal :fetch_multi, events[0].payload[:super_operation] - assert_equal [], events[0].payload[:key] + assert_equal [ "key1", "key2" ], events[0].payload[:key] assert_equal [], events[0].payload[:hits] assert_equal @cache.class.name, events[0].payload[:store] end @@ -72,11 +72,11 @@ def test_read_multi_instrumentation def test_empty_read_multi_instrumentation events = with_instrumentation "read_multi" do - @cache.read_multi() + @cache.read_multi("key1", "key2") end assert_equal %w[ cache_read_multi.active_support ], events.map(&:name) - assert_equal [], events[0].payload[:key] + assert_equal [ "key1", "key2" ], events[0].payload[:key] assert_equal [], events[0].payload[:hits] assert_equal @cache.class.name, events[0].payload[:store] end diff --git a/test/unit/execution_test.rb b/test/unit/execution_test.rb index b452d4c..9c84556 100644 --- a/test/unit/execution_test.rb +++ b/test/unit/execution_test.rb @@ -23,7 +23,7 @@ def test_async_errors_are_reported assert_equal 1, error_subscriber.errors.count assert_equal "Boom!", error_subscriber.errors.first[0].message - assert_equal({ context: {}, handled: false, level: :error, source: nil }, error_subscriber.errors.first[1]) + assert_equal({ context: {}, handled: false, level: :error, source: "application.active_support" }, error_subscriber.errors.first[1]) ensure Rails.error.unsubscribe(error_subscriber) if Rails.error.respond_to?(:unsubscribe) end diff --git a/test/unit/solid_cache_test.rb b/test/unit/solid_cache_test.rb index bf7346b..d2788b5 100644 --- a/test/unit/solid_cache_test.rb +++ b/test/unit/solid_cache_test.rb @@ -55,11 +55,11 @@ class SolidCacheFailsafeTest < ActiveSupport::TestCase def emulating_unavailability stub_matcher = ActiveRecord::Base.connection.class.any_instance - stub_matcher.stubs(:exec_query).raises(ActiveRecord::StatementInvalid) + stub_matcher.stubs(:internal_exec_query).raises(ActiveRecord::StatementInvalid) stub_matcher.stubs(:exec_delete).raises(ActiveRecord::StatementInvalid) yield ActiveSupport::Cache::SolidCacheStore.new(namespace: @namespace) ensure - stub_matcher.unstub(:exec_query) + stub_matcher.unstub(:internal_exec_query) stub_matcher.unstub(:exec_delete) end end @@ -80,12 +80,12 @@ class SolidCacheRaisingTest < ActiveSupport::TestCase def emulating_unavailability stub_matcher = ActiveRecord::Base.connection.class.any_instance - stub_matcher.stubs(:exec_query).raises(ActiveRecord::StatementInvalid) + stub_matcher.stubs(:internal_exec_query).raises(ActiveRecord::StatementInvalid) stub_matcher.stubs(:exec_delete).raises(ActiveRecord::StatementInvalid) yield ActiveSupport::Cache::SolidCacheStore.new(namespace: @namespace, error_handler: ->(method:, returning:, exception:) { raise exception }) ensure - stub_matcher.unstub(:exec_query) + stub_matcher.unstub(:internal_exec_query) stub_matcher.unstub(:exec_delete) end end From 833cd8d686305283b46ff97a57a573a45c4cbab4 Mon Sep 17 00:00:00 2001 From: Jacopo Date: Thu, 16 Nov 2023 09:18:20 +0100 Subject: [PATCH 4/5] Test multiple Rails versions - Support for multiple Rails version in CI via appraisal - Fix tests to work with multiple Rails versions --- .github/workflows/main.yml | 2 + Appraisals | 11 + Gemfile | 1 + Gemfile.lock | 48 +-- README.md | 20 ++ gemfiles/.bundle/config | 2 + gemfiles/rails_7.gemfile | 13 + gemfiles/rails_7.gemfile.lock | 262 +++++++++++++++++ gemfiles/rails_7_1.gemfile | 13 + gemfiles/rails_7_1.gemfile.lock | 275 ++++++++++++++++++ gemfiles/rails_main.gemfile | 13 + gemfiles/rails_main.gemfile.lock | 273 +++++++++++++++++ .../cache_store_format_version_behavior.rb | 24 +- test/unit/execution_test.rb | 6 +- test/unit/solid_cache_test.rb | 4 + 15 files changed, 936 insertions(+), 31 deletions(-) create mode 100644 Appraisals create mode 100644 gemfiles/.bundle/config create mode 100644 gemfiles/rails_7.gemfile create mode 100644 gemfiles/rails_7.gemfile.lock create mode 100644 gemfiles/rails_7_1.gemfile create mode 100644 gemfiles/rails_7_1.gemfile.lock create mode 100644 gemfiles/rails_main.gemfile create mode 100644 gemfiles/rails_main.gemfile.lock diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4371c90..76e2489 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,6 +22,7 @@ jobs: fail-fast: false matrix: ruby-version: [3.2.2] + gemfile: [ rails_7, rails_7_1, rails_main ] database: [sqlite, postgres, mysql] services: redis: @@ -43,6 +44,7 @@ jobs: options: --health-cmd "mysql -h localhost -e \"select now()\"" --health-interval 1s --health-timeout 5s --health-retries 30 env: TARGET_DB: ${{ matrix.database }} + BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/Appraisals b/Appraisals new file mode 100644 index 0000000..6596d08 --- /dev/null +++ b/Appraisals @@ -0,0 +1,11 @@ +appraise "rails-7" do + gem "rails", github: "rails/rails", branch: "7-0-stable" +end + +appraise "rails-7-1" do + gem "rails", github: "rails/rails", branch: "7-1-stable" +end + +appraise "rails-main" do + gem "rails", github: "rails/rails", branch: "main" +end diff --git a/Gemfile b/Gemfile index 3b7c727..785aa43 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ gem "pg" gem "sprockets-rails" gem "rubocop-37signals", github: "basecamp/house-style", require: false +gem "appraisal" # Start debugger with binding.b [https://github.com/ruby/debug] # gem "debug", ">= 1.0.0" diff --git a/Gemfile.lock b/Gemfile.lock index fcfe247..b5090cc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -91,15 +91,19 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) ast (2.4.2) - base64 (0.1.1) + base64 (0.2.0) bigdecimal (3.1.4) builder (3.2.4) concurrent-ruby (1.2.2) connection_pool (2.4.1) crass (1.0.6) date (3.3.4) - debug (1.7.0) + debug (1.8.0) irb (>= 1.5.0) reline (>= 0.3.1) drb (2.2.0) @@ -109,9 +113,10 @@ GEM activesupport (>= 6.1) i18n (1.14.1) concurrent-ruby (~> 1.0) - io-console (0.5.11) - irb (1.6.0) - reline (>= 0.3.0) + io-console (0.6.0) + irb (1.9.0) + rdoc + reline (>= 0.3.8) json (2.6.3) language_server-protocol (3.17.0.3) loofah (2.22.0) @@ -129,7 +134,7 @@ GEM mocha (2.1.0) ruby2_keywords (>= 0.0.5) mutex_m (0.2.0) - mysql2 (0.5.4) + mysql2 (0.5.5) net-imap (0.4.5) date net-protocol @@ -144,10 +149,12 @@ GEM mini_portile2 (~> 2.8.2) racc (~> 1.4) parallel (1.23.0) - parser (3.2.2.3) + parser (3.2.2.4) ast (~> 2.4.1) racc - pg (1.4.5) + pg (1.5.4) + psych (5.1.1.1) + stringio racc (1.7.3) rack (3.0.8) rack-session (2.0.0) @@ -188,49 +195,51 @@ GEM zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.1.0) - regexp_parser (2.8.1) - reline (0.3.1) + rdoc (6.6.0) + psych (>= 4.0.0) + regexp_parser (2.8.2) + reline (0.4.0) io-console (~> 0.5) rexml (3.2.6) - rubocop (1.56.3) - base64 (~> 0.1.1) + rubocop (1.57.2) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.2.3) + parser (>= 3.2.2.4) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) rubocop-ast (>= 1.28.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.29.0) + rubocop-ast (1.30.0) parser (>= 3.2.1.0) - rubocop-minitest (0.32.1) + rubocop-minitest (0.33.0) rubocop (>= 1.39, < 2.0) rubocop-performance (1.19.1) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) - rubocop-rails (2.21.1) + rubocop-rails (2.22.1) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) - sprockets (4.2.0) + 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 (>= 3.0.0) - sqlite3 (1.6.6) + sqlite3 (1.6.8) mini_portile2 (~> 2.8.0) + stringio (3.0.9) thor (1.3.0) timeout (0.4.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.4.2) + unicode-display_width (2.5.0) webrick (1.8.1) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) @@ -245,6 +254,7 @@ PLATFORMS x86_64-linux DEPENDENCIES + appraisal debug mocha mysql2 diff --git a/README.md b/README.md index bb9f7c4..f3a03d2 100644 --- a/README.md +++ b/README.md @@ -273,5 +273,25 @@ $ TARGET_DB=mysql bin/rails test $ TARGET_DB=postgres bin/rails test ``` +### Testing with multiple Rails version + +Solid Cache relies on [appraisal](https://github.com/thoughtbot/appraisal/tree/main) to test +multiple Rails version. + +To run a test for a specific version run: + +```shell +bundle exec appraisal rails-7-1 bin/rails test +``` + +After updating the dependencies in then `Gemfile` please run: + +```shell +$ bundle +$ appraisal update +``` + +This ensures that all the Rails versions dependencies are updated. + ## License Solid Cache is licensed under MIT. diff --git a/gemfiles/.bundle/config b/gemfiles/.bundle/config new file mode 100644 index 0000000..c127f80 --- /dev/null +++ b/gemfiles/.bundle/config @@ -0,0 +1,2 @@ +--- +BUNDLE_RETRY: "1" diff --git a/gemfiles/rails_7.gemfile b/gemfiles/rails_7.gemfile new file mode 100644 index 0000000..2692e90 --- /dev/null +++ b/gemfiles/rails_7.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "sqlite3" +gem "mysql2" +gem "pg" +gem "sprockets-rails" +gem "rubocop-37signals", require: false, git: "https://github.com/basecamp/house-style.git" +gem "appraisal" +gem "rails", branch: "7-0-stable", git: "https://github.com/rails/rails.git" + +gemspec path: "../" diff --git a/gemfiles/rails_7.gemfile.lock b/gemfiles/rails_7.gemfile.lock new file mode 100644 index 0000000..7ec35d4 --- /dev/null +++ b/gemfiles/rails_7.gemfile.lock @@ -0,0 +1,262 @@ +GIT + remote: https://github.com/basecamp/house-style.git + revision: a9ca7e4ab80b72c1a10053c50efefe8cd275e3b8 + specs: + rubocop-37signals (1.0.0) + rubocop + rubocop-minitest + rubocop-performance + rubocop-rails + +GIT + remote: https://github.com/rails/rails.git + revision: a1759f292c48d5ebe49edf6c7a937bfab2901023 + branch: 7-0-stable + specs: + actioncable (7.0.8) + actionpack (= 7.0.8) + activesupport (= 7.0.8) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailbox (7.0.8) + actionpack (= 7.0.8) + activejob (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) + mail (>= 2.7.1) + net-imap + net-pop + net-smtp + actionmailer (7.0.8) + actionpack (= 7.0.8) + actionview (= 7.0.8) + activejob (= 7.0.8) + activesupport (= 7.0.8) + mail (~> 2.5, >= 2.5.4) + net-imap + net-pop + net-smtp + rails-dom-testing (~> 2.0) + actionpack (7.0.8) + actionview (= 7.0.8) + activesupport (= 7.0.8) + racc + 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.8) + actionpack (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.0.8) + activesupport (= 7.0.8) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + activejob (7.0.8) + activesupport (= 7.0.8) + globalid (>= 0.3.6) + activemodel (7.0.8) + activesupport (= 7.0.8) + activerecord (7.0.8) + activemodel (= 7.0.8) + activesupport (= 7.0.8) + activestorage (7.0.8) + actionpack (= 7.0.8) + activejob (= 7.0.8) + activerecord (= 7.0.8) + activesupport (= 7.0.8) + marcel (~> 1.0) + mini_mime (>= 1.1.0) + activesupport (7.0.8) + base64 + concurrent-ruby (~> 1.0, >= 1.0.2) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + rails (7.0.8) + actioncable (= 7.0.8) + actionmailbox (= 7.0.8) + actionmailer (= 7.0.8) + actionpack (= 7.0.8) + actiontext (= 7.0.8) + actionview (= 7.0.8) + activejob (= 7.0.8) + activemodel (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) + bundler (>= 1.15.0) + railties (= 7.0.8) + railties (7.0.8) + actionpack (= 7.0.8) + activesupport (= 7.0.8) + method_source + rake (>= 12.2) + thor (~> 1.0) + zeitwerk (~> 2.5) + +PATH + remote: .. + specs: + solid_cache (0.1.0) + rails (>= 7) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + ast (2.4.2) + base64 (0.2.0) + builder (3.2.4) + concurrent-ruby (1.2.2) + crass (1.0.6) + date (3.3.4) + debug (1.8.0) + irb (>= 1.5.0) + reline (>= 0.3.1) + drb (2.2.0) + ruby2_keywords + erubi (1.12.0) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + io-console (0.6.0) + irb (1.9.0) + rdoc + reline (>= 0.3.8) + json (2.6.3) + language_server-protocol (3.17.0.3) + loofah (2.22.0) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.2) + method_source (1.0.0) + mini_mime (1.1.5) + mini_portile2 (2.8.5) + minitest (5.20.0) + mocha (2.1.0) + ruby2_keywords (>= 0.0.5) + mutex_m (0.2.0) + mysql2 (0.5.5) + net-imap (0.4.5) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.4.0) + net-protocol + nio4r (2.5.9) + nokogiri (1.15.4) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + parallel (1.23.0) + parser (3.2.2.4) + ast (~> 2.4.1) + racc + pg (1.5.4) + psych (5.1.1.1) + stringio + racc (1.7.3) + rack (2.2.8) + rack-test (2.1.0) + rack (>= 1.3) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + rainbow (3.1.1) + rake (13.1.0) + rdoc (6.6.0) + psych (>= 4.0.0) + regexp_parser (2.8.2) + reline (0.4.0) + io-console (~> 0.5) + rexml (3.2.6) + rubocop (1.57.2) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.4) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.28.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.30.0) + parser (>= 3.2.1.0) + rubocop-minitest (0.33.0) + rubocop (>= 1.39, < 2.0) + rubocop-performance (1.19.1) + rubocop (>= 1.7.0, < 2.0) + rubocop-ast (>= 0.4.0) + rubocop-rails (2.22.1) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.33.0, < 2.0) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + 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 (>= 3.0.0) + sqlite3 (1.6.8) + mini_portile2 (~> 2.8.0) + stringio (3.0.9) + thor (1.3.0) + timeout (0.4.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + websocket-driver (0.7.6) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + zeitwerk (2.6.12) + +PLATFORMS + arm64-darwin-21 + arm64-darwin-22 + arm64-darwin-23 + x86_64-darwin-20 + x86_64-linux + +DEPENDENCIES + appraisal + debug + mocha + mysql2 + pg + rails! + rubocop-37signals! + solid_cache! + sprockets-rails + sqlite3 + +BUNDLED WITH + 2.3.26 diff --git a/gemfiles/rails_7_1.gemfile b/gemfiles/rails_7_1.gemfile new file mode 100644 index 0000000..cb238c5 --- /dev/null +++ b/gemfiles/rails_7_1.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "sqlite3" +gem "mysql2" +gem "pg" +gem "sprockets-rails" +gem "rubocop-37signals", require: false, git: "https://github.com/basecamp/house-style.git" +gem "appraisal" +gem "rails", branch: "7-1-stable", git: "https://github.com/rails/rails.git" + +gemspec path: "../" diff --git a/gemfiles/rails_7_1.gemfile.lock b/gemfiles/rails_7_1.gemfile.lock new file mode 100644 index 0000000..c6eb580 --- /dev/null +++ b/gemfiles/rails_7_1.gemfile.lock @@ -0,0 +1,275 @@ +GIT + remote: https://github.com/basecamp/house-style.git + revision: a9ca7e4ab80b72c1a10053c50efefe8cd275e3b8 + specs: + rubocop-37signals (1.0.0) + rubocop + rubocop-minitest + rubocop-performance + rubocop-rails + +GIT + remote: https://github.com/rails/rails.git + revision: 71e0c050b81498adb1fec2a18056038ca14990ca + branch: 7-1-stable + specs: + actioncable (7.1.2) + actionpack (= 7.1.2) + activesupport (= 7.1.2) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (7.1.2) + actionpack (= 7.1.2) + activejob (= 7.1.2) + activerecord (= 7.1.2) + activestorage (= 7.1.2) + activesupport (= 7.1.2) + mail (>= 2.7.1) + net-imap + net-pop + net-smtp + actionmailer (7.1.2) + actionpack (= 7.1.2) + actionview (= 7.1.2) + activejob (= 7.1.2) + activesupport (= 7.1.2) + mail (~> 2.5, >= 2.5.4) + net-imap + net-pop + net-smtp + rails-dom-testing (~> 2.2) + actionpack (7.1.2) + actionview (= 7.1.2) + activesupport (= 7.1.2) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actiontext (7.1.2) + actionpack (= 7.1.2) + activerecord (= 7.1.2) + activestorage (= 7.1.2) + activesupport (= 7.1.2) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.1.2) + activesupport (= 7.1.2) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.1.2) + activesupport (= 7.1.2) + globalid (>= 0.3.6) + activemodel (7.1.2) + activesupport (= 7.1.2) + activerecord (7.1.2) + activemodel (= 7.1.2) + activesupport (= 7.1.2) + timeout (>= 0.4.0) + activestorage (7.1.2) + actionpack (= 7.1.2) + activejob (= 7.1.2) + activerecord (= 7.1.2) + activesupport (= 7.1.2) + marcel (~> 1.0) + activesupport (7.1.2) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + rails (7.1.2) + actioncable (= 7.1.2) + actionmailbox (= 7.1.2) + actionmailer (= 7.1.2) + actionpack (= 7.1.2) + actiontext (= 7.1.2) + actionview (= 7.1.2) + activejob (= 7.1.2) + activemodel (= 7.1.2) + activerecord (= 7.1.2) + activestorage (= 7.1.2) + activesupport (= 7.1.2) + bundler (>= 1.15.0) + railties (= 7.1.2) + railties (7.1.2) + actionpack (= 7.1.2) + activesupport (= 7.1.2) + irb + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + +PATH + remote: .. + specs: + solid_cache (0.1.0) + rails (>= 7) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + ast (2.4.2) + base64 (0.2.0) + bigdecimal (3.1.4) + builder (3.2.4) + concurrent-ruby (1.2.2) + connection_pool (2.4.1) + crass (1.0.6) + date (3.3.4) + debug (1.8.0) + irb (>= 1.5.0) + reline (>= 0.3.1) + drb (2.2.0) + ruby2_keywords + erubi (1.12.0) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + io-console (0.6.0) + irb (1.9.0) + rdoc + reline (>= 0.3.8) + json (2.6.3) + language_server-protocol (3.17.0.3) + loofah (2.22.0) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.2) + mini_mime (1.1.5) + mini_portile2 (2.8.5) + minitest (5.20.0) + mocha (2.1.0) + ruby2_keywords (>= 0.0.5) + mutex_m (0.2.0) + mysql2 (0.5.5) + net-imap (0.4.5) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.4.0) + net-protocol + nio4r (2.5.9) + nokogiri (1.15.4) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + parallel (1.23.0) + parser (3.2.2.4) + ast (~> 2.4.1) + racc + pg (1.5.4) + psych (5.1.1.1) + stringio + racc (1.7.3) + rack (3.0.8) + rack-session (2.0.0) + rack (>= 3.0.0) + rack-test (2.1.0) + rack (>= 1.3) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + rainbow (3.1.1) + rake (13.1.0) + rdoc (6.6.0) + psych (>= 4.0.0) + regexp_parser (2.8.2) + reline (0.4.0) + io-console (~> 0.5) + rexml (3.2.6) + rubocop (1.57.2) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.4) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.28.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.30.0) + parser (>= 3.2.1.0) + rubocop-minitest (0.33.0) + rubocop (>= 1.39, < 2.0) + rubocop-performance (1.19.1) + rubocop (>= 1.7.0, < 2.0) + rubocop-ast (>= 0.4.0) + rubocop-rails (2.22.1) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.33.0, < 2.0) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + 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 (>= 3.0.0) + sqlite3 (1.6.8) + mini_portile2 (~> 2.8.0) + stringio (3.0.9) + thor (1.3.0) + timeout (0.4.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + webrick (1.8.1) + websocket-driver (0.7.6) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + zeitwerk (2.6.12) + +PLATFORMS + arm64-darwin-21 + arm64-darwin-22 + arm64-darwin-23 + x86_64-darwin-20 + x86_64-linux + +DEPENDENCIES + appraisal + debug + mocha + mysql2 + pg + rails! + rubocop-37signals! + solid_cache! + sprockets-rails + sqlite3 + +BUNDLED WITH + 2.3.26 diff --git a/gemfiles/rails_main.gemfile b/gemfiles/rails_main.gemfile new file mode 100644 index 0000000..240793d --- /dev/null +++ b/gemfiles/rails_main.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "sqlite3" +gem "mysql2" +gem "pg" +gem "sprockets-rails" +gem "rubocop-37signals", require: false, git: "https://github.com/basecamp/house-style.git" +gem "appraisal" +gem "rails", branch: "main", git: "https://github.com/rails/rails.git" + +gemspec path: "../" diff --git a/gemfiles/rails_main.gemfile.lock b/gemfiles/rails_main.gemfile.lock new file mode 100644 index 0000000..ec740e8 --- /dev/null +++ b/gemfiles/rails_main.gemfile.lock @@ -0,0 +1,273 @@ +GIT + remote: https://github.com/basecamp/house-style.git + revision: a9ca7e4ab80b72c1a10053c50efefe8cd275e3b8 + specs: + rubocop-37signals (1.0.0) + rubocop + rubocop-minitest + rubocop-performance + rubocop-rails + +GIT + remote: https://github.com/rails/rails.git + revision: f98bb7ed41d513539cd6a1af1f21d96ae4f3cd17 + branch: main + specs: + actioncable (7.2.0.alpha) + actionpack (= 7.2.0.alpha) + activesupport (= 7.2.0.alpha) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (7.2.0.alpha) + actionpack (= 7.2.0.alpha) + activejob (= 7.2.0.alpha) + activerecord (= 7.2.0.alpha) + activestorage (= 7.2.0.alpha) + activesupport (= 7.2.0.alpha) + mail (>= 2.7.1) + net-imap + net-pop + net-smtp + actionmailer (7.2.0.alpha) + actionpack (= 7.2.0.alpha) + actionview (= 7.2.0.alpha) + activejob (= 7.2.0.alpha) + activesupport (= 7.2.0.alpha) + mail (~> 2.5, >= 2.5.4) + net-imap + net-pop + net-smtp + rails-dom-testing (~> 2.2) + actionpack (7.2.0.alpha) + actionview (= 7.2.0.alpha) + activesupport (= 7.2.0.alpha) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actiontext (7.2.0.alpha) + actionpack (= 7.2.0.alpha) + activerecord (= 7.2.0.alpha) + activestorage (= 7.2.0.alpha) + activesupport (= 7.2.0.alpha) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.2.0.alpha) + activesupport (= 7.2.0.alpha) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.2.0.alpha) + activesupport (= 7.2.0.alpha) + globalid (>= 0.3.6) + activemodel (7.2.0.alpha) + activesupport (= 7.2.0.alpha) + activerecord (7.2.0.alpha) + activemodel (= 7.2.0.alpha) + activesupport (= 7.2.0.alpha) + timeout (>= 0.4.0) + activestorage (7.2.0.alpha) + actionpack (= 7.2.0.alpha) + activejob (= 7.2.0.alpha) + activerecord (= 7.2.0.alpha) + activesupport (= 7.2.0.alpha) + marcel (~> 1.0) + activesupport (7.2.0.alpha) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0, >= 2.0.5) + rails (7.2.0.alpha) + actioncable (= 7.2.0.alpha) + actionmailbox (= 7.2.0.alpha) + actionmailer (= 7.2.0.alpha) + actionpack (= 7.2.0.alpha) + actiontext (= 7.2.0.alpha) + actionview (= 7.2.0.alpha) + activejob (= 7.2.0.alpha) + activemodel (= 7.2.0.alpha) + activerecord (= 7.2.0.alpha) + activestorage (= 7.2.0.alpha) + activesupport (= 7.2.0.alpha) + bundler (>= 1.15.0) + railties (= 7.2.0.alpha) + railties (7.2.0.alpha) + actionpack (= 7.2.0.alpha) + activesupport (= 7.2.0.alpha) + irb + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + +PATH + remote: .. + specs: + solid_cache (0.1.0) + rails (>= 7) + +GEM + remote: https://rubygems.org/ + specs: + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + ast (2.4.2) + base64 (0.2.0) + bigdecimal (3.1.4) + builder (3.2.4) + concurrent-ruby (1.2.2) + connection_pool (2.4.1) + crass (1.0.6) + date (3.3.4) + debug (1.8.0) + irb (>= 1.5.0) + reline (>= 0.3.1) + drb (2.2.0) + ruby2_keywords + erubi (1.12.0) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + io-console (0.6.0) + irb (1.9.0) + rdoc + reline (>= 0.3.8) + json (2.6.3) + language_server-protocol (3.17.0.3) + loofah (2.22.0) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.2) + mini_mime (1.1.5) + mini_portile2 (2.8.5) + minitest (5.20.0) + mocha (2.1.0) + ruby2_keywords (>= 0.0.5) + mysql2 (0.5.5) + net-imap (0.4.5) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.4.0) + net-protocol + nio4r (2.5.9) + nokogiri (1.15.4) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + parallel (1.23.0) + parser (3.2.2.4) + ast (~> 2.4.1) + racc + pg (1.5.4) + psych (5.1.1.1) + stringio + racc (1.7.3) + rack (3.0.8) + rack-session (2.0.0) + rack (>= 3.0.0) + rack-test (2.1.0) + rack (>= 1.3) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + rainbow (3.1.1) + rake (13.1.0) + rdoc (6.6.0) + psych (>= 4.0.0) + regexp_parser (2.8.2) + reline (0.4.0) + io-console (~> 0.5) + rexml (3.2.6) + rubocop (1.57.2) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.4) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.28.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.30.0) + parser (>= 3.2.1.0) + rubocop-minitest (0.33.0) + rubocop (>= 1.39, < 2.0) + rubocop-performance (1.19.1) + rubocop (>= 1.7.0, < 2.0) + rubocop-ast (>= 0.4.0) + rubocop-rails (2.22.1) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.33.0, < 2.0) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + 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 (>= 3.0.0) + sqlite3 (1.6.8) + mini_portile2 (~> 2.8.0) + stringio (3.0.9) + thor (1.3.0) + timeout (0.4.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + webrick (1.8.1) + websocket-driver (0.7.6) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + zeitwerk (2.6.12) + +PLATFORMS + arm64-darwin-21 + arm64-darwin-22 + arm64-darwin-23 + x86_64-darwin-20 + x86_64-linux + +DEPENDENCIES + appraisal + debug + mocha + mysql2 + pg + rails! + rubocop-37signals! + solid_cache! + sprockets-rails + sqlite3 + +BUNDLED WITH + 2.3.26 diff --git a/test/unit/behaviors/cache_store_format_version_behavior.rb b/test/unit/behaviors/cache_store_format_version_behavior.rb index b7abbbb..8670e6f 100644 --- a/test/unit/behaviors/cache_store_format_version_behavior.rb +++ b/test/unit/behaviors/cache_store_format_version_behavior.rb @@ -3,20 +3,22 @@ module CacheStoreFormatVersionBehavior FORMAT_VERSIONS = [ 6.1, 7.0, 7.1 ] - FORMAT_VERSIONS.each do |format_version| - define_method "test_marshal_undefined_class_deserialization_error_with_format_#{format_version}" do - with_format(format_version) do |cache| + if Rails.version >= "7.1" + FORMAT_VERSIONS.each do |format_version| + define_method "test_marshal_undefined_class_deserialization_error_with_format_#{format_version}" do + with_format(format_version) do |cache| - key = "marshal-#{rand}" - self.class.const_set(:RemovedConstant, Class.new) + key = "marshal-#{rand}" + self.class.const_set(:RemovedConstant, Class.new) - value_to_compress = [ self.class::RemovedConstant.new, "0" * 100 ] - cache.write(key, value_to_compress, compress: true, compress_threshold: 1) - self.class.send(:remove_const, :RemovedConstant) - assert_equal({}, cache.read_multi(key)) + value_to_compress = [ self.class::RemovedConstant.new, "0" * 100 ] + cache.write(key, value_to_compress, compress: true, compress_threshold: 1) + self.class.send(:remove_const, :RemovedConstant) + assert_equal({}, cache.read_multi(key)) + end + ensure + self.class.send(:remove_const, :RemovedConstant) rescue nil end - ensure - self.class.send(:remove_const, :RemovedConstant) rescue nil end end diff --git a/test/unit/execution_test.rb b/test/unit/execution_test.rb index 9c84556..76b408c 100644 --- a/test/unit/execution_test.rb +++ b/test/unit/execution_test.rb @@ -23,7 +23,11 @@ def test_async_errors_are_reported assert_equal 1, error_subscriber.errors.count assert_equal "Boom!", error_subscriber.errors.first[0].message - assert_equal({ context: {}, handled: false, level: :error, source: "application.active_support" }, error_subscriber.errors.first[1]) + if Rails.version >= "7.1" + assert_equal({ context: {}, handled: false, level: :error, source: "application.active_support" }, error_subscriber.errors.first[1]) + else + assert_equal({ context: {}, handled: false, level: :error, source: nil }, error_subscriber.errors.first[1]) + end ensure Rails.error.unsubscribe(error_subscriber) if Rails.error.respond_to?(:unsubscribe) end diff --git a/test/unit/solid_cache_test.rb b/test/unit/solid_cache_test.rb index d2788b5..3a07ceb 100644 --- a/test/unit/solid_cache_test.rb +++ b/test/unit/solid_cache_test.rb @@ -55,10 +55,12 @@ class SolidCacheFailsafeTest < ActiveSupport::TestCase def emulating_unavailability stub_matcher = ActiveRecord::Base.connection.class.any_instance + stub_matcher.stubs(:exec_query).raises(ActiveRecord::StatementInvalid) stub_matcher.stubs(:internal_exec_query).raises(ActiveRecord::StatementInvalid) stub_matcher.stubs(:exec_delete).raises(ActiveRecord::StatementInvalid) yield ActiveSupport::Cache::SolidCacheStore.new(namespace: @namespace) ensure + stub_matcher.unstub(:exec_query) stub_matcher.unstub(:internal_exec_query) stub_matcher.unstub(:exec_delete) end @@ -80,11 +82,13 @@ class SolidCacheRaisingTest < ActiveSupport::TestCase def emulating_unavailability stub_matcher = ActiveRecord::Base.connection.class.any_instance + stub_matcher.stubs(:exec_query).raises(ActiveRecord::StatementInvalid) stub_matcher.stubs(:internal_exec_query).raises(ActiveRecord::StatementInvalid) stub_matcher.stubs(:exec_delete).raises(ActiveRecord::StatementInvalid) yield ActiveSupport::Cache::SolidCacheStore.new(namespace: @namespace, error_handler: ->(method:, returning:, exception:) { raise exception }) ensure + stub_matcher.unstub(:exec_query) stub_matcher.unstub(:internal_exec_query) stub_matcher.unstub(:exec_delete) end From 2c578d7e364332433dcf7647a89adb66fd6c1343 Mon Sep 17 00:00:00 2001 From: Jacopo Date: Thu, 16 Nov 2023 10:53:54 +0100 Subject: [PATCH 5/5] Port all the cache_store_format_version_behavior tests --- .../cache_store_format_version_behavior.rb | 136 +++++++++++++++--- 1 file changed, 117 insertions(+), 19 deletions(-) diff --git a/test/unit/behaviors/cache_store_format_version_behavior.rb b/test/unit/behaviors/cache_store_format_version_behavior.rb index 8670e6f..4dce407 100644 --- a/test/unit/behaviors/cache_store_format_version_behavior.rb +++ b/test/unit/behaviors/cache_store_format_version_behavior.rb @@ -1,35 +1,133 @@ # frozen_string_literal: true module CacheStoreFormatVersionBehavior - FORMAT_VERSIONS = [ 6.1, 7.0, 7.1 ] - if Rails.version >= "7.1" - FORMAT_VERSIONS.each do |format_version| - define_method "test_marshal_undefined_class_deserialization_error_with_format_#{format_version}" do - with_format(format_version) do |cache| + extend ActiveSupport::Concern + + FORMAT_VERSION_SIGNATURES = { + 6.1 => [ + "\x04\x08o".b, # Marshal.dump(entry) + "\x04\x08o".b, # Marshal.dump(entry.compressed(...)) + ], + 7.0 => [ + "\x00\x04\x08[".b, # "\x00" + Marshal.dump(entry.pack) + "\x01\x78".b, # "\x01" + Zlib::Deflate.deflate(...) + ], + 7.1 => [ + "\x00\x11\x01".b, # ActiveSupport::Cache::Coder#dump + "\x00\x11\x81".b, # ActiveSupport::Cache::Coder#dump_compressed + ], + } + + FORMAT_VERSIONS = FORMAT_VERSION_SIGNATURES.keys + + included do + test "format version affects default coder" do + coders = FORMAT_VERSIONS.map do |format_version| + with_format(format_version) do + lookup_store.instance_variable_get(:@coder) + end + end + + assert_equal coders, coders.uniq + end + + test "invalid format version raises" do + assert_raises do + with_format(0) do + lookup_store + end + end + end + + FORMAT_VERSION_SIGNATURES.each do |format_version, (uncompressed_signature, compressed_signature)| + test "format version #{format_version.inspect} uses correct signature for uncompressed entries" do + serialized = with_format(format_version) do + lookup_store.send(:serialize_entry, ActiveSupport::Cache::Entry.new(["value"] * 100)) + end - key = "marshal-#{rand}" - self.class.const_set(:RemovedConstant, Class.new) + skip if !serialized.is_a?(String) - value_to_compress = [ self.class::RemovedConstant.new, "0" * 100 ] - cache.write(key, value_to_compress, compress: true, compress_threshold: 1) - self.class.send(:remove_const, :RemovedConstant) - assert_equal({}, cache.read_multi(key)) + assert_operator serialized, :start_with?, uncompressed_signature end + + test "format version #{format_version.inspect} uses correct signature for compressed entries" do + serialized = with_format(format_version) do + lookup_store.send(:serialize_entry, ActiveSupport::Cache::Entry.new(["value"] * 100), compress_threshold: 1) + end + + if serialized.is_a?(String) + assert_operator serialized, :start_with?, compressed_signature + end + end + + test "Marshal undefined class/module deserialization error with #{format_version} format" do + key = "marshal-#{rand}" + self.class.const_set(:RemovedConstant, Class.new) + @store = with_format(format_version) { lookup_store } + @store.write(key, self.class::RemovedConstant.new) + assert_instance_of self.class::RemovedConstant, @store.read(key) + + self.class.send(:remove_const, :RemovedConstant) + assert_nil @store.read(key) + assert_equal false, @store.exist?(key) ensure self.class.send(:remove_const, :RemovedConstant) rescue nil end + + test "Compressed Marshal undefined class/module deserialization error with #{format_version} format" do + key = "marshal-#{rand}" + self.class.const_set(:RemovedConstant, Class.new) + @store = with_format(format_version) { lookup_store } + value_to_compress = [ self.class::RemovedConstant.new, "0" * 100 ] + @store.write(key, value_to_compress, compress: true, compress_threshold: 1) + assert_instance_of Array, @store.read(key) + + self.class.send(:remove_const, :RemovedConstant) + assert_nil @store.read(key) + assert_equal({}, @store.read_multi(key)) + assert_equal("new-value", @store.fetch(key) { "new-value" }) + ensure + self.class.send(:remove_const, :RemovedConstant) rescue nil + end + end + + FORMAT_VERSIONS.product(FORMAT_VERSIONS) do |read_version, write_version| + test "format version #{read_version.inspect} can read #{write_version.inspect} entries" do + key = SecureRandom.uuid + + with_format(write_version) do + lookup_store.write(key, "value for #{key}") + end + + with_format(read_version) do + assert_equal "value for #{key}", lookup_store.read(key) + end + end + + test "format version #{read_version.inspect} can read #{write_version.inspect} entries with compression" do + key = SecureRandom.uuid + + with_format(write_version) do + lookup_store(compress_threshold: 1).write(key, key * 10) + end + + with_format(read_version) do + assert_equal key * 10, lookup_store.read(key) + end + end + end end - end - private - def with_format(format_version) - if format_version == 6.1 - ActiveSupport.deprecator.silence do + private + def with_format(format_version) + if format_version == 6.1 + ActiveSupport.deprecator.silence do + ActiveSupport::Cache.with(format_version: format_version) { yield lookup_store } + end + else ActiveSupport::Cache.with(format_version: format_version) { yield lookup_store } end - else - ActiveSupport::Cache.with(format_version: format_version) { yield lookup_store } end - end + end end