Add test coverage for `Mastodon::CLI::Accounts#cull` (#25250)
This commit is contained in:
		
							parent
							
								
									a6c898f501
								
							
						
					
					
						commit
						70cd2d6000
					
				| 
						 | 
					@ -1109,4 +1109,143 @@ describe Mastodon::CLI::Accounts do
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe '#cull' do
 | 
				
			||||||
 | 
					    let(:delete_account_service) { instance_double(DeleteAccountService, call: nil) }
 | 
				
			||||||
 | 
					    let!(:tom)                   { Fabricate(:account, updated_at: 30.days.ago, username: 'tom', uri: 'https://example.com/users/tom', domain: 'example.com') }
 | 
				
			||||||
 | 
					    let!(:bob)                   { Fabricate(:account, updated_at: 30.days.ago, last_webfingered_at: nil, username: 'bob', uri: 'https://example.org/users/bob', domain: 'example.org') }
 | 
				
			||||||
 | 
					    let!(:gon)                   { Fabricate(:account, updated_at: 15.days.ago, last_webfingered_at: 15.days.ago, username: 'gon', uri: 'https://example.net/users/gon', domain: 'example.net') }
 | 
				
			||||||
 | 
					    let!(:ana)                   { Fabricate(:account, username: 'ana', uri: 'https://example.com/users/ana', domain: 'example.com') }
 | 
				
			||||||
 | 
					    let!(:tales)                 { Fabricate(:account, updated_at: 10.days.ago, last_webfingered_at: nil, username: 'tales', uri: 'https://example.net/users/tales', domain: 'example.net') }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    before do
 | 
				
			||||||
 | 
					      allow(DeleteAccountService).to receive(:new).and_return(delete_account_service)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when no domain is specified' do
 | 
				
			||||||
 | 
					      let(:scope) { Account.remote.where(protocol: :activitypub).partitioned }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      before do
 | 
				
			||||||
 | 
					        allow(cli).to receive(:parallelize_with_progress).and_yield(tom)
 | 
				
			||||||
 | 
					                                                         .and_yield(bob)
 | 
				
			||||||
 | 
					                                                         .and_yield(gon)
 | 
				
			||||||
 | 
					                                                         .and_yield(ana)
 | 
				
			||||||
 | 
					                                                         .and_yield(tales)
 | 
				
			||||||
 | 
					                                                         .and_return([5, 3])
 | 
				
			||||||
 | 
					        stub_request(:head, 'https://example.org/users/bob').to_return(status: 404)
 | 
				
			||||||
 | 
					        stub_request(:head, 'https://example.net/users/gon').to_return(status: 410)
 | 
				
			||||||
 | 
					        stub_request(:head, 'https://example.net/users/tales').to_return(status: 200)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'deletes all inactive remote accounts that longer exist in the origin server' do
 | 
				
			||||||
 | 
					        cli.cull
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(cli).to have_received(:parallelize_with_progress).with(scope).once
 | 
				
			||||||
 | 
					        expect(delete_account_service).to have_received(:call).with(bob, reserve_username: false).once
 | 
				
			||||||
 | 
					        expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not delete any active remote account that still exists in the origin server' do
 | 
				
			||||||
 | 
					        cli.cull
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(cli).to have_received(:parallelize_with_progress).with(scope).once
 | 
				
			||||||
 | 
					        expect(delete_account_service).to_not have_received(:call).with(tom, reserve_username: false)
 | 
				
			||||||
 | 
					        expect(delete_account_service).to_not have_received(:call).with(ana, reserve_username: false)
 | 
				
			||||||
 | 
					        expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'touches inactive remote accounts that have not been deleted' do
 | 
				
			||||||
 | 
					        allow(tales).to receive(:touch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cli.cull
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(tales).to have_received(:touch).once
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'displays the summary correctly' do
 | 
				
			||||||
 | 
					        expect { cli.cull }.to output(
 | 
				
			||||||
 | 
					          a_string_including('Visited 5 accounts, removed 3')
 | 
				
			||||||
 | 
					        ).to_stdout
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when a domain is specified' do
 | 
				
			||||||
 | 
					      let(:domain) { 'example.net' }
 | 
				
			||||||
 | 
					      let(:scope)  { Account.remote.where(protocol: :activitypub, domain: domain).partitioned }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      before do
 | 
				
			||||||
 | 
					        allow(cli).to receive(:parallelize_with_progress).and_yield(gon)
 | 
				
			||||||
 | 
					                                                         .and_yield(tales)
 | 
				
			||||||
 | 
					                                                         .and_return([2, 2])
 | 
				
			||||||
 | 
					        stub_request(:head, 'https://example.net/users/gon').to_return(status: 410)
 | 
				
			||||||
 | 
					        stub_request(:head, 'https://example.net/users/tales').to_return(status: 404)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'deletes inactive remote accounts that longer exist in the specified domain' do
 | 
				
			||||||
 | 
					        cli.cull(domain)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(cli).to have_received(:parallelize_with_progress).with(scope).once
 | 
				
			||||||
 | 
					        expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once
 | 
				
			||||||
 | 
					        expect(delete_account_service).to have_received(:call).with(tales, reserve_username: false).once
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'displays the summary correctly' do
 | 
				
			||||||
 | 
					        expect { cli.cull }.to output(
 | 
				
			||||||
 | 
					          a_string_including('Visited 2 accounts, removed 2')
 | 
				
			||||||
 | 
					        ).to_stdout
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when a domain is unavailable' do
 | 
				
			||||||
 | 
					      shared_examples 'an unavailable domain' do
 | 
				
			||||||
 | 
					        before do
 | 
				
			||||||
 | 
					          allow(cli).to receive(:parallelize_with_progress).and_yield(tales).and_return([1, 0])
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'skips accounts from the unavailable domain' do
 | 
				
			||||||
 | 
					          cli.cull
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'displays the summary correctly' do
 | 
				
			||||||
 | 
					          expect { cli.cull }.to output(
 | 
				
			||||||
 | 
					            a_string_including("Visited 1 accounts, removed 0\nThe following domains were not available during the check:\n    example.net")
 | 
				
			||||||
 | 
					          ).to_stdout
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      context 'when a connection timeout occurs' do
 | 
				
			||||||
 | 
					        before do
 | 
				
			||||||
 | 
					          stub_request(:head, 'https://example.net/users/tales').to_timeout
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it_behaves_like 'an unavailable domain'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      context 'when a connection error occurs' do
 | 
				
			||||||
 | 
					        before do
 | 
				
			||||||
 | 
					          stub_request(:head, 'https://example.net/users/tales').to_raise(HTTP::ConnectionError)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it_behaves_like 'an unavailable domain'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      context 'when an ssl error occurs' do
 | 
				
			||||||
 | 
					        before do
 | 
				
			||||||
 | 
					          stub_request(:head, 'https://example.net/users/tales').to_raise(OpenSSL::SSL::SSLError)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it_behaves_like 'an unavailable domain'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      context 'when a private network address error occurs' do
 | 
				
			||||||
 | 
					        before do
 | 
				
			||||||
 | 
					          stub_request(:head, 'https://example.net/users/tales').to_raise(Mastodon::PrivateNetworkAddressError)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it_behaves_like 'an unavailable domain'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue