diff --git a/.rubocop_todo.rspec.yml b/.rubocop_todo.rspec.yml
index a7bf4d619..eab54855d 100644
--- a/.rubocop_todo.rspec.yml
+++ b/.rubocop_todo.rspec.yml
@@ -272,7 +272,7 @@ RSpec/NamedSubject:
# Offense count: 545
RSpec/NestedGroups:
- Max: 7
+ Max: 8
# Offense count: 28
# Cop supports --auto-correct.
diff --git a/spec/models/channel/email_parser_spec.rb b/spec/models/channel/email_parser_spec.rb
index c9a67ecbf..17019be4a 100644
--- a/spec/models/channel/email_parser_spec.rb
+++ b/spec/models/channel/email_parser_spec.rb
@@ -35,6 +35,8 @@ RSpec.describe Channel::EmailParser, type: :model do
describe '#process' do
let(:raw_mail) { File.read(mail_file) }
+ before { Trigger.destroy_all } # triggers may cause additional articles to be created
+
describe 'auto-creating new users' do
context 'with one unrecognized email address' do
it 'creates one new user' do
@@ -98,7 +100,7 @@ RSpec.describe Channel::EmailParser, type: :model do
it 'creates a ticket and article' do
expect { Channel::EmailParser.new.process({}, raw_mail) }
.to change { Ticket.count }.by(1)
- .and change { Ticket::Article.count }.by_at_least(1) # triggers may cause additional articles to be created
+ .and change { Ticket::Article.count }.by_at_least(1)
end
it 'sets #title to email subject' do
@@ -156,99 +158,485 @@ RSpec.describe Channel::EmailParser, type: :model do
end
describe 'associating emails to existing tickets' do
- let(:mail_file) { Rails.root.join('test', 'data', 'mail', 'mail001.box') }
+ let!(:ticket) { create(:ticket) }
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
- let(:raw_mail) { File.read(mail_file).sub(/(?<=^Subject: ).*$/, ticket_ref) }
+ describe 'based on where a ticket reference appears in the message' do
+ shared_context 'ticket reference in subject' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: #{ticket_ref}
- it 'adds message to ticket' do
- expect { described_class.new.process({}, raw_mail) }
- .to change { ticket.articles.length }
+ Lorem ipsum dolor
+ RAW
end
- context 'and ticket is closed' do
- before { ticket.update(state: Ticket::State.find_by(name: 'closed')) }
+ shared_context 'ticket reference in body' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: no reference
+ Lorem ipsum dolor #{ticket_ref}
+ RAW
+ end
+
+ shared_context 'ticket reference in body (text/html)' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: no reference
+ Content-Transfer-Encoding: 7bit
+ Content-Type: text/html;
+
+ Lorem ipsum dolor #{ticket_ref}
+ RAW
+ end
+
+ shared_context 'ticket reference in attachment' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ Content-Type: multipart/mixed; boundary="Apple-Mail=_ED77AC8D-FB6F-40E5-8FBE-D41FF5E1BAF2"
+ Subject: no reference
+ Date: Sun, 30 Aug 2015 23:20:54 +0200
+ To: Martin Edenhofer
+ Mime-Version: 1.0 (Mac OS X Mail 8.2 \(2104\))
+ X-Mailer: Apple Mail (2.2104)
+
+
+ --Apple-Mail=_ED77AC8D-FB6F-40E5-8FBE-D41FF5E1BAF2
+ Content-Transfer-Encoding: 7bit
+ Content-Type: text/plain;
+ charset=us-ascii
+
+ no reference
+ --Apple-Mail=_ED77AC8D-FB6F-40E5-8FBE-D41FF5E1BAF2
+ Content-Disposition: attachment;
+ filename=test1.txt
+ Content-Type: text/plain;
+ name="test.txt"
+ Content-Transfer-Encoding: 7bit
+
+ Some Text #{ticket_ref}
+
+ --Apple-Mail=_ED77AC8D-FB6F-40E5-8FBE-D41FF5E1BAF2--
+ RAW
+ end
+
+ shared_context 'ticket reference in In-Reply-To header' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: no reference
+ In-Reply-To: #{article.message_id}
+
+ Lorem ipsum dolor
+ RAW
+
+ let!(:article) { create(:ticket_article, ticket: ticket, message_id: '<20150830145601.30.608882@edenhofer.zammad.com>') }
+ end
+
+ shared_context 'ticket reference in References header' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: no reference
+ References: #{article.message_id}
+
+ Lorem ipsum dolor
+ RAW
+
+ let!(:article) { create(:ticket_article, ticket: ticket, message_id: '<20150830145601.30.608882@edenhofer.zammad.com>') }
+ end
+
+ shared_examples 'adds message to ticket' do
it 'adds message to ticket' do
expect { described_class.new.process({}, raw_mail) }
- .to change { ticket.articles.length }
+ .to change { ticket.articles.length }.by(1)
end
end
- context 'but ticket group’s #follow_up_possible attribute is "new_ticket"' do
- before { ticket.group.update(follow_up_possible: 'new_ticket') }
+ shared_examples 'creates a new ticket' do
+ it 'creates a new ticket' do
+ expect { described_class.new.process({}, raw_mail) }
+ .to change { Ticket.count }.by(1)
+ .and not_change { ticket.articles.length }
+ end
+ end
- context 'and ticket is open' do
- it 'still adds message to ticket' do
- expect { described_class.new.process({}, raw_mail) }
- .to change { ticket.articles.length }
+ context 'when not explicitly configured to search anywhere' do
+ before { Setting.set('postmaster_follow_up_search_in', nil) }
+
+ context 'when subject contains ticket reference' do
+ include_context 'ticket reference in subject'
+ include_examples 'adds message to ticket'
+
+ context 'alongside other, invalid ticket references' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: [#{Setting.get('ticket_hook') + Setting.get('ticket_hook_divider') + Ticket::Number.generate}] #{ticket_ref}
+
+ Lorem ipsum dolor
+ RAW
+
+ include_examples 'adds message to ticket'
+ end
+
+ context 'and ticket is closed' do
+ before { ticket.update(state: Ticket::State.find_by(name: 'closed')) }
+
+ include_examples 'adds message to ticket'
+ end
+
+ context 'but ticket group’s #follow_up_possible attribute is "new_ticket"' do
+ before { ticket.group.update(follow_up_possible: 'new_ticket') }
+
+ context 'and ticket is open' do
+ include_examples 'adds message to ticket'
+ end
+
+ context 'and ticket is closed' do
+ before { ticket.update(state: Ticket::State.find_by(name: 'closed')) }
+
+ include_examples 'creates a new ticket'
+ end
+
+ context 'and ticket is merged' do
+ before { ticket.update(state: Ticket::State.find_by(name: 'merged')) }
+
+ include_examples 'creates a new ticket'
+ end
+
+ context 'and ticket is removed' do
+ before { ticket.update(state: Ticket::State.find_by(name: 'removed')) }
+
+ include_examples 'creates a new ticket'
+ end
+ end
+
+ context 'and "ticket_hook" setting is non-default value' do
+ before { Setting.set('ticket_hook', 'VD-Ticket#') }
+
+ include_examples 'adds message to ticket'
end
end
- context 'and ticket is closed' do
- before { ticket.update(state: Ticket::State.find_by(name: 'closed')) }
+ context 'when body contains ticket reference' do
+ include_context 'ticket reference in body'
+ include_examples 'creates a new ticket'
+ end
- it 'creates a new ticket' do
- expect { described_class.new.process({}, raw_mail) }
- .to change { Ticket.count }.by(1)
- .and not_change { ticket.articles.length }
+ context 'when attachment contains ticket reference' do
+ include_context 'ticket reference in attachment'
+ include_examples 'creates a new ticket'
+ end
+
+ context 'when In-Reply-To header contains article message-id' do
+ include_context 'ticket reference in In-Reply-To header'
+ include_examples 'creates a new ticket'
+
+ context 'and subject matches article subject' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: customer@example.com
+ To: me@example.com
+ Subject: AW: RE: #{article.subject}
+ In-Reply-To: #{article.message_id}
+
+ Lorem ipsum dolor
+ RAW
+
+ include_examples 'adds message to ticket'
+ end
+
+ context 'and "ticket_hook_position" setting is "none"' do
+ before { Setting.set('ticket_hook_position', 'none') }
+
+ let(:raw_mail) { <<~RAW.chomp }
+ From: customer@example.com
+ To: me@example.com
+ Subject: RE: Foo bar
+ In-Reply-To: #{article.message_id}
+
+ Lorem ipsum dolor
+ RAW
+
+ include_examples 'adds message to ticket'
end
end
- context 'and ticket is merged' do
- before { ticket.update(state: Ticket::State.find_by(name: 'merged')) }
+ context 'when References header contains article message-id' do
+ include_context 'ticket reference in References header'
+ include_examples 'creates a new ticket'
- it 'creates a new ticket' do
- expect { described_class.new.process({}, raw_mail) }
- .to change { Ticket.count }.by(1)
- .and not_change { ticket.articles.length }
+ context 'and Auto-Submitted header reads "auto-replied"' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: no reference
+ References: #{article.message_id}
+ Auto-Submitted: auto-replied
+
+ Lorem ipsum dolor
+ RAW
+
+ include_examples 'adds message to ticket'
+ end
+
+ context 'and subject matches article subject' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: customer@example.com
+ To: me@example.com
+ Subject: AW: RE: #{article.subject}
+ References: #{article.message_id}
+
+ Lorem ipsum dolor
+ RAW
+
+ include_examples 'adds message to ticket'
+ end
+
+ context 'and "ticket_hook_position" setting is "none"' do
+ before { Setting.set('ticket_hook_position', 'none') }
+
+ let(:raw_mail) { <<~RAW.chomp }
+ From: customer@example.com
+ To: me@example.com
+ Subject: RE: Foo bar
+ References: #{article.message_id}
+
+ Lorem ipsum dolor
+ RAW
+
+ include_examples 'adds message to ticket'
+ end
+ end
+ end
+
+ context 'when configured to search body' do
+ before { Setting.set('postmaster_follow_up_search_in', 'body') }
+
+ context 'when subject contains ticket reference' do
+ include_context 'ticket reference in subject'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'when body contains ticket reference' do
+ context 'in visible text' do
+ include_context 'ticket reference in body'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'as part of a larger word' do
+ let(:ticket_ref) { "Foo#{Setting.get('ticket_hook')}#{Setting.get('ticket_hook_divider')}#{ticket.number}bar" }
+
+ include_context 'ticket reference in body'
+ include_examples 'creates a new ticket'
+ end
+
+ context 'between html tags' do
+ include_context 'ticket reference in body (text/html)'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'in html attributes' do
+ let(:ticket_ref) { %() }
+
+ include_context 'ticket reference in body (text/html)'
+ include_examples 'creates a new ticket'
end
end
- context 'and ticket is removed' do
- before { ticket.update(state: Ticket::State.find_by(name: 'removed')) }
+ context 'when attachment contains ticket reference' do
+ include_context 'ticket reference in attachment'
+ include_examples 'creates a new ticket'
+ end
- it 'creates a new ticket' do
- expect { described_class.new.process({}, raw_mail) }
- .to change { Ticket.count }.by(1)
- .and not_change { ticket.articles.length }
+ context 'when In-Reply-To header contains article message-id' do
+ include_context 'ticket reference in In-Reply-To header'
+ include_examples 'creates a new ticket'
+
+ context 'and Auto-Submitted header reads "auto-replied"' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: no reference
+ References: #{article.message_id}
+ Auto-Submitted: auto-replied
+
+ Lorem ipsum dolor
+ RAW
+
+ include_examples 'adds message to ticket'
+ end
+ end
+
+ context 'when References header contains article message-id' do
+ include_context 'ticket reference in References header'
+ include_examples 'creates a new ticket'
+ end
+ end
+
+ context 'when configured to search attachments' do
+ before { Setting.set('postmaster_follow_up_search_in', 'attachment') }
+
+ context 'when subject contains ticket reference' do
+ include_context 'ticket reference in subject'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'when body contains ticket reference' do
+ include_context 'ticket reference in body'
+ include_examples 'creates a new ticket'
+ end
+
+ context 'when attachment contains ticket reference' do
+ include_context 'ticket reference in attachment'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'when In-Reply-To header contains article message-id' do
+ include_context 'ticket reference in In-Reply-To header'
+ include_examples 'creates a new ticket'
+ end
+
+ context 'when References header contains article message-id' do
+ include_context 'ticket reference in References header'
+ include_examples 'creates a new ticket'
+
+ context 'and Auto-Submitted header reads "auto-replied"' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: no reference
+ References: #{article.message_id}
+ Auto-Submitted: auto-replied
+
+ Lorem ipsum dolor
+ RAW
+
+ include_examples 'adds message to ticket'
+ end
+ end
+ end
+
+ context 'when configured to search headers' do
+ before { Setting.set('postmaster_follow_up_search_in', 'references') }
+
+ context 'when subject contains ticket reference' do
+ include_context 'ticket reference in subject'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'when body contains ticket reference' do
+ include_context 'ticket reference in body'
+ include_examples 'creates a new ticket'
+ end
+
+ context 'when attachment contains ticket reference' do
+ include_context 'ticket reference in attachment'
+ include_examples 'creates a new ticket'
+ end
+
+ context 'when In-Reply-To header contains article message-id' do
+ include_context 'ticket reference in In-Reply-To header'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'when References header contains article message-id' do
+ include_context 'ticket reference in References header'
+ include_examples 'adds message to ticket'
+
+ context 'that matches two separate tickets' do
+ let!(:newer_ticket) { create(:ticket) }
+ let!(:newer_article) { create(:ticket_article, ticket: newer_ticket, message_id: article.message_id) }
+
+ it 'returns more recently created ticket' do
+ expect(described_class.new.process({}, raw_mail).first).to eq(newer_ticket)
+ end
+
+ it 'adds message to more recently created ticket' do
+ expect { described_class.new.process({}, raw_mail) }
+ .to change { newer_ticket.articles.count }.by(1)
+ .and not_change { ticket.articles.count }
+ end
+ end
+
+ context 'and Auto-Submitted header reads "auto-replied"' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: no reference
+ References: #{article.message_id}
+ Auto-Submitted: auto-replied
+
+ Lorem ipsum dolor
+ RAW
+
+ include_examples 'adds message to ticket'
+ end
+ end
+ end
+
+ context 'when configured to search everything' do
+ before { Setting.set('postmaster_follow_up_search_in', %w[body attachment references]) }
+
+ context 'when subject contains ticket reference' do
+ include_context 'ticket reference in subject'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'when body contains ticket reference' do
+ include_context 'ticket reference in body'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'when attachment contains ticket reference' do
+ include_context 'ticket reference in attachment'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'when In-Reply-To header contains article message-id' do
+ include_context 'ticket reference in In-Reply-To header'
+ include_examples 'adds message to ticket'
+ end
+
+ context 'when References header contains article message-id' do
+ include_context 'ticket reference in References header'
+ include_examples 'adds message to ticket'
+
+ context 'and Auto-Submitted header reads "auto-replied"' do
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: no reference
+ References: #{article.message_id}
+ Auto-Submitted: auto-replied
+
+ Lorem ipsum dolor
+ RAW
+
+ include_examples 'adds message to ticket'
end
end
end
end
- context 'when configured to search body' do
- before { Setting.set('postmaster_follow_up_search_in', 'body') }
+ context 'for a closed ticket' do
+ let(:ticket) { create(:ticket, state_name: 'closed') }
- context 'when body contains ticket reference' do
- context 'in visible text' do
- let(:raw_mail) { File.read(mail_file).sub(/Hallo =\nMartin,(?=)/, ticket_ref) }
+ let(:raw_mail) { <<~RAW.chomp }
+ From: me@example.com
+ To: customer@example.com
+ Subject: #{ticket_ref}
- it 'adds message to ticket' do
- expect { described_class.new.process({}, raw_mail) }
- .to change { ticket.articles.length }
- end
- end
+ Lorem ipsum dolor
+ RAW
- context 'as part of a larger word' do
- let(:raw_mail) { File.read(mail_file).sub(/(?<=Hallo) =\n(?=Martin,)/, ticket_ref) }
-
- it 'creates a separate ticket' do
- expect { described_class.new.process({}, raw_mail) }
- .not_to change { ticket.articles.length }
- end
- end
-
- context 'in html attributes' do
- let(:raw_mail) { File.read(mail_file).sub(%r{}m, %()) }
-
- it 'creates a separate ticket' do
- expect { described_class.new.process({}, raw_mail) }
- .not_to change { ticket.articles.length }
- end
- end
+ it 'reopens it' do
+ expect { described_class.new.process({}, raw_mail) }
+ .to change { ticket.reload.state.name }.to('open')
end
end
end
diff --git a/spec/models/ticket_spec.rb b/spec/models/ticket_spec.rb
index 01c5aec57..a009a53ae 100644
--- a/spec/models/ticket_spec.rb
+++ b/spec/models/ticket_spec.rb
@@ -255,11 +255,94 @@ RSpec.describe Ticket, type: :model do
end
end
end
+
+ describe '#subject_build' do
+ context 'with default "ticket_hook_position" setting ("right")' do
+ it 'returns the given string followed by a ticket reference (of the form "[Ticket#123]")' do
+ expect(ticket.subject_build('foo'))
+ .to eq("foo [Ticket##{ticket.number}]")
+ end
+
+ context 'and a non-default value for the "ticket_hook" setting' do
+ before { Setting.set('ticket_hook', 'bar baz') }
+
+ it 'replaces "Ticket#" with the new ticket hook' do
+ expect(ticket.subject_build('foo'))
+ .to eq("foo [bar baz#{ticket.number}]")
+ end
+ end
+
+ context 'and a non-default value for the "ticket_hook_divider" setting' do
+ before { Setting.set('ticket_hook_divider', ': ') }
+
+ it 'inserts the new ticket hook divider between "Ticket#" and the ticket number' do
+ expect(ticket.subject_build('foo'))
+ .to eq("foo [Ticket#: #{ticket.number}]")
+ end
+ end
+
+ context 'when the given string already contains a ticket reference, but in the wrong place' do
+ it 'moves the ticket reference to the end' do
+ expect(ticket.subject_build("[Ticket##{ticket.number}] foo"))
+ .to eq("foo [Ticket##{ticket.number}]")
+ end
+ end
+
+ context 'when the given string already contains an alternately formatted ticket reference' do
+ it 'reformats the ticket reference' do
+ expect(ticket.subject_build("foo [Ticket#: #{ticket.number}]"))
+ .to eq("foo [Ticket##{ticket.number}]")
+ end
+ end
+ end
+
+ context 'with alternate "ticket_hook_position" setting ("left")' do
+ before { Setting.set('ticket_hook_position', 'left') }
+
+ it 'returns a ticket reference (of the form "[Ticket#123]") followed by the given string' do
+ expect(ticket.subject_build('foo'))
+ .to eq("[Ticket##{ticket.number}] foo")
+ end
+
+ context 'and a non-default value for the "ticket_hook" setting' do
+ before { Setting.set('ticket_hook', 'bar baz') }
+
+ it 'replaces "Ticket#" with the new ticket hook' do
+ expect(ticket.subject_build('foo'))
+ .to eq("[bar baz#{ticket.number}] foo")
+ end
+ end
+
+ context 'and a non-default value for the "ticket_hook_divider" setting' do
+ before { Setting.set('ticket_hook_divider', ': ') }
+
+ it 'inserts the new ticket hook divider between "Ticket#" and the ticket number' do
+ expect(ticket.subject_build('foo'))
+ .to eq("[Ticket#: #{ticket.number}] foo")
+ end
+ end
+
+ context 'when the given string already contains a ticket reference, but in the wrong place' do
+ it 'moves the ticket reference to the start' do
+ expect(ticket.subject_build("foo [Ticket##{ticket.number}]"))
+ .to eq("[Ticket##{ticket.number}] foo")
+ end
+ end
+
+ context 'when the given string already contains an alternately formatted ticket reference' do
+ it 'reformats the ticket reference' do
+ expect(ticket.subject_build("[Ticket#: #{ticket.number}] foo"))
+ .to eq("[Ticket##{ticket.number}] foo")
+ end
+ end
+ end
+ end
end
describe 'Attributes:' do
describe '#owner' do
let(:original_owner) { create(:agent_user, groups: [ticket.group]) }
+
before { ticket.update(owner: original_owner) }
context 'when assigned directly' do
diff --git a/test/unit/email_process_follow_up_test.rb b/test/unit/email_process_follow_up_test.rb
deleted file mode 100644
index 03ab989dd..000000000
--- a/test/unit/email_process_follow_up_test.rb
+++ /dev/null
@@ -1,546 +0,0 @@
-require 'test_helper'
-
-class EmailProcessFollowUpTest < ActiveSupport::TestCase
-
- test 'process with follow up check' do
-
- ticket = Ticket.create(
- title: 'follow up 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: 'follow up check',
- message_id: '<20150830145601.30.608882@edenhofer.zammad.com>',
- body: 'some message article',
- internal: false,
- sender: Ticket::Article::Sender.lookup(name: 'Agent'),
- type: Ticket::Article::Type.lookup(name: 'email'),
- updated_by_id: 1,
- created_by_id: 1,
- )
-
- email_raw_string_subject = "From: me@example.com
-To: customer@example.com
-Subject: #{ticket.subject_build('some new subject')}
-
-Some Text"
-
- email_raw_string_other_subject = "From: me@example.com
-To: customer@example.com
-Subject: other subject #{Setting.get('ticket_hook')}#{ticket.number}
-
-Some Text"
-
- email_raw_string_body = "From: me@example.com
-To: customer@example.com
-Subject: no reference
-
-Some Text #{ticket.subject_build('some new subject')} "
-
- email_raw_string_attachment = "From: me@example.com
-Content-Type: multipart/mixed; boundary=\"Apple-Mail=_ED77AC8D-FB6F-40E5-8FBE-D41FF5E1BAF2\"
-Subject: no reference
-Date: Sun, 30 Aug 2015 23:20:54 +0200
-To: Martin Edenhofer
-Mime-Version: 1.0 (Mac OS X Mail 8.2 \(2104\))
-X-Mailer: Apple Mail (2.2104)
-
-
---Apple-Mail=_ED77AC8D-FB6F-40E5-8FBE-D41FF5E1BAF2
-Content-Transfer-Encoding: 7bit
-Content-Type: text/plain;
- charset=us-ascii
-
-no reference
---Apple-Mail=_ED77AC8D-FB6F-40E5-8FBE-D41FF5E1BAF2
-Content-Disposition: attachment;
- filename=test1.txt
-Content-Type: text/plain;
- name=\"test.txt\"
-Content-Transfer-Encoding: 7bit
-
-Some Text #{ticket.subject_build('some new subject')}
-
---Apple-Mail=_ED77AC8D-FB6F-40E5-8FBE-D41FF5E1BAF2--"
-
- email_raw_string_references1 = "From: me@example.com
-To: customer@example.com
-Subject: no reference
-In-Reply-To: <20150830145601.30.608882@edenhofer.zammad.com>
-References:
-
-no reference "
-
- email_raw_string_references2 = "From: me@example.com
-To: customer@example.com
-Subject: no reference
-References: <20150830145601.30.608882@edenhofer.zammad.com>
-
-no reference "
-
- setting_orig = Setting.get('postmaster_follow_up_search_in')
- Setting.set('postmaster_follow_up_search_in', %w[body attachment references])
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_other_subject)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
- assert_equal(ticket.id, ticket_p.id)
-
- Setting.set('postmaster_follow_up_search_in', nil)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_other_subject)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body)
- assert_not_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment)
- assert_not_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1)
- assert_not_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
- assert_not_equal(ticket.id, ticket_p.id)
-
- Setting.set('postmaster_follow_up_search_in', 'references')
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_other_subject)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body)
- assert_not_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment)
- assert_not_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
- assert_equal(ticket.id, ticket_p.id)
-
- Setting.set('postmaster_follow_up_search_in', setting_orig)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_other_subject)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body)
- assert_not_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment)
- assert_not_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1)
- assert_not_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
- assert_not_equal(ticket.id, ticket_p.id)
- travel_back
- end
-
- test 'process with follow up check with different ticket hook' do
-
- Setting.set('ticket_hook', 'VD-Ticket#')
-
- ticket = Ticket.create(
- title: 'follow up check ticket hook',
- 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: 'follow up check',
- message_id: '<20150830145601.30.608882.123123@edenhofer.zammad.com>',
- body: 'some message article',
- internal: false,
- sender: Ticket::Article::Sender.lookup(name: 'Agent'),
- type: Ticket::Article::Type.lookup(name: 'email'),
- updated_by_id: 1,
- created_by_id: 1,
- )
-
- email_raw_string_subject = "From: me@example.com
-To: customer@example.com
-Subject: #{ticket.subject_build('some new subject')}
-
-Some Text"
-
- email_raw_string_other_subject = "From: me@example.com
-To: customer@example.com
-Subject: Aw: RE: other subject [#{Setting.get('ticket_hook')}#{ticket.number}]
-
-Some Text"
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
- assert_equal(ticket.id, ticket_p.id)
-
- travel 1.second
- ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_other_subject)
- assert_equal(ticket.id, ticket_p.id)
-
- travel_back
- end
-
- test 'process with follow up check with two external reference headers' do
-
- Setting.set('postmaster_follow_up_search_in', %w[body attachment references])
-
- data1 = "From: me@example.com
-To: z@example.com
-Subject: test 123
-Message-ID: <9d16181c-2db2-c6c1-ff7f-41f2da4e289a@linuxhotel.de>
-
-test 123
-"
- ticket_p1, article_p1, user_p1 = Channel::EmailParser.new.process({}, data1)
-
- travel 1.second
-
- data1 = "From: me@example.com
-To: z@example.com
-Subject: test 123
-Message-ID: <9d16181c-2db2-c6c1-ff7f-41f2da4e289a@linuxhotel.de>
-
-test 123
-"
- ticket_p2, article_p2, user_p2 = Channel::EmailParser.new.process({}, data1)
- assert_not_equal(ticket_p1.id, ticket_p2.id)
-
- data2 = "From: you@example.com
-To: y@example.com
-Subject: RE: test 123
-Message-ID:
-References: <9d16181c-2db2-c6c1-ff7f-41f2da4e289a@linuxhotel.de>
-
-test 123
-"
- ticket_p3, article_p3, user_p3 = Channel::EmailParser.new.process({}, data2)
-
- assert_equal(ticket_p2.id, ticket_p3.id)
-
- travel_back
- end
-
- test 'process with follow up check - with auto responses and no T# in subject_build' do
-
- ticket = Ticket.create(
- title: 'follow up - with references follow up check',
- 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: 'follow up with references follow up check',
- message_id: '<20151222145601.30.608881@edenhofer.zammad.com>',
- body: 'some message with references follow up 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
-
- # auto response without T# in subject, find follow up by references header
- email_raw_string = "From: bob@example.com
-To: customer@example.com
-Subject: =?ISO-8859-1?Q?AUTO=3A_Bob_Smith_ist_au=DFer_Haus=2E_=2F_is_out_of?=
- =?ISO-8859-1?Q?_office=2E_=28R=FCckkehr_am_28=2E12=2E2015=29?=
-In-Reply-To: <20251222081758.116249.983698@portal.znuny.com>
-References: <20151222145601.30.608881@edenhofer.zammad.com> <20251222081758.116249.983698@portal.znuny.com>
-Message-ID:
-Auto-Submitted: auto-replied
-
-Some Text"
-
- ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
- ticket = Ticket.find(ticket.id)
- assert_equal(ticket.id, ticket_p.id)
- assert_equal('open', ticket.state.name)
- travel_back
- end
-
- test 'process with follow up check - email with more forgein T#\'s in subject' do
-
- ticket = Ticket.create(
- title: 'email with more forgein T#\'s in subject',
- 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: 'follow up with references follow up check',
- message_id: '<20151222145601.30.608881@edenhofer.zammad.com>',
- body: 'some message with references follow up 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
-
- system_id = Setting.get('system_id')
- ticket_hook = Setting.get('ticket_hook')
- ticket_hook_divider = Setting.get('ticket_hook_divider')
-
- tn = "[#{ticket_hook}#{ticket_hook_divider}#{system_id}#{Ticket::Number.generate}99]"
-
- email_raw_string_subject = "From: me@example.com
-To: customer@example.com
-Subject: First foreign Tn #{tn} #{tn} #{tn} - #{ticket.subject_build('some new subject')}
-
-Some Text"
-
- ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string_subject)
- ticket = Ticket.find(ticket.id)
- assert_equal(ticket.id, ticket_p.id)
- assert_equal('open', ticket.state.name)
- travel_back
- end
-
- test 'process with follow up check - ticket initiated by customer without T# in subject and other people in Cc reply to all' do
-
- # check if follow up based on inital system sender address
- Setting.set('postmaster_follow_up_search_in', [])
-
- subject = "ticket initiated by customer without T# in subject and other people in Cc reply to all #{rand(9999)}"
-
- email_raw_string = "From: me@example.com
-To: my@system.test, bob@example.com
-Subject: #{subject}
-Message-ID: <123456789-$follow-up-test§-1@linuxhotel.de>
-
-Some Text"
-
- ticket_p1, article_1, user_1, mail = Channel::EmailParser.new.process({}, email_raw_string)
- ticket1 = Ticket.find(ticket_p1.id)
- assert_equal(subject, ticket1.title)
-
- # follow up possible because same subject
- email_raw_string = "From: bob@example.com
-To: my@system.test, me@example.com
-Subject: AW: #{subject}
-Message-ID: <123456789-$follow-up-test§-2@linuxhotel.de>
-References: <123456789-$follow-up-test§-1@linuxhotel.de>
-
-Some Text"
-
- ticket_p2, article_p2, user_p2, mail = Channel::EmailParser.new.process({}, email_raw_string)
- ticket2 = Ticket.find(ticket_p2.id)
- assert_equal(ticket1.id, ticket2.id)
- assert_equal(subject, ticket2.title)
-
- # follow up possible because same subject
- email_raw_string = "From: bob@example.com
-To: my@system.test, me@example.com
-Subject: AW: RE: #{subject}
-Message-ID: <123456789-$follow-up-test§-2@linuxhotel.de>
-References: <123456789-$follow-up-test§-1@linuxhotel.de>
-
-Some Text"
-
- ticket_p3, article_p3, user_p3, mail = Channel::EmailParser.new.process({}, email_raw_string)
- ticket3 = Ticket.find(ticket_p3.id)
- assert_equal(ticket1.id, ticket3.id)
- assert_equal(subject, ticket3.title)
-
- # follow up not possible because subject has changed
- subject = "new subject without ticket ref #{rand(9_999_999)}"
- email_raw_string = "From: bob@example.com
-To: my@system.test
-Subject: #{subject}
-Message-ID: <123456789-$follow-up-test§-3@linuxhotel.de>
-References: <123456789-$follow-up-test§-1@linuxhotel.de>
-
-Some Text"
-
- ticket_p4, article_p4, user_p4, mail = Channel::EmailParser.new.process({}, email_raw_string)
- ticket4 = Ticket.find(ticket_p4.id)
- assert_not_equal(ticket1.id, ticket4.id)
- assert_equal(subject, ticket4.title)
-
- # usecase with same subject but no Ticket# (reference headers check because of same subject)
- subject = 'Embedded Linux 20.03 - 23.03.17'
-
- email_raw_string = "From: iw@example.com
-To: customer@example.com
-Subject: #{subject}
-Message-ID:
-
-Some Text"
-
- ticket_p5, article_5, user_5, mail = Channel::EmailParser.new.process({}, email_raw_string)
- ticket5 = Ticket.find(ticket_p5.id)
- assert_not_equal(ticket1.id, ticket5.id)
- assert_equal(subject, ticket5.title)
-
- email_raw_string = "From: customer@example.com
-To: iw@example.com
-Subject: Re: #{subject}
-Message-ID:
-In-Reply-To:
-
-Some other Text"
-
- ticket_p6, article_6, user_6, mail = Channel::EmailParser.new.process({}, email_raw_string)
- ticket6 = Ticket.find(ticket_p6.id)
- assert_equal(ticket5.id, ticket6.id)
- assert_equal(subject, ticket6.title)
-
- end
-
- test 'process with follow up check - with none ticket# in subject' do
-
- Setting.set('postmaster_follow_up_search_in', [])
- Setting.set('ticket_hook_position', 'none')
-
- subject = 'some title'
- email_raw_string = "From: me@example.com
-To: bob@example.com
-Subject: #{subject}
-Message-ID: <123456789-follow-up-test-ticket_hook_position-none@linuxhotel.de>
-
-Some Text"
-
- ticket_p1, article_1, user_1, mail = Channel::EmailParser.new.process({}, email_raw_string)
- ticket1 = Ticket.find(ticket_p1.id)
- assert_equal(subject, ticket1.title)
-
- # follow up possible because same subject
- subject = 'new subject lalala'
- email_raw_string = "From: bob@example.com
-To: me@example.com
-Subject: AW: #{subject}
-Message-ID: <123456789-follow-up-test-ticket_hook_position-none-2@linuxhotel.de>
-References: <123456789-follow-up-test-ticket_hook_position-none@linuxhotel.de>
-
-Some Text"
-
- ticket_p2, article_p2, user_p2, mail = Channel::EmailParser.new.process({}, email_raw_string)
- ticket2 = Ticket.find(ticket_p2.id)
- assert_equal(ticket1.id, ticket2.id)
- end
-
- test 'process with follow up check - in body' do
-
- Setting.set('postmaster_follow_up_search_in', %w[body attachment references])
- Setting.set('ticket_hook', '#')
-
- email_raw_string = "From: me@example.com
-To: bob@example.com
-Subject: some subject
-
-Some Text"
-
- ticket_p1, article_1, user_1, mail = Channel::EmailParser.new.process({}, email_raw_string)
-
- email_raw_string = "From: me@example.com
-To: bob@example.com
-Subject: some subject
-
-Some Text #{Setting.get('ticket_hook')}#{ticket_p1.number} asdasd"
-
- ticket_p2, article_2, user_2, mail = Channel::EmailParser.new.process({}, email_raw_string)
- assert_equal(ticket_p1.id, ticket_p2.id)
-
- email_raw_string = "From: me@example.com
-To: bob@example.com
-Subject: some subject
-Content-Transfer-Encoding: 7bit
-Content-Type: text/html;
-
-Some Text #{Setting.get('ticket_hook')}#{ticket_p1.number}
-"
-
- ticket_p3, article_3, user_3, mail = Channel::EmailParser.new.process({}, email_raw_string)
- assert_equal(ticket_p1.id, ticket_p3.id)
-
- email_raw_string = "From: me@example.com
-To: bob@example.com
-Subject: some subject
-Content-Transfer-Encoding: 8bit
-Content-Type: text/html;
-
-Some Text test
-"
-
- ticket_p4, article_4, user_4, mail = Channel::EmailParser.new.process({}, email_raw_string)
- assert_not_equal(ticket_p1.id, ticket_p4.id)
-
- end
-end