From 6546f51acf6d5c8dbf8914ee63c8f23389d59f64 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 10 Jun 2015 11:08:33 +0300 Subject: [PATCH 1/9] Set validity, outzone, delete dates on domain creation #2622 --- app/models/domain.rb | 15 +++++++++++++++ config/initializers/initial_settings.rb | 2 ++ spec/models/domain_spec.rb | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/app/models/domain.rb b/app/models/domain.rb index 469847203..1b0620262 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -152,6 +152,12 @@ class Domain < ActiveRecord::Base x.domain_statuses.create(value: DomainStatus::EXPIRED) if x.expirable? end end + + def start_redemption_grace_period + Domain.where('outzone_at <= ?', Time.zone.now).each do |x| + x.domain_statuses.create(value: DomainStatus::SERVER_HOLD) if x.server_holdable? + end + end end def name=(value) @@ -189,6 +195,13 @@ class Domain < ActiveRecord::Base domain_statuses.where(value: DomainStatus::EXPIRED).empty? end + def server_holdable? + return false if outzone_at > Time.zone.now + return false if domain_statuses.where(value: DomainStatus::SERVER_HOLD).any? + return false if domain_statuses.where(value: DomainStatus::SERVER_MANUAL_INZONE).any? + true + end + def pending_update? (domain_statuses.pluck(:value) & %W( #{DomainStatus::PENDING_UPDATE} @@ -329,6 +342,8 @@ class Domain < ActiveRecord::Base self.registered_at = Time.zone.now self.valid_from = Time.zone.now.to_date self.valid_to = valid_from + self.class.convert_period_to_time(period, period_unit) + self.outzone_at = self.valid_to + Setting.expire_warning_period.days + self.delete_at = self.outzone_at + Setting.redemption_grace_period.days end def manage_automatic_statuses diff --git a/config/initializers/initial_settings.rb b/config/initializers/initial_settings.rb index 018e4be22..824cf0fcc 100644 --- a/config/initializers/initial_settings.rb +++ b/config/initializers/initial_settings.rb @@ -32,6 +32,8 @@ if con.present? && con.table_exists?('settings') Setting.save_default(:invoice_number_max, '149999') Setting.save_default(:days_to_keep_overdue_invoices_active, 30) Setting.save_default(:days_to_renew_domain_before_expire, 90) + Setting.save_default(:expire_warning_period, 15) + Setting.save_default(:redemption_grace_period, 30) end # dev only setting diff --git a/spec/models/domain_spec.rb b/spec/models/domain_spec.rb index 6bf183f9f..e8bb34742 100644 --- a/spec/models/domain_spec.rb +++ b/spec/models/domain_spec.rb @@ -55,6 +55,13 @@ describe Domain do @domain.errors.full_messages.should match_array([]) end + it 'should have correct validity dates' do + valid_to = Time.zone.now.beginning_of_day + 1.year + @domain.valid_to.should == valid_to + @domain.outzone_at.should == valid_to + Setting.expire_warning_period.days + @domain.delete_at.should == valid_to + Setting.expire_warning_period.days + Setting.redemption_grace_period.days + end + it 'should validate uniqueness of tech contacts' do same_contact = Fabricate(:contact, code: 'same_contact') domain = Fabricate(:domain) @@ -96,6 +103,17 @@ describe Domain do @domain.domain_statuses.where(value: DomainStatus::EXPIRED).count.should == 1 end + it 'should start redemption grace period' do + Domain.start_redemption_grace_period + @domain.domain_statuses.where(value: DomainStatus::SERVER_HOLD).count.should == 0 + + @domain.outzone_at = Time.zone.now + @domain.save + + Domain.start_redemption_grace_period + @domain.domain_statuses.where(value: DomainStatus::SERVER_HOLD).count.should == 1 + end + context 'about registrant update confirm' do before :all do @domain.registrant_verification_token = 123 From 21829faf38c27ba3108900dfe23723ec0166431f Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 10 Jun 2015 12:11:16 +0300 Subject: [PATCH 2/9] Add domain delete period method #2622 --- app/models/domain.rb | 23 +++++++++++++++++++---- spec/epp/domain_spec.rb | 2 +- spec/models/domain_spec.rb | 38 ++++++++++++++++++++++++++++++++------ 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/app/models/domain.rb b/app/models/domain.rb index 1b0620262..ddf5601ac 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -147,7 +147,7 @@ class Domain < ActiveRecord::Base ) end - def expire_domains + def start_expire_period Domain.where('valid_to <= ?', Time.zone.now).each do |x| x.domain_statuses.create(value: DomainStatus::EXPIRED) if x.expirable? end @@ -158,6 +158,12 @@ class Domain < ActiveRecord::Base x.domain_statuses.create(value: DomainStatus::SERVER_HOLD) if x.server_holdable? end end + + def start_delete_period + Domain.where('delete_at <= ?', Time.zone.now).each do |x| + x.domain_statuses.create(value: DomainStatus::DELETE_CANDIDATE) if x.deletable? + end + end end def name=(value) @@ -202,6 +208,13 @@ class Domain < ActiveRecord::Base true end + def deletable? + return false if delete_at > Time.zone.now + return false if domain_statuses.where(value: DomainStatus::DELETE_CANDIDATE).any? + return false if domain_statuses.where(value: DomainStatus::SERVER_DELETE_PROHIBITED).any? + true + end + def pending_update? (domain_statuses.pluck(:value) & %W( #{DomainStatus::PENDING_UPDATE} @@ -340,13 +353,15 @@ class Domain < ActiveRecord::Base def set_validity_dates self.registered_at = Time.zone.now - self.valid_from = Time.zone.now.to_date + self.valid_from = Time.zone.now self.valid_to = valid_from + self.class.convert_period_to_time(period, period_unit) - self.outzone_at = self.valid_to + Setting.expire_warning_period.days - self.delete_at = self.outzone_at + Setting.redemption_grace_period.days + self.outzone_at = valid_to + Setting.expire_warning_period.days + self.delete_at = outzone_at + Setting.redemption_grace_period.days end def manage_automatic_statuses + # domain_statuses.create(value: DomainStatus::DELETE_CANDIDATE) if deletable? + if domain_statuses.empty? && valid? domain_statuses.create(value: DomainStatus::OK) elsif domain_statuses.length > 1 || !valid? diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index 29f3bd0ab..1973d84f1 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -317,7 +317,7 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml) response[:msg].should == 'Command completed successfully' response[:result_code].should == '1000' - Domain.first.valid_to.should == 1.year.since.to_date + Domain.first.valid_to.should be_within(5).of(1.year.since) end it 'does not create a domain with invalid period' do diff --git a/spec/models/domain_spec.rb b/spec/models/domain_spec.rb index 64094d785..d32eb3040 100644 --- a/spec/models/domain_spec.rb +++ b/spec/models/domain_spec.rb @@ -56,10 +56,10 @@ describe Domain do end it 'should have correct validity dates' do - valid_to = Time.zone.now.beginning_of_day + 1.year - @domain.valid_to.should == valid_to - @domain.outzone_at.should == valid_to + Setting.expire_warning_period.days - @domain.delete_at.should == valid_to + Setting.expire_warning_period.days + Setting.redemption_grace_period.days + valid_to = Time.zone.now + 1.year + @domain.valid_to.should be_within(5).of(valid_to) + @domain.outzone_at.should be_within(5).of(valid_to + Setting.expire_warning_period.days) + @domain.delete_at.should be_within(5).of(valid_to + Setting.expire_warning_period.days + Setting.redemption_grace_period.days) end it 'should validate uniqueness of tech contacts' do @@ -93,13 +93,16 @@ describe Domain do end it 'should expire domains' do - Domain.expire_domains + Domain.start_expire_period @domain.domain_statuses.where(value: DomainStatus::EXPIRED).count.should == 0 @domain.valid_to = Time.zone.now - 10.days @domain.save - Domain.expire_domains + Domain.start_expire_period + @domain.domain_statuses.where(value: DomainStatus::EXPIRED).count.should == 1 + + Domain.start_expire_period @domain.domain_statuses.where(value: DomainStatus::EXPIRED).count.should == 1 end @@ -108,12 +111,35 @@ describe Domain do @domain.domain_statuses.where(value: DomainStatus::SERVER_HOLD).count.should == 0 @domain.outzone_at = Time.zone.now + @domain.domain_statuses.create(value: DomainStatus::SERVER_MANUAL_INZONE) # this prohibits server_hold @domain.save + Domain.start_redemption_grace_period + @domain.domain_statuses.where(value: DomainStatus::SERVER_HOLD).count.should == 0 + + @domain.domain_statuses.destroy_all + Domain.start_redemption_grace_period @domain.domain_statuses.where(value: DomainStatus::SERVER_HOLD).count.should == 1 end + it 'should start delete period' do + Domain.start_delete_period + @domain.domain_statuses.where(value: DomainStatus::DELETE_CANDIDATE).count.should == 0 + + @domain.delete_at = Time.zone.now + @domain.domain_statuses.create(value: DomainStatus::SERVER_DELETE_PROHIBITED) # this prohibits delete_candidate + @domain.save + + Domain.start_delete_period + @domain.domain_statuses.where(value: DomainStatus::DELETE_CANDIDATE).count.should == 0 + + @domain.domain_statuses.destroy_all + Domain.start_delete_period + + @domain.domain_statuses.where(value: DomainStatus::DELETE_CANDIDATE).count.should == 1 + end + context 'about registrant update confirm' do before :all do @domain.registrant_verification_token = 123 From 3c8bbe18cf0bf231593d481b2c69b8ee3a5b78e9 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 10 Jun 2015 12:20:35 +0300 Subject: [PATCH 3/9] Add domain destroy method #2622 --- app/models/domain.rb | 12 +++++++++--- spec/models/domain_spec.rb | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/models/domain.rb b/app/models/domain.rb index ddf5601ac..5cef3b032 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -161,7 +161,13 @@ class Domain < ActiveRecord::Base def start_delete_period Domain.where('delete_at <= ?', Time.zone.now).each do |x| - x.domain_statuses.create(value: DomainStatus::DELETE_CANDIDATE) if x.deletable? + x.domain_statuses.create(value: DomainStatus::DELETE_CANDIDATE) if x.delete_candidateable? + end + end + + def destroy_delete_candidates + DomainStatus.where(value: DomainStatus::DELETE_CANDIDATE).each do |x| + x.domain.destroy end end end @@ -208,7 +214,7 @@ class Domain < ActiveRecord::Base true end - def deletable? + def delete_candidateable? return false if delete_at > Time.zone.now return false if domain_statuses.where(value: DomainStatus::DELETE_CANDIDATE).any? return false if domain_statuses.where(value: DomainStatus::SERVER_DELETE_PROHIBITED).any? @@ -360,7 +366,7 @@ class Domain < ActiveRecord::Base end def manage_automatic_statuses - # domain_statuses.create(value: DomainStatus::DELETE_CANDIDATE) if deletable? + # domain_statuses.create(value: DomainStatus::DELETE_CANDIDATE) if delete_candidateable? if domain_statuses.empty? && valid? domain_statuses.create(value: DomainStatus::OK) diff --git a/spec/models/domain_spec.rb b/spec/models/domain_spec.rb index d32eb3040..ba9066e86 100644 --- a/spec/models/domain_spec.rb +++ b/spec/models/domain_spec.rb @@ -59,7 +59,9 @@ describe Domain do valid_to = Time.zone.now + 1.year @domain.valid_to.should be_within(5).of(valid_to) @domain.outzone_at.should be_within(5).of(valid_to + Setting.expire_warning_period.days) - @domain.delete_at.should be_within(5).of(valid_to + Setting.expire_warning_period.days + Setting.redemption_grace_period.days) + @domain.delete_at.should be_within(5).of( + valid_to + Setting.expire_warning_period.days + Setting.redemption_grace_period.days + ) end it 'should validate uniqueness of tech contacts' do @@ -140,6 +142,19 @@ describe Domain do @domain.domain_statuses.where(value: DomainStatus::DELETE_CANDIDATE).count.should == 1 end + it 'should destroy delete candidates' do + Fabricate(:domain) + Domain.count.should == 2 + + @domain.delete_at = Time.zone.now + @domain.save + + Domain.start_delete_period + + Domain.destroy_delete_candidates + Domain.count.should == 1 + end + context 'about registrant update confirm' do before :all do @domain.registrant_verification_token = 123 From d9aaab1e926856602d404b4818df7971bd02cda9 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 10 Jun 2015 12:39:47 +0300 Subject: [PATCH 4/9] Add scheduler tasks #2622 --- config/schedule.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config/schedule.rb b/config/schedule.rb index 3f366a946..6796ebbde 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -31,3 +31,13 @@ end every 3.hours do runner 'Certificate.update_crl' end + +every :hour do + runner 'Domain.start_expire_period' + runner 'Domain.start_redemption_grace_period' + runner 'Domain.start_delete_period' +end + +every 42.minutes do + runner 'Domain.destroy_delete_candidates' +end From cd1f04f15584cfcee00c345dd0dd35c3cbb7c05f Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 10 Jun 2015 13:10:26 +0300 Subject: [PATCH 5/9] Add test #2622 --- app/models/domain.rb | 12 ++++++++++++ app/models/epp/domain.rb | 9 ++------- spec/epp/domain_spec.rb | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/app/models/domain.rb b/app/models/domain.rb index 5cef3b032..450aa0d9c 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -221,6 +221,18 @@ class Domain < ActiveRecord::Base true end + def renewable? + if Setting.days_to_renew_domain_before_expire != 0 + if (valid_to - Time.zone.now).to_i / 1.day >= Setting.days_to_renew_domain_before_expire + return false + end + end + + return false if domain_statuses.where(value: DomainStatus::DELETE_CANDIDATE).any? + + true + end + def pending_update? (domain_statuses.pluck(:value) & %W( #{DomainStatus::PENDING_UPDATE} diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index 752ced9ab..323b7302c 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -427,21 +427,16 @@ class Epp::Domain < Domain ### RENEW ### def renew(cur_exp_date, period, unit = 'y') - # TODO: Check how much time before domain exp date can it be renewed validate_exp_dates(cur_exp_date) - if Setting.days_to_renew_domain_before_expire != 0 - if (valid_to - Time.zone.now).to_i / 1.day >= Setting.days_to_renew_domain_before_expire - add_epp_error('2105', nil, nil, I18n.t('object_is_not_eligible_for_renewal')) - end - end - + add_epp_error('2105', nil, nil, I18n.t('object_is_not_eligible_for_renewal')) unless renewable? return false if errors.any? p = self.class.convert_period_to_time(period, unit) self.valid_to = valid_to + p self.period = period self.period_unit = unit + save end diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index 1973d84f1..7162db690 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -2047,6 +2047,26 @@ describe 'EPP Domain', epp: true do Setting.days_to_renew_domain_before_expire = 90 end + it 'does not renew a domain if it is a delete candidate' do + domain.valid_to = Time.zone.now + 10.days + domain.delete_at = Time.zone.now + domain.save + + Domain.start_delete_period + + exp_date = domain.valid_to.to_date + + xml = @epp_xml.domain.renew( + name: { value: domain.name }, + curExpDate: { value: exp_date.to_s }, + period: { value: '1', attrs: { unit: 'y' } } + ) + + response = epp_plain_request(xml) + response[:results][0][:msg].should == 'Object is not eligible for renewal' + response[:results][0][:result_code].should == '2105' + end + it 'does not renew foreign domain' do login_as :registrar2 do exp_date = 1.year.since.to_date From d554e1211677f51f346d13e9aade2429240396aa Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 10 Jun 2015 13:57:35 +0300 Subject: [PATCH 6/9] Remove expire statuses on domain on renew #2622 --- app/models/epp/domain.rb | 3 +++ spec/epp/domain_spec.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index 323b7302c..407d100e3 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -437,6 +437,9 @@ class Epp::Domain < Domain self.period = period self.period_unit = unit + domain_statuses.where(value: DomainStatus::SERVER_HOLD).destroy_all + domain_statuses.where(value: DomainStatus::EXPIRED).destroy_all + save end diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index 7162db690..89e60de00 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -2067,6 +2067,33 @@ describe 'EPP Domain', epp: true do response[:results][0][:result_code].should == '2105' end + it 'should renew a expired domain' do + domain.valid_to = Time.zone.now - 50.days + domain.outzone_at = Time.zone.now - 50.days + domain.save + + Domain.start_expire_period + Domain.start_redemption_grace_period + + domain.domain_statuses.where(value: DomainStatus::EXPIRED).count.should == 1 + domain.domain_statuses.where(value: DomainStatus::SERVER_HOLD).count.should == 1 + + exp_date = domain.valid_to.to_date + + xml = @epp_xml.domain.renew( + name: { value: domain.name }, + curExpDate: { value: exp_date.to_s }, + period: { value: '1', attrs: { unit: 'y' } } + ) + + response = epp_plain_request(xml) + response[:results][0][:msg].should == 'Command completed successfully' + response[:results][0][:result_code].should == '1000' + + domain.domain_statuses.where(value: DomainStatus::EXPIRED).count.should == 0 + domain.domain_statuses.where(value: DomainStatus::SERVER_HOLD).count.should == 0 + end + it 'does not renew foreign domain' do login_as :registrar2 do exp_date = 1.year.since.to_date From ff0a33c3c4af1ad01827b7eef95df9815919e903 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 10 Jun 2015 14:06:24 +0300 Subject: [PATCH 7/9] Add logging #2622 --- app/models/domain.rb | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/app/models/domain.rb b/app/models/domain.rb index 450aa0d9c..09e4c875d 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -148,27 +148,48 @@ class Domain < ActiveRecord::Base end def start_expire_period - Domain.where('valid_to <= ?', Time.zone.now).each do |x| + logger.info "#{Time.zone.now.utc} - Expiring domains\n" + + d = Domain.where('valid_to <= ?', Time.zone.now) + d.each do |x| x.domain_statuses.create(value: DomainStatus::EXPIRED) if x.expirable? end + + logger.info "#{Time.zone.now.utc} - Successfully expired #{d.count} domains\n" end def start_redemption_grace_period - Domain.where('outzone_at <= ?', Time.zone.now).each do |x| + logger.info "#{Time.zone.now.utc} - Setting server_hold to domains\n" + + d = Domain.where('outzone_at <= ?', Time.zone.now) + d.each do |x| x.domain_statuses.create(value: DomainStatus::SERVER_HOLD) if x.server_holdable? end + + logger.info "#{Time.zone.now.utc} - Successfully set server_hold to #{d.count} domains\n" end def start_delete_period - Domain.where('delete_at <= ?', Time.zone.now).each do |x| + logger.info "#{Time.zone.now.utc} - Setting delete_candidate to domains\n" + + d = Domain.where('delete_at <= ?', Time.zone.now) + d.each do |x| x.domain_statuses.create(value: DomainStatus::DELETE_CANDIDATE) if x.delete_candidateable? end + + logger.info "#{Time.zone.now.utc} - Successfully set delete_candidate to #{d.count} domains\n" end def destroy_delete_candidates + logger.info "#{Time.zone.now.utc} - Destroying domains\n" + + c = 0 DomainStatus.where(value: DomainStatus::DELETE_CANDIDATE).each do |x| x.domain.destroy + c += 1 end + + logger.info "#{Time.zone.now.utc} - Successfully destroyed #{c} domains\n" end end From c3dfd363b775efe006e9bbfc1a631cf012ebf00c Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 10 Jun 2015 14:11:06 +0300 Subject: [PATCH 8/9] Add settings #2623 --- db/data/20150610111019_add_expire_settings.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 db/data/20150610111019_add_expire_settings.rb diff --git a/db/data/20150610111019_add_expire_settings.rb b/db/data/20150610111019_add_expire_settings.rb new file mode 100644 index 000000000..9f8b9cce8 --- /dev/null +++ b/db/data/20150610111019_add_expire_settings.rb @@ -0,0 +1,10 @@ +class AddExpireSettings < ActiveRecord::Migration + def self.up + Setting.expire_warning_period = 15 + Setting.redemption_grace_period = 30 + end + + def self.down + raise ActiveRecord::IrreversibleMigration + end +end From f6b61a7ac52c330fa830a8da4746fd00bd8104d9 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 10 Jun 2015 14:14:54 +0300 Subject: [PATCH 9/9] Contact email change notification update #2558 --- app/mailers/contact_mailer.rb | 11 +++++++++-- spec/mailers/contact_mailer_spec.rb | 6 ++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/mailers/contact_mailer.rb b/app/mailers/contact_mailer.rb index 4db130006..cf8c03d38 100644 --- a/app/mailers/contact_mailer.rb +++ b/app/mailers/contact_mailer.rb @@ -8,7 +8,14 @@ class ContactMailer < ApplicationMailer return if contact.deliver_emails != true @contact = contact - mail(to: [@contact.email, @contact.email_was], - subject: "#{I18n.t(:contact_email_update_subject)} [#{@contact.code}]") + + emails = [] + emails << [@contact.email, @contact.email_was] if @contact.registrant_domains.present? + emails << @contact.domains.map(&:email) if @contact.domains.present? + emails = emails.uniq + + emails.each do |email| + mail(to: email, subject: "#{I18n.t(:contact_email_update_subject)} [#{@contact.code}]") + end end end diff --git a/spec/mailers/contact_mailer_spec.rb b/spec/mailers/contact_mailer_spec.rb index 76b98e717..9f825b380 100644 --- a/spec/mailers/contact_mailer_spec.rb +++ b/spec/mailers/contact_mailer_spec.rb @@ -27,7 +27,8 @@ describe ContactMailer do describe 'email changed notification' do before :all do - @contact = Fabricate(:contact, email: 'test@example.com') + @domain = Fabricate(:domain) + @contact = @domain.registrant @contact.deliver_emails = true @contact.email = 'test@example.org' # new email @mail = ContactMailer.email_updated(@contact) @@ -42,7 +43,8 @@ describe ContactMailer do end it 'should have both old and new receiver email' do - @mail.to.should == ["test@example.org", "test@example.com"] + @mail.to.size.should == 2 + @mail.to.include? "test@example.org" end it 'should render body' do