require "rails_helper" describe Budget::Ballot::Line do let(:budget) { create(:budget) } let(:group) { create(:budget_group, budget: budget) } let(:heading) { create(:budget_heading, group: group, price: 10000000) } let(:investment) { create(:budget_investment, :selected, price: 5000000, heading: heading) } let(:ballot) { create(:budget_ballot, budget: budget) } let(:ballot_line) { build(:budget_ballot_line, ballot: ballot, investment: investment) } describe "Validations" do it "is valid and automatically denormallyze budget, group and heading when validated" do expect(ballot_line).to be_valid expect(ballot_line.budget).to eq(budget) expect(ballot_line.group).to eq(group) expect(ballot_line.heading).to eq(heading) end describe "Money" do it "is not valid if insufficient funds" do investment.update!(price: heading.price + 1) expect(ballot_line).not_to be_valid end it "is valid if sufficient funds" do investment.update!(price: heading.price - 1) expect(ballot_line).to be_valid end it "validates sufficient funds when creating lines at the same time", :race_condition do investment.update!(price: heading.price) other_investment = create(:budget_investment, :selected, price: heading.price, heading: heading) other_line = build(:budget_ballot_line, ballot: ballot, investment: other_investment) [ballot_line, other_line].map do |line| Thread.new { line.save } end.each(&:join) expect(Budget::Ballot::Line.count).to be 1 end end describe "Approval voting" do before do budget.update!(voting_style: "approval") heading.update!(max_ballot_lines: 1) end it "is valid if there are votes left" do expect(ballot_line).to be_valid end it "is not valid if there are no votes left" do create(:budget_ballot_line, ballot: ballot, investment: create(:budget_investment, :selected, heading: heading)) expect(ballot_line).not_to be_valid end it "is valid if insufficient funds but enough votes" do investment.update!(price: heading.price + 1) expect(ballot_line).to be_valid end it "validates votes when creating lines at the same time", :race_condition do other_investment = create(:budget_investment, :selected, heading: heading) other_line = build(:budget_ballot_line, ballot: ballot, investment: other_investment) [ballot_line, other_line].map do |line| Thread.new { line.save } end.each(&:join) expect(Budget::Ballot::Line.count).to be 1 end end describe "Selectibility" do it "is not valid if investment is unselected" do investment.update!(selected: false) expect(ballot_line).not_to be_valid end it "is valid if investment is selected" do investment.update!(selected: true, price: 20000) expect(ballot_line).to be_valid end end end describe "scopes" do describe "by_investment" do it "returns ballot lines for an investment" do investment1 = create(:budget_investment, :selected, heading: heading) investment2 = create(:budget_investment, :selected, heading: heading) ballot1 = create(:budget_ballot, budget: budget) ballot2 = create(:budget_ballot, budget: budget) ballot3 = create(:budget_ballot, budget: budget) ballot_line1 = create(:budget_ballot_line, ballot: ballot1, investment: investment1) ballot_line2 = create(:budget_ballot_line, ballot: ballot2, investment: investment1) ballot_line3 = create(:budget_ballot_line, ballot: ballot3, investment: investment2) ballot_lines_by_investment = Budget::Ballot::Line.by_investment(investment1.id) expect(ballot_lines_by_investment).to match_array [ballot_line1, ballot_line2] expect(ballot_lines_by_investment).not_to include ballot_line3 end end end end