From 4d21dbfa231ffc8b28548055a635922ffddfaaeb Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Sat, 10 Jun 2023 12:36:09 -0400
Subject: [PATCH] Add coverage for `CLI::Cache` command (#25238)

---
 lib/mastodon/cli/cache.rb                  | 48 +++++++++++++-----
 spec/fabricators/status_stat_fabricator.rb |  8 +++
 spec/lib/mastodon/cli/cache_spec.rb        | 59 ++++++++++++++++++++++
 spec/rails_helper.rb                       | 12 +++++
 4 files changed, 113 insertions(+), 14 deletions(-)
 create mode 100644 spec/fabricators/status_stat_fabricator.rb

diff --git a/lib/mastodon/cli/cache.rb b/lib/mastodon/cli/cache.rb
index 105d4b3c32..e8a2ac1610 100644
--- a/lib/mastodon/cli/cache.rb
+++ b/lib/mastodon/cli/cache.rb
@@ -23,22 +23,12 @@ module Mastodon::CLI
     def recount(type)
       case type
       when 'accounts'
-        processed, = parallelize_with_progress(Account.local.includes(:account_stat)) do |account|
-          account_stat                 = account.account_stat
-          account_stat.following_count = account.active_relationships.count
-          account_stat.followers_count = account.passive_relationships.count
-          account_stat.statuses_count  = account.statuses.where.not(visibility: :direct).count
-
-          account_stat.save if account_stat.changed?
+        processed, = parallelize_with_progress(accounts_with_stats) do |account|
+          recount_account_stats(account)
         end
       when 'statuses'
-        processed, = parallelize_with_progress(Status.includes(:status_stat)) do |status|
-          status_stat                  = status.status_stat
-          status_stat.replies_count    = status.replies.where.not(visibility: :direct).count
-          status_stat.reblogs_count    = status.reblogs.count
-          status_stat.favourites_count = status.favourites.count
-
-          status_stat.save if status_stat.changed?
+        processed, = parallelize_with_progress(statuses_with_stats) do |status|
+          recount_status_stats(status)
         end
       else
         say("Unknown type: #{type}", :red)
@@ -48,5 +38,35 @@ module Mastodon::CLI
       say
       say("OK, recounted #{processed} records", :green)
     end
+
+    private
+
+    def accounts_with_stats
+      Account.local.includes(:account_stat)
+    end
+
+    def statuses_with_stats
+      Status.includes(:status_stat)
+    end
+
+    def recount_account_stats(account)
+      account.account_stat.tap do |account_stat|
+        account_stat.following_count = account.active_relationships.count
+        account_stat.followers_count = account.passive_relationships.count
+        account_stat.statuses_count  = account.statuses.where.not(visibility: :direct).count
+
+        account_stat.save if account_stat.changed?
+      end
+    end
+
+    def recount_status_stats(status)
+      status.status_stat.tap do |status_stat|
+        status_stat.replies_count    = status.replies.where.not(visibility: :direct).count
+        status_stat.reblogs_count    = status.reblogs.count
+        status_stat.favourites_count = status.favourites.count
+
+        status_stat.save if status_stat.changed?
+      end
+    end
   end
 end
diff --git a/spec/fabricators/status_stat_fabricator.rb b/spec/fabricators/status_stat_fabricator.rb
new file mode 100644
index 0000000000..715e6d4ab2
--- /dev/null
+++ b/spec/fabricators/status_stat_fabricator.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+Fabricator(:status_stat) do
+  status
+  replies_count '123'
+  reblogs_count '456'
+  favourites_count '789'
+end
diff --git a/spec/lib/mastodon/cli/cache_spec.rb b/spec/lib/mastodon/cli/cache_spec.rb
index f101bc200f..3ab42dc8ce 100644
--- a/spec/lib/mastodon/cli/cache_spec.rb
+++ b/spec/lib/mastodon/cli/cache_spec.rb
@@ -4,9 +4,68 @@ require 'rails_helper'
 require 'mastodon/cli/cache'
 
 describe Mastodon::CLI::Cache do
+  let(:cli) { described_class.new }
+
   describe '.exit_on_failure?' do
     it 'returns true' do
       expect(described_class.exit_on_failure?).to be true
     end
   end
+
+  describe '#clear' do
+    before { allow(Rails.cache).to receive(:clear) }
+
+    it 'clears the Rails cache' do
+      expect { cli.invoke(:clear) }.to output(
+        a_string_including('OK')
+      ).to_stdout
+      expect(Rails.cache).to have_received(:clear)
+    end
+  end
+
+  describe '#recount' do
+    context 'with the `accounts` argument' do
+      let(:arguments) { ['accounts'] }
+      let(:account_stat) { Fabricate(:account_stat) }
+
+      before do
+        account_stat.update(statuses_count: 123)
+      end
+
+      it 're-calculates account records in the cache' do
+        expect { cli.invoke(:recount, arguments) }.to output(
+          a_string_including('OK')
+        ).to_stdout
+
+        expect(account_stat.reload.statuses_count).to be_zero
+      end
+    end
+
+    context 'with the `statuses` argument' do
+      let(:arguments) { ['statuses'] }
+      let(:status_stat) { Fabricate(:status_stat) }
+
+      before do
+        status_stat.update(replies_count: 123)
+      end
+
+      it 're-calculates account records in the cache' do
+        expect { cli.invoke(:recount, arguments) }.to output(
+          a_string_including('OK')
+        ).to_stdout
+
+        expect(status_stat.reload.replies_count).to be_zero
+      end
+    end
+
+    context 'with an unknown type' do
+      let(:arguments) { ['other-type'] }
+
+      it 'Exits with an error message' do
+        expect { cli.invoke(:recount, arguments) }.to output(
+          a_string_including('Unknown')
+        ).to_stdout.and raise_error(SystemExit)
+      end
+    end
+  end
 end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index f4113d565f..2645f74e40 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -79,6 +79,7 @@ RSpec.configure do |config|
 
   config.before :each, type: :cli do
     stub_stdout
+    stub_reset_connection_pools
   end
 
   config.before :each, type: :feature do
@@ -121,9 +122,20 @@ def attachment_fixture(name)
 end
 
 def stub_stdout
+  # TODO: Is there a bettery way to:
+  # - Avoid CLI command output being printed out
+  # - Allow rspec to assert things against STDOUT
+  # - Avoid disabling stdout for other desirable output (deprecation warnings, for example)
   allow($stdout).to receive(:write)
 end
 
+def stub_reset_connection_pools
+  # TODO: Is there a better way to correctly run specs without stubbing this?
+  # (Avoids reset_connection_pools! in test env)
+  allow(ActiveRecord::Base).to receive(:establish_connection)
+  allow(RedisConfiguration).to receive(:establish_pool)
+end
+
 def stub_jsonld_contexts!
   stub_request(:get, 'https://www.w3.org/ns/activitystreams').to_return(request_fixture('json-ld.activitystreams.txt'))
   stub_request(:get, 'https://w3id.org/identity/v1').to_return(request_fixture('json-ld.identity.txt'))