diff --git a/app/models/better_together/platform_invitation.rb b/app/models/better_together/platform_invitation.rb index 286bef8fd..f70c2f4ba 100644 --- a/app/models/better_together/platform_invitation.rb +++ b/app/models/better_together/platform_invitation.rb @@ -32,8 +32,10 @@ class PlatformInvitation < ApplicationRecord has_rich_text :greeting, encrypted: true - validates :invitee_email, uniqueness: { scope: :invitable_id, allow_nil: true } - validates :invitee_email, uniqueness: { scope: :invitable_id, allow_nil: true, allow_blank: true } + validates :invitee_email, + uniqueness: { scope: :invitable_id, case_sensitive: false }, + format: { with: URI::MailTo::EMAIL_REGEXP }, + allow_blank: true validates :locale, presence: true, inclusion: { in: I18n.available_locales.map(&:to_s) } validates :status, presence: true, inclusion: { in: STATUS_VALUES.values } validates :token, uniqueness: true @@ -46,8 +48,9 @@ class PlatformInvitation < ApplicationRecord scope :pending, -> { where(status: STATUS_VALUES[:pending]) } scope :accepted, -> { where(status: STATUS_VALUES[:accepted]) } - scope :expired, -> { where('valid_until IS NOT NULL AND valid_until < ?', Time.current) } - scope :not_expired, -> { where('valid_until IS NULL OR valid_until >= ?', Time.current) } + + scope :expired, -> { where('valid_until IS NULL OR valid_until < ?', Time.current) } + scope :not_expired, -> { where('valid_until >= ?', Time.current) } def self.load_all_subclasses Rails.application.eager_load! # Ensure all models are loaded diff --git a/spec/models/better_together/platform_invitation_spec.rb b/spec/models/better_together/platform_invitation_spec.rb index d093a1101..05e1d6a17 100644 --- a/spec/models/better_together/platform_invitation_spec.rb +++ b/spec/models/better_together/platform_invitation_spec.rb @@ -24,12 +24,11 @@ module BetterTogether # rubocop:todo Metrics/ModuleLength describe 'ActiveModel validations' do it { - # rubocop:todo RSpec/NamedSubject - expect(subject).to validate_uniqueness_of(:invitee_email).scoped_to(:invitable_id) - # rubocop:enable RSpec/NamedSubject - .allow_nil.allow_blank.case_insensitive + is_expected.to validate_uniqueness_of(:invitee_email).scoped_to(:invitable_id) + .allow_blank.case_insensitive } - + it { is_expected.to allow_value('test@example.com').for(:invitee_email) } + it { is_expected.not_to allow_value('invalid_email').for(:invitee_email) } it { is_expected.to validate_presence_of(:locale) } it { is_expected.to validate_inclusion_of(:locale).in_array(I18n.available_locales.map(&:to_s)) } it { is_expected.to validate_presence_of(:status) } @@ -89,27 +88,22 @@ module BetterTogether # rubocop:todo Metrics/ModuleLength end describe '.expired' do - it 'returns only expired invitations' do # rubocop:todo RSpec/MultipleExpectations - expired_invitation = create(:better_together_platform_invitation, valid_until: 1.day.ago) + it 'includes invitations with nil or past valid_until' do + nil_invitation = create(:better_together_platform_invitation, valid_until: nil) + past_invitation = create(:better_together_platform_invitation, valid_until: 1.day.ago) create(:better_together_platform_invitation, valid_until: 1.day.from_now) - expect(described_class.expired).to include(expired_invitation) - expect(described_class.expired.count).to eq(1) + expect(described_class.expired).to match_array([nil_invitation, past_invitation]) end end describe '.not_expired' do - # rubocop:todo RSpec/MultipleExpectations - it 'returns invitations that are not expired or have no expiration' do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations - # rubocop:enable RSpec/MultipleExpectations - future_invitation = create(:better_together_platform_invitation, valid_until: 1.day.from_now) - nil_invitation = create(:better_together_platform_invitation, valid_until: nil) + it 'includes only invitations with future valid_until' do + create(:better_together_platform_invitation, valid_until: nil) create(:better_together_platform_invitation, valid_until: 1.day.ago) + future_invitation = create(:better_together_platform_invitation, valid_until: 1.day.from_now) - result = described_class.not_expired - - expect(result).to include(future_invitation, nil_invitation) - expect(result.count).to eq(2) + expect(described_class.not_expired).to match_array([future_invitation]) end end end