From 855fedc0255aac83538b4e45436d23ba7afa076d Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Wed, 14 Sep 2022 16:16:49 +0200 Subject: [PATCH] Allow only creation of polls with dates that are not in the past --- app/models/poll.rb | 14 ++++++++++++++ config/locales/en/general.yml | 1 + config/locales/es/general.yml | 1 + db/dev_seeds/polls.rb | 18 +++++++++++++----- spec/factories/polls.rb | 5 +++++ spec/models/poll/poll_spec.rb | 32 ++++++++++++++++++++++++++++++-- 6 files changed, 64 insertions(+), 7 deletions(-) diff --git a/app/models/poll.rb b/app/models/poll.rb index 2acd9cfb0..58ad1b2eb 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -37,6 +37,8 @@ class Poll < ApplicationRecord validates_translation :name, presence: true validate :date_range + validate :start_date_is_not_past_date, on: :create + validate :start_date_change, on: :update validate :only_one_active, unless: :public? accepts_nested_attributes_for :questions, reject_if: :all_blank, allow_destroy: true @@ -143,6 +145,18 @@ class Poll < ApplicationRecord end end + def start_date_is_not_past_date + if starts_at.present? && starts_at < Time.current + errors.add(:starts_at, I18n.t("errors.messages.past_date")) + end + end + + def start_date_change + if will_save_change_to_starts_at? && starts_at < Time.current + errors.add(:starts_at, I18n.t("errors.messages.past_date")) + end + end + def generate_slug? slug.nil? end diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index 96b0655c9..ea032e72f 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -137,6 +137,7 @@ en: allowed_file_content_types: "content type must be one of %{types}" user_not_found: User not found invalid_date_range: "Invalid date range" + past_date: "Must not be a past date" form: accept_terms: I agree to the %{policy} and the %{conditions} accept_terms_title: I agree to the Privacy Policy and the Terms and conditions of use diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index ebfbd6749..e1c01bc8d 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -137,6 +137,7 @@ es: allowed_file_content_types: "el tipo de contenido debe ser uno de los siguientes: %{types}" user_not_found: Usuario no encontrado invalid_date_range: "El rango de fechas no es válido" + past_date: "No puede ser una fecha pasada" form: accept_terms: Acepto la %{policy} y las %{conditions} accept_terms_title: Acepto la Política de privacidad y las Condiciones de uso diff --git a/db/dev_seeds/polls.rb b/db/dev_seeds/polls.rb index 6ed23e601..a4bd79fba 100644 --- a/db/dev_seeds/polls.rb +++ b/db/dev_seeds/polls.rb @@ -2,30 +2,38 @@ require_dependency "poll/answer" require_dependency "poll/question/answer" section "Creating polls" do - Poll.create!(name: I18n.t("seeds.polls.current_poll"), + def create_poll!(attributes) + poll = Poll.create!(attributes.merge(starts_at: 1.day.from_now, ends_at: 2.days.from_now)) + poll.update_columns( + starts_at: attributes[:starts_at].beginning_of_minute, + ends_at: attributes[:ends_at].beginning_of_minute + ) + end + + create_poll!(name: I18n.t("seeds.polls.current_poll"), slug: I18n.t("seeds.polls.current_poll").parameterize, starts_at: 7.days.ago, ends_at: 7.days.from_now, geozone_restricted: false) - Poll.create!(name: I18n.t("seeds.polls.current_poll_geozone_restricted"), + create_poll!(name: I18n.t("seeds.polls.current_poll_geozone_restricted"), slug: I18n.t("seeds.polls.current_poll_geozone_restricted").parameterize, starts_at: 5.days.ago, ends_at: 5.days.from_now, geozone_restricted: true, geozones: Geozone.sample(3)) - Poll.create!(name: I18n.t("seeds.polls.recounting_poll"), + create_poll!(name: I18n.t("seeds.polls.recounting_poll"), slug: I18n.t("seeds.polls.recounting_poll").parameterize, starts_at: 15.days.ago, ends_at: 2.days.ago) - Poll.create!(name: I18n.t("seeds.polls.expired_poll_without_stats"), + create_poll!(name: I18n.t("seeds.polls.expired_poll_without_stats"), slug: I18n.t("seeds.polls.expired_poll_without_stats").parameterize, starts_at: 2.months.ago, ends_at: 1.month.ago) - Poll.create!(name: I18n.t("seeds.polls.expired_poll_with_stats"), + create_poll!(name: I18n.t("seeds.polls.expired_poll_with_stats"), slug: I18n.t("seeds.polls.expired_poll_with_stats").parameterize, starts_at: 2.months.ago, ends_at: 1.month.ago, diff --git a/spec/factories/polls.rb b/spec/factories/polls.rb index 7820532f7..707a1af26 100644 --- a/spec/factories/polls.rb +++ b/spec/factories/polls.rb @@ -6,6 +6,7 @@ FactoryBot.define do starts_at { 1.month.ago } ends_at { 1.month.from_now } + to_create { |poll| poll.save(validate: false) } trait :expired do starts_at { 1.month.ago } @@ -17,6 +18,10 @@ FactoryBot.define do ends_at { 2.months.ago } end + trait :future do + starts_at { 1.day.from_now } + end + trait :published do published { true } end diff --git a/spec/models/poll/poll_spec.rb b/spec/models/poll/poll_spec.rb index 7d48c502f..8b2224364 100644 --- a/spec/models/poll/poll_spec.rb +++ b/spec/models/poll/poll_spec.rb @@ -1,7 +1,7 @@ require "rails_helper" describe Poll do - let(:poll) { build(:poll) } + let(:poll) { build(:poll, :future) } describe "Concerns" do it_behaves_like "notifiable" @@ -22,7 +22,9 @@ describe Poll do it "is not valid without a start date" do poll.starts_at = nil + expect(poll).not_to be_valid + expect(poll.errors[:starts_at]).to eq ["Invalid date range"] end it "is not valid without an end date" do @@ -35,11 +37,37 @@ describe Poll do poll.ends_at = 2.months.ago expect(poll).not_to be_valid end + + it "is valid if start date is greater than current time" do + poll.starts_at = 1.minute.from_now + expect(poll).to be_valid + end + + it "is not valid if start date is a past date" do + poll.starts_at = 1.minute.ago + + expect(poll).not_to be_valid + expect(poll.errors[:starts_at]).to eq ["Must not be a past date"] + end + + context "persisted poll" do + let(:poll) { create(:poll, :future) } + + it "is valid if the start date changes to a future date" do + poll.starts_at = 1.minute.from_now + expect(poll).to be_valid + end + + it "is not valid if the start date changes to a past date" do + poll.starts_at = 1.minute.ago + expect(poll).not_to be_valid + end + end end describe "proposal polls specific validations" do let(:proposal) { create(:proposal) } - let(:poll) { build(:poll, related: proposal) } + let(:poll) { build(:poll, :future, related: proposal) } it "is valid when overlapping but different proposals" do other_proposal = create(:proposal)