From c00aaa132a8ee73f933cfd311e516a30e9b207ba Mon Sep 17 00:00:00 2001 From: Nat Budin Date: Wed, 14 Jan 2026 08:51:23 -0800 Subject: [PATCH 1/2] EmailForwardingRouter is normalizing email addresses for forwarding lists and shouldn't be Fixes #11160 --- app/services/email_forwarding_router.rb | 11 +++-- test/services/email_forwarding_router_test.rb | 48 ++++++++++--------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/app/services/email_forwarding_router.rb b/app/services/email_forwarding_router.rb index bdad3d195c..0223a01500 100644 --- a/app/services/email_forwarding_router.rb +++ b/app/services/email_forwarding_router.rb @@ -18,7 +18,7 @@ def self.from_address(address, destination_addresses: []) def initialize(inbound_domain:, inbound_local:, destination_addresses:) @inbound_domain = EmailRoute.normalize_domain(inbound_domain) @inbound_local = EmailRoute.normalize_local(inbound_local) - @destination_addresses = destination_addresses.map { |address| EmailRoute.normalize_address(address) }.uniq + @destination_addresses = EmailForwardingRouter.deduplicate_destinations(destination_addresses) end def inbound_email @@ -166,6 +166,11 @@ def self.all_destination_addresses all_mappings.values.flatten.uniq end + def self.deduplicate_destinations(destinations) + destinations_by_normalized_address = destinations.index_by { |dest| EmailRoute.normalize_address(dest) } + destinations_by_normalized_address.values + end + attr_reader :address def initialize(address) @@ -174,12 +179,12 @@ def initialize(address) def convention_by_domain return @convention_by_domain if defined?(@convention_by_domain) - @convention_by_domain ||= Convention.find_by(domain: Mail::Address.new(address).domain) + @convention_by_domain = Convention.find_by(domain: Mail::Address.new(address).domain) end def convention_by_events_domain return @convention_by_events_domain if defined?(@convention_by_events_domain) - @convention_by_events_domain ||= Convention.find_by(event_mailing_list_domain: Mail::Address.new(address).domain) + @convention_by_events_domain = Convention.find_by(event_mailing_list_domain: Mail::Address.new(address).domain) end def forward_addresses diff --git a/test/services/email_forwarding_router_test.rb b/test/services/email_forwarding_router_test.rb index 0b83cd3444..9068af364f 100644 --- a/test/services/email_forwarding_router_test.rb +++ b/test/services/email_forwarding_router_test.rb @@ -39,9 +39,8 @@ class EmailForwardingRouterTest < ActiveSupport::TestCase help_mapping = mappings.values.find { |m| m.inbound_email == "help@example.com" } support_mapping = mappings.values.find { |m| m.inbound_email == "support@example.com" } - expected_destinations = [user1.email, user2.email, "cc@example.com"].map do |addr| - EmailRoute.normalize_address(addr) - end + expected_destinations = + [user1.email, user2.email, "cc@example.com"].map { |addr| EmailRoute.normalize_address(addr) } assert_equal expected_destinations.sort, primary_mapping.destination_addresses.sort assert_equal expected_destinations.sort, help_mapping.destination_addresses.sort @@ -50,14 +49,7 @@ class EmailForwardingRouterTest < ActiveSupport::TestCase end describe "without email aliases" do - let(:staff_position) do - create( - :staff_position, - convention:, - email: "staff@example.com", - email_aliases: [] - ) - end + let(:staff_position) { create(:staff_position, convention:, email: "staff@example.com", email_aliases: []) } setup { staff_position.user_con_profiles << user_con_profile1 } @@ -72,21 +64,11 @@ class EmailForwardingRouterTest < ActiveSupport::TestCase describe "with multiple staff positions with aliases" do let(:staff_position1) do - create( - :staff_position, - convention:, - email: "staff1@example.com", - email_aliases: %w[help support] - ) + create(:staff_position, convention:, email: "staff1@example.com", email_aliases: %w[help support]) end let(:staff_position2) do - create( - :staff_position, - convention:, - email: "staff2@example.com", - email_aliases: ["info"] - ) + create(:staff_position, convention:, email: "staff2@example.com", email_aliases: ["info"]) end setup do @@ -111,4 +93,24 @@ class EmailForwardingRouterTest < ActiveSupport::TestCase end end end + + describe "deduplication" do + let(:staff_position) do + create( + :staff_position, + convention:, + email: "staff@example.com", + cc_addresses: %w[email.address1@example.com Email.Address1@example.com] + ) + end + + setup { staff_position } + + it "deduplicates email addresses but does not modify them" do + mappings = EmailForwardingRouter.all_staff_position_mappings + + assert_equal 1, mappings.values.size + assert_equal ["email.address1@example.com"], mappings.values.first.destination_addresses.map(&:downcase) + end + end end From 3a8c4dc64aa213b56c2fb7252af010aff4c054b3 Mon Sep 17 00:00:00 2001 From: Nat Budin Date: Wed, 14 Jan 2026 08:52:57 -0800 Subject: [PATCH 2/2] Speed up tsc --- tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 56488da7d9..45ea1d7071 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,8 @@ "skipLibCheck": true, "maxNodeModuleJsDepth": 10, "baseUrl": "./app/javascript", - "types": ["vitest/globals", "vite/client"] + "types": ["vitest/globals", "vite/client"], + "incremental": true }, "include": [ "./app/javascript/",