require "test_helper" class GoalsControllerTest < ActionDispatch::IntegrationTest setup do sign_in users(:family_admin) @goal = goals(:vacation_italy) @depository = accounts(:depository) @connected = accounts(:connected) ensure_tailwind_build end test "index renders with active filter by default" do get goals_url assert_response :success assert_match(/Goals/i, response.body) end test "index honors state filter" do get goals_url(state: "paused") assert_response :success end test "show renders the goal" do get goal_url(@goal) assert_response :success assert_match(@goal.name, response.body) end test "new renders the modal form" do get new_goal_url assert_response :success end test "create persists a goal with linked accounts" do assert_difference -> { Goal.count } => 1, -> { GoalAccount.count } => 2 do post goals_url, params: { goal: { name: "New goal", target_amount: "1000", target_date: 3.months.from_now.to_date.iso8601, color: "#4da568", account_ids: [ @depository.id, @connected.id ] } } end goal = Goal.order(created_at: :desc).first assert_redirected_to goal_path(goal) end test "create with initial contribution writes the contribution" do assert_difference -> { GoalContribution.count } => 1 do post goals_url, params: { goal: { name: "Goal with initial", target_amount: "1000", color: "#4da568", account_ids: [ @depository.id ], initial_contribution_amount: "50", initial_contribution_account_id: @depository.id } } end contribution = GoalContribution.order(created_at: :desc).first assert_equal "initial", contribution.source assert_equal 50, contribution.amount.to_i end test "create rejects missing account_ids" do assert_no_difference "Goal.count" do post goals_url, params: { goal: { name: "Bad goal", target_amount: "1000", color: "#4da568" } } end assert_response :unprocessable_entity end test "create rejects foreign accounts" do other_family = Family.create!(name: "Other", currency: "USD", locale: "en", country: "US", timezone: "UTC") foreign = Account.create!(family: other_family, accountable: Depository.new, name: "Foreign", currency: "USD", balance: 100) assert_no_difference "Goal.count" do post goals_url, params: { goal: { name: "Foreign goal", target_amount: "1000", color: "#4da568", account_ids: [ foreign.id ] } } end assert_response :unprocessable_entity end test "update modifies identity fields" do patch goal_url(@goal), params: { goal: { name: "Renamed" } } assert_redirected_to goal_path(@goal) assert_equal "Renamed", @goal.reload.name end test "pause/resume/complete/archive/unarchive flow" do fresh = goals(:emergency_fund) patch pause_goal_url(fresh) assert fresh.reload.paused? patch resume_goal_url(fresh) assert fresh.reload.active? patch complete_goal_url(fresh) assert fresh.reload.completed? patch archive_goal_url(fresh) assert fresh.reload.archived? patch unarchive_goal_url(fresh) assert fresh.reload.active? end test "destroy on non-archived is rejected" do assert_no_difference "Goal.count" do delete goal_url(@goal) end assert_redirected_to goal_path(@goal) end test "destroy on archived deletes" do @goal.archive! assert_difference "Goal.count", -1 do delete goal_url(@goal) end assert_redirected_to goals_path end test "another family's goal returns 404" do other_family = Family.create!(name: "Other", currency: "USD", locale: "en", country: "US", timezone: "UTC") other_account = Account.create!(family: other_family, accountable: Depository.new, name: "Foreign", currency: "USD", balance: 100) other_goal = other_family.goals.new(name: "Foreign goal", target_amount: 100, currency: "USD") other_goal.goal_accounts.build(account: other_account) other_goal.save! get goal_url(other_goal) assert_redirected_to goals_path assert_equal I18n.t("goals.errors.not_found"), flash[:alert] end end