error reply for failed incoming email (#10726)

* error reply for failed incoming email

* deprecated date.to_s(:db) replaced with to_fs(:db)

* rubocop concessions

* nest away

* do not report errors to unknown users by default

* use verifying double
pull/10754/head
Markus Kahl 2 years ago committed by GitHub
parent f7a88f7cd9
commit d5f00b5700
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .rubocop.yml
  2. 32
      app/mailers/user_mailer.rb
  3. 40
      app/models/mail_handler.rb
  4. 50
      app/views/user_mailer/incoming_email_error.html.erb
  5. 42
      app/views/user_mailer/incoming_email_error.text.erb
  6. 2
      config/constants/settings/definitions.rb
  7. 4
      config/locales/en.yml
  8. 2
      spec/controllers/activities_controller_spec.rb
  9. 2
      spec/features/work_packages/edit_on_assign_version_permission_spec.rb
  10. 4
      spec/features/work_packages/edit_work_package_spec.rb
  11. 2
      spec/features/work_packages/tabs/activity_notifications_spec.rb
  12. 8
      spec/features/work_packages/tabs/activity_revisions_spec.rb
  13. 6
      spec/features/work_packages/tabs/activity_tab_spec.rb
  14. 2
      spec/features/work_packages/work_package_workflow_form_spec.rb
  15. 51
      spec/mailers/user_mailer_spec.rb
  16. 86
      spec/models/mail_handler_spec.rb
  17. 12
      spec_legacy/fixtures/messages.yml
  18. 2
      spec_legacy/fixtures/versions.yml
  19. 74
      spec_legacy/fixtures/work_packages.yml
  20. 2
      spec_legacy/unit/mail_handler_spec.rb

@ -175,7 +175,7 @@ RSpec/MultipleMemoizedHelpers:
Enabled: false
RSpec/NestedGroups:
Max: 4
Enabled: false
# Don't force the second argument of describe
# to be .class_method or #instance_method

@ -224,8 +224,40 @@ class UserMailer < ApplicationMailer
send_mail(admin, t("mail_user_activation_limit_reached.subject"))
end
##
# E-Mail sent to a user when they tried sending an email to OpenProject to create or update
# a work package, or forum message for instance.
#
# @param [User] user User who sent the email
# @param [Mail] mail Sent email
# @param [Array<String>] List of logs collected during processing of the email
def incoming_email_error(user, mail, logs)
@user = user
@mail = mail
@logs = logs
@received_at = DateTime.now
@incoming_text = incoming_email_text mail
@quote = incoming_email_quote mail
headers['References'] = ["<#{mail.message_id}>"]
headers['In-Reply-To'] = ["<#{mail.message_id}>"]
send_mail user, mail.subject.present? ? "Re: #{mail.subject}" : I18n.t("mail_subject_incoming_email_error")
end
private
def incoming_email_text(mail)
mail.text_part.present? ? mail.text_part.body.to_s : mail.body.to_s
end
def incoming_email_quote(mail)
quote = incoming_email_text(mail)
quoted = String(quote).lines.join("> ")
"> #{quoted}"
end
def open_project_wiki_headers(wiki_content)
open_project_headers 'Project' => wiki_content.project.identifier,
'Wiki-Page-Id' => wiki_content.page.id,

@ -34,7 +34,14 @@ class MailHandler < ActionMailer::Base
class MissingInformation < StandardError; end
attr_reader :email, :sender_email, :user, :options
attr_reader :email, :sender_email, :user, :options, :logs
def initialize
super
@result = false
@logs = []
end
##
# Code copied from base class and extended with optional options parameter
@ -70,7 +77,8 @@ class MailHandler < ActionMailer::Base
@sender_email = email.from.to_a.first.to_s.strip
# Ignore emails received from the application emission address to avoid hell cycles
if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
log "ignoring email from emission address [#{sender_email}]"
log "ignoring email from emission address [#{sender_email}]", report: false
# don't report back errors to ourselves
return false
end
# Ignore auto generated emails
@ -79,7 +87,8 @@ class MailHandler < ActionMailer::Base
if value
value = value.to_s.downcase
if (ignored_value.is_a?(Regexp) && value.match(ignored_value)) || value == ignored_value
log "ignoring email with #{key}:#{value} header"
log "ignoring email with #{key}:#{value} header", report: false
# no point reporting back in case of auto-generated emails
return false
end
end
@ -106,12 +115,14 @@ class MailHandler < ActionMailer::Base
end
else
# Default behaviour, emails from unknown users are ignored
log "ignoring email from unknown user [#{sender_email}]"
log "ignoring email from unknown user [#{sender_email}]", report: false
return false
end
end
User.current = @user
dispatch
ensure
report_errors if !@result && Setting.report_incoming_email_errors?
end
def options=(value)
@ -151,14 +162,11 @@ class MailHandler < ActionMailer::Base
# Relying on the subject of the mail, which had been implemented before, is brittle as it relies on the user not altering
# the subject. Additionally, the subject structure might change, e.g. via localization changes.
def dispatch
if (m, object_id = dispatch_target_from_header)
m.call(object_id)
else
dispatch_to_default
end
m, object_id = dispatch_target_from_header
@result = m ? m.call(object_id) : dispatch_to_default
rescue ActiveRecord::RecordInvalid => e
# TODO: send a email to the user
logger&.error e.message
log "could not save record: #{e.message}", :error
false
rescue MissingInformation => e
log "missing information from #{user}: #{e.message}", :error
@ -597,11 +605,19 @@ class MailHandler < ActionMailer::Base
get_keyword(:done_ratio, override: true, format: '(\d|10)?0')
end
def log(message, level = :info)
def log(message, level = :info, report: true)
logs << "#{level}: #{message}" if report
message = "MailHandler: #{message}"
logger.public_send(level, message)
end
def report_errors
return if logs.empty?
UserMailer.incoming_email_error(user, email, logs).deliver_later
end
def work_package_create_contract_class
if options[:no_permission_check]
CreateWorkPackageWithoutAuthorizationsContract

@ -0,0 +1,50 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2022 the OpenProject GmbH
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details.
++#%>
<h2><%= t(:mail_body_incoming_email_error) %></h2>
<p>
<%= t(:mail_body_incoming_email_error_logs) %>:
</p>
<code>
<%= @logs.join("\n") %>
</code>
<p>
<%= t(
:mail_body_incoming_email_error_in_reply_to,
received_at: format_time(@received_at),
from_email: @mail.from.first
)%>:
</p>
<blockquote>
<%= @incoming_text %>
</blockquote>

@ -0,0 +1,42 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2022 the OpenProject GmbH
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details.
++#%>
<%= t(:mail_body_incoming_email_error) %>
<%= t(:mail_body_incoming_email_error_logs) %>:
<%= @logs.join("\n") %>
<%= t(
:mail_body_incoming_email_error_in_reply_to,
received_at: format_time(@received_at),
from_email: @mail.from.first
)%>:
<%= @quote %>

@ -683,6 +683,8 @@ Settings::Definition.define do
},
writable: false
add :report_incoming_email_errors, default: true
add :repositories_automatic_managed_vendor,
default: nil,
format: :string,

@ -2074,6 +2074,9 @@ en:
mail_body_backup_token_info: The previous token is no longer valid.
mail_body_backup_waiting_period: The new token will be enabled in %{hours} hours.
mail_body_backup_token_warning: If this wasn't you, login to OpenProject immediately and reset it again.
mail_body_incoming_email_error: The email you sent to OpenProject could not be processed.
mail_body_incoming_email_error_in_reply_to: "At %{received_at} %{from_email} wrote"
mail_body_incoming_email_error_logs: "Logs"
mail_body_lost_password: "To change your password, click on the following link:"
mail_body_register: "Welcome to %{app_title}. Please activate your account by clicking on this link:"
mail_body_register_header_title: "Project member invitation email"
@ -2092,6 +2095,7 @@ en:
mail_subject_account_activation_request: "%{value} account activation request"
mail_subject_backup_ready: "Your backup is ready"
mail_subject_backup_token_reset: "Backup token reset"
mail_subject_incoming_email_error: "An email you sent to OpenProject could not be processed"
mail_subject_lost_password: "Your %{value} password"
mail_subject_register: "Your %{value} account activation"
mail_subject_reminder: "%{count} work package(s) due in the next %{days} days"

@ -50,7 +50,7 @@ describe ActivitiesController, type: :controller do
let!(:journal) do
create(:work_package_journal,
journable_id: work_package.id,
created_at: 3.days.ago.to_date.to_s(:db),
created_at: 3.days.ago.to_date.to_fs(:db),
version: Journal.maximum(:version) + 1,
data: build(:journal_work_package_journal,
subject: work_package.subject,

@ -22,7 +22,7 @@ describe 'edit work package', js: true do
author: current_user,
project: project,
type: type,
created_at: 5.days.ago.to_date.to_s(:db))
created_at: 5.days.ago.to_date.to_fs(:db))
end
let(:status) { work_package.status }

@ -53,10 +53,10 @@ describe 'edit work package', js: true do
author: dev,
project: project,
type: type,
created_at: 5.days.ago.to_date.to_s(:db))
created_at: 5.days.ago.to_date.to_fs(:db))
note_journal = work_package.journals.last
note_journal.update_column(:created_at, 5.days.ago.to_date.to_s(:db))
note_journal.update_column(:created_at, 5.days.ago.to_date.to_fs(:db))
work_package
end

@ -8,7 +8,7 @@ describe 'Activity tab notifications', js: true, selenium: true do
shared_let(:work_package) do
work_package = create(:work_package,
project: project,
created_at: 5.days.ago.to_date.to_s(:db))
created_at: 5.days.ago.to_date.to_fs(:db))
work_package.update({ journal_notes: 'First comment on this wp.',
updated_at: 5.days.ago.to_date.to_s })

@ -15,7 +15,7 @@ describe 'Activity tab', js: true, selenium: true do
let!(:work_package) do
work_package = create(:work_package,
project: project,
created_at: 5.days.ago.to_date.to_s(:db),
created_at: 5.days.ago.to_date.to_fs(:db),
subject: initial_subject,
journal_notes: initial_comment)
@ -39,7 +39,7 @@ describe 'Activity tab', js: true, selenium: true do
alter_work_package_at(work_package,
attributes: attributes,
at: 3.days.ago.to_date.to_s(:db),
at: 3.days.ago.to_date.to_fs(:db),
user: user)
work_package.journals.last
@ -50,7 +50,7 @@ describe 'Activity tab', js: true, selenium: true do
alter_work_package_at(work_package,
attributes: attributes,
at: 1.days.ago.to_date.to_s(:db),
at: 1.day.ago.to_date.to_fs(:db),
user: create(:admin))
work_package.journals.last
@ -66,7 +66,7 @@ describe 'Activity tab', js: true, selenium: true do
changeset = build(:changeset,
comments: 'A comment on a changeset',
committed_on: 2.days.ago.to_date.to_s(:db),
committed_on: 2.days.ago.to_date.to_fs(:db),
repository: repo,
committer: 'cool@person.org')

@ -43,7 +43,7 @@ describe 'Activity tab', js: true, selenium: true do
let!(:work_package) do
work_package = create(:work_package,
project: project,
created_at: 5.days.ago.to_date.to_s(:db),
created_at: 5.days.ago.to_date.to_fs(:db),
subject: initial_subject,
journal_notes: initial_comment)
@ -67,7 +67,7 @@ describe 'Activity tab', js: true, selenium: true do
alter_work_package_at(work_package,
attributes: attributes,
at: 3.days.ago.to_date.to_s(:db),
at: 3.days.ago.to_date.to_fs(:db),
user: user)
work_package.journals.last
@ -78,7 +78,7 @@ describe 'Activity tab', js: true, selenium: true do
alter_work_package_at(work_package,
attributes: attributes,
at: 1.day.ago.to_date.to_s(:db),
at: 1.day.ago.to_date.to_fs(:db),
user: create(:admin))
work_package.journals.last

@ -50,7 +50,7 @@ describe 'Work package transitive status workflows', js: true do
work_package = create :work_package,
project: project,
type: type,
created_at: 5.days.ago.to_date.to_s(:db)
created_at: 5.days.ago.to_date.to_fs(:db)
note_journal = work_package.journals.last
note_journal.update(created_at: 5.days.ago.to_date.to_s)

@ -230,6 +230,57 @@ describe UserMailer, type: :mailer do
end
end
describe '#incoming_email_error' do
let(:logs) { ['info: foo', 'error: bar'] }
let(:recipient) { user }
let(:current_time) { "2022-11-03 9:15".to_time }
let(:incoming_email) do
Mail.new(subject: mail_subject, message_id:, body:, from:)
end
let(:mail_subject) { 'New work package 42' }
let(:message_id) { '<000501c8d452$a95cd7e0$0a00a8c0@osiris>' }
let(:from) { 'l.lustig@openproject.com' }
let(:body) { "Project: demo-project" }
let(:outgoing_email) { deliveries.first }
before do
described_class
.incoming_email_error(user, incoming_email, logs)
.deliver_now
end
it_behaves_like 'mail is sent' do
it "references the incoming email's subject in its own" do
expect(outgoing_email.subject).to eql "Re: #{mail_subject}"
end
it "it's a reply to the incoming email" do
expect(message_id).to include outgoing_email.in_reply_to
expect(message_id).to include outgoing_email.references
end
it "contains the incoming email's quoted content" do
expect(html_body).to include body
end
it 'contains the date the mail was received' do
expect(html_body).to include "11/03/2022 09:15 AM"
end
it 'contains the email address from which the email was sent' do
expect(html_body).to include from
end
it 'contains the logs' do
logs.each do |log|
expect(html_body).to include log
end
end
end
end
describe '#news_added' do
let(:news) { build_stubbed(:news) }

@ -39,6 +39,10 @@ describe MailHandler, type: :model do
# there is a default work package priority to save any work packages
priority_low
anno_user
allow(UserMailer)
.to receive(:incoming_email_error)
.and_return instance_double(ActionMailer::MessageDelivery, deliver_later: nil)
end
after do
@ -338,6 +342,12 @@ describe MailHandler, type: :model do
end
end.to change(Notification.where(recipient: user), :count).by(1)
end
it 'does not send an error reply email' do
subject # send mail
expect(UserMailer).not_to have_received(:incoming_email_error)
end
end
context 'in given project with a default type' do
@ -387,28 +397,74 @@ describe MailHandler, type: :model do
end.to change(User, :count).by(1)
end
it 'rejects if unknown_user=accept and permission check is present' do
expected =
context 'with unknown_user=default' do
let(:results) { [] }
before do
results << submit_email(
'ticket_by_unknown_user.eml',
issue: { project: project.identifier },
unknown_user: nil
)
end
it "ignores the email" do
expect(results).to eq [false]
end
it "does not respond with an error email" do
expect(UserMailer).not_to have_received(:incoming_email_error)
end
end
context 'with unknown_user=accept and permision check present' do
let(:expected) do
'MailHandler: work_package could not be created by Anonymous due to ' \
'#["may not be accessed.", ' \
'"Type was attempted to be written but is not writable.", ' \
'"Project was attempted to be written but is not writable.", ' \
'"Subject was attempted to be written but is not writable.", ' \
'"Description was attempted to be written but is not writable."]'
end
allow(Rails.logger)
.to receive(:error)
.with(expected)
let(:results) { [] }
result = submit_email 'ticket_by_unknown_user.eml',
issue: { project: project.identifier },
unknown_user: 'accept'
before do
allow(Rails.logger).to receive(:error).with(expected)
results << submit_email(
'ticket_by_unknown_user.eml',
issue: { project: project.identifier },
unknown_user: 'accept'
)
end
expect(result).to eq false
it 'rejects the email' do
expect(results).to eq [false]
end
it 'logs the error' do
expect(Rails.logger).to have_received(:error).with(expected)
end
expect(Rails.logger)
.to have_received(:error)
.with(expected)
context 'with report_incoming_email_errors true (default)' do
it 'responds with an error email' do
expect(UserMailer).to have_received(:incoming_email_error) do |user, mail, logs|
expect(user).to eq anno_user
expect(mail.subject).to eq "Ticket by unknown user"
expect(logs).to eq [expected.sub(/^MailHandler/, "error")]
end
end
end
context(
'with report_incoming_email_errors false',
with_settings: { report_incoming_email_errors: false }
) do
it 'does not respond with an error email' do
expect(UserMailer).not_to have_received(:incoming_email_error)
end
end
end
it 'accepts if unknown_user=accept and no_permission_check' do
@ -446,6 +502,12 @@ describe MailHandler, type: :model do
expect { subject }
.not_to(change { WorkPackage.count })
end
it 'does not result in an error email response' do
subject # send email
expect(UserMailer).not_to have_received(:incoming_email_error)
end
end
context 'wp with status' do

@ -72,8 +72,8 @@ messages_004:
parent_id:
forum_id: 1
messages_005:
created_at: <%= 3.days.ago.to_date.to_s(:db) %>
updated_at: <%= 3.days.ago.to_date.to_s(:db) %>
created_at: <%= 3.days.ago.to_date.to_fs(:db) %>
updated_at: <%= 3.days.ago.to_date.to_fs(:db) %>
subject: 'RE: post 2'
id: 5
replies_count: 0
@ -83,8 +83,8 @@ messages_005:
parent_id: 4
forum_id: 1
messages_006:
created_at: <%= 2.days.ago.to_date.to_s(:db) %>
updated_at: <%= 2.days.ago.to_date.to_s(:db) %>
created_at: <%= 2.days.ago.to_date.to_fs(:db) %>
updated_at: <%= 2.days.ago.to_date.to_fs(:db) %>
subject: 'RE: post 2'
id: 6
replies_count: 0
@ -94,8 +94,8 @@ messages_006:
parent_id: 4
forum_id: 1
messages_007:
created_at: <%= 2.days.ago.to_date.to_s(:db) %>
updated_at: <%= 2.days.ago.to_date.to_s(:db) %>
created_at: <%= 2.days.ago.to_date.to_fs(:db) %>
updated_at: <%= 2.days.ago.to_date.to_fs(:db) %>
subject: 'Message on a private project'
id: 7
replies_count: 0

@ -44,7 +44,7 @@ versions_002:
updated_at: 2006-07-19 21:00:33 +02:00
id: 2
description: Stable release
effective_date: <%= 20.day.from_now.to_date.to_s(:db) %>
effective_date: <%= 20.day.from_now.to_date.to_fs(:db) %>
status: locked
sharing: 'none'
versions_003:

@ -28,9 +28,9 @@
---
issues_001:
created_at: <%= 3.days.ago.to_date.to_s(:db) %>
created_at: <%= 3.days.ago.to_date.to_fs(:db) %>
project_id: 1
updated_at: <%= 1.day.ago.to_date.to_s(:db) %>
updated_at: <%= 1.day.ago.to_date.to_fs(:db) %>
priority_id: 4
subject: Can't print recipes
id: 1
@ -41,8 +41,8 @@ issues_001:
assigned_to_id:
author_id: 2
status_id: 1
start_date: <%= 1.day.ago.to_date.to_s(:db) %>
due_date: <%= 10.day.from_now.to_date.to_s(:db) %>
start_date: <%= 1.day.ago.to_date.to_fs(:db) %>
due_date: <%= 10.day.from_now.to_date.to_fs(:db) %>
lock_version: 3
issues_002:
created_at: 2006-07-19 21:04:21 +02:00
@ -58,7 +58,7 @@ issues_002:
assigned_to_id: 3
author_id: 2
status_id: 2
start_date: <%= 2.day.ago.to_date.to_s(:db) %>
start_date: <%= 2.day.ago.to_date.to_fs(:db) %>
due_date:
lock_version: 3
done_ratio: 30
@ -76,12 +76,12 @@ issues_003:
assigned_to_id: 3
author_id: 2
status_id: 1
start_date: <%= 15.day.ago.to_date.to_s(:db) %>
due_date: <%= 5.day.ago.to_date.to_s(:db) %>
start_date: <%= 15.day.ago.to_date.to_fs(:db) %>
due_date: <%= 5.day.ago.to_date.to_fs(:db) %>
issues_004:
created_at: <%= 5.days.ago.to_date.to_s(:db) %>
created_at: <%= 5.days.ago.to_date.to_fs(:db) %>
project_id: 2
updated_at: <%= 2.days.ago.to_date.to_s(:db) %>
updated_at: <%= 2.days.ago.to_date.to_fs(:db) %>
priority_id: 4
subject: Issue on project 2
id: 4
@ -93,9 +93,9 @@ issues_004:
author_id: 2
status_id: 1
issues_005:
created_at: <%= 5.days.ago.to_date.to_s(:db) %>
created_at: <%= 5.days.ago.to_date.to_fs(:db) %>
project_id: 3
updated_at: <%= 2.days.ago.to_date.to_s(:db) %>
updated_at: <%= 2.days.ago.to_date.to_fs(:db) %>
priority_id: 4
subject: Subproject issue
id: 5
@ -107,9 +107,9 @@ issues_005:
author_id: 2
status_id: 1
issues_006:
created_at: <%= 1.minute.ago.to_date.to_s(:db) %>
created_at: <%= 1.minute.ago.to_date.to_fs(:db) %>
project_id: 5
updated_at: <%= 1.minute.ago.to_date.to_s(:db) %>
updated_at: <%= 1.minute.ago.to_date.to_fs(:db) %>
priority_id: 4
subject: Issue of a private subproject
id: 6
@ -120,12 +120,12 @@ issues_006:
assigned_to_id:
author_id: 2
status_id: 1
start_date: <%= Date.today.to_s(:db) %>
due_date: <%= 1.days.from_now.to_date.to_s(:db) %>
start_date: <%= Date.today.to_fs(:db) %>
due_date: <%= 1.days.from_now.to_date.to_fs(:db) %>
issues_007:
created_at: <%= 10.days.ago.to_date.to_s(:db) %>
created_at: <%= 10.days.ago.to_date.to_fs(:db) %>
project_id: 1
updated_at: <%= 10.days.ago.to_date.to_s(:db) %>
updated_at: <%= 10.days.ago.to_date.to_fs(:db) %>
priority_id: 5
subject: Issue due today
id: 7
@ -136,12 +136,12 @@ issues_007:
assigned_to_id:
author_id: 2
status_id: 1
start_date: <%= 10.days.ago.to_s(:db) %>
due_date: <%= Date.today.to_s(:db) %>
start_date: <%= 10.days.ago.to_fs(:db) %>
due_date: <%= Date.today.to_fs(:db) %>
issues_008:
created_at: <%= 10.days.ago.to_date.to_s(:db) %>
created_at: <%= 10.days.ago.to_date.to_fs(:db) %>
project_id: 1
updated_at: <%= 10.days.ago.to_date.to_s(:db) %>
updated_at: <%= 10.days.ago.to_date.to_fs(:db) %>
priority_id: 5
subject: Closed issue
id: 8
@ -155,9 +155,9 @@ issues_008:
start_date:
due_date:
issues_009:
created_at: <%= 1.minute.ago.to_date.to_s(:db) %>
created_at: <%= 1.minute.ago.to_date.to_fs(:db) %>
project_id: 5
updated_at: <%= 1.minute.ago.to_date.to_s(:db) %>
updated_at: <%= 1.minute.ago.to_date.to_fs(:db) %>
priority_id: 5
subject: Blocked Issue
id: 9
@ -168,12 +168,12 @@ issues_009:
assigned_to_id:
author_id: 2
status_id: 1
start_date: <%= Date.today.to_s(:db) %>
due_date: <%= 1.days.from_now.to_date.to_s(:db) %>
start_date: <%= Date.today.to_fs(:db) %>
due_date: <%= 1.days.from_now.to_date.to_fs(:db) %>
issues_010:
created_at: <%= 1.minute.ago.to_date.to_s(:db) %>
created_at: <%= 1.minute.ago.to_date.to_fs(:db) %>
project_id: 5
updated_at: <%= 1.minute.ago.to_date.to_s(:db) %>
updated_at: <%= 1.minute.ago.to_date.to_fs(:db) %>
priority_id: 5
subject: Issue Doing the Blocking
id: 10
@ -184,12 +184,12 @@ issues_010:
assigned_to_id:
author_id: 2
status_id: 1
start_date: <%= Date.today.to_s(:db) %>
due_date: <%= 1.days.from_now.to_date.to_s(:db) %>
start_date: <%= Date.today.to_fs(:db) %>
due_date: <%= 1.days.from_now.to_date.to_fs(:db) %>
issues_011:
created_at: <%= 3.days.ago.to_date.to_s(:db) %>
created_at: <%= 3.days.ago.to_date.to_fs(:db) %>
project_id: 1
updated_at: <%= 1.day.ago.to_date.to_s(:db) %>
updated_at: <%= 1.day.ago.to_date.to_fs(:db) %>
priority_id: 5
subject: Closed issue on a closed version
id: 11
@ -200,12 +200,12 @@ issues_011:
assigned_to_id:
author_id: 2
status_id: 5
start_date: <%= 1.day.ago.to_date.to_s(:db) %>
start_date: <%= 1.day.ago.to_date.to_fs(:db) %>
due_date:
issues_012:
created_at: <%= 3.days.ago.to_date.to_s(:db) %>
created_at: <%= 3.days.ago.to_date.to_fs(:db) %>
project_id: 1
updated_at: <%= 1.day.ago.to_date.to_s(:db) %>
updated_at: <%= 1.day.ago.to_date.to_fs(:db) %>
priority_id: 5
subject: Closed issue on a locked version
id: 12
@ -216,12 +216,12 @@ issues_012:
assigned_to_id:
author_id: 3
status_id: 5
start_date: <%= 1.day.ago.to_date.to_s(:db) %>
start_date: <%= 1.day.ago.to_date.to_fs(:db) %>
due_date:
issues_013:
created_at: <%= 5.days.ago.to_date.to_s(:db) %>
created_at: <%= 5.days.ago.to_date.to_fs(:db) %>
project_id: 3
updated_at: <%= 2.days.ago.to_date.to_s(:db) %>
updated_at: <%= 2.days.ago.to_date.to_fs(:db) %>
priority_id: 4
subject: Subproject issue two
id: 13

@ -27,7 +27,7 @@
#++
require_relative '../legacy_spec_helper'
describe MailHandler, type: :model do
describe MailHandler, type: :model, with_settings: { report_incoming_email_errors: false } do
fixtures :all
FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'

Loading…
Cancel
Save