From 964916c71ba6be242fddf862f21ba92f47baa9fe Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 30 Jun 2025 06:28:14 -0400 Subject: [PATCH] Add coverage for `TermsOfService` scopes/validations (#35204) --- app/models/terms_of_service.rb | 6 +-- spec/models/terms_of_service_spec.rb | 81 ++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/app/models/terms_of_service.rb b/app/models/terms_of_service.rb index 59411f710..41afaf10d 100644 --- a/app/models/terms_of_service.rb +++ b/app/models/terms_of_service.rb @@ -15,9 +15,9 @@ # class TermsOfService < ApplicationRecord scope :published, -> { where.not(published_at: nil).order(Arel.sql('coalesce(effective_date, published_at) DESC')) } - scope :live, -> { published.where('effective_date IS NULL OR effective_date < now()').limit(1) } - scope :upcoming, -> { published.reorder(effective_date: :asc).where('effective_date IS NOT NULL AND effective_date > now()').limit(1) } - scope :draft, -> { where(published_at: nil).order(id: :desc).limit(1) } + scope :live, -> { published.where('effective_date IS NULL OR effective_date < now()') } + scope :upcoming, -> { published.reorder(effective_date: :asc).where('effective_date IS NOT NULL AND effective_date > now()') } + scope :draft, -> { where(published_at: nil).order(id: :desc) } validates :text, presence: true validates :changelog, :effective_date, presence: true, if: -> { published? } diff --git a/spec/models/terms_of_service_spec.rb b/spec/models/terms_of_service_spec.rb index 41b9e650a..7e68c4eac 100644 --- a/spec/models/terms_of_service_spec.rb +++ b/spec/models/terms_of_service_spec.rb @@ -3,6 +3,87 @@ require 'rails_helper' RSpec.describe TermsOfService do + describe 'Validations' do + subject { Fabricate.build :terms_of_service } + + it { is_expected.to validate_presence_of(:text) } + it { is_expected.to validate_uniqueness_of(:effective_date) } + + it { is_expected.to allow_values(2.days.from_now).for(:effective_date) } + it { is_expected.to_not allow_values(2.days.ago).for(:effective_date) } + + context 'with an existing published effective TOS' do + before do + travel_to 5.days.ago do + Fabricate :terms_of_service, published_at: 2.days.ago, effective_date: 1.day.from_now + end + end + + it { is_expected.to allow_values(1.day.ago).for(:effective_date) } + end + + context 'when published' do + subject { Fabricate.build :terms_of_service, published_at: Time.zone.today } + + it { is_expected.to validate_presence_of(:changelog) } + it { is_expected.to validate_presence_of(:effective_date) } + end + end + + describe 'Scopes' do + describe '.published' do + let!(:unpublished) { Fabricate :terms_of_service, published_at: nil } + let!(:published_older_effective) { travel_to(3.days.ago) { Fabricate :terms_of_service, published_at: 5.days.ago, effective_date: Time.zone.today } } + let!(:published_newer_effective) { travel_to(2.days.ago) { Fabricate :terms_of_service, published_at: 5.days.ago, effective_date: Time.zone.today } } + + it 'returns published records in correct order' do + expect(described_class.published) + .to eq([published_newer_effective, published_older_effective]) + .and not_include(unpublished) + end + end + + describe '.live' do + let!(:not_effective) { Fabricate :terms_of_service } + let!(:effective_past) { travel_to(3.days.ago) { Fabricate :terms_of_service, effective_date: Time.zone.today } } + let!(:effective_future) { Fabricate :terms_of_service, effective_date: 3.days.from_now } + + before { not_effective.update_attribute(:effective_date, nil) } + + it 'returns records without effective or with past effective' do + expect(described_class.live) + .to include(not_effective) + .and include(effective_past) + .and not_include(effective_future) + end + end + + describe '.upcoming' do + let!(:unpublished) { Fabricate :terms_of_service, published_at: nil } + let!(:effective_past) { travel_to(3.days.ago) { Fabricate :terms_of_service, effective_date: Time.zone.today } } + let!(:effective_future_near) { Fabricate :terms_of_service, effective_date: 3.days.from_now } + let!(:effective_future_far) { Fabricate :terms_of_service, effective_date: 5.days.from_now } + + it 'returns published records with future effective date in order of soonest first' do + expect(described_class.upcoming) + .to eq([effective_future_near, effective_future_far]) + .and not_include(unpublished) + .and not_include(effective_past) + end + end + + describe '.draft' do + let!(:published) { Fabricate :terms_of_service, published_at: 2.days.ago } + let!(:unpublished) { Fabricate :terms_of_service, published_at: nil } + + it 'returns not published records' do + expect(described_class.draft) + .to include(unpublished) + .and not_include(published) + end + end + end + describe '#scope_for_notification' do subject { terms_of_service.scope_for_notification }