Refactoring: Migrate email-process unit sub-tests to RSpec.

This commit is contained in:
Ryan Lue 2019-03-11 08:28:18 +01:00 committed by Martin Edenhofer
parent 92d7ee95e0
commit b19ac485bf
8 changed files with 311 additions and 613 deletions

View file

@ -1,10 +1,15 @@
FactoryBot.define do FactoryBot.define do
factory :ticket do factory :ticket do
transient do
state_name 'new'
priority_name '2 normal'
end
title { 'Test Ticket' } title { 'Test Ticket' }
group group
customer customer
state { Ticket::State.lookup(name: 'new') } state { Ticket::State.lookup(name: state_name) }
priority { Ticket::Priority.lookup(name: '2 normal') } priority { Ticket::Priority.lookup(name: priority_name) }
updated_by_id { 1 } updated_by_id { 1 }
created_by_id { 1 } created_by_id { 1 }

View file

@ -0,0 +1,18 @@
require 'rails_helper'
RSpec.describe Mail::Encodings do
# Regression test for https://github.com/zammad/zammad/issues/2456
# (Mail lib was originally broken, so we patched it.
# Then, upstream was fixed, whereas our patch broke.)
describe '.value_decode' do
it 'decodes us-ascii encoded strings' do
expect(Mail::Encodings.value_decode('=?us-ascii?Q?Test?='))
.to eql('Test')
end
it 'decodes utf-8 encoded strings' do
expect(Mail::Encodings.value_decode('=?UTF-8?Q? Personal=C3=A4nderung?='))
.to eql(' Personaländerung')
end
end
end

View file

@ -1,50 +1,68 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe Channel::EmailParser, type: :model do RSpec.describe Channel::EmailParser, type: :model do
let(:subject) { described_class.new } describe '#parse' do
let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail001.box') } # regression test for issue 2390 - Add a postmaster filter to not show emails with potential issue
let(:raw_mail) { File.read(mail_file) } describe 'handling HTML links in message content' do
context 'with under 5,000 links' do
it 'parses message content as normal' do
expect(described_class.new.parse(<<~RAW)[:body]).to start_with('<a href="https://zammad.com/"')
From: nicole.braun@zammad.com
Content-Type: text/html
describe '#process' do <html><body>
let(:raw_mail) { File.read(mail_file).sub(/(?<=^Subject: ).*$/, test_string) } #{Array.new(10) { '<a href="https://zammad.com/">Dummy Link</a>' }.join(' ')}
let(:test_string) { Setting.get('ticket_hook') + Setting.get('ticket_hook_divider') + ticket.number } </body></html>
let(:ticket) { create(:ticket) } RAW
context 'for creating new users' do
context 'with one unrecognized email address' do
let(:raw_mail) { <<~RAW }
From: #{Faker::Internet.unique.email}
To: #{User.pluck(:email).reject(&:blank?).sample}
Subject: Foo bar
Lorem ipsum dolor
RAW
it 'creates one new user' do
expect { Channel::EmailParser.new.process({}, raw_mail) }
.to change { User.count }.by(1)
end end
end end
context 'with a large number (42+) of unrecognized email addresses' do context 'with 5,000+ links' do
let(:raw_mail) { <<~RAW } it 'replaces message content with error message' do
From: #{Faker::Internet.unique.email} expect(described_class.new.parse(<<~RAW)).to include('body' => Channel::EmailParser::EXCESSIVE_LINKS_MSG)
To: #{Array.new(22) { Faker::Internet.unique.email }.join(', ')} From: nicole.braun@zammad.com
Cc: #{Array.new(22) { Faker::Internet.unique.email }.join(', ')} Content-Type: text/html
Subject: test max sender identify
Some Text <html><body>
RAW #{Array.new(5001) { '<a href="https://zammad.com/">Dummy Link</a>' }.join(' ')}
</body></html>
RAW
end
end
end
end
it 'never creates more than 41 users per email' do describe '#process' do
expect { Channel::EmailParser.new.process({}, raw_mail) } let(:raw_mail) { File.read(mail_file) }
.to change { User.count }.by(41)
describe 'auto-creating new users' do
context 'with one unrecognized email address' do
it 'creates one new user' do
expect { Channel::EmailParser.new.process({}, <<~RAW) }.to change { User.count }.by(1)
From: #{Faker::Internet.unique.email}
RAW
end
end
context 'with a large number of unrecognized recipient addresses' do
it 'never creates more than 40 users' do
expect { Channel::EmailParser.new.process({}, <<~RAW) }.to change { User.count }.by(40)
From: nicole.braun@zammad.org
To: #{Array.new(20) { Faker::Internet.unique.email }.join(', ')}
Cc: #{Array.new(21) { Faker::Internet.unique.email }.join(', ')}
RAW
end end
end end
end end
context 'for associating emails to tickets' do describe 'associating emails to tickets' do
let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail001.box') }
let(:ticket_ref) { Setting.get('ticket_hook') + Setting.get('ticket_hook_divider') + ticket.number }
let(:ticket) { create(:ticket) }
context 'when email subject contains ticket reference' do context 'when email subject contains ticket reference' do
let(:raw_mail) { File.read(mail_file).sub(/(?<=^Subject: ).*$/, ticket_ref) }
it 'adds message to ticket' do it 'adds message to ticket' do
expect { described_class.new.process({}, raw_mail) } expect { described_class.new.process({}, raw_mail) }
.to change { ticket.articles.length } .to change { ticket.articles.length }
@ -56,7 +74,7 @@ RSpec.describe Channel::EmailParser, type: :model do
context 'when body contains ticket reference' do context 'when body contains ticket reference' do
context 'in visible text' do context 'in visible text' do
let(:raw_mail) { File.read(mail_file).sub(/Hallo =\nMartin,(?=<o:p>)/, test_string) } let(:raw_mail) { File.read(mail_file).sub(/Hallo =\nMartin,(?=<o:p>)/, ticket_ref) }
it 'adds message to ticket' do it 'adds message to ticket' do
expect { described_class.new.process({}, raw_mail) } expect { described_class.new.process({}, raw_mail) }
@ -65,7 +83,7 @@ RSpec.describe Channel::EmailParser, type: :model do
end end
context 'as part of a larger word' do context 'as part of a larger word' do
let(:raw_mail) { File.read(mail_file).sub(/(?<=Hallo) =\n(?=Martin,<o:p>)/, test_string) } let(:raw_mail) { File.read(mail_file).sub(/(?<=Hallo) =\n(?=Martin,<o:p>)/, ticket_ref) }
it 'creates a separate ticket' do it 'creates a separate ticket' do
expect { described_class.new.process({}, raw_mail) } expect { described_class.new.process({}, raw_mail) }
@ -74,7 +92,7 @@ RSpec.describe Channel::EmailParser, type: :model do
end end
context 'in html attributes' do context 'in html attributes' do
let(:raw_mail) { File.read(mail_file).sub(%r{<a href.*?/a>}m, %(<table bgcolor="#{test_string}"> </table>)) } let(:raw_mail) { File.read(mail_file).sub(%r{<a href.*?/a>}m, %(<table bgcolor="#{ticket_ref}"> </table>)) }
it 'creates a separate ticket' do it 'creates a separate ticket' do
expect { described_class.new.process({}, raw_mail) } expect { described_class.new.process({}, raw_mail) }
@ -85,7 +103,7 @@ RSpec.describe Channel::EmailParser, type: :model do
end end
end end
context 'for sender/recipient address formatting' do describe 'sender/recipient address formatting' do
# see https://github.com/zammad/zammad/issues/2198 # see https://github.com/zammad/zammad/issues/2198
context 'when sender address contains spaces (#2198)' do context 'when sender address contains spaces (#2198)' do
let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail071.box') } let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail071.box') }
@ -93,7 +111,7 @@ RSpec.describe Channel::EmailParser, type: :model do
it 'removes them before creating a new user' do it 'removes them before creating a new user' do
expect { described_class.new.process({}, raw_mail) } expect { described_class.new.process({}, raw_mail) }
.to change { User.where(email: sender_email).count }.to(1) .to change { User.exists?(email: sender_email) }
end end
it 'marks new user email as invalid' do it 'marks new user email as invalid' do
@ -113,7 +131,7 @@ RSpec.describe Channel::EmailParser, type: :model do
it 'removes them before creating a new user' do it 'removes them before creating a new user' do
expect { described_class.new.process({}, raw_mail) } expect { described_class.new.process({}, raw_mail) }
.to change { User.where(email: sender_email).count }.to(1) .to change { User.exists?(email: sender_email) }
end end
it 'marks new user email as invalid' do it 'marks new user email as invalid' do
@ -127,7 +145,7 @@ RSpec.describe Channel::EmailParser, type: :model do
end end
end end
context 'for charset handling' do describe 'charset handling' do
# see https://github.com/zammad/zammad/issues/2224 # see https://github.com/zammad/zammad/issues/2224
context 'when header specifies Windows-1258 charset (#2224)' do context 'when header specifies Windows-1258 charset (#2224)' do
let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail072.box') } let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail072.box') }
@ -139,46 +157,10 @@ RSpec.describe Channel::EmailParser, type: :model do
end end
end end
context 'mail with links' do describe 'attachment handling' do
context 'with header "Content-Transfer-Encoding: x-uuencode"' do
def mock_mail(number_of_links)
link = '<a href="https://zammad.com/">Dummy Link</a> '
mail = Mail.new
mail.html_part = "<html><body>#{link * number_of_links}</body></html>"
mail
end
let(:mail_10) { mock_mail(10).to_s }
let(:mail_5k) { mock_mail(5001).to_s }
# regression test for issue 2390 - Add a postmaster filter to not show emails with potential issue
it '(>5k links) are replaced by a warning message' do
expect( described_class.new.parse(mail_5k)[:body] )
.to eql( Channel::EmailParser::EXCESSIVE_LINKS_MSG )
end
it '(10 links) are not touched' do
expect( described_class.new.parse(mail_10)[:body] )
.to start_with( '<a href="https://zammad.com/"' )
end
end
context 'Mail::Encodings.value_decode' do
it 'decode us-ascii encoded strings' do
expect( Mail::Encodings.value_decode('=?us-ascii?Q?Test?=') ).to eql( 'Test' )
end
it 'decode utf-8 encoded strings' do
expect( Mail::Encodings.value_decode('=?UTF-8?Q? Personal=C3=A4nderung?=') ).to eql( ' Personaländerung' )
end
end
context 'when handling Content-Transfer-Encoding of attachments' do
context 'with x-uuencode' do
let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail078-content_transfer_encoding_x_uuencode.box') } let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail078-content_transfer_encoding_x_uuencode.box') }
let(:article) { described_class.new.process({}, raw_mail).second }
it 'does not raise RuntimeError' do it 'does not raise RuntimeError' do
expect { described_class.new.process({}, raw_mail) } expect { described_class.new.process({}, raw_mail) }
@ -186,26 +168,115 @@ RSpec.describe Channel::EmailParser, type: :model do
end end
it 'parses the content correctly' do it 'parses the content correctly' do
_ticket, article, _user, _mail = described_class.new.process({}, raw_mail)
expect(article.attachments.first.filename).to eq('PGP_Cmts_on_12-14-01_Pkg.txt') expect(article.attachments.first.filename).to eq('PGP_Cmts_on_12-14-01_Pkg.txt')
expect(article.attachments.first.content).to eq('Hello Zammad') expect(article.attachments.first.content).to eq('Hello Zammad')
end end
end end
end end
context 'when mail contains image(s)' do describe 'inline image handling' do
# see https://github.com/zammad/zammad/issues/2486 # see https://github.com/zammad/zammad/issues/2486
context 'when image is large but not resizable' do context 'when image is large but not resizable' do
let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail079.box') } let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail079.box') }
let(:attachment) { article.attachments.last }
let(:article) { described_class.new.process({}, raw_mail).second }
it "doesn't set resizable preference" do it "doesn't set resizable preference" do
_ticket, article = described_class.new.process({}, raw_mail)
attachment = article.attachments.last
expect(attachment.filename).to eq('a.jpg') expect(attachment.filename).to eq('a.jpg')
expect(attachment.preferences).not_to include('resizable' => true) expect(attachment.preferences).not_to include('resizable' => true)
end end
end end
end end
context 'for “delivery failed” notifications (a.k.a. bounce messages)' do
let(:ticket) { article.ticket }
let(:article) { create(:ticket_article, sender_name: 'Agent', message_id: message_id) }
let(:message_id) { raw_mail[/(?<=^(References|Message-ID): )\S*/] }
context 'with future retries (delayed)' do
let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail078.box') }
context 'on a closed ticket' do
before { ticket.update(state: Ticket::State.find_by(name: 'closed')) }
it 'sets #preferences on resulting ticket to { "send-auto-responses" => false, "is-auto-reponse" => true }' do
article = Channel::EmailParser.new.process({}, raw_mail).second
expect(article.preferences)
.to include('send-auto-response' => false, 'is-auto-response' => true)
end
it 'returns a Mail object with an x-zammad-out-of-office header' do
output_mail = Channel::EmailParser.new.process({}, raw_mail).last
expect(output_mail).to include('x-zammad-out-of-office': true)
end
it 'finds the article referenced in the bounce message headers, then adds the bounce message to its ticket' do
expect { Channel::EmailParser.new.process({}, raw_mail) }
.to change { ticket.articles.count }.by(1)
end
it 'does not re-open the ticket' do
expect { Channel::EmailParser.new.process({}, raw_mail) }
.not_to change { ticket.reload.state.name }.from('closed')
end
end
end
context 'with no future retries (undeliverable): sample input 1' do
let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail033-undelivered-mail-returned-to-sender.box') }
context 'for original message sent by Agent' do
it 'sets #preferences on resulting ticket to { "send-auto-responses" => false, "is-auto-reponse" => true }' do
article = Channel::EmailParser.new.process({}, raw_mail).second
expect(article.preferences)
.to include('send-auto-response' => false, 'is-auto-response' => true)
end
it 'finds the article referenced in the bounce message headers, then adds the bounce message to its ticket' do
expect { Channel::EmailParser.new.process({}, raw_mail) }
.to change { ticket.articles.count }.by(1)
end
it 'does not alter the ticket state' do
expect { Channel::EmailParser.new.process({}, raw_mail) }
.not_to change { ticket.reload.state.name }.from('open')
end
end
context 'for original message sent by Customer' do
let(:article) { create(:ticket_article, sender_name: 'Customer', message_id: message_id) }
it 'sets #preferences on resulting ticket to { "send-auto-responses" => false, "is-auto-reponse" => true }' do
article = Channel::EmailParser.new.process({}, raw_mail).second
expect(article.preferences)
.to include('send-auto-response' => false, 'is-auto-response' => true)
end
it 'finds the article referenced in the bounce message headers, then adds the bounce message to its ticket' do
expect { Channel::EmailParser.new.process({}, raw_mail) }
.to change { ticket.articles.count }.by(1)
end
it 'does not alter the ticket state' do
expect { Channel::EmailParser.new.process({}, raw_mail) }
.not_to change { ticket.reload.state.name }.from('new')
end
end
end
context 'with no future retries (undeliverable): sample input 2' do
let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail055.box') }
it 'finds the article referenced in the bounce message headers, then adds the bounce message to its ticket' do
expect { Channel::EmailParser.new.process({}, raw_mail) }
.to change { ticket.articles.count }.by(1)
end
it 'does not alter the ticket state' do
expect { Channel::EmailParser.new.process({}, raw_mail) }
.not_to change { ticket.reload.state.name }.from('open')
end
end
end
end end
end end

View file

@ -256,6 +256,18 @@ RSpec.describe Ticket, type: :model do
end end
describe 'Attributes:' do describe 'Attributes:' do
describe '#state' do
context 'for brand new tickets' do
context 'when a non-customer article is added' do
let(:article) { create(:ticket_article, ticket: ticket, sender_name: 'Agent') }
it 'switches to "open"' do
expect { article }.to change { ticket.state.name }.from('new').to('open')
end
end
end
end
describe '#pending_time' do describe '#pending_time' do
subject(:ticket) { create(:ticket, pending_time: Time.zone.now + 2.days) } subject(:ticket) { create(:ticket, pending_time: Time.zone.now + 2.days) }

View file

@ -4,10 +4,10 @@ require 'models/application_model_examples'
RSpec.describe Trigger, type: :model do RSpec.describe Trigger, type: :model do
it_behaves_like 'ApplicationModel', can_assets: { selectors: %i[condition perform] } it_behaves_like 'ApplicationModel', can_assets: { selectors: %i[condition perform] }
subject(:trigger) { create(:trigger) } before { Trigger.destroy_all } # Default DB state includes three sample triggers
subject!(:trigger) { create(:trigger, condition: condition, perform: perform) }
describe '#assets (for supplying model data to front-end framework)' do describe '#assets (for supplying model data to front-end framework)' do
subject(:trigger) { create(:trigger, condition: condition, perform: perform) }
let(:condition) { { 'ticket.state_id' => { operator: 'is', value: 1 } } } let(:condition) { { 'ticket.state_id' => { operator: 'is', value: 1 } } }
let(:perform) { { 'ticket.priority_id' => { value: 1 } } } let(:perform) { { 'ticket.priority_id' => { value: 1 } } }
@ -17,4 +17,113 @@ RSpec.describe Trigger, type: :model do
.and include(Ticket::Priority.first.assets({})) .and include(Ticket::Priority.first.assets({}))
end end
end end
describe 'Send-email triggers' do
let(:perform) do
{
'notification.email' => {
'recipient' => 'ticket_customer',
'subject' => 'foo',
'body' => 'bar'
}
}
end
context 'for condition "ticket created"' do
let(:condition) do
{ 'ticket.action' => { 'operator' => 'is', 'value' => 'create' } }
end
context 'when ticket is created directly' do
let!(:ticket) { create(:ticket) }
it 'fires (without altering ticket state)' do
expect { Observer::Transaction.commit }
.to change { Ticket::Article.count }.by(1)
.and not_change { ticket.reload.state.name }.from('new')
end
end
context 'when ticket is created via Channel::EmailParser.process' do
before { create(:email_address, groups: [Group.first]) }
let(:raw_email) { File.read(Rails.root.join('test', 'data', 'mail', 'mail001.box')) }
it 'fires (without altering ticket state)' do
expect { Channel::EmailParser.new.process({}, raw_email) }
.to change { Ticket.count }.by(1)
.and change { Ticket::Article.count }.by(2)
expect(Ticket.last.state.name).to eq('new')
end
end
end
context 'for condition "ticket updated"' do
let(:condition) do
{ 'ticket.action' => { 'operator' => 'is', 'value' => 'update' } }
end
let!(:ticket) { create(:ticket).tap { Observer::Transaction.commit } }
context 'when new article is created directly' do
context 'with empty #preferences hash' do
let!(:article) { create(:ticket_article, ticket: ticket) }
it 'fires (without altering ticket state)' do
expect { Observer::Transaction.commit }
.to change { ticket.reload.articles.count }.by(1)
.and not_change { ticket.reload.state.name }.from('new')
end
end
context 'with #preferences { "send-auto-response" => false }' do
let!(:article) do
create(:ticket_article,
ticket: ticket,
preferences: { 'send-auto-response' => false })
end
it 'does not fire' do
expect { Observer::Transaction.commit }
.not_to change { ticket.reload.articles.count }
end
end
end
context 'when new article is created via Channel::EmailParser.process' do
context 'with a regular message' do
let!(:article) do
create(:ticket_article,
ticket: ticket,
message_id: raw_email[/(?<=^References: )\S*/],
subject: raw_email[/(?<=^Subject: Re: ).*$/])
end
let(:raw_email) { File.read(Rails.root.join('test', 'data', 'mail', 'mail005.box')) }
it 'fires (without altering ticket state)' do
expect { Channel::EmailParser.new.process({}, raw_email) }
.to not_change { Ticket.count }
.and change { ticket.reload.articles.count }.by(2)
.and not_change { ticket.reload.state.name }.from('new')
end
end
context 'with delivery-failed "bounce message"' do
let!(:article) do
create(:ticket_article,
ticket: ticket,
message_id: raw_email[/(?<=^Message-ID: )\S*/])
end
let(:raw_email) { File.read(Rails.root.join('test', 'data', 'mail', 'mail055.box')) }
it 'does not fire' do
expect { Channel::EmailParser.new.process({}, raw_email) }
.to change { ticket.reload.articles.count }.by(1)
end
end
end
end
end
end end

View file

@ -1,52 +1,21 @@
require 'test_helper' Content-Type: multipart/report; boundary="000000000000ca4a1a057417a375"; report-type=delivery-status
class EmailProcessBounceDeliveryTemporaryFailed < ActiveSupport::TestCase
test 'process with temp faild bounce email' do
ticket = Ticket.create!(
title: 'temp failed check - ms',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'closed'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create!(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'temp failed check',
message_id: '<20150830145601.30.608881@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.lookup(name: 'Agent'),
type: Ticket::Article::Type.lookup(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
travel 1.second
# temp faild bounce email
email_raw_string = "Content-Type: multipart/report; boundary=\"000000000000ca4a1a057417a375\"; report-type=delivery-status
From: Mail Delivery Subsystem <mailer-daemon@example.com> From: Mail Delivery Subsystem <mailer-daemon@example.com>
To: service@example.de To: service@example.de
Auto-Submitted: auto-replied Auto-Submitted: auto-replied
Subject: Delivery Status Notification (Delay) Subject: Delivery Status Notification (Delay)
References: #{article.message_id} References: <20150830145601.30.608881@example.com>
In-Reply-To: #{article.message_id} In-Reply-To: <20150830145601.30.608881@example.com>
Message-ID: <5b7e8af4.1c69fb81.3ac1b.e296.GMRIR@mx.example.com> Message-ID: <5b7e8af4.1c69fb81.3ac1b.e296.GMRIR@mx.example.com>
Date: Thu, 23 Aug 2018 03:22:44 -0700 (PDT) Date: Thu, 23 Aug 2018 03:22:44 -0700 (PDT)
--000000000000ca4a1a057417a375 --000000000000ca4a1a057417a375
Content-Type: multipart/related; boundary=\"000000000000ca53d4057417a37c\" Content-Type: multipart/related; boundary="000000000000ca53d4057417a37c"
--000000000000ca53d4057417a37c --000000000000ca53d4057417a37c
Content-Type: multipart/alternative; boundary=\"000000000000ca53de057417a37d\" Content-Type: multipart/alternative; boundary="000000000000ca53de057417a37d"
--000000000000ca53de057417a37d --000000000000ca53de057417a37d
Content-Type: text/plain; charset=\"UTF-8\" Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable Content-Transfer-Encoding: quoted-printable
@ -72,7 +41,7 @@ Content-Type: message/delivery-status
Reporting-MTA: dns; example.com Reporting-MTA: dns; example.com
Received-From-MTA: dns; service@example.de Received-From-MTA: dns; service@example.de
Arrival-Date: Wed, 22 Aug 2018 02:00:03 -0700 (PDT) Arrival-Date: Wed, 22 Aug 2018 02:00:03 -0700 (PDT)
X-Original-Message-ID: #{article.message_id} X-Original-Message-ID: <20150830145601.30.608881@example.com>
Final-Recipient: rfc822; bob.smith@example.de Final-Recipient: rfc822; bob.smith@example.de
Action: delayed Action: delayed
@ -89,21 +58,10 @@ Content-Type: message/rfc822
Date: Wed, 22 Aug 2018 11:00:03 +0200 Date: Wed, 22 Aug 2018 11:00:03 +0200
From: example Helpdesk <service@example.de> From: example Helpdesk <service@example.de>
To: bob.smith@example.de To: bob.smith@example.de
Message-ID: #{article.message_id} Message-ID: <20150830145601.30.608881@example.com>
Subject: Ihre Anfrage () [Ticket#638810] Subject: Ihre Anfrage () [Ticket#638810]
Content-Type: text/plain; charset=UTF-8 Content-Type: text/plain; charset=UTF-8
ABC ABC
--000000000000ca4a1a057417a375-- --000000000000ca4a1a057417a375--
"
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(true, mail['x-zammad-out-of-office'.to_sym])
ticket = Ticket.find(ticket.id)
assert_equal(ticket.id, ticket_p.id)
assert_equal('closed', ticket.state.name)
end
end

View file

@ -1,219 +0,0 @@
require 'test_helper'
class EmailProcessBounceDeliveryPermanentFailedTest < ActiveSupport::TestCase
test 'process with bounce trigger email loop check - article based blocker' do
roles = Role.where(name: %w[Customer])
customer1 = User.create_or_update(
login: 'ticket-bounce-trigger1@example.com',
firstname: 'Notification',
lastname: 'Customer1',
email: 'ticket-bounce-trigger1@example.com',
active: true,
roles: roles,
preferences: {},
updated_by_id: 1,
created_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply new ticket',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'create',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquiry (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply followup',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'update',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your follow up (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket = Ticket.create!(
title: 'bounce check',
group: Group.lookup(name: 'Users'),
customer: customer1,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create!(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.6088xx@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal('new', ticket.state.name)
assert_equal(2, ticket.articles.count)
article = Ticket::Article.create!(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: customer1.email,
subject: 'bounce check 2',
message_id: '<20150830145601.30.608881@edenhofer.zammad.com>',
body: 'some message bounce check 2',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal(4, ticket.articles.count)
travel 1.second
email_raw_string = File.read(Rails.root.join('test', 'data', 'mail', 'mail033-undelivered-mail-returned-to-sender.box'))
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket_p.state.name)
assert_equal(5, ticket_p.articles.count)
travel_back
ticket.destroy
end
test 'process with bounce trigger email loop check - bounce based blocker' do
roles = Role.where(name: %w[Customer])
customer2 = User.create_or_update(
login: 'ticket-bounce-trigger2@example.com',
firstname: 'Notification',
lastname: 'Customer2',
email: 'ticket-bounce-trigger2@example.com',
active: true,
roles: roles,
preferences: {},
updated_by_id: 1,
created_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply new ticket',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'create',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquiry (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply followup',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'update',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your follow up (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket = Ticket.create!(
title: 'bounce check',
group: Group.lookup(name: 'Users'),
customer: customer2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create!(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.6088xx@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal('new', ticket.state.name)
assert_equal(2, ticket.articles.count)
article = Ticket::Article.create!(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check 2',
message_id: '<20170526150141.232.13312@example.zammad.loc>',
body: 'some message bounce check 2',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal(4, ticket.articles.count)
travel 1.second
email_raw_string = File.read(Rails.root.join('test', 'data', 'mail', 'mail055.box'))
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket_p.state.name)
assert_equal(5, ticket_p.articles.count)
travel_back
ticket.destroy
end
end

View file

@ -1,256 +0,0 @@
require 'test_helper'
class EmailProcessBounceFollowUpTest < ActiveSupport::TestCase
test 'process with bounce follow up check' do
ticket = Ticket.create(
title: 'bounce check',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.608881@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Customer').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
travel 1.second
email_file_path = Rails.root.join('test', 'data', 'mail', 'mail033-undelivered-mail-returned-to-sender.box')
email_raw_string = File.read(email_file_path)
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('new', ticket_p.state.name)
travel_back
ticket.destroy
end
test 'process with bounce trigger email loop check - article based blocker' do
roles = Role.where(name: %w[Customer])
customer1 = User.create_or_update(
login: 'ticket-bounce-trigger1@example.com',
firstname: 'Notification',
lastname: 'Customer1',
email: 'ticket-bounce-trigger1@example.com',
active: true,
roles: roles,
preferences: {},
updated_by_id: 1,
created_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply new ticket',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'create',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquiry (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply followup',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'update',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your follow up (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket = Ticket.create(
title: 'bounce check',
group: Group.lookup(name: 'Users'),
customer: customer1,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.6088xx@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal('new', ticket.state.name)
assert_equal(2, ticket.articles.count)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: customer1.email,
subject: 'bounce check 2',
message_id: '<20150830145601.30.608881@edenhofer.zammad.com>',
body: 'some message bounce check 2',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal(4, ticket.articles.count)
travel 1.second
email_file_path = Rails.root.join('test', 'data', 'mail', 'mail033-undelivered-mail-returned-to-sender.box')
email_raw_string = File.read(email_file_path)
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket_p.state.name)
assert_equal(5, ticket_p.articles.count)
travel_back
ticket.destroy
end
test 'process with bounce trigger email loop check - bounce based blocker' do
roles = Role.where(name: %w[Customer])
customer2 = User.create_or_update(
login: 'ticket-bounce-trigger2@example.com',
firstname: 'Notification',
lastname: 'Customer2',
email: 'ticket-bounce-trigger2@example.com',
active: true,
roles: roles,
preferences: {},
updated_by_id: 1,
created_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply new ticket',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'create',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquiry (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply followup',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'update',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your follow up (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket = Ticket.create(
title: 'bounce check',
group: Group.lookup(name: 'Users'),
customer: customer2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.6088xx@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal('new', ticket.state.name)
assert_equal(2, ticket.articles.count)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check 2',
message_id: '<20170526150141.232.13312@example.zammad.loc>',
body: 'some message bounce check 2',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal(4, ticket.articles.count)
travel 1.second
email_file_path = Rails.root.join('test', 'data', 'mail', 'mail055.box')
email_raw_string = File.read(email_file_path)
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket_p.state.name)
assert_equal(5, ticket_p.articles.count)
travel_back
ticket.destroy
end
end