diff options
Diffstat (limited to 'spec/models/concerns')
-rw-r--r-- | spec/models/concerns/account_interactions_spec.rb | 592 | ||||
-rw-r--r-- | spec/models/concerns/remotable_spec.rb | 205 | ||||
-rw-r--r-- | spec/models/concerns/streamable_spec.rb | 63 |
3 files changed, 860 insertions, 0 deletions
diff --git a/spec/models/concerns/account_interactions_spec.rb b/spec/models/concerns/account_interactions_spec.rb index 1e238e27c..95bf9561d 100644 --- a/spec/models/concerns/account_interactions_spec.rb +++ b/spec/models/concerns/account_interactions_spec.rb @@ -1,6 +1,561 @@ require 'rails_helper' describe AccountInteractions do + let(:account) { Fabricate(:account, username: 'account') } + let(:account_id) { account.id } + let(:account_ids) { [account_id] } + let(:target_account) { Fabricate(:account, username: 'target') } + let(:target_account_id) { target_account.id } + let(:target_account_ids) { [target_account_id] } + + describe '.following_map' do + subject { Account.following_map(target_account_ids, account_id) } + + context 'account with Follow' do + it 'returns { target_account_id => { reblogs: true } }' do + Fabricate(:follow, account: account, target_account: target_account) + is_expected.to eq(target_account_id => { reblogs: true }) + end + end + + context 'account with Follow but with reblogs disabled' do + it 'returns { target_account_id => { reblogs: false } }' do + Fabricate(:follow, account: account, target_account: target_account, show_reblogs: false) + is_expected.to eq(target_account_id => { reblogs: false }) + end + end + + context 'account without Follow' do + it 'returns {}' do + is_expected.to eq({}) + end + end + end + + describe '.followed_by_map' do + subject { Account.followed_by_map(target_account_ids, account_id) } + + context 'account with Follow' do + it 'returns { target_account_id => true }' do + Fabricate(:follow, account: target_account, target_account: account) + is_expected.to eq(target_account_id => true) + end + end + + context 'account without Follow' do + it 'returns {}' do + is_expected.to eq({}) + end + end + end + + describe '.blocking_map' do + subject { Account.blocking_map(target_account_ids, account_id) } + + context 'account with Block' do + it 'returns { target_account_id => true }' do + Fabricate(:block, account: account, target_account: target_account) + is_expected.to eq(target_account_id => true) + end + end + + context 'account without Block' do + it 'returns {}' do + is_expected.to eq({}) + end + end + end + + describe '.muting_map' do + subject { Account.muting_map(target_account_ids, account_id) } + + context 'account with Mute' do + before do + Fabricate(:mute, target_account: target_account, account: account, hide_notifications: hide) + end + + context 'if Mute#hide_notifications?' do + let(:hide) { true } + + it 'returns { target_account_id => { notifications: true } }' do + is_expected.to eq(target_account_id => { notifications: true }) + end + end + + context 'unless Mute#hide_notifications?' do + let(:hide) { false } + + it 'returns { target_account_id => { notifications: false } }' do + is_expected.to eq(target_account_id => { notifications: false }) + end + end + end + + context 'account without Mute' do + it 'returns {}' do + is_expected.to eq({}) + end + end + end + + describe '#follow!' do + it 'creates and returns Follow' do + expect do + expect(account.follow!(target_account)).to be_kind_of Follow + end.to change { account.following.count }.by 1 + end + end + + describe '#block' do + it 'creates and returns Block' do + expect do + expect(account.block!(target_account)).to be_kind_of Block + end.to change { account.block_relationships.count }.by 1 + end + end + + describe '#mute!' do + context 'Mute does not exist yet' do + context 'arg :notifications is nil' do + let(:arg_notifications) { nil } + + it 'creates Mute, and returns nil' do + expect do + expect(account.mute!(target_account, notifications: arg_notifications)).to be nil + end.to change { account.mute_relationships.count }.by 1 + end + end + + context 'arg :notifications is false' do + let(:arg_notifications) { false } + + it 'creates Mute, and returns nil' do + expect do + expect(account.mute!(target_account, notifications: arg_notifications)).to be nil + end.to change { account.mute_relationships.count }.by 1 + end + end + + context 'arg :notifications is true' do + let(:arg_notifications) { true } + + it 'creates Mute, and returns nil' do + expect do + expect(account.mute!(target_account, notifications: arg_notifications)).to be nil + end.to change { account.mute_relationships.count }.by 1 + end + end + end + + context 'Mute already exists' do + before do + account.mute_relationships << mute + end + + let(:mute) do + Fabricate(:mute, + account: account, + target_account: target_account, + hide_notifications: hide_notifications) + end + + context 'mute.hide_notifications is true' do + let(:hide_notifications) { true } + + context 'arg :notifications is nil' do + let(:arg_notifications) { nil } + + it 'returns nil without updating mute.hide_notifications' do + expect do + expect(account.mute!(target_account, notifications: arg_notifications)).to be nil + mute = account.mute_relationships.find_by(target_account: target_account) + expect(mute.hide_notifications?).to be true + end + end + end + + context 'arg :notifications is false' do + let(:arg_notifications) { false } + + it 'returns true, and updates mute.hide_notifications false' do + expect do + expect(account.mute!(target_account, notifications: arg_notifications)).to be true + mute = account.mute_relationships.find_by(target_account: target_account) + expect(mute.hide_notifications?).to be false + end + end + end + + context 'arg :notifications is true' do + let(:arg_notifications) { true } + + it 'returns nil without updating mute.hide_notifications' do + expect do + expect(account.mute!(target_account, notifications: arg_notifications)).to be nil + mute = account.mute_relationships.find_by(target_account: target_account) + expect(mute.hide_notifications?).to be true + end + end + end + end + + context 'mute.hide_notifications is false' do + let(:hide_notifications) { false } + + context 'arg :notifications is nil' do + let(:arg_notifications) { nil } + + it 'returns true, and updates mute.hide_notifications true' do + expect do + expect(account.mute!(target_account, notifications: arg_notifications)).to be true + mute = account.mute_relationships.find_by(target_account: target_account) + expect(mute.hide_notifications?).to be true + end + end + end + + context 'arg :notifications is false' do + let(:arg_notifications) { false } + + it 'returns nil without updating mute.hide_notifications' do + expect do + expect(account.mute!(target_account, notifications: arg_notifications)).to be nil + mute = account.mute_relationships.find_by(target_account: target_account) + expect(mute.hide_notifications?).to be false + end + end + end + + context 'arg :notifications is true' do + let(:arg_notifications) { true } + + it 'returns true, and updates mute.hide_notifications true' do + expect do + expect(account.mute!(target_account, notifications: arg_notifications)).to be true + mute = account.mute_relationships.find_by(target_account: target_account) + expect(mute.hide_notifications?).to be true + end + end + end + end + end + end + + describe '#mute_conversation!' do + let(:conversation) { Fabricate(:conversation) } + + subject { account.mute_conversation!(conversation) } + + it 'creates and returns ConversationMute' do + expect do + is_expected.to be_kind_of ConversationMute + end.to change { account.conversation_mutes.count }.by 1 + end + end + + describe '#block_domain!' do + let(:domain_block) { Fabricate(:domain_block) } + + subject { account.block_domain!(domain_block) } + + it 'creates and returns AccountDomainBlock' do + expect do + is_expected.to be_kind_of AccountDomainBlock + end.to change { account.domain_blocks.count }.by 1 + end + end + + describe '#unfollow!' do + subject { account.unfollow!(target_account) } + + context 'following target_account' do + it 'returns destroyed Follow' do + account.active_relationships.create(target_account: target_account) + is_expected.to be_kind_of Follow + expect(subject).to be_destroyed + end + end + + context 'not following target_account' do + it 'returns nil' do + is_expected.to be_nil + end + end + end + + describe '#unblock!' do + subject { account.unblock!(target_account) } + + context 'blocking target_account' do + it 'returns destroyed Block' do + account.block_relationships.create(target_account: target_account) + is_expected.to be_kind_of Block + expect(subject).to be_destroyed + end + end + + context 'not blocking target_account' do + it 'returns nil' do + is_expected.to be_nil + end + end + end + + describe '#unmute!' do + subject { account.unmute!(target_account) } + + context 'muting target_account' do + it 'returns destroyed Mute' do + account.mute_relationships.create(target_account: target_account) + is_expected.to be_kind_of Mute + expect(subject).to be_destroyed + end + end + + context 'not muting target_account' do + it 'returns nil' do + is_expected.to be_nil + end + end + end + + describe '#unmute_conversation!' do + let(:conversation) { Fabricate(:conversation) } + + subject { account.unmute_conversation!(conversation) } + + context 'muting the conversation' do + it 'returns destroyed ConversationMute' do + account.conversation_mutes.create(conversation: conversation) + is_expected.to be_kind_of ConversationMute + expect(subject).to be_destroyed + end + end + + context 'not muting the conversation' do + it 'returns nil' do + is_expected.to be nil + end + end + end + + describe '#unblock_domain!' do + let(:domain) { 'example.com' } + + subject { account.unblock_domain!(domain) } + + context 'blocking the domain' do + it 'returns destroyed AccountDomainBlock' do + account_domain_block = Fabricate(:account_domain_block, domain: domain) + account.domain_blocks << account_domain_block + is_expected.to be_kind_of AccountDomainBlock + expect(subject).to be_destroyed + end + end + + context 'unblocking the domain' do + it 'returns nil' do + is_expected.to be_nil + end + end + end + + describe '#following?' do + subject { account.following?(target_account) } + + context 'following target_account' do + it 'returns true' do + account.active_relationships.create(target_account: target_account) + is_expected.to be true + end + end + + context 'not following target_account' do + it 'returns false' do + is_expected.to be false + end + end + end + + describe '#blocking?' do + subject { account.blocking?(target_account) } + + context 'blocking target_account' do + it 'returns true' do + account.block_relationships.create(target_account: target_account) + is_expected.to be true + end + end + + context 'not blocking target_account' do + it 'returns false' do + is_expected.to be false + end + end + end + + describe '#domain_blocking?' do + let(:domain) { 'example.com' } + + subject { account.domain_blocking?(domain) } + + context 'blocking the domain' do + it' returns true' do + account_domain_block = Fabricate(:account_domain_block, domain: domain) + account.domain_blocks << account_domain_block + is_expected.to be true + end + end + + context 'not blocking the domain' do + it 'returns false' do + is_expected.to be false + end + end + end + + describe '#muting?' do + subject { account.muting?(target_account) } + + context 'muting target_account' do + it 'returns true' do + mute = Fabricate(:mute, account: account, target_account: target_account) + account.mute_relationships << mute + is_expected.to be true + end + end + + context 'not muting target_account' do + it 'returns false' do + is_expected.to be false + end + end + end + + describe '#muting_conversation?' do + let(:conversation) { Fabricate(:conversation) } + + subject { account.muting_conversation?(conversation) } + + context 'muting the conversation' do + it 'returns true' do + account.conversation_mutes.create(conversation: conversation) + is_expected.to be true + end + end + + context 'not muting the conversation' do + it 'returns false' do + is_expected.to be false + end + end + end + + describe '#muting_notifications?' do + before do + mute = Fabricate(:mute, target_account: target_account, account: account, hide_notifications: hide) + account.mute_relationships << mute + end + + subject { account.muting_notifications?(target_account) } + + context 'muting notifications of target_account' do + let(:hide) { true } + + it 'returns true' do + is_expected.to be true + end + end + + context 'not muting notifications of target_account' do + let(:hide) { false } + + it 'returns false' do + is_expected.to be false + end + end + end + + describe '#requested?' do + subject { account.requested?(target_account) } + + context 'requested by target_account' do + it 'returns true' do + Fabricate(:follow_request, account: account, target_account: target_account) + is_expected.to be true + end + end + + context 'not requested by target_account' do + it 'returns false' do + is_expected.to be false + end + end + end + + describe '#favourited?' do + let(:status) { Fabricate(:status, account: account, favourites: favourites) } + + subject { account.favourited?(status) } + + context 'favorited' do + let(:favourites) { [Fabricate(:favourite, account: account)] } + + it 'returns true' do + is_expected.to be true + end + end + + context 'not favorited' do + let(:favourites) { [] } + + it 'returns false' do + is_expected.to be false + end + end + end + + describe '#reblogged?' do + let(:status) { Fabricate(:status, account: account, reblogs: reblogs) } + + subject { account.reblogged?(status) } + + context 'reblogged' do + let(:reblogs) { [Fabricate(:status, account: account)] } + + it 'returns true' do + is_expected.to be true + end + end + + context 'not reblogged' do + let(:reblogs) { [] } + + it 'returns false' do + is_expected.to be false + end + end + end + + describe '#pinned?' do + let(:status) { Fabricate(:status, account: account) } + + subject { account.pinned?(status) } + + context 'pinned' do + it 'returns true' do + Fabricate(:status_pin, account: account, status: status) + is_expected.to be true + end + end + + context 'not pinned' do + it 'returns false' do + is_expected.to be false + end + end + end + describe 'muting an account' do let(:me) { Fabricate(:account, username: 'Me') } let(:you) { Fabricate(:account, username: 'You') } @@ -72,4 +627,41 @@ describe AccountInteractions do end end end + + describe 'ignoring reblogs from an account' do + before do + @me = Fabricate(:account, username: 'Me') + @you = Fabricate(:account, username: 'You') + end + + context 'with the reblogs option unspecified' do + before do + @me.follow!(@you) + end + + it 'defaults to showing reblogs' do + expect(@me.muting_reblogs?(@you)).to be(false) + end + end + + context 'with the reblogs option set to false' do + before do + @me.follow!(@you, reblogs: false) + end + + it 'does mute reblogs' do + expect(@me.muting_reblogs?(@you)).to be(true) + end + end + + context 'with the reblogs option set to true' do + before do + @me.follow!(@you, reblogs: true) + end + + it 'does not mute reblogs' do + expect(@me.muting_reblogs?(@you)).to be(false) + end + end + end end diff --git a/spec/models/concerns/remotable_spec.rb b/spec/models/concerns/remotable_spec.rb new file mode 100644 index 000000000..0b2dad23f --- /dev/null +++ b/spec/models/concerns/remotable_spec.rb @@ -0,0 +1,205 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Remotable do + class Foo + def initialize + @attrs = {} + end + + def [](arg) + @attrs[arg] + end + + def []=(arg1, arg2) + @attrs[arg1] = arg2 + end + + def hoge=(arg); end + + def hoge_file_name=(arg); end + + def has_attribute?(arg); end + + def self.attachment_definitions + { hoge: nil } + end + end + + context 'Remotable module is included' do + before do + class Foo; include Remotable; end + end + + let(:attribute_name) { "#{hoge}_remote_url".to_sym } + let(:code) { 200 } + let(:file) { 'filename="foo.txt"' } + let(:foo) { Foo.new } + let(:headers) { { 'content-disposition' => file } } + let(:hoge) { :hoge } + let(:url) { 'https://google.com' } + + let(:request) do + stub_request(:get, url) + .to_return(status: code, headers: headers) + end + + it 'defines a method #hoge_remote_url=' do + expect(foo).to respond_to(:hoge_remote_url=) + end + + it 'defines a method #reset_hoge!' do + expect(foo).to respond_to(:reset_hoge!) + end + + describe '#hoge_remote_url' do + before do + request + end + + it 'always returns arg' do + [nil, '', [], {}].each do |arg| + expect(foo.hoge_remote_url = arg).to be arg + end + end + + context 'Addressable::URI::InvalidURIError raised' do + it 'makes no request' do + allow(Addressable::URI).to receive_message_chain(:parse, :normalize) + .with(url).with(no_args).and_raise(Addressable::URI::InvalidURIError) + + foo.hoge_remote_url = url + expect(request).not_to have_been_requested + end + end + + context 'scheme is neither http nor https' do + let(:url) { 'ftp://google.com' } + + it 'makes no request' do + foo.hoge_remote_url = url + expect(request).not_to have_been_requested + end + end + + context 'parsed_url.host is empty' do + it 'makes no request' do + parsed_url = double(scheme: 'https', host: double(empty?: true)) + allow(Addressable::URI).to receive_message_chain(:parse, :normalize) + .with(url).with(no_args).and_return(parsed_url) + + foo.hoge_remote_url = url + expect(request).not_to have_been_requested + end + end + + context 'foo[attribute_name] == url' do + it 'makes no request' do + allow(foo).to receive(:[]).with(attribute_name).and_return(url) + + foo.hoge_remote_url = url + expect(request).not_to have_been_requested + end + end + + context "scheme is https, parsed_url.host isn't empty, and foo[attribute_name] != url" do + it 'makes a request' do + foo.hoge_remote_url = url + expect(request).to have_been_requested + end + + context 'response.code != 200' do + let(:code) { 500 } + + it 'calls not send' do + expect(foo).not_to receive(:send).with("#{hoge}=", any_args) + expect(foo).not_to receive(:send).with("#{hoge}_file_name=", any_args) + foo.hoge_remote_url = url + end + end + + context 'response.code == 200' do + let(:code) { 200 } + + context 'response contains headers["content-disposition"]' do + let(:file) { 'filename="foo.txt"' } + let(:headers) { { 'content-disposition' => file } } + + it 'calls send' do + string_io = StringIO.new('') + extname = '.txt' + basename = '0123456789abcdef' + + allow(SecureRandom).to receive(:hex).and_return(basename) + allow(StringIO).to receive(:new).with(anything).and_return(string_io) + + expect(foo).to receive(:send).with("#{hoge}=", string_io) + expect(foo).to receive(:send).with("#{hoge}_file_name=", basename + extname) + foo.hoge_remote_url = url + end + end + + context 'if has_attribute?' do + it 'calls foo[attribute_name] = url' do + allow(foo).to receive(:has_attribute?).with(attribute_name).and_return(true) + expect(foo).to receive('[]=').with(attribute_name, url) + foo.hoge_remote_url = url + end + end + + context 'unless has_attribute?' do + it 'calls not foo[attribute_name] = url' do + allow(foo).to receive(:has_attribute?) + .with(attribute_name).and_return(false) + expect(foo).not_to receive('[]=').with(attribute_name, url) + foo.hoge_remote_url = url + end + end + end + + context 'an error raised during the request' do + let(:request) { stub_request(:get, url).to_raise(error_class) } + + error_classes = [ + HTTP::TimeoutError, + HTTP::ConnectionError, + OpenSSL::SSL::SSLError, + Paperclip::Errors::NotIdentifiedByImageMagickError, + Addressable::URI::InvalidURIError, + ] + + error_classes.each do |error_class| + let(:error_class) { error_class } + + it 'calls Rails.logger.debug' do + expect(Rails.logger).to receive(:debug).with(/^Error fetching remote #{hoge}: /) + foo.hoge_remote_url = url + end + end + end + end + end + + describe '#reset_hoge!' do + context 'if url.blank?' do + it 'returns nil, without clearing foo[attribute_name] and calling #hoge_remote_url=' do + url = nil + expect(foo).not_to receive(:send).with(:hoge_remote_url=, url) + foo[attribute_name] = url + expect(foo.reset_hoge!).to be_nil + expect(foo[attribute_name]).to be_nil + end + end + + context 'unless url.blank?' do + it 'clears foo[attribute_name] and calls #hoge_remote_url=' do + foo[attribute_name] = url + expect(foo).to receive(:send).with(:hoge_remote_url=, url) + foo.reset_hoge! + expect(foo[attribute_name]).to be '' + end + end + end + end +end diff --git a/spec/models/concerns/streamable_spec.rb b/spec/models/concerns/streamable_spec.rb new file mode 100644 index 000000000..b5f2d5192 --- /dev/null +++ b/spec/models/concerns/streamable_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Streamable do + class Parent + def title; end + + def target; end + + def thread; end + + def self.has_one(*); end + + def self.after_create; end + end + + class Child < Parent + include Streamable + end + + child = Child.new + + describe '#title' do + it 'calls Parent#title' do + expect_any_instance_of(Parent).to receive(:title) + child.title + end + end + + describe '#content' do + it 'calls #title' do + expect_any_instance_of(Parent).to receive(:title) + child.content + end + end + + describe '#target' do + it 'calls Parent#target' do + expect_any_instance_of(Parent).to receive(:target) + child.target + end + end + + describe '#object_type' do + it 'returns :activity' do + expect(child.object_type).to eq :activity + end + end + + describe '#thread' do + it 'calls Parent#thread' do + expect_any_instance_of(Parent).to receive(:thread) + child.thread + end + end + + describe '#hidden?' do + it 'returns false' do + expect(child.hidden?).to be false + end + end +end |