From 3e979b0fa93ff5946fdf93996b29a488e5e197c2 Mon Sep 17 00:00:00 2001
From: SeongHoon Ryu <4997174+ryush00@users.noreply.github.com>
Date: Sun, 8 Mar 2026 00:58:57 +0900
Subject: [PATCH 1/4] fix: revert from_message refactor and keep minimal change
---
lib/mailtrap/mail.rb | 2 +
...converts_the_message_and_sends_via_API.yml | 2 +-
spec/mailtrap/mail_spec.rb | 43 ++++++++++++++++++-
3 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/lib/mailtrap/mail.rb b/lib/mailtrap/mail.rb
index 20091ec..99e1bc1 100644
--- a/lib/mailtrap/mail.rb
+++ b/lib/mailtrap/mail.rb
@@ -17,6 +17,7 @@ module Mail # rubocop:disable Metrics/ModuleLength
category
customvariables
contenttype
+ replyto
].freeze
private_constant :SPECIAL_HEADERS
@@ -195,6 +196,7 @@ def from_message(message)
to: prepare_addresses(message['to']),
cc: prepare_addresses(message['cc']),
bcc: prepare_addresses(message['bcc']),
+ reply_to: prepare_addresses(message['reply-to']).first,
subject: message.subject,
text: prepare_text_part(message),
html: prepare_html_part(message),
diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ActionMailer_DeliveryMethod/_deliver_/converts_the_message_and_sends_via_API.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ActionMailer_DeliveryMethod/_deliver_/converts_the_message_and_sends_via_API.yml
index 002dcfe..e391e0c 100644
--- a/spec/fixtures/vcr_cassettes/Mailtrap_ActionMailer_DeliveryMethod/_deliver_/converts_the_message_and_sends_via_API.yml
+++ b/spec/fixtures/vcr_cassettes/Mailtrap_ActionMailer_DeliveryMethod/_deliver_/converts_the_message_and_sends_via_API.yml
@@ -5,7 +5,7 @@ http_interactions:
uri: https://send.api.mailtrap.io/api/send
body:
encoding: UTF-8
- string: '{"from":{"email":"mailtrap@mailtrap.io","name":"Mailtrap Test"},"to":[{"email":"to_1@railsware.com","name":"To 1"},{"email":"to_2@railsware.com"}],"cc":[{"email":"cc_1@railsware.com"},{"email":"cc_2@railsware.com","name":"Cc 2"}],"bcc":[{"email":"bcc_1@railsware.com"},{"email":"bcc_2@railsware.com"}],"subject":"You are awesome!","text":"Some text","html":"
HTML part
","attachments":[{"content":"VGhpcyBpcyBhIHRleHQgZmlsZQo=","type":"text/plain","filename":"file.txt","disposition":"attachment","content_id":"txt_content_id@test.mail"},{"content":"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABiElEQVRIid3VSW5UMRCA4U8d0gIx7AjhAghBTgFICAkxRFHukDDDIRiuwI7xPNAMISQRV4CABFmkWbie2nrt9muaFZTkxavhr7Jfucz/Ln2s4hU28B0/sBO6VczPCl/GNoYd6wsuZ3ELWKuBe3iSAQa4g7M4jEM4jRt4g5+4lMEHEbc+KUED/xVOvY5iThXgg/gek+UMfq4CbstU8L7RmU/c3qxwUkc0TnN/CT9Ycn4djrenhB/H24j5iMXQX8TTUsCncD6T6daUtzyp8hNSV30uJfgWAUfje70AqMFF7FC6kGOy20rQPoKTBd1ii3EsbF9LCUpH1K62q1uWwr5RSvAyjHdb+rzqSZU38iB8npWMTZu+M96mzU5qfT6H98FYKTn0sRUONwv2hQqcNK+G2FSZsNeNRsX5CqwtF7CHfVzpcn6cJbmlfqsPSJXvRczDaarpZUmaf3JP6pAjsZZw3+jM9/FIffKOyTXpRnY9OJu4+ifgXOaljnghtedurA94HraZn8x/Q34DYaON8Fk9Z1IAAAAASUVORK5CYII=","type":"image/png","filename":"file.png","disposition":"inline","content_id":"png_content_id@test.mail"}],"headers":{"Reply-To":"reply-to@railsware.com","X-Special-Domain-Specific-Header":"SecretValue","One-more-custom-header":"CustomValue"},"category":"Module Test"}'
+ string: '{"from":{"email":"mailtrap@mailtrap.io","name":"Mailtrap Test"},"to":[{"email":"to_1@railsware.com","name":"To 1"},{"email":"to_2@railsware.com"}],"reply_to":{"email":"reply-to@railsware.com"},"cc":[{"email":"cc_1@railsware.com"},{"email":"cc_2@railsware.com","name":"Cc 2"}],"bcc":[{"email":"bcc_1@railsware.com"},{"email":"bcc_2@railsware.com"}],"subject":"You are awesome!","text":"Some text","html":"HTML part
","attachments":[{"content":"VGhpcyBpcyBhIHRleHQgZmlsZQo=","type":"text/plain","filename":"file.txt","disposition":"attachment","content_id":"txt_content_id@test.mail"},{"content":"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABiElEQVRIid3VSW5UMRCA4U8d0gIx7AjhAghBTgFICAkxRFHukDDDIRiuwI7xPNAMISQRV4CABFmkWbie2nrt9muaFZTkxavhr7Jfucz/Ln2s4hU28B0/sBO6VczPCl/GNoYd6wsuZ3ELWKuBe3iSAQa4g7M4jEM4jRt4g5+4lMEHEbc+KUED/xVOvY5iThXgg/gek+UMfq4CbstU8L7RmU/c3qxwUkc0TnN/CT9Ycn4djrenhB/H24j5iMXQX8TTUsCncD6T6daUtzyp8hNSV30uJfgWAUfje70AqMFF7FC6kGOy20rQPoKTBd1ii3EsbF9LCUpH1K62q1uWwr5RSvAyjHdb+rzqSZU38iB8npWMTZu+M96mzU5qfT6H98FYKTn0sRUONwv2hQqcNK+G2FSZsNeNRsX5CqwtF7CHfVzpcn6cJbmlfqsPSJXvRczDaarpZUmaf3JP6pAjsZZw3+jM9/FIffKOyTXpRnY9OJu4+ifgXOaljnghtedurA94HraZn8x/Q34DYaON8Fk9Z1IAAAAASUVORK5CYII=","type":"image/png","filename":"file.png","disposition":"inline","content_id":"png_content_id@test.mail"}],"headers":{"X-Special-Domain-Specific-Header":"SecretValue","One-more-custom-header":"CustomValue"},"category":"Module Test"}'
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
diff --git a/spec/mailtrap/mail_spec.rb b/spec/mailtrap/mail_spec.rb
index 80c7e56..7e555d3 100644
--- a/spec/mailtrap/mail_spec.rb
+++ b/spec/mailtrap/mail_spec.rb
@@ -40,7 +40,6 @@
let(:expected_headers) do
{
'X-Special-Domain-Specific-Header' => 'SecretValue',
- 'Reply-To' => 'Reply To ',
'One-more-custom-header' => 'CustomValue'
}
end
@@ -53,6 +52,35 @@
it { is_expected.to eq(expected_headers) }
end
+
+ context 'when reply-to is added in varying formats' do
+ [
+ ['"메일트랩" ', { email: 'support@mailtrap.io', name: '메일트랩' }],
+ ['"Mailtrap Team" ', { email: 'support@mailtrap.io', name: 'Mailtrap Team' }],
+ ['support@mailtrap.io', { email: 'support@mailtrap.io' }]
+ ].each do |reply_to, expected_reply_to|
+ it "maps '#{reply_to}' to structured field and excludes header copy" do
+ message.reply_to = reply_to
+
+ expect(mail.reply_to).to eq(expected_reply_to)
+ expect(headers).not_to have_key('Reply-To')
+ end
+ end
+ end
+
+ context 'when custom header and reply-to variants are present' do
+ before do
+ message.header['Reply-To'] = 'Reply To '
+ message.header['REPLY-TO'] = 'Upper Case '
+ message.header['reply-to'] = 'Lower Case '
+ message.header['X-Special-Domain-Specific-Header'] = 'SecretValue'
+ end
+
+ it 'keeps only custom headers and strips all reply-to header variants' do
+ expect(mail.reply_to).to eq({ email: 'lower-reply-to@railsware.com', name: 'Lower Case' })
+ expect(headers).to eq('X-Special-Domain-Specific-Header' => 'SecretValue')
+ end
+ end
end
describe '#attachment' do
@@ -155,6 +183,19 @@
its(:template_variables) { is_expected.to eq('first_name' => 'John') }
end
+ context "when 'reply-to' is invalid" do
+ before do
+ message.header['Reply-To'] = 'invalid email@example.com'
+ end
+
+ it 'raises an error' do
+ expect { mail }.to raise_error(
+ Mailtrap::Error,
+ "failed to parse 'Reply-To': 'invalid email@example.com'"
+ )
+ end
+ end
+
%i[from to cc bcc].each do |header|
context "when '#{header}' is invalid" do
let(:message_params) { super().merge(header => 'invalid email@example.com') }
From 1bf3b140c667c71b54b161835e173720ff3deb90 Mon Sep 17 00:00:00 2001
From: SeongHoon Ryu <4997174+ryush00@users.noreply.github.com>
Date: Sun, 8 Mar 2026 13:06:53 +0900
Subject: [PATCH 2/4] test: cover folded line-break reply-to parsing error
---
lib/mailtrap/mail.rb | 2 +
...converts_the_message_and_sends_via_API.yml | 2 +-
spec/mailtrap/mail_spec.rb | 61 ++++++++++++++++++-
3 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/lib/mailtrap/mail.rb b/lib/mailtrap/mail.rb
index 20091ec..99e1bc1 100644
--- a/lib/mailtrap/mail.rb
+++ b/lib/mailtrap/mail.rb
@@ -17,6 +17,7 @@ module Mail # rubocop:disable Metrics/ModuleLength
category
customvariables
contenttype
+ replyto
].freeze
private_constant :SPECIAL_HEADERS
@@ -195,6 +196,7 @@ def from_message(message)
to: prepare_addresses(message['to']),
cc: prepare_addresses(message['cc']),
bcc: prepare_addresses(message['bcc']),
+ reply_to: prepare_addresses(message['reply-to']).first,
subject: message.subject,
text: prepare_text_part(message),
html: prepare_html_part(message),
diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ActionMailer_DeliveryMethod/_deliver_/converts_the_message_and_sends_via_API.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ActionMailer_DeliveryMethod/_deliver_/converts_the_message_and_sends_via_API.yml
index 002dcfe..e391e0c 100644
--- a/spec/fixtures/vcr_cassettes/Mailtrap_ActionMailer_DeliveryMethod/_deliver_/converts_the_message_and_sends_via_API.yml
+++ b/spec/fixtures/vcr_cassettes/Mailtrap_ActionMailer_DeliveryMethod/_deliver_/converts_the_message_and_sends_via_API.yml
@@ -5,7 +5,7 @@ http_interactions:
uri: https://send.api.mailtrap.io/api/send
body:
encoding: UTF-8
- string: '{"from":{"email":"mailtrap@mailtrap.io","name":"Mailtrap Test"},"to":[{"email":"to_1@railsware.com","name":"To 1"},{"email":"to_2@railsware.com"}],"cc":[{"email":"cc_1@railsware.com"},{"email":"cc_2@railsware.com","name":"Cc 2"}],"bcc":[{"email":"bcc_1@railsware.com"},{"email":"bcc_2@railsware.com"}],"subject":"You are awesome!","text":"Some text","html":"HTML part
","attachments":[{"content":"VGhpcyBpcyBhIHRleHQgZmlsZQo=","type":"text/plain","filename":"file.txt","disposition":"attachment","content_id":"txt_content_id@test.mail"},{"content":"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABiElEQVRIid3VSW5UMRCA4U8d0gIx7AjhAghBTgFICAkxRFHukDDDIRiuwI7xPNAMISQRV4CABFmkWbie2nrt9muaFZTkxavhr7Jfucz/Ln2s4hU28B0/sBO6VczPCl/GNoYd6wsuZ3ELWKuBe3iSAQa4g7M4jEM4jRt4g5+4lMEHEbc+KUED/xVOvY5iThXgg/gek+UMfq4CbstU8L7RmU/c3qxwUkc0TnN/CT9Ycn4djrenhB/H24j5iMXQX8TTUsCncD6T6daUtzyp8hNSV30uJfgWAUfje70AqMFF7FC6kGOy20rQPoKTBd1ii3EsbF9LCUpH1K62q1uWwr5RSvAyjHdb+rzqSZU38iB8npWMTZu+M96mzU5qfT6H98FYKTn0sRUONwv2hQqcNK+G2FSZsNeNRsX5CqwtF7CHfVzpcn6cJbmlfqsPSJXvRczDaarpZUmaf3JP6pAjsZZw3+jM9/FIffKOyTXpRnY9OJu4+ifgXOaljnghtedurA94HraZn8x/Q34DYaON8Fk9Z1IAAAAASUVORK5CYII=","type":"image/png","filename":"file.png","disposition":"inline","content_id":"png_content_id@test.mail"}],"headers":{"Reply-To":"reply-to@railsware.com","X-Special-Domain-Specific-Header":"SecretValue","One-more-custom-header":"CustomValue"},"category":"Module Test"}'
+ string: '{"from":{"email":"mailtrap@mailtrap.io","name":"Mailtrap Test"},"to":[{"email":"to_1@railsware.com","name":"To 1"},{"email":"to_2@railsware.com"}],"reply_to":{"email":"reply-to@railsware.com"},"cc":[{"email":"cc_1@railsware.com"},{"email":"cc_2@railsware.com","name":"Cc 2"}],"bcc":[{"email":"bcc_1@railsware.com"},{"email":"bcc_2@railsware.com"}],"subject":"You are awesome!","text":"Some text","html":"HTML part
","attachments":[{"content":"VGhpcyBpcyBhIHRleHQgZmlsZQo=","type":"text/plain","filename":"file.txt","disposition":"attachment","content_id":"txt_content_id@test.mail"},{"content":"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABiElEQVRIid3VSW5UMRCA4U8d0gIx7AjhAghBTgFICAkxRFHukDDDIRiuwI7xPNAMISQRV4CABFmkWbie2nrt9muaFZTkxavhr7Jfucz/Ln2s4hU28B0/sBO6VczPCl/GNoYd6wsuZ3ELWKuBe3iSAQa4g7M4jEM4jRt4g5+4lMEHEbc+KUED/xVOvY5iThXgg/gek+UMfq4CbstU8L7RmU/c3qxwUkc0TnN/CT9Ycn4djrenhB/H24j5iMXQX8TTUsCncD6T6daUtzyp8hNSV30uJfgWAUfje70AqMFF7FC6kGOy20rQPoKTBd1ii3EsbF9LCUpH1K62q1uWwr5RSvAyjHdb+rzqSZU38iB8npWMTZu+M96mzU5qfT6H98FYKTn0sRUONwv2hQqcNK+G2FSZsNeNRsX5CqwtF7CHfVzpcn6cJbmlfqsPSJXvRczDaarpZUmaf3JP6pAjsZZw3+jM9/FIffKOyTXpRnY9OJu4+ifgXOaljnghtedurA94HraZn8x/Q34DYaON8Fk9Z1IAAAAASUVORK5CYII=","type":"image/png","filename":"file.png","disposition":"inline","content_id":"png_content_id@test.mail"}],"headers":{"X-Special-Domain-Specific-Header":"SecretValue","One-more-custom-header":"CustomValue"},"category":"Module Test"}'
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
diff --git a/spec/mailtrap/mail_spec.rb b/spec/mailtrap/mail_spec.rb
index 80c7e56..2e84548 100644
--- a/spec/mailtrap/mail_spec.rb
+++ b/spec/mailtrap/mail_spec.rb
@@ -40,7 +40,6 @@
let(:expected_headers) do
{
'X-Special-Domain-Specific-Header' => 'SecretValue',
- 'Reply-To' => 'Reply To ',
'One-more-custom-header' => 'CustomValue'
}
end
@@ -53,6 +52,35 @@
it { is_expected.to eq(expected_headers) }
end
+
+ context 'when reply-to is added in varying formats' do
+ [
+ ['"메일트랩" ', { email: 'support@mailtrap.io', name: '메일트랩' }],
+ ['"Mailtrap Team" ', { email: 'support@mailtrap.io', name: 'Mailtrap Team' }],
+ ['support@mailtrap.io', { email: 'support@mailtrap.io' }]
+ ].each do |reply_to, expected_reply_to|
+ it "maps '#{reply_to}' to structured field and excludes header copy" do
+ message.reply_to = reply_to
+
+ expect(mail.reply_to).to eq(expected_reply_to)
+ expect(headers).not_to have_key('Reply-To')
+ end
+ end
+ end
+
+ context 'when custom header and reply-to variants are present' do
+ before do
+ message.header['Reply-To'] = 'Reply To '
+ message.header['REPLY-TO'] = 'Upper Case '
+ message.header['reply-to'] = 'Lower Case '
+ message.header['X-Special-Domain-Specific-Header'] = 'SecretValue'
+ end
+
+ it 'keeps only custom headers and strips all reply-to header variants' do
+ expect(mail.reply_to).to eq({ email: 'lower-reply-to@railsware.com', name: 'Lower Case' })
+ expect(headers).to eq('X-Special-Domain-Specific-Header' => 'SecretValue')
+ end
+ end
end
describe '#attachment' do
@@ -167,5 +195,36 @@
end
end
end
+
+ context "when 'reply-to' is invalid" do
+ let(:invalid_reply_to) { 'invalid email@example.com' }
+
+ before do
+ message.header['Reply-To'] = invalid_reply_to
+ end
+
+ it 'raises an error' do
+ expect { mail }.to raise_error(
+ Mailtrap::Error,
+ "failed to parse 'Reply-To': 'invalid email@example.com'"
+ )
+ end
+
+ context 'when address contains a folded line break' do
+ let(:invalid_reply_to) do
+ encoded_name = ['메일트랩 팀'.encode('UTF-8')].pack('m0')
+ folded_domain = %w[exa mple.com].join("\r\n ")
+
+ "=?UTF-8?B?#{encoded_name}?= "
+ end
+
+ it 'raises an error' do
+ expect { mail }.to raise_error(
+ Mailtrap::Error,
+ /failed to parse 'Reply-To': '.*no-reply@exa/m
+ )
+ end
+ end
+ end
end
end
From 41ffc54c08c29ff557f7893207befe63c3e7eff4 Mon Sep 17 00:00:00 2001
From: SeongHoon Ryu <4997174+ryush00@users.noreply.github.com>
Date: Tue, 10 Mar 2026 11:59:33 +0000
Subject: [PATCH 3/4] refactor: cleanup tests
---
spec/mailtrap/mail_spec.rb | 69 ++++++++++++++++----------------------
1 file changed, 28 insertions(+), 41 deletions(-)
diff --git a/spec/mailtrap/mail_spec.rb b/spec/mailtrap/mail_spec.rb
index af0c6b8..d8ebdab 100644
--- a/spec/mailtrap/mail_spec.rb
+++ b/spec/mailtrap/mail_spec.rb
@@ -54,17 +54,12 @@
end
context 'when reply-to is added in varying formats' do
- [
- ['"메일트랩" ', { email: 'support@mailtrap.io', name: '메일트랩' }],
- ['"Mailtrap Team" ', { email: 'support@mailtrap.io', name: 'Mailtrap Team' }],
- ['support@mailtrap.io', { email: 'support@mailtrap.io' }]
- ].each do |reply_to, expected_reply_to|
- it "maps '#{reply_to}' to structured field and excludes header copy" do
- message.reply_to = reply_to
-
- expect(mail.reply_to).to eq(expected_reply_to)
- expect(headers).not_to have_key('Reply-To')
- end
+ before do
+ message.reply_to = '"메일트랩" '
+ end
+
+ it 'excludes reply-to from custom headers' do
+ expect(headers).not_to have_key('Reply-To')
end
end
@@ -77,12 +72,33 @@
end
it 'keeps only custom headers and strips all reply-to header variants' do
- expect(mail.reply_to).to eq({ email: 'lower-reply-to@railsware.com', name: 'Lower Case' })
expect(headers).to eq('X-Special-Domain-Specific-Header' => 'SecretValue')
end
end
end
+ describe '#reply_to' do
+ before do
+ message.reply_to = '"메일트랩" '
+ end
+
+ it 'maps reply-to to the structured field' do
+ expect(mail.reply_to).to eq(email: 'support@mailtrap.io', name: '메일트랩')
+ end
+
+ context 'when reply-to header variants are present' do
+ before do
+ message.header['Reply-To'] = 'Reply To '
+ message.header['REPLY-TO'] = 'Upper Case '
+ message.header['reply-to'] = 'Lower Case '
+ end
+
+ it 'maps the reply-to value to the structured field' do
+ expect(mail.reply_to).to eq({ email: 'lower-reply-to@railsware.com', name: 'Lower Case' })
+ end
+ end
+ end
+
describe '#attachment' do
subject(:json_attachments) { mail.attachments.map(&:as_json) }
@@ -183,19 +199,6 @@
its(:template_variables) { is_expected.to eq('first_name' => 'John') }
end
- context "when 'reply-to' is invalid" do
- before do
- message.header['Reply-To'] = 'invalid email@example.com'
- end
-
- it 'raises an error' do
- expect { mail }.to raise_error(
- Mailtrap::Error,
- "failed to parse 'Reply-To': 'invalid email@example.com'"
- )
- end
- end
-
%i[from to cc bcc].each do |header|
context "when '#{header}' is invalid" do
let(:message_params) { super().merge(header => 'invalid email@example.com') }
@@ -222,22 +225,6 @@
"failed to parse 'Reply-To': 'invalid email@example.com'"
)
end
-
- context 'when address contains a folded line break' do
- let(:invalid_reply_to) do
- encoded_name = ['메일트랩 팀'.encode('UTF-8')].pack('m0')
- folded_domain = %w[exa mple.com].join("\r\n ")
-
- "=?UTF-8?B?#{encoded_name}?= "
- end
-
- it 'raises an error' do
- expect { mail }.to raise_error(
- Mailtrap::Error,
- /failed to parse 'Reply-To': '.*no-reply@exa/m
- )
- end
- end
end
end
end
From 0ec636712c91b5ff3b9c454e28582e32db5af333 Mon Sep 17 00:00:00 2001
From: SeongHoon Ryu <4997174+ryush00@users.noreply.github.com>
Date: Tue, 10 Mar 2026 12:43:54 +0000
Subject: [PATCH 4/4] test: restore original reply-to address examples
---
spec/mailtrap/mail_spec.rb | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/spec/mailtrap/mail_spec.rb b/spec/mailtrap/mail_spec.rb
index d8ebdab..2ee0e09 100644
--- a/spec/mailtrap/mail_spec.rb
+++ b/spec/mailtrap/mail_spec.rb
@@ -55,7 +55,7 @@
context 'when reply-to is added in varying formats' do
before do
- message.reply_to = '"메일트랩" '
+ message.reply_to = 'Reply To '
end
it 'excludes reply-to from custom headers' do
@@ -79,11 +79,11 @@
describe '#reply_to' do
before do
- message.reply_to = '"메일트랩" '
+ message.reply_to = 'Reply To '
end
it 'maps reply-to to the structured field' do
- expect(mail.reply_to).to eq(email: 'support@mailtrap.io', name: '메일트랩')
+ expect(mail.reply_to).to eq(email: 'reply-to@railsware.com', name: 'Reply To')
end
context 'when reply-to header variants are present' do