Browse Source

Merge branch 'main' into CLDC-764/PageTitlesErrorPrefix

pull/145/head
Matthew Phelan 3 years ago
parent
commit
8c12ed5da9
  1. 7
      Gemfile
  2. 17
      Gemfile.lock
  3. 6
      app/controllers/bulk_upload_controller.rb
  4. 94
      app/controllers/case_logs_controller.rb
  5. 83
      app/controllers/form_controller.rb
  6. 9
      app/controllers/organisations_controller.rb
  7. 2
      app/controllers/soft_validations_controller.rb
  8. 16
      app/controllers/users_controller.rb
  9. 6
      app/models/bulk_upload.rb
  10. 11
      app/models/case_log.rb
  11. 42
      app/models/constants/case_log.rb
  12. 6
      app/models/constants/organisation.rb
  13. 2
      app/models/organisation.rb
  14. 2
      app/models/validations/household_validations.rb
  15. 2
      config/forms/2021_2022.json
  16. 6
      config/routes.rb
  17. 15
      docs/adr/adr-012-controller-http-return-statuses.md
  18. 172
      spec/controllers/case_logs_controller_spec.rb
  19. 2
      spec/factories/case_log.rb
  20. 2
      spec/fixtures/complete_case_log.json
  21. 24
      spec/models/case_log_spec.rb
  22. 29
      spec/requests/bulk_upload_controller_spec.rb
  23. 120
      spec/requests/case_log_controller_spec.rb
  24. 278
      spec/requests/form_controller_spec.rb
  25. 79
      spec/requests/organisations_controller_spec.rb
  26. 15
      spec/requests/soft_validations_controller_spec.rb
  27. 99
      spec/requests/user_controller_spec.rb

7
Gemfile

@ -15,9 +15,9 @@ gem "puma", "~> 5.0"
gem "webpacker", "~> 5.0"
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", ">= 1.4.4", require: false
# Gov.UK frontend components
# GOV UK frontend components
gem "govuk-components"
# Gov.UK component form builder DSL
# GOV UK component form builder DSL
gem "govuk_design_system_formbuilder"
# Turbo & Stimulus
gem "hotwire-rails"
@ -34,8 +34,9 @@ gem "json-schema"
# Authentication
# Point at branch until devise is compatible with Turbo, see https://github.com/heartcombo/devise/pull/5340
gem "devise", github: "ghiculescu/devise", branch: "error-code-422"
gem "turbo-rails", "~> 0.8"
# UK postcode parsing and validation
gem "uk_postcode"
# Use Ruby objects to build reusable markup. A React inspired evolution of the presenter pattern
gem "view_component"
group :development, :test do

17
Gemfile.lock

@ -38,7 +38,7 @@ GIT
GIT
remote: https://github.com/rspec/rspec-rails.git
revision: 1fe6c2e8a56f46ae4c2f13ffa0733cce3b717c2d
revision: 5f54d780a3d8f21cfe4f424a7d0435be2e279356
branch: main
specs:
rspec-rails (5.1.0.pre)
@ -52,7 +52,7 @@ GIT
GIT
remote: https://github.com/rspec/rspec-support.git
revision: 9499cb622f9feef43a53f357809f9e656e7cb6de
revision: 321dc7227df43cd682c293fa31fa427b94b2ea74
branch: main
specs:
rspec-support (3.11.0.pre)
@ -178,13 +178,13 @@ GEM
formtastic (4.0.0)
actionpack (>= 5.2.0)
formtastic_i18n (0.7.0)
globalid (0.6.0)
globalid (1.0.0)
activesupport (>= 5.0)
govuk-components (2.1.4)
govuk-components (2.1.5)
activemodel (>= 6.0)
railties (>= 6.0)
view_component (~> 2.39.0)
govuk_design_system_formbuilder (2.7.6)
govuk_design_system_formbuilder (2.8.0)
actionview (>= 6.0)
activemodel (>= 6.0)
activesupport (>= 6.0)
@ -249,7 +249,7 @@ GEM
iniparse (~> 1.4)
rexml (~> 3.2)
parallel (1.21.0)
parser (3.0.2.0)
parser (3.0.3.1)
ast (~> 2.4.1)
pg (1.2.3)
pry (0.13.1)
@ -304,7 +304,7 @@ GEM
rb-fsevent (0.11.0)
rb-inotify (0.10.1)
ffi (~> 1.0)
regexp_parser (2.1.1)
regexp_parser (2.2.0)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
@ -377,7 +377,7 @@ GEM
rails (>= 6.0.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
uk_postcode (2.1.6)
uk_postcode (2.1.7)
unicode-display_width (2.1.0)
view_component (2.39.0)
activesupport (>= 5.0.0, < 8.0)
@ -441,7 +441,6 @@ DEPENDENCIES
scss_lint-govuk
selenium-webdriver
simplecov
turbo-rails (~> 0.8)
tzinfo-data
uk_postcode
view_component

6
app/controllers/bulk_upload_controller.rb

@ -1,4 +1,6 @@
class BulkUploadController < ApplicationController
before_action :authenticate_user!
def show
@bulk_upload = BulkUpload.new(nil, nil)
render "case_logs/bulk_upload"
@ -8,7 +10,7 @@ class BulkUploadController < ApplicationController
file = upload_params.tempfile
content_type = upload_params.content_type
@bulk_upload = BulkUpload.new(file, content_type)
@bulk_upload.process
@bulk_upload.process(current_user)
if @bulk_upload.errors.present?
render "case_logs/bulk_upload", status: :unprocessable_entity
else
@ -16,6 +18,8 @@ class BulkUploadController < ApplicationController
end
end
private
def upload_params
params.require("bulk_upload")["case_log_bulk_upload"]
end

94
app/controllers/case_logs_controller.rb

@ -2,6 +2,7 @@ class CaseLogsController < ApplicationController
skip_before_action :verify_authenticity_token, if: :json_api_request?
before_action :authenticate, if: :json_api_request?
before_action :authenticate_user!, unless: :json_api_request?
before_action :find_resource, except: %i[create index edit]
def index
@completed_case_logs = current_user.completed_case_logs
@ -23,11 +24,11 @@ class CaseLogsController < ApplicationController
end
def update
if (case_log = CaseLog.find_by(id: params[:id]))
if case_log.update(api_case_log_params)
render json: case_log, status: :ok
if @case_log
if @case_log.update(api_case_log_params)
render json: @case_log, status: :ok
else
render json: { errors: case_log.errors.messages }, status: :unprocessable_entity
render json: { errors: @case_log.errors.messages }, status: :unprocessable_entity
end
else
render_not_found_json("Case log", params[:id])
@ -39,8 +40,8 @@ class CaseLogsController < ApplicationController
# We don't have a dedicated non-editable show view
format.html { edit }
format.json do
if (case_log = CaseLog.find_by(id: params[:id]))
render json: case_log, status: :ok
if @case_log
render json: @case_log, status: :ok
else
render_not_found_json("Case log", params[:id])
end
@ -58,93 +59,22 @@ class CaseLogsController < ApplicationController
end
end
def submit_form
form = FormHandler.instance.get_form("2021_2022")
@case_log = current_user.case_logs.find_by(id: params[:id])
if @case_log
page = form.get_page(params[:case_log][:page])
responses_for_page = responses_for_page(page)
if @case_log.update(responses_for_page) && @case_log.has_no_unresolved_soft_errors?
redirect_path = form.next_page_redirect_path(page, @case_log)
redirect_to(send(redirect_path, @case_log))
else
subsection = form.subsection_for_page(page)
render "form/page", locals: { form: form, page: page, subsection: subsection.label }, status: :unprocessable_entity
end
else
render_not_found_html
end
end
def destroy
if (case_log = CaseLog.find_by(id: params[:id]))
if case_log.discard
if @case_log
if @case_log.discard
head :no_content
else
render json: { errors: case_log.errors.messages }, status: :unprocessable_entity
render json: { errors: @case_log.errors.messages }, status: :unprocessable_entity
end
else
render_not_found_json("Case log", params[:id])
end
end
def check_answers
form = FormHandler.instance.get_form("2021_2022")
@case_log = current_user.case_logs.find_by(id: params[:case_log_id])
if @case_log
current_url = request.env["PATH_INFO"]
subsection = form.get_subsection(current_url.split("/")[-2])
render "form/check_answers", locals: { subsection: subsection, form: form }
else
render_not_found_html
end
end
form = FormHandler.instance.get_form("2021_2022")
form.pages.map do |page|
define_method(page.id) do |_errors = {}|
@case_log = current_user.case_logs.find_by(id: params[:case_log_id])
if @case_log
subsection = form.subsection_for_page(page)
render "form/page", locals: { form: form, page: page, subsection: subsection.label }
else
render_not_found_html
end
end
end
private
API_ACTIONS = %w[create show update destroy].freeze
def responses_for_page(page)
page.expected_responses.each_with_object({}) do |question, result|
question_params = params["case_log"][question.id]
if question.type == "date"
day = params["case_log"]["#{question.id}(3i)"]
month = params["case_log"]["#{question.id}(2i)"]
year = params["case_log"]["#{question.id}(1i)"]
next unless [day, month, year].any?(&:present?)
result[question.id] = if day.to_i.between?(1, 31) && month.to_i.between?(1, 12) && year.to_i.between?(2000, 2200)
Date.new(year.to_i, month.to_i, day.to_i)
else
Date.new(0, 1, 1)
end
end
next unless question_params
if %w[checkbox validation_override].include?(question.type)
question.answer_options.keys.reject { |x| x.match(/divider/) }.each do |option|
result[option] = question_params.include?(option) ? 1 : 0
end
else
result[question.id] = question_params
end
result
end
end
def json_api_request?
API_ACTIONS.include?(request["action"]) && request.format.json?
end
@ -173,4 +103,8 @@ private
params.require(:case_log).permit(CaseLog.editable_fields)
end
def find_resource
@case_log = CaseLog.find_by(id: params[:id])
end
end

83
app/controllers/form_controller.rb

@ -0,0 +1,83 @@
class FormController < ApplicationController
before_action :authenticate_user!
before_action :find_resource, only: [:submit_form]
before_action :find_resource_by_named_id, except: [:submit_form]
def submit_form
form = FormHandler.instance.get_form("2021_2022")
if @case_log
page = form.get_page(params[:case_log][:page])
responses_for_page = responses_for_page(page)
if @case_log.update(responses_for_page) && @case_log.has_no_unresolved_soft_errors?
redirect_path = form.next_page_redirect_path(page, @case_log)
redirect_to(send(redirect_path, @case_log))
else
subsection = form.subsection_for_page(page)
render "form/page", locals: { form: form, page: page, subsection: subsection.label }, status: :unprocessable_entity
end
else
render_not_found_html
end
end
def check_answers
form = FormHandler.instance.get_form("2021_2022")
if @case_log
current_url = request.env["PATH_INFO"]
subsection = form.get_subsection(current_url.split("/")[-2])
render "form/check_answers", locals: { subsection: subsection, form: form }
else
render_not_found_html
end
end
form = FormHandler.instance.get_form("2021_2022")
form.pages.map do |page|
define_method(page.id) do |_errors = {}|
if @case_log
subsection = form.subsection_for_page(page)
render "form/page", locals: { form: form, page: page, subsection: subsection.label }
else
render_not_found_html
end
end
end
private
def responses_for_page(page)
page.expected_responses.each_with_object({}) do |question, result|
question_params = params["case_log"][question.id]
if question.type == "date"
day = params["case_log"]["#{question.id}(3i)"]
month = params["case_log"]["#{question.id}(2i)"]
year = params["case_log"]["#{question.id}(1i)"]
next unless [day, month, year].any?(&:present?)
result[question.id] = if day.to_i.between?(1, 31) && month.to_i.between?(1, 12) && year.to_i.between?(2000, 2200)
Date.new(year.to_i, month.to_i, day.to_i)
else
Date.new(0, 1, 1)
end
end
next unless question_params
if %w[checkbox validation_override].include?(question.type)
question.answer_options.keys.reject { |x| x.match(/divider/) }.each do |option|
result[option] = question_params.include?(option) ? 1 : 0
end
else
result[question.id] = question_params
end
result
end
end
def find_resource
@case_log = current_user.case_logs.find_by(id: params[:id])
end
def find_resource_by_named_id
@case_log = current_user.case_logs.find_by(id: params[:case_log_id])
end
end

9
app/controllers/organisations_controller.rb

@ -1,6 +1,7 @@
class OrganisationsController < ApplicationController
before_action :authenticate_user!
before_action :find_organisation
before_action :find_resource
before_action :authenticate_scope!
def show
redirect_to details_organisation_path(@organisation)
@ -20,7 +21,11 @@ class OrganisationsController < ApplicationController
private
def find_organisation
def authenticate_scope!
head :not_found if current_user.organisation != @organisation
end
def find_resource
@organisation = Organisation.find(params[:id])
end
end

2
app/controllers/soft_validations_controller.rb

@ -1,4 +1,6 @@
class SoftValidationsController < ApplicationController
before_action :authenticate_user!
def show
@case_log = CaseLog.find(params[:case_log_id])
page_id = request.env["PATH_INFO"].split("/")[-2]

16
app/controllers/users_controller.rb

@ -2,12 +2,14 @@ class UsersController < ApplicationController
include Devise::Controllers::SignInOut
include Helpers::Email
before_action :authenticate_user!
before_action :find_resource, except: %i[new create]
before_action :authenticate_scope!, except: %i[new create]
def update
if current_user.update(user_params)
bypass_sign_in current_user
if @user.update(user_params)
bypass_sign_in @user
flash[:notice] = I18n.t("devise.passwords.updated")
redirect_to user_path(current_user)
redirect_to user_path(@user)
end
end
@ -48,4 +50,12 @@ private
def user_params
params.require(:user).permit(:email, :name, :password, :role)
end
def find_resource
@user = User.find(params[:id])
end
def authenticate_scope!
head :not_found if current_user != @user
end
end

6
app/models/bulk_upload.rb

@ -15,7 +15,7 @@ class BulkUpload
@content_type = content_type
end
def process
def process(current_user)
return unless valid_content_type?
xlsx = Roo::Spreadsheet.open(@file, extension: :xlsx)
@ -30,8 +30,8 @@ class BulkUpload
owning_organisation = Organisation.find(row[111])
managing_organisation = Organisation.find(row[113])
case_log = CaseLog.create!(
owning_organisation: owning_organisation,
managing_organisation: managing_organisation,
owning_organisation: current_user.organisation,
managing_organisation: current_user.organisation,
)
map_row(row).each do |attr_key, attr_val|
update = case_log.update(attr_key => attr_val)

11
app/models/case_log.rb

@ -30,7 +30,7 @@ end
class CaseLog < ApplicationRecord
include Discard::Model
include Validations::SoftValidations
include Constants::DbEnums
include Constants::CaseLog
include Constants::IncomeRanges
default_scope -> { kept }
@ -156,15 +156,6 @@ class CaseLog < ApplicationRecord
private
RENT_TYPE_MAPPING = {
"Social Rent" => "Social Rent",
"Affordable Rent" => "Affordable Rent",
"London Affordable Rent" => "Affordable Rent",
"Rent To Buy" => "Intermediate Rent",
"London Living Rent" => "Intermediate Rent",
"Other Intermediate Rent Product" => "Intermediate Rent",
}.freeze
def update_status!
self.status = if all_fields_completed? && errors.empty?
"completed"

42
app/models/constants/db_enums.rb → app/models/constants/case_log.rb

@ -1,4 +1,4 @@
module Constants::DbEnums
module Constants::CaseLog
BENEFITCAP = {
"Yes - benefit cap" => 5,
"Yes - removal of the spare room subsidy" => 4,
@ -672,27 +672,31 @@ module Constants::DbEnums
}.freeze
NEEDS_TYPE = {
"General Needs" => 1,
"Supported Housing" => 2,
"General needs" => 1,
"Supported housing" => 2,
}.freeze
ORG_TYPE = {
"LA" => 1,
"PRP" => 2,
LET_TYPE = {
"Social Rent General needs PRP" => 1,
"Social Rent Supported housing PRP" => 2,
"Social Rent General needs LA" => 3,
"Social Rent Supported housing LA" => 4,
"Affordable Rent General needs PRP" => 5,
"Affordable Rent Supported housing PRP" => 6,
"Affordable Rent General needs LA" => 7,
"Affordable Rent Supported housing LA" => 8,
"Intermediate Rent General needs PRP" => 9,
"Intermediate Rent Supported housing PRP" => 10,
"Intermediate Rent General needs LA" => 11,
"Intermediate Rent Supported housing LA" => 12,
}.freeze
LET_TYPE = {
"Social Rent General Needs PRP" => 1,
"Social Rent Supported Housing PRP" => 2,
"Social Rent General Needs LA" => 3,
"Social Rent Supported Housing LA" => 4,
"Affordable Rent General Needs PRP" => 5,
"Affordable Rent Supported Housing PRP" => 6,
"Affordable Rent General Needs LA" => 7,
"Affordable Rent Supported Housing LA" => 8,
"Intermediate Rent General Needs PRP" => 9,
"Intermediate Rent Supported Housing PRP" => 10,
"Intermediate Rent General Needs LA" => 11,
"Intermediate Rent Supported Housing LA" => 12,
RENT_TYPE_MAPPING = {
"Social rent" => "Social Rent",
"Affordable rent" => "Affordable Rent",
"London Affordable rent" => "Affordable Rent",
"Rent to buy" => "Intermediate Rent",
"London living rent" => "Intermediate Rent",
"Other intermediate rent product" => "Intermediate Rent",
}.freeze
end

6
app/models/constants/organisation.rb

@ -0,0 +1,6 @@
module Constants::Organisation
ORG_TYPE = {
"LA" => 1,
"PRP" => 2,
}.freeze
end

2
app/models/organisation.rb

@ -3,7 +3,7 @@ class Organisation < ApplicationRecord
has_many :owned_case_logs, class_name: "CaseLog", foreign_key: "owning_organisation_id"
has_many :managed_case_logs, class_name: "CaseLog", foreign_key: "managing_organisation_id"
include Constants::DbEnums
include Constants::Organisation
enum "Org type": ORG_TYPE, _suffix: true
def case_logs

2
app/models/validations/household_validations.rb

@ -76,7 +76,7 @@ module Validations::HouseholdValidations
def validate_shared_housing_rooms(record)
unless record.unittype_gn.nil?
if record.unittype_gn == "Bed-sit" && record.beds != 1
if record.unittype_gn == "Bed-sit" && record.beds != 1 && record.beds.present?
record.errors.add :unittype_gn, "A bedsit can only have one bedroom"
end

2
config/forms/2021_2022.json

@ -1668,7 +1668,7 @@
"step": 1
}
},
"depends_on": { "needstype": "General Needs" }
"depends_on": { "needstype": "General needs" }
},
"void_or_renewal_date": {
"header": "",

6
config/routes.rb

@ -38,16 +38,16 @@ Rails.application.routes.draw do
end
member do
post "form", to: "case_logs#submit_form"
post "form", to: "form#submit_form"
end
form.pages.map do |page|
get page.id.to_s.dasherize, to: "case_logs##{page.id}"
get page.id.to_s.dasherize, to: "form##{page.id}"
get "#{page.id.to_s.dasherize}/soft-validations", to: "soft_validations#show" if page.has_soft_validations?
end
form.subsections.map do |subsection|
get "#{subsection.id.to_s.dasherize}/check-answers", to: "case_logs#check_answers"
get "#{subsection.id.to_s.dasherize}/check-answers", to: "form#check_answers"
end
end
end

15
docs/adr/adr-012-controller-http-return-statuses.md

@ -0,0 +1,15 @@
### ADR - 012: Controller HTTP return statuses
Controllers assess authentication by 3 criteria:
1. Are you signed in at all?
2. Are you signed in and requesting an action that your role/user type has access to?
3. Are you signed in, requesting an action that your role/user type has access to and requesting a resource that your user has access to.
When these aren't met they fail with the following response types:
1. 401: Unauthorized. Redirect to sign-in page.
2. 401: Unauthorized
3. 404: Not found.
This helps make it harder to determine whether a resource exists or not just by enumerating ids.

172
spec/controllers/case_logs_controller_spec.rb

@ -1,172 +0,0 @@
require "rails_helper"
RSpec.describe CaseLogsController, type: :controller do
let(:valid_session) { {} }
let(:user) { FactoryBot.create(:user) }
let(:case_log) do
FactoryBot.create(
:case_log,
owning_organisation: user.organisation,
managing_organisation: user.organisation,
)
end
let(:id) { case_log.id }
before do
sign_in user
end
context "Collection routes" do
describe "GET #index" do
it "returns a success response" do
get :index, params: {}, session: valid_session
expect(response).to be_successful
end
end
describe "Post #create" do
let(:owning_organisation) { FactoryBot.create(:organisation) }
let(:managing_organisation) { owning_organisation }
let(:params) do
{
"owning_organisation_id": owning_organisation.id,
"managing_organisation_id": managing_organisation.id,
}
end
it "creates a new case log record" do
expect {
post :create, params: params, session: valid_session
}.to change(CaseLog, :count).by(1)
end
it "redirects to that case log" do
post :create, params: params, session: valid_session
expect(response.status).to eq(302)
end
end
end
context "Instance routes" do
describe "GET #show" do
it "returns a success response" do
get :show, params: { id: id }
expect(response).to be_successful
end
end
describe "GET #edit" do
it "returns a success response" do
get :edit, params: { id: id }
expect(response).to be_successful
end
end
end
describe "submit_form" do
let(:case_log_form_params) do
{ accessibility_requirements:
%w[ housingneeds_a
housingneeds_b
housingneeds_c],
page: "accessibility_requirements" }
end
let(:new_case_log_form_params) do
{
accessibility_requirements: %w[housingneeds_c],
page: "accessibility_requirements",
}
end
it "sets checked items to true" do
post :submit_form, params: { id: id, case_log: case_log_form_params }
case_log.reload
expect(case_log.housingneeds_a).to eq("Yes")
expect(case_log.housingneeds_b).to eq("Yes")
expect(case_log.housingneeds_c).to eq("Yes")
end
it "sets previously submitted items to false when resubmitted with new values" do
post :submit_form, params: { id: id, case_log: new_case_log_form_params }
case_log.reload
expect(case_log.housingneeds_a).to eq("No")
expect(case_log.housingneeds_b).to eq("No")
expect(case_log.housingneeds_c).to eq("Yes")
end
context "given a page with checkbox and non-checkbox questions" do
let(:tenant_code) { "BZ355" }
let(:case_log_form_params) do
{ accessibility_requirements:
%w[ housingneeds_a
housingneeds_b
housingneeds_c],
tenant_code: tenant_code,
page: "accessibility_requirements" }
end
let(:questions_for_page) do
[
Form::Question.new(
"accessibility_requirements",
{
"type" => "checkbox",
"answer_options" =>
{ "housingneeds_a" => "Fully wheelchair accessible housing",
"housingneeds_b" => "Wheelchair access to essential rooms",
"housingneeds_c" => "Level access housing",
"housingneeds_f" => "Other disability requirements",
"housingneeds_g" => "No disability requirements",
"divider_a" => true,
"housingneeds_h" => "Do not know",
"divider_b" => true,
"accessibility_requirements_prefer_not_to_say" => "Prefer not to say" },
}, nil
),
Form::Question.new("tenant_code", { "type" => "text" }, nil),
]
end
it "updates both question fields" do
allow_any_instance_of(Form::Page).to receive(:expected_responses).and_return(questions_for_page)
post :submit_form, params: { id: id, case_log: case_log_form_params }
case_log.reload
expect(case_log.housingneeds_a).to eq("Yes")
expect(case_log.housingneeds_b).to eq("Yes")
expect(case_log.housingneeds_c).to eq("Yes")
expect(case_log.tenant_code).to eq(tenant_code)
end
end
context "conditional routing" do
before do
allow_any_instance_of(CaseLogValidator).to receive(:validate_pregnancy).and_return(true)
end
let(:case_log_form_conditional_question_yes_params) do
{
preg_occ: "Yes",
page: "conditional_question",
}
end
let(:case_log_form_conditional_question_no_params) do
{
preg_occ: "No",
page: "conditional_question",
}
end
it "routes to the appropriate conditional page based on the question answer of the current page" do
post :submit_form, params: { id: id, case_log: case_log_form_conditional_question_yes_params }
expect(response).to redirect_to("/case-logs/#{id}/conditional-question-yes-page")
post :submit_form, params: { id: id, case_log: case_log_form_conditional_question_no_params }
expect(response).to redirect_to("/case-logs/#{id}/conditional-question-no-page")
end
end
end
end

2
spec/factories/case_log.rb

@ -52,7 +52,7 @@ FactoryBot.define do
startertenancy { "No" }
tenancylength { 5 }
tenancy { "Secure (including flexible)" }
lettype { "Affordable Rent General Needs LA" }
lettype { "Affordable Rent General needs LA" }
landlord { "This landlord" }
previous_postcode { "SE2 6RT" }
rsnvac { "Tenant abandoned property" }

2
spec/fixtures/complete_case_log.json vendored

@ -127,7 +127,7 @@
"sale_or_letting": "",
"rent_type": "Social Rent",
"intermediate_rent_product_name": "",
"needstype": "General Needs",
"needstype": "General needs",
"sale_completion_date": "01/01/2020",
"purchaser_code": "",
"propcode": "123",

24
spec/models/case_log_spec.rb

@ -790,6 +790,24 @@ RSpec.describe Form, type: :model do
}.not_to raise_error
end
end
context "Validate type of unit" do
it "Cannot be bedsit if no of bedrooms is greater than 1" do
expect {
CaseLog.create!(unittype_gn: "Bed-sit",
beds: 2,
owning_organisation: owning_organisation,
managing_organisation: managing_organisation)
}.to raise_error(ActiveRecord::RecordInvalid)
expect {
CaseLog.create!(unittype_gn: "Bed-sit",
beds: 1,
owning_organisation: owning_organisation,
managing_organisation: managing_organisation)
}.not_to raise_error
end
end
end
describe "status" do
@ -850,8 +868,8 @@ RSpec.describe Form, type: :model do
# rubocop:enable Style/DateTime
net_income_known: "Prefer not to say",
other_hhmemb: 6,
rent_type: "London Living Rent",
needstype: "General Needs",
rent_type: "London living rent",
needstype: "General needs",
})
end
@ -909,7 +927,7 @@ RSpec.describe Form, type: :model do
case_log.reload
record_from_db = ActiveRecord::Base.connection.execute("select lettype from case_logs where id=#{case_log.id}").to_a[0]
expect(case_log.lettype).to eq("Intermediate Rent General Needs PRP")
expect(case_log.lettype).to eq("Intermediate Rent General needs PRP")
expect(record_from_db["lettype"]).to eq(9)
end
end

29
spec/requests/bulk_upload_controller_spec.rb

@ -2,11 +2,37 @@ require "rails_helper"
RSpec.describe BulkUploadController, type: :request do
let(:url) { "/case-logs/bulk-upload" }
let(:organisation) { FactoryBot.create(:organisation) }
let(:user) { FactoryBot.create(:user) }
let(:organisation) { user.organisation }
before do
allow(Organisation).to receive(:find).with(107_242).and_return(organisation)
end
context "a not signed in user" do
describe "GET #show" do
it "does not let you see the bulk upload page" do
get url, headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
describe "POST #bulk upload" do
before do
@file = fixture_file_upload("2021_22_lettings_bulk_upload.xlsx", "application/vnd.ms-excel")
end
it "does not let you submit bulk uploads" do
post url, params: { bulk_upload: { case_log_bulk_upload: @file } }
expect(response).to redirect_to("/users/sign-in")
end
end
end
context "a signed in user" do
before do
sign_in user
end
describe "GET #show" do
before do
get url, params: {}
@ -61,4 +87,5 @@ RSpec.describe BulkUploadController, type: :request do
end
end
end
end
end

120
spec/requests/case_log_controller_spec.rb

@ -180,6 +180,14 @@ RSpec.describe CaseLogsController, type: :request do
allow(FormHandler.instance).to receive(:get_form).and_return(form)
end
context "a user that is not signed in" do
it "does not let the user get case log tasklist pages they don't have access to" do
get "/case-logs/#{case_log.id}", headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
context "a signed in user" do
context "case logs that are owned or managed by your organisation" do
before do
sign_in user
@ -231,35 +239,6 @@ RSpec.describe CaseLogsController, type: :request do
end
end
end
context "form pages" do
let(:headers) { { "Accept" => "text/html" } }
context "case logs that are not owned or managed by your organisation" do
before do
sign_in user
get "/case-logs/#{unauthorized_case_log.id}/person-1-age", headers: headers, params: {}
end
it "does not show form pages for case logs you don't have access to" do
expect(response).to have_http_status(:not_found)
end
end
end
context "check answers pages" do
let(:headers) { { "Accept" => "text/html" } }
context "case logs that are not owned or managed by your organisation" do
before do
sign_in user
get "/case-logs/#{unauthorized_case_log.id}/household-characteristics/check-answers", headers: headers, params: {}
end
it "does not show a check answers for case logs you don't have access to" do
expect(response).to have_http_status(:not_found)
end
end
end
end
end
@ -414,87 +393,4 @@ RSpec.describe CaseLogsController, type: :request do
end
end
end
describe "Submit Form" do
let(:user) { FactoryBot.create(:user) }
let(:form) { Form.new("spec/fixtures/forms/test_form.json") }
let(:organisation) { user.organisation }
let(:case_log) do
FactoryBot.create(
:case_log,
owning_organisation: organisation,
managing_organisation: organisation,
)
end
let(:page_id) { "person_1_age" }
let(:params) do
{
id: case_log.id,
case_log: {
page: page_id,
age1: answer,
},
}
end
before do
allow(FormHandler.instance).to receive(:get_form).and_return(form)
sign_in user
post "/case-logs/#{case_log.id}/form", params: params
end
context "invalid answers" do
let(:answer) { 2000 }
it "re-renders the same page with errors if validation fails" do
expect(response).to have_http_status(:unprocessable_entity)
end
end
context "valid answers" do
let(:answer) { 20 }
it "re-renders the same page with errors if validation fails" do
expect(response).to have_http_status(:redirect)
end
let(:params) do
{
id: case_log.id,
case_log: {
page: page_id,
age1: answer,
age2: 2000,
},
}
end
it "only updates answers that apply to the page being submitted" do
case_log.reload
expect(case_log.age1).to eq(answer)
expect(case_log.age2).to be nil
end
end
context "case logs that are not owned or managed by your organisation" do
let(:answer) { 25 }
let(:other_organisation) { FactoryBot.create(:organisation) }
let(:unauthorized_case_log) do
FactoryBot.create(
:case_log,
owning_organisation: other_organisation,
managing_organisation: other_organisation,
)
end
before do
sign_in user
post "/case-logs/#{unauthorized_case_log.id}/form", params: params
end
it "does not let you post form answers to case logs you don't have access to" do
expect(response).to have_http_status(:not_found)
end
end
end
end

278
spec/requests/form_controller_spec.rb

@ -0,0 +1,278 @@
require "rails_helper"
RSpec.describe FormController, type: :request do
let(:user) { FactoryBot.create(:user) }
let(:organisation) { user.organisation }
let(:other_organisation) { FactoryBot.create(:organisation) }
let!(:case_log) do
FactoryBot.create(
:case_log,
owning_organisation: organisation,
managing_organisation: organisation,
)
end
let!(:unauthorized_case_log) do
FactoryBot.create(
:case_log,
owning_organisation: other_organisation,
managing_organisation: other_organisation,
)
end
let(:headers) { { "Accept" => "text/html" } }
context "a not signed in user" do
describe "GET" do
it "does not let you get case logs pages you don't have access to" do
get "/case-logs/#{case_log.id}/person-1-age", headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
it "does not let you get case log check answer pages you don't have access to" do
get "/case-logs/#{case_log.id}/household-characteristics/check-answers", headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
describe "POST" do
it "does not let you post form answers to case logs you don't have access to" do
post "/case-logs/#{case_log.id}/form", params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
end
context "a signed in user" do
before do
sign_in user
end
describe "GET" do
context "form pages" do
context "case logs that are not owned or managed by your organisation" do
it "does not show form pages for case logs you don't have access to" do
get "/case-logs/#{unauthorized_case_log.id}/person-1-age", headers: headers, params: {}
expect(response).to have_http_status(:not_found)
end
end
end
context "check answers pages" do
context "case logs that are not owned or managed by your organisation" do
it "does not show a check answers for case logs you don't have access to" do
get "/case-logs/#{unauthorized_case_log.id}/household-characteristics/check-answers", headers: headers, params: {}
expect(response).to have_http_status(:not_found)
end
end
end
end
describe "Submit Form" do
context "a form page" do
let(:user) { FactoryBot.create(:user) }
let(:form) { Form.new("spec/fixtures/forms/test_form.json") }
let(:organisation) { user.organisation }
let(:case_log) do
FactoryBot.create(
:case_log,
owning_organisation: organisation,
managing_organisation: organisation,
)
end
let(:page_id) { "person_1_age" }
let(:params) do
{
id: case_log.id,
case_log: {
page: page_id,
age1: answer,
},
}
end
before do
allow(FormHandler.instance).to receive(:get_form).and_return(form)
post "/case-logs/#{case_log.id}/form", params: params
end
context "invalid answers" do
let(:answer) { 2000 }
it "re-renders the same page with errors if validation fails" do
expect(response).to have_http_status(:unprocessable_entity)
end
end
context "valid answers" do
let(:answer) { 20 }
it "re-renders the same page with errors if validation fails" do
expect(response).to have_http_status(:redirect)
end
let(:params) do
{
id: case_log.id,
case_log: {
page: page_id,
age1: answer,
age2: 2000,
},
}
end
it "only updates answers that apply to the page being submitted" do
case_log.reload
expect(case_log.age1).to eq(answer)
expect(case_log.age2).to be nil
end
end
end
context "checkbox questions" do
let(:case_log_form_params) do
{
id: case_log.id,
case_log: {
page: "accessibility_requirements",
accessibility_requirements:
%w[ housingneeds_a
housingneeds_b
housingneeds_c],
},
}
end
let(:new_case_log_form_params) do
{
id: case_log.id,
case_log: {
page: "accessibility_requirements",
accessibility_requirements: %w[housingneeds_c],
},
}
end
it "sets checked items to true" do
post "/case-logs/#{case_log.id}/form", params: case_log_form_params
case_log.reload
expect(case_log.housingneeds_a).to eq("Yes")
expect(case_log.housingneeds_b).to eq("Yes")
expect(case_log.housingneeds_c).to eq("Yes")
end
it "sets previously submitted items to false when resubmitted with new values" do
post "/case-logs/#{case_log.id}/form", params: new_case_log_form_params
case_log.reload
expect(case_log.housingneeds_a).to eq("No")
expect(case_log.housingneeds_b).to eq("No")
expect(case_log.housingneeds_c).to eq("Yes")
end
context "given a page with checkbox and non-checkbox questions" do
let(:tenant_code) { "BZ355" }
let(:case_log_form_params) do
{
id: case_log.id,
case_log: {
page: "accessibility_requirements",
accessibility_requirements:
%w[ housingneeds_a
housingneeds_b
housingneeds_c],
tenant_code: tenant_code,
},
}
end
let(:questions_for_page) do
[
Form::Question.new(
"accessibility_requirements",
{
"type" => "checkbox",
"answer_options" =>
{ "housingneeds_a" => "Fully wheelchair accessible housing",
"housingneeds_b" => "Wheelchair access to essential rooms",
"housingneeds_c" => "Level access housing",
"housingneeds_f" => "Other disability requirements",
"housingneeds_g" => "No disability requirements",
"divider_a" => true,
"housingneeds_h" => "Do not know",
"divider_b" => true,
"accessibility_requirements_prefer_not_to_say" => "Prefer not to say" },
}, nil
),
Form::Question.new("tenant_code", { "type" => "text" }, nil),
]
end
it "updates both question fields" do
allow_any_instance_of(Form::Page).to receive(:expected_responses).and_return(questions_for_page)
post "/case-logs/#{case_log.id}/form", params: case_log_form_params
case_log.reload
expect(case_log.housingneeds_a).to eq("Yes")
expect(case_log.housingneeds_b).to eq("Yes")
expect(case_log.housingneeds_c).to eq("Yes")
expect(case_log.tenant_code).to eq(tenant_code)
end
end
end
context "conditional routing" do
before do
allow_any_instance_of(CaseLogValidator).to receive(:validate_pregnancy).and_return(true)
end
let(:case_log_form_conditional_question_yes_params) do
{
id: case_log.id,
case_log: {
page: "conditional_question",
preg_occ: "Yes",
},
}
end
let(:case_log_form_conditional_question_no_params) do
{
id: case_log.id,
case_log: {
page: "conditional_question",
preg_occ: "No",
},
}
end
it "routes to the appropriate conditional page based on the question answer of the current page" do
post "/case-logs/#{case_log.id}/form", params: case_log_form_conditional_question_yes_params
expect(response).to redirect_to("/case-logs/#{case_log.id}/conditional-question-yes-page")
post "/case-logs/#{case_log.id}/form", params: case_log_form_conditional_question_no_params
expect(response).to redirect_to("/case-logs/#{case_log.id}/conditional-question-no-page")
end
end
context "case logs that are not owned or managed by your organisation" do
let(:answer) { 25 }
let(:other_organisation) { FactoryBot.create(:organisation) }
let(:unauthorized_case_log) do
FactoryBot.create(
:case_log,
owning_organisation: other_organisation,
managing_organisation: other_organisation,
)
end
before do
post "/case-logs/#{unauthorized_case_log.id}/form", params: {}
end
it "does not let you post form answers to case logs you don't have access to" do
expect(response).to have_http_status(:not_found)
end
end
end
end
end

79
spec/requests/organisations_controller_spec.rb

@ -2,12 +2,33 @@ require "rails_helper"
RSpec.describe OrganisationsController, type: :request do
let(:organisation) { user.organisation }
let(:unauthorised_organisation) { FactoryBot.create(:organisation) }
let(:headers) { { "Accept" => "text/html" } }
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:user) { FactoryBot.create(:user, :data_coordinator) }
context "a not signed in user" do
describe "#show" do
let(:user) { FactoryBot.create(:user, :data_coordinator) }
it "does not let you see organisation details from org route" do
get "/organisations/#{organisation.id}", headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
it "does not let you see organisation details from details route" do
get "/organisations/#{organisation.id}/details", headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
it "does not let you see organisation users" do
get "/organisations/#{organisation.id}/users", headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
end
context "a signed in user" do
describe "#show" do
context "organisation that the user belongs to" do
before do
sign_in user
get "/organisations/#{organisation.id}", headers: headers, params: {}
@ -18,10 +39,21 @@ RSpec.describe OrganisationsController, type: :request do
end
end
context "As a data coordinator user" do
let(:user) { FactoryBot.create(:user, :data_coordinator) }
context "organisation that are not in scope for the user, i.e. that they do not belong to" do
before do
sign_in user
get "/organisations/#{unauthorised_organisation.id}", headers: headers, params: {}
end
it "returns not found 404 from org route" do
expect(response).to have_http_status(:not_found)
end
end
end
context "As a data coordinator user" do
context "details tab" do
context "organisation that the user belongs to" do
before do
sign_in user
get "/organisations/#{organisation.id}/details", headers: headers, params: {}
@ -44,7 +76,20 @@ RSpec.describe OrganisationsController, type: :request do
end
end
context "organisation that are not in scope for the user, i.e. that they do not belong to" do
before do
sign_in user
get "/organisations/#{unauthorised_organisation.id}/details", headers: headers, params: {}
end
it "returns not found 404 from org details route" do
expect(response).to have_http_status(:not_found)
end
end
end
context "users tab" do
context "organisation that the user belongs to" do
before do
sign_in user
get "/organisations/#{organisation.id}/users", headers: headers, params: {}
@ -70,12 +115,25 @@ RSpec.describe OrganisationsController, type: :request do
expect(response.body).to include(expected_html)
end
end
context "organisation that are not in scope for the user, i.e. that they do not belong to" do
before do
sign_in user
get "/organisations/#{unauthorised_organisation.id}/users", headers: headers, params: {}
end
it "returns not found 404 from users page" do
expect(response).to have_http_status(:not_found)
end
end
end
end
context "As a data provider user" do
let(:user) { FactoryBot.create(:user) }
context "details tab" do
context "organisation that the user belongs to" do
before do
sign_in user
get "/organisations/#{organisation.id}/details", headers: headers, params: {}
@ -98,15 +156,28 @@ RSpec.describe OrganisationsController, type: :request do
end
end
context "organisation that are not in scope for the user, i.e. that they do not belong to" do
before do
sign_in user
get "/organisations/#{unauthorised_organisation.id}/details", headers: headers, params: {}
end
it "returns not found 404" do
expect(response).to have_http_status(:not_found)
end
end
end
context "users tab" do
before do
sign_in user
get "/organisations/#{organisation.id}/users", headers: headers, params: {}
end
it "should return unauthorised 401" do
it "should return unauthorized 401" do
expect(response).to have_http_status(:unauthorized)
end
end
end
end
end

15
spec/requests/soft_validations_controller_spec.rb

@ -3,8 +3,22 @@ require "rails_helper"
RSpec.describe SoftValidationsController, type: :request do
let(:params) { { case_log_id: case_log.id } }
let(:url) { "/case-logs/#{case_log.id}/net-income/soft-validations" }
let(:user) { FactoryBot.create(:user) }
context "a not signed in user" do
let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
describe "GET #show" do
it "redirects to the sign in page" do
get url, headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
end
context "a signed in user" do
before do
sign_in user
get url, params: {}
end
@ -36,4 +50,5 @@ RSpec.describe SoftValidationsController, type: :request do
end
end
end
end
end

99
spec/requests/user_controller_spec.rb

@ -3,10 +3,44 @@ require_relative "../support/devise"
RSpec.describe UsersController, type: :request do
let(:user) { FactoryBot.create(:user) }
let(:unauthorised_user) { FactoryBot.create(:user) }
let(:headers) { { "Accept" => "text/html" } }
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:new_value) { "new test name" }
let(:params) { { id: user.id, user: { name: new_value } } }
context "a not signed in user" do
describe "#show" do
it "does not let you see user details" do
get "/users/#{user.id}", headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
describe "#edit" do
it "does not let you edit user details" do
get "/users/#{user.id}/edit", headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
describe "#password" do
it "does not let you edit user passwords" do
get "/users/#{user.id}/password/edit", headers: headers, params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
describe "#patch" do
it "does not let you update user details" do
patch "/case-logs/#{user.id}", params: {}
expect(response).to redirect_to("/users/sign-in")
end
end
end
describe "#show" do
context "current user is user" do
before do
sign_in user
get "/users/#{user.id}", headers: headers, params: {}
@ -17,7 +51,20 @@ RSpec.describe UsersController, type: :request do
end
end
context "current user is another user" do
before do
sign_in user
get "/users/#{unauthorised_user.id}", headers: headers, params: {}
end
it "returns not found 404" do
expect(response).to have_http_status(:not_found)
end
end
end
describe "#edit" do
context "current user is user" do
before do
sign_in user
get "/users/#{user.id}/edit", headers: headers, params: {}
@ -28,7 +75,20 @@ RSpec.describe UsersController, type: :request do
end
end
context "current user is another user" do
before do
sign_in user
get "/users/#{unauthorised_user.id}/edit", headers: headers, params: {}
end
it "returns not found 404" do
expect(response).to have_http_status(:not_found)
end
end
end
describe "#edit_password" do
context "current user is user" do
before do
sign_in user
get "/users/#{user.id}/password/edit", headers: headers, params: {}
@ -38,4 +98,43 @@ RSpec.describe UsersController, type: :request do
expect(page).to have_content("Change your password")
end
end
context "current user is another user" do
before do
sign_in user
get "/users/#{unauthorised_user.id}/edit", headers: headers, params: {}
end
it "returns not found 404" do
expect(response).to have_http_status(:not_found)
end
end
end
describe "#update" do
context "current user is user" do
before do
sign_in user
patch "/users/#{user.id}", headers: headers, params: params
end
it "updates the user" do
user.reload
expect(user.name).to eq(new_value)
end
end
context "current user is another user" do
let(:params) { { id: unauthorised_user.id, user: { name: new_value } } }
before do
sign_in user
patch "/users/#{unauthorised_user.id}", headers: headers, params: params
end
it "returns not found 404" do
expect(response).to have_http_status(:not_found)
end
end
end
end

Loading…
Cancel
Save