diff --git a/config/boot.rb b/config/boot.rb index 7479b5aff..f21c8e585 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,5 +1,15 @@ # frozen_string_literal: true +# Rack 3.1.14 or later limits query parameters to 4096 by default, which can +# prevent saving workflows with many statuses. +# Setting RACK_QUERY_PARSER_PARAMS_LIMIT to 65536 allows handling up to +# approximately 100 statuses. +# +# See also: +# - https://www.redmine.org/issues/42875 +# - https://github.com/rack/rack/blob/v3.1.16/README.md#configuration +ENV['RACK_QUERY_PARSER_PARAMS_LIMIT'] ||= '65536' + # Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) diff --git a/test/functional/workflows_controller_test.rb b/test/functional/workflows_controller_test.rb index b30559d80..dcdc8d5bb 100644 --- a/test/functional/workflows_controller_test.rb +++ b/test/functional/workflows_controller_test.rb @@ -211,6 +211,45 @@ class WorkflowsControllerTest < Redmine::ControllerTest assert w.assignee end + def test_post_edit_with_large_number_of_statuses + # This test ensures that workflows with many statuses can be saved. + # Without setting `ENV['RACK_QUERY_PARSER_PARAMS_LIMIT']`, this raises + # ActionController::BadRequest exception due to exceeding the default + # query parameter limit of 4096. + WorkflowTransition.delete_all + + num_statuses = 40 + transitions_data = {} + + # Allowed statuses for a new issue (status_id = 0) + transitions_data['0'] = {} + (1..num_statuses).each do |status_id| + transitions_data['0'][status_id.to_s] = {'always' => '1'} + end + + # Status transitions between statuses + (1..num_statuses).each do |status_id_from| # rubocop:disable RuboCopStyle/CombinableLoops + transitions_data[status_id_from.to_s] = {} + (1..num_statuses).each do |status_id_to| + # skip self-transitions + next if status_id_from == status_id_to + + transitions_data[status_id_from.to_s][status_id_to.to_s] = { + 'always' => '1', 'author' => '1', 'assignee' => '1' + } + end + end + + assert_nothing_raised do + patch :update, :params => { + :role_id => 2, + :tracker_id => 1, + :transitions => transitions_data + } + end + assert_response :found + end + def test_get_permissions get :permissions