From 2ab16e196410b9985d29a976fb64d58171f483e3 Mon Sep 17 00:00:00 2001 From: Matt Cruz Date: Wed, 31 Dec 2025 02:33:32 -0500 Subject: [PATCH 1/3] Add ActiveRecord model integration tests --- .app-image-tag | 2 +- .github/workflows/test.yml | 13 +- .rspec | 2 +- Gemfile | 1 + Gemfile.lock | 2 + bin/rspec-all-versions | 6 +- db/mysql_structure.sql | 5 +- db/postgresql_structure.sql | 1 + db/sqlite3_structure.sql | 1 + gemfiles/ar_7.2.gemfile | 1 + gemfiles/ar_8.0.gemfile | 1 + gemfiles/ar_8.1.gemfile | 1 + ..._mysql2_proxy_adapter_backed_model_spec.rb | 13 + ...tgresql_proxy_adapter_backed_model_spec.rb | 13 + ...sqlite3_proxy_adapter_backed_model_spec.rb | 13 + ...trilogy_proxy_adapter_backed_model_spec.rb | 13 + .../middleware_spec.rb | 6 +- spec/shared_examples/a_proxied_model.rb | 286 ++++++++++++++++++ spec/spec_helper.rb | 20 +- 19 files changed, 386 insertions(+), 14 deletions(-) create mode 100644 spec/active_record_proxy_adapters/integration/a_mysql2_proxy_adapter_backed_model_spec.rb create mode 100644 spec/active_record_proxy_adapters/integration/a_postgresql_proxy_adapter_backed_model_spec.rb create mode 100644 spec/active_record_proxy_adapters/integration/a_sqlite3_proxy_adapter_backed_model_spec.rb create mode 100644 spec/active_record_proxy_adapters/integration/a_trilogy_proxy_adapter_backed_model_spec.rb create mode 100644 spec/shared_examples/a_proxied_model.rb diff --git a/.app-image-tag b/.app-image-tag index bb749ba..0c6f42a 100644 --- a/.app-image-tag +++ b/.app-image-tag @@ -1 +1 @@ -v0.10.1.2 +v0.10.1.3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ad31427..655add8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -159,10 +159,17 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Setup database + run: bundle exec rake db:setup + - name: Run RSpec tests - run: | - bundle exec rake db:setup - bundle exec rspec --format progress + run: bundle exec rspec --format progress --tag ~integration + + - name: Run RSpec integration tests + env: + PG_REPLICA_HOST: postgres_primary + PG_REPLICA_PORT: 5432 + run: bundle exec rspec --format progress --tag integration - name: Upload Coverage Report uses: actions/upload-artifact@v4 diff --git a/.rspec b/.rspec index 24f2abf..b5b44ab 100644 --- a/.rspec +++ b/.rspec @@ -1,4 +1,4 @@ --format documentation --color --order random ---require spec_helper \ No newline at end of file +--require spec_helper diff --git a/Gemfile b/Gemfile index 95815b3..0b24ff5 100644 --- a/Gemfile +++ b/Gemfile @@ -28,6 +28,7 @@ group :test do gem "rubocop", "~> 1.82" gem "rubocop-rspec", "~> 3.8.0" gem "simplecov" + gem "timecop" end # Specify your gem's dependencies in active_record_proxy_adapters.gemspec diff --git a/Gemfile.lock b/Gemfile.lock index 74a9d93..c5db976 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -123,6 +123,7 @@ GEM simplecov-html (0.13.1) simplecov_json_formatter (0.1.4) thor (1.4.0) + timecop (0.9.10) timeout (0.4.4) trilogy (2.9.0) tzinfo (2.0.6) @@ -154,6 +155,7 @@ DEPENDENCIES rubocop (~> 1.82) rubocop-rspec (~> 3.8.0) simplecov + timecop trilogy (~> 2.9) yard diff --git a/bin/rspec-all-versions b/bin/rspec-all-versions index 3c874b6..d64d391 100755 --- a/bin/rspec-all-versions +++ b/bin/rspec-all-versions @@ -16,7 +16,11 @@ run_tests() { local env_number=${ar//./_} TEST_ENV_NUMBER=$env_number $bin_dir/appraisal-exec $ar rake db:drop db:setup - TEST_ENV_NUMBER=$env_number $bin_dir/appraisal-exec $ar rspec -fp --fail-fast + TEST_ENV_NUMBER=$env_number $bin_dir/appraisal-exec $ar rspec -fp --fail-fast --tag ~integration + TEST_ENV_NUMBER=$env_number \ + PG_REPLICA_HOST=$PG_PRIMARY_HOST \ + PG_REPLICA_PORT=$PG_PRIMARY_PORT \ + $bin_dir/appraisal-exec $ar rspec -fp --fail-fast --tag integration } active_record_versions() { diff --git a/db/mysql_structure.sql b/db/mysql_structure.sql index 530b912..bc12361 100644 --- a/db/mysql_structure.sql +++ b/db/mysql_structure.sql @@ -12,18 +12,19 @@ /*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; DROP TABLE IF EXISTS `schema_migrations`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `schema_migrations` ( `version` varchar(255) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; /*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `users`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `users` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `name` text NOT NULL, `email` text NOT NULL, + `age` int(11) NOT NULL DEFAULT 0, `created_at` timestamp NULL DEFAULT current_timestamp(), `updated_at` timestamp NULL DEFAULT current_timestamp(), PRIMARY KEY (`id`) diff --git a/db/postgresql_structure.sql b/db/postgresql_structure.sql index 8556e1d..f31e99b 100644 --- a/db/postgresql_structure.sql +++ b/db/postgresql_structure.sql @@ -43,6 +43,7 @@ CREATE TABLE public.users ( id integer NOT NULL, name text NOT NULL, email text NOT NULL, + age integer DEFAULT 0 NOT NULL, created_at timestamp without time zone DEFAULT now() NOT NULL, updated_at timestamp without time zone DEFAULT now() NOT NULL ); diff --git a/db/sqlite3_structure.sql b/db/sqlite3_structure.sql index 6ada9ab..6265480 100644 --- a/db/sqlite3_structure.sql +++ b/db/sqlite3_structure.sql @@ -5,6 +5,7 @@ CREATE TABLE users ( id integer PRIMARY KEY NOT NULL, name text NOT NULL, email text NOT NULL, + age integer not null default 0, created_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL ); diff --git a/gemfiles/ar_7.2.gemfile b/gemfiles/ar_7.2.gemfile index 9e1b811..03a5315 100644 --- a/gemfiles/ar_7.2.gemfile +++ b/gemfiles/ar_7.2.gemfile @@ -25,6 +25,7 @@ group :test do gem "rubocop", "~> 1.82" gem "rubocop-rspec", "~> 3.8.0" gem "simplecov" + gem "timecop" end gemspec path: "../" diff --git a/gemfiles/ar_8.0.gemfile b/gemfiles/ar_8.0.gemfile index 7e7dd31..cb32718 100644 --- a/gemfiles/ar_8.0.gemfile +++ b/gemfiles/ar_8.0.gemfile @@ -25,6 +25,7 @@ group :test do gem "rubocop", "~> 1.82" gem "rubocop-rspec", "~> 3.8.0" gem "simplecov" + gem "timecop" end gemspec path: "../" diff --git a/gemfiles/ar_8.1.gemfile b/gemfiles/ar_8.1.gemfile index c3679e5..4c2fcaf 100644 --- a/gemfiles/ar_8.1.gemfile +++ b/gemfiles/ar_8.1.gemfile @@ -25,6 +25,7 @@ group :test do gem "rubocop", "~> 1.82" gem "rubocop-rspec", "~> 3.8.0" gem "simplecov" + gem "timecop" end gemspec path: "../" diff --git a/spec/active_record_proxy_adapters/integration/a_mysql2_proxy_adapter_backed_model_spec.rb b/spec/active_record_proxy_adapters/integration/a_mysql2_proxy_adapter_backed_model_spec.rb new file mode 100644 index 0000000..760f78b --- /dev/null +++ b/spec/active_record_proxy_adapters/integration/a_mysql2_proxy_adapter_backed_model_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "shared_examples/a_proxied_model" + +RSpec.describe "A Mysql2ProxyAdapter-backed model", :integration do # rubocop:disable RSpec/DescribeClass + it_behaves_like "a proxied model" do + let(:model_class) { TestHelper::Mysql2Record } + let(:database_identifier) { :mysql2 } + let(:log_subscriber_primary_prefix) { "mysql2_primary" } + let(:log_subscriber_replica_prefix) { "mysql2_replica" } + let(:truncate_database) { TestHelper.truncate_mysql2_database } + end +end diff --git a/spec/active_record_proxy_adapters/integration/a_postgresql_proxy_adapter_backed_model_spec.rb b/spec/active_record_proxy_adapters/integration/a_postgresql_proxy_adapter_backed_model_spec.rb new file mode 100644 index 0000000..4d9cddd --- /dev/null +++ b/spec/active_record_proxy_adapters/integration/a_postgresql_proxy_adapter_backed_model_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "shared_examples/a_proxied_model" + +RSpec.describe "A PostgreSQLProxyAdapter-backed model", :integration do # rubocop:disable RSpec/DescribeClass + it_behaves_like "a proxied model" do + let(:model_class) { TestHelper::PostgreSQLRecord } + let(:database_identifier) { :postgresql } + let(:log_subscriber_primary_prefix) { "postgresql_primary" } + let(:log_subscriber_replica_prefix) { "postgresql_replica" } + let(:truncate_database) { TestHelper.truncate_postgresql_database } + end +end diff --git a/spec/active_record_proxy_adapters/integration/a_sqlite3_proxy_adapter_backed_model_spec.rb b/spec/active_record_proxy_adapters/integration/a_sqlite3_proxy_adapter_backed_model_spec.rb new file mode 100644 index 0000000..44e2c9b --- /dev/null +++ b/spec/active_record_proxy_adapters/integration/a_sqlite3_proxy_adapter_backed_model_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "shared_examples/a_proxied_model" + +RSpec.describe "A SQLite3ProxyAdapter-backed model", :integration do # rubocop:disable RSpec/DescribeClass + it_behaves_like "a proxied model" do + let(:model_class) { TestHelper::SQLite3Record } + let(:database_identifier) { :sqlite3 } + let(:log_subscriber_primary_prefix) { "sqlite3_primary" } + let(:log_subscriber_replica_prefix) { "sqlite3_replica" } + let(:truncate_database) { TestHelper.truncate_sqlite3_database } + end +end diff --git a/spec/active_record_proxy_adapters/integration/a_trilogy_proxy_adapter_backed_model_spec.rb b/spec/active_record_proxy_adapters/integration/a_trilogy_proxy_adapter_backed_model_spec.rb new file mode 100644 index 0000000..a5b737a --- /dev/null +++ b/spec/active_record_proxy_adapters/integration/a_trilogy_proxy_adapter_backed_model_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "shared_examples/a_proxied_model" + +RSpec.describe "A TrilogyProxyAdapter-backed model", :integration do # rubocop:disable RSpec/DescribeClass + it_behaves_like "a proxied model" do + let(:model_class) { TestHelper::TrilogyRecord } + let(:database_identifier) { :trilogy } + let(:log_subscriber_primary_prefix) { "trilogy_primary" } + let(:log_subscriber_replica_prefix) { "trilogy_replica" } + let(:truncate_database) { TestHelper.truncate_trilogy_database } + end +end diff --git a/spec/active_record_proxy_adapters/middleware_spec.rb b/spec/active_record_proxy_adapters/middleware_spec.rb index dfb9f4d..2ee5f04 100644 --- a/spec/active_record_proxy_adapters/middleware_spec.rb +++ b/spec/active_record_proxy_adapters/middleware_spec.rb @@ -28,7 +28,7 @@ def from_cookie_string(cookie_string) end end - describe "#call" do + describe "#call", freeze_time: { to: Time.utc(2025) } do let(:middleware) { described_class.new(app, {}) } let(:app) do @@ -44,12 +44,8 @@ def from_cookie_string(cookie_string) config.proxy_delay = 2.seconds config.checkout_timeout = 2.seconds end - - travel_to(Time.utc(2025)) end - after { travel_back } - context "when context cookie is not set" do it "initializes the cookie with an empty hash" do env = {} diff --git a/spec/shared_examples/a_proxied_model.rb b/spec/shared_examples/a_proxied_model.rb new file mode 100644 index 0000000..2cf7031 --- /dev/null +++ b/spec/shared_examples/a_proxied_model.rb @@ -0,0 +1,286 @@ +# frozen_string_literal: true + +RSpec.shared_context "a proxied model setup" do # rubocop:disable RSpec/ContextWording + attr_reader :log_file + + def latest_log_output + log_string.lines.last&.chomp + end + + def log_string + log_file.string + end + + def proxy_delay + 2.seconds + end + + def log_subscriber_primary_prefix + "#{database_identifier}_primary" + end + + def log_subscriber_replica_prefix + "#{database_identifier}_replica" + end + + let(:model_class) { nil } + let(:database_identifier) { nil } + let(:truncate_database) { nil } + + let(:user_model_class) do + Class.new(model_class) do + self.table_name = "users" + end + end + + before { stub_const("TestUser", user_model_class) } + + around do |example| + truncate_database + + @log_file = StringIO.new + logger = Logger.new(log_file, level: :debug) + logger.formatter = proc do |_severity, datetime, _progname, msg| + formatted_time = datetime.strftime("%Y-%m-%d %H:%M:%S") + "#{formatted_time} - #{msg}\n" + end + + logger_original = ActiveRecord::Base.logger + ActiveRecord::Base.logger = logger + ActiveRecordProxyAdapters.configure do |config| + config.logger = logger + + config.database(:"#{database_identifier}_primary") do |db_config| + db_config.proxy_delay = proxy_delay + db_config.log_subscriber_prefix = log_subscriber_primary_prefix + end + + config.database(:"#{database_identifier}_replica") do |db_config| + db_config.log_subscriber_prefix = log_subscriber_replica_prefix + end + end + + example.run + + ActiveRecord::Base.logger = logger_original + end +end + +RSpec.shared_examples_for "a write instance method" do |method_name| + subject(:run_test!) { user.public_send(method_name) } + + let(:user) { TestUser.new(name: "Jane Doe", email: "jane.doe@example.com") } + let(:read_query) { TestUser.exists? } + + before do + Timecop.travel((proxy_delay + 1.second).ago) { user } + end + + it "sticks to the primary pool" do + run_test! + + expect(latest_log_output).to include("[#{log_subscriber_primary_prefix}]") + end + + context "when within the proxy delay period", :freeze_time do + it "sticks to the primary for reads after a write" do + run_test! + read_query + + expect(latest_log_output).to include("[#{log_subscriber_primary_prefix}]") + end + end + + context "when out of the proxy delay period", :freeze_time do + it "resumes connecting to replica after the proxy delay" do + run_test! + Timecop.travel((proxy_delay + 1.second).from_now) { read_query } + + expect(latest_log_output).to include("[#{log_subscriber_replica_prefix}]") + end + end +end + +RSpec.shared_examples_for "a write class method" do |method_name| + subject(:run_test!) { TestUser.public_send(method_name) } + + let(:read_query) { TestUser.exists? } + + it "sticks to the primary pool" do + run_test! + + expect(latest_log_output).to include("[#{log_subscriber_primary_prefix}]") + end + + it "sticks to the primary for reads after a write" do + run_test! + + read_query + + expect(latest_log_output).to include("[#{log_subscriber_primary_prefix}]") + end + + it "resumes connecting to replica after the proxy delay" do + Timecop.freeze(proxy_delay.from_now + 1.0.second) do + read_query + + expect(latest_log_output).to include("[#{log_subscriber_replica_prefix}]") + end + end +end + +RSpec.shared_examples_for "a read instance method" do |method_name| + subject(:run_test!) { user.public_send(method_name) } + + let(:user) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + + context "when within the proxy delay period", :freeze_time do + it "sticks to the primary pool" do + user + + run_test! + + expect(latest_log_output).to include("[#{log_subscriber_primary_prefix}]") + end + end + + context "when out of the proxy delay period" do + before do + Timecop.freeze(proxy_delay.ago - 1.second) { user } + end + + it "reroutes query to the replica pool" do + run_test! + + expect(latest_log_output).to include("[#{log_subscriber_replica_prefix}]") + end + + context "when inside a transaction" do + it "sticks to the primary pool" do + TestUser.transaction do + run_test! + + expect(latest_log_output).to include("[#{log_subscriber_primary_prefix}]") + end + end + end + end +end + +RSpec.shared_examples_for "a proxied model" do + include_context "a proxied model setup" + + describe "ActiveRecord::Relation methods" do + describe ".all" do + subject(:all) { TestUser.all } + + it "reroutes query to the replica pool" do + all.to_a + + expect(latest_log_output).to include("[#{log_subscriber_replica_prefix}]") + end + end + end + + describe ".create" do + it_behaves_like "a write class method", :create do + subject(:run_test!) { TestUser.create(name: "Jane Doe", email: "jane.doe@example.com") } + end + end + + describe ".create!" do + it_behaves_like "a write class method", :create! do + subject(:run_test!) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + end + end + + describe ".insert" do + it_behaves_like "a write class method", :insert do + subject(:run_test!) { TestUser.insert({ name: "Jane Doe", email: "jane.doe@example.com" }) } + end + end + + describe ".insert!" do + it_behaves_like "a write class method", :insert! do + subject(:run_test!) { TestUser.insert!({ name: "Jane Doe", email: "jane.doe@example.com" }) } + end + end + + describe "#reload" do + it_behaves_like "a read instance method", :reload + end + + describe "#save" do + it_behaves_like "a write instance method", :save + end + + describe "#save!" do + it_behaves_like "a write instance method", :save! + end + + describe "#update" do + it_behaves_like "a write instance method", :update do + subject(:run_test!) { user.update(name: "Janet Doe", email: "janet.doe@example.com") } + + let(:user) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + let(:read_query) { user.reload } + end + end + + describe "#update!" do + it_behaves_like "a write instance method", :update! do + subject(:run_test!) { user.update!(name: "Janet Doe", email: "janet.doe@example.com") } + + let(:user) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + let(:read_query) { user.reload } + end + end + + describe "#touch" do + it_behaves_like "a write instance method", :touch do + let(:user) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + end + end + + describe "#increment!" do + it_behaves_like "a write instance method", :increment! do + subject(:run_test!) { user.increment!(:age) } + + let(:user) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + end + end + + describe "#decrement!" do + it_behaves_like "a write instance method", :decrement! do + subject(:run_test!) { user.decrement!(:age) } + + let(:user) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + end + end + + describe "#update_columns" do + it_behaves_like "a write instance method", :update_columns do + subject(:run_test!) { user.update_columns(name: "Janet Doe", email: "janet.doe@example.com") } + + let(:user) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + end + end + + describe "#destroy" do + it_behaves_like "a write instance method", :destroy do + let(:user) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + end + end + + describe "#destroy!" do + it_behaves_like "a write instance method", :destroy! do + let(:user) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + end + end + + describe "#delete" do + it_behaves_like "a write instance method", :delete do + let(:user) { TestUser.create!(name: "Jane Doe", email: "jane.doe@example.com") } + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c9e6f05..de4db00 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,10 +6,14 @@ require "simple_cov_groups" require "active_record" require "active_support/testing/time_helpers" +require "timecop" +require "pry" simple_cov_formatters = [SimpleCov::Formatter::JSONFormatter] simple_cov_formatters << SimpleCov::Formatter::HTMLFormatter unless ENV["CI"] +Timecop.safe_mode = true + SimpleCov.start do self.formatters = simple_cov_formatters add_filter "/spec/" @@ -18,11 +22,16 @@ sanitize = ->(filename) { filename.tr(".", "_").tr("~>", "").strip } ruby_version = sanitize.call(RUBY_VERSION) ar_version = sanitize.call(ActiveRecord.version.to_s) + rspec_options = RSpec::Core::Parser.parse(ARGV) + included_tags = rspec_options.fetch(:inclusion_filter, {}).keys + excluded_tags = rspec_options.fetch(:exclusion_filter, {}).keys coverage_path = [ "ruby", ruby_version, "ar", - ar_version + ar_version, + *(included_tags.any? ? ["inclusion_filter_#{included_tags.join("_")}"] : []), + *(excluded_tags.any? ? ["exclusion_filter_#{excluded_tags.join("_")}"] : []) ].reject(&:blank?).join("-") coverage_dir "coverage/#{coverage_path}" @@ -35,6 +44,7 @@ require "active_record_proxy_adapters/connection_handling" ActiveSupport.on_load(:active_record) do require "active_record_proxy_adapters/log_subscriber" + ActiveRecord::LogSubscriber.detach_from(:active_record) end adapter_loaded = proc { |adapter| $stdout.puts "#{adapter} loaded" } @@ -73,6 +83,14 @@ ActiveRecordProxyAdapters::Contextualizer.current_context = proxy_config.context_store.new({}) end + config.around(:each, :freeze_time) do |example| + metadata = example.metadata + freeze_time_metadata = metadata[:freeze_time] + freeze_time_metadata = { to: Time.current } if freeze_time_metadata.is_a?(TrueClass) + + Timecop.freeze(freeze_time_metadata.fetch(:to)) { example.run } + end + wrap_test_case_in_transaction = proc do |example| connection = ActiveRecord::Base.connection From c0be2a6d52aaa8a8d2c9e42eda9256ae4f3e9290 Mon Sep 17 00:00:00 2001 From: Matt Cruz Date: Sat, 3 Jan 2026 01:37:07 -0500 Subject: [PATCH 2/3] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b84d0f4..091b777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Add Ruby 4.0.0 to test matrix - Hijack only higher-level active record adapter methods [1d7eae3](https://github.com/Nasdaq/active_record_proxy_adapters/commit/1d7eae3a9c7a75cc4adebe64a1fdc1289205bfe) +- Add ActiveRecord model integration tests [2ab16e1](https://github.com/Nasdaq/active_record_proxy_adapters/commit/2ab16e196410b9985d29a976fb64d58171f483e3) ## [0.10.1, 0.9.2] - 2026-01-02 - Fix hijacked methods for all supported ActiveRecord versions [bc0501a](https://github.com/Nasdaq/active_record_proxy_adapters/commit/bc0501af613193db4d0e67c44234b8ea6e7038e4) From 2d71a4ed157691a8f47e7803d77375b3064bc78f Mon Sep 17 00:00:00 2001 From: Matt Cruz Date: Tue, 6 Jan 2026 10:38:29 -0500 Subject: [PATCH 3/3] Happy New Year! Co-authored-by: Anderson --- spec/active_record_proxy_adapters/middleware_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/active_record_proxy_adapters/middleware_spec.rb b/spec/active_record_proxy_adapters/middleware_spec.rb index 2ee5f04..3668b39 100644 --- a/spec/active_record_proxy_adapters/middleware_spec.rb +++ b/spec/active_record_proxy_adapters/middleware_spec.rb @@ -28,7 +28,7 @@ def from_cookie_string(cookie_string) end end - describe "#call", freeze_time: { to: Time.utc(2025) } do + describe "#call", freeze_time: { to: Time.utc(2026) } do let(:middleware) { described_class.new(app, {}) } let(:app) do