From 166f0500a123d0cfd7fa7c462ef21b2727cee6b7 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 2 Apr 2026 21:58:47 -0400 Subject: [PATCH 1/2] Add tests for EmancipationsController --- .../emancipations_controller_spec.rb | 260 +++++++++++++++--- 1 file changed, 226 insertions(+), 34 deletions(-) diff --git a/spec/controllers/emancipations_controller_spec.rb b/spec/controllers/emancipations_controller_spec.rb index eef5a28de6..330db44b45 100644 --- a/spec/controllers/emancipations_controller_spec.rb +++ b/spec/controllers/emancipations_controller_spec.rb @@ -1,48 +1,240 @@ require "rails_helper" RSpec.describe EmancipationsController, type: :controller do - let(:organization) { build(:casa_org) } - let(:volunteer) { create(:volunteer, :with_casa_cases, casa_org: organization) } - let(:test_case_category) { build(:casa_case_emancipation_category) } - let(:casa_case) { create(:casa_case, casa_org: organization) } - let(:casa_case_id) { casa_case.id.to_s } - let(:params) do - { - casa_case_id: casa_case_id - } - end + let(:organization) { create(:casa_org) } + let(:other_org) { create(:casa_org) } + let(:user) { create(:supervisor, casa_org: organization) } + let(:casa_case) { create(:casa_case, casa_org: organization, birth_month_year_youth: 20.years.ago) } + let(:non_transition_case) { create(:casa_case, :pre_transition, casa_org: organization) } + let(:emancipation_category) { create(:emancipation_category) } + let(:emancipation_option) { create(:emancipation_option, emancipation_category: emancipation_category) } + + before { sign_in user } + + describe "GET #show" do + context "when authenticated and authorized" do + it "returns http success" do + get :show, params: { casa_case_id: casa_case.friendly_id } + expect(response).to have_http_status(:success) + end + + it "assigns @current_case" do + get :show, params: { casa_case_id: casa_case.friendly_id } + expect(assigns(:current_case)).to eq(casa_case) + end + + it "assigns @emancipation_form_data with all categories" do + get :show, params: { casa_case_id: casa_case.friendly_id } + expect(assigns(:emancipation_form_data)).to match_array(EmancipationCategory.all) + end + end - before do - allow(controller).to receive(:authenticate_user!).and_return(true) - allow(controller).to receive(:current_user).and_return(volunteer) + context "when case does not exist" do + it "raises a record not found error" do + expect { + get :show, params: { casa_case_id: "nonexistent-case" } + }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + context "when user belongs to a different org" do + let(:user) { create(:supervisor, casa_org: other_org) } + + it "redirects to root with an authorization notice" do + get :show, params: { casa_case_id: casa_case.friendly_id } + expect(response).to redirect_to(root_url) + expect(flash[:notice]).to match(/not authorized/) + end + end + + context "docx format" do + it "sends a docx file with the correct filename" do + get :show, params: { casa_case_id: casa_case.friendly_id }, format: :docx + expect(response.headers["Content-Disposition"]).to include( + "#{casa_case.case_number} Emancipation Checklist.docx" + ) + end + end end - describe "show" do - context "json request" do - subject(:show) { get :show, params: {casa_case_id: casa_case_id, format: :json} } + describe "POST #save" do + def post_save(action, check_item_id, case_id: casa_case.friendly_id) + post :save, params: { + casa_case_id: case_id, + check_item_action: action, + check_item_id: check_item_id + }, format: :json + end + + # Authorization + context "when user belongs to a different org" do + let(:user) { create(:supervisor, casa_org: other_org) } + + it "returns unauthorized with a json error message" do + post_save("add_category", emancipation_category.id) + expect(response).to have_http_status(:unauthorized) + expect(json_response["error"]).to match(/not authorized/) + end + end + + # Case not found + context "when casa_case_id does not match any case" do + it "returns 404 with a descriptive error" do + post_save("add_category", emancipation_category.id, case_id: "nonexistent-id") + expect(response).to have_http_status(:not_found) + expect(json_response["error"]).to match(/Could not find case/) + end + end + + # Non-transitioning case + context "when the case is not in transition age" do + it "returns bad_request" do + post_save("add_category", emancipation_category.id, case_id: non_transition_case.friendly_id) + expect(response).to have_http_status(:bad_request) + expect(json_response["error"]).to match(/not marked as transitioning/) + end + end + + # Unsupported action + context "when check_item_action is not supported" do + it "returns bad_request with unsupported action message" do + post_save("unsupported_action", emancipation_category.id) + expect(response).to have_http_status(:bad_request) + expect(json_response["error"]).to match(/not a supported action/) + end + end + + # ADD_CATEGORY + context "with action: add_category" do + it "adds the category to the case and returns success" do + expect { + post_save("add_category", emancipation_category.id) + }.to change { casa_case.emancipation_categories.count }.by(1) + + expect(response).to have_http_status(:ok) + expect(json_response).to eq("success") + end + + it "returns bad_request when category is already associated" do + casa_case.add_emancipation_category(emancipation_category.id) + post_save("add_category", emancipation_category.id) + expect(response).to have_http_status(:bad_request) + expect(json_response["error"]).to match(/already exists/) + end + + it "returns bad_request when category id does not exist" do + post_save("add_category", -1) + expect(response).to have_http_status(:bad_request) + end + end + + # ADD_OPTION + context "with action: add_option" do + it "adds the option to the case and returns success" do + expect { + post_save("add_option", emancipation_option.id) + }.to change { casa_case.emancipation_options.count }.by(1) + + expect(response).to have_http_status(:ok) + expect(json_response).to eq("success") + end + + it "returns bad_request when option is already associated" do + casa_case.add_emancipation_option(emancipation_option.id) + post_save("add_option", emancipation_option.id) + expect(response).to have_http_status(:bad_request) + expect(json_response["error"]).to match(/already exists/) + end - context "not_authorized" do - before do - allow_any_instance_of(Volunteer).to receive(:casa_org).and_return nil - end + it "returns bad_request when option id does not exist" do + post_save("add_option", -1) + expect(response).to have_http_status(:bad_request) + end + end - it "responds unauthorized" do - show - expect(response).to have_http_status(:unauthorized) - end + # DELETE_CATEGORY + context "with action: delete_category" do + before do + casa_case.add_emancipation_category(emancipation_category.id) + casa_case.add_emancipation_option(emancipation_option.id) + end - context "the backtrace ends in 'save'" do - before do - allow_any_instance_of(Organizational::UnknownOrganization).to receive(:backtrace).and_return(["", "", "save'"]) - end + it "removes the category and its associated options from the case" do + post_save("delete_category", emancipation_category.id) + expect(response).to have_http_status(:ok) + expect(json_response).to eq("success") + expect(casa_case.reload.emancipation_categories).not_to include(emancipation_category) + expect(casa_case.reload.emancipation_options).not_to include(emancipation_option) + end - it "renders the correct json message" do - show - expect(response).to have_http_status(:unauthorized) - expect(response.body).to eq({error: "Sorry, you are not authorized to perform this action. Did the session expire?"}.to_json) - end - end + it "returns bad_request when category is not associated with the case" do + other_category = create(:emancipation_category) + post_save("delete_category", other_category.id) + expect(response).to have_http_status(:bad_request) + expect(json_response["error"]).to match(/does not exist/) end end + + # DELETE_OPTION + context "with action: delete_option" do + before { casa_case.add_emancipation_option(emancipation_option.id) } + + it "removes the option from the case and returns success" do + expect { + post_save("delete_option", emancipation_option.id) + }.to change { casa_case.emancipation_options.count }.by(-1) + + expect(response).to have_http_status(:ok) + expect(json_response).to eq("success") + end + + it "returns bad_request when option is not associated with the case" do + other_option = create(:emancipation_option, emancipation_category: emancipation_category) + post_save("delete_option", other_option.id) + expect(response).to have_http_status(:bad_request) + expect(json_response["error"]).to match(/does not exist/) + end + end + + # SET_OPTION + context "with action: set_option" do + let(:other_option) { create(:emancipation_option, emancipation_category: emancipation_category) } + + before { casa_case.add_emancipation_option(other_option.id) } + + it "replaces the existing option in the same category with the new one" do + post_save("set_option", emancipation_option.id) + expect(response).to have_http_status(:ok) + expect(json_response).to eq("success") + expect(casa_case.reload.emancipation_options).to include(emancipation_option) + expect(casa_case.reload.emancipation_options).not_to include(other_option) + end + + it "returns bad_request when option id does not exist" do + post_save("set_option", -1) + expect(response).to have_http_status(:bad_request) + end + end + end + + # JSON error handler for unauthorized access from save + describe "#not_authorized" do + let(:user) { create(:supervisor, casa_org: other_org) } + + it "renders a json unauthorized error when called from save" do + post :save, params: { + casa_case_id: casa_case.friendly_id, + check_item_action: "add_category", + check_item_id: emancipation_category.id + }, format: :json + + expect(response).to have_http_status(:unauthorized) + expect(json_response["error"]).to match(/not authorized/) + end + end + + # Helper to parse JSON responses + def json_response + JSON.parse(response.body) end end From 4b0719902286efbd09c81675ce279c8b3f944b29 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 3 Apr 2026 13:58:58 -0400 Subject: [PATCH 2/2] Fix standardrb linting --- spec/controllers/emancipations_controller_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/controllers/emancipations_controller_spec.rb b/spec/controllers/emancipations_controller_spec.rb index 330db44b45..b457ce706c 100644 --- a/spec/controllers/emancipations_controller_spec.rb +++ b/spec/controllers/emancipations_controller_spec.rb @@ -14,17 +14,17 @@ describe "GET #show" do context "when authenticated and authorized" do it "returns http success" do - get :show, params: { casa_case_id: casa_case.friendly_id } + get :show, params: {casa_case_id: casa_case.friendly_id} expect(response).to have_http_status(:success) end it "assigns @current_case" do - get :show, params: { casa_case_id: casa_case.friendly_id } + get :show, params: {casa_case_id: casa_case.friendly_id} expect(assigns(:current_case)).to eq(casa_case) end it "assigns @emancipation_form_data with all categories" do - get :show, params: { casa_case_id: casa_case.friendly_id } + get :show, params: {casa_case_id: casa_case.friendly_id} expect(assigns(:emancipation_form_data)).to match_array(EmancipationCategory.all) end end @@ -32,7 +32,7 @@ context "when case does not exist" do it "raises a record not found error" do expect { - get :show, params: { casa_case_id: "nonexistent-case" } + get :show, params: {casa_case_id: "nonexistent-case"} }.to raise_error(ActiveRecord::RecordNotFound) end end @@ -41,7 +41,7 @@ let(:user) { create(:supervisor, casa_org: other_org) } it "redirects to root with an authorization notice" do - get :show, params: { casa_case_id: casa_case.friendly_id } + get :show, params: {casa_case_id: casa_case.friendly_id} expect(response).to redirect_to(root_url) expect(flash[:notice]).to match(/not authorized/) end @@ -49,7 +49,7 @@ context "docx format" do it "sends a docx file with the correct filename" do - get :show, params: { casa_case_id: casa_case.friendly_id }, format: :docx + get :show, params: {casa_case_id: casa_case.friendly_id}, format: :docx expect(response.headers["Content-Disposition"]).to include( "#{casa_case.case_number} Emancipation Checklist.docx" )