From 48d7ec75d0f4e085e51555acf67ab3ddfdf0d156 Mon Sep 17 00:00:00 2001 From: decabeza Date: Mon, 5 Sep 2022 16:19:19 +0200 Subject: [PATCH] Do not allow to create more answers than the maximum defined In case we receive consecutive requests we are locking the poll author record until the first request transaction ends, so the author answers count during subsequent requests validations is up to date. --- app/models/poll/answer.rb | 13 +++++++++++++ spec/models/poll/answer_spec.rb | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/app/models/poll/answer.rb b/app/models/poll/answer.rb index 685830201..904216355 100644 --- a/app/models/poll/answer.rb +++ b/app/models/poll/answer.rb @@ -7,6 +7,7 @@ class Poll::Answer < ApplicationRecord validates :question, presence: true validates :author, presence: true validates :answer, presence: true + validate :max_votes validates :answer, inclusion: { in: ->(a) { a.question.possible_answers }}, unless: ->(a) { a.question.blank? } @@ -21,4 +22,16 @@ class Poll::Answer < ApplicationRecord Poll::Voter.find_or_create_by!(user: author, poll: poll, origin: "web") end end + + private + + def max_votes + return if !question || question&.unique? || persisted? + + author.lock! + + if question.answers.by_author(author).count >= question.max_votes + errors.add(:answer, "Maximum number of votes per user exceeded") + end + end end diff --git a/spec/models/poll/answer_spec.rb b/spec/models/poll/answer_spec.rb index d420e367e..b2b419c23 100644 --- a/spec/models/poll/answer_spec.rb +++ b/spec/models/poll/answer_spec.rb @@ -23,6 +23,30 @@ describe Poll::Answer do expect(answer).not_to be_valid end + it "is not valid when user already reached multiple answers question max votes" do + author = create(:user) + question = create(:poll_question_multiple, :abc, max_votes: 2) + create(:poll_answer, author: author, question: question, answer: "Answer A") + create(:poll_answer, author: author, question: question, answer: "Answer B") + answer = build(:poll_answer, author: author, question: question, answer: "Answer C") + + expect(answer).not_to be_valid + end + + it "validates max votes when creating answers at the same time", :race_condition do + author = create(:user, :level_two) + question = create(:poll_question_multiple, :abc, max_votes: 2) + create(:poll_answer, question: question, answer: "Answer A", author: author) + answer = build(:poll_answer, question: question, answer: "Answer B", author: author) + other_answer = build(:poll_answer, question: question, answer: "Answer C", author: author) + + [answer, other_answer].map do |a| + Thread.new { a.save } + end.each(&:join) + + expect(Poll::Answer.count).to be 2 + end + it "is valid for answers included in the Poll::Question's question_answers list" do question = create(:poll_question) create(:poll_question_answer, title: "One", question: question)