Formalize some patterns in cli specs (#28255)

This commit is contained in:
Matt Jankowski 2023-12-07 08:49:14 -05:00 committed by GitHub
parent 5d97a897c8
commit ad34d33bfd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 492 additions and 449 deletions

File diff suppressed because it is too large Load diff

View file

@ -4,22 +4,29 @@ require 'rails_helper'
require 'mastodon/cli/cache' require 'mastodon/cli/cache'
describe Mastodon::CLI::Cache do describe Mastodon::CLI::Cache do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#clear' do describe '#clear' do
let(:action) { :clear }
before { allow(Rails.cache).to receive(:clear) } before { allow(Rails.cache).to receive(:clear) }
it 'clears the Rails cache' do it 'clears the Rails cache' do
expect { cli.invoke(:clear) }.to output( expect { subject }
a_string_including('OK') .to output_results('OK')
).to_stdout
expect(Rails.cache).to have_received(:clear) expect(Rails.cache).to have_received(:clear)
end end
end end
describe '#recount' do describe '#recount' do
let(:action) { :recount }
context 'with the `accounts` argument' do context 'with the `accounts` argument' do
let(:arguments) { ['accounts'] } let(:arguments) { ['accounts'] }
let(:account_stat) { Fabricate(:account_stat) } let(:account_stat) { Fabricate(:account_stat) }
@ -29,9 +36,8 @@ describe Mastodon::CLI::Cache do
end end
it 're-calculates account records in the cache' do it 're-calculates account records in the cache' do
expect { cli.invoke(:recount, arguments) }.to output( expect { subject }
a_string_including('OK') .to output_results('OK')
).to_stdout
expect(account_stat.reload.statuses_count).to be_zero expect(account_stat.reload.statuses_count).to be_zero
end end
@ -46,9 +52,8 @@ describe Mastodon::CLI::Cache do
end end
it 're-calculates account records in the cache' do it 're-calculates account records in the cache' do
expect { cli.invoke(:recount, arguments) }.to output( expect { subject }
a_string_including('OK') .to output_results('OK')
).to_stdout
expect(status_stat.reload.replies_count).to be_zero expect(status_stat.reload.replies_count).to be_zero
end end
@ -58,9 +63,9 @@ describe Mastodon::CLI::Cache do
let(:arguments) { ['other-type'] } let(:arguments) { ['other-type'] }
it 'Exits with an error message' do it 'Exits with an error message' do
expect { cli.invoke(:recount, arguments) }.to output( expect { subject }
a_string_including('Unknown') .to output_results('Unknown')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
end end

View file

@ -4,42 +4,45 @@ require 'rails_helper'
require 'mastodon/cli/canonical_email_blocks' require 'mastodon/cli/canonical_email_blocks'
describe Mastodon::CLI::CanonicalEmailBlocks do describe Mastodon::CLI::CanonicalEmailBlocks do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#find' do describe '#find' do
let(:action) { :find }
let(:arguments) { ['user@example.com'] } let(:arguments) { ['user@example.com'] }
context 'when a block is present' do context 'when a block is present' do
before { Fabricate(:canonical_email_block, email: 'user@example.com') } before { Fabricate(:canonical_email_block, email: 'user@example.com') }
it 'announces the presence of the block' do it 'announces the presence of the block' do
expect { cli.invoke(:find, arguments) }.to output( expect { subject }
a_string_including('user@example.com is blocked') .to output_results('user@example.com is blocked')
).to_stdout
end end
end end
context 'when a block is not present' do context 'when a block is not present' do
it 'announces the absence of the block' do it 'announces the absence of the block' do
expect { cli.invoke(:find, arguments) }.to output( expect { subject }
a_string_including('user@example.com is not blocked') .to output_results('user@example.com is not blocked')
).to_stdout
end end
end end
end end
describe '#remove' do describe '#remove' do
let(:action) { :remove }
let(:arguments) { ['user@example.com'] } let(:arguments) { ['user@example.com'] }
context 'when a block is present' do context 'when a block is present' do
before { Fabricate(:canonical_email_block, email: 'user@example.com') } before { Fabricate(:canonical_email_block, email: 'user@example.com') }
it 'removes the block' do it 'removes the block' do
expect { cli.invoke(:remove, arguments) }.to output( expect { subject }
a_string_including('Unblocked user@example.com') .to output_results('Unblocked user@example.com')
).to_stdout
expect(CanonicalEmailBlock.matching_email('user@example.com')).to be_empty expect(CanonicalEmailBlock.matching_email('user@example.com')).to be_empty
end end
@ -47,9 +50,8 @@ describe Mastodon::CLI::CanonicalEmailBlocks do
context 'when a block is not present' do context 'when a block is not present' do
it 'announces the absence of the block' do it 'announces the absence of the block' do
expect { cli.invoke(:remove, arguments) }.to output( expect { subject }
a_string_including('user@example.com is not blocked') .to output_results('user@example.com is not blocked')
).to_stdout
end end
end end
end end

View file

@ -4,20 +4,26 @@ require 'rails_helper'
require 'mastodon/cli/domains' require 'mastodon/cli/domains'
describe Mastodon::CLI::Domains do describe Mastodon::CLI::Domains do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#purge' do describe '#purge' do
let(:action) { :purge }
context 'with accounts from the domain' do context 'with accounts from the domain' do
let(:options) { {} }
let(:domain) { 'host.example' } let(:domain) { 'host.example' }
let!(:account) { Fabricate(:account, domain: domain) } let!(:account) { Fabricate(:account, domain: domain) }
let(:arguments) { [domain] }
it 'removes the account' do it 'removes the account' do
expect { cli.invoke(:purge, [domain], options) }.to output( expect { subject }
a_string_including('Removed 1 accounts') .to output_results('Removed 1 accounts')
).to_stdout
expect { account.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { account.reload }.to raise_error(ActiveRecord::RecordNotFound)
end end
end end

View file

@ -4,96 +4,99 @@ require 'rails_helper'
require 'mastodon/cli/email_domain_blocks' require 'mastodon/cli/email_domain_blocks'
describe Mastodon::CLI::EmailDomainBlocks do describe Mastodon::CLI::EmailDomainBlocks do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#list' do describe '#list' do
let(:action) { :list }
context 'with email domain block records' do context 'with email domain block records' do
let!(:parent_block) { Fabricate(:email_domain_block) } let!(:parent_block) { Fabricate(:email_domain_block) }
let!(:child_block) { Fabricate(:email_domain_block, parent: parent_block) } let!(:child_block) { Fabricate(:email_domain_block, parent: parent_block) }
let(:options) { {} }
it 'lists the blocks' do it 'lists the blocks' do
expect { cli.invoke(:list, [], options) }.to output( expect { subject }
a_string_including(parent_block.domain) .to output_results(
.and(a_string_including(child_block.domain)) parent_block.domain,
).to_stdout child_block.domain
)
end end
end end
end end
describe '#add' do describe '#add' do
context 'without any options' do let(:action) { :add }
let(:options) { {} }
context 'without any options' do
it 'warns about usage and exits' do it 'warns about usage and exits' do
expect { cli.invoke(:add, [], options) }.to output( expect { subject }
a_string_including('No domain(s) given') .to output_results('No domain(s) given')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
context 'when blocks exist' do context 'when blocks exist' do
let(:options) { {} } let(:options) { {} }
let(:domain) { 'host.example' } let(:domain) { 'host.example' }
let(:arguments) { [domain] }
before { Fabricate(:email_domain_block, domain: domain) } before { Fabricate(:email_domain_block, domain: domain) }
it 'does not add a new block' do it 'does not add a new block' do
expect { cli.invoke(:add, [domain], options) }.to output( expect { subject }
a_string_including('is already blocked') .to output_results('is already blocked')
).to_stdout
.and(not_change(EmailDomainBlock, :count)) .and(not_change(EmailDomainBlock, :count))
end end
end end
context 'when no blocks exist' do context 'when no blocks exist' do
let(:options) { {} }
let(:domain) { 'host.example' } let(:domain) { 'host.example' }
let(:arguments) { [domain] }
it 'adds a new block' do it 'adds a new block' do
expect { cli.invoke(:add, [domain], options) }.to output( expect { subject }
a_string_including('Added 1') .to output_results('Added 1')
).to_stdout
.and(change(EmailDomainBlock, :count).by(1)) .and(change(EmailDomainBlock, :count).by(1))
end end
end end
end end
describe '#remove' do describe '#remove' do
context 'without any options' do let(:action) { :remove }
let(:options) { {} }
context 'without any options' do
it 'warns about usage and exits' do it 'warns about usage and exits' do
expect { cli.invoke(:remove, [], options) }.to output( expect { subject }
a_string_including('No domain(s) given') .to output_results('No domain(s) given')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
context 'when blocks exist' do context 'when blocks exist' do
let(:options) { {} }
let(:domain) { 'host.example' } let(:domain) { 'host.example' }
let(:arguments) { [domain] }
before { Fabricate(:email_domain_block, domain: domain) } before { Fabricate(:email_domain_block, domain: domain) }
it 'removes the block' do it 'removes the block' do
expect { cli.invoke(:remove, [domain], options) }.to output( expect { subject }
a_string_including('Removed 1') .to output_results('Removed 1')
).to_stdout
.and(change(EmailDomainBlock, :count).by(-1)) .and(change(EmailDomainBlock, :count).by(-1))
end end
end end
context 'when no blocks exist' do context 'when no blocks exist' do
let(:options) { {} }
let(:domain) { 'host.example' } let(:domain) { 'host.example' }
let(:arguments) { [domain] }
it 'does not remove a block' do it 'does not remove a block' do
expect { cli.invoke(:remove, [domain], options) }.to output( expect { subject }
a_string_including('is not yet blocked') .to output_results('is not yet blocked')
).to_stdout
.and(not_change(EmailDomainBlock, :count)) .and(not_change(EmailDomainBlock, :count))
end end
end end

View file

@ -4,10 +4,10 @@ require 'rails_helper'
require 'mastodon/cli/emoji' require 'mastodon/cli/emoji'
describe Mastodon::CLI::Emoji do describe Mastodon::CLI::Emoji do
subject { cli.invoke(action, args, options) } subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:args) { [] } let(:arguments) { [] }
let(:options) { {} } let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
@ -29,7 +29,7 @@ describe Mastodon::CLI::Emoji do
context 'with existing custom emoji' do context 'with existing custom emoji' do
let(:import_path) { Rails.root.join('spec', 'fixtures', 'files', 'elite-assets.tar.gz') } let(:import_path) { Rails.root.join('spec', 'fixtures', 'files', 'elite-assets.tar.gz') }
let(:action) { :import } let(:action) { :import }
let(:args) { [import_path] } let(:arguments) { [import_path] }
it 'reports about imported emoji' do it 'reports about imported emoji' do
expect { subject } expect { subject }
@ -51,7 +51,7 @@ describe Mastodon::CLI::Emoji do
after { FileUtils.rm_rf(export_path.dirname) } after { FileUtils.rm_rf(export_path.dirname) }
let(:export_path) { Rails.root.join('tmp', 'cli-tests', 'export.tar.gz') } let(:export_path) { Rails.root.join('tmp', 'cli-tests', 'export.tar.gz') }
let(:args) { [export_path.dirname.to_s] } let(:arguments) { [export_path.dirname.to_s] }
let(:action) { :export } let(:action) { :export }
it 'reports about exported emoji' do it 'reports about exported emoji' do
@ -61,8 +61,4 @@ describe Mastodon::CLI::Emoji do
end end
end end
end end
def output_results(string)
output(a_string_including(string)).to_stdout
end
end end

View file

@ -4,20 +4,25 @@ require 'rails_helper'
require 'mastodon/cli/feeds' require 'mastodon/cli/feeds'
describe Mastodon::CLI::Feeds do describe Mastodon::CLI::Feeds do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#build' do describe '#build' do
let(:action) { :build }
before { Fabricate(:account) } before { Fabricate(:account) }
context 'with --all option' do context 'with --all option' do
let(:options) { { all: true } } let(:options) { { all: true } }
it 'regenerates feeds for all accounts' do it 'regenerates feeds for all accounts' do
expect { cli.invoke(:build, [], options) }.to output( expect { subject }
a_string_including('Regenerated feeds') .to output_results('Regenerated feeds')
).to_stdout
end end
end end
@ -27,9 +32,8 @@ describe Mastodon::CLI::Feeds do
let(:arguments) { ['alice'] } let(:arguments) { ['alice'] }
it 'regenerates feeds for the account' do it 'regenerates feeds for the account' do
expect { cli.invoke(:build, arguments) }.to output( expect { subject }
a_string_including('OK') .to output_results('OK')
).to_stdout
end end
end end
@ -37,22 +41,23 @@ describe Mastodon::CLI::Feeds do
let(:arguments) { ['invalid-username'] } let(:arguments) { ['invalid-username'] }
it 'displays an error and exits' do it 'displays an error and exits' do
expect { cli.invoke(:build, arguments) }.to output( expect { subject }
a_string_including('No such account') .to output_results('No such account')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
end end
describe '#clear' do describe '#clear' do
let(:action) { :clear }
before do before do
allow(redis).to receive(:del).with(key_namespace) allow(redis).to receive(:del).with(key_namespace)
end end
it 'clears the redis `feed:*` namespace' do it 'clears the redis `feed:*` namespace' do
expect { cli.invoke(:clear) }.to output( expect { subject }
a_string_including('OK') .to output_results('OK')
).to_stdout
expect(redis).to have_received(:del).with(key_namespace).once expect(redis).to have_received(:del).with(key_namespace).once
end end

View file

@ -4,11 +4,16 @@ require 'rails_helper'
require 'mastodon/cli/ip_blocks' require 'mastodon/cli/ip_blocks'
describe Mastodon::CLI::IpBlocks do describe Mastodon::CLI::IpBlocks do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#add' do describe '#add' do
let(:action) { :add }
let(:ip_list) do let(:ip_list) do
[ [
'192.0.2.1', '192.0.2.1',
@ -25,10 +30,11 @@ describe Mastodon::CLI::IpBlocks do
] ]
end end
let(:options) { { severity: 'no_access' } } let(:options) { { severity: 'no_access' } }
let(:arguments) { ip_list }
shared_examples 'ip address blocking' do shared_examples 'ip address blocking' do
it 'blocks all specified IP addresses' 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) blocked_ip_addresses = IpBlock.where(ip: ip_list).pluck(:ip)
expected_ip_addresses = ip_list.map { |ip| IPAddr.new(ip) } expected_ip_addresses = ip_list.map { |ip| IPAddr.new(ip) }
@ -37,7 +43,7 @@ describe Mastodon::CLI::IpBlocks do
end end
it 'sets the severity for all blocked IP addresses' do 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]) blocked_ips_severity = IpBlock.where(ip: ip_list).pluck(:severity).all?(options[:severity])
@ -45,9 +51,8 @@ describe Mastodon::CLI::IpBlocks do
end end
it 'displays a success message with a summary' do it 'displays a success message with a summary' do
expect { cli.invoke(:add, ip_list, options) }.to output( expect { subject }
a_string_including("Added #{ip_list.size}, skipped 0, failed 0") .to output_results("Added #{ip_list.size}, skipped 0, failed 0")
).to_stdout
end end
end end
@ -57,19 +62,19 @@ describe Mastodon::CLI::IpBlocks do
context 'when a specified IP address is already blocked' do context 'when a specified IP address is already blocked' do
let!(:blocked_ip) { IpBlock.create(ip: ip_list.last, severity: options[:severity]) } let!(:blocked_ip) { IpBlock.create(ip: ip_list.last, severity: options[:severity]) }
let(:arguments) { ip_list }
it 'skips the already blocked IP address' do it 'skips the already blocked IP address' do
allow(IpBlock).to receive(:new).and_call_original 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) expect(IpBlock).to_not have_received(:new).with(ip: ip_list.last)
end end
it 'displays the correct summary' do it 'displays the correct summary' do
expect { cli.invoke(:add, ip_list, options) }.to output( expect { subject }
a_string_including("#{ip_list.last} is already blocked\nAdded #{ip_list.size - 1}, skipped 1, failed 0") .to output_results("#{ip_list.last} is already blocked\nAdded #{ip_list.size - 1}, skipped 1, failed 0")
).to_stdout
end end
context 'with --force option' do context 'with --force option' do
@ -77,7 +82,7 @@ describe Mastodon::CLI::IpBlocks do
let(:options) { { severity: 'sign_up_requires_approval', force: true } } let(:options) { { severity: 'sign_up_requires_approval', force: true } }
it 'overwrites the existing IP block record' do it 'overwrites the existing IP block record' do
expect { cli.invoke(:add, ip_list, options) } expect { subject }
.to change { blocked_ip.reload.severity } .to change { blocked_ip.reload.severity }
.from('no_access') .from('no_access')
.to('sign_up_requires_approval') .to('sign_up_requires_approval')
@ -89,11 +94,11 @@ describe Mastodon::CLI::IpBlocks do
context 'when a specified IP address is invalid' 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(:ip_list) { ['320.15.175.0', '9.5.105.255', '0.0.0.0'] }
let(:arguments) { ip_list }
it 'displays the correct summary' do it 'displays the correct summary' do
expect { cli.invoke(:add, ip_list, options) }.to output( expect { subject }
a_string_including("#{ip_list.first} is invalid\nAdded #{ip_list.size - 1}, skipped 0, failed 1") .to output_results("#{ip_list.first} is invalid\nAdded #{ip_list.size - 1}, skipped 0, failed 1")
).to_stdout
end end
end end
@ -124,6 +129,7 @@ describe Mastodon::CLI::IpBlocks do
context 'when a specified IP address fails to be blocked' do context 'when a specified IP address fails to be blocked' do
let(:ip_address) { '127.0.0.1' } let(:ip_address) { '127.0.0.1' }
let(:ip_block) { instance_double(IpBlock, ip: ip_address, save: false) } let(:ip_block) { instance_double(IpBlock, ip: ip_address, save: false) }
let(:arguments) { [ip_address] }
before do before do
allow(IpBlock).to receive(:new).and_return(ip_block) allow(IpBlock).to receive(:new).and_return(ip_block)
@ -132,24 +138,25 @@ describe Mastodon::CLI::IpBlocks do
end end
it 'displays an error message' do it 'displays an error message' do
expect { cli.invoke(:add, [ip_address], options) } expect { subject }
.to output( .to output_results("#{ip_address} could not be saved")
a_string_including("#{ip_address} could not be saved")
).to_stdout
end end
end end
context 'when no IP address is provided' do context 'when no IP address is provided' do
let(:arguments) { [] }
it 'exits with an error message' do it 'exits with an error message' do
expect { cli.add }.to output( expect { subject }
a_string_including('No IP(s) given') .to output_results('No IP(s) given')
).to_stdout
.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
end end
describe '#remove' do describe '#remove' do
let(:action) { :remove }
context 'when removing exact matches' do context 'when removing exact matches' do
let(:ip_list) do let(:ip_list) do
[ [
@ -166,21 +173,21 @@ describe Mastodon::CLI::IpBlocks do
'::/128', '::/128',
] ]
end end
let(:arguments) { ip_list }
before do before do
ip_list.each { |ip| IpBlock.create(ip: ip, severity: :no_access) } ip_list.each { |ip| IpBlock.create(ip: ip, severity: :no_access) }
end end
it 'removes exact IP blocks' do it 'removes exact IP blocks' do
cli.invoke(:remove, ip_list) subject
expect(IpBlock.where(ip: ip_list)).to_not exist expect(IpBlock.where(ip: ip_list)).to_not exist
end end
it 'displays success message with a summary' do it 'displays success message with a summary' do
expect { cli.invoke(:remove, ip_list) }.to output( expect { subject }
a_string_including("Removed #{ip_list.size}, skipped 0") .to output_results("Removed #{ip_list.size}, skipped 0")
).to_stdout
end end
end end
@ -192,13 +199,13 @@ describe Mastodon::CLI::IpBlocks do
let(:options) { { force: true } } let(:options) { { force: true } }
it 'removes blocks for IP ranges that cover given IP(s)' do 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 expect(IpBlock.where(id: [first_ip_range_block.id, second_ip_range_block.id])).to_not exist
end end
it 'does not remove other IP ranges' do 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 expect(IpBlock.where(id: third_ip_range_block.id)).to exist
end end
@ -206,47 +213,46 @@ describe Mastodon::CLI::IpBlocks do
context 'when a specified IP address is not blocked' do context 'when a specified IP address is not blocked' do
let(:unblocked_ip) { '192.0.2.1' } let(:unblocked_ip) { '192.0.2.1' }
let(:arguments) { [unblocked_ip] }
it 'skips the IP address' do it 'skips the IP address' do
expect { cli.invoke(:remove, [unblocked_ip]) }.to output( expect { subject }
a_string_including("#{unblocked_ip} is not yet blocked") .to output_results("#{unblocked_ip} is not yet blocked")
).to_stdout
end end
it 'displays the summary correctly' do it 'displays the summary correctly' do
expect { cli.invoke(:remove, [unblocked_ip]) }.to output( expect { subject }
a_string_including('Removed 0, skipped 1') .to output_results('Removed 0, skipped 1')
).to_stdout
end end
end end
context 'when a specified IP address is invalid' do context 'when a specified IP address is invalid' do
let(:invalid_ip) { '320.15.175.0' } let(:invalid_ip) { '320.15.175.0' }
let(:arguments) { [invalid_ip] }
it 'skips the invalid IP address' do it 'skips the invalid IP address' do
expect { cli.invoke(:remove, [invalid_ip]) }.to output( expect { subject }
a_string_including("#{invalid_ip} is invalid") .to output_results("#{invalid_ip} is invalid")
).to_stdout
end end
it 'displays the summary correctly' do it 'displays the summary correctly' do
expect { cli.invoke(:remove, [invalid_ip]) }.to output( expect { subject }
a_string_including('Removed 0, skipped 1') .to output_results('Removed 0, skipped 1')
).to_stdout
end end
end end
context 'when no IP address is provided' do context 'when no IP address is provided' do
it 'exits with an error message' do it 'exits with an error message' do
expect { cli.remove }.to output( expect { subject }
a_string_including('No IP(s) given') .to output_results('No IP(s) given')
).to_stdout
.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
end end
describe '#export' do describe '#export' do
let(:action) { :export }
let(:first_ip_range_block) { IpBlock.create(ip: '192.168.0.0/24', severity: :no_access) } 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(: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) } 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' } } let(:options) { { format: 'plain' } }
it 'exports blocked IPs with "no_access" severity in plain format' do it 'exports blocked IPs with "no_access" severity in plain format' do
expect { cli.invoke(:export, nil, options) }.to output( expect { subject }
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 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}")
).to_stdout
end end
it 'does not export bloked IPs with different severities' do it 'does not export bloked IPs with different severities' do
expect { cli.invoke(:export, nil, options) }.to_not output( expect { subject }
a_string_including("#{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}") .to_not output_results("#{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}")
).to_stdout
end end
end end
@ -271,23 +275,20 @@ describe Mastodon::CLI::IpBlocks do
let(:options) { { format: 'nginx' } } let(:options) { { format: 'nginx' } }
it 'exports blocked IPs with "no_access" severity in plain format' do it 'exports blocked IPs with "no_access" severity in plain format' do
expect { cli.invoke(:export, nil, options) }.to output( expect { subject }
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 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};")
).to_stdout
end end
it 'does not export bloked IPs with different severities' do it 'does not export bloked IPs with different severities' do
expect { cli.invoke(:export, nil, options) }.to_not output( expect { subject }
a_string_including("deny #{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};") .to_not output_results("deny #{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};")
).to_stdout
end end
end end
context 'when --format option is not provided' do context 'when --format option is not provided' do
it 'exports blocked IPs in plain format by default' do it 'exports blocked IPs in plain format by default' do
expect { cli.export }.to output( expect { subject }
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 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}")
).to_stdout
end end
end end
end end

View file

@ -4,13 +4,20 @@ require 'rails_helper'
require 'mastodon/cli/main' require 'mastodon/cli/main'
describe Mastodon::CLI::Main do 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' it_behaves_like 'CLI Command'
describe 'version' do describe '#version' do
let(:action) { :version }
it 'returns the Mastodon version' do it 'returns the Mastodon version' do
expect { described_class.new.invoke(:version) }.to output( expect { subject }
a_string_including(Mastodon::Version.to_s) .to output_results(Mastodon::Version.to_s)
).to_stdout
end end
end end
end end

View file

@ -4,20 +4,26 @@ require 'rails_helper'
require 'mastodon/cli/maintenance' require 'mastodon/cli/maintenance'
describe Mastodon::CLI::Maintenance do describe Mastodon::CLI::Maintenance do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#fix_duplicates' do describe '#fix_duplicates' do
let(:action) { :fix_duplicates }
context 'when the database version is too old' do context 'when the database version is too old' do
before do before do
allow(ActiveRecord::Migrator).to receive(:current_version).and_return(2000_01_01_000000) # Earlier than minimum allow(ActiveRecord::Migrator).to receive(:current_version).and_return(2000_01_01_000000) # Earlier than minimum
end end
it 'Exits with error message' do it 'Exits with error message' do
expect { cli.invoke :fix_duplicates }.to output( expect { subject }
a_string_including('is too old') .to output_results('is too old')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
@ -28,9 +34,9 @@ describe Mastodon::CLI::Maintenance do
end end
it 'Exits with error message' do it 'Exits with error message' do
expect { cli.invoke :fix_duplicates }.to output( expect { subject }
a_string_including('more recent') .to output_results('more recent')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
@ -41,9 +47,9 @@ describe Mastodon::CLI::Maintenance do
end end
it 'Exits with error message' do it 'Exits with error message' do
expect { cli.invoke :fix_duplicates }.to output( expect { subject }
a_string_including('Sidekiq is running') .to output_results('Sidekiq is running')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
end end

View file

@ -4,18 +4,24 @@ require 'rails_helper'
require 'mastodon/cli/media' require 'mastodon/cli/media'
describe Mastodon::CLI::Media do describe Mastodon::CLI::Media do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#remove' do describe '#remove' do
let(:action) { :remove }
context 'with --prune-profiles and --remove-headers' do context 'with --prune-profiles and --remove-headers' do
let(:options) { { prune_profiles: true, remove_headers: true } } let(:options) { { prune_profiles: true, remove_headers: true } }
it 'warns about usage and exits' do it 'warns about usage and exits' do
expect { cli.invoke(:remove, [], options) }.to output( expect { subject }
a_string_including('--prune-profiles and --remove-headers should not be specified simultaneously') .to output_results('--prune-profiles and --remove-headers should not be specified simultaneously')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
@ -23,9 +29,9 @@ describe Mastodon::CLI::Media do
let(:options) { { include_follows: true } } let(:options) { { include_follows: true } }
it 'warns about usage and exits' do it 'warns about usage and exits' do
expect { cli.invoke(:remove, [], options) }.to output( expect { subject }
a_string_including('--include-follows can only be used with --prune-profiles or --remove-headers') .to output_results('--include-follows can only be used with --prune-profiles or --remove-headers')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
@ -38,9 +44,8 @@ describe Mastodon::CLI::Media do
let(:options) { { prune_profiles: true } } let(:options) { { prune_profiles: true } }
it 'removes account avatars' do it 'removes account avatars' do
expect { cli.invoke(:remove, [], options) }.to output( expect { subject }
a_string_including('Visited 1') .to output_results('Visited 1')
).to_stdout
expect(account.reload.avatar).to be_blank expect(account.reload.avatar).to be_blank
end end
@ -50,9 +55,8 @@ describe Mastodon::CLI::Media do
let(:options) { { remove_headers: true } } let(:options) { { remove_headers: true } }
it 'removes account header' do it 'removes account header' do
expect { cli.invoke(:remove, [], options) }.to output( expect { subject }
a_string_including('Visited 1') .to output_results('Visited 1')
).to_stdout
expect(account.reload.header).to be_blank expect(account.reload.header).to be_blank
end end
@ -64,9 +68,8 @@ describe Mastodon::CLI::Media do
context 'without options' do context 'without options' do
it 'removes account avatars' do it 'removes account avatars' do
expect { cli.invoke(:remove) }.to output( expect { subject }
a_string_including('Removed 1') .to output_results('Removed 1')
).to_stdout
expect(media_attachment.reload.file).to be_blank expect(media_attachment.reload.file).to be_blank
expect(media_attachment.reload.thumbnail).to be_blank expect(media_attachment.reload.thumbnail).to be_blank
@ -76,25 +79,24 @@ describe Mastodon::CLI::Media do
end end
describe '#usage' do describe '#usage' do
context 'without options' do let(:action) { :usage }
let(:options) { {} }
context 'without options' do
it 'reports about storage size' do it 'reports about storage size' do
expect { cli.invoke(:usage, [], options) }.to output( expect { subject }
a_string_including('0 Bytes') .to output_results('0 Bytes')
).to_stdout
end end
end end
end end
describe '#refresh' do describe '#refresh' do
context 'without any options' do let(:action) { :refresh }
let(:options) { {} }
context 'without any options' do
it 'warns about usage and exits' do it 'warns about usage and exits' do
expect { cli.invoke(:refresh, [], options) }.to output( expect { subject }
a_string_including('Specify the source') .to output_results('Specify the source')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
@ -108,9 +110,8 @@ describe Mastodon::CLI::Media do
let(:status) { Fabricate(:status) } let(:status) { Fabricate(:status) }
it 'redownloads the attachment file' do it 'redownloads the attachment file' do
expect { cli.invoke(:refresh, [], options) }.to output( expect { subject }
a_string_including('Downloaded 1 media') .to output_results('Downloaded 1 media')
).to_stdout
end end
end end
@ -119,9 +120,9 @@ describe Mastodon::CLI::Media do
let(:options) { { account: 'not-real-user@example.host' } } let(:options) { { account: 'not-real-user@example.host' } }
it 'warns about usage and exits' do it 'warns about usage and exits' do
expect { cli.invoke(:refresh, [], options) }.to output( expect { subject }
a_string_including('No such account') .to output_results('No such account')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
@ -135,9 +136,8 @@ describe Mastodon::CLI::Media do
let(:account) { Fabricate(:account) } let(:account) { Fabricate(:account) }
it 'redownloads the attachment file' do it 'redownloads the attachment file' do
expect { cli.invoke(:refresh, [], options) }.to output( expect { subject }
a_string_including('Downloaded 1 media') .to output_results('Downloaded 1 media')
).to_stdout
end end
end end
end end
@ -153,9 +153,8 @@ describe Mastodon::CLI::Media do
let(:account) { Fabricate(:account, domain: domain) } let(:account) { Fabricate(:account, domain: domain) }
it 'redownloads the attachment file' do it 'redownloads the attachment file' do
expect { cli.invoke(:refresh, [], options) }.to output( expect { subject }
a_string_including('Downloaded 1 media') .to output_results('Downloaded 1 media')
).to_stdout
end end
end end
end end

View file

@ -4,11 +4,17 @@ require 'rails_helper'
require 'mastodon/cli/preview_cards' require 'mastodon/cli/preview_cards'
describe Mastodon::CLI::PreviewCards do describe Mastodon::CLI::PreviewCards do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#remove' do describe '#remove' do
let(:action) { :remove }
context 'with relevant preview cards' do context 'with relevant preview cards' do
before do before do
Fabricate(:preview_card, updated_at: 10.years.ago, type: :link) Fabricate(:preview_card, updated_at: 10.years.ago, type: :link)
@ -18,10 +24,11 @@ describe Mastodon::CLI::PreviewCards do
context 'with no arguments' do context 'with no arguments' do
it 'deletes thumbnails for local preview cards' do it 'deletes thumbnails for local preview cards' do
expect { cli.invoke(:remove) }.to output( expect { subject }
a_string_including('Removed 2 preview cards') .to output_results(
.and(a_string_including('approx. 119 KB')) 'Removed 2 preview cards',
).to_stdout 'approx. 119 KB'
)
end end
end end
@ -29,10 +36,11 @@ describe Mastodon::CLI::PreviewCards do
let(:options) { { link: true } } let(:options) { { link: true } }
it 'deletes thumbnails for local preview cards' do it 'deletes thumbnails for local preview cards' do
expect { cli.invoke(:remove, [], options) }.to output( expect { subject }
a_string_including('Removed 1 link-type preview cards') .to output_results(
.and(a_string_including('approx. 59.6 KB')) 'Removed 1 link-type preview cards',
).to_stdout 'approx. 59.6 KB'
)
end end
end end
@ -40,10 +48,11 @@ describe Mastodon::CLI::PreviewCards do
let(:options) { { days: 365 } } let(:options) { { days: 365 } }
it 'deletes thumbnails for local preview cards' do it 'deletes thumbnails for local preview cards' do
expect { cli.invoke(:remove, [], options) }.to output( expect { subject }
a_string_including('Removed 1 preview cards') .to output_results(
.and(a_string_including('approx. 59.6 KB')) 'Removed 1 preview cards',
).to_stdout 'approx. 59.6 KB'
)
end end
end end
end end

View file

@ -7,59 +7,64 @@ describe Mastodon::CLI::Settings do
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe 'subcommand "registrations"' do describe 'subcommand "registrations"' do
subject { cli.invoke(action, arguments, options) }
let(:cli) { Mastodon::CLI::Registrations.new } let(:cli) { Mastodon::CLI::Registrations.new }
let(:arguments) { [] }
let(:options) { {} }
before do before do
Setting.registrations_mode = nil Setting.registrations_mode = nil
end end
describe '#open' do describe '#open' do
let(:action) { :open }
it 'changes "registrations_mode" to "open"' do 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 end
it 'displays success message' do it 'displays success message' do
expect { cli.open }.to output( expect { subject }
a_string_including('OK') .to output_results('OK')
).to_stdout
end end
end end
describe '#approved' do describe '#approved' do
let(:action) { :approved }
it 'changes "registrations_mode" to "approved"' do 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 end
it 'displays success message' do it 'displays success message' do
expect { cli.approved }.to output( expect { subject }
a_string_including('OK') .to output_results('OK')
).to_stdout
end end
context 'with --require-reason' do context 'with --require-reason' do
before do let(:options) { { require_reason: true } }
cli.options = { require_reason: true }
end
it 'changes "registrations_mode" to "approved"' do 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 end
it 'sets "require_invite_text" to "true"' do 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 end
end end
describe '#close' do describe '#close' do
let(:action) { :close }
it 'changes "registrations_mode" to "none"' do 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 end
it 'displays success message' do it 'displays success message' do
expect { cli.close }.to output( expect { subject }
a_string_including('OK') .to output_results('OK')
).to_stdout
end end
end end
end end

View file

@ -4,26 +4,31 @@ require 'rails_helper'
require 'mastodon/cli/statuses' require 'mastodon/cli/statuses'
describe Mastodon::CLI::Statuses do describe Mastodon::CLI::Statuses do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#remove', use_transactional_tests: false do describe '#remove', use_transactional_tests: false do
let(:action) { :remove }
context 'with small batch size' do context 'with small batch size' do
let(:options) { { batch_size: 0 } } let(:options) { { batch_size: 0 } }
it 'exits with error message' do it 'exits with error message' do
expect { cli.invoke :remove, [], options }.to output( expect { subject }
a_string_including('Cannot run') .to output_results('Cannot run')
).to_stdout.and raise_error(SystemExit) .and raise_error(SystemExit)
end end
end end
context 'with default batch size' do context 'with default batch size' do
it 'removes unreferenced statuses' do it 'removes unreferenced statuses' do
expect { cli.invoke :remove }.to output( expect { subject }
a_string_including('Done after') .to output_results('Done after')
).to_stdout
end end
end end
end end

View file

@ -4,23 +4,26 @@ require 'rails_helper'
require 'mastodon/cli/upgrade' require 'mastodon/cli/upgrade'
describe Mastodon::CLI::Upgrade do describe Mastodon::CLI::Upgrade do
subject { cli.invoke(action, arguments, options) }
let(:cli) { described_class.new } let(:cli) { described_class.new }
let(:arguments) { [] }
let(:options) { {} }
it_behaves_like 'CLI Command' it_behaves_like 'CLI Command'
describe '#storage_schema' do describe '#storage_schema' do
context 'with records that dont need upgrading' do let(:action) { :storage_schema }
let(:options) { {} }
context 'with records that dont need upgrading' do
before do before do
Fabricate(:account) Fabricate(:account)
Fabricate(:media_attachment) Fabricate(:media_attachment)
end end
it 'does not upgrade storage for the attachments' do it 'does not upgrade storage for the attachments' do
expect { cli.invoke(:storage_schema, [], options) }.to output( expect { subject }
a_string_including('Upgraded storage schema of 0 records') .to output_results('Upgraded storage schema of 0 records')
).to_stdout
end end
end end
end end

View file

@ -88,6 +88,7 @@ RSpec.configure do |config|
config.include Chewy::Rspec::Helpers config.include Chewy::Rspec::Helpers
config.include Redisable config.include Redisable
config.include SignedRequestHelpers, type: :request config.include SignedRequestHelpers, type: :request
config.include CommandLineHelpers, type: :cli
config.around(:each, use_transactional_tests: false) do |example| config.around(:each, use_transactional_tests: false) do |example|
self.use_transactional_tests = false self.use_transactional_tests = false

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
module CommandLineHelpers
def output_results(*args)
output(
include(*args)
).to_stdout
end
end