Merge remote-tracking branch 'parent/main' into kb_development
This commit is contained in:
commit
f20cb3fd59
60 changed files with 1036 additions and 695 deletions
BIN
spec/fixtures/files/elite-assets.tar.gz
vendored
Normal file
BIN
spec/fixtures/files/elite-assets.tar.gz
vendored
Normal file
Binary file not shown.
|
@ -12,7 +12,7 @@ describe PostDeploymentMigrationGenerator, type: :generator do
|
|||
include FileUtils
|
||||
|
||||
tests described_class
|
||||
destination File.expand_path('../../tmp', __dir__)
|
||||
destination Rails.root.join('tmp', 'generator-test')
|
||||
before { prepare_destination }
|
||||
after { rm_rf(destination_root) }
|
||||
|
||||
|
|
|
@ -29,15 +29,25 @@ describe ApplicationHelper do
|
|||
|
||||
describe 'body_classes' do
|
||||
context 'with a body class string from a controller' do
|
||||
before do
|
||||
without_partial_double_verification do
|
||||
allow(helper).to receive_messages(body_class_string: 'modal-layout compose-standalone', current_theme: 'default', current_account: Fabricate(:account))
|
||||
end
|
||||
end
|
||||
before { helper.extend controller_helpers }
|
||||
|
||||
it 'uses the controller body classes in the result' do
|
||||
expect(helper.body_classes).to match(/modal-layout compose-standalone/)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def controller_helpers
|
||||
Module.new do
|
||||
def body_class_string = 'modal-layout compose-standalone'
|
||||
|
||||
def current_account
|
||||
@current_account ||= Fabricate(:account)
|
||||
end
|
||||
|
||||
def current_theme = 'default'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -122,9 +132,7 @@ describe ApplicationHelper do
|
|||
describe 'available_sign_up_path' do
|
||||
context 'when registrations are closed' do
|
||||
before do
|
||||
without_partial_double_verification do
|
||||
allow(Setting).to receive(:registrations_mode).and_return('none')
|
||||
end
|
||||
allow(Setting).to receive(:[]).with('registrations_mode').and_return 'none'
|
||||
end
|
||||
|
||||
it 'redirects to joinmastodon site' do
|
||||
|
|
|
@ -23,12 +23,19 @@ RSpec.describe HomeHelper do
|
|||
context 'with a valid account' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
it 'returns a link to the account' do
|
||||
without_partial_double_verification do
|
||||
allow(helper).to receive_messages(current_account: account, prefers_autoplay?: false)
|
||||
result = helper.account_link_to(account)
|
||||
before { helper.extend controller_helpers }
|
||||
|
||||
expect(result).to match "@#{account.acct}"
|
||||
it 'returns a link to the account' do
|
||||
result = helper.account_link_to(account)
|
||||
|
||||
expect(result).to match "@#{account.acct}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def controller_helpers
|
||||
Module.new do
|
||||
def current_account = Account.last
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,16 +3,12 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe MediaComponentHelper do
|
||||
before { helper.extend controller_helpers }
|
||||
|
||||
describe 'render_video_component' do
|
||||
let(:media) { Fabricate(:media_attachment, type: :video, status: Fabricate(:status)) }
|
||||
let(:result) { helper.render_video_component(media.status) }
|
||||
|
||||
before do
|
||||
without_partial_double_verification do
|
||||
allow(helper).to receive(:current_account).and_return(media.account)
|
||||
end
|
||||
end
|
||||
|
||||
it 'renders a react component for the video' do
|
||||
expect(parsed_html.div['data-component']).to eq('Video')
|
||||
end
|
||||
|
@ -22,12 +18,6 @@ describe MediaComponentHelper do
|
|||
let(:media) { Fabricate(:media_attachment, type: :audio, status: Fabricate(:status)) }
|
||||
let(:result) { helper.render_audio_component(media.status) }
|
||||
|
||||
before do
|
||||
without_partial_double_verification do
|
||||
allow(helper).to receive(:current_account).and_return(media.account)
|
||||
end
|
||||
end
|
||||
|
||||
it 'renders a react component for the audio' do
|
||||
expect(parsed_html.div['data-component']).to eq('Audio')
|
||||
end
|
||||
|
@ -37,12 +27,6 @@ describe MediaComponentHelper do
|
|||
let(:media) { Fabricate(:media_attachment, type: :audio, status: Fabricate(:status)) }
|
||||
let(:result) { helper.render_media_gallery_component(media.status) }
|
||||
|
||||
before do
|
||||
without_partial_double_verification do
|
||||
allow(helper).to receive(:current_account).and_return(media.account)
|
||||
end
|
||||
end
|
||||
|
||||
it 'renders a react component for the media gallery' do
|
||||
expect(parsed_html.div['data-component']).to eq('MediaGallery')
|
||||
end
|
||||
|
@ -54,10 +38,6 @@ describe MediaComponentHelper do
|
|||
|
||||
before do
|
||||
PreviewCardsStatus.create(status: status, preview_card: Fabricate(:preview_card))
|
||||
|
||||
without_partial_double_verification do
|
||||
allow(helper).to receive(:current_account).and_return(status.account)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns the correct react component markup' do
|
||||
|
@ -69,12 +49,6 @@ describe MediaComponentHelper do
|
|||
let(:status) { Fabricate(:status, poll: Fabricate(:poll)) }
|
||||
let(:result) { helper.render_poll_component(status) }
|
||||
|
||||
before do
|
||||
without_partial_double_verification do
|
||||
allow(helper).to receive(:current_account).and_return(status.account)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns the correct react component markup' do
|
||||
expect(parsed_html.div['data-component']).to eq('Poll')
|
||||
end
|
||||
|
@ -85,4 +59,10 @@ describe MediaComponentHelper do
|
|||
def parsed_html
|
||||
Nokogiri::Slop(result)
|
||||
end
|
||||
|
||||
def controller_helpers
|
||||
Module.new do
|
||||
def current_account = Account.last
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,22 +4,29 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/cache'
|
||||
|
||||
describe Mastodon::CLI::Cache do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#clear' do
|
||||
let(:action) { :clear }
|
||||
|
||||
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 { subject }
|
||||
.to output_results('OK')
|
||||
expect(Rails.cache).to have_received(:clear)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#recount' do
|
||||
let(:action) { :recount }
|
||||
|
||||
context 'with the `accounts` argument' do
|
||||
let(:arguments) { ['accounts'] }
|
||||
let(:account_stat) { Fabricate(:account_stat) }
|
||||
|
@ -29,9 +36,8 @@ describe Mastodon::CLI::Cache do
|
|||
end
|
||||
|
||||
it 're-calculates account records in the cache' do
|
||||
expect { cli.invoke(:recount, arguments) }.to output(
|
||||
a_string_including('OK')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('OK')
|
||||
|
||||
expect(account_stat.reload.statuses_count).to be_zero
|
||||
end
|
||||
|
@ -46,9 +52,8 @@ describe Mastodon::CLI::Cache do
|
|||
end
|
||||
|
||||
it 're-calculates account records in the cache' do
|
||||
expect { cli.invoke(:recount, arguments) }.to output(
|
||||
a_string_including('OK')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('OK')
|
||||
|
||||
expect(status_stat.reload.replies_count).to be_zero
|
||||
end
|
||||
|
@ -58,9 +63,9 @@ describe Mastodon::CLI::Cache 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)
|
||||
expect { subject }
|
||||
.to output_results('Unknown')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,42 +4,45 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/canonical_email_blocks'
|
||||
|
||||
describe Mastodon::CLI::CanonicalEmailBlocks do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#find' do
|
||||
let(:action) { :find }
|
||||
let(:arguments) { ['user@example.com'] }
|
||||
|
||||
context 'when a block is present' do
|
||||
before { Fabricate(:canonical_email_block, email: 'user@example.com') }
|
||||
|
||||
it 'announces the presence of the block' do
|
||||
expect { cli.invoke(:find, arguments) }.to output(
|
||||
a_string_including('user@example.com is blocked')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('user@example.com is blocked')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a block is not present' do
|
||||
it 'announces the absence of the block' do
|
||||
expect { cli.invoke(:find, arguments) }.to output(
|
||||
a_string_including('user@example.com is not blocked')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('user@example.com is not blocked')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#remove' do
|
||||
let(:action) { :remove }
|
||||
let(:arguments) { ['user@example.com'] }
|
||||
|
||||
context 'when a block is present' do
|
||||
before { Fabricate(:canonical_email_block, email: 'user@example.com') }
|
||||
|
||||
it 'removes the block' do
|
||||
expect { cli.invoke(:remove, arguments) }.to output(
|
||||
a_string_including('Unblocked user@example.com')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Unblocked user@example.com')
|
||||
|
||||
expect(CanonicalEmailBlock.matching_email('user@example.com')).to be_empty
|
||||
end
|
||||
|
@ -47,9 +50,8 @@ describe Mastodon::CLI::CanonicalEmailBlocks do
|
|||
|
||||
context 'when a block is not present' do
|
||||
it 'announces the absence of the block' do
|
||||
expect { cli.invoke(:remove, arguments) }.to output(
|
||||
a_string_including('user@example.com is not blocked')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('user@example.com is not blocked')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,22 +4,75 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/domains'
|
||||
|
||||
describe Mastodon::CLI::Domains do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#purge' do
|
||||
let(:action) { :purge }
|
||||
|
||||
context 'with accounts from the domain' do
|
||||
let(:options) { {} }
|
||||
let(:domain) { 'host.example' }
|
||||
let!(:account) { Fabricate(:account, domain: domain) }
|
||||
let(:arguments) { [domain] }
|
||||
|
||||
it 'removes the account' do
|
||||
expect { cli.invoke(:purge, [domain], options) }.to output(
|
||||
a_string_including('Removed 1 accounts')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Removed 1 accounts')
|
||||
|
||||
expect { account.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#crawl' do
|
||||
let(:action) { :crawl }
|
||||
|
||||
context 'with accounts from the domain' do
|
||||
let(:domain) { 'host.example' }
|
||||
|
||||
before do
|
||||
Fabricate(:account, domain: domain)
|
||||
stub_request(:get, 'https://host.example/api/v1/instance').to_return(status: 200, body: {}.to_json)
|
||||
stub_request(:get, 'https://host.example/api/v1/instance/peers').to_return(status: 200, body: {}.to_json)
|
||||
stub_request(:get, 'https://host.example/api/v1/instance/activity').to_return(status: 200, body: {}.to_json)
|
||||
stub_const('Mastodon::CLI::Domains::CRAWL_SLEEP_TIME', 0)
|
||||
end
|
||||
|
||||
context 'with --format of summary' do
|
||||
let(:options) { { format: 'summary' } }
|
||||
|
||||
it 'crawls the domains and summarizes results' do
|
||||
expect { subject }
|
||||
.to output_results('Visited 1 domains, 0 failed')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with --format of domains' do
|
||||
let(:options) { { format: 'domains' } }
|
||||
|
||||
it 'crawls the domains and summarizes results' do
|
||||
expect { subject }
|
||||
.to output_results(domain)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with --format of json' do
|
||||
let(:options) { { format: 'json' } }
|
||||
|
||||
it 'crawls the domains and summarizes results' do
|
||||
expect { subject }
|
||||
.to output_results(json_summary)
|
||||
end
|
||||
|
||||
def json_summary
|
||||
Oj.dump('host.example': { activity: {} })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,96 +4,99 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/email_domain_blocks'
|
||||
|
||||
describe Mastodon::CLI::EmailDomainBlocks do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#list' do
|
||||
let(:action) { :list }
|
||||
|
||||
context 'with email domain block records' do
|
||||
let!(:parent_block) { Fabricate(:email_domain_block) }
|
||||
let!(:child_block) { Fabricate(:email_domain_block, parent: parent_block) }
|
||||
let(:options) { {} }
|
||||
|
||||
it 'lists the blocks' do
|
||||
expect { cli.invoke(:list, [], options) }.to output(
|
||||
a_string_including(parent_block.domain)
|
||||
.and(a_string_including(child_block.domain))
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results(
|
||||
parent_block.domain,
|
||||
child_block.domain
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#add' do
|
||||
context 'without any options' do
|
||||
let(:options) { {} }
|
||||
let(:action) { :add }
|
||||
|
||||
context 'without any options' do
|
||||
it 'warns about usage and exits' do
|
||||
expect { cli.invoke(:add, [], options) }.to output(
|
||||
a_string_including('No domain(s) given')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('No domain(s) given')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when blocks exist' do
|
||||
let(:options) { {} }
|
||||
let(:domain) { 'host.example' }
|
||||
let(:arguments) { [domain] }
|
||||
|
||||
before { Fabricate(:email_domain_block, domain: domain) }
|
||||
|
||||
it 'does not add a new block' do
|
||||
expect { cli.invoke(:add, [domain], options) }.to output(
|
||||
a_string_including('is already blocked')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('is already blocked')
|
||||
.and(not_change(EmailDomainBlock, :count))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no blocks exist' do
|
||||
let(:options) { {} }
|
||||
let(:domain) { 'host.example' }
|
||||
let(:arguments) { [domain] }
|
||||
|
||||
it 'adds a new block' do
|
||||
expect { cli.invoke(:add, [domain], options) }.to output(
|
||||
a_string_including('Added 1')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Added 1')
|
||||
.and(change(EmailDomainBlock, :count).by(1))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#remove' do
|
||||
context 'without any options' do
|
||||
let(:options) { {} }
|
||||
let(:action) { :remove }
|
||||
|
||||
context 'without any options' do
|
||||
it 'warns about usage and exits' do
|
||||
expect { cli.invoke(:remove, [], options) }.to output(
|
||||
a_string_including('No domain(s) given')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('No domain(s) given')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when blocks exist' do
|
||||
let(:options) { {} }
|
||||
let(:domain) { 'host.example' }
|
||||
let(:arguments) { [domain] }
|
||||
|
||||
before { Fabricate(:email_domain_block, domain: domain) }
|
||||
|
||||
it 'removes the block' do
|
||||
expect { cli.invoke(:remove, [domain], options) }.to output(
|
||||
a_string_including('Removed 1')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Removed 1')
|
||||
.and(change(EmailDomainBlock, :count).by(-1))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no blocks exist' do
|
||||
let(:options) { {} }
|
||||
let(:domain) { 'host.example' }
|
||||
let(:arguments) { [domain] }
|
||||
|
||||
it 'does not remove a block' do
|
||||
expect { cli.invoke(:remove, [domain], options) }.to output(
|
||||
a_string_including('is not yet blocked')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('is not yet blocked')
|
||||
.and(not_change(EmailDomainBlock, :count))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,5 +4,61 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/emoji'
|
||||
|
||||
describe Mastodon::CLI::Emoji do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#purge' do
|
||||
let(:action) { :purge }
|
||||
|
||||
context 'with existing custom emoji' do
|
||||
before { Fabricate(:custom_emoji) }
|
||||
|
||||
it 'reports a successful purge' do
|
||||
expect { subject }
|
||||
.to output_results('OK')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#import' do
|
||||
context 'with existing custom emoji' do
|
||||
let(:import_path) { Rails.root.join('spec', 'fixtures', 'files', 'elite-assets.tar.gz') }
|
||||
let(:action) { :import }
|
||||
let(:arguments) { [import_path] }
|
||||
|
||||
it 'reports about imported emoji' do
|
||||
expect { subject }
|
||||
.to output_results('Imported 1')
|
||||
.and change(CustomEmoji, :count).by(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#export' do
|
||||
context 'with existing custom emoji' do
|
||||
before do
|
||||
FileUtils.rm_rf(export_path.dirname)
|
||||
FileUtils.mkdir_p(export_path.dirname)
|
||||
|
||||
Fabricate(:custom_emoji)
|
||||
end
|
||||
|
||||
after { FileUtils.rm_rf(export_path.dirname) }
|
||||
|
||||
let(:export_path) { Rails.root.join('tmp', 'cli-tests', 'export.tar.gz') }
|
||||
let(:arguments) { [export_path.dirname.to_s] }
|
||||
let(:action) { :export }
|
||||
|
||||
it 'reports about exported emoji' do
|
||||
expect { subject }
|
||||
.to output_results('Exported 1')
|
||||
.and change { File.exist?(export_path) }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,20 +4,25 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/feeds'
|
||||
|
||||
describe Mastodon::CLI::Feeds do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#build' do
|
||||
let(:action) { :build }
|
||||
|
||||
before { Fabricate(:account) }
|
||||
|
||||
context 'with --all option' do
|
||||
let(:options) { { all: true } }
|
||||
|
||||
it 'regenerates feeds for all accounts' do
|
||||
expect { cli.invoke(:build, [], options) }.to output(
|
||||
a_string_including('Regenerated feeds')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Regenerated feeds')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -27,9 +32,8 @@ describe Mastodon::CLI::Feeds do
|
|||
let(:arguments) { ['alice'] }
|
||||
|
||||
it 'regenerates feeds for the account' do
|
||||
expect { cli.invoke(:build, arguments) }.to output(
|
||||
a_string_including('OK')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('OK')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -37,22 +41,23 @@ describe Mastodon::CLI::Feeds do
|
|||
let(:arguments) { ['invalid-username'] }
|
||||
|
||||
it 'displays an error and exits' do
|
||||
expect { cli.invoke(:build, arguments) }.to output(
|
||||
a_string_including('No such account')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('No such account')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#clear' do
|
||||
let(:action) { :clear }
|
||||
|
||||
before do
|
||||
allow(redis).to receive(:del).with(key_namespace)
|
||||
end
|
||||
|
||||
it 'clears the redis `feed:*` namespace' do
|
||||
expect { cli.invoke(:clear) }.to output(
|
||||
a_string_including('OK')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('OK')
|
||||
|
||||
expect(redis).to have_received(:del).with(key_namespace).once
|
||||
end
|
||||
|
|
|
@ -4,11 +4,16 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/ip_blocks'
|
||||
|
||||
describe Mastodon::CLI::IpBlocks do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#add' do
|
||||
let(:action) { :add }
|
||||
let(:ip_list) do
|
||||
[
|
||||
'192.0.2.1',
|
||||
|
@ -25,10 +30,11 @@ describe Mastodon::CLI::IpBlocks do
|
|||
]
|
||||
end
|
||||
let(:options) { { severity: 'no_access' } }
|
||||
let(:arguments) { ip_list }
|
||||
|
||||
shared_examples 'ip address blocking' do
|
||||
it 'blocks all specified IP addresses' do
|
||||
cli.invoke(:add, ip_list, options)
|
||||
subject
|
||||
|
||||
blocked_ip_addresses = IpBlock.where(ip: ip_list).pluck(:ip)
|
||||
expected_ip_addresses = ip_list.map { |ip| IPAddr.new(ip) }
|
||||
|
@ -37,7 +43,7 @@ describe Mastodon::CLI::IpBlocks do
|
|||
end
|
||||
|
||||
it 'sets the severity for all blocked IP addresses' do
|
||||
cli.invoke(:add, ip_list, options)
|
||||
subject
|
||||
|
||||
blocked_ips_severity = IpBlock.where(ip: ip_list).pluck(:severity).all?(options[:severity])
|
||||
|
||||
|
@ -45,9 +51,8 @@ describe Mastodon::CLI::IpBlocks do
|
|||
end
|
||||
|
||||
it 'displays a success message with a summary' do
|
||||
expect { cli.invoke(:add, ip_list, options) }.to output(
|
||||
a_string_including("Added #{ip_list.size}, skipped 0, failed 0")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results("Added #{ip_list.size}, skipped 0, failed 0")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -57,19 +62,19 @@ describe Mastodon::CLI::IpBlocks do
|
|||
|
||||
context 'when a specified IP address is already blocked' do
|
||||
let!(:blocked_ip) { IpBlock.create(ip: ip_list.last, severity: options[:severity]) }
|
||||
let(:arguments) { ip_list }
|
||||
|
||||
it 'skips the already blocked IP address' do
|
||||
allow(IpBlock).to receive(:new).and_call_original
|
||||
|
||||
cli.invoke(:add, ip_list, options)
|
||||
subject
|
||||
|
||||
expect(IpBlock).to_not have_received(:new).with(ip: ip_list.last)
|
||||
end
|
||||
|
||||
it 'displays the correct summary' do
|
||||
expect { cli.invoke(:add, ip_list, options) }.to output(
|
||||
a_string_including("#{ip_list.last} is already blocked\nAdded #{ip_list.size - 1}, skipped 1, failed 0")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results("#{ip_list.last} is already blocked\nAdded #{ip_list.size - 1}, skipped 1, failed 0")
|
||||
end
|
||||
|
||||
context 'with --force option' do
|
||||
|
@ -77,7 +82,7 @@ describe Mastodon::CLI::IpBlocks do
|
|||
let(:options) { { severity: 'sign_up_requires_approval', force: true } }
|
||||
|
||||
it 'overwrites the existing IP block record' do
|
||||
expect { cli.invoke(:add, ip_list, options) }
|
||||
expect { subject }
|
||||
.to change { blocked_ip.reload.severity }
|
||||
.from('no_access')
|
||||
.to('sign_up_requires_approval')
|
||||
|
@ -89,11 +94,11 @@ describe Mastodon::CLI::IpBlocks do
|
|||
|
||||
context 'when a specified IP address is invalid' do
|
||||
let(:ip_list) { ['320.15.175.0', '9.5.105.255', '0.0.0.0'] }
|
||||
let(:arguments) { ip_list }
|
||||
|
||||
it 'displays the correct summary' do
|
||||
expect { cli.invoke(:add, ip_list, options) }.to output(
|
||||
a_string_including("#{ip_list.first} is invalid\nAdded #{ip_list.size - 1}, skipped 0, failed 1")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results("#{ip_list.first} is invalid\nAdded #{ip_list.size - 1}, skipped 0, failed 1")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -124,6 +129,7 @@ describe Mastodon::CLI::IpBlocks do
|
|||
context 'when a specified IP address fails to be blocked' do
|
||||
let(:ip_address) { '127.0.0.1' }
|
||||
let(:ip_block) { instance_double(IpBlock, ip: ip_address, save: false) }
|
||||
let(:arguments) { [ip_address] }
|
||||
|
||||
before do
|
||||
allow(IpBlock).to receive(:new).and_return(ip_block)
|
||||
|
@ -132,24 +138,25 @@ describe Mastodon::CLI::IpBlocks do
|
|||
end
|
||||
|
||||
it 'displays an error message' do
|
||||
expect { cli.invoke(:add, [ip_address], options) }
|
||||
.to output(
|
||||
a_string_including("#{ip_address} could not be saved")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results("#{ip_address} could not be saved")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no IP address is provided' do
|
||||
let(:arguments) { [] }
|
||||
|
||||
it 'exits with an error message' do
|
||||
expect { cli.add }.to output(
|
||||
a_string_including('No IP(s) given')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('No IP(s) given')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#remove' do
|
||||
let(:action) { :remove }
|
||||
|
||||
context 'when removing exact matches' do
|
||||
let(:ip_list) do
|
||||
[
|
||||
|
@ -166,21 +173,21 @@ describe Mastodon::CLI::IpBlocks do
|
|||
'::/128',
|
||||
]
|
||||
end
|
||||
let(:arguments) { ip_list }
|
||||
|
||||
before do
|
||||
ip_list.each { |ip| IpBlock.create(ip: ip, severity: :no_access) }
|
||||
end
|
||||
|
||||
it 'removes exact IP blocks' do
|
||||
cli.invoke(:remove, ip_list)
|
||||
subject
|
||||
|
||||
expect(IpBlock.where(ip: ip_list)).to_not exist
|
||||
end
|
||||
|
||||
it 'displays success message with a summary' do
|
||||
expect { cli.invoke(:remove, ip_list) }.to output(
|
||||
a_string_including("Removed #{ip_list.size}, skipped 0")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results("Removed #{ip_list.size}, skipped 0")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -192,13 +199,13 @@ describe Mastodon::CLI::IpBlocks do
|
|||
let(:options) { { force: true } }
|
||||
|
||||
it 'removes blocks for IP ranges that cover given IP(s)' do
|
||||
cli.invoke(:remove, arguments, options)
|
||||
subject
|
||||
|
||||
expect(IpBlock.where(id: [first_ip_range_block.id, second_ip_range_block.id])).to_not exist
|
||||
end
|
||||
|
||||
it 'does not remove other IP ranges' do
|
||||
cli.invoke(:remove, arguments, options)
|
||||
subject
|
||||
|
||||
expect(IpBlock.where(id: third_ip_range_block.id)).to exist
|
||||
end
|
||||
|
@ -206,47 +213,46 @@ describe Mastodon::CLI::IpBlocks do
|
|||
|
||||
context 'when a specified IP address is not blocked' do
|
||||
let(:unblocked_ip) { '192.0.2.1' }
|
||||
let(:arguments) { [unblocked_ip] }
|
||||
|
||||
it 'skips the IP address' do
|
||||
expect { cli.invoke(:remove, [unblocked_ip]) }.to output(
|
||||
a_string_including("#{unblocked_ip} is not yet blocked")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results("#{unblocked_ip} is not yet blocked")
|
||||
end
|
||||
|
||||
it 'displays the summary correctly' do
|
||||
expect { cli.invoke(:remove, [unblocked_ip]) }.to output(
|
||||
a_string_including('Removed 0, skipped 1')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Removed 0, skipped 1')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a specified IP address is invalid' do
|
||||
let(:invalid_ip) { '320.15.175.0' }
|
||||
let(:arguments) { [invalid_ip] }
|
||||
|
||||
it 'skips the invalid IP address' do
|
||||
expect { cli.invoke(:remove, [invalid_ip]) }.to output(
|
||||
a_string_including("#{invalid_ip} is invalid")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results("#{invalid_ip} is invalid")
|
||||
end
|
||||
|
||||
it 'displays the summary correctly' do
|
||||
expect { cli.invoke(:remove, [invalid_ip]) }.to output(
|
||||
a_string_including('Removed 0, skipped 1')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Removed 0, skipped 1')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no IP address is provided' do
|
||||
it 'exits with an error message' do
|
||||
expect { cli.remove }.to output(
|
||||
a_string_including('No IP(s) given')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('No IP(s) given')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#export' do
|
||||
let(:action) { :export }
|
||||
|
||||
let(:first_ip_range_block) { IpBlock.create(ip: '192.168.0.0/24', severity: :no_access) }
|
||||
let(:second_ip_range_block) { IpBlock.create(ip: '10.0.0.0/16', severity: :no_access) }
|
||||
let(:third_ip_range_block) { IpBlock.create(ip: '127.0.0.1', severity: :sign_up_block) }
|
||||
|
@ -255,15 +261,13 @@ describe Mastodon::CLI::IpBlocks do
|
|||
let(:options) { { format: 'plain' } }
|
||||
|
||||
it 'exports blocked IPs with "no_access" severity in plain format' do
|
||||
expect { cli.invoke(:export, nil, options) }.to output(
|
||||
a_string_including("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}")
|
||||
end
|
||||
|
||||
it 'does not export bloked IPs with different severities' do
|
||||
expect { cli.invoke(:export, nil, options) }.to_not output(
|
||||
a_string_including("#{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to_not output_results("#{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -271,23 +275,20 @@ describe Mastodon::CLI::IpBlocks do
|
|||
let(:options) { { format: 'nginx' } }
|
||||
|
||||
it 'exports blocked IPs with "no_access" severity in plain format' do
|
||||
expect { cli.invoke(:export, nil, options) }.to output(
|
||||
a_string_including("deny #{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};\ndeny #{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix};")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results("deny #{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};\ndeny #{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix};")
|
||||
end
|
||||
|
||||
it 'does not export bloked IPs with different severities' do
|
||||
expect { cli.invoke(:export, nil, options) }.to_not output(
|
||||
a_string_including("deny #{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to_not output_results("deny #{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when --format option is not provided' do
|
||||
it 'exports blocked IPs in plain format by default' do
|
||||
expect { cli.export }.to output(
|
||||
a_string_including("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}")
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,13 +4,20 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/main'
|
||||
|
||||
describe Mastodon::CLI::Main do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe 'version' do
|
||||
describe '#version' do
|
||||
let(:action) { :version }
|
||||
|
||||
it 'returns the Mastodon version' do
|
||||
expect { described_class.new.invoke(:version) }.to output(
|
||||
a_string_including(Mastodon::Version.to_s)
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results(Mastodon::Version.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,20 +4,26 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/maintenance'
|
||||
|
||||
describe Mastodon::CLI::Maintenance do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#fix_duplicates' do
|
||||
let(:action) { :fix_duplicates }
|
||||
|
||||
context 'when the database version is too old' do
|
||||
before do
|
||||
allow(ActiveRecord::Migrator).to receive(:current_version).and_return(2000_01_01_000000) # Earlier than minimum
|
||||
end
|
||||
|
||||
it 'Exits with error message' do
|
||||
expect { cli.invoke :fix_duplicates }.to output(
|
||||
a_string_including('is too old')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('is too old')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -28,9 +34,9 @@ describe Mastodon::CLI::Maintenance do
|
|||
end
|
||||
|
||||
it 'Exits with error message' do
|
||||
expect { cli.invoke :fix_duplicates }.to output(
|
||||
a_string_including('more recent')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('more recent')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -41,9 +47,9 @@ describe Mastodon::CLI::Maintenance do
|
|||
end
|
||||
|
||||
it 'Exits with error message' do
|
||||
expect { cli.invoke :fix_duplicates }.to output(
|
||||
a_string_including('Sidekiq is running')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('Sidekiq is running')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,18 +4,24 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/media'
|
||||
|
||||
describe Mastodon::CLI::Media do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#remove' do
|
||||
let(:action) { :remove }
|
||||
|
||||
context 'with --prune-profiles and --remove-headers' do
|
||||
let(:options) { { prune_profiles: true, remove_headers: true } }
|
||||
|
||||
it 'warns about usage and exits' do
|
||||
expect { cli.invoke(:remove, [], options) }.to output(
|
||||
a_string_including('--prune-profiles and --remove-headers should not be specified simultaneously')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('--prune-profiles and --remove-headers should not be specified simultaneously')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -23,9 +29,9 @@ describe Mastodon::CLI::Media do
|
|||
let(:options) { { include_follows: true } }
|
||||
|
||||
it 'warns about usage and exits' do
|
||||
expect { cli.invoke(:remove, [], options) }.to output(
|
||||
a_string_including('--include-follows can only be used with --prune-profiles or --remove-headers')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('--include-follows can only be used with --prune-profiles or --remove-headers')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -38,9 +44,8 @@ describe Mastodon::CLI::Media do
|
|||
let(:options) { { prune_profiles: true } }
|
||||
|
||||
it 'removes account avatars' do
|
||||
expect { cli.invoke(:remove, [], options) }.to output(
|
||||
a_string_including('Visited 1')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Visited 1')
|
||||
|
||||
expect(account.reload.avatar).to be_blank
|
||||
end
|
||||
|
@ -50,9 +55,8 @@ describe Mastodon::CLI::Media do
|
|||
let(:options) { { remove_headers: true } }
|
||||
|
||||
it 'removes account header' do
|
||||
expect { cli.invoke(:remove, [], options) }.to output(
|
||||
a_string_including('Visited 1')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Visited 1')
|
||||
|
||||
expect(account.reload.header).to be_blank
|
||||
end
|
||||
|
@ -64,9 +68,8 @@ describe Mastodon::CLI::Media do
|
|||
|
||||
context 'without options' do
|
||||
it 'removes account avatars' do
|
||||
expect { cli.invoke(:remove) }.to output(
|
||||
a_string_including('Removed 1')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Removed 1')
|
||||
|
||||
expect(media_attachment.reload.file).to be_blank
|
||||
expect(media_attachment.reload.thumbnail).to be_blank
|
||||
|
@ -76,25 +79,50 @@ describe Mastodon::CLI::Media do
|
|||
end
|
||||
|
||||
describe '#usage' do
|
||||
context 'without options' do
|
||||
let(:options) { {} }
|
||||
let(:action) { :usage }
|
||||
|
||||
context 'without options' do
|
||||
it 'reports about storage size' do
|
||||
expect { cli.invoke(:usage, [], options) }.to output(
|
||||
a_string_including('0 Bytes')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('0 Bytes')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#lookup' do
|
||||
let(:action) { :lookup }
|
||||
let(:arguments) { [url] }
|
||||
|
||||
context 'with valid url not connected to a record' do
|
||||
let(:url) { 'https://example.host/assets/1' }
|
||||
|
||||
it 'warns about url and exits' do
|
||||
expect { subject }
|
||||
.to output_results('Not a media URL')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a valid media url' do
|
||||
let(:status) { Fabricate(:status) }
|
||||
let(:media_attachment) { Fabricate(:media_attachment, status: status) }
|
||||
let(:url) { media_attachment.file.url(:original) }
|
||||
|
||||
it 'displays the url of a connected status' do
|
||||
expect { subject }
|
||||
.to output_results(status.id.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#refresh' do
|
||||
context 'without any options' do
|
||||
let(:options) { {} }
|
||||
let(:action) { :refresh }
|
||||
|
||||
context 'without any options' do
|
||||
it 'warns about usage and exits' do
|
||||
expect { cli.invoke(:refresh, [], options) }.to output(
|
||||
a_string_including('Specify the source')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('Specify the source')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -108,9 +136,8 @@ describe Mastodon::CLI::Media do
|
|||
let(:status) { Fabricate(:status) }
|
||||
|
||||
it 'redownloads the attachment file' do
|
||||
expect { cli.invoke(:refresh, [], options) }.to output(
|
||||
a_string_including('Downloaded 1 media')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Downloaded 1 media')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -119,9 +146,9 @@ describe Mastodon::CLI::Media do
|
|||
let(:options) { { account: 'not-real-user@example.host' } }
|
||||
|
||||
it 'warns about usage and exits' do
|
||||
expect { cli.invoke(:refresh, [], options) }.to output(
|
||||
a_string_including('No such account')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('No such account')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -135,9 +162,8 @@ describe Mastodon::CLI::Media do
|
|||
let(:account) { Fabricate(:account) }
|
||||
|
||||
it 'redownloads the attachment file' do
|
||||
expect { cli.invoke(:refresh, [], options) }.to output(
|
||||
a_string_including('Downloaded 1 media')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Downloaded 1 media')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -153,9 +179,8 @@ describe Mastodon::CLI::Media do
|
|||
let(:account) { Fabricate(:account, domain: domain) }
|
||||
|
||||
it 'redownloads the attachment file' do
|
||||
expect { cli.invoke(:refresh, [], options) }.to output(
|
||||
a_string_including('Downloaded 1 media')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Downloaded 1 media')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,11 +4,17 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/preview_cards'
|
||||
|
||||
describe Mastodon::CLI::PreviewCards do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#remove' do
|
||||
let(:action) { :remove }
|
||||
|
||||
context 'with relevant preview cards' do
|
||||
before do
|
||||
Fabricate(:preview_card, updated_at: 10.years.ago, type: :link)
|
||||
|
@ -18,10 +24,11 @@ describe Mastodon::CLI::PreviewCards do
|
|||
|
||||
context 'with no arguments' do
|
||||
it 'deletes thumbnails for local preview cards' do
|
||||
expect { cli.invoke(:remove) }.to output(
|
||||
a_string_including('Removed 2 preview cards')
|
||||
.and(a_string_including('approx. 119 KB'))
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results(
|
||||
'Removed 2 preview cards',
|
||||
'approx. 119 KB'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -29,10 +36,11 @@ describe Mastodon::CLI::PreviewCards do
|
|||
let(:options) { { link: true } }
|
||||
|
||||
it 'deletes thumbnails for local preview cards' do
|
||||
expect { cli.invoke(:remove, [], options) }.to output(
|
||||
a_string_including('Removed 1 link-type preview cards')
|
||||
.and(a_string_including('approx. 59.6 KB'))
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results(
|
||||
'Removed 1 link-type preview cards',
|
||||
'approx. 59.6 KB'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -40,10 +48,11 @@ describe Mastodon::CLI::PreviewCards do
|
|||
let(:options) { { days: 365 } }
|
||||
|
||||
it 'deletes thumbnails for local preview cards' do
|
||||
expect { cli.invoke(:remove, [], options) }.to output(
|
||||
a_string_including('Removed 1 preview cards')
|
||||
.and(a_string_including('approx. 59.6 KB'))
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results(
|
||||
'Removed 1 preview cards',
|
||||
'approx. 59.6 KB'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,5 +4,79 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/search'
|
||||
|
||||
describe Mastodon::CLI::Search do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#deploy' do
|
||||
let(:action) { :deploy }
|
||||
|
||||
context 'with concurrency out of range' do
|
||||
let(:options) { { concurrency: -100 } }
|
||||
|
||||
it 'Exits with error message' do
|
||||
expect { subject }
|
||||
.to output_results('this concurrency setting')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with batch size out of range' do
|
||||
let(:options) { { batch_size: -100_000 } }
|
||||
|
||||
it 'Exits with error message' do
|
||||
expect { subject }
|
||||
.to output_results('this batch_size setting')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without options' do
|
||||
before { stub_search_indexes }
|
||||
|
||||
let(:indexed_count) { 1 }
|
||||
let(:deleted_count) { 2 }
|
||||
|
||||
it 'reports about storage size' do
|
||||
expect { subject }
|
||||
.to output_results(
|
||||
"Indexed #{described_class::INDICES.size * indexed_count} records",
|
||||
"de-indexed #{described_class::INDICES.size * deleted_count}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def stub_search_indexes
|
||||
described_class::INDICES.each do |index|
|
||||
allow(index)
|
||||
.to receive_messages(
|
||||
specification: instance_double(Chewy::Index::Specification, changed?: true, lock!: nil),
|
||||
purge: nil
|
||||
)
|
||||
|
||||
importer_double = importer_double_for(index)
|
||||
allow(importer_double).to receive(:on_progress).and_yield([indexed_count, deleted_count])
|
||||
allow("Importer::#{index}Importer".constantize)
|
||||
.to receive(:new)
|
||||
.and_return(importer_double)
|
||||
end
|
||||
end
|
||||
|
||||
def importer_double_for(index)
|
||||
instance_double(
|
||||
"Importer::#{index}Importer".constantize,
|
||||
clean_up!: nil,
|
||||
estimate!: 100,
|
||||
import!: nil,
|
||||
on_failure: nil,
|
||||
# on_progress: nil,
|
||||
optimize_for_import!: nil,
|
||||
optimize_for_search!: nil
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,59 +7,64 @@ describe Mastodon::CLI::Settings do
|
|||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe 'subcommand "registrations"' do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { Mastodon::CLI::Registrations.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
before do
|
||||
Setting.registrations_mode = nil
|
||||
end
|
||||
|
||||
describe '#open' do
|
||||
let(:action) { :open }
|
||||
|
||||
it 'changes "registrations_mode" to "open"' do
|
||||
expect { cli.open }.to change(Setting, :registrations_mode).from(nil).to('open')
|
||||
expect { subject }.to change(Setting, :registrations_mode).from(nil).to('open')
|
||||
end
|
||||
|
||||
it 'displays success message' do
|
||||
expect { cli.open }.to output(
|
||||
a_string_including('OK')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('OK')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#approved' do
|
||||
let(:action) { :approved }
|
||||
|
||||
it 'changes "registrations_mode" to "approved"' do
|
||||
expect { cli.approved }.to change(Setting, :registrations_mode).from(nil).to('approved')
|
||||
expect { subject }.to change(Setting, :registrations_mode).from(nil).to('approved')
|
||||
end
|
||||
|
||||
it 'displays success message' do
|
||||
expect { cli.approved }.to output(
|
||||
a_string_including('OK')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('OK')
|
||||
end
|
||||
|
||||
context 'with --require-reason' do
|
||||
before do
|
||||
cli.options = { require_reason: true }
|
||||
end
|
||||
let(:options) { { require_reason: true } }
|
||||
|
||||
it 'changes "registrations_mode" to "approved"' do
|
||||
expect { cli.approved }.to change(Setting, :registrations_mode).from(nil).to('approved')
|
||||
expect { subject }.to change(Setting, :registrations_mode).from(nil).to('approved')
|
||||
end
|
||||
|
||||
it 'sets "require_invite_text" to "true"' do
|
||||
expect { cli.approved }.to change(Setting, :require_invite_text).from(false).to(true)
|
||||
expect { subject }.to change(Setting, :require_invite_text).from(false).to(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#close' do
|
||||
let(:action) { :close }
|
||||
|
||||
it 'changes "registrations_mode" to "none"' do
|
||||
expect { cli.close }.to change(Setting, :registrations_mode).from(nil).to('none')
|
||||
expect { subject }.to change(Setting, :registrations_mode).from(nil).to('none')
|
||||
end
|
||||
|
||||
it 'displays success message' do
|
||||
expect { cli.close }.to output(
|
||||
a_string_including('OK')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('OK')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,26 +4,31 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/statuses'
|
||||
|
||||
describe Mastodon::CLI::Statuses do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#remove', use_transactional_tests: false do
|
||||
let(:action) { :remove }
|
||||
|
||||
context 'with small batch size' do
|
||||
let(:options) { { batch_size: 0 } }
|
||||
|
||||
it 'exits with error message' do
|
||||
expect { cli.invoke :remove, [], options }.to output(
|
||||
a_string_including('Cannot run')
|
||||
).to_stdout.and raise_error(SystemExit)
|
||||
expect { subject }
|
||||
.to output_results('Cannot run')
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with default batch size' do
|
||||
it 'removes unreferenced statuses' do
|
||||
expect { cli.invoke :remove }.to output(
|
||||
a_string_including('Done after')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Done after')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,23 +4,26 @@ require 'rails_helper'
|
|||
require 'mastodon/cli/upgrade'
|
||||
|
||||
describe Mastodon::CLI::Upgrade do
|
||||
subject { cli.invoke(action, arguments, options) }
|
||||
|
||||
let(:cli) { described_class.new }
|
||||
let(:arguments) { [] }
|
||||
let(:options) { {} }
|
||||
|
||||
it_behaves_like 'CLI Command'
|
||||
|
||||
describe '#storage_schema' do
|
||||
context 'with records that dont need upgrading' do
|
||||
let(:options) { {} }
|
||||
let(:action) { :storage_schema }
|
||||
|
||||
context 'with records that dont need upgrading' do
|
||||
before do
|
||||
Fabricate(:account)
|
||||
Fabricate(:media_attachment)
|
||||
end
|
||||
|
||||
it 'does not upgrade storage for the attachments' do
|
||||
expect { cli.invoke(:storage_schema, [], options) }.to output(
|
||||
a_string_including('Upgraded storage schema of 0 records')
|
||||
).to_stdout
|
||||
expect { subject }
|
||||
.to output_results('Upgraded storage schema of 0 records')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -88,6 +88,7 @@ RSpec.configure do |config|
|
|||
config.include Chewy::Rspec::Helpers
|
||||
config.include Redisable
|
||||
config.include SignedRequestHelpers, type: :request
|
||||
config.include CommandLineHelpers, type: :cli
|
||||
|
||||
config.around(:each, use_transactional_tests: false) do |example|
|
||||
self.use_transactional_tests = false
|
||||
|
|
|
@ -2,25 +2,21 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V2::SearchController do
|
||||
render_views
|
||||
|
||||
describe 'Search API' do
|
||||
context 'with token' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:search') }
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:search' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
describe 'GET /api/v2/search' do
|
||||
let!(:bob) { Fabricate(:account, username: 'bob_test') }
|
||||
let!(:ana) { Fabricate(:account, username: 'ana_test') }
|
||||
let!(:tom) { Fabricate(:account, username: 'tom_test') }
|
||||
let(:params) { { q: 'test' } }
|
||||
|
||||
it 'returns http success' do
|
||||
get :index, params: params
|
||||
get '/api/v2/search', headers: headers, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
@ -29,7 +25,7 @@ RSpec.describe Api::V2::SearchController do
|
|||
let(:params) { { q: 'test', type: 'accounts' } }
|
||||
|
||||
it 'returns all matching accounts' do
|
||||
get :index, params: params
|
||||
get '/api/v2/search', headers: headers, params: params
|
||||
|
||||
expect(body_as_json[:accounts].pluck(:id)).to contain_exactly(bob.id.to_s, ana.id.to_s, tom.id.to_s)
|
||||
end
|
||||
|
@ -38,7 +34,7 @@ RSpec.describe Api::V2::SearchController do
|
|||
let(:params) { { q: 'test1', resolve: '1' } }
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
get :index, params: params
|
||||
get '/api/v2/search', headers: headers, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
@ -48,7 +44,7 @@ RSpec.describe Api::V2::SearchController do
|
|||
let(:params) { { q: 'test1', offset: 1 } }
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
get :index, params: params
|
||||
get '/api/v2/search', headers: headers, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
@ -62,7 +58,7 @@ RSpec.describe Api::V2::SearchController do
|
|||
end
|
||||
|
||||
it 'returns only the followed accounts' do
|
||||
get :index, params: params
|
||||
get '/api/v2/search', headers: headers, params: params
|
||||
|
||||
expect(body_as_json[:accounts].pluck(:id)).to contain_exactly(ana.id.to_s)
|
||||
end
|
||||
|
@ -73,7 +69,7 @@ RSpec.describe Api::V2::SearchController do
|
|||
before { allow(Search).to receive(:new).and_raise(Mastodon::SyntaxError) }
|
||||
|
||||
it 'returns http unprocessable_entity' do
|
||||
get :index, params: params
|
||||
get '/api/v2/search', headers: headers, params: params
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
|
@ -83,7 +79,7 @@ RSpec.describe Api::V2::SearchController do
|
|||
before { allow(Search).to receive(:new).and_raise(ActiveRecord::RecordNotFound) }
|
||||
|
||||
it 'returns http not_found' do
|
||||
get :index, params: params
|
||||
get '/api/v2/search', headers: headers, params: params
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
|
@ -92,11 +88,11 @@ RSpec.describe Api::V2::SearchController do
|
|||
end
|
||||
|
||||
context 'without token' do
|
||||
describe 'GET #index' do
|
||||
describe 'GET /api/v2/search' do
|
||||
let(:search_params) { nil }
|
||||
|
||||
before do
|
||||
get :index, params: search_params
|
||||
get '/api/v2/search', params: search_params
|
||||
end
|
||||
|
||||
context 'without a `q` param' do
|
|
@ -378,12 +378,10 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
|
|||
end
|
||||
end
|
||||
|
||||
it 'creates at least some accounts' do
|
||||
expect { subject }.to change { Account.remote.count }.by_at_least(2)
|
||||
end
|
||||
|
||||
it 'creates no more account than the limit allows' do
|
||||
expect { subject }.to change { Account.remote.count }.by_at_most(5)
|
||||
it 'creates accounts without exceeding rate limit' do
|
||||
expect { subject }
|
||||
.to create_some_remote_accounts
|
||||
.and create_fewer_than_rate_limit_accounts
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -445,12 +443,20 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
|
|||
end
|
||||
end
|
||||
|
||||
it 'creates at least some accounts' do
|
||||
expect { subject.call('user1', 'foo.test', payload) }.to change { Account.remote.count }.by_at_least(2)
|
||||
end
|
||||
|
||||
it 'creates no more account than the limit allows' do
|
||||
expect { subject.call('user1', 'foo.test', payload) }.to change { Account.remote.count }.by_at_most(5)
|
||||
it 'creates accounts without exceeding rate limit' do
|
||||
expect { subject.call('user1', 'foo.test', payload) }
|
||||
.to create_some_remote_accounts
|
||||
.and create_fewer_than_rate_limit_accounts
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_some_remote_accounts
|
||||
change(Account.remote, :count).by_at_least(2)
|
||||
end
|
||||
|
||||
def create_fewer_than_rate_limit_accounts
|
||||
change(Account.remote, :count).by_at_most(5)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -47,8 +47,15 @@ RSpec.describe DeleteAccountService, type: :service do
|
|||
|
||||
let!(:account_note) { Fabricate(:account_note, account: account) }
|
||||
|
||||
it 'deletes associated owned records' do
|
||||
expect { subject }.to change {
|
||||
it 'deletes associated owned and target records and target notifications' do
|
||||
expect { subject }
|
||||
.to delete_associated_owned_records
|
||||
.and delete_associated_target_records
|
||||
.and delete_associated_target_notifications
|
||||
end
|
||||
|
||||
def delete_associated_owned_records
|
||||
change do
|
||||
[
|
||||
account.statuses,
|
||||
account.media_attachments,
|
||||
|
@ -61,7 +68,7 @@ RSpec.describe DeleteAccountService, type: :service do
|
|||
account.polls,
|
||||
account.account_notes,
|
||||
].map(&:count)
|
||||
}.from([3, 1, 1, 1, 1, 1, 2, 2, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
end.from([3, 1, 1, 1, 1, 1, 2, 2, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
end
|
||||
|
||||
it 'deletes associated owned record groups' do
|
||||
|
@ -82,20 +89,20 @@ RSpec.describe DeleteAccountService, type: :service do
|
|||
expect { bookmark_category_status.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
|
||||
it 'deletes associated target records' do
|
||||
expect { subject }.to change {
|
||||
def delete_associated_target_records
|
||||
change do
|
||||
[
|
||||
AccountPin.where(target_account: account),
|
||||
].map(&:count)
|
||||
}.from([1]).to([0])
|
||||
end.from([1]).to([0])
|
||||
end
|
||||
|
||||
it 'deletes associated target notifications' do
|
||||
expect { subject }.to change {
|
||||
def delete_associated_target_notifications
|
||||
change do
|
||||
%w(
|
||||
poll favourite emoji_reaction status mention follow
|
||||
).map { |type| Notification.where(type: type).count }
|
||||
}.from([1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0])
|
||||
end.from([1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -18,14 +18,15 @@ RSpec.describe SuspendAccountService, type: :service do
|
|||
account.suspend!
|
||||
end
|
||||
|
||||
it "unmerges from local followers' feeds" do
|
||||
subject
|
||||
it 'unmerges from feeds of local followers and preserves suspended flag' do
|
||||
expect { subject }
|
||||
.to_not change_suspended_flag
|
||||
expect(FeedManager.instance).to have_received(:unmerge_from_home).with(account, local_follower)
|
||||
expect(FeedManager.instance).to have_received(:unmerge_from_list).with(account, list)
|
||||
end
|
||||
|
||||
it 'does not change the “suspended” flag' do
|
||||
expect { subject }.to_not change(account, :suspended?)
|
||||
def change_suspended_flag
|
||||
change(account, :suspended?)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -45,14 +45,19 @@ RSpec.describe UnsuspendAccountService, type: :service do
|
|||
remote_follower.follow!(account)
|
||||
end
|
||||
|
||||
it "merges back into local followers' feeds" do
|
||||
it 'merges back into feeds of local followers and sends update' do
|
||||
subject
|
||||
|
||||
expect_feeds_merged
|
||||
expect_updates_sent
|
||||
end
|
||||
|
||||
def expect_feeds_merged
|
||||
expect(FeedManager.instance).to have_received(:merge_into_home).with(account, local_follower)
|
||||
expect(FeedManager.instance).to have_received(:merge_into_list).with(account, list)
|
||||
end
|
||||
|
||||
it 'sends an update actor to followers and reporters' do
|
||||
subject
|
||||
def expect_updates_sent
|
||||
expect(a_request(:post, remote_follower.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once
|
||||
expect(a_request(:post, remote_reporter.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once
|
||||
end
|
||||
|
@ -73,19 +78,20 @@ RSpec.describe UnsuspendAccountService, type: :service do
|
|||
allow(resolve_account_service).to receive(:call).with(account).and_return(account)
|
||||
end
|
||||
|
||||
it 're-fetches the account' do
|
||||
subject
|
||||
it 're-fetches the account, merges feeds, and preserves suspended' do
|
||||
expect { subject }
|
||||
.to_not change_suspended_flag
|
||||
expect_feeds_merged
|
||||
expect(resolve_account_service).to have_received(:call).with(account)
|
||||
end
|
||||
|
||||
it "merges back into local followers' feeds" do
|
||||
subject
|
||||
def expect_feeds_merged
|
||||
expect(FeedManager.instance).to have_received(:merge_into_home).with(account, local_follower)
|
||||
expect(FeedManager.instance).to have_received(:merge_into_list).with(account, list)
|
||||
end
|
||||
|
||||
it 'does not change the “suspended” flag' do
|
||||
expect { subject }.to_not change(account, :suspended?)
|
||||
def change_suspended_flag
|
||||
change(account, :suspended?)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -97,19 +103,20 @@ RSpec.describe UnsuspendAccountService, type: :service do
|
|||
end
|
||||
end
|
||||
|
||||
it 're-fetches the account' do
|
||||
subject
|
||||
it 're-fetches the account, does not merge feeds, marks suspended' do
|
||||
expect { subject }
|
||||
.to change_suspended_to_true
|
||||
expect(resolve_account_service).to have_received(:call).with(account)
|
||||
expect_feeds_not_merged
|
||||
end
|
||||
|
||||
it "does not merge back into local followers' feeds" do
|
||||
subject
|
||||
def expect_feeds_not_merged
|
||||
expect(FeedManager.instance).to_not have_received(:merge_into_home).with(account, local_follower)
|
||||
expect(FeedManager.instance).to_not have_received(:merge_into_list).with(account, list)
|
||||
end
|
||||
|
||||
it 'marks account as suspended' do
|
||||
expect { subject }.to change(account, :suspended?).from(false).to(true)
|
||||
def change_suspended_to_true
|
||||
change(account, :suspended?).from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -118,13 +125,14 @@ RSpec.describe UnsuspendAccountService, type: :service do
|
|||
allow(resolve_account_service).to receive(:call).with(account).and_return(nil)
|
||||
end
|
||||
|
||||
it 're-fetches the account' do
|
||||
it 're-fetches the account and does not merge feeds' do
|
||||
subject
|
||||
|
||||
expect(resolve_account_service).to have_received(:call).with(account)
|
||||
expect_feeds_not_merged
|
||||
end
|
||||
|
||||
it "does not merge back into local followers' feeds" do
|
||||
subject
|
||||
def expect_feeds_not_merged
|
||||
expect(FeedManager.instance).to_not have_received(:merge_into_home).with(account, local_follower)
|
||||
expect(FeedManager.instance).to_not have_received(:merge_into_list).with(account, list)
|
||||
end
|
||||
|
|
9
spec/support/command_line_helpers.rb
Normal file
9
spec/support/command_line_helpers.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module CommandLineHelpers
|
||||
def output_results(*args)
|
||||
output(
|
||||
include(*args)
|
||||
).to_stdout
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue