Validate and normalize auto-postpone period to days (#2672)

* Validate and normalize auto-postpone period to days

- Add Entropy::AUTO_POSTPONE_PERIODS and validate auto_postpone_period against the allowed set
- Introduce auto_postpone_period_in_days for forms and entropy update endpoints
- Fall back to index 0 in knob partial when current value isn't in options
- Remove entropy_auto_close_options helper in favor of model constant
- Update tests to use allowed period values

* Use auto_postpone_period_in_days for JSON entropy updates

The JSON API was accepting auto_postpone_period (seconds) which
bypassed the days normalization and validation. Switch to
auto_postpone_period_in_days consistently and add explicit
wrap_parameters so flat JSON params are wrapped correctly for
the virtual attribute.

* Default to account or 30-day fallback when knob value is invalid

* Address PR review feedback for entropy validation

- Fall back to first knob option (index 0) when persisted value isn't in
  the allowed set, preventing nil index errors for legacy values
- Use integer division (1.day.to_i) for consistent day calculations

* Default to account entropy period instead of first option for knob fallback

* Test that default auto-postpone period is in the allowed periods

* Return 422 instead of 500 for invalid entropy auto-postpone values

Rescue ActiveRecord::RecordInvalid in entropy controllers so invalid
auto_postpone_period_in_days values return 422 Unprocessable Entity
instead of raising a 500.

* Fix NoMethodError when board entropy falls back to account default

Use container.account.entropy.auto_postpone_period_in_days instead of
container.account.auto_postpone_period_in_days since Account doesn't
delegate that method.

* Test board entropy fallback to account default for invalid periods
This commit is contained in:
Rob Zolkos
2026-03-09 17:37:56 -04:00
committed by GitHub
parent 0435db30ba
commit 23ac76555b
15 changed files with 113 additions and 41 deletions
@@ -6,24 +6,33 @@ class Account::EntropiesControllerTest < ActionDispatch::IntegrationTest
end
test "update" do
put account_entropy_path, params: { entropy: { auto_postpone_period: 1.day } }
put account_entropy_path, params: { entropy: { auto_postpone_period_in_days: 7 } }
assert_equal 1.day, entropies("37s_account").auto_postpone_period
assert_equal 7.days, entropies("37s_account").auto_postpone_period
assert_redirected_to account_settings_path
end
test "update as JSON" do
put account_entropy_path, params: { entropy: { auto_postpone_period: 2.days } }, as: :json
put account_entropy_path, params: { entropy: { auto_postpone_period_in_days: 7 } }, as: :json
assert_response :no_content
assert_equal 2.days, entropies("37s_account").reload.auto_postpone_period
assert_equal 7.days, entropies("37s_account").reload.auto_postpone_period
end
test "update requires admin" do
logout_and_sign_in_as :david
put account_entropy_path, params: { entropy: { auto_postpone_period: 1.day } }
put account_entropy_path, params: { entropy: { auto_postpone_period_in_days: 7 } }
assert_response :forbidden
end
test "update rejects invalid auto_postpone_period" do
original_period = entropies("37s_account").auto_postpone_period
put account_entropy_path, params: { entropy: { auto_postpone_period_in_days: 1 } }
assert_response :unprocessable_entity
assert_equal original_period, entropies("37s_account").reload.auto_postpone_period
end
end
@@ -39,17 +39,17 @@ class FlatJsonParamsTest < ActionDispatch::IntegrationTest
test "update board entropy with flat JSON" do
board = boards(:writebook)
put board_entropy_path(board), params: { auto_postpone_period: 99.days }, as: :json
put board_entropy_path(board), params: { auto_postpone_period_in_days: 90 }, as: :json
assert_response :no_content
assert_equal 99.days, board.entropy.reload.auto_postpone_period
assert_equal 90.days, board.entropy.reload.auto_postpone_period
end
test "update account entropy with flat JSON" do
put account_entropy_path, params: { auto_postpone_period: 2.days }, as: :json
put account_entropy_path, params: { auto_postpone_period_in_days: 7 }, as: :json
assert_response :no_content
assert_equal 2.days, Current.account.entropy.reload.auto_postpone_period
assert_equal 7.days, Current.account.entropy.reload.auto_postpone_period
end
test "create push subscription with flat JSON" do
@@ -8,19 +8,19 @@ class Boards::EntropiesControllerTest < ActionDispatch::IntegrationTest
test "update" do
assert_no_difference -> { Current.account.entropy.reload.auto_postpone_period } do
put board_entropy_path(@board, format: :turbo_stream), params: { board: { auto_postpone_period: 123.days } }
put board_entropy_path(@board, format: :turbo_stream), params: { board: { auto_postpone_period_in_days: 90 } }
assert_equal 123.days, @board.entropy.reload.auto_postpone_period
assert_equal 90.days, @board.entropy.reload.auto_postpone_period
assert_turbo_stream action: :replace, target: dom_id(@board, :entropy)
end
end
test "update as JSON" do
put board_entropy_path(@board), params: { board: { auto_postpone_period: 99.days } }, as: :json
put board_entropy_path(@board), params: { board: { auto_postpone_period_in_days: 90 } }, as: :json
assert_response :no_content
assert_equal 99.days, @board.entropy.reload.auto_postpone_period
assert_equal 90.days, @board.entropy.reload.auto_postpone_period
end
test "update requires board admin permission" do
@@ -28,9 +28,18 @@ class Boards::EntropiesControllerTest < ActionDispatch::IntegrationTest
original_period = @board.entropy.auto_postpone_period
put board_entropy_path(@board, format: :turbo_stream), params: { board: { auto_postpone_period: 1.day } }
put board_entropy_path(@board, format: :turbo_stream), params: { board: { auto_postpone_period_in_days: 7 } }
assert_response :forbidden
assert_equal original_period, @board.entropy.reload.auto_postpone_period
end
test "update rejects invalid auto_postpone_period" do
original_period = @board.entropy.auto_postpone_period
put board_entropy_path(@board, format: :turbo_stream), params: { board: { auto_postpone_period_in_days: 1 } }
assert_response :unprocessable_entity
assert_equal original_period, @board.entropy.reload.auto_postpone_period
end
end
+12 -2
View File
@@ -41,12 +41,22 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest
assert_response :success
end
test "edit renders 11-day auto-close option last on the knob" do
get edit_board_path(boards(:writebook))
assert_response :success
assert_select "input[type=radio][name='board[auto_postpone_period_in_days]']" do |options|
assert_equal Entropy::AUTO_POSTPONE_PERIODS_IN_DAYS.map(&:to_s), options.map { |option| option["value"] }
assert_equal "11", options.last["value"]
end
end
test "update" do
patch board_path(boards(:writebook)), params: {
board: {
name: "Writebook bugs",
all_access: false,
auto_postpone_period: 1.day
auto_postpone_period_in_days: 7
},
user_ids: users(:kevin, :jz).pluck(:id)
}
@@ -54,7 +64,7 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest
assert_redirected_to edit_board_path(boards(:writebook))
assert_equal "Writebook bugs", boards(:writebook).reload.name
assert_equal users(:kevin, :jz).sort, boards(:writebook).users.sort
assert_equal 1.day, entropies(:writebook_board).auto_postpone_period
assert_equal 7.days, entropies(:writebook_board).auto_postpone_period
assert_not boards(:writebook).all_access?
end