From 430a7dcfb1cff1a2b718b96dadbfe2599b68d088 Mon Sep 17 00:00:00 2001 From: Tungle Duy Date: Thu, 13 Aug 2026 13:40:15 +0700 Subject: [PATCH 01/10] Upgrade to Rails 7.2 - Bump rails to ~> 7.2 in Gemfile/kaui.gemspec, bump required_ruby_version to >= 3.1.0 (Rails 7.2's new minimum) - Bump activerecord-jdbc-adapter to ~> 72.0 for Rails 7.2 compatibility - Bump CI JRuby version to 9.4.15.0, update README dependencies accordingly - Fix Rails 7.2 removal of TestFixtures.fixture_path/= (use fixture_paths) - Add sorbet-runtime workaround for js-routes crash under JRuby 10 - Regenerate test/dummy/db/schema.rb for Rails 7.2 schema format --- .github/workflows/ci.yml | 6 ++-- Gemfile | 4 +-- README.md | 2 +- kaui.gemspec | 4 +-- test/dummy/config/application.rb | 9 ++++++ test/dummy/db/schema.rb | 32 +++++++++---------- .../kaui/admin_tenants_controller_test.rb | 6 ++-- .../kaui/functional_test_helper_nosetup.rb | 2 +- 8 files changed, 36 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83008fb1b..ba5393b47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,8 +24,8 @@ jobs: database-password: 'root' database-port: '3306' docker-compose-file: 'docker-compose.ci.mysql.yml' - - ruby-version: 'jruby-9.4.2.0' - database-adapter: 'mariadb' + - ruby-version: 'jruby-9.4.15.0' + database-adapter: 'mysql2' database-user: 'root' database-password: 'root' database-port: '3306' @@ -36,7 +36,7 @@ jobs: database-password: 'postgres' database-port: '5432' docker-compose-file: 'docker-compose.ci.postgresql.yml' - - ruby-version: 'jruby-9.4.2.0' + - ruby-version: 'jruby-9.4.15.0' database-adapter: 'postgresql' database-user: 'postgres' database-password: 'postgres' diff --git a/Gemfile b/Gemfile index 7b809cb7f..db4304596 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ ruby '>= 3.1.0' gemspec -gem 'rails', '~> 7.0.1' +gem 'rails', '~> 7.2.0' # Lock i18n to 1.14.x for: https://github.com/ruby-i18n/i18n/issues/735 gem 'i18n', '~> 1.14.0' @@ -26,7 +26,7 @@ group :development do gem 'simplecov' if defined?(JRUBY_VERSION) - gem 'activerecord-jdbc-adapter', '~> 70.0' + gem 'activerecord-jdbc-adapter', '~> 72.0' # Add the drivers gem 'jdbc-mariadb' gem 'jdbc-postgres' diff --git a/README.md b/README.md index f9a7d6471..4c220121a 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Kill Bill compatibility Dependencies ------------ -Ruby 3.2.2+ or JRuby 9.4.2.0+ required. +Ruby 3.1.0+ or JRuby 9.4.15.0+ required. Running Kaui locally --------------------- diff --git a/kaui.gemspec b/kaui.gemspec index dcb30b4ba..c16e96e3c 100644 --- a/kaui.gemspec +++ b/kaui.gemspec @@ -10,7 +10,7 @@ Gem::Specification.new do |s| s.summary = 'Killbill Admin UI mountable engine' s.description = 'Rails UI plugin for Killbill administration.' - s.required_ruby_version = '>= 2.7.0' + s.required_ruby_version = '>= 3.1.0' s.license = 'Apache License (2.0)' @@ -37,7 +37,7 @@ Gem::Specification.new do |s| s.add_dependency 'money-rails' s.add_dependency 'mustache-js-rails' s.add_dependency 'popper_js', '~> 2.11.5' - s.add_dependency 'rails', '~> 7.0' + s.add_dependency 'rails', '~> 7.2' s.add_dependency 'spinjs-rails' s.add_dependency 'sprockets' s.add_dependency 'sprockets-rails' diff --git a/test/dummy/config/application.rb b/test/dummy/config/application.rb index f8b8fda2c..aa48bc751 100644 --- a/test/dummy/config/application.rb +++ b/test/dummy/config/application.rb @@ -15,6 +15,15 @@ require "rails/test_unit/railtie" require 'sprockets/railtie' +# Work around a sorbet-runtime/js-routes crash under JRuby 10 (Ruby 4.0 compat): +# js-routes' sig-decorated methods trigger a sorbet-runtime signature-validation +# NoMethodError at load time. Disabling runtime checks avoids building the +# crashing validation wrapper. See repo memory for details. +if defined?(JRUBY_VERSION) + require 'sorbet-runtime' + T::Configuration.default_checked_level = :never +end + # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index 64726d4b3..eb3f684c2 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -2,49 +2,47 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# This file is the source Rails uses to define your schema when running `rails -# db:schema:load`. When creating a new database, `rails db:schema:load` tends to +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to # be faster and is potentially less error prone than running all of your # migrations from scratch. Old migrations may fail to apply correctly if those # migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2015_01_12_232813) do - - create_table "kaui_allowed_user_tenants", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t| +ActiveRecord::Schema[7.2].define(version: 2015_01_12_232813) do + create_table "kaui_allowed_user_tenants", id: :integer, charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| t.integer "kaui_allowed_user_id" t.integer "kaui_tenant_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["kaui_allowed_user_id", "kaui_tenant_id"], name: "kaui_allowed_user_tenants_uniq", unique: true t.index ["kaui_allowed_user_id"], name: "index_kaui_allowed_user_tenants_on_kaui_allowed_user_id" t.index ["kaui_tenant_id"], name: "index_kaui_allowed_user_tenants_on_kaui_tenant_id" end - create_table "kaui_allowed_users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "kaui_allowed_users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| t.string "kb_username" t.string "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["kb_username"], name: "index_kaui_allowed_users_on_kb_username", unique: true end - create_table "kaui_tenants", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "kaui_tenants", id: :integer, charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| t.string "name" t.string "kb_tenant_id" t.string "api_key" t.string "encrypted_api_secret" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end - create_table "kaui_users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "kaui_users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| t.string "kb_username", null: false t.string "kb_session_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["kb_username"], name: "index_kaui_users_on_kb_username", unique: true end - end diff --git a/test/functional/kaui/admin_tenants_controller_test.rb b/test/functional/kaui/admin_tenants_controller_test.rb index 6b34e3b2c..00d2c8ade 100644 --- a/test/functional/kaui/admin_tenants_controller_test.rb +++ b/test/functional/kaui/admin_tenants_controller_test.rb @@ -145,7 +145,7 @@ class AdminTenantsControllerTest < Kaui::FunctionalTestHelper test 'should upload plugin config' do tenant = create_kaui_tenant - stripe_yml = YAML.load_file(File.join(self.class.fixture_path, 'stripe.yml'))[:stripe] + stripe_yml = YAML.load_file(File.join(self.class.fixture_paths.first, 'stripe.yml'))[:stripe] stripe_yml.stringify_keys! stripe_yml.each { |k, v| stripe_yml[k] = v.to_s } post :upload_plugin_config, params: { id: tenant.id, plugin_name: 'killbill-stripe', plugin_key: 'stripe', plugin_type: 'ruby', plugin_properties: stripe_yml } @@ -166,7 +166,7 @@ class AdminTenantsControllerTest < Kaui::FunctionalTestHelper tenant = create_kaui_tenant # retrieve plan id from catalog xml - catalog_xml = File.read(File.join(self.class.fixture_path, 'catalog-v1.xml')) + catalog_xml = File.read(File.join(self.class.fixture_paths.first, 'catalog-v1.xml')) doc = Nokogiri::XML(catalog_xml) plan_id = doc.css('plan').first['name'] @@ -327,7 +327,7 @@ class AdminTenantsControllerTest < Kaui::FunctionalTestHelper end test 'should display overdue xml' do - overdue_xml = File.read(File.join(self.class.fixture_path, 'overdue-v1.xml')) + overdue_xml = File.read(File.join(self.class.fixture_paths.first, 'overdue-v1.xml')) post :display_overdue_xml, params: { xml: overdue_xml } assert_equal @response.body, overdue_xml diff --git a/test/functional/kaui/functional_test_helper_nosetup.rb b/test/functional/kaui/functional_test_helper_nosetup.rb index 2c6b25575..54e71d166 100644 --- a/test/functional/kaui/functional_test_helper_nosetup.rb +++ b/test/functional/kaui/functional_test_helper_nosetup.rb @@ -7,7 +7,7 @@ class FunctionalTestHelperNoSetup < ActionController::TestCase protected - self.fixture_path = Kaui::Engine.root.join('test', 'fixtures') + self.fixture_paths = [Kaui::Engine.root.join('test', 'fixtures')] # # Rails helpers From 0b83df1bcd2d8bd2531c3820d270f4c83e86e863 Mon Sep 17 00:00:00 2001 From: Tungle Duy Date: Sun, 16 Aug 2026 10:53:49 +0700 Subject: [PATCH 02/10] Fix database.yml for arjdbc 72.x / Rails 7.2 - Add explicit driver: org.mariadb.jdbc.Driver hint for the mysql2 adapter under JRuby (Rails 7.2 instantiates the adapter class directly, so arjdbc's old mariadb_connection helper that used to auto-set the driver is no longer invoked) - Allow adapter: mysql2 in the JRuby test block (arjdbc 72.x dropped the mariadb adapter name) - Align local dev default DB password with the docker MariaDB container --- test/dummy/config/database.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/dummy/config/database.yml b/test/dummy/config/database.yml index 9f47a9c21..a967bbdc1 100644 --- a/test/dummy/config/database.yml +++ b/test/dummy/config/database.yml @@ -15,13 +15,16 @@ development: &default port: <%= java.lang.System.getProperty('kaui.db.port', ENV['DB_PORT']) %> pool: <%= java.lang.System.getProperty('kaui.db.pool', ENV['DB_POOL'] || '50') %> timeout: <%= java.lang.System.getProperty('kaui.db.timeout', ENV['DB_TIMEOUT'] || '5000') %> +<% if adapter == 'mysql2' %> + driver: org.mariadb.jdbc.Driver +<% end %> <% end %> <% else %> adapter: mysql2 encoding: utf8 database: <%= ENV['DB_DATABASE'] || 'kaui' %> username: <%= ENV['DB_USERNAME'] || 'root' %> - password: <%= ENV['DB_PASSWORD'] || 'root' %> + password: <%= ENV['DB_PASSWORD'] || 'killbill' %> host: <%= ENV['DB_HOST'] || '127.0.0.1' %> port: <%= ENV['DB_PORT'] || 3306 %> pool: 50 @@ -35,9 +38,12 @@ test: <% if defined?(JRUBY_VERSION) %> <% adapter = java.lang.System.getProperty('kaui.db.adapter', ENV['DB_ADAPTER'] || 'mariadb') %> adapter: <%= adapter %> -<% if adapter == 'mariadb' %> +<% if adapter == 'mariadb' || adapter == 'mysql2' %> url: <%= java.lang.System.getProperty('kaui.db.url', ENV['DB_URL'] || 'jdbc:mariadb://localhost:3306/kaui?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC') %> <% end %> +<% if adapter == 'mysql2' %> + driver: org.mariadb.jdbc.Driver +<% end %> <% else %> adapter: <%= ENV.fetch('DB_ADAPTER', 'mysql2') %> <% end %> From c24d86564d0108a6d994015cf28a0e3d1dcbb630 Mon Sep 17 00:00:00 2001 From: Tungle Duy Date: Sun, 16 Aug 2026 11:17:27 +0700 Subject: [PATCH 03/10] Revert dev DB password default back to root --- test/dummy/config/database.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dummy/config/database.yml b/test/dummy/config/database.yml index a967bbdc1..d3f779129 100644 --- a/test/dummy/config/database.yml +++ b/test/dummy/config/database.yml @@ -24,7 +24,7 @@ development: &default encoding: utf8 database: <%= ENV['DB_DATABASE'] || 'kaui' %> username: <%= ENV['DB_USERNAME'] || 'root' %> - password: <%= ENV['DB_PASSWORD'] || 'killbill' %> + password: <%= ENV['DB_PASSWORD'] || 'root' %> host: <%= ENV['DB_HOST'] || '127.0.0.1' %> port: <%= ENV['DB_PORT'] || 3306 %> pool: 50 From 4c06d196a68a66ecf5404c2f779a0daf8ac211cc Mon Sep 17 00:00:00 2001 From: Tungle Duy Date: Sun, 16 Aug 2026 11:25:43 +0700 Subject: [PATCH 04/10] Revert schema.rb to pre-Rails-7.2 format --- test/dummy/db/schema.rb | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index eb3f684c2..64726d4b3 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -2,47 +2,49 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# This file is the source Rails uses to define your schema when running `bin/rails -# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# This file is the source Rails uses to define your schema when running `rails +# db:schema:load`. When creating a new database, `rails db:schema:load` tends to # be faster and is potentially less error prone than running all of your # migrations from scratch. Old migrations may fail to apply correctly if those # migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2015_01_12_232813) do - create_table "kaui_allowed_user_tenants", id: :integer, charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| +ActiveRecord::Schema.define(version: 2015_01_12_232813) do + + create_table "kaui_allowed_user_tenants", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "kaui_allowed_user_id" t.integer "kaui_tenant_id" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.index ["kaui_allowed_user_id", "kaui_tenant_id"], name: "kaui_allowed_user_tenants_uniq", unique: true t.index ["kaui_allowed_user_id"], name: "index_kaui_allowed_user_tenants_on_kaui_allowed_user_id" t.index ["kaui_tenant_id"], name: "index_kaui_allowed_user_tenants_on_kaui_tenant_id" end - create_table "kaui_allowed_users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| + create_table "kaui_allowed_users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "kb_username" t.string "description" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.index ["kb_username"], name: "index_kaui_allowed_users_on_kb_username", unique: true end - create_table "kaui_tenants", id: :integer, charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| + create_table "kaui_tenants", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.string "kb_tenant_id" t.string "api_key" t.string "encrypted_api_secret" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end - create_table "kaui_users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| + create_table "kaui_users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "kb_username", null: false t.string "kb_session_id" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.index ["kb_username"], name: "index_kaui_users_on_kb_username", unique: true end + end From 56fd702f39ea6f5617a785f0bf7f436484ebdbf4 Mon Sep 17 00:00:00 2001 From: Tungle Duy Date: Sun, 16 Aug 2026 15:20:42 +0700 Subject: [PATCH 05/10] Fix MariaDB JDBC driver not loading for mysql2 adapter under JRuby arjdbc 72.x dropped the 'mariadb' adapter name, so CI now uses 'mysql2' with an explicit driver: org.mariadb.jdbc.Driver override. But nothing was actually loading the driver jar onto the JRuby classpath, causing: Java::JavaLang::ClassNotFoundException: org.mariadb.jdbc.Driver Explicitly require 'jdbc/mariadb' and call Jdbc::MariaDB.load_driver(:require) before establishing the connection. Note :require must be passed explicitly - the gem's default :load method does not register the jar with the classloader. --- test/dummy/config/database.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/dummy/config/database.yml b/test/dummy/config/database.yml index d3f779129..d4825d244 100644 --- a/test/dummy/config/database.yml +++ b/test/dummy/config/database.yml @@ -1,6 +1,15 @@ development: &default <% if defined?(JRUBY_VERSION) %> <% adapter = java.lang.System.getProperty('kaui.db.adapter', ENV['DB_ADAPTER'] || 'mariadb') %> +<% if adapter == 'mysql2' + # arjdbc's mysql2 adapter doesn't auto-load the MariaDB JDBC driver the way the + # (now removed in arjdbc 72.x) 'mariadb' adapter used to; without this, connecting + # raises Java::JavaLang::ClassNotFoundException: org.mariadb.jdbc.Driver + # Note: must pass :require explicitly - the default :load method does not + # actually register the jar with JRuby's classloader. + require 'jdbc/mariadb' + Jdbc::MariaDB.load_driver(:require) +end %> adapter: <%= adapter %> encoding: <%= java.lang.System.getProperty('kaui.db.encoding', ENV['DB_ENCODING'] || 'utf8') %> <% if adapter == 'sqlite3' %> From c0d2b71a305b2654abea20a32c482a8c8a186e06 Mon Sep 17 00:00:00 2001 From: Tungle Duy Date: Sun, 16 Aug 2026 15:45:30 +0700 Subject: [PATCH 06/10] Fix RuboCop offense and 2 of 4 Rails 7.2 test failures; add temp debug backtrace - app/helpers/kaui/exception_helper.rb: Rails/EnvLocal offense, use Rails.env.local? - test/functional/kaui/accounts_controller_test.rb: Rails 7.2's redirect_to no longer populates a fallback HTML body (previously 'You are being redirected.'), so parsing @response.body for the account id no longer works. Use the Location header instead. - test/functional/kaui/admin_tenants_controller_test.rb: ActionController::TestCase recycles the request env between successive calls in the same test but does not clear a stale multipart Content-Type header (see scrub_env! in Rails' test_case.rb), so Rack 3.x now raises Rack::Multipart::EmptyContentError on the subsequent GET. Explicitly clear the header first. - lib/kaui/error_handler.rb: TEMPORARY - append exception backtrace to the flash error message to diagnose the remaining 2 failures (AdminAllowedUsersControllerTest#test_should_get_create/test_should_get_edit) via CI output. Will revert once root cause is found. --- app/helpers/kaui/exception_helper.rb | 2 +- lib/kaui/error_handler.rb | 2 +- test/functional/kaui/accounts_controller_test.rb | 7 ++++--- test/functional/kaui/admin_tenants_controller_test.rb | 5 +++++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/helpers/kaui/exception_helper.rb b/app/helpers/kaui/exception_helper.rb index 072fa1d05..bb53593af 100644 --- a/app/helpers/kaui/exception_helper.rb +++ b/app/helpers/kaui/exception_helper.rb @@ -21,7 +21,7 @@ def standardize_exception(exception) I18n.t('errors.messages.error_communicating_killbill') else # Show detailed error in development/test, or when KAUI_SHOW_ERROR_DETAILS is set (for Docker) - show_details = Rails.env.development? || Rails.env.test? || ENV['KAUI_SHOW_ERROR_DETAILS'].present? + show_details = Rails.env.local? || ENV['KAUI_SHOW_ERROR_DETAILS'].present? show_details ? exception.message : nil end end diff --git a/lib/kaui/error_handler.rb b/lib/kaui/error_handler.rb index a76b9df31..7947d4f8f 100644 --- a/lib/kaui/error_handler.rb +++ b/lib/kaui/error_handler.rb @@ -7,7 +7,7 @@ module ErrorHandler included do rescue_from(StandardError) do |error| - error_message = "Error: #{as_string(error)}" + error_message = "Error: #{as_string(error)} || DEBUG_BACKTRACE: #{error.backtrace&.first(8)&.join(' <- ')}" try_to_redirect_to_account_path = !params[:controller].ends_with?('accounts') perform_redirect_after_error(redirect: try_to_redirect_to_account_path, error:, error_message:) end diff --git a/test/functional/kaui/accounts_controller_test.rb b/test/functional/kaui/accounts_controller_test.rb index 6340963bf..c8b347c89 100644 --- a/test/functional/kaui/accounts_controller_test.rb +++ b/test/functional/kaui/accounts_controller_test.rb @@ -290,10 +290,11 @@ class AccountsControllerTest < Kaui::FunctionalTestHelper private def redirected_account_id - fields = %r{.*?)">}.match(@response.body) if fields.nil? - - return nil if fields.nil? + # Rails 7.2 no longer includes a fallback HTML body (with a link to the redirect + # target) in redirect responses, so parse the account id from the Location header instead. + return nil if @response.location.blank? + fields = %r{/accounts/(?[^/?]+)}.match(@response.location) fields.nil? ? nil : fields[:id] end end diff --git a/test/functional/kaui/admin_tenants_controller_test.rb b/test/functional/kaui/admin_tenants_controller_test.rb index 00d2c8ade..6eac428cb 100644 --- a/test/functional/kaui/admin_tenants_controller_test.rb +++ b/test/functional/kaui/admin_tenants_controller_test.rb @@ -402,6 +402,11 @@ class AdminTenantsControllerTest < Kaui::FunctionalTestHelper assert_redirected_to admin_tenant_path(tenant.id) assert_equal I18n.t('flashes.notices.catalog_uploaded_successfully'), flash[:notice] + # ActionController::TestCase recycles the request env between calls in the same test but + # does not clear a stale multipart Content-Type header (see scrub_env! in Rails' test_case.rb), + # which makes Rack 3.x raise Rack::Multipart::EmptyContentError on this subsequent GET. + @request.delete_header('CONTENT_TYPE') + get :download_catalog_xml, params: { effective_date:, id: tenant.id } assert_response :success assert_equal 'application/xml', @response.header['Content-Type'] From 8d718e709f49cbaf80730683670da57c694a14b8 Mon Sep 17 00:00:00 2001 From: Tungle Duy Date: Sun, 16 Aug 2026 15:53:35 +0700 Subject: [PATCH 07/10] TEMP: print full backtrace via warn for StandardError rescues in test env Diagnostic only - does not change behavior/flow, just logs to help find the root cause of the humanize NoMethodError affecting admin_allowed_users edit action tests. Will revert once fixed. --- lib/kaui/error_handler.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/kaui/error_handler.rb b/lib/kaui/error_handler.rb index 7947d4f8f..f61da24b0 100644 --- a/lib/kaui/error_handler.rb +++ b/lib/kaui/error_handler.rb @@ -7,7 +7,11 @@ module ErrorHandler included do rescue_from(StandardError) do |error| - error_message = "Error: #{as_string(error)} || DEBUG_BACKTRACE: #{error.backtrace&.first(8)&.join(' <- ')}" + if Rails.env.test? # TEMPORARY: print full backtrace to CI log for debugging, without changing behavior + warn "DEBUG_BACKTRACE (#{error.class}: #{error.message}): #{error.backtrace&.first(15)&.join(' <- ')}" + end + + error_message = "Error: #{as_string(error)}" try_to_redirect_to_account_path = !params[:controller].ends_with?('accounts') perform_redirect_after_error(redirect: try_to_redirect_to_account_path, error:, error_message:) end From 97fb661b626ca5d6233c17cdee7a8010ee05d79f Mon Sep 17 00:00:00 2001 From: Tungle Duy Date: Sun, 16 Aug 2026 16:05:19 +0700 Subject: [PATCH 08/10] Fix remaining redirect-body and stale multipart Content-Type failures Found via CI fail-fast + random test order surfacing more instances of the same two Rails 7.2 regressions already fixed once: - accounts_controller_test.rb#test_should_trigger_invoice: same redirect-body-is-now-empty issue as test_should_validate_external_key_if_found. Use @response.location directly instead of regex-matching the body. - admin_allowed_users_controller_test.rb#extract_allowed_id: same issue, fall back to parsing response_path (Location header) instead of the body. - functional_test_helper_nosetup.rb: fix the stale multipart Content-Type bug (Rack::Multipart::EmptyContentError) once for all tests instead of per-test-file, by clearing CONTENT_TYPE before every #process call. This also fixes test_should_get_new_plan_currency which hit the same issue. - Removed the now-redundant manual fix from admin_tenants_controller_test.rb#test_should_download_a_catalog. --- test/functional/kaui/accounts_controller_test.rb | 5 +++-- .../kaui/admin_allowed_users_controller_test.rb | 5 ++++- test/functional/kaui/admin_tenants_controller_test.rb | 5 ----- .../functional/kaui/functional_test_helper_nosetup.rb | 11 +++++++++++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/test/functional/kaui/accounts_controller_test.rb b/test/functional/kaui/accounts_controller_test.rb index c8b347c89..03d4360ee 100644 --- a/test/functional/kaui/accounts_controller_test.rb +++ b/test/functional/kaui/accounts_controller_test.rb @@ -188,8 +188,9 @@ class AccountsControllerTest < Kaui::FunctionalTestHelper post :trigger_invoice, params: parameters assert_response :redirect assert_match(/Generated invoice.*for target date.*/, flash[:notice]) - a_tag = /.*?)">/.match(@response.body) - assert_redirected_to a_tag[:href] + # Rails 7.2 no longer includes a fallback HTML body in redirect responses; + # use the Location header directly instead of parsing @response.body. + assert_redirected_to @response.location end test 'should get next_invoice_date' do diff --git a/test/functional/kaui/admin_allowed_users_controller_test.rb b/test/functional/kaui/admin_allowed_users_controller_test.rb index 395ac2f6e..38fa4f197 100644 --- a/test/functional/kaui/admin_allowed_users_controller_test.rb +++ b/test/functional/kaui/admin_allowed_users_controller_test.rb @@ -228,8 +228,11 @@ def extract_allowed_description def extract_allowed_id(response_body) fields = %r{.*?)".accept-charset=.*method="post">}.match(response_body) - fields = %r{.*?)">}.match(response_body) if fields.nil? + return fields[:id] if fields + # Rails 7.2 no longer includes a fallback HTML body in redirect responses, + # so fall back to parsing the id out of the Location header instead. + fields = %r{/(?[^/?]+)\z}.match(response_path) fields.nil? ? nil : fields[:id] end end diff --git a/test/functional/kaui/admin_tenants_controller_test.rb b/test/functional/kaui/admin_tenants_controller_test.rb index 6eac428cb..00d2c8ade 100644 --- a/test/functional/kaui/admin_tenants_controller_test.rb +++ b/test/functional/kaui/admin_tenants_controller_test.rb @@ -402,11 +402,6 @@ class AdminTenantsControllerTest < Kaui::FunctionalTestHelper assert_redirected_to admin_tenant_path(tenant.id) assert_equal I18n.t('flashes.notices.catalog_uploaded_successfully'), flash[:notice] - # ActionController::TestCase recycles the request env between calls in the same test but - # does not clear a stale multipart Content-Type header (see scrub_env! in Rails' test_case.rb), - # which makes Rack 3.x raise Rack::Multipart::EmptyContentError on this subsequent GET. - @request.delete_header('CONTENT_TYPE') - get :download_catalog_xml, params: { effective_date:, id: tenant.id } assert_response :success assert_equal 'application/xml', @response.header['Content-Type'] diff --git a/test/functional/kaui/functional_test_helper_nosetup.rb b/test/functional/kaui/functional_test_helper_nosetup.rb index 54e71d166..1c9c08f7b 100644 --- a/test/functional/kaui/functional_test_helper_nosetup.rb +++ b/test/functional/kaui/functional_test_helper_nosetup.rb @@ -9,6 +9,17 @@ class FunctionalTestHelperNoSetup < ActionController::TestCase self.fixture_paths = [Kaui::Engine.root.join('test', 'fixtures')] + # ActionController::TestCase recycles the request env between successive calls in the + # same test (e.g. a multipart POST followed by a GET), but doesn't clear a stale multipart + # Content-Type header (see scrub_env! in Rails' action_controller/test_case.rb). Rack 3.x + # then raises Rack::Multipart::EmptyContentError when that later request's (now empty) + # body gets parsed as multipart. Clear it before every request; #process re-sets it as + # needed for whichever request is actually being made. + def process(*, **) + @request.delete_header('CONTENT_TYPE') if @request + super + end + # # Rails helpers # From dfaa60ad1e408e74bba07610e10abfdc2c79181b Mon Sep 17 00:00:00 2001 From: Tungle Duy Date: Sun, 16 Aug 2026 16:27:25 +0700 Subject: [PATCH 09/10] Fix root cause of humanize NoMethodError; revert temp diagnostic Root cause found via the temporary backtrace diagnostic: app/views/kaui/admin_allowed_users/_form.html.erb passed a blank string (' ') as the attribute name to f.label, purely for layout spacing. Under Rails 7.2, ActiveModel::Translation#human_attribute_name has: translation = attribute.present? ? attribute.humanize : namespace.humanize For a blank attribute that doesn't contain a '.', the 'namespace' local variable is never assigned (it's only set in the dotted-attribute branch), so this evaluates nil.humanize - a Rails core edge case bug when passing a blank/whitespace label method name. Fixed by using label_tag instead of f.label, since this label isn't actually associated with any model attribute and doesn't need i18n attribute name translation. Also reverts the temporary warn-based backtrace diagnostic in lib/kaui/error_handler.rb added earlier purely for CI debugging - no longer needed now that the root cause is fixed. --- app/views/kaui/admin_allowed_users/_form.html.erb | 6 +++++- lib/kaui/error_handler.rb | 4 ---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/kaui/admin_allowed_users/_form.html.erb b/app/views/kaui/admin_allowed_users/_form.html.erb index 59cd35ba7..ff42ae7ca 100644 --- a/app/views/kaui/admin_allowed_users/_form.html.erb +++ b/app/views/kaui/admin_allowed_users/_form.html.erb @@ -1,7 +1,11 @@ <%= form_for @allowed_user, :url => @allowed_user.persisted? ? admin_allowed_user_path(@allowed_user.id) : admin_allowed_users_path, :method => @allowed_user.persisted? ? :put : :post, :html => {:class => 'form-horizontal'} do |f| %>
- <%= f.label ' ', :class => 'col-sm-3 control-label' %> + <%# Not associated with a real attribute - use label_tag instead of f.label, which under + Rails 7.2 raises NoMethodError (undefined method `humanize' for nil) for a blank name, + since ActiveModel::Translation#human_attribute_name only handles a blank *namespaced* + attribute (e.g. "foo."), not a plain blank string. %> + <%= label_tag nil, ' ', :class => 'col-sm-3 control-label' %>