Merge branch 'master' into glitch-soc/merge-upstream
Conflicts: - `.env.production.sample`: Upstream deleted it, I decided to keep it. - `package.json`: Upstream updated a dependency textually too close to wavesurfer.js which has been deleted from upstream but is kept in glitch-soc for now.
This commit is contained in:
5
spec/fabricators/account_note_fabricator.rb
Normal file
5
spec/fabricators/account_note_fabricator.rb
Normal file
@ -0,0 +1,5 @@
|
||||
Fabricator(:account_note) do
|
||||
account
|
||||
target_account { Fabricate(:account) }
|
||||
comment "User note text"
|
||||
end
|
@ -29,201 +29,179 @@ RSpec.describe Remotable do
|
||||
end
|
||||
end
|
||||
|
||||
context 'Remotable module is included' do
|
||||
before do
|
||||
class Foo
|
||||
include Remotable
|
||||
|
||||
remotable_attachment :hoge, 1.kilobyte
|
||||
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' }
|
||||
|
||||
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
|
||||
|
||||
it 'defines a method #download_hoge!' do
|
||||
expect(foo).to respond_to(:download_hoge!)
|
||||
end
|
||||
|
||||
describe '#hoge_remote_url=' do
|
||||
before do
|
||||
class Foo
|
||||
include Remotable
|
||||
remotable_attachment :hoge, 1.kilobyte
|
||||
stub_request(:get, url).to_return(status: code, headers: headers)
|
||||
end
|
||||
|
||||
it 'always returns its argument' do
|
||||
[nil, '', [], {}].each do |arg|
|
||||
expect(foo.hoge_remote_url = arg).to be arg
|
||||
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
|
||||
context 'with an invalid URL' do
|
||||
before do
|
||||
request
|
||||
allow(Addressable::URI).to receive_message_chain(:parse, :normalize).with(url).with(no_args).and_raise(Addressable::URI::InvalidURIError)
|
||||
end
|
||||
|
||||
it 'always returns arg' do
|
||||
[nil, '', [], {}].each do |arg|
|
||||
expect(foo.hoge_remote_url = arg).to be arg
|
||||
it 'makes no request' do
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to_not have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'with scheme that is neither http nor https' do
|
||||
let(:url) { 'ftp://google.com' }
|
||||
|
||||
it 'makes no request' do
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to_not have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'with relative URL' do
|
||||
let(:url) { 'https:///path' }
|
||||
|
||||
it 'makes no request' do
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to_not have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'when URL has not changed' do
|
||||
it 'makes no request if file is already saved' do
|
||||
allow(foo).to receive(:[]).with(attribute_name).and_return(url)
|
||||
allow(foo).to receive(:hoge_file_name).and_return('foo.jpg')
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to_not have_been_made
|
||||
end
|
||||
|
||||
it 'makes request if file is not already saved' do
|
||||
allow(foo).to receive(:[]).with(attribute_name).and_return(url)
|
||||
allow(foo).to receive(:hoge_file_name).and_return(nil)
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'when instance has no attribute for URL' do
|
||||
before do
|
||||
allow(foo).to receive(:has_attribute?).with(attribute_name).and_return(false)
|
||||
end
|
||||
|
||||
it 'does not try to write attribute' do
|
||||
expect(foo).to_not receive('[]=').with(attribute_name, url)
|
||||
foo.hoge_remote_url = url
|
||||
end
|
||||
end
|
||||
|
||||
context 'when instance has an attribute for URL' do
|
||||
before do
|
||||
allow(foo).to receive(:has_attribute?).with(attribute_name).and_return(true)
|
||||
end
|
||||
|
||||
it 'does not try to write attribute' do
|
||||
expect(foo).to receive('[]=').with(attribute_name, url)
|
||||
foo.hoge_remote_url = url
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a valid URL' do
|
||||
it 'makes a request' do
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to have_been_made
|
||||
end
|
||||
|
||||
context 'when the response is not successful' do
|
||||
let(:code) { 500 }
|
||||
|
||||
it 'does not assign file' do
|
||||
expect(foo).not_to receive(:public_send).with("#{hoge}=", any_args)
|
||||
expect(foo).not_to receive(:public_send).with("#{hoge}_file_name=", any_args)
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
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)
|
||||
context 'when the response is successful' do
|
||||
let(:code) { 200 }
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).not_to have_been_requested
|
||||
context 'and contains Content-Disposition header' do
|
||||
let(:file) { 'filename="foo.txt"' }
|
||||
let(:headers) { { 'content-disposition' => file } }
|
||||
|
||||
it 'assigns file' 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(:public_send).with("download_#{hoge}!", url)
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
|
||||
expect(foo).to receive(:public_send).with("#{hoge}=", string_io)
|
||||
expect(foo).to receive(:public_send).with("#{hoge}_file_name=", basename + extname)
|
||||
|
||||
foo.download_hoge!(url)
|
||||
end
|
||||
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(blank?: 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 'parsed_url.host is nil' do
|
||||
it 'makes no request' do
|
||||
parsed_url = Addressable::URI.parse('https:https://example.com/path/file.png')
|
||||
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 if file is saved' do
|
||||
allow(foo).to receive(:[]).with(attribute_name).and_return(url)
|
||||
allow(foo).to receive(:hoge_file_name).and_return('foo.jpg')
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).not_to have_been_requested
|
||||
context 'when an error is raised during the request' do
|
||||
before do
|
||||
stub_request(:get, url).to_raise(error_class)
|
||||
end
|
||||
|
||||
it 'makes request if file is not saved' do
|
||||
allow(foo).to receive(:[]).with(attribute_name).and_return(url)
|
||||
allow(foo).to receive(:hoge_file_name).and_return(nil)
|
||||
error_classes = [
|
||||
HTTP::TimeoutError,
|
||||
HTTP::ConnectionError,
|
||||
OpenSSL::SSL::SSLError,
|
||||
Paperclip::Errors::NotIdentifiedByImageMagickError,
|
||||
Addressable::URI::InvalidURIError,
|
||||
]
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).to have_been_requested
|
||||
end
|
||||
end
|
||||
error_classes.each do |error_class|
|
||||
let(:error_class) { error_class }
|
||||
|
||||
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)
|
||||
it 'calls Rails.logger.debug' do
|
||||
expect(Rails.logger).to receive(:debug).with(/^Error fetching remote #{hoge}: /)
|
||||
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
|
||||
|
@ -6,6 +6,8 @@ describe MoveWorker do
|
||||
let(:local_follower) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
|
||||
let(:source_account) { Fabricate(:account, protocol: :activitypub, domain: 'example.com') }
|
||||
let(:target_account) { Fabricate(:account, protocol: :activitypub, domain: 'example.com') }
|
||||
let(:local_user) { Fabricate(:user) }
|
||||
let!(:account_note) { Fabricate(:account_note, account: local_user.account, target_account: source_account) }
|
||||
|
||||
subject { described_class.new }
|
||||
|
||||
@ -13,6 +15,25 @@ describe MoveWorker do
|
||||
local_follower.follow!(source_account)
|
||||
end
|
||||
|
||||
shared_examples 'user note handling' do
|
||||
it 'copies user note' do
|
||||
allow(UnfollowFollowWorker).to receive(:push_bulk)
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(source_account.acct)
|
||||
expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment)
|
||||
end
|
||||
|
||||
it 'merges user notes when needed' do
|
||||
new_account_note = AccountNote.create!(account: account_note.account, target_account: target_account, comment: 'new note prior to move')
|
||||
|
||||
allow(UnfollowFollowWorker).to receive(:push_bulk)
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(source_account.acct)
|
||||
expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment)
|
||||
expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(new_account_note.comment)
|
||||
end
|
||||
end
|
||||
|
||||
context 'both accounts are distant' do
|
||||
describe 'perform' do
|
||||
it 'calls UnfollowFollowWorker' do
|
||||
@ -20,6 +41,8 @@ describe MoveWorker do
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(UnfollowFollowWorker).to have_received(:push_bulk).with([local_follower.id])
|
||||
end
|
||||
|
||||
include_examples 'user note handling'
|
||||
end
|
||||
end
|
||||
|
||||
@ -32,6 +55,8 @@ describe MoveWorker do
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(UnfollowFollowWorker).to have_received(:push_bulk).with([local_follower.id])
|
||||
end
|
||||
|
||||
include_examples 'user note handling'
|
||||
end
|
||||
end
|
||||
|
||||
@ -45,6 +70,8 @@ describe MoveWorker do
|
||||
expect(local_follower.following?(target_account)).to be true
|
||||
end
|
||||
|
||||
include_examples 'user note handling'
|
||||
|
||||
it 'does not fail when a local user is already following both accounts' do
|
||||
double_follower = Fabricate(:user, email: 'eve@example.com', account: Fabricate(:account, username: 'eve')).account
|
||||
double_follower.follow!(source_account)
|
||||
|
Reference in New Issue
Block a user