diff --git a/plugins/poll/lib/ranked_choice.rb b/plugins/poll/lib/ranked_choice.rb index 24a376e8fc6..5f320356746 100644 --- a/plugins/poll/lib/ranked_choice.rb +++ b/plugins/poll/lib/ranked_choice.rb @@ -44,7 +44,7 @@ class DiscoursePoll::RankedChoice potential_winners = find_potential_winners(tally, max_votes) # Check for a majority and return if found - if majority_check(tally, potential_winners, max_votes) + if majority_check(tally, max_votes) majority_candidate = enrich(potential_winners.keys.first, options) round_activity << { round: round, majority: majority_candidate, eliminated: nil } @@ -112,10 +112,10 @@ class DiscoursePoll::RankedChoice tally.select { |k, v| v == max_votes } end - def self.majority_check(tally, potential_winners, max_votes) + def self.majority_check(tally, max_votes) total_votes = tally.values.sum - max_votes > total_votes / 2 || potential_winners.count == 1 + max_votes && max_votes > total_votes / 2 end def self.identify_losers(tally) diff --git a/plugins/poll/spec/lib/ranked_choice_spec.rb b/plugins/poll/spec/lib/ranked_choice_spec.rb index f7cfd8c61e2..09ef6c8a5dd 100644 --- a/plugins/poll/spec/lib/ranked_choice_spec.rb +++ b/plugins/poll/spec/lib/ranked_choice_spec.rb @@ -14,6 +14,19 @@ RSpec.describe DiscoursePoll::RankedChoice do ] end + let(:options_4) do + [ + { id: "Belle-lettres", html: "Belle-lettres" }, + { id: "Comedy", html: "Comedy" }, + { id: "Fantasy", html: "Fantasy" }, + { id: "Historical", html: "Historical" }, + { id: "Mystery", html: "Mystery" }, + { id: "Non-fiction", html: "Non-fiction" }, + { id: "Sci-fi", html: "Sci-fi" }, + { id: "Thriller", html: "Thriller" }, + ] + end + it "correctly finds the winner with a simple majority" do votes = [%w[Alice Bob], %w[Bob Alice], %w[Alice Bob], %w[Bob Alice], %w[Alice Bob]] expect(described_class.run(votes, options_1)[:winning_candidate]).to eq( @@ -60,10 +73,38 @@ RSpec.describe DiscoursePoll::RankedChoice do expect(described_class.run(votes, options_3)[:round_activity].length).to eq(2) end - it "handles the winner with a simple majority" do + it "handles a tie after an elimination" do votes = [%w[Dave Alice], %w[Bob Dave]] expect(described_class.run(votes, options_3)[:tied_candidates]).to eq( [{ digest: "Dave", html: "Dave" }, { digest: "Bob", html: "Bob" }], ) end + + it "handles a complex multi-round tie" do + votes = [ + %w[Belle-lettres Thriller Non-fiction Sci-fi Mystery Comedy Historical Fantasy], + %w[Mystery Fantasy Belle-lettres Sci-fi Non-fiction Historical Thriller Comedy], + %w[Mystery Thriller Belle-lettres Sci-fi Comedy Non-fiction Fantasy Historical], + %w[Mystery Sci-fi Fantasy Thriller Non-fiction Belle-lettres Historical Comedy], + %w[Mystery Thriller Non-fiction Sci-fi Comedy Historical Belle-lettres Fantasy], + %w[Fantasy Non-fiction Mystery Sci-fi Thriller Historical Belle-lettres Comedy], + %w[Fantasy Mystery Historical Thriller Sci-fi Non-fiction Comedy Belle-lettres], + %w[Thriller Mystery Fantasy Non-fiction Sci-fi Historical Comedy Belle-lettres], + %w[Mystery Fantasy Historical Thriller Non-fiction Comedy Sci-fi Belle-lettres], + %w[Fantasy Sci-fi Thriller Mystery Non-fiction Comedy Historical Belle-lettres], + %w[Fantasy Sci-fi Historical Non-fiction Comedy], + %w[Fantasy Sci-fi Mystery Comedy Thriller Non-fiction Historical], + %w[Fantasy Mystery Historical Non-fiction Sci-fi Comedy], + %w[Fantasy Sci-fi Mystery Comedy Thriller Historical Non-fiction], + %w[Comedy Historical Fantasy Sci-fi Mystery], + %w[Sci-fi Thriller Mystery Non-fiction Comedy Fantasy], + ] + + outcome = described_class.run(votes, options_4) + + expect(outcome[:tied_candidates]).to eq( + [{ digest: "Mystery", html: "Mystery" }, { digest: "Fantasy", html: "Fantasy" }], + ) + expect(outcome[:round_activity].length).to eq(3) + end end