From f758d598c4ee4e9a60c3002cd27a9d1dfc968e68 Mon Sep 17 00:00:00 2001 From: natdeanlewissoftwire <94526761+natdeanlewissoftwire@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:26:29 +0000 Subject: [PATCH] CLDC-1917 Add Startdate Validation (#1378) * feat: add validation with feature flag, typo fix and update tests * feat: flip feature toggle * feat: update feature toggle name * feat: fix form handler inequality * refactor: linting * refactor: use between in form handler * feat: remove feature toggle * feat: add dynamic date to lettings log factory * feat: fix log_summary_component_spec.rb tests * feat: update lettings_log.rb and start fixing lettings_log_spec.rb * feat: fix more tests * feat: fix more tests * feat: fix lettings log import service * refactor: linting * feat: fix checkboxes_spec.rb * feat: fix interruption_screen_helper_spec.rb * feat: fix check_answers_helper_spec.rb * feat: fix page_routing_spec.rb * feat: fix lettings_logs_field_import_service_spec.rb * feat: fix lettings_log_spec.rb * feat: fix question_spec.rb * feat: fix lettings_logs_controller_spec.rb * feat: fix check_answers_page_lettings_logs_spec.rb * feat: fix tenancy_validations_spec.rb * feat: fix validations_spec.rb * feat: fix accessible_autocomplete_spec.rb * feat: fix form_navigation_spec.rb * feat: fix soft_validations_spec.rb * feat: fix lettings_log_export_service_spec.rb * feat: fix saving_data_spec.rb * feat: fix page_spec.rb * feat: fix form_controller_spec.rb * refactor: linting * feat: fix subsection_spec.rb * feat: fix lettings_log_spec.rb * feat: fix financial_validations_spec.rb * feat: fix tasklist_page_spec.rb * feat: fix conditional_questions_spec.rb * feat: fix form_page_error_helper_spec.rb and log_summary_component_spec.rb * feat: fix lettings_log_csv_service_spec.rb * feat: fix tasklist_helper_spec.rb * refactor: linting * refactor: linting * feat: fix lettings_log_spec.rb * refactor: linting * refactor: replace financial year with collection yaer * feat: respond to PR comments pt. 1 * feat: respond to PR comments pt. 2 --- app/models/form_handler.rb | 4 +- app/models/validations/date_validations.rb | 26 - .../validations/sales/setup_validations.rb | 12 +- app/models/validations/setup_validations.rb | 40 + config/initializers/feature_toggle.rb | 4 +- config/locales/en.yml | 14 +- db/schema.rb | 2 +- spec/components/log_summary_component_spec.rb | 6 +- spec/factories/lettings_log.rb | 13 +- .../form/accessible_autocomplete_spec.rb | 13 +- .../check_answers_page_lettings_logs_spec.rb | 11 +- spec/features/form/checkboxes_spec.rb | 9 + .../form/conditional_questions_spec.rb | 10 + spec/features/form/form_navigation_spec.rb | 9 + spec/features/form/page_routing_spec.rb | 159 ++-- .../form/progressive_total_field_spec.rb | 9 + spec/features/form/saving_data_spec.rb | 9 + spec/features/form/tasklist_page_spec.rb | 16 +- spec/features/form/validations_spec.rb | 11 + spec/fixtures/exports/general_needs_log.csv | 2 +- spec/fixtures/exports/general_needs_log.xml | 4 +- .../exports/supported_housing_logs.xml | 4 +- spec/helpers/check_answers_helper_spec.rb | 9 + spec/helpers/form_page_error_helper_spec.rb | 9 + .../interruption_screen_helper_spec.rb | 9 + spec/helpers/tasklist_helper_spec.rb | 86 +- spec/jobs/email_csv_job_spec.rb | 6 +- spec/models/form/page_spec.rb | 9 + spec/models/form/question_spec.rb | 11 +- spec/models/form/subsection_spec.rb | 9 + spec/models/form_spec.rb | 9 + spec/models/lettings_log_spec.rb | 181 ++++- .../validations/date_validations_spec.rb | 24 +- .../validations/financial_validations_spec.rb | 2 +- .../sales/setup_validations_spec.rb | 20 +- .../validations/setup_validations_spec.rb | 102 ++- .../validations/soft_validations_spec.rb | 16 + .../validations/tenancy_validations_spec.rb | 20 + spec/requests/form_controller_spec.rb | 16 +- .../requests/lettings_logs_controller_spec.rb | 44 +- spec/requests/sales_logs_controller_spec.rb | 9 +- .../csv/lettings_log_csv_service_spec.rb | 2 +- .../lettings_log_export_service_spec.rb | 8 +- ...lettings_logs_field_import_service_spec.rb | 11 +- .../lettings_logs_import_service_spec.rb | 733 ++++++++++-------- spec/views/form/page_view_spec.rb | 9 + 46 files changed, 1175 insertions(+), 566 deletions(-) diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb index 4ba300552..12ae4473b 100644 --- a/app/models/form_handler.rb +++ b/app/models/form_handler.rb @@ -79,12 +79,12 @@ class FormHandler def lettings_in_crossover_period?(now: Time.zone.now) forms = lettings_forms.values - forms.count { |form| form.start_date < now && now < form.end_date } > 1 + forms.count { |form| now.between?(form.start_date, form.end_date) } > 1 end def sales_in_crossover_period?(now: Time.zone.now) forms = sales_forms.values - forms.count { |form| form.start_date < now && now < form.end_date } > 1 + forms.count { |form| now.between?(form.start_date, form.end_date) } > 1 end def use_fake_forms! diff --git a/app/models/validations/date_validations.rb b/app/models/validations/date_validations.rb index 66e472ba0..ee7789171 100644 --- a/app/models/validations/date_validations.rb +++ b/app/models/validations/date_validations.rb @@ -33,16 +33,6 @@ module Validations::DateValidations def validate_startdate(record) return unless record.startdate && date_valid?("startdate", record) - created_at = record.created_at || Time.zone.now - - if created_at > first_collection_end_date && record.startdate < second_collection_start_date - record.errors.add :startdate, I18n.t("validations.date.outside_collection_window") - end - - if (record.startdate < first_collection_start_date || record.startdate > second_collection_end_date) && FeatureToggle.startdate_collection_window_validation_enabled? - record.errors.add :startdate, I18n.t("validations.date.outside_collection_window") - end - if FeatureToggle.startdate_two_week_validation_enabled? && record.startdate > Time.zone.today + 14 record.errors.add :startdate, I18n.t("validations.setup.startdate.later_than_14_days_after") end @@ -68,22 +58,6 @@ module Validations::DateValidations private - def first_collection_start_date - @first_collection_start_date ||= FormHandler.instance.lettings_forms["previous_lettings"].start_date - end - - def first_collection_end_date - @first_collection_end_date ||= FormHandler.instance.lettings_forms["previous_lettings"].end_date - end - - def second_collection_start_date - @second_collection_start_date ||= FormHandler.instance.lettings_forms["current_lettings"].start_date - end - - def second_collection_end_date - @second_collection_end_date ||= FormHandler.instance.lettings_forms["current_lettings"].end_date - end - def is_rsnvac_first_let?(record) [15, 16, 17].include?(record["rsnvac"]) end diff --git a/app/models/validations/sales/setup_validations.rb b/app/models/validations/sales/setup_validations.rb index e7090aabf..e76993907 100644 --- a/app/models/validations/sales/setup_validations.rb +++ b/app/models/validations/sales/setup_validations.rb @@ -3,10 +3,10 @@ module Validations::Sales::SetupValidations include CollectionTimeHelper def validate_saledate(record) - return unless record.saledate && date_valid?("saledate", record) + return unless record.saledate && date_valid?("saledate", record) && FeatureToggle.saledate_collection_window_validation_enabled? - unless record.saledate.between?(active_collection_start_date, current_collection_end_date) || !FeatureToggle.saledate_collection_window_validation_enabled? - record.errors.add :saledate, validation_error_message + unless record.saledate.between?(active_collection_start_date, current_collection_end_date) + record.errors.add :saledate, saledate_validation_error_message end end @@ -20,12 +20,12 @@ private end end - def validation_error_message + def saledate_validation_error_message current_end_year_long = current_collection_end_date.strftime("#{current_collection_end_date.day.ordinalize} %B %Y") if FormHandler.instance.sales_in_crossover_period? I18n.t( - "validations.setup.saledate.previous_and_current_financial_year", + "validations.setup.saledate.previous_and_current_collection_year", previous_start_year_short: previous_collection_start_date.strftime("%y"), previous_end_year_short: previous_collection_end_date.strftime("%y"), previous_start_year_long: previous_collection_start_date.strftime("#{previous_collection_start_date.day.ordinalize} %B %Y"), @@ -34,7 +34,7 @@ private ) else I18n.t( - "validations.setup.saledate.current_financial_year", + "validations.setup.saledate.current_collection_year", current_start_year_short: current_collection_start_date.strftime("%y"), current_end_year_short: current_collection_end_date.strftime("%y"), current_start_year_long: current_collection_start_date.strftime("#{current_collection_start_date.day.ordinalize} %B %Y"), diff --git a/app/models/validations/setup_validations.rb b/app/models/validations/setup_validations.rb index 93105f9ea..0865ebae6 100644 --- a/app/models/validations/setup_validations.rb +++ b/app/models/validations/setup_validations.rb @@ -1,5 +1,14 @@ module Validations::SetupValidations include Validations::SharedValidations + include CollectionTimeHelper + + def validate_startdate_setup(record) + return unless record.startdate && date_valid?("startdate", record) && FeatureToggle.startdate_collection_window_validation_enabled? + + unless record.startdate.between?(active_collection_start_date, current_collection_end_date) + record.errors.add :startdate, startdate_validation_error_message + end + end def validate_irproduct_other(record) if intermediate_product_rent_type?(record) && record.irproduct_other.blank? @@ -27,6 +36,37 @@ module Validations::SetupValidations private + def active_collection_start_date + if FormHandler.instance.lettings_in_crossover_period? + previous_collection_start_date + else + current_collection_start_date + end + end + + def startdate_validation_error_message + current_end_year_long = current_collection_end_date.strftime("#{current_collection_end_date.day.ordinalize} %B %Y") + + if FormHandler.instance.lettings_in_crossover_period? + I18n.t( + "validations.setup.startdate.previous_and_current_collection_year", + previous_start_year_short: previous_collection_start_date.strftime("%y"), + previous_end_year_short: previous_collection_end_date.strftime("%y"), + previous_start_year_long: previous_collection_start_date.strftime("#{previous_collection_start_date.day.ordinalize} %B %Y"), + current_end_year_short: current_collection_end_date.strftime("%y"), + current_end_year_long:, + ) + else + I18n.t( + "validations.setup.startdate.current_collection_year", + current_start_year_short: current_collection_start_date.strftime("%y"), + current_end_year_short: current_collection_end_date.strftime("%y"), + current_start_year_long: current_collection_start_date.strftime("#{current_collection_start_date.day.ordinalize} %B %Y"), + current_end_year_long:, + ) + end + end + def intermediate_product_rent_type?(record) record.rent_type == 5 end diff --git a/config/initializers/feature_toggle.rb b/config/initializers/feature_toggle.rb index 0253bcc41..e00a7190b 100644 --- a/config/initializers/feature_toggle.rb +++ b/config/initializers/feature_toggle.rb @@ -4,11 +4,11 @@ class FeatureToggle Rails.env.production? || Rails.env.test? || Rails.env.staging? end - def self.startdate_two_week_validation_enabled? + def self.startdate_collection_window_validation_enabled? Rails.env.production? || Rails.env.test? || Rails.env.staging? end - def self.startdate_collection_window_validation_enabled? + def self.startdate_two_week_validation_enabled? Rails.env.production? || Rails.env.test? || Rails.env.staging? end diff --git a/config/locales/en.yml b/config/locales/en.yml index af33836a0..f44ba1d41 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -141,7 +141,7 @@ en: above_min: "%{field} must be at least %{min}" date: invalid_date: "Enter a date in the correct format, for example 31 1 2022" - outside_collection_window: Enter a date within the 22/23 financial year, which is between 1st April 2022 and 31st March 2023 + outside_collection_window: Enter a date within the 22/23 collection year, which is between 1st April 2022 and 31st March 2023 postcode: "Enter a postcode in the correct format, for example AA1 1AA" location_admin_district: "Select a local authority" email: @@ -155,14 +155,18 @@ en: intermediate_rent_product_name: blank: "Enter name of other intermediate rent product" saledate: - current_financial_year: - Enter a date within the %{current_start_year_short}/%{current_end_year_short} financial year, which is between %{current_start_year_long} and %{current_end_year_long} - previous_and_current_financial_year: - "Enter a date within the %{previous_start_year_short}/%{previous_end_year_short} or %{previous_end_year_short}/%{current_end_year_short} financial years, which is between %{previous_start_year_long} and %{current_end_year_long}" + current_collection_year: + Enter a date within the %{current_start_year_short}/%{current_end_year_short} collection year, which is between %{current_start_year_long} and %{current_end_year_long} + previous_and_current_collection_year: + "Enter a date within the %{previous_start_year_short}/%{previous_end_year_short} or %{previous_end_year_short}/%{current_end_year_short} collection years, which is between %{previous_start_year_long} and %{current_end_year_long}" type: percentage_bought_must_be_at_least_threshold: "The minimum increase in equity while staircasing is %{threshold}% for this shared ownership type" startdate: + current_collection_year: + Enter a date within the %{current_start_year_short}/%{current_end_year_short} collection year, which is between %{current_start_year_long} and %{current_end_year_long} + previous_and_current_collection_year: + "Enter a date within the %{previous_start_year_short}/%{previous_end_year_short} or %{previous_end_year_short}/%{current_end_year_short} collection years, which is between %{previous_start_year_long} and %{current_end_year_long}" later_than_14_days_after: "The tenancy start date must not be later than 14 days from today’s date" before_scheme_end_date: "The tenancy start date must be before the end date for this supported housing scheme" after_void_date: "Enter a tenancy start date that is after the void date" diff --git a/db/schema.rb b/db/schema.rb index daf421930..075cc4b5e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -557,9 +557,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_09_145740) do t.integer "ethnicbuy2" t.integer "proplen_asked" t.string "old_id" - t.integer "pregblank" t.integer "buy2living" t.integer "prevtenbuy2" + t.integer "pregblank" t.integer "nationalbuy2" t.string "uprn" t.integer "uprn_known" diff --git a/spec/components/log_summary_component_spec.rb b/spec/components/log_summary_component_spec.rb index f8edfb948..99015b6d6 100644 --- a/spec/components/log_summary_component_spec.rb +++ b/spec/components/log_summary_component_spec.rb @@ -5,7 +5,7 @@ RSpec.describe LogSummaryComponent, type: :component do let(:coordinator_user) { FactoryBot.create(:user) } let(:propcode) { "P3647" } let(:tenancycode) { "T62863" } - let(:lettings_log) { FactoryBot.create(:lettings_log, needstype: 1, startdate: Time.utc(2022, 1, 1), tenancycode:, propcode:) } + let(:lettings_log) { FactoryBot.create(:lettings_log, needstype: 1, tenancycode:, propcode:, startdate: Time.zone.today) } let(:sales_log) { FactoryBot.create(:sales_log) } context "when rendering lettings log for a support user" do @@ -16,8 +16,8 @@ RSpec.describe LogSummaryComponent, type: :component do expect(result).to have_text(lettings_log.tenancycode) expect(result).to have_text(lettings_log.propcode) expect(result).to have_text("General needs") - expect(result).to have_text("Tenancy starts 1 January 2022") - expect(result).to have_text("Created 8 February 2022") + expect(result).to have_text("Tenancy starts #{Time.zone.today.strftime('%e %B %Y')}") + expect(result).to have_text("Created #{Time.zone.today.strftime('%e %B %Y')}") expect(result).to have_text("by Danny Rojas") expect(result).to have_content("Owned by\n DLUHC") expect(result).to have_content("Managed by\n DLUHC") diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb index cb97eed68..b0667b817 100644 --- a/spec/factories/lettings_log.rb +++ b/spec/factories/lettings_log.rb @@ -7,7 +7,6 @@ FactoryBot.define do renewal { 0 } needstype { 1 } rent_type { 1 } - startdate { Time.zone.local(2022, 5, 1) } end trait :in_progress do status { 1 } @@ -18,7 +17,7 @@ FactoryBot.define do age2 { 19 } renewal { 1 } rent_type { 1 } - startdate { Time.zone.local(2021, 5, 1) } + startdate { Time.zone.today } end trait :soft_validations_triggered do status { 1 } @@ -36,6 +35,7 @@ FactoryBot.define do hhmemb { 1 } end trait :completed do + startdate { Time.zone.today } status { 2 } tenancycode { Faker::Name.initials(number: 10) } age1_known { 0 } @@ -66,7 +66,7 @@ FactoryBot.define do rsnvac { 6 } unittype_gn { 7 } beds { 3 } - voiddate { "03/11/2019" } + voiddate { 2.days.ago } offered { 2 } wchair { 1 } earnings { 68 } @@ -132,9 +132,8 @@ FactoryBot.define do hbrentshortfall { 1 } tshortfall { 12 } property_relet { 0 } - mrcdate { Time.zone.local(2020, 5, 5, 10, 36, 49) } + mrcdate { 1.day.ago } incref { 0 } - startdate { Time.zone.today } armedforces { 1 } builtype { 1 } unitletas { 2 } @@ -155,7 +154,7 @@ FactoryBot.define do sheltered { 0 } household_charge { 0 } end - created_at { Time.utc(2022, 2, 8, 16, 52, 15) } - updated_at { Time.utc(2022, 2, 8, 16, 52, 15) } + created_at { Time.zone.today } + updated_at { Time.zone.today } end end diff --git a/spec/features/form/accessible_autocomplete_spec.rb b/spec/features/form/accessible_autocomplete_spec.rb index 160d32d38..15d23fe32 100644 --- a/spec/features/form/accessible_autocomplete_spec.rb +++ b/spec/features/form/accessible_autocomplete_spec.rb @@ -2,6 +2,15 @@ require "rails_helper" require_relative "helpers" RSpec.describe "Accessible Autocomplete" do + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + include Helpers let(:user) { FactoryBot.create(:user) } let(:lettings_log) do @@ -64,8 +73,8 @@ RSpec.describe "Accessible Autocomplete" do let(:scheme) { FactoryBot.create(:scheme, owning_organisation_id: lettings_log.created_by.organisation_id, primary_client_group: "Q", secondary_client_group: "P") } before do - FactoryBot.create(:location, scheme:, postcode: "W6 0ST") - FactoryBot.create(:location, scheme:, postcode: "SE6 1LB") + FactoryBot.create(:location, scheme:, postcode: "W6 0ST", startdate: Time.zone.local(2022, 1, 1)) + FactoryBot.create(:location, scheme:, postcode: "SE6 1LB", startdate: Time.zone.local(2022, 1, 1)) lettings_log.update!(needstype: 2) visit("/lettings-logs/#{lettings_log.id}/scheme") end diff --git a/spec/features/form/check_answers_page_lettings_logs_spec.rb b/spec/features/form/check_answers_page_lettings_logs_spec.rb index 9062e1abc..97a9ccbec 100644 --- a/spec/features/form/check_answers_page_lettings_logs_spec.rb +++ b/spec/features/form/check_answers_page_lettings_logs_spec.rb @@ -2,6 +2,15 @@ require "rails_helper" require_relative "helpers" RSpec.describe "Lettings Log Check Answers Page" do + around do |example| + Timecop.freeze(Time.zone.local(2021, 5, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + include Helpers let(:user) { FactoryBot.create(:user) } let(:subsection) { "household-characteristics" } @@ -152,7 +161,7 @@ RSpec.describe "Lettings Log Check Answers Page" do context "when viewing setup section answers" do before do - FactoryBot.create(:location, scheme:) + FactoryBot.create(:location, scheme:, startdate: Time.zone.local(2021, 1, 1)) end it "displays inferred postcode with the location id" do diff --git a/spec/features/form/checkboxes_spec.rb b/spec/features/form/checkboxes_spec.rb index 056ff93cf..dae505bcf 100644 --- a/spec/features/form/checkboxes_spec.rb +++ b/spec/features/form/checkboxes_spec.rb @@ -14,6 +14,15 @@ RSpec.describe "Checkboxes" do end let(:id) { lettings_log.id } + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + before do allow(lettings_log.form).to receive(:end_date).and_return(Time.zone.today + 1.day) RequestHelper.stub_http_requests diff --git a/spec/features/form/conditional_questions_spec.rb b/spec/features/form/conditional_questions_spec.rb index 52d8886e0..f79b33191 100644 --- a/spec/features/form/conditional_questions_spec.rb +++ b/spec/features/form/conditional_questions_spec.rb @@ -2,6 +2,15 @@ require "rails_helper" require_relative "helpers" RSpec.describe "Form Conditional Questions" do + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + include Helpers let(:user) { FactoryBot.create(:user) } let(:lettings_log) do @@ -16,6 +25,7 @@ RSpec.describe "Form Conditional Questions" do :sales_log, :completed, created_by: user, + saledate: Time.zone.local(2022, 1, 1), ) end let(:id) { lettings_log.id } diff --git a/spec/features/form/form_navigation_spec.rb b/spec/features/form/form_navigation_spec.rb index 351675804..aa0223616 100644 --- a/spec/features/form/form_navigation_spec.rb +++ b/spec/features/form/form_navigation_spec.rb @@ -2,6 +2,15 @@ require "rails_helper" require_relative "helpers" RSpec.describe "Form Navigation" do + around do |example| + Timecop.travel(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + include Helpers let(:user) { FactoryBot.create(:user) } let(:lettings_log) do diff --git a/spec/features/form/page_routing_spec.rb b/spec/features/form/page_routing_spec.rb index eb4f6acc2..312e0752b 100644 --- a/spec/features/form/page_routing_spec.rb +++ b/spec/features/form/page_routing_spec.rb @@ -20,95 +20,106 @@ RSpec.describe "Form Page Routing" do sign_in user end - it "can route the user to a different page based on their answer on the current page", js: true do - visit("/lettings-logs/#{id}/conditional-question") - # using a question name that is already in the db to avoid - # having to add a new column to the db for this test - choose("lettings-log-preg-occ-1-field", allow_label_click: true) - click_button("Save and continue") - expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-yes-page") - click_link(text: "Back") - expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question") - choose("lettings-log-preg-occ-2-field", allow_label_click: true) - click_button("Save and continue") - expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-no-page") - end - - it "can route based on multiple conditions", js: true do - visit("/lettings-logs/#{id}/person-1-gender") - choose("lettings-log-sex1-f-field", allow_label_click: true) - click_button("Save and continue") - expect(page).to have_current_path("/lettings-logs/#{id}/person-1-working-situation") - visit("/lettings-logs/#{id}/conditional-question") - choose("lettings-log-preg-occ-2-field", allow_label_click: true) - click_button("Save and continue") - expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-no-page") - choose("lettings-log-cbl-0-field", allow_label_click: true) - click_button("Save and continue") - expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question/check-answers") - end + context "with 21/22 logs" do + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end - context "when the answers are inferred", js: true do - it "shows question if the answer could not be inferred" do - visit("/lettings-logs/#{id}/property-postcode") - fill_in("lettings-log-postcode-full-field", with: "PO5 3TE") + it "can route the user to a different page based on their answer on the current page", js: true do + visit("/lettings-logs/#{id}/conditional-question") + # using a question name that is already in the db to avoid + # having to add a new column to the db for this test + choose("lettings-log-preg-occ-1-field", allow_label_click: true) + click_button("Save and continue") + expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-yes-page") + click_link(text: "Back") + expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question") + choose("lettings-log-preg-occ-2-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/lettings-logs/#{id}/do-you-know-the-local-authority") + expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-no-page") end - it "shows question if the answer could not be inferred from an empty input" do - visit("/lettings-logs/#{id}/property-postcode") + it "can route based on multiple conditions", js: true do + visit("/lettings-logs/#{id}/person-1-gender") + choose("lettings-log-sex1-f-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/lettings-logs/#{id}/do-you-know-the-local-authority") + expect(page).to have_current_path("/lettings-logs/#{id}/person-1-working-situation") + visit("/lettings-logs/#{id}/conditional-question") + choose("lettings-log-preg-occ-2-field", allow_label_click: true) + click_button("Save and continue") + expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-no-page") + choose("lettings-log-cbl-0-field", allow_label_click: true) + click_button("Save and continue") + expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question/check-answers") end - it "does not show question if the answer could be inferred" do - stub_request(:get, /api.postcodes.io/) - .to_return(status: 200, body: "{\"status\":200,\"result\":{\"admin_district\":\"Manchester\", \"codes\":{\"admin_district\": \"E08000003\"}}}", headers: {}) + context "when the answers are inferred", js: true do + it "shows question if the answer could not be inferred" do + visit("/lettings-logs/#{id}/property-postcode") + fill_in("lettings-log-postcode-full-field", with: "PO5 3TE") + click_button("Save and continue") + expect(page).to have_current_path("/lettings-logs/#{id}/do-you-know-the-local-authority") + end - visit("/lettings-logs/#{id}/property-postcode") - fill_in("lettings-log-postcode-full-field", with: "P0 5ST") - click_button("Save and continue") - expect(page).to have_current_path("/lettings-logs/#{id}/property-wheelchair-accessible") - end - end + it "shows question if the answer could not be inferred from an empty input" do + visit("/lettings-logs/#{id}/property-postcode") + click_button("Save and continue") + expect(page).to have_current_path("/lettings-logs/#{id}/do-you-know-the-local-authority") + end - context "when answer is invalid" do - it "shows error with invalid value in the field" do - visit("/lettings-logs/#{id}/property-postcode") - fill_in("lettings-log-postcode-full-field", with: "fake_postcode") - click_button("Save and continue") + it "does not show question if the answer could be inferred" do + stub_request(:get, /api.postcodes.io/) + .to_return(status: 200, body: "{\"status\":200,\"result\":{\"admin_district\":\"Manchester\", \"codes\":{\"admin_district\": \"E08000003\"}}}", headers: {}) - expect(page).to have_current_path("/lettings-logs/#{id}/property-postcode") - expect(find("#lettings-log-postcode-full-field-error").value).to eq("fake_postcode") + visit("/lettings-logs/#{id}/property-postcode") + fill_in("lettings-log-postcode-full-field", with: "P0 5ST") + click_button("Save and continue") + expect(page).to have_current_path("/lettings-logs/#{id}/property-wheelchair-accessible") + end end - it "does not reset the displayed date" do - lettings_log.update!(startdate: "2021/10/13") - visit("/lettings-logs/#{id}/tenancy-start-date") - fill_in("lettings_log[startdate(1i)]", with: "202") - fill_in("lettings_log[startdate(2i)]", with: "32") - fill_in("lettings_log[startdate(3i)]", with: "0") - click_button("Save and continue") + context "when answer is invalid" do + it "shows error with invalid value in the field" do + visit("/lettings-logs/#{id}/property-postcode") + fill_in("lettings-log-postcode-full-field", with: "fake_postcode") + click_button("Save and continue") - expect(page).to have_current_path("/lettings-logs/#{id}/tenancy-start-date") - expect(find_field("lettings_log[startdate(3i)]").value).to eq("13") - expect(find_field("lettings_log[startdate(2i)]").value).to eq("10") - expect(find_field("lettings_log[startdate(1i)]").value).to eq("2021") - end + expect(page).to have_current_path("/lettings-logs/#{id}/property-postcode") + expect(find("#lettings-log-postcode-full-field-error").value).to eq("fake_postcode") + end - it "does not reset the displayed date if it's empty" do - lettings_log.update!(startdate: nil) - visit("/lettings-logs/#{id}/tenancy-start-date") - fill_in("lettings_log[startdate(1i)]", with: "202") - fill_in("lettings_log[startdate(2i)]", with: "32") - fill_in("lettings_log[startdate(3i)]", with: "0") - click_button("Save and continue") + it "does not reset the displayed date" do + lettings_log.update!(startdate: "2021/10/13") + visit("/lettings-logs/#{id}/tenancy-start-date") + fill_in("lettings_log[startdate(1i)]", with: "202") + fill_in("lettings_log[startdate(2i)]", with: "32") + fill_in("lettings_log[startdate(3i)]", with: "0") + click_button("Save and continue") + + expect(page).to have_current_path("/lettings-logs/#{id}/tenancy-start-date") + expect(find_field("lettings_log[startdate(3i)]").value).to eq("13") + expect(find_field("lettings_log[startdate(2i)]").value).to eq("10") + expect(find_field("lettings_log[startdate(1i)]").value).to eq("2021") + end - expect(page).to have_current_path("/lettings-logs/#{id}/tenancy-start-date") - expect(find_field("lettings_log[startdate(3i)]").value).to eq(nil) - expect(find_field("lettings_log[startdate(2i)]").value).to eq(nil) - expect(find_field("lettings_log[startdate(1i)]").value).to eq(nil) + it "does not reset the displayed date if it's empty" do + lettings_log.update!(startdate: nil) + visit("/lettings-logs/#{id}/tenancy-start-date") + fill_in("lettings_log[startdate(1i)]", with: "202") + fill_in("lettings_log[startdate(2i)]", with: "32") + fill_in("lettings_log[startdate(3i)]", with: "0") + click_button("Save and continue") + + expect(page).to have_current_path("/lettings-logs/#{id}/tenancy-start-date") + expect(find_field("lettings_log[startdate(3i)]").value).to eq(nil) + expect(find_field("lettings_log[startdate(2i)]").value).to eq(nil) + expect(find_field("lettings_log[startdate(1i)]").value).to eq(nil) + end end end diff --git a/spec/features/form/progressive_total_field_spec.rb b/spec/features/form/progressive_total_field_spec.rb index 787174c20..cbf50f206 100644 --- a/spec/features/form/progressive_total_field_spec.rb +++ b/spec/features/form/progressive_total_field_spec.rb @@ -12,6 +12,15 @@ RSpec.describe "Accessible Autocomplete" do ) end + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + before do allow(lettings_log.form).to receive(:end_date).and_return(Time.zone.today + 1.day) sign_in user diff --git a/spec/features/form/saving_data_spec.rb b/spec/features/form/saving_data_spec.rb index 7a7996457..0c7397511 100644 --- a/spec/features/form/saving_data_spec.rb +++ b/spec/features/form/saving_data_spec.rb @@ -2,6 +2,15 @@ require "rails_helper" require_relative "helpers" RSpec.describe "Form Saving Data" do + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + include Helpers let(:user) { FactoryBot.create(:user) } let(:lettings_log) do diff --git a/spec/features/form/tasklist_page_spec.rb b/spec/features/form/tasklist_page_spec.rb index 09d332583..ba06a4051 100644 --- a/spec/features/form/tasklist_page_spec.rb +++ b/spec/features/form/tasklist_page_spec.rb @@ -36,18 +36,32 @@ RSpec.describe "Task List" do :about_completed, owning_organisation: user.organisation, managing_organisation: user.organisation, - startdate: Time.zone.local(2021, 5, 1), created_by: user, ) end let(:id) { lettings_log.id } let(:status) { lettings_log.status } + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + before do + Timecop.freeze(Time.zone.local(2021, 5, 1)) + setup_completed_log.update!(startdate: Time.zone.local(2021, 5, 1)) allow(lettings_log.form).to receive(:end_date).and_return(Time.zone.today + 1.day) sign_in user end + after do + Timecop.unfreeze + end + it "shows if the section has not been started" do visit("/lettings-logs/#{empty_lettings_log.id}") expect(page).to have_content("This log has not been started.") diff --git a/spec/features/form/validations_spec.rb b/spec/features/form/validations_spec.rb index 24b2816ce..12e5bb657 100644 --- a/spec/features/form/validations_spec.rb +++ b/spec/features/form/validations_spec.rb @@ -2,6 +2,15 @@ require "rails_helper" require_relative "helpers" RSpec.describe "validations" do + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } let(:user) { FactoryBot.create(:user) } let(:lettings_log) do @@ -26,6 +35,8 @@ RSpec.describe "validations" do status: 1, declaration: nil, startdate: Time.zone.local(2021, 5, 1), + voiddate: Time.zone.local(2021, 5, 1), + mrcdate: Time.zone.local(2021, 5, 1), ) end let(:id) { lettings_log.id } diff --git a/spec/fixtures/exports/general_needs_log.csv b/spec/fixtures/exports/general_needs_log.csv index 8cc656f19..0185f54e0 100644 --- a/spec/fixtures/exports/general_needs_log.csv +++ b/spec/fixtures/exports/general_needs_log.csv @@ -1,2 +1,2 @@ status,tenancycode,age1,sex1,ethnic,national,prevten,ecstat1,hhmemb,age2,sex2,ecstat2,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,leftreg,reservist,illness,preg_occ,startertenancy,tenancylength,tenancy,ppostcode_full,rsnvac,unittype_gn,beds,offered,wchair,earnings,incfreq,benefits,period,layear,waityear,postcode_full,reasonpref,cbl,chr,cap,reasonother,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,illness_type_1,illness_type_2,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,irproduct_other,reason,propcode,la,prevloc,hb,hbrentshortfall,mrcdate,incref,startdate,armedforces,unitletas,builtype,voiddate,renttype,needstype,lettype,totchild,totelder,totadult,nocharge,referral,brent,scharge,pscharge,supcharg,tcharge,tshortfall,chcharge,ppcodenk,has_benefits,renewal,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat2,relat3,relat4,relat5,relat6,relat7,relat8,lar,irproduct,joint,sheltered,hhtype,new_old,vacdays,formid,owningorgid,owningorgname,hcnum,maningorgid,maningorgname,manhcnum,createddate,uploaddate -2,BZ737,35,F,2,4,6,0,2,32,M,6,,,,,,,,,,,,,,,,,,,1,4,1,1,1,2,1,5,1,SE2 6RT,6,7,3,2,1,68,1,1,2,2,1,NW1 5TY,1,2,1,2,,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,,,4,123,E09000003,E07000105,6,1,2020-05-05T10:36:49+01:00,0,2022-02-02T10:36:49+00:00,1,2,1,2019-11-03T00:00:00+00:00,2,1,7,0,0,2,0,2,200.0,50.0,40.0,35.0,325.0,12.0,,1,1,0,100.0,25.0,20.0,17.5,162.5,6.0,0,1,,2,P,,,,,,,,,,,4,2,638,{id},{owning_org_id},DLUHC,1234,{managing_org_id},DLUHC,1234,2022-02-08T16:52:15+00:00,2022-02-08T16:52:15+00:00 +2,BZ737,35,F,2,4,6,0,2,32,M,6,,,,,,,,,,,,,,,,,,,1,4,1,1,1,2,1,5,1,SE2 6RT,6,7,3,2,1,68,1,1,2,2,1,NW1 5TY,1,2,1,2,,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,,,4,123,E09000003,E07000105,6,1,2020-05-05T10:36:49+01:00,0,2022-02-02T10:36:49+00:00,1,2,1,2019-11-03T00:00:00+00:00,2,1,7,0,0,2,0,2,200.0,50.0,40.0,35.0,325.0,12.0,,1,1,0,100.0,25.0,20.0,17.5,162.5,6.0,0,1,,2,P,,,,,,,,,,,4,2,638,{id},{owning_org_id},DLUHC,1234,{managing_org_id},DLUHC,1234,2022-05-01T00:00:00+01:00,2022-05-01T00:00:00+01:00 diff --git a/spec/fixtures/exports/general_needs_log.xml b/spec/fixtures/exports/general_needs_log.xml index 62542af54..f05de09e3 100644 --- a/spec/fixtures/exports/general_needs_log.xml +++ b/spec/fixtures/exports/general_needs_log.xml @@ -142,8 +142,8 @@ {managing_org_id} DLUHC 1234 - 2022-02-08T16:52:15+00:00 - 2022-02-08T16:52:15+00:00 + 2022-05-01T00:00:00+01:00 + 2022-05-01T00:00:00+01:00 1 diff --git a/spec/fixtures/exports/supported_housing_logs.xml b/spec/fixtures/exports/supported_housing_logs.xml index 69f52161c..0a0ed0981 100644 --- a/spec/fixtures/exports/supported_housing_logs.xml +++ b/spec/fixtures/exports/supported_housing_logs.xml @@ -141,8 +141,8 @@ {managing_org_id} DLUHC 1234 - 2022-02-08T16:52:15+00:00 - 2022-02-08T16:52:15+00:00 + 2022-05-01T00:00:00+01:00 + 2022-05-01T00:00:00+01:00 7 1 G diff --git a/spec/helpers/check_answers_helper_spec.rb b/spec/helpers/check_answers_helper_spec.rb index d60c79f51..716c93827 100644 --- a/spec/helpers/check_answers_helper_spec.rb +++ b/spec/helpers/check_answers_helper_spec.rb @@ -6,6 +6,15 @@ RSpec.describe CheckAnswersHelper do let(:lettings_log) { FactoryBot.build(:lettings_log, :in_progress) } let(:current_user) { FactoryBot.build(:user) } + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + describe "display_answered_questions_summary" do context "when a section hasn't been completed yet" do it "returns that you have unanswered questions" do diff --git a/spec/helpers/form_page_error_helper_spec.rb b/spec/helpers/form_page_error_helper_spec.rb index e2b342a40..ae9d22494 100644 --- a/spec/helpers/form_page_error_helper_spec.rb +++ b/spec/helpers/form_page_error_helper_spec.rb @@ -3,6 +3,15 @@ require "rails_helper" RSpec.describe FormPageErrorHelper do describe "#remove_other_page_errors" do context "when non base other questions are removed" do + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + let!(:lettings_log) { FactoryBot.create(:lettings_log, :in_progress) } let!(:form) { lettings_log.form } diff --git a/spec/helpers/interruption_screen_helper_spec.rb b/spec/helpers/interruption_screen_helper_spec.rb index f46aca9ae..fba8782da 100644 --- a/spec/helpers/interruption_screen_helper_spec.rb +++ b/spec/helpers/interruption_screen_helper_spec.rb @@ -17,6 +17,15 @@ RSpec.describe InterruptionScreenHelper do ) end + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + describe "display_informative_text" do context "when 2 out of 2 arguments are given" do it "returns correct informative text" do diff --git a/spec/helpers/tasklist_helper_spec.rb b/spec/helpers/tasklist_helper_spec.rb index f0cec71ad..5d75616ec 100644 --- a/spec/helpers/tasklist_helper_spec.rb +++ b/spec/helpers/tasklist_helper_spec.rb @@ -1,7 +1,7 @@ require "rails_helper" RSpec.describe TasklistHelper do - let(:now) { Time.utc(2022, 6, 1) } + let(:now) { Time.utc(2022, 1, 1) } around do |example| Timecop.freeze(now) do @@ -13,7 +13,7 @@ RSpec.describe TasklistHelper do describe "with lettings" do let(:empty_lettings_log) { create(:lettings_log) } - let(:lettings_log) { build(:lettings_log, :in_progress, needstype: 1) } + let(:lettings_log) { build(:lettings_log, :in_progress, needstype: 1, startdate: now) } describe "get next incomplete section" do it "returns the first subsection name if it is not completed" do @@ -27,8 +27,14 @@ RSpec.describe TasklistHelper do end describe "get sections count" do + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:get_form).and_return(fake_2021_2022_form) + end + it "returns the total of sections if no status is given" do - expect(get_subsections_count(empty_lettings_log)).to eq(1) + expect(get_subsections_count(empty_lettings_log)).to eq(8) end it "returns 0 sections for completed sections if no sections are completed" do @@ -36,7 +42,7 @@ RSpec.describe TasklistHelper do end it "returns the number of not started sections" do - expect(get_subsections_count(empty_lettings_log, :not_started)).to eq(1) + expect(get_subsections_count(empty_lettings_log, :not_started)).to eq(8) end it "returns the number of sections in progress" do @@ -48,41 +54,6 @@ RSpec.describe TasklistHelper do end end - describe "review_log_text" do - context "when collection_period_open? == true" do - context "with 2023 deadline" do - let(:now) { Time.utc(2022, 6, 1) } - let(:lettings_log) { create(:lettings_log, :completed) } - - it "returns relevant text" do - expect(review_log_text(lettings_log)).to eq( - "You can #{govuk_link_to 'review and make changes to this log', review_lettings_log_path(lettings_log)} until 1 July 2023.".html_safe, - ) - end - end - - context "with 2024 deadline" do - let(:now) { Time.utc(2023, 6, 20) } - let(:lettings_log) { create(:lettings_log, :completed, national: 18, waityear: 2) } - - it "returns relevant text" do - expect(review_log_text(lettings_log)).to eq( - "You can #{govuk_link_to 'review and make changes to this log', review_lettings_log_path(lettings_log)} until 9 July 2024.".html_safe, - ) - end - end - end - - context "when collection_period_open? == false" do - let(:now) { Time.utc(2023, 7, 8) } - let(:lettings_log) { create(:lettings_log, :completed, startdate: Time.utc(2023, 2, 8)) } - - it "returns relevant text" do - expect(review_log_text(lettings_log)).to eq("This log is from the 2022/2023 collection window, which is now closed.") - end - end - end - describe "subsection link" do let(:lettings_log) { create(:lettings_log, :completed) } let(:subsection) { lettings_log.form.get_subsection("household_characteristics") } @@ -130,5 +101,42 @@ RSpec.describe TasklistHelper do end end end + + context "with lettings log" do + context "when collection_period_open? == true" do + context "with 2023 deadline" do + let(:now) { Time.utc(2022, 6, 1) } + let(:lettings_log) { create(:lettings_log, :completed) } + + it "returns relevant text" do + expect(review_log_text(lettings_log)).to eq( + "You can #{govuk_link_to 'review and make changes to this log', review_lettings_log_path(lettings_log)} until 1 July 2023.".html_safe, + ) + end + end + + context "with 2024 deadline" do + let(:now) { Time.utc(2023, 6, 20) } + let(:lettings_log) { create(:lettings_log, :completed, national: 18, waityear: 2) } + + it "returns relevant text" do + expect(review_log_text(lettings_log)).to eq( + "You can #{govuk_link_to 'review and make changes to this log', review_lettings_log_path(lettings_log)} until 9 July 2024.".html_safe, + ) + end + end + end + + context "when collection_period_open? == false" do + let(:now) { Time.utc(2022, 6, 1) } + let!(:sales_log) { create(:lettings_log, :completed) } + + it "returns relevant text" do + Timecop.freeze(now + 1.year) do + expect(review_log_text(sales_log)).to eq("This log is from the 2021/2022 collection window, which is now closed.") + end + end + end + end end end diff --git a/spec/jobs/email_csv_job_spec.rb b/spec/jobs/email_csv_job_spec.rb index 5ff0b3355..04776aac7 100644 --- a/spec/jobs/email_csv_job_spec.rb +++ b/spec/jobs/email_csv_job_spec.rb @@ -31,7 +31,9 @@ describe EmailCsvJob do FactoryBot.create(:lettings_log, :completed, created_by: user, - startdate: Time.zone.local(2021, 5, 1)) + startdate: Time.zone.local(2022, 5, 1), + voiddate: Time.zone.local(2022, 5, 1), + mrcdate: Time.zone.local(2022, 5, 1)) allow(Storage::S3Service).to receive(:new).and_return(storage_service) allow(storage_service).to receive(:write_file) @@ -104,7 +106,7 @@ describe EmailCsvJob do it "writes answer labels rather than values" do expect_csv do |csv| - expect(csv.second[16]).to eq("Full-time – 30 hours or more") + expect(csv.second[19]).to eq("Full-time – 30 hours or more") end job.perform(user) diff --git a/spec/models/form/page_spec.rb b/spec/models/form/page_spec.rb index 4a870fe46..17bd7dcc9 100644 --- a/spec/models/form/page_spec.rb +++ b/spec/models/form/page_spec.rb @@ -3,6 +3,15 @@ require "rails_helper" RSpec.describe Form::Page, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + let(:user) { FactoryBot.create(:user) } let(:lettings_log) { FactoryBot.build(:lettings_log) } let(:form) { lettings_log.form } diff --git a/spec/models/form/question_spec.rb b/spec/models/form/question_spec.rb index 48e71c76b..ede6636f7 100644 --- a/spec/models/form/question_spec.rb +++ b/spec/models/form/question_spec.rb @@ -3,6 +3,15 @@ require "rails_helper" RSpec.describe Form::Question, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + let(:lettings_log) { FactoryBot.build(:lettings_log) } let(:form) { lettings_log.form } let(:section_id) { "rent_and_charges" } @@ -367,7 +376,7 @@ RSpec.describe Form::Question, type: :model do end context "when Sales form" do - let(:sales_log) { FactoryBot.create(:sales_log, :completed, ethnic_group: 17) } + let(:sales_log) { FactoryBot.create(:sales_log, :completed, ethnic_group: 17, saledate: Time.zone.local(2022, 1, 1)) } let(:question) { sales_log.form.get_question("ethnic_group", sales_log) } it "returns the inferred label value" do diff --git a/spec/models/form/subsection_spec.rb b/spec/models/form/subsection_spec.rb index 46880a33a..3e224dd38 100644 --- a/spec/models/form/subsection_spec.rb +++ b/spec/models/form/subsection_spec.rb @@ -4,6 +4,15 @@ require_relative "../../request_helper" RSpec.describe Form::Subsection, type: :model do subject(:subsection) { described_class.new(subsection_id, subsection_definition, section) } + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + let(:lettings_log) { FactoryBot.build(:lettings_log) } let(:form) { lettings_log.form } let(:section_id) { "household" } diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb index cd58587dc..cac779e33 100644 --- a/spec/models/form_spec.rb +++ b/spec/models/form_spec.rb @@ -1,6 +1,15 @@ require "rails_helper" RSpec.describe Form, type: :model do + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + let(:user) { FactoryBot.build(:user) } let(:lettings_log) { FactoryBot.build(:lettings_log, :in_progress) } let(:form) { lettings_log.form } diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index b35ab5bb1..e2431ff92 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -6,6 +6,16 @@ RSpec.describe LettingsLog do let(:created_by_user) { create(:user) } let(:owning_organisation) { created_by_user.organisation } let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + let(:fake_2022_2023_form) { Form.new("spec/fixtures/forms/2022_2023.json") } + + around do |example| + Timecop.freeze(Time.utc(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end before do allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) @@ -33,7 +43,15 @@ RSpec.describe LettingsLog do let(:lettings_log_2) { build(:lettings_log, startdate: Time.zone.local(2022, 1, 1), created_by: created_by_user) } let(:lettings_log_year_2) { build(:lettings_log, startdate: Time.zone.local(2023, 5, 1), created_by: created_by_user) } - it "has returns the correct form based on the start date" do + before do + Timecop.freeze(2023, 1, 1) + end + + after do + Timecop.unfreeze + end + + it "returns the correct form based on the start date" do expect(lettings_log.form_name).to be_nil expect(lettings_log.form).to be_a(Form) expect(lettings_log_2.form_name).to eq("previous_lettings") @@ -1477,10 +1495,15 @@ RSpec.describe LettingsLog do context "when deriving renttype and unitletas" do before do + Timecop.freeze(Time.zone.local(2022, 1, 1)) allow(FeatureToggle).to receive(:startdate_two_week_validation_enabled?).and_return(false) lettings_log.update!(rent_type:, irproduct_other: "other") end + after do + Timecop.unfreeze + end + context "when the rent_type is Social Rent (0)" do let(:rent_type) { 0 } @@ -1497,6 +1520,23 @@ RSpec.describe LettingsLog do end context "and it is a 23/24 form" do + before do + Timecop.freeze(Time.zone.local(2023, 5, 1)) + end + + after do + Timecop.unfreeze + end + + around do |example| + Timecop.freeze(Time.zone.local(2023, 5, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + it "derives and saves unitletas as Social rent(1)" do lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] @@ -1522,6 +1562,23 @@ RSpec.describe LettingsLog do end context "and it is a 23/24 form" do + before do + Timecop.freeze(Time.zone.local(2023, 5, 1)) + end + + after do + Timecop.unfreeze + end + + around do |example| + Timecop.freeze(Time.zone.local(2023, 5, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + it "derives and saves unitletas as Affordable Rent basis(2)" do lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] @@ -1547,6 +1604,23 @@ RSpec.describe LettingsLog do end context "and it is a 23/24 form" do + before do + Timecop.freeze(Time.zone.local(2023, 5, 1)) + end + + after do + Timecop.unfreeze + end + + around do |example| + Timecop.freeze(Time.zone.local(2023, 5, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + it "derives and saves unitletas as London Affordable Rent basis(5)" do lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] @@ -1572,6 +1646,23 @@ RSpec.describe LettingsLog do end context "and it is a 23/24 form" do + before do + Timecop.freeze(Time.zone.local(2023, 5, 1)) + end + + after do + Timecop.unfreeze + end + + around do |example| + Timecop.freeze(Time.zone.local(2023, 5, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + it "derives and saves unitletas as Rent to Buy basis(6)" do lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] @@ -1597,6 +1688,23 @@ RSpec.describe LettingsLog do end context "and it is a 23/24 form" do + before do + Timecop.freeze(Time.zone.local(2023, 5, 1)) + end + + after do + Timecop.unfreeze + end + + around do |example| + Timecop.freeze(Time.zone.local(2023, 5, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + it "derives and saves unitletas as London Living Rent basis(7)" do lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] @@ -1622,6 +1730,23 @@ RSpec.describe LettingsLog do end context "and it is a 23/24 form" do + before do + Timecop.freeze(Time.zone.local(2023, 5, 1)) + end + + after do + Timecop.unfreeze + end + + around do |example| + Timecop.freeze(Time.zone.local(2023, 5, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + it "derives and saves unitletas as Other intermediate rent basis(8)" do lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] @@ -1863,7 +1988,11 @@ RSpec.describe LettingsLog do let(:scheme) { create(:scheme) } let!(:location) { create(:location, scheme:) } - before { lettings_log.update!(startdate: Time.zone.local(2022, 4, 2), scheme:) } + before do + Timecop.freeze(Time.zone.local(2022, 4, 2)) + lettings_log.update!(startdate: Time.zone.local(2022, 4, 2), scheme:) + Timecop.unfreeze + end it "derives the scheme location" do record_from_db = ActiveRecord::Base.connection.execute("select location_id from lettings_logs where id=#{lettings_log.id}").to_a[0] @@ -1950,9 +2079,16 @@ RSpec.describe LettingsLog do end context "and renewal" do + before do + Timecop.freeze(Time.zone.local(2022, 4, 2)) + end + + after do + Timecop.unfreeze + end + let(:scheme) { create(:scheme) } let(:location) { create(:location, scheme:) } - let!(:supported_housing_lettings_log) do described_class.create!({ managing_organisation: owning_organisation, @@ -1962,12 +2098,12 @@ RSpec.describe LettingsLog do scheme_id: scheme.id, location_id: location.id, renewal: 1, - startdate: Time.zone.now, + startdate: Time.zone.local(2022, 4, 2), created_at: Time.utc(2022, 2, 8, 16, 52, 15), }) end - it "correcly infers and saves the renewal date" do + it "correctly infers and saves the renewal date" do record_from_db = ActiveRecord::Base.connection.execute("SELECT voiddate from lettings_logs where id=#{supported_housing_lettings_log.id}").to_a[0] expect(record_from_db["voiddate"].to_i).to eq(supported_housing_lettings_log.startdate.to_i) end @@ -2333,6 +2469,15 @@ RSpec.describe LettingsLog do end context "and the new location triggers the rent range validation" do + around do |example| + Timecop.freeze(Time.zone.local(2022, 4, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + it "clears rent values" do lettings_log.update!(location:, scheme:) lettings_log.reload @@ -2467,8 +2612,8 @@ RSpec.describe LettingsLog do end describe "scopes" do - let!(:lettings_log_1) { create(:lettings_log, :in_progress, startdate: Time.utc(2021, 5, 3), created_by: created_by_user) } - let!(:lettings_log_2) { create(:lettings_log, :completed, startdate: Time.utc(2021, 5, 3), created_by: created_by_user) } + let!(:lettings_log_1) { create(:lettings_log, :in_progress, startdate: Time.utc(2021, 5, 3), mrcdate: Time.utc(2021, 5, 3), voiddate: Time.utc(2021, 5, 3), created_by: created_by_user) } + let!(:lettings_log_2) { create(:lettings_log, :completed, startdate: Time.utc(2021, 5, 3), mrcdate: Time.utc(2021, 5, 3), voiddate: Time.utc(2021, 5, 3), created_by: created_by_user) } before do Timecop.freeze(Time.utc(2022, 6, 3)) @@ -2755,7 +2900,7 @@ RSpec.describe LettingsLog do context "with values represented as human readable labels" do before do Timecop.freeze(Time.utc(2022, 6, 5)) - lettings_log = FactoryBot.create(:lettings_log, needstype: 2, scheme:, location:, owning_organisation: scheme.owning_organisation, created_by: user, rent_type: 2, startdate: Time.zone.local(2021, 10, 2)) + lettings_log = FactoryBot.create(:lettings_log, needstype: 2, scheme:, location:, owning_organisation: scheme.owning_organisation, created_by: user, rent_type: 2, startdate: Time.zone.local(2021, 10, 2), created_at: Time.zone.local(2022, 2, 8, 16, 52, 15), updated_at: Time.zone.local(2022, 2, 8, 16, 52, 15)) expected_content.sub!(/\{id\}/, lettings_log["id"].to_s) expected_content.sub!(/\{scheme_code\}/, "S#{scheme['id']}") expected_content.sub!(/\{scheme_service_name\}/, scheme["service_name"].to_s) @@ -2770,6 +2915,15 @@ RSpec.describe LettingsLog do expected_content.sub!(/\{location_id\}/, location["id"].to_s) end + around do |example| + Timecop.freeze(Time.zone.local(2022, 6, 5)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + context "with a support user" do let(:csv_export_file) { File.open("spec/fixtures/files/lettings_logs_download.csv", "r:UTF-8") } @@ -2790,7 +2944,7 @@ RSpec.describe LettingsLog do context "with values represented as codes" do before do Timecop.freeze(Time.utc(2022, 6, 5)) - lettings_log = FactoryBot.create(:lettings_log, needstype: 2, scheme:, location:, owning_organisation: scheme.owning_organisation, created_by: user, rent_type: 2, startdate: Time.zone.local(2021, 10, 2)) + lettings_log = FactoryBot.create(:lettings_log, needstype: 2, scheme:, location:, owning_organisation: scheme.owning_organisation, created_by: user, rent_type: 2, startdate: Time.zone.local(2021, 10, 2), created_at: Time.zone.local(2022, 2, 8, 16, 52, 15), updated_at: Time.zone.local(2022, 2, 8, 16, 52, 15)) expected_content.sub!(/\{id\}/, lettings_log["id"].to_s) expected_content.sub!(/\{scheme_code\}/, "S#{scheme.id}") expected_content.sub!(/\{scheme_service_name\}/, scheme.service_name.to_s) @@ -2807,6 +2961,15 @@ RSpec.describe LettingsLog do let(:csv_export_file) { File.open("spec/fixtures/files/lettings_logs_download_codes_only.csv", "r:UTF-8") } + around do |example| + Timecop.freeze(Time.zone.local(2022, 6, 5)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + it "generates a correct csv from a lettings log" do expect(described_class.to_csv(codes_only_export: true)).to eq(expected_content) end diff --git a/spec/models/validations/date_validations_spec.rb b/spec/models/validations/date_validations_spec.rb index 20d1e094c..cd6e38f50 100644 --- a/spec/models/validations/date_validations_spec.rb +++ b/spec/models/validations/date_validations_spec.rb @@ -9,18 +9,6 @@ RSpec.describe Validations::DateValidations do let(:scheme_no_end_date) { FactoryBot.create(:scheme, end_date: nil) } describe "tenancy start date" do - it "cannot be before the first collection window start date" do - record.startdate = Time.zone.local(2020, 1, 1) - date_validator.validate_startdate(record) - expect(record.errors["startdate"]).to include(match I18n.t("validations.date.outside_collection_window")) - end - - it "cannot be after the second collection window end date" do - record.startdate = Time.zone.local(2023, 7, 1, 6) - date_validator.validate_startdate(record) - expect(record.errors["startdate"]).to include(match I18n.t("validations.date.outside_collection_window")) - end - it "must be a valid date" do record.startdate = Time.zone.local(0, 7, 1) date_validator.validate_startdate(record) @@ -98,7 +86,7 @@ RSpec.describe Validations::DateValidations do record.location = location date_validator.validate_startdate(record) expect(record.errors["startdate"]) - .to include(match I18n.t("validations.setup.startdate.location.deactivated", postcode: location.postcode, date: "4 June 2022")) + .to include(match I18n.t("validations.setup.startdate.location.deactivated", postcode: location.postcode, date: "4 June 2022")) end it "produces no error when tenancy start date is during an active location period" do @@ -123,7 +111,7 @@ RSpec.describe Validations::DateValidations do record.location = location date_validator.validate_startdate(record) expect(record.errors["startdate"]) - .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 August 2022")) + .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 August 2022")) end it "produces no error when tenancy start date is during an active location period" do @@ -150,7 +138,7 @@ RSpec.describe Validations::DateValidations do record.location = location date_validator.validate_startdate(record) expect(record.errors["startdate"]) - .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 September 2022")) + .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 September 2022")) end it "produces no error when tenancy start date is during an active location period" do @@ -177,7 +165,7 @@ RSpec.describe Validations::DateValidations do record.location = location date_validator.validate_startdate(record) expect(record.errors["startdate"]) - .to include(match I18n.t("validations.setup.startdate.location.activating_soon", postcode: location.postcode, date: "15 September 2022")) + .to include(match I18n.t("validations.setup.startdate.location.activating_soon", postcode: location.postcode, date: "15 September 2022")) end end @@ -194,7 +182,7 @@ RSpec.describe Validations::DateValidations do record.scheme = scheme date_validator.validate_startdate(record) expect(record.errors["startdate"]) - .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 August 2022")) + .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 August 2022")) end it "produces no error when tenancy start date is during an active scheme period" do @@ -220,7 +208,7 @@ RSpec.describe Validations::DateValidations do record.scheme = scheme date_validator.validate_startdate(record) expect(record.errors["startdate"]) - .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 September 2022")) + .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 September 2022")) end it "produces no error when tenancy start date is during an active scheme period" do diff --git a/spec/models/validations/financial_validations_spec.rb b/spec/models/validations/financial_validations_spec.rb index bdd76cded..c2fe3b963 100644 --- a/spec/models/validations/financial_validations_spec.rb +++ b/spec/models/validations/financial_validations_spec.rb @@ -86,7 +86,7 @@ RSpec.describe Validations::FinancialValidations do end context "when outstanding rent or charges is yes" do - let(:record) { FactoryBot.create(:lettings_log, :about_completed) } + let(:record) { FactoryBot.create(:lettings_log, :about_completed, startdate: Time.zone.now) } it "expects that a shortfall is provided" do record.hbrentshortfall = 1 diff --git a/spec/models/validations/sales/setup_validations_spec.rb b/spec/models/validations/sales/setup_validations_spec.rb index e5c563c9e..136e239d3 100644 --- a/spec/models/validations/sales/setup_validations_spec.rb +++ b/spec/models/validations/sales/setup_validations_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Validations::Sales::SetupValidations do end end - context "when saledate is in the 22/23 financial year" do + context "when saledate is in the 22/23 collection year" do let(:record) { build(:sales_log, saledate: Time.zone.local(2023, 1, 1)) } it "does not add an error" do @@ -27,23 +27,23 @@ RSpec.describe Validations::Sales::SetupValidations do end end - context "when saledate is before the 22/23 financial year" do + context "when saledate is before the 22/23 collection year" do let(:record) { build(:sales_log, saledate: Time.zone.local(2020, 1, 1)) } it "adds error" do setup_validator.validate_saledate(record) - expect(record.errors[:saledate]).to include("Enter a date within the 22/23 financial year, which is between 1st April 2022 and 31st March 2023") + expect(record.errors[:saledate]).to include("Enter a date within the 22/23 collection year, which is between 1st April 2022 and 31st March 2023") end end - context "when saledate is after the 22/23 financial year" do + context "when saledate is after the 22/23 collection year" do let(:record) { build(:sales_log, saledate: Time.zone.local(2025, 4, 1)) } it "adds error" do setup_validator.validate_saledate(record) - expect(record.errors[:saledate]).to include("Enter a date within the 22/23 financial year, which is between 1st April 2022 and 31st March 2023") + expect(record.errors[:saledate]).to include("Enter a date within the 22/23 collection year, which is between 1st April 2022 and 31st March 2023") end end end @@ -67,7 +67,7 @@ RSpec.describe Validations::Sales::SetupValidations do end end - context "when saledate is in the 22/23 financial year" do + context "when saledate is in the 22/23 collection year" do let(:record) { build(:sales_log, saledate: Time.zone.local(2024, 1, 1)) } it "does not add an error" do @@ -77,23 +77,23 @@ RSpec.describe Validations::Sales::SetupValidations do end end - context "when saledate is before the 22/23 financial year" do + context "when saledate is before the 22/23 collection year" do let(:record) { build(:sales_log, saledate: Time.zone.local(2020, 5, 1)) } it "adds error" do setup_validator.validate_saledate(record) - expect(record.errors[:saledate]).to include("Enter a date within the 23/24 or 24/25 financial years, which is between 1st April 2023 and 31st March 2025") + expect(record.errors[:saledate]).to include("Enter a date within the 23/24 or 24/25 collection years, which is between 1st April 2023 and 31st March 2025") end end - context "when saledate is after the 22/23 financial year" do + context "when saledate is after the 22/23 collection year" do let(:record) { build(:sales_log, saledate: Time.zone.local(2025, 4, 1)) } it "adds error" do setup_validator.validate_saledate(record) - expect(record.errors[:saledate]).to include("Enter a date within the 23/24 or 24/25 financial years, which is between 1st April 2023 and 31st March 2025") + expect(record.errors[:saledate]).to include("Enter a date within the 23/24 or 24/25 collection years, which is between 1st April 2023 and 31st March 2025") end end end diff --git a/spec/models/validations/setup_validations_spec.rb b/spec/models/validations/setup_validations_spec.rb index 326a6936d..69e22f1e2 100644 --- a/spec/models/validations/setup_validations_spec.rb +++ b/spec/models/validations/setup_validations_spec.rb @@ -6,13 +6,95 @@ RSpec.describe Validations::SetupValidations do let(:setup_validator_class) { Class.new { include Validations::SetupValidations } } let(:record) { FactoryBot.create(:lettings_log) } + describe "tenancy start date" do + context "when in 22/23 collection" do + context "when in the crossover period" do + before do + allow(Time).to receive(:now).and_return(Time.zone.local(2022, 4, 1)) + record.created_at = Time.zone.local(2022, 4, 1) + end + + it "cannot be before the first collection window start date" do + record.startdate = Time.zone.local(2021, 1, 1) + setup_validator.validate_startdate_setup(record) + expect(record.errors["startdate"]).to include(match "Enter a date within the 21/22 or 22/23 collection years, which is between 1st April 2021 and 31st March 2023") + end + + it "cannot be after the second collection window end date" do + record.startdate = Time.zone.local(2023, 7, 1, 6) + setup_validator.validate_startdate_setup(record) + expect(record.errors["startdate"]).to include(match "Enter a date within the 21/22 or 22/23 collection years, which is between 1st April 2021 and 31st March 2023") + end + end + + context "when after the crossover period" do + before do + allow(Time).to receive(:now).and_return(Time.zone.local(2023, 1, 1)) + record.created_at = Time.zone.local(2023, 1, 1) + end + + it "cannot be before the first collection window start date" do + record.startdate = Time.zone.local(2022, 1, 1) + setup_validator.validate_startdate_setup(record) + expect(record.errors["startdate"]).to include(match "Enter a date within the 22/23 collection year, which is between 1st April 2022 and 31st March 2023") + end + + it "cannot be after the second collection window end date" do + record.startdate = Time.zone.local(2023, 7, 1, 6) + setup_validator.validate_startdate_setup(record) + expect(record.errors["startdate"]).to include(match "Enter a date within the 22/23 collection year, which is between 1st April 2022 and 31st March 2023") + end + end + end + + context "when in 23/24 collection" do + context "when in the crossover period" do + before do + allow(Time).to receive(:now).and_return(Time.zone.local(2023, 4, 1)) + record.created_at = Time.zone.local(2023, 4, 1) + end + + it "cannot be before the first collection window start date" do + record.startdate = Time.zone.local(2022, 1, 1) + setup_validator.validate_startdate_setup(record) + expect(record.errors["startdate"]).to include(match "Enter a date within the 22/23 or 23/24 collection years, which is between 1st April 2022 and 31st March 2024") + end + + it "cannot be after the second collection window end date" do + record.startdate = Time.zone.local(2024, 7, 1, 6) + setup_validator.validate_startdate_setup(record) + expect(record.errors["startdate"]).to include(match "Enter a date within the 22/23 or 23/24 collection years, which is between 1st April 2022 and 31st March 2024") + end + end + + context "when after the crossover period" do + before do + allow(Time).to receive(:now).and_return(Time.zone.local(2024, 1, 1)) + record.created_at = Time.zone.local(2024, 1, 1) + end + + it "cannot be before the first collection window start date" do + record.startdate = Time.zone.local(2023, 1, 1) + setup_validator.validate_startdate_setup(record) + expect(record.errors["startdate"]).to include(match "Enter a date within the 23/24 collection year, which is between 1st April 2023 and 31st March 2024") + end + + it "cannot be after the second collection window end date" do + record.startdate = Time.zone.local(2024, 7, 1, 6) + setup_validator.validate_startdate_setup(record) + expect(record.errors["startdate"]).to include(match "Enter a date within the 23/24 collection year, which is between 1st April 2023 and 31st March 2024") + end + end + end + end + describe "#validate_irproduct" do it "adds an error when the intermediate rent product name is not provided but the rent type was given as other intermediate rent product" do record.rent_type = 5 record.irproduct_other = nil setup_validator.validate_irproduct_other(record) expect(record.errors["irproduct_other"]) - .to include(match I18n.t("validations.setup.intermediate_rent_product_name.blank")) + .to include(match I18n.t("validations.setup.intermediate_rent_product_name.blank")) end it "adds an error when the intermediate rent product name is blank but the rent type was given as other intermediate rent product" do @@ -20,7 +102,7 @@ RSpec.describe Validations::SetupValidations do record.irproduct_other = "" setup_validator.validate_irproduct_other(record) expect(record.errors["irproduct_other"]) - .to include(match I18n.t("validations.setup.intermediate_rent_product_name.blank")) + .to include(match I18n.t("validations.setup.intermediate_rent_product_name.blank")) end it "Does not add an error when the intermediate rent product name is provided and the rent type was given as other intermediate rent product" do @@ -46,7 +128,7 @@ RSpec.describe Validations::SetupValidations do record.location = location setup_validator.validate_scheme(record) expect(record.errors["scheme_id"]) - .to include(match I18n.t("validations.setup.startdate.location.deactivated", postcode: location.postcode, date: "4 June 2022")) + .to include(match I18n.t("validations.setup.startdate.location.deactivated", postcode: location.postcode, date: "4 June 2022")) end it "produces no error when tenancy start date is during an active location period" do @@ -71,7 +153,7 @@ RSpec.describe Validations::SetupValidations do record.location = location setup_validator.validate_scheme(record) expect(record.errors["scheme_id"]) - .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 August 2022")) + .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 August 2022")) end it "produces no error when tenancy start date is during an active location period" do @@ -98,7 +180,7 @@ RSpec.describe Validations::SetupValidations do record.location = location setup_validator.validate_scheme(record) expect(record.errors["scheme_id"]) - .to include(match I18n.t("validations.setup.startdate.location.activating_soon", postcode: location.postcode, date: "15 September 2022")) + .to include(match I18n.t("validations.setup.startdate.location.activating_soon", postcode: location.postcode, date: "15 September 2022")) end end @@ -115,7 +197,7 @@ RSpec.describe Validations::SetupValidations do record.scheme = scheme setup_validator.validate_scheme(record) expect(record.errors["scheme_id"]) - .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 August 2022")) + .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 August 2022")) end it "produces no error when tenancy start date is during an active scheme period" do @@ -141,7 +223,7 @@ RSpec.describe Validations::SetupValidations do record.scheme = scheme setup_validator.validate_scheme(record) expect(record.errors["scheme_id"]) - .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 September 2022")) + .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 September 2022")) end it "produces no error when tenancy start date is during an active scheme period" do @@ -168,7 +250,7 @@ RSpec.describe Validations::SetupValidations do record.location = location setup_validator.validate_location(record) expect(record.errors["location_id"]) - .to include(match I18n.t("validations.setup.startdate.location.deactivated", postcode: location.postcode, date: "4 June 2022")) + .to include(match I18n.t("validations.setup.startdate.location.deactivated", postcode: location.postcode, date: "4 June 2022")) end it "produces no error when tenancy start date is during an active location period" do @@ -193,7 +275,7 @@ RSpec.describe Validations::SetupValidations do record.location = location setup_validator.validate_location(record) expect(record.errors["location_id"]) - .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 August 2022")) + .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 August 2022")) end it "produces no error when tenancy start date is during an active location period" do @@ -220,7 +302,7 @@ RSpec.describe Validations::SetupValidations do record.location = location setup_validator.validate_location(record) expect(record.errors["location_id"]) - .to include(match I18n.t("validations.setup.startdate.location.activating_soon", postcode: location.postcode, date: "15 September 2022")) + .to include(match I18n.t("validations.setup.startdate.location.activating_soon", postcode: location.postcode, date: "15 September 2022")) end end end diff --git a/spec/models/validations/soft_validations_spec.rb b/spec/models/validations/soft_validations_spec.rb index 9ca9436fb..3ce2b4527 100644 --- a/spec/models/validations/soft_validations_spec.rb +++ b/spec/models/validations/soft_validations_spec.rb @@ -207,6 +207,14 @@ RSpec.describe Validations::SoftValidations do end describe "major repairs date soft validations" do + before do + Timecop.freeze(Time.zone.local(2022, 2, 1)) + end + + after do + Timecop.unfreeze + end + context "when the major repairs date is within 10 years of the tenancy start date" do it "shows the interruption screen" do record.update!(startdate: Time.zone.local(2022, 2, 1), mrcdate: Time.zone.local(2013, 2, 1)) @@ -223,6 +231,14 @@ RSpec.describe Validations::SoftValidations do end describe "void date soft validations" do + before do + Timecop.freeze(Time.zone.local(2022, 2, 1)) + end + + after do + Timecop.unfreeze + end + context "when the void date is within 10 years of the tenancy start date" do it "shows the interruption screen" do record.update!(startdate: Time.zone.local(2022, 2, 1), voiddate: Time.zone.local(2013, 2, 1)) diff --git a/spec/models/validations/tenancy_validations_spec.rb b/spec/models/validations/tenancy_validations_spec.rb index 2d008e6e3..8f7d79e29 100644 --- a/spec/models/validations/tenancy_validations_spec.rb +++ b/spec/models/validations/tenancy_validations_spec.rb @@ -3,6 +3,10 @@ require "rails_helper" RSpec.describe Validations::TenancyValidations do subject(:tenancy_validator) { validator_class.new } + before do + Timecop.freeze(Time.zone.local(2021, 5, 1)) + end + let(:validator_class) { Class.new { include Validations::TenancyValidations } } let(:record) { FactoryBot.create(:lettings_log, startdate: Time.zone.local(2021, 5, 1), needstype: 1, rent_type: 1) } @@ -117,6 +121,14 @@ RSpec.describe Validations::TenancyValidations do end context "when the collection start year is 2022 or later" do + before do + Timecop.freeze(2022, 5, 1) + end + + after do + Timecop.unfreeze + end + let(:record) { FactoryBot.create(:lettings_log, startdate: Time.zone.local(2022, 5, 1), needstype: 1, rent_type: 1) } context "when type of tenancy is Secure - fixed term" do @@ -260,6 +272,14 @@ RSpec.describe Validations::TenancyValidations do describe "joint tenancy validation" do context "when the data inputter has said that there is only one member in the household" do + before do + 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(:hhmemb_expected_error) { I18n.t("validations.tenancy.joint_more_than_one_member") } diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb index d5a8dbcf8..9dce39af2 100644 --- a/spec/requests/form_controller_spec.rb +++ b/spec/requests/form_controller_spec.rb @@ -17,7 +17,6 @@ RSpec.describe FormController, type: :request do :lettings_log, :about_completed, status: 1, - startdate: Time.zone.local(2021, 10, 10), created_by: user, ) end @@ -26,7 +25,6 @@ RSpec.describe FormController, type: :request do :lettings_log, :completed, created_by: user, - startdate: Time.zone.local(2021, 5, 1), ) end let(:headers) { { "Accept" => "text/html" } } @@ -204,10 +202,13 @@ RSpec.describe FormController, type: :request do describe "GET" do context "with form pages" do context "when forms exist for multiple years" do - let(:lettings_log_year_1) { create(:lettings_log, startdate: Time.zone.local(2021, 5, 1), owning_organisation: organisation, created_by: user) } + let(:lettings_log_year_1) { create(:lettings_log, owning_organisation: organisation, created_by: user) } let(:lettings_log_year_2) { create(:lettings_log, :about_completed, startdate: Time.zone.local(2022, 5, 1), owning_organisation: organisation, created_by: user) } before do + Timecop.freeze(Time.zone.local(2021, 5, 1)) + lettings_log_year_1.update!(startdate: Time.zone.local(2021, 5, 1)) + Timecop.unfreeze allow(lettings_log_year_1.form).to receive(:end_date).and_return(Time.zone.today + 1.day) end @@ -663,6 +664,15 @@ RSpec.describe FormController, type: :request do end let(:referrer) { "/lettings-logs/#{completed_lettings_log.id}/net-income-value-check?referrer=check_answers" } + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + before do completed_lettings_log.update!(ecstat1: 1, earnings: 130, hhmemb: 1) # we're not routing to that page, so it gets cleared? allow(completed_lettings_log).to receive(:net_income_soft_validation_triggered?).and_return(true) diff --git a/spec/requests/lettings_logs_controller_spec.rb b/spec/requests/lettings_logs_controller_spec.rb index 116c88c4c..c5ee2a6ab 100644 --- a/spec/requests/lettings_logs_controller_spec.rb +++ b/spec/requests/lettings_logs_controller_spec.rb @@ -333,6 +333,13 @@ RSpec.describe LettingsLogsController, type: :request do end context "with year filter" do + around do |example| + Timecop.freeze(2022, 3, 1) do + example.run + end + Timecop.unfreeze + end + let!(:lettings_log_2021) do FactoryBot.create(:lettings_log, :in_progress, created_by: user, @@ -364,6 +371,8 @@ RSpec.describe LettingsLogsController, type: :request do context "with year and status filter" do before do + Timecop.freeze(Time.zone.local(2022, 3, 1)) + lettings_log_2021.update!(startdate: Time.zone.local(2022, 3, 1)) Timecop.freeze(Time.zone.local(2022, 12, 1)) end @@ -374,7 +383,6 @@ RSpec.describe LettingsLogsController, type: :request do let!(:lettings_log_2021) do FactoryBot.create(:lettings_log, :in_progress, owning_organisation: organisation, - startdate: Time.zone.local(2022, 3, 1), managing_organisation: organisation, created_by: user) end @@ -383,6 +391,7 @@ RSpec.describe LettingsLogsController, type: :request do owning_organisation: organisation, mrcdate: Time.zone.local(2022, 2, 1), startdate: Time.zone.local(2022, 12, 1), + voiddate: Time.zone.local(2022, 2, 1), tenancy: 6, managing_organisation: organisation, created_by: user) @@ -846,6 +855,10 @@ RSpec.describe LettingsLogsController, type: :request do before do sign_in user get "/lettings-logs/#{lettings_log.id}", headers:, params: {} + Timecop.freeze(2021, 4, 1) + completed_lettings_log.update!(startdate: Time.zone.local(2021, 4, 1), voiddate: Time.zone.local(2021, 4, 1), mrcdate: Time.zone.local(2021, 4, 1)) + completed_lettings_log.reload + Timecop.unfreeze end it "shows the tasklist for lettings logs you have access to" do @@ -871,9 +884,6 @@ RSpec.describe LettingsLogsController, type: :request do end it "displays a closed collection window message for previous collection year logs" do - completed_lettings_log.update!(startdate: Time.zone.local(2021, 4, 1)) - completed_lettings_log.reload - get "/lettings-logs/#{completed_lettings_log.id}", headers:, params: {} expect(completed_lettings_log.form.end_date).to eq(Time.zone.local(2022, 7, 1)) expect(completed_lettings_log.status).to eq("completed") @@ -940,19 +950,22 @@ RSpec.describe LettingsLogsController, type: :request do end context "when accessing the check answers page" do + before do + Timecop.freeze(2021, 4, 1) + completed_lettings_log.update!(startdate: Time.zone.local(2021, 4, 1), voiddate: Time.zone.local(2021, 4, 1), mrcdate: Time.zone.local(2021, 4, 1)) + Timecop.unfreeze + stub_request(:get, /api.postcodes.io/) + .to_return(status: 200, body: "{\"status\":200,\"result\":{\"admin_district\":\"Manchester\", \"codes\":{\"admin_district\": \"E08000003\"}}}", headers: {}) + sign_in user + end + let(:postcode_lettings_log) do FactoryBot.create(:lettings_log, created_by: user, postcode_known: "No") end let(:id) { postcode_lettings_log.id } - let(:completed_lettings_log) { FactoryBot.create(:lettings_log, :completed, owning_organisation: user.organisation, managing_organisation: user.organisation, created_by: user, startdate: Time.zone.local(2021, 4, 1)) } - - before do - stub_request(:get, /api.postcodes.io/) - .to_return(status: 200, body: "{\"status\":200,\"result\":{\"admin_district\":\"Manchester\", \"codes\":{\"admin_district\": \"E08000003\"}}}", headers: {}) - sign_in user - end + let(:completed_lettings_log) { FactoryBot.create(:lettings_log, :completed, owning_organisation: user.organisation, managing_organisation: user.organisation, created_by: user) } it "shows the inferred la" do lettings_log = FactoryBot.create(:lettings_log, @@ -1154,6 +1167,15 @@ RSpec.describe LettingsLogsController, type: :request do end context "with an invalid lettings log params" do + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + let(:params) { { age1: 200 } } it "returns 422" do diff --git a/spec/requests/sales_logs_controller_spec.rb b/spec/requests/sales_logs_controller_spec.rb index e45fee440..1d68b045b 100644 --- a/spec/requests/sales_logs_controller_spec.rb +++ b/spec/requests/sales_logs_controller_spec.rb @@ -231,11 +231,10 @@ RSpec.describe SalesLogsController, type: :request do end context "with year and status filter" do - before do - Timecop.freeze(Time.zone.local(2022, 12, 1)) - end - - after do + around do |example| + Timecop.freeze(2022, 12, 1) do + example.run + end Timecop.unfreeze end diff --git a/spec/services/csv/lettings_log_csv_service_spec.rb b/spec/services/csv/lettings_log_csv_service_spec.rb index 3cc244456..b1064acd5 100644 --- a/spec/services/csv/lettings_log_csv_service_spec.rb +++ b/spec/services/csv/lettings_log_csv_service_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Csv::LettingsLogCsvService do let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json") } before do - LettingsLog.create!(startdate: "2021-10-10", created_at: Time.utc(2022, 2, 8, 16, 52, 15)) + LettingsLog.create!(startdate: Time.zone.today, created_at: Time.utc(2022, 2, 8, 16, 52, 15)) allow(FormHandler.instance).to receive(:get_form).and_return(real_2021_2022_form) end diff --git a/spec/services/exports/lettings_log_export_service_spec.rb b/spec/services/exports/lettings_log_export_service_spec.rb index 55897c354..7c17f95dc 100644 --- a/spec/services/exports/lettings_log_export_service_spec.rb +++ b/spec/services/exports/lettings_log_export_service_spec.rb @@ -59,7 +59,7 @@ RSpec.describe Exports::LettingsLogExportService do end context "and one lettings log is available for export" do - let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenancycode: "BZ737", startdate: Time.utc(2022, 2, 2, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) } + let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenancycode: "BZ737", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) } it "generates a ZIP export file with the expected filename" do expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) @@ -161,7 +161,7 @@ RSpec.describe Exports::LettingsLogExportService do context "when this is a second export (partial)" do before do - start_time = Time.zone.local(2022, 4, 1) + start_time = Time.zone.local(2022, 6, 1) LogsExport.new(started_at: start_time).save! end @@ -237,7 +237,7 @@ RSpec.describe Exports::LettingsLogExportService do let(:csv_export_file) { File.open("spec/fixtures/exports/general_needs_log.csv", "r:UTF-8") } let(:expected_csv_filename) { "export_2022_05_01.csv" } - let(:lettings_log) { FactoryBot.create(:lettings_log, :completed, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenancycode: "BZ737", startdate: Time.utc(2022, 2, 2, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) } + let(:lettings_log) { FactoryBot.create(:lettings_log, :completed, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenancycode: "BZ737", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) } it "generates an CSV export file with the expected content" do expected_content = replace_entity_ids(lettings_log, csv_export_file.read) @@ -256,7 +256,7 @@ RSpec.describe Exports::LettingsLogExportService do let(:scheme) { FactoryBot.create(:scheme, :export, owning_organisation: organisation) } let(:location) { FactoryBot.create(:location, :export, scheme:, startdate: Time.zone.local(2021, 4, 1)) } - let(:lettings_log) { FactoryBot.create(:lettings_log, :completed, :export, :sh, scheme:, location:, created_by: user, owning_organisation: organisation, startdate: Time.utc(2022, 2, 2, 10, 36, 49), underoccupation_benefitcap: 4, sheltered: 1) } + let(:lettings_log) { FactoryBot.create(:lettings_log, :completed, :export, :sh, scheme:, location:, created_by: user, owning_organisation: organisation, startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), underoccupation_benefitcap: 4, sheltered: 1) } before do FactoryBot.create(:location, scheme:, startdate: Time.zone.local(2021, 4, 1), units: nil) diff --git a/spec/services/imports/lettings_logs_field_import_service_spec.rb b/spec/services/imports/lettings_logs_field_import_service_spec.rb index 685617041..0c55139e8 100644 --- a/spec/services/imports/lettings_logs_field_import_service_spec.rb +++ b/spec/services/imports/lettings_logs_field_import_service_spec.rb @@ -16,6 +16,15 @@ RSpec.describe Imports::LettingsLogsFieldImportService do let(:old_user_id) { "c3061a2e6ea0b702e6f6210d5c52d2a92612d2aa" } let(:organisation) { FactoryBot.create(:organisation, old_visible_id: "1", provider_type: "PRP") } + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + def open_file(directory, filename) File.open("#{directory}/#{filename}.xml") end @@ -27,7 +36,7 @@ RSpec.describe Imports::LettingsLogsFieldImportService do FactoryBot.create(:user, old_user_id:, organisation:) # Stub the form handler to use the real form - allow(FormHandler.instance).to receive(:get_form).with("previous_lettings").and_return(real_2021_2022_form) + allow(FormHandler.instance).to receive(:get_form).with("current_lettings").and_return(real_2021_2022_form) WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/LS166FT/) .to_return(status: 200, body: '{"status":200,"result":{"codes":{"admin_district":"E08000035"}}}', headers: {}) diff --git a/spec/services/imports/lettings_logs_import_service_spec.rb b/spec/services/imports/lettings_logs_import_service_spec.rb index 846c266a5..bfd907620 100644 --- a/spec/services/imports/lettings_logs_import_service_spec.rb +++ b/spec/services/imports/lettings_logs_import_service_spec.rb @@ -1,348 +1,333 @@ require "rails_helper" RSpec.describe Imports::LettingsLogsImportService do - subject(:lettings_log_service) { described_class.new(storage_service, logger) } + context "with 21/22 logs" do + subject(:lettings_log_service) { described_class.new(storage_service, logger) } - let(:storage_service) { instance_double(Storage::S3Service) } - let(:logger) { instance_double(ActiveSupport::Logger) } - - let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json") } - let(:real_2022_2023_form) { Form.new("config/forms/2022_2023.json") } - let(:fixture_directory) { "spec/fixtures/imports/logs" } - - let(:organisation) { FactoryBot.create(:organisation, old_visible_id: "1", provider_type: "PRP") } - let(:managing_organisation) { FactoryBot.create(:organisation, old_visible_id: "2", provider_type: "PRP") } - let(:scheme1) { FactoryBot.create(:scheme, old_visible_id: "0123", owning_organisation: organisation) } - let(:scheme2) { FactoryBot.create(:scheme, old_visible_id: "456", owning_organisation: organisation) } - - def open_file(directory, filename) - File.open("#{directory}/#{filename}.xml") - end - - before do - WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/LS166FT/) - .to_return(status: 200, body: '{"status":200,"result":{"admin_district":"Westminster","codes":{"admin_district":"E08000035"}}}', headers: {}) - - allow(Organisation).to receive(:find_by).and_return(nil) - allow(Organisation).to receive(:find_by).with(old_visible_id: organisation.old_visible_id).and_return(organisation) - allow(Organisation).to receive(:find_by).with(old_visible_id: managing_organisation.old_visible_id).and_return(managing_organisation) - - # Created by users - FactoryBot.create(:user, old_user_id: "c3061a2e6ea0b702e6f6210d5c52d2a92612d2aa", organisation:) - FactoryBot.create(:user, old_user_id: "e29c492473446dca4d50224f2bb7cf965a261d6f", organisation:) - - # Location setup - FactoryBot.create(:location, old_visible_id: "10", postcode: "LS166FT", scheme_id: scheme1.id, mobility_type: "W", startdate: Time.zone.local(2021, 4, 1)) - FactoryBot.create(:location, scheme_id: scheme1.id, startdate: Time.zone.local(2021, 4, 1)) - FactoryBot.create(:location, old_visible_id: "10", postcode: "LS166FT", scheme_id: scheme2.id, mobility_type: "W", startdate: Time.zone.local(2021, 4, 1)) - - # Stub the form handler to use the real form - allow(FormHandler.instance).to receive(:get_form).with("previous_lettings").and_return(real_2021_2022_form) - allow(FormHandler.instance).to receive(:get_form).with("current_lettings").and_return(real_2022_2023_form) - end + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end - context "when importing lettings logs" do - let(:remote_folder) { "lettings_logs" } - let(:lettings_log_id) { "0ead17cb-1668-442d-898c-0d52879ff592" } - let(:lettings_log_id2) { "166fc004-392e-47a8-acb8-1c018734882b" } - let(:lettings_log_id3) { "00d2343e-d5fa-4c89-8400-ec3854b0f2b4" } - let(:lettings_log_id4) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } - let(:sales_log) { "shared_ownership_sales_log" } + let(:storage_service) { instance_double(Storage::S3Service) } + let(:logger) { instance_double(ActiveSupport::Logger) } - before do - # Stub the S3 file listing and download - allow(storage_service).to receive(:list_files) - .and_return(%W[#{remote_folder}/#{lettings_log_id}.xml #{remote_folder}/#{lettings_log_id2}.xml #{remote_folder}/#{lettings_log_id3}.xml #{remote_folder}/#{lettings_log_id4}.xml #{remote_folder}/#{sales_log}.xml]) - allow(storage_service).to receive(:get_file_io) - .with("#{remote_folder}/#{lettings_log_id}.xml") - .and_return(open_file(fixture_directory, lettings_log_id), open_file(fixture_directory, lettings_log_id)) - allow(storage_service).to receive(:get_file_io) - .with("#{remote_folder}/#{lettings_log_id2}.xml") - .and_return(open_file(fixture_directory, lettings_log_id2), open_file(fixture_directory, lettings_log_id2)) - allow(storage_service).to receive(:get_file_io) - .with("#{remote_folder}/#{lettings_log_id3}.xml") - .and_return(open_file(fixture_directory, lettings_log_id3), open_file(fixture_directory, lettings_log_id3)) - allow(storage_service).to receive(:get_file_io) - .with("#{remote_folder}/#{lettings_log_id4}.xml") - .and_return(open_file(fixture_directory, lettings_log_id4), open_file(fixture_directory, lettings_log_id4)) - allow(storage_service).to receive(:get_file_io) - .with("#{remote_folder}/#{sales_log}.xml") - .and_return(open_file(fixture_directory, sales_log), open_file(fixture_directory, sales_log)) - end + let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json") } + let(:real_2022_2023_form) { Form.new("config/forms/2022_2023.json") } + let(:fixture_directory) { "spec/fixtures/imports/logs" } - it "successfully create all lettings logs" do - expect(logger).not_to receive(:error) - expect(logger).not_to receive(:warn) - expect(logger).not_to receive(:info) - expect { lettings_log_service.create_logs(remote_folder) } - .to change(LettingsLog, :count).by(4) - end + let(:organisation) { FactoryBot.create(:organisation, old_visible_id: "1", provider_type: "PRP") } + let(:managing_organisation) { FactoryBot.create(:organisation, old_visible_id: "2", provider_type: "PRP") } + let(:scheme1) { FactoryBot.create(:scheme, old_visible_id: "0123", owning_organisation: organisation) } + let(:scheme2) { FactoryBot.create(:scheme, old_visible_id: "456", owning_organisation: organisation) } - it "only updates existing lettings logs" do - expect(logger).not_to receive(:error) - expect(logger).not_to receive(:warn) - expect(logger).to receive(:info).with(/Updating lettings log/).exactly(4).times - expect { 2.times { lettings_log_service.create_logs(remote_folder) } } - .to change(LettingsLog, :count).by(4) + def open_file(directory, filename) + File.open("#{directory}/#{filename}.xml") end - it "creates organisation relationship once" do - expect(logger).not_to receive(:error) - expect(logger).not_to receive(:warn) - expect { lettings_log_service.create_logs(remote_folder) } - .to change(OrganisationRelationship, :count).by(1) + before do + WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/LS166FT/) + .to_return(status: 200, body: '{"status":200,"result":{"admin_district":"Westminster","codes":{"admin_district":"E08000035"}}}', headers: {}) + + allow(Organisation).to receive(:find_by).and_return(nil) + allow(Organisation).to receive(:find_by).with(old_visible_id: organisation.old_visible_id).and_return(organisation) + allow(Organisation).to receive(:find_by).with(old_visible_id: managing_organisation.old_visible_id).and_return(managing_organisation) + + # Created by users + FactoryBot.create(:user, old_user_id: "c3061a2e6ea0b702e6f6210d5c52d2a92612d2aa", organisation:) + FactoryBot.create(:user, old_user_id: "e29c492473446dca4d50224f2bb7cf965a261d6f", organisation:) + + # Location setup + FactoryBot.create(:location, old_visible_id: "10", postcode: "LS166FT", scheme_id: scheme1.id, mobility_type: "W", startdate: Time.zone.local(2021, 4, 1)) + FactoryBot.create(:location, scheme_id: scheme1.id, startdate: Time.zone.local(2021, 4, 1)) + FactoryBot.create(:location, old_visible_id: "10", postcode: "LS166FT", scheme_id: scheme2.id, mobility_type: "W", startdate: Time.zone.local(2021, 4, 1)) + + # Stub the form handler to use the real form + allow(FormHandler.instance).to receive(:get_form).with("current_lettings").and_return(real_2021_2022_form) + allow(FormHandler.instance).to receive(:get_form).with("previous_lettings").and_return(real_2021_2022_form) + allow(FormHandler.instance).to receive(:get_form).with("next_lettings").and_return(real_2022_2023_form) end - context "when there are status discrepancies" do - let(:lettings_log_id5) { "893ufj2s-lq77-42m4-rty6-ej09gh585uy1" } - let(:lettings_log_id6) { "5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd" } - let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id5) } - let(:lettings_log_xml) { Nokogiri::XML(lettings_log_file) } + context "when importing lettings logs" do + let(:remote_folder) { "lettings_logs" } + let(:lettings_log_id) { "0ead17cb-1668-442d-898c-0d52879ff592" } + let(:lettings_log_id2) { "166fc004-392e-47a8-acb8-1c018734882b" } + let(:lettings_log_id3) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } + let(:sales_log) { "shared_ownership_sales_log" } before do + # Stub the S3 file listing and download + allow(storage_service).to receive(:list_files) + .and_return(%W[#{remote_folder}/#{lettings_log_id}.xml #{remote_folder}/#{lettings_log_id2}.xml #{remote_folder}/#{lettings_log_id3}.xml #{remote_folder}/#{sales_log}.xml]) allow(storage_service).to receive(:get_file_io) - .with("#{remote_folder}/#{lettings_log_id5}.xml") - .and_return(open_file(fixture_directory, lettings_log_id5), open_file(fixture_directory, lettings_log_id5)) + .with("#{remote_folder}/#{lettings_log_id}.xml") + .and_return(open_file(fixture_directory, lettings_log_id), open_file(fixture_directory, lettings_log_id)) allow(storage_service).to receive(:get_file_io) - .with("#{remote_folder}/#{lettings_log_id6}.xml") - .and_return(open_file(fixture_directory, lettings_log_id6), open_file(fixture_directory, lettings_log_id6)) + .with("#{remote_folder}/#{lettings_log_id2}.xml") + .and_return(open_file(fixture_directory, lettings_log_id2), open_file(fixture_directory, lettings_log_id2)) + allow(storage_service).to receive(:get_file_io) + .with("#{remote_folder}/#{lettings_log_id3}.xml") + .and_return(open_file(fixture_directory, lettings_log_id3), open_file(fixture_directory, lettings_log_id3)) + allow(storage_service).to receive(:get_file_io) + .with("#{remote_folder}/#{sales_log}.xml") + .and_return(open_file(fixture_directory, sales_log), open_file(fixture_directory, sales_log)) end - it "the logger logs a warning with the lettings log's old id/filename" do - expect(logger).to receive(:warn).with(/is not completed/).once - expect(logger).to receive(:warn).with(/lettings log with old id:#{lettings_log_id5} is incomplete but status should be complete/).once - - lettings_log_service.send(:create_log, lettings_log_xml) + it "successfully create all lettings logs" do + expect(logger).not_to receive(:error) + expect(logger).not_to receive(:warn) + expect(logger).not_to receive(:info) + expect { lettings_log_service.create_logs(remote_folder) } + .to change(LettingsLog, :count).by(3) end - it "on completion the ids of all logs with status discrepancies are logged in a warning" do - allow(storage_service).to receive(:list_files) - .and_return(%W[#{remote_folder}/#{lettings_log_id5}.xml #{remote_folder}/#{lettings_log_id6}.xml]) - expect(logger).to receive(:warn).with(/is not completed/).twice - expect(logger).to receive(:warn).with(/is incomplete but status should be complete/).twice - expect(logger).to receive(:warn).with(/The following lettings logs had status discrepancies: \[893ufj2s-lq77-42m4-rty6-ej09gh585uy1, 5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd\]/) - - lettings_log_service.create_logs(remote_folder) + it "only updates existing lettings logs" do + expect(logger).not_to receive(:error) + expect(logger).not_to receive(:warn) + expect(logger).to receive(:info).with(/Updating lettings log/).exactly(3).times + expect { 2.times { lettings_log_service.create_logs(remote_folder) } } + .to change(LettingsLog, :count).by(3) end - end - end - context "when importing a specific log" do - let(:lettings_log_id) { "0ead17cb-1668-442d-898c-0d52879ff592" } - let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id) } - let(:lettings_log_xml) { Nokogiri::XML(lettings_log_file) } + it "creates organisation relationship once" do + expect(logger).not_to receive(:error) + expect(logger).not_to receive(:warn) + expect { lettings_log_service.create_logs(remote_folder) } + .to change(OrganisationRelationship, :count).by(1) + end - context "and the void date is after the start date" do - before { lettings_log_xml.at_xpath("//xmlns:VYEAR").content = 2023 } + context "when there are status discrepancies" do + let(:lettings_log_id5) { "893ufj2s-lq77-42m4-rty6-ej09gh585uy1" } + let(:lettings_log_id6) { "5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd" } + let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id5) } + let(:lettings_log_xml) { Nokogiri::XML(lettings_log_file) } - it "does not import the voiddate" do - expect(logger).to receive(:warn).with(/is not completed/) - expect(logger).to receive(:warn).with(/lettings log with old id:#{lettings_log_id} is incomplete but status should be complete/) + before do + allow(storage_service).to receive(:get_file_io) + .with("#{remote_folder}/#{lettings_log_id5}.xml") + .and_return(open_file(fixture_directory, lettings_log_id5), open_file(fixture_directory, lettings_log_id5)) + allow(storage_service).to receive(:get_file_io) + .with("#{remote_folder}/#{lettings_log_id6}.xml") + .and_return(open_file(fixture_directory, lettings_log_id6), open_file(fixture_directory, lettings_log_id6)) + end - lettings_log_service.send(:create_log, lettings_log_xml) + it "the logger logs a warning with the lettings log's old id/filename" do + expect(logger).to receive(:warn).with(/is not completed/).once + expect(logger).to receive(:warn).with(/lettings log with old id:#{lettings_log_id5} is incomplete but status should be complete/).once - lettings_log = LettingsLog.where(old_id: lettings_log_id).first - expect(lettings_log&.voiddate).to be_nil - end - end + lettings_log_service.send(:create_log, lettings_log_xml) + end - context "and the organisation legacy ID does not exist" do - before { lettings_log_xml.at_xpath("//xmlns:OWNINGORGID").content = 99_999 } + it "on completion the ids of all logs with status discrepancies are logged in a warning" do + allow(storage_service).to receive(:list_files) + .and_return(%W[#{remote_folder}/#{lettings_log_id5}.xml #{remote_folder}/#{lettings_log_id6}.xml]) + expect(logger).to receive(:warn).with(/is not completed/).twice + expect(logger).to receive(:warn).with(/is incomplete but status should be complete/).twice + expect(logger).to receive(:warn).with(/The following lettings logs had status discrepancies: \[893ufj2s-lq77-42m4-rty6-ej09gh585uy1, 5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd\]/) - it "raises an exception" do - expect { lettings_log_service.send(:create_log, lettings_log_xml) } - .to raise_error(RuntimeError, "Organisation not found with legacy ID 99999") + lettings_log_service.create_logs(remote_folder) + end end end - context "and a person is under 16" do - before { lettings_log_xml.at_xpath("//xmlns:P2Age").content = 14 } + context "when importing a specific log" do + let(:lettings_log_id) { "0ead17cb-1668-442d-898c-0d52879ff592" } + let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id) } + let(:lettings_log_xml) { Nokogiri::XML(lettings_log_file) } + + context "and the void date is after the start date" do + before { lettings_log_xml.at_xpath("//xmlns:VYEAR").content = 2023 } - context "when the economic status is set to refuse" do - before { lettings_log_xml.at_xpath("//xmlns:P2Eco").content = "10) Refused" } + it "does not import the voiddate" do + expect(logger).to receive(:warn).with(/is not completed/) + expect(logger).to receive(:warn).with(/lettings log with old id:#{lettings_log_id} is incomplete but status should be complete/) - it "sets the economic status to child under 16" do - # The update is done when calculating derived variables - expect(logger).to receive(:warn).with(/Differences found when saving log/) lettings_log_service.send(:create_log, lettings_log_xml) lettings_log = LettingsLog.where(old_id: lettings_log_id).first - expect(lettings_log&.ecstat2).to be(9) + expect(lettings_log&.voiddate).to be_nil end end - context "when the relationship to lead tenant is set to refuse" do - before { lettings_log_xml.at_xpath("//xmlns:P2Rel").content = "Refused" } - - it "sets the relationship to lead tenant to child" do - lettings_log_service.send(:create_log, lettings_log_xml) + context "and the organisation legacy ID does not exist" do + before { lettings_log_xml.at_xpath("//xmlns:OWNINGORGID").content = 99_999 } - lettings_log = LettingsLog.where(old_id: lettings_log_id).first - expect(lettings_log&.relat2).to eq("C") + it "raises an exception" do + expect { lettings_log_service.send(:create_log, lettings_log_xml) } + .to raise_error(RuntimeError, "Organisation not found with legacy ID 99999") end end - end - context "and this is an internal transfer that is in-progress with invalid answers" do - before do - lettings_log_xml.at_xpath("//meta:status").content = "submitted-invalid" - lettings_log_xml.at_xpath("//xmlns:P2Age").content = 999 - end + context "and a person is under 16" do + before { lettings_log_xml.at_xpath("//xmlns:P2Age").content = 14 } - it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing field age2 from log triggering validation: Answer cannot be over 16/) - expect(logger).to receive(:warn).with(/Removing field age2 from log triggering validation: Person 2’s age must be between/) - expect(logger).to receive(:warn).with(/Removing field ecstat2 from log triggering validation: Answer cannot be ‘child under 16’/) - expect { lettings_log_service.send(:create_log, lettings_log_xml) } - .not_to raise_error - end + context "when the economic status is set to refuse" do + before { lettings_log_xml.at_xpath("//xmlns:P2Eco").content = "10) Refused" } - it "clears out the invalid answers" do - allow(logger).to receive(:warn) + it "sets the economic status to child under 16" do + # The update is done when calculating derived variables + expect(logger).to receive(:warn).with(/Differences found when saving log/) + lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + lettings_log = LettingsLog.where(old_id: lettings_log_id).first + expect(lettings_log&.ecstat2).to be(9) + end + end - expect(lettings_log).not_to be_nil - expect(lettings_log.age2).to be_nil - expect(lettings_log.ecstat2).to be_nil - end - end + context "when the relationship to lead tenant is set to refuse" do + before { lettings_log_xml.at_xpath("//xmlns:P2Rel").content = "Refused" } - context "and it has zero earnings" do - before do - lettings_log_xml.at_xpath("//meta:status").content = "submitted" - lettings_log_xml.at_xpath("//xmlns:Q8Money").content = 0 - end + it "sets the relationship to lead tenant to child" do + lettings_log_service.send(:create_log, lettings_log_xml) - it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Where the income is 0, set earnings and income to blank and set incref to refused/) - expect { lettings_log_service.send(:create_log, lettings_log_xml) } - .not_to raise_error + lettings_log = LettingsLog.where(old_id: lettings_log_id).first + expect(lettings_log&.relat2).to eq("C") + end + end end - it "clears out the invalid answers" do - allow(logger).to receive(:warn) + context "and this is an internal transfer that is in-progress with invalid answers" do + before do + lettings_log_xml.at_xpath("//meta:status").content = "submitted-invalid" + lettings_log_xml.at_xpath("//xmlns:P2Age").content = 999 + end - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Removing field age2 from log triggering validation: Answer cannot be over 16/) + expect(logger).to receive(:warn).with(/Removing field age2 from log triggering validation: Person 2’s age must be between/) + expect(logger).to receive(:warn).with(/Removing field ecstat2 from log triggering validation: Answer cannot be ‘child under 16’/) + expect { lettings_log_service.send(:create_log, lettings_log_xml) } + .not_to raise_error + end - expect(lettings_log).not_to be_nil - expect(lettings_log.earnings).to be_nil - expect(lettings_log.incref).to eq(1) - expect(lettings_log.net_income_known).to eq(2) - end - end + it "clears out the invalid answers" do + allow(logger).to receive(:warn) - context "and an invalid tenancy length for tenancy type" do - before do - lettings_log_xml.at_xpath("//meta:status").content = "submitted" - lettings_log_xml.at_xpath("//xmlns:_2cYears").content = "1" - lettings_log_xml.at_xpath("//xmlns:Q2b").content = "4" - end + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) - it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing tenancylength as invalid/) - expect { lettings_log_service.send(:create_log, lettings_log_xml) } - .not_to raise_error + expect(lettings_log).not_to be_nil + expect(lettings_log.age2).to be_nil + expect(lettings_log.ecstat2).to be_nil + end end - it "clears out the invalid answers" do - allow(logger).to receive(:warn) + context "and it has zero earnings" do + before do + lettings_log_xml.at_xpath("//meta:status").content = "submitted" + lettings_log_xml.at_xpath("//xmlns:Q8Money").content = 0 + end - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Where the income is 0, set earnings and income to blank and set incref to refused/) + expect { lettings_log_service.send(:create_log, lettings_log_xml) } + .not_to raise_error + end - expect(lettings_log).not_to be_nil - expect(lettings_log.tenancylength).to be_nil - expect(lettings_log.tenancy).to be_nil - end - end + it "clears out the invalid answers" do + allow(logger).to receive(:warn) - context "and an lead tenant must be under 20 if childrens home or foster care" do - before do - lettings_log_xml.at_xpath("//meta:status").content = "submitted" - lettings_log_xml.at_xpath("//xmlns:Q11").content = "13" - lettings_log_xml.at_xpath("//xmlns:P1Age").content = "22" - end + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) - it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing age1 and prevten as incompatible/) - expect { lettings_log_service.send(:create_log, lettings_log_xml) } - .not_to raise_error + expect(lettings_log).not_to be_nil + expect(lettings_log.earnings).to be_nil + expect(lettings_log.incref).to eq(1) + expect(lettings_log.net_income_known).to eq(2) + end end - it "clears out the invalid answers" do - allow(logger).to receive(:warn) + context "and an invalid tenancy length for tenancy type" do + before do + lettings_log_xml.at_xpath("//meta:status").content = "submitted" + lettings_log_xml.at_xpath("//xmlns:_2cYears").content = "1" + lettings_log_xml.at_xpath("//xmlns:Q2b").content = "4" + end - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Removing tenancylength as invalid/) + expect { lettings_log_service.send(:create_log, lettings_log_xml) } + .not_to raise_error + end - expect(lettings_log).not_to be_nil - expect(lettings_log.age1).to be_nil - expect(lettings_log.prevten).to be_nil - end - end + it "clears out the invalid answers" do + allow(logger).to receive(:warn) - context "and is a carehome but missing carehome charge" do - let(:lettings_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) - before do - lettings_log_xml.at_xpath("//meta:status").content = "submitted" - lettings_log_xml.at_xpath("//xmlns:_1cmangroupcode").content = scheme2.old_visible_id - scheme2.update!(registered_under_care_act: 2) - lettings_log_xml.at_xpath("//xmlns:Q18b").content = "" + expect(lettings_log).not_to be_nil + expect(lettings_log.tenancylength).to be_nil + expect(lettings_log.tenancy).to be_nil + end end - it "intercepts the relevant validation error" do - allow(logger).to receive(:warn) + context "and an lead tenant must be under 20 if childrens home or foster care" do + before do + lettings_log_xml.at_xpath("//meta:status").content = "submitted" + lettings_log_xml.at_xpath("//xmlns:Q11").content = "13" + lettings_log_xml.at_xpath("//xmlns:P1Age").content = "22" + end - expect { lettings_log_service.send(:create_log, lettings_log_xml) } - .not_to raise_error - end + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Removing age1 and prevten as incompatible/) + expect { lettings_log_service.send(:create_log, lettings_log_xml) } + .not_to raise_error + end - it "clears out the invalid answers" do - allow(logger).to receive(:warn) + it "clears out the invalid answers" do + allow(logger).to receive(:warn) - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) - expect(lettings_log).not_to be_nil - expect(lettings_log.is_carehome).to be_truthy - expect(lettings_log.chcharge).to be_nil + expect(lettings_log).not_to be_nil + expect(lettings_log.age1).to be_nil + expect(lettings_log.prevten).to be_nil + end end - end - context "and this is an internal transfer from a non social housing" do - before do - lettings_log_xml.at_xpath("//xmlns:Q11").content = "9 Residential care home" - lettings_log_xml.at_xpath("//xmlns:Q16").content = "1 Internal Transfer" - end + context "and is a carehome but missing carehome charge" do + let(:lettings_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } - it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing internal transfer referral since previous tenancy is a non social housing/) - expect { lettings_log_service.send(:create_log, lettings_log_xml) } - .not_to raise_error - end + before do + lettings_log_xml.at_xpath("//meta:status").content = "submitted" + lettings_log_xml.at_xpath("//xmlns:_1cmangroupcode").content = scheme2.old_visible_id + scheme2.update!(registered_under_care_act: 2) + lettings_log_xml.at_xpath("//xmlns:Q18b").content = "" + end - it "clears out the referral answer" do - allow(logger).to receive(:warn) + it "intercepts the relevant validation error" do + allow(logger).to receive(:warn) - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + expect { lettings_log_service.send(:create_log, lettings_log_xml) } + .not_to raise_error + end + + it "clears out the invalid answers" do + allow(logger).to receive(:warn) + + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) - expect(lettings_log).not_to be_nil - expect(lettings_log.referral).to be_nil + expect(lettings_log).not_to be_nil + expect(lettings_log.is_carehome).to be_truthy + expect(lettings_log.chcharge).to be_nil + end end - context "and this is an internal transfer from a previous fixed term tenancy" do + context "and this is an internal transfer from a non social housing" do before do - lettings_log_xml.at_xpath("//xmlns:Q11").content = "30 Fixed term Local Authority General Needs tenancy" + lettings_log_xml.at_xpath("//xmlns:Q11").content = "9 Residential care home" lettings_log_xml.at_xpath("//xmlns:Q16").content = "1 Internal Transfer" end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing internal transfer referral since previous tenancy is fixed terms or lifetime/) + expect(logger).to receive(:warn).with(/Removing internal transfer referral since previous tenancy is a non social housing/) expect { lettings_log_service.send(:create_log, lettings_log_xml) } .not_to raise_error end @@ -356,86 +341,202 @@ RSpec.describe Imports::LettingsLogsImportService do expect(lettings_log).not_to be_nil expect(lettings_log.referral).to be_nil end + + context "and this is an internal transfer from a previous fixed term tenancy" do + before do + lettings_log_xml.at_xpath("//xmlns:Q11").content = "30 Fixed term Local Authority General Needs tenancy" + lettings_log_xml.at_xpath("//xmlns:Q16").content = "1 Internal Transfer" + end + + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Removing internal transfer referral since previous tenancy is fixed terms or lifetime/) + expect { lettings_log_service.send(:create_log, lettings_log_xml) } + .not_to raise_error + end + + it "clears out the referral answer" do + allow(logger).to receive(:warn) + + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + + expect(lettings_log).not_to be_nil + expect(lettings_log.referral).to be_nil + end + end end - end - context "and the net income soft validation is triggered (net_income_value_check)" do - before do - lettings_log_xml.at_xpath("//xmlns:Q8a").content = "1 Weekly" - lettings_log_xml.at_xpath("//xmlns:Q8Money").content = 890.00 + context "and the net income soft validation is triggered (net_income_value_check)" do + before do + lettings_log_xml.at_xpath("//xmlns:Q8a").content = "1 Weekly" + lettings_log_xml.at_xpath("//xmlns:Q8Money").content = 890.00 + end + + it "completes the log" do + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + expect(lettings_log.status).to eq("completed") + end end - it "completes the log" do - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) - expect(lettings_log.status).to eq("completed") + context "and the rent soft validation is triggered (rent_value_check)" do + before do + lettings_log_xml.at_xpath("//xmlns:Q18ai").content = 200.00 + lettings_log_xml.at_xpath("//xmlns:Q18av").content = 232.02 + lettings_log_xml.at_xpath("//xmlns:Q17").content = "1 Weekly for 52 weeks" + LaRentRange.create!( + start_year: 2021, + la: "E08000035", + beds: 2, + lettype: 1, + soft_max: 900, + hard_max: 1500, + soft_min: 500, + hard_min: 100, + ) + end + + it "completes the log" do + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + expect(lettings_log.status).to eq("completed") + end end - end - context "and the rent soft validation is triggered (rent_value_check)" do - before do - lettings_log_xml.at_xpath("//xmlns:Q18ai").content = 200.00 - lettings_log_xml.at_xpath("//xmlns:Q18av").content = 232.02 - lettings_log_xml.at_xpath("//xmlns:Q17").content = "1 Weekly for 52 weeks" - LaRentRange.create!( - start_year: 2021, - la: "E08000035", - beds: 2, - lettype: 1, - soft_max: 900, - hard_max: 1500, - soft_min: 500, - hard_min: 100, - ) + context "and the retirement soft validation is triggered (retirement_value_check)" do + before do + lettings_log_xml.at_xpath("//xmlns:P1Age").content = 68 + lettings_log_xml.at_xpath("//xmlns:P1Eco").content = "6) Not Seeking Work" + end + + it "completes the log" do + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + expect(lettings_log.status).to eq("completed") + end end - it "completes the log" do - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) - expect(lettings_log.status).to eq("completed") + context "and this is a supported housing log with multiple locations under a scheme" do + let(:lettings_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } + + it "sets the scheme and location values" do + expect(logger).not_to receive(:warn) + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + + expect(lettings_log.scheme_id).not_to be_nil + expect(lettings_log.location_id).not_to be_nil + expect(lettings_log.status).to eq("completed") + end end - end - context "and the retirement soft validation is triggered (retirement_value_check)" do - before do - lettings_log_xml.at_xpath("//xmlns:P1Age").content = 68 - lettings_log_xml.at_xpath("//xmlns:P1Eco").content = "6) Not Seeking Work" + context "and this is a supported housing log with a single location under a scheme" do + let(:lettings_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } + + before { lettings_log_xml.at_xpath("//xmlns:_1cmangroupcode").content = scheme2.old_visible_id } + + it "sets the scheme and location values" do + expect(logger).not_to receive(:warn) + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + + expect(lettings_log.scheme_id).not_to be_nil + expect(lettings_log.location_id).not_to be_nil + expect(lettings_log.status).to eq("completed") + end end + end + end - it "completes the log" do - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) - expect(lettings_log.status).to eq("completed") + context "with 22/23 logs" do + subject(:lettings_log_service) { described_class.new(storage_service, logger) } + + around do |example| + Timecop.freeze(Time.zone.local(2023, 1, 1)) do + Singleton.__init__(FormHandler) + example.run end + Timecop.return + Singleton.__init__(FormHandler) end - context "and this is a supported housing log with multiple locations under a scheme" do - let(:lettings_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } + let(:storage_service) { instance_double(Storage::S3Service) } + let(:logger) { instance_double(ActiveSupport::Logger) } - it "sets the scheme and location values" do - expect(logger).not_to receive(:warn) - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json") } + let(:real_2022_2023_form) { Form.new("config/forms/2022_2023.json") } + let(:fixture_directory) { "spec/fixtures/imports/logs" } - expect(lettings_log.scheme_id).not_to be_nil - expect(lettings_log.location_id).not_to be_nil - expect(lettings_log.status).to eq("completed") - end + let(:organisation) { FactoryBot.create(:organisation, old_visible_id: "1", provider_type: "PRP") } + let(:managing_organisation) { FactoryBot.create(:organisation, old_visible_id: "2", provider_type: "PRP") } + let(:scheme1) { FactoryBot.create(:scheme, old_visible_id: "0123", owning_organisation: organisation) } + let(:scheme2) { FactoryBot.create(:scheme, old_visible_id: "456", owning_organisation: organisation) } + + def open_file(directory, filename) + File.open("#{directory}/#{filename}.xml") end - context "and this is a supported housing log with a single location under a scheme" do - let(:lettings_log_id) { "0b4a68df-30cc-474a-93c0-a56ce8fdad3b" } + before do + WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/LS166FT/) + .to_return(status: 200, body: '{"status":200,"result":{"admin_district":"Westminster","codes":{"admin_district":"E08000035"}}}', headers: {}) + + allow(Organisation).to receive(:find_by).and_return(nil) + allow(Organisation).to receive(:find_by).with(old_visible_id: organisation.old_visible_id).and_return(organisation) + allow(Organisation).to receive(:find_by).with(old_visible_id: managing_organisation.old_visible_id).and_return(managing_organisation) + + # Created by users + FactoryBot.create(:user, old_user_id: "c3061a2e6ea0b702e6f6210d5c52d2a92612d2aa", organisation:) + FactoryBot.create(:user, old_user_id: "e29c492473446dca4d50224f2bb7cf965a261d6f", organisation:) + + # Location setup + FactoryBot.create(:location, old_visible_id: "10", postcode: "LS166FT", scheme_id: scheme1.id, mobility_type: "W", startdate: Time.zone.local(2021, 4, 1)) + FactoryBot.create(:location, scheme_id: scheme1.id, startdate: Time.zone.local(2021, 4, 1)) + FactoryBot.create(:location, old_visible_id: "10", postcode: "LS166FT", scheme_id: scheme2.id, mobility_type: "W", startdate: Time.zone.local(2021, 4, 1)) + + # Stub the form handler to use the real form + allow(FormHandler.instance).to receive(:get_form).with("current_lettings").and_return(real_2022_2023_form) + allow(FormHandler.instance).to receive(:get_form).with("previous_lettings").and_return(real_2021_2022_form) + allow(FormHandler.instance).to receive(:get_form).with("next_lettings").and_return(real_2022_2023_form) + end - before { lettings_log_xml.at_xpath("//xmlns:_1cmangroupcode").content = scheme2.old_visible_id } + context "when importing lettings logs" do + let(:remote_folder) { "lettings_logs" } + let(:lettings_log_id) { "00d2343e-d5fa-4c89-8400-ec3854b0f2b4" } + let(:sales_log) { "shared_ownership_sales_log" } - it "sets the scheme and location values" do + before do + # Stub the S3 file listing and download + allow(storage_service).to receive(:list_files) + .and_return(%W[#{remote_folder}/#{lettings_log_id}.xml #{remote_folder}/#{sales_log}.xml]) + allow(storage_service).to receive(:get_file_io) + .with("#{remote_folder}/#{lettings_log_id}.xml") + .and_return(open_file(fixture_directory, lettings_log_id), open_file(fixture_directory, lettings_log_id)) + allow(storage_service).to receive(:get_file_io) + .with("#{remote_folder}/#{sales_log}.xml") + .and_return(open_file(fixture_directory, sales_log), open_file(fixture_directory, sales_log)) + end + + it "successfully create all lettings logs" do + expect(logger).not_to receive(:error) expect(logger).not_to receive(:warn) - lettings_log_service.send(:create_log, lettings_log_xml) - lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + expect(logger).not_to receive(:info) + expect { lettings_log_service.create_logs(remote_folder) } + .to change(LettingsLog, :count).by(1) + end - expect(lettings_log.scheme_id).not_to be_nil - expect(lettings_log.location_id).not_to be_nil - expect(lettings_log.status).to eq("completed") + it "only updates existing lettings logs" do + expect(logger).not_to receive(:error) + expect(logger).not_to receive(:warn) + expect(logger).to receive(:info).with(/Updating lettings log/).once + expect { 2.times { lettings_log_service.create_logs(remote_folder) } } + .to change(LettingsLog, :count).by(1) + end + + it "creates organisation relationship once" do + expect(logger).not_to receive(:error) + expect(logger).not_to receive(:warn) + expect { lettings_log_service.create_logs(remote_folder) } + .to change(OrganisationRelationship, :count).by(1) end end end diff --git a/spec/views/form/page_view_spec.rb b/spec/views/form/page_view_spec.rb index a7039a9a9..074a7fd6a 100644 --- a/spec/views/form/page_view_spec.rb +++ b/spec/views/form/page_view_spec.rb @@ -17,6 +17,15 @@ RSpec.describe "form/page" do end end + around do |example| + Timecop.freeze(Time.zone.local(2022, 1, 1)) do + Singleton.__init__(FormHandler) + example.run + end + Timecop.return + Singleton.__init__(FormHandler) + end + before do assign(:log, lettings_log) assign(:page, page)