From 9dd13b9a2be045e2f1ec549745800bc8af5e009b Mon Sep 17 00:00:00 2001 From: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:54:52 +0530 Subject: [PATCH 1/3] fix: topup checkout flaky test (#13616) --- .../billing/topup_checkout_service_spec.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/spec/enterprise/services/enterprise/billing/topup_checkout_service_spec.rb b/spec/enterprise/services/enterprise/billing/topup_checkout_service_spec.rb index 8a64e6fd2..24c7889de 100644 --- a/spec/enterprise/services/enterprise/billing/topup_checkout_service_spec.rb +++ b/spec/enterprise/services/enterprise/billing/topup_checkout_service_spec.rb @@ -44,17 +44,19 @@ describe Enterprise::Billing::TopupCheckoutService do end it 'raises error for invalid credits' do - expect do - service.create_checkout_session(credits: 500) - end.to raise_error(Enterprise::Billing::TopupCheckoutService::Error) + expect { service.create_checkout_session(credits: 500) }.to raise_error do |error| + expect(error.class.name).to eq('Enterprise::Billing::TopupCheckoutService::Error') + expect(error.message).to eq(I18n.t('errors.topup.invalid_option')) + end end it 'raises error when account is on free plan' do account.update!(custom_attributes: { plan_name: 'Hacker', stripe_customer_id: stripe_customer_id }) - expect do - service.create_checkout_session(credits: 1000) - end.to raise_error(Enterprise::Billing::TopupCheckoutService::Error) + expect { service.create_checkout_session(credits: 1000) }.to raise_error do |error| + expect(error.class.name).to eq('Enterprise::Billing::TopupCheckoutService::Error') + expect(error.message).to eq(I18n.t('errors.topup.plan_not_eligible')) + end end end end From 957a1b17c9178ac8b838dec0e8384a405572c07b Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma <32020192+tds-1@users.noreply.github.com> Date: Mon, 23 Feb 2026 15:08:11 +0530 Subject: [PATCH 2/3] perf: add default configs for assignment V2 (#13577) ## Description AutoAssignment::RateLimiter#within_limit? returned true early for inboxes without an AssignmentPolicy, bypassing fair distribution entirely and allowing unlimited conversation assignment. ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) ## Script - Script to enable users with V2 assignment: https://www.notion.so/chatwoot/Script-to-migrate-account-to-assignment-V2-30ca5f274c9280f5b8ecfd15e28eeb9c?source=copy_link ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Shivam Mishra --- app/services/auto_assignment/rate_limiter.rb | 12 ++---------- spec/services/auto_assignment/rate_limiter_spec.rb | 6 +++--- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/app/services/auto_assignment/rate_limiter.rb b/app/services/auto_assignment/rate_limiter.rb index da8daac9c..b46148526 100644 --- a/app/services/auto_assignment/rate_limiter.rb +++ b/app/services/auto_assignment/rate_limiter.rb @@ -2,8 +2,6 @@ class AutoAssignment::RateLimiter pattr_initialize [:inbox!, :agent!] def within_limit? - return true unless enabled? - current_count < limit end @@ -13,24 +11,18 @@ class AutoAssignment::RateLimiter end def current_count - return 0 unless enabled? - pattern = assignment_key_pattern Redis::Alfred.keys_count(pattern) end private - def enabled? - config.present? && limit.positive? - end - def limit - config&.fair_distribution_limit.present? ? config.fair_distribution_limit.to_i : Float::INFINITY + config&.fair_distribution_limit.present? ? config.fair_distribution_limit.to_i : 5 end def window - config&.fair_distribution_window&.to_i || 24.hours.to_i + config&.fair_distribution_window&.to_i || 5.minutes.to_i end def config diff --git a/spec/services/auto_assignment/rate_limiter_spec.rb b/spec/services/auto_assignment/rate_limiter_spec.rb index d5c3de047..59b145c87 100644 --- a/spec/services/auto_assignment/rate_limiter_spec.rb +++ b/spec/services/auto_assignment/rate_limiter_spec.rb @@ -61,7 +61,7 @@ RSpec.describe AutoAssignment::RateLimiter do it 'still tracks the assignment with default window' do expected_key = format(Redis::RedisKeys::ASSIGNMENT_KEY, inbox_id: inbox.id, agent_id: agent.id, conversation_id: conversation.id) - expect(Redis::Alfred).to receive(:set).with(expected_key, conversation.id.to_s, ex: 24.hours.to_i) + expect(Redis::Alfred).to receive(:set).with(expected_key, conversation.id.to_s, ex: 5.minutes.to_i) rate_limiter.track_assignment(conversation) end end @@ -154,12 +154,12 @@ RSpec.describe AutoAssignment::RateLimiter do allow(inbox).to receive(:assignment_policy).and_return(assignment_policy) end - it 'uses the default window value of 24 hours' do + it 'uses the default window value of 5 minutes' do expected_key = format(Redis::RedisKeys::ASSIGNMENT_KEY, inbox_id: inbox.id, agent_id: agent.id, conversation_id: conversation.id) expect(Redis::Alfred).to receive(:set).with( expected_key, conversation.id.to_s, - ex: 86_400 + ex: 5.minutes.to_i ) rate_limiter.track_assignment(conversation) end From 40da358dc2079687d8eff02ca70dcf85dc6da54e Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 23 Feb 2026 16:00:17 +0530 Subject: [PATCH 3/3] feat: better errors for SMTP (#13401) Co-authored-by: Muhsin Keloth --- app/helpers/api/v1/inboxes_helper.rb | 38 +++++++++---------- .../dashboard/settings/inbox/SmtpSettings.vue | 4 +- config/locales/en.yml | 5 +++ .../v1/accounts/inboxes_controller_spec.rb | 3 ++ 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/app/helpers/api/v1/inboxes_helper.rb b/app/helpers/api/v1/inboxes_helper.rb index d734a346e..3d6b559c8 100644 --- a/app/helpers/api/v1/inboxes_helper.rb +++ b/app/helpers/api/v1/inboxes_helper.rb @@ -57,39 +57,35 @@ module Api::V1::InboxesHelper end def check_smtp_connection(channel_data, smtp) + smtp.open_timeout = 10 smtp.start(channel_data[:smtp_domain], channel_data[:smtp_login], channel_data[:smtp_password], channel_data[:smtp_authentication]&.to_sym || :login) smtp.finish + rescue Net::SMTPAuthenticationError + raise StandardError, I18n.t('errors.inboxes.smtp.authentication_error') + rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Net::OpenTimeout + raise StandardError, I18n.t('errors.inboxes.smtp.connection_error') + rescue OpenSSL::SSL::SSLError + raise StandardError, I18n.t('errors.inboxes.smtp.ssl_error') + rescue Net::SMTPServerBusy, Net::SMTPSyntaxError, Net::SMTPFatalError + raise StandardError, I18n.t('errors.inboxes.smtp.smtp_error') + rescue StandardError => e + raise StandardError, e.message end def set_smtp_encryption(channel_data, smtp) if channel_data[:smtp_enable_ssl_tls] - set_enable_tls(channel_data, smtp) + set_smtp_ssl_method(smtp, :enable_tls, channel_data[:smtp_openssl_verify_mode]) elsif channel_data[:smtp_enable_starttls_auto] - set_enable_starttls_auto(channel_data, smtp) + set_smtp_ssl_method(smtp, :enable_starttls_auto, channel_data[:smtp_openssl_verify_mode]) end end - def set_enable_starttls_auto(channel_data, smtp) - return unless smtp.respond_to?(:enable_starttls_auto) + def set_smtp_ssl_method(smtp, method, openssl_verify_mode) + return unless smtp.respond_to?(method) - if channel_data[:smtp_openssl_verify_mode] - context = enable_openssl_mode(channel_data[:smtp_openssl_verify_mode]) - smtp.enable_starttls_auto(context) - else - smtp.enable_starttls_auto - end - end - - def set_enable_tls(channel_data, smtp) - return unless smtp.respond_to?(:enable_tls) - - if channel_data[:smtp_openssl_verify_mode] - context = enable_openssl_mode(channel_data[:smtp_openssl_verify_mode]) - smtp.enable_tls(context) - else - smtp.enable_tls - end + context = enable_openssl_mode(openssl_verify_mode) if openssl_verify_mode + context ? smtp.send(method, context) : smtp.send(method) end def enable_openssl_mode(smtp_openssl_verify_mode) diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/SmtpSettings.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/SmtpSettings.vue index 575e0e5d2..e8bc723c2 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/SmtpSettings.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/SmtpSettings.vue @@ -147,7 +147,9 @@ export default { await this.$store.dispatch('inboxes/updateInboxSMTP', payload); useAlert(this.$t('INBOX_MGMT.SMTP.EDIT.SUCCESS_MESSAGE')); } catch (error) { - useAlert(this.$t('INBOX_MGMT.SMTP.EDIT.ERROR_MESSAGE')); + useAlert( + error.message || this.$t('INBOX_MGMT.SMTP.EDIT.ERROR_MESSAGE') + ); } }, }, diff --git a/config/locales/en.yml b/config/locales/en.yml index a058d28c7..05276009f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -108,6 +108,11 @@ en: host_unreachable_error: Host unreachable, Please check the IMAP address, IMAP port and try again. connection_timed_out_error: Connection timed out for %{address}:%{port} connection_closed_error: Connection closed. + smtp: + authentication_error: SMTP authentication failed. Please verify your login credentials. + connection_error: Could not connect to SMTP server. Please check the server address and port. + ssl_error: SSL/TLS error. Please verify your encryption settings. + smtp_error: SMTP server error. Please check your configuration and try again. validations: name: should not start or end with symbols, and it should not have < > / \ @ characters. custom_filters: diff --git a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb index fafd6788e..b93ca8ecf 100644 --- a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb @@ -631,6 +631,7 @@ RSpec.describe 'Inboxes API', type: :request do it 'updates smtp configuration with starttls encryption' do smtp_connection = double + allow(smtp_connection).to receive(:open_timeout=).and_return(10) allow(smtp_connection).to receive(:start).and_return(true) allow(smtp_connection).to receive(:finish).and_return(true) allow(smtp_connection).to receive(:respond_to?).and_return(true) @@ -661,6 +662,7 @@ RSpec.describe 'Inboxes API', type: :request do it 'updates smtp configuration with ssl/tls encryption' do smtp_connection = double + allow(smtp_connection).to receive(:open_timeout=).and_return(10) allow(smtp_connection).to receive(:start).and_return(true) allow(smtp_connection).to receive(:finish).and_return(true) allow(smtp_connection).to receive(:respond_to?).and_return(true) @@ -691,6 +693,7 @@ RSpec.describe 'Inboxes API', type: :request do it 'updates smtp configuration with authentication mechanism' do smtp_connection = double + allow(smtp_connection).to receive(:open_timeout=).and_return(10) allow(smtp_connection).to receive(:start).and_return(true) allow(smtp_connection).to receive(:finish).and_return(true) allow(smtp_connection).to receive(:respond_to?).and_return(true)