Browse Source

CLDC-3278: Restructure tenancy length validations to only error on relevant fields

pull/2295/head
Rachael Booth 2 years ago
parent
commit
5d1348a696
  1. 106
      app/models/validations/tenancy_validations.rb
  2. 9
      spec/models/lettings_log_spec.rb
  3. 382
      spec/models/validations/tenancy_validations_spec.rb

106
app/models/validations/tenancy_validations.rb

@ -3,48 +3,68 @@ module Validations::TenancyValidations
# or 'validate_' to run on submit as well # or 'validate_' to run on submit as well
include Validations::SharedValidations include Validations::SharedValidations
def validate_fixed_term_tenancy(record) # N.B. To match the page split and naming, this is the supported housing case
is_present = record.tenancylength.present? # General needs cases are in the other validations below
is_in_range = record.tenancylength.to_i.between?(min_tenancy_length(record), 99) def validate_tenancy_length(record)
rent_type_dependent_conditions = [ return unless record.tenancy_type_fixed_term? && record.is_supported_housing?
{ return if record.tenancylength.blank?
condition: (record.is_assured_shorthold_tenancy? && !is_in_range) && is_present,
error: I18n.t( min_tenancy_length = 1
"validations.tenancy.length.shorthold", return if record.tenancylength.to_i.between?(min_tenancy_length, 99)
min_tenancy_length: min_tenancy_length(record),
), message = record.is_assured_shorthold_tenancy? ? I18n.t("validations.tenancy.length.shorthold", min_tenancy_length:) : I18n.t("validations.tenancy.length.secure", min_tenancy_length:)
}, record.errors.add :needstype, message
{ record.errors.add :rent_type, message
condition: (record.is_secure_tenancy? && !is_in_range) && is_present, record.errors.add :tenancylength, :tenancylength_invalid, message: message
error: I18n.t( record.errors.add :tenancy, message
"validations.tenancy.length.secure", end
min_tenancy_length: min_tenancy_length(record),
), def validate_tenancy_length_affordable_rent(record)
}, return unless record.tenancy_type_fixed_term? && record.affordable_or_social_rent? && record.is_general_needs?
{ return if record.tenancylength.blank?
condition: (record.is_periodic_tenancy? && !is_in_range) && is_present,
error: I18n.t( min_tenancy_length = 2
"validations.tenancy.length.secure", return if record.tenancylength.to_i.between?(min_tenancy_length, 99)
min_tenancy_length: min_tenancy_length(record),
), message = record.is_assured_shorthold_tenancy? ? I18n.t("validations.tenancy.length.shorthold", min_tenancy_length:) : I18n.t("validations.tenancy.length.secure", min_tenancy_length:)
}, record.errors.add :needstype, message
] record.errors.add :rent_type, message
rent_type_independent_conditions = [ record.errors.add :tenancylength, :tenancylength_invalid, message: message
{ record.errors.add :tenancy, message
condition: !(record.is_secure_tenancy? || record.is_assured_shorthold_tenancy? || record.is_periodic_tenancy?) && is_present, end
error: I18n.t("validations.tenancy.length.fixed_term_not_required"),
}, def validate_tenancy_length_intermediate_rent(record)
] return unless record.tenancy_type_fixed_term? && !record.affordable_or_social_rent? && record.is_general_needs?
conditions = rent_type_dependent_conditions + rent_type_independent_conditions return if record.tenancylength.blank?
conditions.each do |condition| min_tenancy_length = 1
next unless condition[:condition] return if record.tenancylength.to_i.between?(min_tenancy_length, 99)
record.errors.add :needstype, condition[:error] message = record.is_assured_shorthold_tenancy? ? I18n.t("validations.tenancy.length.shorthold", min_tenancy_length:) : I18n.t("validations.tenancy.length.secure", min_tenancy_length:)
record.errors.add :rent_type, condition[:error] if rent_type_dependent_conditions.include?(condition) record.errors.add :needstype, message
record.errors.add :tenancylength, :tenancylength_invalid, message: condition[:error] record.errors.add :rent_type, message
record.errors.add :tenancy, condition[:error] record.errors.add :tenancylength, :tenancylength_invalid, message: message
record.errors.add :tenancy, message
end end
def validate_tenancy_length_periodic(record)
return unless record.is_periodic_tenancy? && record.tenancylength.present?
min_tenancy_length = 1
return if record.tenancylength.to_i.between?(min_tenancy_length, 99)
message = I18n.t("validations.tenancy.length.secure", min_tenancy_length:)
record.errors.add :tenancylength, :tenancylength_invalid, message: message
record.errors.add :tenancy, message
end
def validate_tenancy_length_blank_when_not_required(record)
return if record.tenancylength.blank?
return if record.tenancy_type_fixed_term? || record.is_periodic_tenancy?
message = I18n.t("validations.tenancy.length.fixed_term_not_required")
record.errors.add :tenancylength, :tenancylength_invalid, message: message
record.errors.add :tenancy, message
end end
def validate_other_tenancy_type(record) def validate_other_tenancy_type(record)
@ -59,8 +79,4 @@ module Validations::TenancyValidations
record.errors.add :hhmemb, I18n.t("validations.tenancy.joint_more_than_one_member") record.errors.add :hhmemb, I18n.t("validations.tenancy.joint_more_than_one_member")
end end
end end
def min_tenancy_length(record)
record.is_supported_housing? || record.renttype == 3 || record.is_periodic_tenancy? ? 1 : 2
end
end end

9
spec/models/lettings_log_spec.rb

@ -112,10 +112,17 @@ RSpec.describe LettingsLog do
end end
it "validates tenancy type" do it "validates tenancy type" do
expect(validator).to receive(:validate_fixed_term_tenancy)
expect(validator).to receive(:validate_other_tenancy_type) expect(validator).to receive(:validate_other_tenancy_type)
end end
it "validates tenancy length" do
expect(validator).to receive(:validate_tenancy_length)
expect(validator).to receive(:validate_tenancy_length_affordable_rent)
expect(validator).to receive(:validate_tenancy_length_intermediate_rent)
expect(validator).to receive(:validate_tenancy_length_periodic)
expect(validator).to receive(:validate_tenancy_length_blank_when_not_required)
end
it "validates the previous postcode" do it "validates the previous postcode" do
expect(validator).to receive(:validate_previous_accommodation_postcode) expect(validator).to receive(:validate_previous_accommodation_postcode)
end end

382
spec/models/validations/tenancy_validations_spec.rb

@ -3,280 +3,271 @@ require "rails_helper"
RSpec.describe Validations::TenancyValidations do RSpec.describe Validations::TenancyValidations do
subject(:tenancy_validator) { validator_class.new } subject(:tenancy_validator) { validator_class.new }
before do let(:validator_class) { Class.new { include Validations::TenancyValidations } }
Timecop.freeze(Time.zone.local(2021, 5, 1))
end
after do describe "tenancy length validations" do
Timecop.unfreeze let(:record) { FactoryBot.create(:lettings_log, :setup_completed) }
end
let(:validator_class) { Class.new { include Validations::TenancyValidations } } shared_examples "adds expected errors based on the tenancy length" do |tenancy_type_case, min_tenancy_length|
let(:record) { FactoryBot.create(:lettings_log, startdate: Time.zone.local(2021, 5, 1), needstype: 1, rent_type: 1) } context "and tenancy type is #{tenancy_type_case[:name]}" do
let(:expected_error) { tenancy_type_case[:expected_error].call(min_tenancy_length) }
describe "fixed term tenancy validations" do before { record.tenancy = tenancy_type_case[:code] }
context "when fixed term tenancy" do
context "when type of tenancy is not assured or assured shorthold" do
let(:expected_error) { I18n.t("validations.tenancy.length.fixed_term_not_required") }
it "tenancy length should not be present" do context "and tenancy length is less than #{min_tenancy_length}" do
record.tenancy = 3 before { record.tenancylength = min_tenancy_length - 1 }
record.tenancylength = 10
tenancy_validator.validate_fixed_term_tenancy(record) it "adds errors to #{tenancy_type_case[:error_fields].join(', ')}" do
expect(record.errors["needstype"]).to include(match(expected_error)) validation.call(record)
expect(record.errors["rent_type"]).not_to include(match(expected_error)) tenancy_type_case[:error_fields].each do |field|
expect(record.errors["tenancylength"]).to include(match(expected_error)) expect(record.errors[field]).to include(match(expected_error))
expect(record.errors["tenancy"]).to include(match(expected_error))
end end
expect(record.errors.size).to be(tenancy_type_case[:error_fields].length)
end end
context "when type of tenancy is assured shorthold" do
let(:expected_error) do
I18n.t(
"validations.tenancy.length.shorthold",
min_tenancy_length: 2,
)
end end
before { record.tenancy = 4 } context "and tenancy length is more than 99" do
before { record.tenancylength = 100 }
context "when tenancy length is less than 2" do it "adds errors to #{tenancy_type_case[:error_fields].join(', ')}" do
it "adds an error" do validation.call(record)
record.tenancylength = 1 tenancy_type_case[:error_fields].each do |field|
tenancy_validator.validate_fixed_term_tenancy(record) expect(record.errors[field]).to include(match(expected_error))
expect(record.errors["needstype"]).to include(match(expected_error))
expect(record.errors["rent_type"]).to include(match(expected_error))
expect(record.errors["tenancylength"]).to include(match(expected_error))
expect(record.errors["tenancy"]).to include(match(expected_error))
end end
end expect(record.errors.size).to be(tenancy_type_case[:error_fields].length)
context "when tenancy length is greater than 99" do
it "adds an error" do
record.tenancylength = 100
tenancy_validator.validate_fixed_term_tenancy(record)
expect(record.errors["needstype"]).to include(match(expected_error))
expect(record.errors["rent_type"]).to include(match(expected_error))
expect(record.errors["tenancylength"]).to include(match(expected_error))
expect(record.errors["tenancy"]).to include(match(expected_error))
end end
end end
context "when tenancy length is between 2-99" do context "and tenancy length is between #{min_tenancy_length} and 99" do
it "does not add an error" do before { record.tenancylength = min_tenancy_length }
record.tenancylength = 3
tenancy_validator.validate_fixed_term_tenancy(record) it "does not add errors" do
validation.call(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end end
end end
context "when tenancy length has not been answered" do context "and tenancy length is not set" do
it "does not add an error" do before { record.tenancylength = nil }
record.tenancylength = nil
tenancy_validator.validate_fixed_term_tenancy(record) it "does not add errors" do
validation.call(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end end
end end
end end
context "when the collection start year is before 2022" do
context "when type of tenancy is secure" do
let(:expected_error) do
I18n.t(
"validations.tenancy.length.secure",
min_tenancy_length: 2,
)
end end
before { record.tenancy = 1 } shared_examples "does not add errors when tenancy type is not fixed term" do
context "and tenancy type is not fixed term" do
before do
record.tenancy = 8
record.tenancylength = 0
end
context "when tenancy length is less than 2" do it "does not add errors" do
it "adds an error" do validation.call(record)
record.tenancylength = 1 expect(record.errors).to be_empty
tenancy_validator.validate_fixed_term_tenancy(record)
expect(record.errors["needstype"]).to include(match(expected_error))
expect(record.errors["tenancylength"]).to include(match(expected_error))
expect(record.errors["tenancy"]).to include(match(expected_error))
end end
end end
context "when tenancy length is greater than 99" do
it "adds an error" do
record.tenancylength = 100
tenancy_validator.validate_fixed_term_tenancy(record)
expect(record.errors["needstype"]).to include(match(expected_error))
expect(record.errors["tenancylength"]).to include(match(expected_error))
expect(record.errors["tenancy"]).to include(match(expected_error))
end end
fixed_term_tenancy_type_cases = [
{
name: "assured shorthold",
code: 4,
expected_error: ->(min_tenancy_length) { I18n.t("validations.tenancy.length.shorthold", min_tenancy_length:) },
error_fields: %w[needstype rent_type tenancylength tenancy],
},
{
name: "secure fixed term",
code: 6,
expected_error: ->(min_tenancy_length) { I18n.t("validations.tenancy.length.secure", min_tenancy_length:) },
error_fields: %w[needstype rent_type tenancylength tenancy],
},
]
describe "#validate_tenancy_length" do
subject(:validation) { ->(record) { tenancy_validator.validate_tenancy_length(record) } }
context "when needs type is supported housing" do
before { record.needstype = 2 }
fixed_term_tenancy_type_cases.each do |tenancy_type_case|
include_examples "adds expected errors based on the tenancy length", tenancy_type_case, 1
end end
context "when tenancy length is between 2-99" do include_examples "does not add errors when tenancy type is not fixed term"
it "does not add an error" do
record.tenancylength = 3
tenancy_validator.validate_fixed_term_tenancy(record)
expect(record.errors).to be_empty
end end
context "when needs type is general needs" do
before do
record.needstype = 1
record.tenancy = 4
record.tenancylength = 0
end end
context "when tenancy length has not been answered" do it "does not add errors" do
it "does not add an error" do validation.call(record)
record.tenancylength = nil
tenancy_validator.validate_fixed_term_tenancy(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end end
end end
end end
end
context "when the collection start year is 2022 or later" do describe "#validate_tenancy_length_affordable_rent" do
before do subject(:validation) { ->(record) { tenancy_validator.validate_tenancy_length_affordable_rent(record) } }
Timecop.freeze(2022, 5, 1)
end
after do context "when needs type is general needs" do
Timecop.unfreeze before { record.needstype = 1 }
end
let(:record) { FactoryBot.create(:lettings_log, startdate: Time.zone.local(2022, 5, 1), needstype: 1, rent_type: 1) } context "and rent type is affordable or social rent" do
before { record.renttype = 1 }
context "when type of tenancy is Secure - fixed term" do fixed_term_tenancy_type_cases.each do |tenancy_type_case|
let(:expected_error) do include_examples "adds expected errors based on the tenancy length", tenancy_type_case, 2
I18n.t(
"validations.tenancy.length.secure",
min_tenancy_length: 2,
)
end end
before { record.tenancy = 6 } include_examples "does not add errors when tenancy type is not fixed term"
context "when tenancy length is less than 2" do
it "adds an error" do
record.tenancylength = 1
tenancy_validator.validate_fixed_term_tenancy(record)
expect(record.errors["needstype"]).to include(match(expected_error))
expect(record.errors["tenancylength"]).to include(match(expected_error))
expect(record.errors["tenancy"]).to include(match(expected_error))
end
end end
context "when tenancy length is greater than 99" do context "and rent type is intermediate rent" do
it "adds an error" do before do
record.tenancylength = 100 record.renttype = 3
tenancy_validator.validate_fixed_term_tenancy(record) record.tenancy = 4
expect(record.errors["needstype"]).to include(match(expected_error)) record.tenancylength = 0
expect(record.errors["tenancylength"]).to include(match(expected_error))
expect(record.errors["tenancy"]).to include(match(expected_error))
end
end end
context "when tenancy length is between 2-99" do it "does not add errors" do
it "does not add an error" do validation.call(record)
record.tenancylength = 3
tenancy_validator.validate_fixed_term_tenancy(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end end
end end
end
context "when tenancy length has not been answered" do context "when needs type is supported housing" do
it "does not add an error" do before do
record.tenancylength = nil record.needstype = 2
tenancy_validator.validate_fixed_term_tenancy(record) record.renttype = 1
record.tenancy = 4
record.tenancylength = 0
end
it "does not add errors" do
validation.call(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end end
end end
end end
context "when type of tenancy is Secure - lifetime" do describe "#validate_tenancy_length_intermediate_rent" do
let(:expected_error) do subject(:validation) { ->(record) { tenancy_validator.validate_tenancy_length_intermediate_rent(record) } }
I18n.t(
"validations.tenancy.length.secure",
min_tenancy_length: 2,
)
end
before { record.tenancy = 7 } context "when needs type is general needs" do
before { record.needstype = 1 }
context "when tenancy length is less than 2" do context "and rent type is intermediate rent" do
it "adds an error" do before { record.renttype = 3 }
record.tenancylength = 1
tenancy_validator.validate_fixed_term_tenancy(record) fixed_term_tenancy_type_cases.each do |tenancy_type_case|
expect(record.errors["needstype"]).to include(match(expected_error)) include_examples "adds expected errors based on the tenancy length", tenancy_type_case, 1
expect(record.errors["tenancylength"]).to include(match(expected_error))
expect(record.errors["tenancy"]).to include(match(expected_error))
end
end end
context "when tenancy length is greater than 99" do include_examples "does not add errors when tenancy type is not fixed term"
it "adds an error" do
record.tenancylength = 100
tenancy_validator.validate_fixed_term_tenancy(record)
expect(record.errors["needstype"]).to include(match(expected_error))
expect(record.errors["tenancylength"]).to include(match(expected_error))
expect(record.errors["tenancy"]).to include(match(expected_error))
end end
context "and rent type is not intermediate rent" do
before do
record.renttype = 2
record.tenancy = 4
record.tenancylength = 0
end end
context "when tenancy length is between 2-99" do it "does not add errors" do
it "does not add an error" do validation.call(record)
record.tenancylength = 3
tenancy_validator.validate_fixed_term_tenancy(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end end
end end
end
context "when tenancy length has not been answered" do context "when needs type is supported housing" do
it "does not add an error" do before do
record.tenancylength = nil record.needstype = 2
tenancy_validator.validate_fixed_term_tenancy(record) record.renttype = 3
record.tenancy = 4
record.tenancylength = 0
end
it "does not add errors" do
validation.call(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end end
end end
end end
context "when type of tenancy is periodic" do describe "#validate_tenancy_length_periodic" do
let(:expected_error) do subject(:validation) { ->(record) { tenancy_validator.validate_tenancy_length_periodic(record) } }
I18n.t(
"validations.tenancy.length.secure",
min_tenancy_length: 1,
)
end
before { record.tenancy = 8 } periodic_tenancy_case = {
name: "periodic",
code: 8,
expected_error: ->(min_tenancy_length) { I18n.t("validations.tenancy.length.secure", min_tenancy_length:) },
error_fields: %w[tenancylength tenancy],
}
include_examples "adds expected errors based on the tenancy length", periodic_tenancy_case, 1
context "when tenancy length is less than 1" do context "when tenancy type is not periodic" do
it "adds an error" do before do
record.tenancy = 6
record.tenancylength = 0 record.tenancylength = 0
tenancy_validator.validate_fixed_term_tenancy(record)
expect(record.errors["needstype"]).to include(match(expected_error))
expect(record.errors["tenancylength"]).to include(match(expected_error))
expect(record.errors["tenancy"]).to include(match(expected_error))
end end
it "does not add errors" do
validation.call(record)
expect(record.errors).to be_empty
end end
end
describe "#validate_tenancy_length_blank_when_not_required" do
context "when a tenancy length is provided" do
before { record.tenancylength = 10 }
context "when tenancy length is greater than 99" do context "and tenancy type is not fixed term or periodic" do
it "adds an error" do before { record.tenancy = 5 }
record.tenancylength = 100
tenancy_validator.validate_fixed_term_tenancy(record) it "adds errors to tenancylength and tenancy" do
expect(record.errors["needstype"]).to include(match(expected_error)) tenancy_validator.validate_tenancy_length_blank_when_not_required(record)
expect(record.errors["tenancylength"]).to include(match(expected_error)) expected_error = I18n.t("validations.tenancy.length.fixed_term_not_required")
expect(record.errors["tenancy"]).to include(match(expected_error)) expect(record.errors["tenancylength"]).to include(expected_error)
expect(record.errors["tenancy"]).to include(expected_error)
end end
end end
context "when tenancy length is between 2-99" do tenancy_types_with_length = [
it "does not add an error" do { name: "assured shorthold", code: 4 },
record.tenancylength = 3 { name: "secure fixed term", code: 6 },
tenancy_validator.validate_fixed_term_tenancy(record) { name: "periodic", code: 8 },
]
tenancy_types_with_length.each do |type|
context "and tenancy type is #{type[:name]}" do
before { record.tenancy = type[:code] }
it "does not add errors" do
tenancy_validator.validate_tenancy_length_blank_when_not_required(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end end
end end
end
end
context "when tenancy length has not been answered" do context "when tenancy length is not provided" do
it "does not add an error" do before do
record.tenancylength = nil record.tenancylength = nil
tenancy_validator.validate_fixed_term_tenancy(record) record.tenancy = 5
expect(record.errors).to be_empty
end end
it "does not add errors" do
tenancy_validator.validate_tenancy_length_blank_when_not_required(record)
expect(record.errors).to be_empty
end end
end end
end end
@ -284,6 +275,7 @@ RSpec.describe Validations::TenancyValidations do
end end
describe "tenancy type validations" do describe "tenancy type validations" do
let(:record) { FactoryBot.create(:lettings_log, :setup_completed) }
let(:field) { "validations.other_field_missing" } let(:field) { "validations.other_field_missing" }
let(:main_field_label) { "tenancy type" } let(:main_field_label) { "tenancy type" }
let(:other_field) { "tenancyother" } let(:other_field) { "tenancyother" }
@ -327,20 +319,11 @@ RSpec.describe Validations::TenancyValidations do
describe "joint tenancy validation" do describe "joint tenancy validation" do
context "when the data inputter has said that there is only one member in the household" do context "when the data inputter has said that there is only one member in the household" do
before do let(:record) { FactoryBot.create(:lettings_log, :setup_completed, hhmemb: 1) }
Timecop.freeze(2022, 5, 1)
end
after do
Timecop.unfreeze
end
let(:record) { FactoryBot.create(:lettings_log, startdate: Time.zone.local(2022, 5, 1)) }
let(:expected_error) { I18n.t("validations.tenancy.not_joint") } let(:expected_error) { I18n.t("validations.tenancy.not_joint") }
let(:hhmemb_expected_error) { I18n.t("validations.tenancy.joint_more_than_one_member") } let(:hhmemb_expected_error) { I18n.t("validations.tenancy.joint_more_than_one_member") }
it "displays an error if the data inputter says the letting is a joint tenancy" do it "displays an error if the data inputter says the letting is a joint tenancy" do
record.hhmemb = 1
record.joint = 1 record.joint = 1
tenancy_validator.validate_joint_tenancy(record) tenancy_validator.validate_joint_tenancy(record)
expect(record.errors["joint"]).to include(match(expected_error)) expect(record.errors["joint"]).to include(match(expected_error))
@ -348,7 +331,6 @@ RSpec.describe Validations::TenancyValidations do
end end
it "does not display an error if the data inputter says the letting is not a joint tenancy" do it "does not display an error if the data inputter says the letting is not a joint tenancy" do
record.hhmemb = 1
record.joint = 2 record.joint = 2
tenancy_validator.validate_joint_tenancy(record) tenancy_validator.validate_joint_tenancy(record)
expect(record.errors["joint"]).to be_empty expect(record.errors["joint"]).to be_empty
@ -356,7 +338,6 @@ RSpec.describe Validations::TenancyValidations do
end end
it "does not display an error if the data inputter has given the household members but not input if it is a joint tenancy" do it "does not display an error if the data inputter has given the household members but not input if it is a joint tenancy" do
record.hhmemb = 1
record.joint = nil record.joint = nil
tenancy_validator.validate_joint_tenancy(record) tenancy_validator.validate_joint_tenancy(record)
expect(record.errors["joint"]).to be_empty expect(record.errors["joint"]).to be_empty
@ -364,7 +345,6 @@ RSpec.describe Validations::TenancyValidations do
end end
it "does not error when don't know answer to joint" do it "does not error when don't know answer to joint" do
record.hhmemb = 1
record.joint = 3 record.joint = 3
tenancy_validator.validate_joint_tenancy(record) tenancy_validator.validate_joint_tenancy(record)

Loading…
Cancel
Save