diff --git a/app/controllers/api/v1/event_procedures_controller.rb b/app/controllers/api/v1/event_procedures_controller.rb index d62647a..53d021e 100644 --- a/app/controllers/api/v1/event_procedures_controller.rb +++ b/app/controllers/api/v1/event_procedures_controller.rb @@ -54,7 +54,7 @@ def update def destroy authorize(event_procedure) - result = EventProcedures::Destroy.result(id: event_procedure.id.to_s) + result = EventProcedures::Destroy.result(id: event_procedure.id.to_s, scope: policy_scope(EventProcedure)) if result.success? deleted_successfully_render(result.event_procedure) diff --git a/app/controllers/api/v1/hospitals_controller.rb b/app/controllers/api/v1/hospitals_controller.rb index 5936eaa..2ae41cd 100644 --- a/app/controllers/api/v1/hospitals_controller.rb +++ b/app/controllers/api/v1/hospitals_controller.rb @@ -36,7 +36,7 @@ def update def destroy authorize(hospital) - result = Hospitals::Destroy.result(id: hospital.id.to_s) + result = Hospitals::Destroy.result(id: hospital.id.to_s, scope: policy_scope(Hospital)) if result.success? deleted_successfully_render(result.hospital) diff --git a/app/controllers/api/v1/medical_shifts_controller.rb b/app/controllers/api/v1/medical_shifts_controller.rb index 4f5b59c..41c7d63 100644 --- a/app/controllers/api/v1/medical_shifts_controller.rb +++ b/app/controllers/api/v1/medical_shifts_controller.rb @@ -68,7 +68,7 @@ def update def destroy authorize(medical_shift) - result = MedicalShifts::Destroy.result(id: medical_shift.id.to_s) + result = MedicalShifts::Destroy.result(id: medical_shift.id.to_s, scope: policy_scope(MedicalShift)) if result.success? deleted_successfully_render(result.medical_shift) diff --git a/app/controllers/api/v1/patients_controller.rb b/app/controllers/api/v1/patients_controller.rb index d6a38cd..5e7972c 100644 --- a/app/controllers/api/v1/patients_controller.rb +++ b/app/controllers/api/v1/patients_controller.rb @@ -40,7 +40,7 @@ def update def destroy authorize(patient) - result = Patients::Destroy.result(id: patient.id.to_s) + result = Patients::Destroy.result(id: patient.id.to_s, scope: policy_scope(Patient)) if result.success? deleted_successfully_render(result.patient) diff --git a/app/controllers/api/v1/procedures_controller.rb b/app/controllers/api/v1/procedures_controller.rb index 289eb1b..ce80fa7 100644 --- a/app/controllers/api/v1/procedures_controller.rb +++ b/app/controllers/api/v1/procedures_controller.rb @@ -38,7 +38,7 @@ def update def destroy authorize(procedure) - result = Procedures::Destroy.result(id: procedure.id.to_s) + result = Procedures::Destroy.result(id: procedure.id.to_s, scope: policy_scope(Procedure)) if result.success? deleted_successfully_render(result.procedure) diff --git a/app/operations/event_procedures/destroy.rb b/app/operations/event_procedures/destroy.rb index 5819692..dc6e95b 100644 --- a/app/operations/event_procedures/destroy.rb +++ b/app/operations/event_procedures/destroy.rb @@ -3,11 +3,12 @@ module EventProcedures class Destroy < Actor input :id, type: String + input :scope, type: Enumerable, default: -> { EventProcedure.all } output :event_procedure, type: EventProcedure def call - self.event_procedure = EventProcedure.find(id) + self.event_procedure = scope.find(id) fail!(error: :cannot_destroy) unless event_procedure.destroy end diff --git a/app/operations/hospitals/destroy.rb b/app/operations/hospitals/destroy.rb index fc121da..5590aff 100644 --- a/app/operations/hospitals/destroy.rb +++ b/app/operations/hospitals/destroy.rb @@ -3,11 +3,12 @@ module Hospitals class Destroy < Actor input :id, type: String + input :scope, type: Enumerable, default: -> { Hospital.all } output :hospital, type: Hospital def call - self.hospital = Hospital.find(id) + self.hospital = scope.find(id) fail!(error: :cannot_destroy) unless hospital.destroy end diff --git a/app/operations/medical_shifts/destroy.rb b/app/operations/medical_shifts/destroy.rb index 79b0c97..f3783ec 100644 --- a/app/operations/medical_shifts/destroy.rb +++ b/app/operations/medical_shifts/destroy.rb @@ -3,10 +3,11 @@ module MedicalShifts class Destroy < Actor input :id, type: String + input :scope, type: Enumerable, default: -> { MedicalShift.all } output :medical_shift, type: MedicalShift def call - self.medical_shift = MedicalShift.find(id) + self.medical_shift = scope.find(id) fail!(error: :cannot_destroy) unless medical_shift.destroy end diff --git a/app/operations/patients/destroy.rb b/app/operations/patients/destroy.rb index dbe64eb..c1e0448 100644 --- a/app/operations/patients/destroy.rb +++ b/app/operations/patients/destroy.rb @@ -3,11 +3,12 @@ module Patients class Destroy < Actor input :id, type: String + input :scope, type: Enumerable, default: -> { Patient.all } output :patient, type: Patient def call - self.patient = Patient.find(id) + self.patient = scope.find(id) fail!(error: :cannot_destroy) unless patient.destroy end diff --git a/app/operations/procedures/destroy.rb b/app/operations/procedures/destroy.rb index e7f1315..31ba61d 100644 --- a/app/operations/procedures/destroy.rb +++ b/app/operations/procedures/destroy.rb @@ -3,11 +3,12 @@ module Procedures class Destroy < Actor input :id, type: String + input :scope, type: Enumerable, default: -> { Procedure.all } output :procedure, type: Procedure def call - self.procedure = Procedure.find(id) + self.procedure = scope.find(id) fail!(error: :cannot_destroy) unless procedure.destroy end diff --git a/app/pdfs/medical_shifts_report_pdf.rb b/app/pdfs/medical_shifts_report_pdf.rb index f35a0ee..64701e3 100644 --- a/app/pdfs/medical_shifts_report_pdf.rb +++ b/app/pdfs/medical_shifts_report_pdf.rb @@ -70,7 +70,7 @@ def add_item_line(left_text, right_text) end def truncate_text(text, length = 35) - text.length > length ? "#..." : text + text.length > length ? "#{text[0, length]}..." : text end def item_shift(item) diff --git a/spec/operations/event_procedures/destroy_spec.rb b/spec/operations/event_procedures/destroy_spec.rb index 0b0f00b..0bdacb8 100644 --- a/spec/operations/event_procedures/destroy_spec.rb +++ b/spec/operations/event_procedures/destroy_spec.rb @@ -19,27 +19,31 @@ context "when event_procedure cannot be destroyed" do let!(:event_procedure) { create(:event_procedure) } - it "is failure" do - allow(EventProcedure).to receive(:find).with(event_procedure.id.to_s).and_return(event_procedure) - allow(event_procedure).to receive(:destroy).and_return(false) + before do + allow_any_instance_of(EventProcedure).to receive(:destroy).and_return(false) # rubocop:disable RSpec/AnyInstance + end + it "is failure" do expect(described_class.result(id: event_procedure.id.to_s)).to be_failure end it "does not destroy event_procedure" do - allow(EventProcedure).to receive(:find).with(event_procedure.id.to_s).and_return(event_procedure) - allow(event_procedure).to receive(:destroy).and_return(false) - expect { described_class.result(id: event_procedure.id.to_s) }.not_to change(EventProcedure, :count) end it "returns error message" do - allow(EventProcedure).to receive(:find).with(event_procedure.id.to_s).and_return(event_procedure) - allow(event_procedure).to receive(:destroy).and_return(false) + expect(described_class.result(id: event_procedure.id.to_s).error).to eq(:cannot_destroy) + end + end - result = described_class.result(id: event_procedure.id.to_s) + context "when event_procedure is outside the given scope" do + let!(:event_procedure) { create(:event_procedure) } + let(:empty_scope) { EventProcedure.none } - expect(result.error).to eq(:cannot_destroy) + it "raises ActiveRecord::RecordNotFound" do + expect do + described_class.result(id: event_procedure.id.to_s, scope: empty_scope) + end.to raise_error(ActiveRecord::RecordNotFound) end end diff --git a/spec/operations/hospitals/destroy_spec.rb b/spec/operations/hospitals/destroy_spec.rb index f33df44..e6c7f44 100644 --- a/spec/operations/hospitals/destroy_spec.rb +++ b/spec/operations/hospitals/destroy_spec.rb @@ -19,28 +19,34 @@ context "when hospital cannot be destroyed" do let!(:hospital) { create(:hospital) } - it "is failure" do - allow(Hospital).to receive(:find).with(hospital.id.to_s).and_return(hospital) - allow(hospital).to receive(:destroy).and_return(false) + before do + allow_any_instance_of(Hospital).to receive(:destroy).and_return(false) # rubocop:disable RSpec/AnyInstance + end + it "is failure" do expect(described_class.result(id: hospital.id.to_s)).to be_failure end it "does not destroy hospital" do - allow(Hospital).to receive(:find).with(hospital.id.to_s).and_return(hospital) - allow(hospital).to receive(:destroy).and_return(false) - expect { described_class.result(id: hospital.id.to_s) }.not_to change(Hospital, :count) end it "returns error cannot_destroy" do - allow(Hospital).to receive(:find).with(hospital.id.to_s).and_return(hospital) - allow(hospital).to receive(:destroy).and_return(false) - expect(described_class.result(id: hospital.id.to_s).error).to eq(:cannot_destroy) end end + context "when hospital is outside the given scope" do + let!(:hospital) { create(:hospital) } + let(:empty_scope) { Hospital.none } + + it "raises ActiveRecord::RecordNotFound" do + expect do + described_class.result(id: hospital.id.to_s, scope: empty_scope) + end.to raise_error(ActiveRecord::RecordNotFound) + end + end + context "when hospital with given id does not exist" do it "raises ActiveRecord::RecordNotFound" do expect { described_class.result(id: "non-existent-id") }.to raise_error(ActiveRecord::RecordNotFound) diff --git a/spec/operations/medical_shifts/destroy_spec.rb b/spec/operations/medical_shifts/destroy_spec.rb index e5f2314..024fc4e 100644 --- a/spec/operations/medical_shifts/destroy_spec.rb +++ b/spec/operations/medical_shifts/destroy_spec.rb @@ -19,8 +19,8 @@ let(:medical_shift) { create(:medical_shift) } before do - allow(MedicalShift).to receive(:find).with(medical_shift.id.to_s).and_return(medical_shift) - allow(medical_shift).to receive(:destroy).and_return(false) + medical_shift + allow_any_instance_of(MedicalShift).to receive(:destroy).and_return(false) # rubocop:disable RSpec/AnyInstance end it { expect(result).to be_failure } @@ -28,7 +28,18 @@ it { expect(result.error).to eq(:cannot_destroy) } end - context "when event_procedure with given id doesn't exist" do + context "when medical_shift is outside the given scope" do + let(:medical_shift) { create(:medical_shift) } + let(:empty_scope) { MedicalShift.none } + + it "raises ActiveRecord::RecordNotFound" do + expect do + described_class.result(id: medical_shift.id.to_s, scope: empty_scope) + end.to raise_error(ActiveRecord::RecordNotFound) + end + end + + context "when medical_shift with given id doesn't exist" do subject(:result) { described_class.result(id: "nonexistent") } it { expect { result }.to raise_error(ActiveRecord::RecordNotFound) } diff --git a/spec/operations/patients/destroy_spec.rb b/spec/operations/patients/destroy_spec.rb index 6d7bb09..3799e8e 100644 --- a/spec/operations/patients/destroy_spec.rb +++ b/spec/operations/patients/destroy_spec.rb @@ -19,28 +19,34 @@ context "when the patient cannot be destroyed" do let!(:patient) { create(:patient) } - it "is failure" do - allow(Patient).to receive(:find).with(patient.id.to_s).and_return(patient) - allow(patient).to receive(:destroy).and_return(false) + before do + allow_any_instance_of(Patient).to receive(:destroy).and_return(false) # rubocop:disable RSpec/AnyInstance + end + it "is failure" do expect(described_class.result(id: patient.id.to_s)).to be_failure end it "does not destroy the patient" do - allow(Patient).to receive(:find).with(patient.id.to_s).and_return(patient) - allow(patient).to receive(:destroy).and_return(false) - expect { described_class.result(id: patient.id.to_s) }.not_to change(Patient, :count) end it "returns error :cannot_destroy" do - allow(Patient).to receive(:find).with(patient.id.to_s).and_return(patient) - allow(patient).to receive(:destroy).and_return(false) - expect(described_class.result(id: patient.id.to_s).error).to eq(:cannot_destroy) end end + context "when patient is outside the given scope" do + let!(:patient) { create(:patient) } + let(:empty_scope) { Patient.none } + + it "raises ActiveRecord::RecordNotFound" do + expect do + described_class.result(id: patient.id.to_s, scope: empty_scope) + end.to raise_error(ActiveRecord::RecordNotFound) + end + end + context "when the patient with given id does not exist" do it "raises ActiveRecord::RecordNotFound" do expect { described_class.result(id: "non-existent-id") }.to raise_error(ActiveRecord::RecordNotFound) diff --git a/spec/operations/procedures/destroy_spec.rb b/spec/operations/procedures/destroy_spec.rb index 1763b55..6c1625d 100644 --- a/spec/operations/procedures/destroy_spec.rb +++ b/spec/operations/procedures/destroy_spec.rb @@ -19,27 +19,31 @@ context "when procedure cannot be destroyed" do let!(:procedure) { create(:procedure) } - it "is failure" do - allow(Procedure).to receive(:find).with(procedure.id.to_s).and_return(procedure) - allow(procedure).to receive(:destroy).and_return(false) + before do + allow_any_instance_of(Procedure).to receive(:destroy).and_return(false) # rubocop:disable RSpec/AnyInstance + end + it "is failure" do expect(described_class.result(id: procedure.id.to_s)).to be_failure end it "does not destroy procedure" do - allow(Procedure).to receive(:find).with(procedure.id.to_s).and_return(procedure) - allow(procedure).to receive(:destroy).and_return(false) - expect { described_class.result(id: procedure.id.to_s) }.not_to change(Procedure, :count) end it "returns error message" do - allow(Procedure).to receive(:find).with(procedure.id.to_s).and_return(procedure) - allow(procedure).to receive(:destroy).and_return(false) + expect(described_class.result(id: procedure.id.to_s).error).to eq(:cannot_destroy) + end + end - result = described_class.result(id: procedure.id.to_s) + context "when procedure is outside the given scope" do + let!(:procedure) { create(:procedure) } + let(:empty_scope) { Procedure.none } - expect(result.error).to eq(:cannot_destroy) + it "raises ActiveRecord::RecordNotFound" do + expect do + described_class.result(id: procedure.id.to_s, scope: empty_scope) + end.to raise_error(ActiveRecord::RecordNotFound) end end diff --git a/spec/pdfs/medical_shifts_report_pdf_spec.rb b/spec/pdfs/medical_shifts_report_pdf_spec.rb index 1a04493..9439e28 100644 --- a/spec/pdfs/medical_shifts_report_pdf_spec.rb +++ b/spec/pdfs/medical_shifts_report_pdf_spec.rb @@ -23,6 +23,39 @@ end end + context "when hospital name exceeds 35 characters" do + it "truncates the name with ellipsis" do + long_name = "A" * 36 + medical_shift = create(:medical_shift, user_id: user.id, hospital_name: long_name) + pdf = Prawn::Document.new + + described_class.new( + pdf: pdf, amount: amount, items: [medical_shift], title: "Plantões", email: user.email + ).generate + rendered_pdf = pdf.render + text_analysis = PDF::Inspector::Text.analyze(rendered_pdf) + + expect(text_analysis.strings).to include("#{long_name[0, 35]}...") + expect(text_analysis.strings).not_to include(long_name) + end + end + + context "when hospital name is within 35 characters" do + it "displays the name without truncation" do + short_name = "A" * 35 + medical_shift = create(:medical_shift, user_id: user.id, hospital_name: short_name) + pdf = Prawn::Document.new + + described_class.new( + pdf: pdf, amount: amount, items: [medical_shift], title: "Plantões", email: user.email + ).generate + rendered_pdf = pdf.render + text_analysis = PDF::Inspector::Text.analyze(rendered_pdf) + + expect(text_analysis.strings).to include(short_name) + end + end + context "when hide_values is true" do it "does not include monetary values in the report" do pdf = Prawn::Document.new diff --git a/spec/requests/api/v1/event_procedures_request_spec.rb b/spec/requests/api/v1/event_procedures_request_spec.rb index ad00390..590b678 100644 --- a/spec/requests/api/v1/event_procedures_request_spec.rb +++ b/spec/requests/api/v1/event_procedures_request_spec.rb @@ -593,26 +593,29 @@ include_examples "delete request returns ok", EventProcedure + context "when trying to destroy another user's event_procedure" do + let(:event_procedure) { create(:event_procedure, user_id: create(:user).id) } + + it "returns not_found to prevent ID enumeration" do + delete "/api/v1/event_procedures/#{event_procedure.id}", headers: headers + expect(response).to have_http_status(:not_found) + end + end + context "when event_procedure cannot be destroyed" do - it "returns unprocessable_content" do - event_procedure = create(:event_procedure, user_id: user.id) + let(:event_procedure) { create(:event_procedure, user_id: user.id) } - allow(EventProcedure).to receive(:find).with(event_procedure.id.to_s).and_return(event_procedure) - allow(event_procedure).to receive(:destroy).and_return(false) + before do + allow_any_instance_of(EventProcedure).to receive(:destroy).and_return(false) # rubocop:disable RSpec/AnyInstance + end + it "returns unprocessable_content" do delete "/api/v1/event_procedures/#{event_procedure.id}", headers: headers - expect(response).to have_http_status(:unprocessable_content) end it "returns error message" do - event_procedure = create(:event_procedure, user_id: user.id) - - allow(EventProcedure).to receive(:find).with(event_procedure.id.to_s).and_return(event_procedure) - allow(event_procedure).to receive(:destroy).and_return(false) - delete "/api/v1/event_procedures/#{event_procedure.id}", headers: headers - expect(response.parsed_body).to eq("cannot_destroy") end end diff --git a/spec/requests/api/v1/hospitals_request_spec.rb b/spec/requests/api/v1/hospitals_request_spec.rb index 26cb57c..c759b11 100644 --- a/spec/requests/api/v1/hospitals_request_spec.rb +++ b/spec/requests/api/v1/hospitals_request_spec.rb @@ -173,21 +173,17 @@ include_examples "delete request returns ok", Hospital context "when hospital cannot be destroyed" do - it "returns unprocessable_content" do - allow(Hospital).to receive(:find).with(hospital.id.to_s).and_return(hospital) - allow(hospital).to receive(:destroy).and_return(false) + before do + allow_any_instance_of(Hospital).to receive(:destroy).and_return(false) # rubocop:disable RSpec/AnyInstance + end + it "returns unprocessable_content" do delete path, headers: headers - expect(response).to have_http_status(:unprocessable_content) end it "returns errors" do - allow(Hospital).to receive(:find).with(hospital.id.to_s).and_return(hospital) - allow(hospital).to receive(:destroy).and_return(false) - delete path, headers: headers - expect(response.parsed_body).to eq("cannot_destroy") end end diff --git a/spec/requests/api/v1/medical_shifts_request_spec.rb b/spec/requests/api/v1/medical_shifts_request_spec.rb index 4f10d35..4a27dd0 100644 --- a/spec/requests/api/v1/medical_shifts_request_spec.rb +++ b/spec/requests/api/v1/medical_shifts_request_spec.rb @@ -412,10 +412,17 @@ it { expect(response.parsed_body[:error]).to include("Couldn't find MedicalShift with 'id'=#{fake_id}") } end + context "when trying to destroy another user's medical_shift" do + let(:medical_shift) { create(:medical_shift, user: create(:user)) } + + before { delete path, headers: headers } + + it { expect(response).to have_http_status(:not_found) } + end + context "when medical_shift cannot be destroyed" do before do - allow(MedicalShift).to receive(:find).with(medical_shift.id.to_s).and_return(medical_shift) - allow(medical_shift).to receive(:destroy).and_return(false) + allow_any_instance_of(MedicalShift).to receive(:destroy).and_return(false) # rubocop:disable RSpec/AnyInstance delete path, headers: headers end diff --git a/spec/requests/api/v1/patients_request_spec.rb b/spec/requests/api/v1/patients_request_spec.rb index bec8737..c9dcd01 100644 --- a/spec/requests/api/v1/patients_request_spec.rb +++ b/spec/requests/api/v1/patients_request_spec.rb @@ -186,7 +186,8 @@ { error: "Error message." } ) - allow(Patients::Destroy).to receive(:result).with(id: patient.id.to_s).and_return(result_object) + allow(Patients::Destroy).to receive(:result) + .with(hash_including(id: patient.id.to_s)).and_return(result_object) delete "/api/v1/patients/#{patient.id}", headers: headers diff --git a/spec/requests/api/v1/procedures_request_spec.rb b/spec/requests/api/v1/procedures_request_spec.rb index 7b3e570..fcc041f 100644 --- a/spec/requests/api/v1/procedures_request_spec.rb +++ b/spec/requests/api/v1/procedures_request_spec.rb @@ -128,22 +128,27 @@ context "when user is authenticated" do include_examples "delete request returns ok", Procedure - context "when procedure cannot be destroyed" do - it "returns unprocessable_content" do - allow(Procedure).to receive(:find).with(procedure.id.to_s).and_return(procedure) - allow(procedure).to receive(:destroy).and_return(false) + context "when trying to destroy another user's procedure" do + let(:procedure) { create(:procedure, user: create(:user)) } + it "returns not_found to prevent ID enumeration" do delete path, headers: headers + expect(response).to have_http_status(:not_found) + end + end + context "when procedure cannot be destroyed" do + before do + allow_any_instance_of(Procedure).to receive(:destroy).and_return(false) # rubocop:disable RSpec/AnyInstance + end + + it "returns unprocessable_content" do + delete path, headers: headers expect(response).to have_http_status(:unprocessable_content) end it "returns error messages" do - allow(Procedure).to receive(:find).with(procedure.id.to_s).and_return(procedure) - allow(procedure).to receive(:destroy).and_return(false) - delete path, headers: headers - expect(response.parsed_body).to eq("cannot_destroy") end end