From c0f46e90314f0cec405974bc7e14d818ed380248 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Wed, 23 Oct 2024 03:46:55 -0400
Subject: [PATCH] Fortify coverage for `Follow` model (#32472)

---
 spec/models/follow_spec.rb | 81 +++++++++++++++++++++++++++++++-------
 1 file changed, 67 insertions(+), 14 deletions(-)

diff --git a/spec/models/follow_spec.rb b/spec/models/follow_spec.rb
index f22bd6ea8..8684170dc 100644
--- a/spec/models/follow_spec.rb
+++ b/spec/models/follow_spec.rb
@@ -3,27 +3,26 @@
 require 'rails_helper'
 
 RSpec.describe Follow do
-  let(:alice) { Fabricate(:account, username: 'alice') }
-  let(:bob)   { Fabricate(:account, username: 'bob') }
-
-  describe 'validations' do
-    subject { described_class.new(account: alice, target_account: bob, rate_limit: true) }
-
+  describe 'Associations' do
     it { is_expected.to belong_to(:account).required }
     it { is_expected.to belong_to(:target_account).required }
+  end
 
-    it 'is invalid if account already follows too many people' do
-      alice.update(following_count: FollowLimitValidator::LIMIT)
+  describe 'Validations' do
+    subject { Fabricate.build :follow, rate_limit: true }
 
-      expect(subject).to_not be_valid
-      expect(subject).to model_have_error_on_field(:base)
+    let(:account) { Fabricate(:account) }
+
+    context 'when account follows too many people' do
+      before { account.update(following_count: FollowLimitValidator::LIMIT) }
+
+      it { is_expected.to_not allow_value(account).for(:account).against(:base) }
     end
 
-    it 'is valid if account is only on the brink of following too many people' do
-      alice.update(following_count: FollowLimitValidator::LIMIT - 1)
+    context 'when account is on brink of following too many people' do
+      before { account.update(following_count: FollowLimitValidator::LIMIT - 1) }
 
-      expect(subject).to be_valid
-      expect(subject).to_not model_have_error_on_field(:base)
+      it { is_expected.to allow_value(account).for(:account).against(:base) }
     end
   end
 
@@ -54,4 +53,58 @@ RSpec.describe Follow do
       expect(account.requested?(target_account)).to be true
     end
   end
+
+  describe '#local?' do
+    it { is_expected.to_not be_local }
+  end
+
+  describe 'Callbacks' do
+    describe 'Setting a URI' do
+      context 'when URI exists' do
+        subject { Fabricate.build :follow, uri: 'https://uri/value' }
+
+        it 'does not change' do
+          expect { subject.save }
+            .to not_change(subject, :uri)
+        end
+      end
+
+      context 'when URI is blank' do
+        subject { Fabricate.build :follow, uri: nil }
+
+        it 'populates the value' do
+          expect { subject.save }
+            .to change(subject, :uri).to(be_present)
+        end
+      end
+    end
+
+    describe 'Maintaining counters' do
+      subject { Fabricate.build :follow, account:, target_account: }
+
+      let(:account) { Fabricate :account }
+      let(:target_account) { Fabricate :account }
+
+      before do
+        account.account_stat.update following_count: 123
+        target_account.account_stat.update followers_count: 123
+      end
+
+      describe 'saving the follow' do
+        it 'increments counters' do
+          expect { subject.save }
+            .to change(account, :following_count).by(1)
+            .and(change(target_account, :followers_count).by(1))
+        end
+      end
+
+      describe 'destroying the follow' do
+        it 'decrements counters' do
+          expect { subject.destroy }
+            .to change(account, :following_count).by(-1)
+            .and(change(target_account, :followers_count).by(-1))
+        end
+      end
+    end
+  end
 end