Browse Source

CLDC 1865 Refactor questions to avoid race conditions (#1255)

* Extract time helper methods

* Add missing specs for BulkUploadController#start

* refactor ManagingOrganisation question

* refactor StockOwner question
pull/1270/head
Jack 2 years ago committed by GitHub
parent
commit
d6a5f6718e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      app/helpers/collection_time_helper.rb
  2. 44
      app/models/form/lettings/questions/managing_organisation.rb
  3. 42
      app/models/form/lettings/questions/stock_owner.rb
  4. 16
      app/models/form_handler.rb
  5. 34
      spec/helpers/collection_time_helper_spec.rb
  6. 32
      spec/models/form/lettings/questions/managing_organisation_spec.rb
  7. 12
      spec/models/form_handler_spec.rb
  8. 42
      spec/requests/bulk_upload_controller_spec.rb

16
app/helpers/collection_time_helper.rb

@ -0,0 +1,16 @@
module CollectionTimeHelper
def current_collection_start_year
today = Time.zone.now
window_end_date = Time.zone.local(today.year, 4, 1)
today < window_end_date ? today.year - 1 : today.year
end
def collection_start_date(date)
window_end_date = Time.zone.local(date.year, 4, 1)
date < window_end_date ? Time.zone.local(date.year - 1, 4, 1) : Time.zone.local(date.year, 4, 1)
end
def current_collection_start_date
Time.zone.local(current_collection_start_year, 4, 1)
end
end

44
app/models/form/lettings/questions/managing_organisation.rb

@ -1,48 +1,45 @@
class Form::Lettings::Questions::ManagingOrganisation < ::Form::Question class Form::Lettings::Questions::ManagingOrganisation < ::Form::Question
attr_accessor :current_user, :log
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "managing_organisation_id" @id = "managing_organisation_id"
@check_answer_label = "Managing agent" @check_answer_label = "Managing agent"
@header = "Which organisation manages this letting?" @header = "Which organisation manages this letting?"
@type = "select" @type = "select"
@answer_options = answer_options
end end
def answer_options def answer_options(log = nil, user = nil)
opts = { "" => "Select an option" } opts = { "" => "Select an option" }
return opts unless ActiveRecord::Base.connected? return opts unless ActiveRecord::Base.connected?
return opts unless current_user return opts unless user
return opts unless log return opts unless log
if log.managing_organisation.present? if log.managing_organisation.present?
opts = opts.merge({ log.managing_organisation.id => log.managing_organisation.name }) opts = opts.merge({ log.managing_organisation.id => log.managing_organisation.name })
end end
if current_user.support? if user.support?
if log.owning_organisation.holds_own_stock? if log.owning_organisation.holds_own_stock?
opts[log.owning_organisation.id] = "#{log.owning_organisation.name} (Owning organisation)" opts[log.owning_organisation.id] = "#{log.owning_organisation.name} (Owning organisation)"
end end
else else
opts[current_user.organisation.id] = "#{current_user.organisation.name} (Your organisation)" opts[user.organisation.id] = "#{user.organisation.name} (Your organisation)"
end end
opts.merge(managing_organisations_answer_options) orgs = if user.support?
log.owning_organisation
else
user.organisation
end.managing_agents.pluck(:id, :name).to_h
opts.merge(orgs)
end end
def displayed_answer_options(log, user) def displayed_answer_options(log, user)
@current_user = user answer_options(log, user)
@log = log
answer_options
end end
def label_from_value(value, log = nil, user = nil) def label_from_value(value, _log = nil, _user = nil)
@log = log
@current_user = user
return unless value return unless value
answer_options[value] answer_options[value]
@ -53,25 +50,20 @@ class Form::Lettings::Questions::ManagingOrganisation < ::Form::Question
end end
def hidden_in_check_answers?(log, user = nil) def hidden_in_check_answers?(log, user = nil)
@current_user = user user.nil? || !@page.routed_to?(log, user)
@current_user.nil? || !@page.routed_to?(log, user)
end end
def enabled def enabled
true true
end end
def answer_label(log, _current_user = nil)
Organisation.find_by(id: log.managing_organisation_id)&.name
end
private private
def selected_answer_option_is_derived?(_log) def selected_answer_option_is_derived?(_log)
true true
end end
def managing_organisations_answer_options
if current_user.support?
log.owning_organisation
else
current_user.organisation
end.managing_agents.pluck(:id, :name).to_h
end
end end

42
app/models/form/lettings/questions/stock_owner.rb

@ -1,6 +1,4 @@
class Form::Lettings::Questions::StockOwner < ::Form::Question class Form::Lettings::Questions::StockOwner < ::Form::Question
attr_accessor :current_user, :log
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "owning_organisation_id" @id = "owning_organisation_id"
@ -9,38 +7,38 @@ class Form::Lettings::Questions::StockOwner < ::Form::Question
@type = "select" @type = "select"
end end
def answer_options def answer_options(log = nil, user = nil)
answer_opts = { "" => "Select an option" } answer_opts = { "" => "Select an option" }
return answer_opts unless ActiveRecord::Base.connected? return answer_opts unless ActiveRecord::Base.connected?
return answer_opts unless current_user return answer_opts unless user
return answer_opts unless log return answer_opts unless log
if log.owning_organisation_id.present? if log.owning_organisation_id.present?
answer_opts = answer_opts.merge({ log.owning_organisation.id => log.owning_organisation.name }) answer_opts = answer_opts.merge({ log.owning_organisation.id => log.owning_organisation.name })
end end
if !current_user.support? && current_user.organisation.holds_own_stock? if !user.support? && user.organisation.holds_own_stock?
answer_opts[current_user.organisation.id] = "#{current_user.organisation.name} (Your organisation)" answer_opts[user.organisation.id] = "#{user.organisation.name} (Your organisation)"
end end
stock_owners_answer_options = if user.support?
Organisation
else
user.organisation.stock_owners
end.pluck(:id, :name).to_h
answer_opts.merge(stock_owners_answer_options) answer_opts.merge(stock_owners_answer_options)
end end
def displayed_answer_options(log, user = nil) def displayed_answer_options(log, user = nil)
@current_user = user answer_options(log, user)
@log = log
answer_options
end end
def label_from_value(value, log = nil, user = nil) def label_from_value(value, log = nil, user = nil)
@log = log
@current_user = user
return unless value return unless value
answer_options[value] answer_options(log, user)[value]
end end
def derived? def derived?
@ -48,13 +46,11 @@ class Form::Lettings::Questions::StockOwner < ::Form::Question
end end
def hidden_in_check_answers?(_log, user = nil) def hidden_in_check_answers?(_log, user = nil)
@current_user = user return false if user.support?
return false if current_user.support? stock_owners = user.organisation.stock_owners
stock_owners = current_user.organisation.stock_owners if user.organisation.holds_own_stock?
if current_user.organisation.holds_own_stock?
stock_owners.count.zero? stock_owners.count.zero?
else else
stock_owners.count <= 1 stock_owners.count <= 1
@ -70,12 +66,4 @@ private
def selected_answer_option_is_derived?(_log) def selected_answer_option_is_derived?(_log)
true true
end end
def stock_owners_answer_options
if current_user.support?
Organisation
else
current_user.organisation.stock_owners
end.pluck(:id, :name).to_h
end
end end

16
app/models/form_handler.rb

@ -1,5 +1,6 @@
class FormHandler class FormHandler
include Singleton include Singleton
include CollectionTimeHelper
attr_reader :forms attr_reader :forms
def initialize def initialize
@ -44,21 +45,6 @@ class FormHandler
forms forms
end end
def current_collection_start_year
today = Time.zone.now
window_end_date = Time.zone.local(today.year, 4, 1)
today < window_end_date ? today.year - 1 : today.year
end
def collection_start_date(date)
window_end_date = Time.zone.local(date.year, 4, 1)
date < window_end_date ? Time.zone.local(date.year - 1, 4, 1) : Time.zone.local(date.year, 4, 1)
end
def current_collection_start_date
Time.zone.local(current_collection_start_year, 4, 1)
end
def form_name_from_start_year(year, type) def form_name_from_start_year(year, type)
form_mappings = { 0 => "current_#{type}", 1 => "previous_#{type}", -1 => "next_#{type}" } form_mappings = { 0 => "current_#{type}", 1 => "previous_#{type}", -1 => "next_#{type}" }
form_mappings[current_collection_start_year - year] form_mappings[current_collection_start_year - year]

34
spec/helpers/collection_time_helper_spec.rb

@ -0,0 +1,34 @@
require "rails_helper"
RSpec.describe CollectionTimeHelper do
let(:current_user) { create(:user, :data_coordinator) }
let(:user) { create(:user, :data_coordinator) }
around do |example|
Timecop.freeze(now) do
example.run
end
end
describe "Current collection start year" do
context "when the date is after 1st of April" do
let(:now) { Time.utc(2022, 8, 3) }
it "returns the same year as the current start year" do
expect(current_collection_start_year).to eq(2022)
end
it "returns the correct current start date" do
expect(current_collection_start_date).to eq(Time.zone.local(2022, 4, 1))
end
end
context "with the date before 1st of April" do
let(:now) { Time.utc(2022, 2, 3) }
it "returns the previous year as the current start year" do
expect(current_collection_start_year).to eq(2021)
end
end
end
end

32
spec/models/form/lettings/questions/managing_organisation_spec.rb

@ -199,4 +199,36 @@ RSpec.describe Form::Lettings::Questions::ManagingOrganisation, type: :model do
end end
end end
end end
describe "#answer_label" do
context "when answered" do
let(:managing_organisation) { create(:organisation) }
let(:log) { create(:lettings_log, managing_organisation:) }
it "returns org name" do
expect(question.answer_label(log)).to eq(managing_organisation.name)
end
end
context "when unanswered" do
let(:log) { create(:lettings_log, managing_organisation: nil) }
it "returns nil" do
expect(question.answer_label(log)).to be_nil
end
end
context "when org does not exist" do
let(:managing_organisation) { create(:organisation) }
let(:log) { create(:lettings_log, managing_organisation:) }
before do
allow(Organisation).to receive(:find_by).and_return(nil)
end
it "returns nil" do
expect(question.answer_label(log)).to be_nil
end
end
end
end end

12
spec/models/form_handler_spec.rb

@ -76,10 +76,6 @@ RSpec.describe FormHandler do
context "when the date is after 1st of April" do context "when the date is after 1st of April" do
let(:now) { Time.utc(2022, 8, 3) } let(:now) { Time.utc(2022, 8, 3) }
it "returns the same year as the current start year" do
expect(form_handler.current_collection_start_year).to eq(2022)
end
it "returns the correct current lettings form name" do it "returns the correct current lettings form name" do
expect(form_handler.form_name_from_start_year(2022, "lettings")).to eq("current_lettings") expect(form_handler.form_name_from_start_year(2022, "lettings")).to eq("current_lettings")
end end
@ -103,19 +99,11 @@ RSpec.describe FormHandler do
it "returns the correct next sales form name" do it "returns the correct next sales form name" do
expect(form_handler.form_name_from_start_year(2023, "sales")).to eq("next_sales") expect(form_handler.form_name_from_start_year(2023, "sales")).to eq("next_sales")
end end
it "returns the correct current start date" do
expect(form_handler.current_collection_start_date).to eq(Time.zone.local(2022, 4, 1))
end
end end
context "with the date before 1st of April" do context "with the date before 1st of April" do
let(:now) { Time.utc(2022, 2, 3) } let(:now) { Time.utc(2022, 2, 3) }
it "returns the previous year as the current start year" do
expect(form_handler.current_collection_start_year).to eq(2021)
end
it "returns the correct current lettings form name" do it "returns the correct current lettings form name" do
expect(form_handler.form_name_from_start_year(2021, "lettings")).to eq("current_lettings") expect(form_handler.form_name_from_start_year(2021, "lettings")).to eq("current_lettings")
end end

42
spec/requests/bulk_upload_controller_spec.rb

@ -13,6 +13,14 @@ RSpec.describe BulkUploadController, type: :request do
end end
context "when a user is not signed in" do context "when a user is not signed in" do
describe "GET #start" do
before { get start_bulk_upload_lettings_logs_path, headers:, params: {} }
it "does not let you see the bulk upload page" do
expect(response).to redirect_to("/account/sign-in")
end
end
describe "GET #show" do describe "GET #show" do
before { get url, headers:, params: {} } before { get url, headers:, params: {} }
@ -50,6 +58,40 @@ RSpec.describe BulkUploadController, type: :request do
end end
end end
describe "GET #start" do
before do
Timecop.freeze(time)
get start_bulk_upload_lettings_logs_path
end
after do
Timecop.unfreeze
end
context "when not crossover period" do
let(:time) { Time.utc(2022, 2, 8) }
it "redirects to bulk upload path" do
expect(request).to redirect_to(
bulk_upload_lettings_log_path(
id: "prepare-your-file",
form: { year: 2021 },
),
)
end
end
context "when crossover period" do
let(:time) { Time.utc(2022, 6, 8) }
it "redirects to bulk upload path" do
expect(request).to redirect_to(
bulk_upload_lettings_log_path(id: "year"),
)
end
end
end
describe "POST #bulk upload" do describe "POST #bulk upload" do
context "with a valid file based on the upload template" do context "with a valid file based on the upload template" do
let(:request) { post url, params: { bulk_upload: { lettings_log_bulk_upload: valid_file } } } let(:request) { post url, params: { bulk_upload: { lettings_log_bulk_upload: valid_file } } }

Loading…
Cancel
Save