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.
This commit is contained in:
decabeza
2022-09-05 16:19:19 +02:00
committed by Senén Rodero Rodríguez
parent a25629951c
commit 48d7ec75d0
2 changed files with 37 additions and 0 deletions

View File

@@ -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

View File

@@ -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)