zammad/spec/models/failed_email_spec.rb
2026-01-02 15:41:09 +02:00

214 lines
5.8 KiB
Ruby

# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
require 'rails_helper'
RSpec.describe FailedEmail, :aggregate_failures, type: :model do
subject(:instance) { create(:failed_email) }
describe '#parsing_error' do
it 'sets parsing error' do
instance.parsing_error = 'text'
expect(instance).to have_attributes(parsing_error: 'text')
end
it 'sets parsing error off error' do
instance.parsing_error = StandardError.new('Sample error')
expect(instance).to have_attributes(parsing_error: include('Sample error'))
end
end
describe '.by_filepath' do
let!(:failed_email) { create(:failed_email) }
it 'finds the email by filename' do
expect(described_class.by_filepath("some/folder/#{failed_email.id}.eml")).to eq(failed_email)
end
it 'finds the email by id' do
expect(described_class.by_filepath(failed_email.id.to_s)).to eq(failed_email)
end
it 'does not find with another extension' do
expect(described_class.by_filepath("some/folder/#{failed_email.id}.yml")).to be_nil
end
it 'does not find if not existant' do
expect(described_class.by_filepath('1337.eml')).to be_nil
end
end
describe '#reprocess' do
context 'when it succeeds' do
it 'destroys entry' do
instance.reprocess
expect(instance).to be_destroyed
end
it 'creates a ticket' do
ticket = instance.reprocess
expect(ticket.articles.first).to have_attributes(
body: 'Some Text'
)
end
end
context 'when it succeeds and an ignore postmaster filter exists' do
let!(:failed_email) { create(:failed_email, :invalid) }
# init postmaster filter
before do
failed_email.data = "From: ME Bob <me@example.com>\nTo: customer@example.com\nSubject: some subject\n\nSome Text"
failed_email.save!
create(:postmaster_filter,
match: {
'subject' => {
'operator' => 'contains',
'value' => 'subject',
},
},
perform: {
'x-zammad-ignore' => { 'value' => 'true' },
},)
end
it 'destroys entry' do
failed_email.reprocess
expect(failed_email).to be_destroyed
end
it 'does not create a ticket' do
expect { failed_email.reprocess }.not_to change(Ticket, :count)
end
it 'successfully processed by postmaster filter' do
expect(failed_email.reprocess).to eq({})
end
end
context 'when it fails' do
before do
allow_any_instance_of(Channel::EmailParser)
.to receive(:process_with_timeout)
.and_return([])
end
it 'increases retries count on failure' do
expect { instance.reprocess }
.to change(instance, :retries).by(1)
end
it 'does not create a ticket' do
expect { instance.reprocess }.not_to change(Ticket, :count)
end
end
end
describe '.reprocess_all' do
let!(:failed_email) { create(:failed_email, :invalid) }
let!(:failed_but_correct_email) { create(:failed_email) }
before do
failed_email
failed_but_correct_email
end
it 'creates one ticket for the parseable mail and keeps the other' do
expect { described_class.reprocess_all }
.to change(Ticket, :count).by(1)
.and(change(described_class, :count).by(-1))
end
it 'returns a list of processed email files' do
expect(described_class.reprocess_all).to eq(["#{failed_but_correct_email.id}.eml"])
end
end
describe '.export_all' do
it 'calls export with all records' do
instance
allow_any_instance_of(described_class)
.to receive(:export)
.with('path')
.and_return('path/file.eml')
expect(described_class.export_all('path'))
.to contain_exactly('path/file.eml')
end
end
describe '#export' do
it 'creates a file' do
path = instance.export
expect(File.binread(path)).to eq(instance.data)
end
end
describe '.import_all' do
it 'calls import with all files' do
path = described_class.generate_path
instance.export(path)
allow(described_class)
.to receive(:import)
.with(path.join("#{instance.id}.eml"))
.and_return('imported_path')
expect(described_class.import_all(path))
.to contain_exactly('imported_path')
end
end
describe '.import' do
let(:path) { described_class.generate_path }
let!(:file_path) { instance.export(path) }
let(:sample_text) { "#{instance.data}\n" }
let(:import_result) { described_class.import(path.join("#{instance.id}.eml")) }
after do
file_path.unlink if file_path.exist?
end
context 'with unprocessable content' do
subject(:instance) { create(:failed_email, data: Faker::Lorem.sentence) }
it 'fails on reimporting' do
expect(import_result).to be_nil
expect(instance.reload.retries).to eq(2)
end
it 'keeps the file' do
expect(file_path).to exist
end
context 'with changed valid file content' do
before { File.binwrite(file_path, create(:failed_email).data) }
it 'reimports correctly' do
expect { import_result }.to change(Ticket, :count).by(1)
expect(import_result).to eq(file_path)
expect(file_path).not_to exist
end
end
context 'with changed invalid file content' do
before { File.binwrite(file_path, Faker::Lorem.sentence) }
it 'fails to import again' do
expect(import_result).to be_nil
expect(instance.reload.retries).to eq(2)
end
end
end
it 'returns nil if database row does not exist' do
expect(described_class.import(Pathname.new('tmp/1337.eml'))).to be_nil
end
end
end