failed_email_spec.rb 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe FailedEmail, :aggregate_failures, type: :model do
  4. subject(:instance) { create(:failed_email) }
  5. describe '#parsing_error' do
  6. it 'sets parsing error' do
  7. instance.parsing_error = 'text'
  8. expect(instance).to have_attributes(parsing_error: 'text')
  9. end
  10. it 'sets parsing error off error' do
  11. instance.parsing_error = StandardError.new('Sample error')
  12. expect(instance).to have_attributes(parsing_error: include('Sample error'))
  13. end
  14. end
  15. describe '.by_filepath' do
  16. let!(:failed_email) { create(:failed_email) }
  17. it 'finds the email by filename' do
  18. expect(described_class.by_filepath("some/folder/#{failed_email.id}.eml")).to eq(failed_email)
  19. end
  20. it 'finds the email by id' do
  21. expect(described_class.by_filepath(failed_email.id.to_s)).to eq(failed_email)
  22. end
  23. it 'does not find with another extension' do
  24. expect(described_class.by_filepath("some/folder/#{failed_email.id}.yml")).to be_nil
  25. end
  26. it 'does not find if not existant' do
  27. expect(described_class.by_filepath('1337.eml')).to be_nil
  28. end
  29. end
  30. describe '#reprocess' do
  31. context 'when it succeeds' do
  32. it 'destroys entry' do
  33. instance.reprocess
  34. expect(instance).to be_destroyed
  35. end
  36. it 'creates a ticket' do
  37. ticket = instance.reprocess
  38. expect(ticket.articles.first).to have_attributes(
  39. body: 'Some Text'
  40. )
  41. end
  42. end
  43. context 'when it succeeds and an ignore postmaster filter exists' do
  44. let!(:failed_email) { create(:failed_email, :invalid) }
  45. # init postmaster filter
  46. before do
  47. failed_email.data = "From: ME Bob <me@example.com>\nTo: customer@example.com\nSubject: some subject\n\nSome Text"
  48. failed_email.save!
  49. create(:postmaster_filter,
  50. match: {
  51. 'subject' => {
  52. 'operator' => 'contains',
  53. 'value' => 'subject',
  54. },
  55. },
  56. perform: {
  57. 'x-zammad-ignore' => { 'value' => 'true' },
  58. },)
  59. end
  60. it 'destroys entry' do
  61. failed_email.reprocess
  62. expect(failed_email).to be_destroyed
  63. end
  64. it 'does not create a ticket' do
  65. expect { failed_email.reprocess }.not_to change(Ticket, :count)
  66. end
  67. it 'successfully processed by postmaster filter' do
  68. expect(failed_email.reprocess).to eq({})
  69. end
  70. end
  71. context 'when it fails' do
  72. before do
  73. allow_any_instance_of(Channel::EmailParser)
  74. .to receive(:process_with_timeout)
  75. .and_return([])
  76. end
  77. it 'increases retries count on failure' do
  78. expect { instance.reprocess }
  79. .to change(instance, :retries).by(1)
  80. end
  81. it 'does not create a ticket' do
  82. expect { instance.reprocess }.not_to change(Ticket, :count)
  83. end
  84. end
  85. end
  86. describe '.reprocess_all' do
  87. let!(:failed_email) { create(:failed_email, :invalid) }
  88. let!(:failed_but_correct_email) { create(:failed_email) }
  89. before do
  90. failed_email
  91. failed_but_correct_email
  92. end
  93. it 'creates one ticket for the parseable mail and keeps the other' do
  94. expect { described_class.reprocess_all }
  95. .to change(Ticket, :count).by(1)
  96. .and(change(described_class, :count).by(-1))
  97. end
  98. it 'returns a list of processed email files' do
  99. expect(described_class.reprocess_all).to eq(["#{failed_but_correct_email.id}.eml"])
  100. end
  101. end
  102. describe '.export_all' do
  103. it 'calls export with all records' do
  104. instance
  105. allow_any_instance_of(described_class)
  106. .to receive(:export)
  107. .with('path')
  108. .and_return('path/file.eml')
  109. expect(described_class.export_all('path'))
  110. .to contain_exactly('path/file.eml')
  111. end
  112. end
  113. describe '#export' do
  114. it 'creates a file' do
  115. path = instance.export
  116. expect(File.binread(path)).to eq(instance.data)
  117. end
  118. end
  119. describe '.import_all' do
  120. it 'calls import with all files' do
  121. path = described_class.generate_path
  122. instance.export(path)
  123. allow(described_class)
  124. .to receive(:import)
  125. .with(path.join("#{instance.id}.eml"))
  126. .and_return('imported_path')
  127. expect(described_class.import_all(path))
  128. .to contain_exactly('imported_path')
  129. end
  130. end
  131. describe '.import' do
  132. let(:path) { described_class.generate_path }
  133. let!(:file_path) { instance.export(path) }
  134. let(:sample_text) { "#{instance.data}\n" }
  135. let(:import_result) { described_class.import(path.join("#{instance.id}.eml")) }
  136. after do
  137. file_path.unlink if file_path.exist?
  138. end
  139. context 'with unprocessable content' do
  140. subject(:instance) { create(:failed_email, data: Faker::Lorem.sentence) }
  141. it 'fails on reimporting' do
  142. expect(import_result).to be_nil
  143. expect(instance.reload.retries).to eq(2)
  144. end
  145. it 'keeps the file' do
  146. expect(file_path).to exist
  147. end
  148. context 'with changed valid file content' do
  149. before { File.binwrite(file_path, create(:failed_email).data) }
  150. it 'reimports correctly' do
  151. expect { import_result }.to change(Ticket, :count).by(1)
  152. expect(import_result).to eq(file_path)
  153. expect(file_path).not_to exist
  154. end
  155. end
  156. context 'with changed invalid file content' do
  157. before { File.binwrite(file_path, Faker::Lorem.sentence) }
  158. it 'fails to import again' do
  159. expect(import_result).to be_nil
  160. expect(instance.reload.retries).to eq(2)
  161. end
  162. end
  163. end
  164. it 'returns nil if database row does not exist' do
  165. expect(described_class.import(Pathname.new('tmp/1337.eml'))).to be_nil
  166. end
  167. end
  168. end