From 28e1a7a394db5586bdeb1d872f2bf4bfc18b5d0a Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 14 Dec 2023 05:29:10 -0500 Subject: [PATCH] Improve spec coverage for `models/announcement` class (#28350) --- spec/controllers/emojis_controller_spec.rb | 2 +- .../announcement_mute_fabricator.rb | 6 + .../announcement_reaction_fabricator.rb | 7 + spec/fabricators/custom_emoji_fabricator.rb | 2 +- spec/models/announcement_spec.rb | 210 ++++++++++++++++++ spec/models/custom_emoji_spec.rb | 2 +- spec/requests/api/v1/custom_emojis_spec.rb | 2 +- 7 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 spec/fabricators/announcement_mute_fabricator.rb create mode 100644 spec/fabricators/announcement_reaction_fabricator.rb create mode 100644 spec/models/announcement_spec.rb diff --git a/spec/controllers/emojis_controller_spec.rb b/spec/controllers/emojis_controller_spec.rb index 3fe19ee5c..dd139de93 100644 --- a/spec/controllers/emojis_controller_spec.rb +++ b/spec/controllers/emojis_controller_spec.rb @@ -5,7 +5,7 @@ require 'rails_helper' describe EmojisController do render_views - let(:emoji) { Fabricate(:custom_emoji) } + let(:emoji) { Fabricate(:custom_emoji, shortcode: 'coolcat') } describe 'GET #show' do let(:response) { get :show, params: { id: emoji.id, format: :json } } diff --git a/spec/fabricators/announcement_mute_fabricator.rb b/spec/fabricators/announcement_mute_fabricator.rb new file mode 100644 index 000000000..0adc1d60b --- /dev/null +++ b/spec/fabricators/announcement_mute_fabricator.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +Fabricator(:announcement_mute) do + announcement { Fabricate.build(:announcement) } + account { Fabricate.build(:account) } +end diff --git a/spec/fabricators/announcement_reaction_fabricator.rb b/spec/fabricators/announcement_reaction_fabricator.rb new file mode 100644 index 000000000..e84579729 --- /dev/null +++ b/spec/fabricators/announcement_reaction_fabricator.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +Fabricator(:announcement_reaction) do + account { Fabricate.build(:account) } + announcement { Fabricate.build(:announcement) } + name { Fabricate(:custom_emoji).shortcode } +end diff --git a/spec/fabricators/custom_emoji_fabricator.rb b/spec/fabricators/custom_emoji_fabricator.rb index fa570eec6..7ef875286 100644 --- a/spec/fabricators/custom_emoji_fabricator.rb +++ b/spec/fabricators/custom_emoji_fabricator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true Fabricator(:custom_emoji) do - shortcode 'coolcat' + shortcode { sequence(:shortcode) { |i| "code_#{i}" } } domain nil image { Rails.root.join('spec', 'fixtures', 'files', 'emojo.png').open } end diff --git a/spec/models/announcement_spec.rb b/spec/models/announcement_spec.rb new file mode 100644 index 000000000..e37b81a52 --- /dev/null +++ b/spec/models/announcement_spec.rb @@ -0,0 +1,210 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Announcement do + describe 'Scopes' do + context 'with published and unpublished records' do + let!(:published) { Fabricate(:announcement, published: true) } + let!(:unpublished) { Fabricate(:announcement, published: false, scheduled_at: 10.days.from_now) } + + describe '#unpublished' do + it 'returns records with published false' do + results = described_class.unpublished + + expect(results).to eq([unpublished]) + end + end + + describe '#published' do + it 'returns records with published true' do + results = described_class.published + + expect(results).to eq([published]) + end + end + end + + describe '#without_muted' do + let!(:announcement) { Fabricate(:announcement) } + let(:account) { Fabricate(:account) } + let(:muted_announcement) { Fabricate(:announcement) } + + before do + Fabricate(:announcement_mute, account: account, announcement: muted_announcement) + end + + it 'returns the announcements not muted by the account' do + results = described_class.without_muted(account) + expect(results).to include(announcement) + expect(results).to_not include(muted_announcement) + end + end + + context 'with timestamped announcements' do + let!(:adam_announcement) { Fabricate(:announcement, starts_at: 100.days.ago, scheduled_at: 10.days.ago, published_at: 10.days.ago, ends_at: 5.days.from_now) } + let!(:brenda_announcement) { Fabricate(:announcement, starts_at: 10.days.ago, scheduled_at: 100.days.ago, published_at: 10.days.ago, ends_at: 5.days.from_now) } + let!(:clara_announcement) { Fabricate(:announcement, starts_at: 10.days.ago, scheduled_at: 10.days.ago, published_at: 100.days.ago, ends_at: 5.days.from_now) } + let!(:darnelle_announcement) { Fabricate(:announcement, starts_at: 10.days.ago, scheduled_at: 10.days.ago, published_at: 10.days.ago, ends_at: 5.days.from_now, created_at: 100.days.ago) } + + describe '#chronological' do + it 'orders the records correctly' do + results = described_class.chronological + + expect(results).to eq( + [ + adam_announcement, + brenda_announcement, + clara_announcement, + darnelle_announcement, + ] + ) + end + end + + describe '#reverse_chronological' do + it 'orders the records correctly' do + results = described_class.reverse_chronological + + expect(results).to eq( + [ + darnelle_announcement, + clara_announcement, + brenda_announcement, + adam_announcement, + ] + ) + end + end + end + end + + describe 'Validations' do + describe 'text' do + it 'validates presence of attribute' do + record = Fabricate.build(:announcement, text: nil) + + expect(record).to_not be_valid + expect(record.errors[:text]).to be_present + end + end + + describe 'ends_at' do + it 'validates presence when starts_at is present' do + record = Fabricate.build(:announcement, starts_at: 1.day.ago) + + expect(record).to_not be_valid + expect(record.errors[:ends_at]).to be_present + end + + it 'does not validate presence when starts_at is missing' do + record = Fabricate.build(:announcement, starts_at: nil) + + expect(record).to be_valid + expect(record.errors[:ends_at]).to_not be_present + end + end + end + + describe '#publish!' do + it 'publishes an unpublished record' do + announcement = Fabricate(:announcement, published: false, scheduled_at: 10.days.from_now) + + announcement.publish! + + expect(announcement).to be_published + expect(announcement.published_at).to_not be_nil + expect(announcement.scheduled_at).to be_nil + end + end + + describe '#unpublish!' do + it 'unpublishes a published record' do + announcement = Fabricate(:announcement, published: true) + + announcement.unpublish! + + expect(announcement).to_not be_published + expect(announcement.scheduled_at).to be_nil + end + end + + describe '#time_range?' do + it 'returns false when starts_at and ends_at are missing' do + record = Fabricate.build(:announcement, starts_at: nil, ends_at: nil) + + expect(record.time_range?).to be(false) + end + + it 'returns false when starts_at is present and ends_at is missing' do + record = Fabricate.build(:announcement, starts_at: 5.days.from_now, ends_at: nil) + + expect(record.time_range?).to be(false) + end + + it 'returns false when starts_at is missing and ends_at is present' do + record = Fabricate.build(:announcement, starts_at: nil, ends_at: 5.days.from_now) + + expect(record.time_range?).to be(false) + end + + it 'returns true when starts_at and ends_at are present' do + record = Fabricate.build(:announcement, starts_at: 5.days.from_now, ends_at: 10.days.from_now) + + expect(record.time_range?).to be(true) + end + end + + describe '#reactions' do + context 'with announcement_reactions present' do + let!(:account) { Fabricate(:account) } + let!(:announcement) { Fabricate(:announcement) } + let!(:announcement_reaction) { Fabricate(:announcement_reaction, announcement: announcement, created_at: 10.days.ago) } + let!(:announcement_reaction_account) { Fabricate(:announcement_reaction, announcement: announcement, created_at: 5.days.ago, account: account) } + + before do + Fabricate(:announcement_reaction) + end + + it 'returns the announcement reactions for the announcement' do + results = announcement.reactions + + expect(results.first.name).to eq(announcement_reaction.name) + expect(results.last.name).to eq(announcement_reaction_account.name) + end + + it 'returns the announcement reactions for the announcement limited to account' do + results = announcement.reactions(account) + + expect(results.first.name).to eq(announcement_reaction.name) + end + end + end + + describe '#statuses' do + let(:announcement) { Fabricate(:announcement, status_ids: status_ids) } + + context 'with empty status_ids' do + let(:status_ids) { nil } + + it 'returns empty array' do + results = announcement.statuses + + expect(results).to eq([]) + end + end + + context 'with relevant status_ids' do + let(:status) { Fabricate(:status, visibility: :public) } + let(:direct_status) { Fabricate(:status, visibility: :direct) } + let(:status_ids) { [status.id, direct_status.id] } + + it 'returns public and unlisted statuses' do + results = announcement.statuses + + expect(results).to include(status) + expect(results).to_not include(direct_status) + end + end + end +end diff --git a/spec/models/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb index 8a6487c32..06affd634 100644 --- a/spec/models/custom_emoji_spec.rb +++ b/spec/models/custom_emoji_spec.rb @@ -59,7 +59,7 @@ RSpec.describe CustomEmoji do describe '.from_text' do subject { described_class.from_text(text, nil) } - let!(:emojo) { Fabricate(:custom_emoji) } + let!(:emojo) { Fabricate(:custom_emoji, shortcode: 'coolcat') } context 'with plain text' do let(:text) { 'Hello :coolcat:' } diff --git a/spec/requests/api/v1/custom_emojis_spec.rb b/spec/requests/api/v1/custom_emojis_spec.rb index 5de0dda0b..2f0dc7294 100644 --- a/spec/requests/api/v1/custom_emojis_spec.rb +++ b/spec/requests/api/v1/custom_emojis_spec.rb @@ -9,7 +9,7 @@ describe 'Custom Emojis' do describe 'GET /api/v1/custom_emojis' do before do - Fabricate(:custom_emoji, domain: nil, disabled: false, visible_in_picker: true) + Fabricate(:custom_emoji, domain: nil, disabled: false, visible_in_picker: true, shortcode: 'coolcat') end context 'when logged out' do