Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/services/event_registration_services/public_registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ def call

Result.new(success?: true, event_registration: event_registration, form_submission: submission, errors: [])
end
rescue ActiveRecord::ValueTooLong => e
Result.new(success?: false, event_registration: nil, errors: [ too_long_message(e) ])

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: ValueTooLong is a StatementInvalid, not a RecordInvalid, so it needs its own rescue — otherwise an answer longer than its varchar(255) column 500s mid-registration instead of re-rendering the form.

rescue ActiveRecord::RecordInvalid => e
Result.new(success?: false, event_registration: nil, errors: [ e.message ])
rescue ActiveRecord::RecordNotUnique => e
Expand All @@ -75,6 +77,17 @@ def call

private

# Turn a database "Data too long for column 'city'" failure into a friendly,
# form-level message. We can't always map the column back to a single form
# field (both the mailing and agency address write `city`), so we name the
# column generically and ask the registrant to shorten that answer.
def too_long_message(error)
column = error.message[/column '([^']+)'/, 1]
return "One of your answers is too long. Please shorten it and try again." if column.blank?

"Your #{column.humanize.downcase} is too long. Please shorten it and try again."
end

def field_value(key)
field = @form.form_fields.find_by(field_identifier: key)
return nil unless field
Expand Down
35 changes: 35 additions & 0 deletions spec/requests/events/public_registrations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,41 @@ def post_registration(answer)
end
end

describe "POST create with an answer longer than its database column" do
# A real registration form maps answers onto person/address columns; `city`
# and friends are varchar(255). An over-length answer used to 500 with
# ActiveRecord::ValueTooLong — it should re-render the form with an error.
# These optional fields carry the identifiers the service maps to columns.
%w[first_name last_name primary_email mailing_street mailing_city mailing_state mailing_zip].each do |identifier|
let!("#{identifier}_field".to_sym) do
create(:form_field, form: form, field_identifier: identifier, name: identifier.humanize, required: false)
end
end

def fid(key)
form.form_fields.find_by!(field_identifier: key).id.to_s
end

it "re-renders the form with an error instead of raising" do
expect {
post event_public_registration_path(event),
params: { public_registration: { form_fields: {
essay_field.id.to_s => "this answer has plenty of words",
fid("first_name") => "Pat",
fid("last_name") => "Lee",
fid("primary_email") => "pat@example.com",
fid("mailing_street") => "1 Main St",
fid("mailing_city") => "a" * 256,
fid("mailing_state") => "CA",
fid("mailing_zip") => "90001"
} } }
}.not_to change(EventRegistration, :count)

expect(response).to have_http_status(:unprocessable_content)
expect(response.body).to match(/city is too long/i)
end
end

describe "POST create with a maximum character count" do
let!(:bio_field) do
create(:form_field, form: form, answer_type: :free_form_input_one_line,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,36 @@ def base_form_params(first_name:, last_name:, email:)
}
end

describe "an answer longer than its database column" do
# `city` (like the other mapped person/address columns) is a varchar(255).
# A longer answer must surface as a form error, not an ActiveRecord::ValueTooLong
# 500 that escapes the registration flow.
let(:params) do
base_form_params(first_name: "Pat", last_name: "Lee", email: "pat@example.com").merge(
field_id("mailing_street") => "1 Main St",
field_id("mailing_city") => "a" * 256,
field_id("mailing_state") => "CA",
field_id("mailing_zip") => "90001"
)
end

it "returns a failed result instead of raising" do
result = nil
expect {
result = described_class.call(event: event, form: form, form_params: params)
}.not_to raise_error

expect(result.success?).to be false
expect(result.errors.join).to match(/city.*too long/i)
end

it "does not create a registration" do
expect {
described_class.call(event: event, form: form, form_params: params)
}.not_to change(EventRegistration, :count)
end
end

describe "primary service area tagging" do
let!(:primary_sector) { create(:sector, name: "Healthcare") }
let!(:other_sector) { create(:sector, name: "Education") }
Expand Down