From 9cfb0a02d14e0ddf40fcac138d17be92c9b626e4 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Tue, 24 Sep 2024 20:38:12 +0100 Subject: [PATCH] Add validation on programmes For models which have a relation to programmes and sessions, we need to validate that the programme is one of the programmes in the session. It's difficult to do this purely through the database structure because we have many to many relationships between programmes and sessions and sessions and patients; while the triage and vaccination record model needs a reference to all three in one record. --- app/models/consent_form.rb | 2 + app/models/session.rb | 8 +++ app/models/triage.rb | 2 + app/models/vaccination_record.rb | 2 + .../app_activity_log_component_spec.rb | 2 + ...pp_session_patient_table_component_spec.rb | 6 +- .../app_triage_form_component_spec.rb | 5 +- .../app_triage_notes_component_spec.rb | 7 ++- ...ccination_record_summary_component_spec.rb | 1 + ...vaccination_record_table_component_spec.rb | 1 + ...session_patient_table_component_preview.rb | 6 +- .../concerns/patient_sorting_concern_spec.rb | 27 +++++++-- .../vaccination_mailer_concern_spec.rb | 15 ++++- spec/factories/patient_sessions.rb | 55 +++++++++++++++++-- spec/factories/sessions.rb | 4 +- spec/factories/vaccination_records.rb | 6 +- spec/mailers/consent_mailer_spec.rb | 4 +- spec/mailers/vaccination_mailer_spec.rb | 18 +++++- spec/models/consent_form_spec.rb | 35 +++++++++--- spec/models/patient_session_spec.rb | 28 +++++++--- spec/models/triage_spec.rb | 4 +- spec/policies/session_policy_spec.rb | 16 +++--- 22 files changed, 200 insertions(+), 54 deletions(-) diff --git a/app/models/consent_form.rb b/app/models/consent_form.rb index 7d3cef291..c0b4631cd 100644 --- a/app/models/consent_form.rb +++ b/app/models/consent_form.rb @@ -120,6 +120,8 @@ class ConsentForm < ApplicationRecord normalizes :parent_email, with: -> { _1.blank? ? nil : _1.to_s.downcase.strip } + validates :programme, inclusion: { in: -> { _1.session.programmes } } + validates :address_line_1, :address_line_2, :address_town, diff --git a/app/models/session.rb b/app/models/session.rb index f2332a621..d18c11aa8 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -64,6 +64,8 @@ class Session < ApplicationRecord after_initialize :set_timeline_attributes after_validation :set_timeline_timestamps + validate :programmes_part_of_team + on_wizard_step :location, exact: true do validates :location_id, presence: true end @@ -127,6 +129,12 @@ def open_for_consent? private + def programmes_part_of_team + return if programmes.empty? + + errors.add(:programmes, :inclusion) if programmes.map(&:team).uniq != [team] + end + def set_timeline_attributes unless send_consent_reminders_at.nil? if send_consent_requests_at + DEFAULT_DAYS_FOR_REMINDER.days == diff --git a/app/models/triage.rb b/app/models/triage.rb index 85958aa09..168dde46c 100644 --- a/app/models/triage.rb +++ b/app/models/triage.rb @@ -50,5 +50,7 @@ class Triage < ApplicationRecord encrypts :notes + validates :programme, inclusion: { in: -> { _1.patient_session.programmes } } + validates :notes, length: { maximum: 1000 } end diff --git a/app/models/vaccination_record.rb b/app/models/vaccination_record.rb index 8cfb9439a..6a3d16797 100644 --- a/app/models/vaccination_record.rb +++ b/app/models/vaccination_record.rb @@ -118,6 +118,8 @@ class VaccinationRecord < ApplicationRecord encrypts :notes + validates :programme, inclusion: { in: -> { _1.patient_session.programmes } } + validates :notes, length: { maximum: 1000 } validates :delivery_site, diff --git a/spec/components/app_activity_log_component_spec.rb b/spec/components/app_activity_log_component_spec.rb index abd05c08f..4618b78c2 100644 --- a/spec/components/app_activity_log_component_spec.rb +++ b/spec/components/app_activity_log_component_spec.rb @@ -60,6 +60,7 @@ create( :triage, :needs_follow_up, + programme:, patient_session:, created_at: Time.zone.parse("2024-05-30 14:00"), notes: "Some notes", @@ -68,6 +69,7 @@ create( :triage, :ready_to_vaccinate, + programme:, patient_session:, created_at: Time.zone.parse("2024-05-30 14:30"), performed_by: user diff --git a/spec/components/app_session_patient_table_component_spec.rb b/spec/components/app_session_patient_table_component_spec.rb index 10def5e8a..8a01c4e22 100644 --- a/spec/components/app_session_patient_table_component_spec.rb +++ b/spec/components/app_session_patient_table_component_spec.rb @@ -60,7 +60,11 @@ def have_column(text) patient_sessions:, section: :matching, consent_form: - create(:consent_form, session: patient_sessions.first.session), + create( + :consent_form, + programme:, + session: patient_sessions.first.session + ), columns: %i[name postcode dob select_for_matching] ) end diff --git a/spec/components/app_triage_form_component_spec.rb b/spec/components/app_triage_form_component_spec.rb index 2a48d3d9a..6d6b5c215 100644 --- a/spec/components/app_triage_form_component_spec.rb +++ b/spec/components/app_triage_form_component_spec.rb @@ -3,7 +3,8 @@ describe AppTriageFormComponent, type: :component do subject(:rendered) { render_inline(component) } - let(:patient_session) { create(:patient_session) } + let(:programme) { create(:programme) } + let(:patient_session) { create(:patient_session, programme:) } let(:component) { described_class.new(patient_session:, url: "#") } it { should have_text("Is it safe to vaccinate") } @@ -19,7 +20,7 @@ end context "patient_session has existing triage" do - before { create(:triage, :needs_follow_up, patient_session:) } + before { create(:triage, :needs_follow_up, programme:, patient_session:) } it { should_not be_nil } it { should be_needs_follow_up } diff --git a/spec/components/app_triage_notes_component_spec.rb b/spec/components/app_triage_notes_component_spec.rb index dc50075d9..090d362d9 100644 --- a/spec/components/app_triage_notes_component_spec.rb +++ b/spec/components/app_triage_notes_component_spec.rb @@ -4,7 +4,9 @@ subject(:rendered) { render_inline(component) } let(:component) { described_class.new(patient_session:) } - let(:patient_session) { create(:patient_session) } + + let(:programme) { create(:programme) } + let(:patient_session) { create(:patient_session, programme:) } context "triage notes are not present" do it "does not render" do @@ -23,6 +25,7 @@ create( :triage, :ready_to_vaccinate, + programme:, notes: "Some notes", patient_session:, performed_by: @@ -41,7 +44,7 @@ end context "multiple triage notes are present" do - before { create_list(:triage, 2, patient_session:) } + before { create_list(:triage, 2, programme:, patient_session:) } it "renders" do expect(component).to be_render diff --git a/spec/components/app_vaccination_record_summary_component_spec.rb b/spec/components/app_vaccination_record_summary_component_spec.rb index d1c4792c4..8067d6335 100644 --- a/spec/components/app_vaccination_record_summary_component_spec.rb +++ b/spec/components/app_vaccination_record_summary_component_spec.rb @@ -22,6 +22,7 @@ let(:vaccination_record) do create( :vaccination_record, + programme:, administered_at:, batch:, vaccine:, diff --git a/spec/components/app_vaccination_record_table_component_spec.rb b/spec/components/app_vaccination_record_table_component_spec.rb index abb12e350..db88536d4 100644 --- a/spec/components/app_vaccination_record_table_component_spec.rb +++ b/spec/components/app_vaccination_record_table_component_spec.rb @@ -10,6 +10,7 @@ [ create( :vaccination_record, + programme:, administered_at: Time.zone.local(2020, 9, 1), patient: create( diff --git a/spec/components/previews/app_session_patient_table_component_preview.rb b/spec/components/previews/app_session_patient_table_component_preview.rb index 28bb0d95c..ce6a4e6ea 100644 --- a/spec/components/previews/app_session_patient_table_component_preview.rb +++ b/spec/components/previews/app_session_patient_table_component_preview.rb @@ -19,7 +19,9 @@ def check_consent end def matching_consent_form_to_a_patient - patient_sessions = create_list(:patient_session, 2, :added_to_session) + programme = create(:programme) + patient_sessions = + create_list(:patient_session, 2, :added_to_session, programme:) # add a common name to one of the patients above patient_sessions.first.patient.update!(common_name: "Bobby") @@ -30,7 +32,7 @@ def matching_consent_form_to_a_patient end consent_form = - create(:consent_form, session: patient_sessions.first.session) + create(:consent_form, programme:, session: patient_sessions.first.session) render AppSessionPatientTableComponent.new( patient_sessions:, diff --git a/spec/controllers/concerns/patient_sorting_concern_spec.rb b/spec/controllers/concerns/patient_sorting_concern_spec.rb index e2e3b74a7..b91488c09 100644 --- a/spec/controllers/concerns/patient_sorting_concern_spec.rb +++ b/spec/controllers/concerns/patient_sorting_concern_spec.rb @@ -24,13 +24,32 @@ def initialize(params) create(:patient, first_name: "Casey", date_of_birth: Date.new(2010, 1, 3)) end - let(:session) { create(:session) } + let(:programme) { create(:programme) } + let(:session) { create(:session, programme:) } let(:patient_sessions) do [ - create(:patient_session, :added_to_session, patient: alex, session:), - create(:patient_session, :delay_vaccination, patient: blair, session:), - create(:patient_session, :vaccinated, patient: casey, session:) + create( + :patient_session, + :added_to_session, + patient: alex, + programme:, + session: + ), + create( + :patient_session, + :delay_vaccination, + patient: blair, + programme:, + session: + ), + create( + :patient_session, + :vaccinated, + patient: casey, + programme:, + session: + ) ] end diff --git a/spec/controllers/concerns/vaccination_mailer_concern_spec.rb b/spec/controllers/concerns/vaccination_mailer_concern_spec.rb index 12ff110c2..e396a57dc 100644 --- a/spec/controllers/concerns/vaccination_mailer_concern_spec.rb +++ b/spec/controllers/concerns/vaccination_mailer_concern_spec.rb @@ -14,7 +14,9 @@ let(:consent) { create(:consent, :given, :recorded, programme:, route:) } let(:patient) { create(:patient, consents: [consent]) } let(:patient_session) { create(:patient_session, session:, patient:) } - let(:vaccination_record) { create(:vaccination_record, patient_session:) } + let(:vaccination_record) do + create(:vaccination_record, programme:, patient_session:) + end context "when the vaccination has taken place" do it "sends an email" do @@ -33,7 +35,12 @@ context "when the vaccination hasn't taken place" do let(:vaccination_record) do - create(:vaccination_record, :not_administered, patient_session:) + create( + :vaccination_record, + :not_administered, + programme:, + patient_session: + ) end it "sends an email" do @@ -52,7 +59,9 @@ context "when the consent was done through gillick assessment" do let(:route) { "self_consent" } - let(:vaccination_record) { create(:vaccination_record, patient_session:) } + let(:vaccination_record) do + create(:vaccination_record, programme:, patient_session:) + end it "doesn't send an email" do expect { send_vaccination_confirmation }.not_to have_enqueued_mail diff --git a/spec/factories/patient_sessions.rb b/spec/factories/patient_sessions.rb index 1904f157b..e0c6f8879 100644 --- a/spec/factories/patient_sessions.rb +++ b/spec/factories/patient_sessions.rb @@ -101,6 +101,7 @@ association( :triage, :ready_to_vaccinate, + programme:, notes: "Okay to vaccinate", performed_by: created_by ) @@ -117,7 +118,14 @@ end triage do - [association(:triage, :do_not_vaccinate, performed_by: created_by)] + [ + association( + :triage, + :do_not_vaccinate, + programme:, + performed_by: created_by + ) + ] end end @@ -130,7 +138,14 @@ end triage do - [association(:triage, :needs_follow_up, performed_by: created_by)] + [ + association( + :triage, + :needs_follow_up, + programme:, + performed_by: created_by + ) + ] end end @@ -143,7 +158,14 @@ end triage do - [association(:triage, :delay_vaccination, performed_by: created_by)] + [ + association( + :triage, + :delay_vaccination, + programme:, + performed_by: created_by + ) + ] end vaccination_records do @@ -178,7 +200,14 @@ end triage do - [association(:triage, :ready_to_vaccinate, performed_by: created_by)] + [ + association( + :triage, + :ready_to_vaccinate, + programme:, + performed_by: created_by + ) + ] end vaccination_records do @@ -206,7 +235,14 @@ end triage do - [association(:triage, :ready_to_vaccinate, performed_by: created_by)] + [ + association( + :triage, + :ready_to_vaccinate, + programme:, + performed_by: created_by + ) + ] end vaccination_records do @@ -232,7 +268,14 @@ end triage do - [association(:triage, :ready_to_vaccinate, performed_by: created_by)] + [ + association( + :triage, + :ready_to_vaccinate, + programme:, + performed_by: created_by + ) + ] end vaccination_records do diff --git a/spec/factories/sessions.rb b/spec/factories/sessions.rb index 3c6b99406..238ed8ff5 100644 --- a/spec/factories/sessions.rb +++ b/spec/factories/sessions.rb @@ -26,10 +26,10 @@ # FactoryBot.define do factory :session do - transient { programme { association :programme, team: } } + transient { programme { association :programme } } - team programmes { [programme] } + team { programmes.first&.team || association(:team) } location { association :location, :school } date { Time.zone.today } diff --git a/spec/factories/vaccination_records.rb b/spec/factories/vaccination_records.rb index 8f820754f..edb3f3d64 100644 --- a/spec/factories/vaccination_records.rb +++ b/spec/factories/vaccination_records.rb @@ -44,11 +44,13 @@ factory :vaccination_record do transient do session { association :session, programme: } - patient { association :patient } + patient { association :patient, school: session.location } end programme - patient_session { association :patient_session, patient:, session: } + patient_session do + association :patient_session, programme:, patient:, session: + end recorded_at { "2023-06-09" } delivery_site { "left_arm_upper_position" } diff --git a/spec/mailers/consent_mailer_spec.rb b/spec/mailers/consent_mailer_spec.rb index 7407b6c54..7852a5ed0 100644 --- a/spec/mailers/consent_mailer_spec.rb +++ b/spec/mailers/consent_mailer_spec.rb @@ -6,13 +6,15 @@ described_class.with(consent_form:).confirmation_injection end + let(:programme) { create(:programme, :flu) } let(:consent_form) do create( :consent_form, :recorded, :refused, reason: :contains_gelatine, - session: create(:session, programme: create(:programme, :flu)) + programme:, + session: create(:session, programme:) ) end diff --git a/spec/mailers/vaccination_mailer_spec.rb b/spec/mailers/vaccination_mailer_spec.rb index 639aa1281..97c29485c 100644 --- a/spec/mailers/vaccination_mailer_spec.rb +++ b/spec/mailers/vaccination_mailer_spec.rb @@ -18,7 +18,9 @@ create(:patient, :consent_given_triage_not_needed, programme:) end let(:patient_session) { create(:patient_session, patient:, session:) } - let(:vaccination_record) { create(:vaccination_record, patient_session:) } + let(:vaccination_record) do + create(:vaccination_record, programme:, patient_session:) + end it do expect(mail).to have_attributes( @@ -66,7 +68,12 @@ subject { personalisation[:today_or_date_of_vaccination] } let(:vaccination_record) do - create(:vaccination_record, patient_session:, recorded_at:) + create( + :vaccination_record, + programme:, + patient_session:, + recorded_at: + ) end context "when the vaccination was recorded today" do @@ -97,7 +104,12 @@ end let(:patient_session) { create(:patient_session, session:, patient:) } let(:vaccination_record) do - create(:vaccination_record, :not_administered, patient_session:) + create( + :vaccination_record, + :not_administered, + programme:, + patient_session: + ) end it do diff --git a/spec/models/consent_form_spec.rb b/spec/models/consent_form_spec.rb index 825b6ec5f..0aa97592f 100644 --- a/spec/models/consent_form_spec.rb +++ b/spec/models/consent_form_spec.rb @@ -567,9 +567,11 @@ let(:session) { create(:session, programme:) } let(:consent) { create(:consent, programme:) } let(:unmatched_consent_form) do - create(:consent_form, consent: nil, session:) + create(:consent_form, consent: nil, programme:, session:) + end + let(:matched_consent_form) do + create(:consent_form, consent:, programme:, session:) end - let(:matched_consent_form) { create(:consent_form, consent:, session:) } it "returns unmatched consent forms" do expect(described_class.unmatched).to include unmatched_consent_form @@ -582,9 +584,11 @@ let(:session) { create(:session, programme:) } let(:consent) { create(:consent, programme:) } let(:recorded_consent_form) do - create(:consent_form, :recorded, consent:, session:) + create(:consent_form, :recorded, programme:, consent:, session:) + end + let(:draft_consent_form) do + create(:consent_form, programme:, consent:, session:) end - let(:draft_consent_form) { create(:consent_form, consent:, session:) } it "returns unmatched consent forms" do expect(described_class.recorded).to include recorded_consent_form @@ -838,16 +842,25 @@ end it "resets unused fields" do - session = create(:session) + programme = create(:programme) + + session = create(:session, programme:) consent_form = - build(:consent_form, common_name: "John", use_common_name: true, session:) + build( + :consent_form, + programme:, + common_name: "John", + use_common_name: true, + session: + ) consent_form.update!(use_common_name: false) expect(consent_form.common_name).to be_nil consent_form = build( :consent_form, + programme:, response: "refused", reason: "contains_gelatine", reason_notes: "I'm vegan", @@ -858,11 +871,17 @@ expect(consent_form.reason_notes).to be_nil consent_form = - build(:consent_form, gp_response: "yes", gp_name: "Dr. Foo", session:) + build( + :consent_form, + programme:, + gp_response: "yes", + gp_name: "Dr. Foo", + session: + ) consent_form.update!(gp_response: "no") expect(consent_form.gp_name).to be_nil - consent_form = build(:consent_form, session:) + consent_form = build(:consent_form, programme:, session:) consent_form.update!(response: "refused") expect(consent_form.gp_response).to be_nil expect(consent_form.address_line_1).to be_nil diff --git a/spec/models/patient_session_spec.rb b/spec/models/patient_session_spec.rb index 92ce1231b..8fd9e6758 100644 --- a/spec/models/patient_session_spec.rb +++ b/spec/models/patient_session_spec.rb @@ -25,11 +25,14 @@ # describe PatientSession do + let(:programme) { create(:programme) } + describe "#triage" do it "returns the triage records in ascending order" do - patient_session = create(:patient_session) - later_triage = create(:triage, patient_session:) - earlier_triage = create(:triage, patient_session:, updated_at: 1.day.ago) + patient_session = create(:patient_session, programme:) + later_triage = create(:triage, programme:, patient_session:) + earlier_triage = + create(:triage, programme:, patient_session:, updated_at: 1.day.ago) expect(patient_session.triage).to eq [earlier_triage, later_triage] end @@ -37,10 +40,12 @@ describe "#vaccine_record" do it "returns the last non-draft vaccination record" do - patient_session = create(:patient_session) - vaccination_record = create(:vaccination_record, patient_session:) + patient_session = create(:patient_session, programme:) + vaccination_record = + create(:vaccination_record, programme:, patient_session:) vaccination_record.update!(recorded_at: 1.day.ago) - draft_vaccination_record = create(:vaccination_record, patient_session:) + draft_vaccination_record = + create(:vaccination_record, programme:, patient_session:) draft_vaccination_record.update!(recorded_at: nil) expect(patient_session.vaccination_record).to eq vaccination_record @@ -50,7 +55,6 @@ describe "#latest_consents" do subject(:latest_consents) { patient_session.latest_consents } - let(:programme) { create(:programme) } let(:patient_session) { create(:patient_session, programme:, patient:) } context "multiple consent given responses from different parents" do @@ -116,15 +120,21 @@ describe "#latest_triage" do it "returns the latest triage record" do - patient_session = create(:patient_session) + patient_session = create(:patient_session, programme:) create( :triage, + programme:, status: :needs_follow_up, created_at: 1.day.ago, patient_session: ) later_triage = - create(:triage, status: :ready_to_vaccinate, patient_session:) + create( + :triage, + programme:, + status: :ready_to_vaccinate, + patient_session: + ) expect(patient_session.latest_triage).to eq later_triage end diff --git a/spec/models/triage_spec.rb b/spec/models/triage_spec.rb index b390414d5..60501a31d 100644 --- a/spec/models/triage_spec.rb +++ b/spec/models/triage_spec.rb @@ -26,8 +26,8 @@ # fk_rails_... (programme_id => programmes.id) # -describe Triage, type: :model do - subject(:triage) { build(:triage) } +describe Triage do + subject(:triage) { create(:triage) } describe "validations" do it { should_not validate_presence_of(:notes) } diff --git a/spec/policies/session_policy_spec.rb b/spec/policies/session_policy_spec.rb index 014c7d297..73e588ad3 100644 --- a/spec/policies/session_policy_spec.rb +++ b/spec/policies/session_policy_spec.rb @@ -4,13 +4,15 @@ describe "Scope#resolve" do subject { SessionPolicy::Scope.new(user, Session).resolve } - let(:users_team) { create :team } - let(:another_team) { create :team } - let(:user) { create :user, teams: [users_team] } - let(:users_teams_session) { create :session, team: users_team } - let(:another_teams_session) { create :session, team: another_team } + let(:team) { create(:team) } + let(:user) { create(:user, teams: [team]) } - it { should include users_teams_session } - it { should_not include another_teams_session } + let(:users_teams_session) do + create(:session, programme: create(:programme, team:)) + end + let(:another_teams_session) { create(:session) } + + it { should include(users_teams_session) } + it { should_not include(another_teams_session) } end end