Merge branch 'master' into glitch-soc/merge-upstream
Conflicts manually resolved: - app/services/post_status_service.rb - config/locales/simple_form.pl.yml - config/routes.rb - config/webpack/loaders/sass.js - config/webpack/shared.js - package.json - yarn.lock
This commit is contained in:
@@ -19,6 +19,40 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:app) { Fabricate(:application) }
|
||||
let(:token) { Doorkeeper::AccessToken.find_or_create_for(app, nil, 'read write', nil, false) }
|
||||
let(:agreement) { nil }
|
||||
|
||||
before do
|
||||
post :create, params: { username: 'test', password: '12345678', email: 'hello@world.tld', agreement: agreement }
|
||||
end
|
||||
|
||||
context 'given truthy agreement' do
|
||||
let(:agreement) { 'true' }
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns a new access token as JSON' do
|
||||
expect(body_as_json[:access_token]).to_not be_blank
|
||||
end
|
||||
|
||||
it 'creates a user' do
|
||||
user = User.find_by(email: 'hello@world.tld')
|
||||
expect(user).to_not be_nil
|
||||
expect(user.created_by_application_id).to eq app.id
|
||||
end
|
||||
end
|
||||
|
||||
context 'given no agreement' do
|
||||
it 'returns http unprocessable entity' do
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
let(:scopes) { 'read:accounts' }
|
||||
|
||||
|
||||
@@ -3,4 +3,5 @@ Fabricator(:user) do
|
||||
email { sequence(:email) { |i| "#{i}#{Faker::Internet.email}" } }
|
||||
password "123456789"
|
||||
confirmed_at { Time.zone.now }
|
||||
agreement true
|
||||
end
|
||||
|
||||
@@ -14,15 +14,72 @@ RSpec.describe ActivityPub::Activity::Block do
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
context 'when the recipient does not follow the sender' do
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates a block from sender to recipient' do
|
||||
expect(sender.blocking?(recipient)).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the recipient follows the sender' do
|
||||
before do
|
||||
subject.perform
|
||||
recipient.follow!(sender)
|
||||
end
|
||||
|
||||
it 'creates a block from sender to recipient' do
|
||||
expect(sender.blocking?(recipient)).to be true
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates a block from sender to recipient' do
|
||||
expect(sender.blocking?(recipient)).to be true
|
||||
end
|
||||
|
||||
it 'ensures recipient is not following sender' do
|
||||
expect(recipient.following?(sender)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a matching undo has been received first' do
|
||||
let(:undo_json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'bar',
|
||||
type: 'Undo',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: json,
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
before do
|
||||
recipient.follow!(sender)
|
||||
ActivityPub::Activity::Undo.new(undo_json, sender).perform
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'does not create a block from sender to recipient' do
|
||||
expect(sender.blocking?(recipient)).to be false
|
||||
end
|
||||
|
||||
it 'ensures recipient is not following sender' do
|
||||
expect(recipient.following?(sender)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
52
spec/lib/activitypub/activity/move_spec.rb
Normal file
52
spec/lib/activitypub/activity/move_spec.rb
Normal file
@@ -0,0 +1,52 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Move do
|
||||
let(:follower) { Fabricate(:account) }
|
||||
let(:old_account) { Fabricate(:account) }
|
||||
let(:new_account) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
follower.follow!(old_account)
|
||||
|
||||
old_account.update!(uri: 'https://example.org/alice', domain: 'example.org', protocol: :activitypub, inbox_url: 'https://example.org/inbox')
|
||||
new_account.update!(uri: 'https://example.com/alice', domain: 'example.com', protocol: :activitypub, inbox_url: 'https://example.com/inbox', also_known_as: [old_account.uri])
|
||||
|
||||
stub_request(:post, 'https://example.org/inbox').to_return(status: 200)
|
||||
stub_request(:post, 'https://example.com/inbox').to_return(status: 200)
|
||||
|
||||
service_stub = double
|
||||
allow(ActivityPub::FetchRemoteAccountService).to receive(:new).and_return(service_stub)
|
||||
allow(service_stub).to receive(:call).and_return(new_account)
|
||||
end
|
||||
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Move',
|
||||
actor: old_account.uri,
|
||||
object: old_account.uri,
|
||||
target: new_account.uri,
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, old_account) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'sets moved account on old account' do
|
||||
expect(old_account.reload.moved_to_account_id).to eq new_account.id
|
||||
end
|
||||
|
||||
it 'makes followers unfollow old account' do
|
||||
expect(follower.following?(old_account)).to be false
|
||||
end
|
||||
|
||||
it 'makes followers follow-request the new account' do
|
||||
expect(follower.requested?(new_account)).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -126,19 +126,7 @@ RSpec.describe NotificationMailer, type: :mailer do
|
||||
end
|
||||
end
|
||||
|
||||
it 'includes activities since the date specified by :since option' do
|
||||
receiver.update!(last_emailed_at: '2000-02-01T00:00:00Z', current_sign_in_at: '2000-03-01T00:00:00Z')
|
||||
mail = NotificationMailer.digest(receiver.account, since: Time.parse('2000-01-01T00:00:00Z'))
|
||||
expect(mail.body.encoded).to include 'Jan 01, 2000, 00:00'
|
||||
end
|
||||
|
||||
it 'includes activities since the receiver was last emailed if :since option is unavailable' do
|
||||
receiver.update!(last_emailed_at: '2000-02-01T00:00:00Z', current_sign_in_at: '2000-03-01T00:00:00Z')
|
||||
mail = NotificationMailer.digest(receiver.account)
|
||||
expect(mail.body.encoded).to include 'Feb 01, 2000, 00:00'
|
||||
end
|
||||
|
||||
it 'includes activities since the receiver last signed in if :since option and the last emailed date are unavailable' do
|
||||
it 'includes activities since the receiver last signed in' do
|
||||
receiver.update!(last_emailed_at: nil, current_sign_in_at: '2000-03-01T00:00:00Z')
|
||||
mail = NotificationMailer.digest(receiver.account)
|
||||
expect(mail.body.encoded).to include 'Mar 01, 2000, 00:00'
|
||||
|
||||
@@ -106,19 +106,19 @@ RSpec.describe User, type: :model do
|
||||
end
|
||||
|
||||
it 'should allow a non-blacklisted user to be created' do
|
||||
user = User.new(email: 'foo@example.com', account: account, password: password)
|
||||
user = User.new(email: 'foo@example.com', account: account, password: password, agreement: true)
|
||||
|
||||
expect(user.valid?).to be_truthy
|
||||
end
|
||||
|
||||
it 'should not allow a blacklisted user to be created' do
|
||||
user = User.new(email: 'foo@mvrht.com', account: account, password: password)
|
||||
user = User.new(email: 'foo@mvrht.com', account: account, password: password, agreement: true)
|
||||
|
||||
expect(user.valid?).to be_falsey
|
||||
end
|
||||
|
||||
it 'should not allow a subdomain blacklisted user to be created' do
|
||||
user = User.new(email: 'foo@mvrht.com.topdomain.tld', account: account, password: password)
|
||||
user = User.new(email: 'foo@mvrht.com.topdomain.tld', account: account, password: password, agreement: true)
|
||||
|
||||
expect(user.valid?).to be_falsey
|
||||
end
|
||||
@@ -210,17 +210,17 @@ RSpec.describe User, type: :model do
|
||||
end
|
||||
|
||||
it 'should not allow a user to be created unless they are whitelisted' do
|
||||
user = User.new(email: 'foo@example.com', account: account, password: password)
|
||||
user = User.new(email: 'foo@example.com', account: account, password: password, agreement: true)
|
||||
expect(user.valid?).to be_falsey
|
||||
end
|
||||
|
||||
it 'should allow a user to be created if they are whitelisted' do
|
||||
user = User.new(email: 'foo@mastodon.space', account: account, password: password)
|
||||
user = User.new(email: 'foo@mastodon.space', account: account, password: password, agreement: true)
|
||||
expect(user.valid?).to be_truthy
|
||||
end
|
||||
|
||||
it 'should not allow a user with a whitelisted top domain as subdomain in their email address to be created' do
|
||||
user = User.new(email: 'foo@mastodon.space.userdomain.com', account: account, password: password)
|
||||
user = User.new(email: 'foo@mastodon.space.userdomain.com', account: account, password: password, agreement: true)
|
||||
expect(user.valid?).to be_falsey
|
||||
end
|
||||
|
||||
@@ -242,7 +242,7 @@ RSpec.describe User, type: :model do
|
||||
|
||||
it_behaves_like 'Settings-extended' do
|
||||
def create!
|
||||
User.create!(account: Fabricate(:account), email: 'foo@mastodon.space', password: 'abcd1234')
|
||||
User.create!(account: Fabricate(:account), email: 'foo@mastodon.space', password: 'abcd1234', agreement: true)
|
||||
end
|
||||
|
||||
def fabricate
|
||||
|
||||
@@ -26,9 +26,9 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
|
||||
context 'when actor differs from sender' do
|
||||
let(:forwarder) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/other_account') }
|
||||
|
||||
it 'processes payload with sender if no signature exists' do
|
||||
expect_any_instance_of(ActivityPub::LinkedDataSignature).not_to receive(:verify_account!)
|
||||
expect(ActivityPub::Activity).to receive(:factory).with(instance_of(Hash), forwarder, instance_of(Hash))
|
||||
it 'does not process payload if no signature exists' do
|
||||
expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_account!).and_return(nil)
|
||||
expect(ActivityPub::Activity).not_to receive(:factory)
|
||||
|
||||
subject.call(json, forwarder)
|
||||
end
|
||||
|
||||
41
spec/services/app_sign_up_service_spec.rb
Normal file
41
spec/services/app_sign_up_service_spec.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AppSignUpService, type: :service do
|
||||
let(:app) { Fabricate(:application, scopes: 'read write') }
|
||||
let(:good_params) { { username: 'alice', password: '12345678', email: 'good@email.com', agreement: true } }
|
||||
|
||||
subject { described_class.new }
|
||||
|
||||
describe '#call' do
|
||||
it 'returns nil when registrations are closed' do
|
||||
Setting.open_registrations = false
|
||||
expect(subject.call(app, good_params)).to be_nil
|
||||
end
|
||||
|
||||
it 'raises an error when params are missing' do
|
||||
expect { subject.call(app, {}) }.to raise_error ActiveRecord::RecordInvalid
|
||||
end
|
||||
|
||||
it 'creates an unconfirmed user with access token' do
|
||||
access_token = subject.call(app, good_params)
|
||||
expect(access_token).to_not be_nil
|
||||
user = User.find_by(id: access_token.resource_owner_id)
|
||||
expect(user).to_not be_nil
|
||||
expect(user.confirmed?).to be false
|
||||
end
|
||||
|
||||
it 'creates access token with the app\'s scopes' do
|
||||
access_token = subject.call(app, good_params)
|
||||
expect(access_token).to_not be_nil
|
||||
expect(access_token.scopes.to_s).to eq 'read write'
|
||||
end
|
||||
|
||||
it 'creates an account' do
|
||||
access_token = subject.call(app, good_params)
|
||||
expect(access_token).to_not be_nil
|
||||
user = User.find_by(id: access_token.resource_owner_id)
|
||||
expect(user).to_not be_nil
|
||||
expect(user.account).to_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -68,6 +68,13 @@ RSpec.describe PostStatusService, type: :service do
|
||||
expect(status.visibility).to eq "private"
|
||||
end
|
||||
|
||||
it 'creates a status with limited visibility for silenced users' do
|
||||
status = subject.call(Fabricate(:account, silenced: true), 'test', nil, visibility: :public)
|
||||
|
||||
expect(status).to be_persisted
|
||||
expect(status.visibility).to eq "unlisted"
|
||||
end
|
||||
|
||||
it 'creates a status for the given application' do
|
||||
application = Fabricate(:application)
|
||||
|
||||
|
||||
31
spec/validators/blacklisted_email_validator_spec.rb
Normal file
31
spec/validators/blacklisted_email_validator_spec.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe BlacklistedEmailValidator, type: :validator do
|
||||
describe '#validate' do
|
||||
let(:user) { double(email: 'info@mail.com', errors: errors) }
|
||||
let(:errors) { double(add: nil) }
|
||||
|
||||
before do
|
||||
allow_any_instance_of(described_class).to receive(:blocked_email?) { blocked_email }
|
||||
described_class.new.validate(user)
|
||||
end
|
||||
|
||||
context 'blocked_email?' do
|
||||
let(:blocked_email) { true }
|
||||
|
||||
it 'calls errors.add' do
|
||||
expect(errors).to have_received(:add).with(:email, I18n.t('users.invalid_email'))
|
||||
end
|
||||
end
|
||||
|
||||
context '!blocked_email?' do
|
||||
let(:blocked_email) { false }
|
||||
|
||||
it 'not calls errors.add' do
|
||||
expect(errors).not_to have_received(:add).with(:email, I18n.t('users.invalid_email'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
46
spec/validators/disallowed_hashtags_validator_spec.rb
Normal file
46
spec/validators/disallowed_hashtags_validator_spec.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe DisallowedHashtagsValidator, type: :validator do
|
||||
describe '#validate' do
|
||||
before do
|
||||
allow_any_instance_of(described_class).to receive(:select_tags) { tags }
|
||||
described_class.new.validate(status)
|
||||
end
|
||||
|
||||
let(:status) { double(errors: errors, local?: local, reblog?: reblog, text: '') }
|
||||
let(:errors) { double(add: nil) }
|
||||
|
||||
context 'unless status.local? && !status.reblog?' do
|
||||
let(:local) { false }
|
||||
let(:reblog) { true }
|
||||
|
||||
it 'not calls errors.add' do
|
||||
expect(errors).not_to have_received(:add).with(:text, any_args)
|
||||
end
|
||||
end
|
||||
|
||||
context 'status.local? && !status.reblog?' do
|
||||
let(:local) { true }
|
||||
let(:reblog) { false }
|
||||
|
||||
context 'tags.empty?' do
|
||||
let(:tags) { [] }
|
||||
|
||||
it 'not calls errors.add' do
|
||||
expect(errors).not_to have_received(:add).with(:text, any_args)
|
||||
end
|
||||
end
|
||||
|
||||
context '!tags.empty?' do
|
||||
let(:tags) { %w(a b c) }
|
||||
|
||||
it 'calls errors.add' do
|
||||
expect(errors).to have_received(:add)
|
||||
.with(:text, I18n.t('statuses.disallowed_hashtags', tags: tags.join(', '), count: tags.size))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
51
spec/validators/follow_limit_validator_spec.rb
Normal file
51
spec/validators/follow_limit_validator_spec.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe FollowLimitValidator, type: :validator do
|
||||
describe '#validate' do
|
||||
before do
|
||||
allow_any_instance_of(described_class).to receive(:limit_reached?).with(account) do
|
||||
limit_reached
|
||||
end
|
||||
|
||||
described_class.new.validate(follow)
|
||||
end
|
||||
|
||||
let(:follow) { double(account: account, errors: errors) }
|
||||
let(:errors) { double(add: nil) }
|
||||
let(:account) { double(nil?: _nil, local?: local, following_count: 0, followers_count: 0) }
|
||||
let(:_nil) { true }
|
||||
let(:local) { false }
|
||||
|
||||
context 'follow.account.nil? || !follow.account.local?' do
|
||||
let(:_nil) { true }
|
||||
|
||||
it 'not calls errors.add' do
|
||||
expect(errors).not_to have_received(:add).with(:base, any_args)
|
||||
end
|
||||
end
|
||||
|
||||
context '!(follow.account.nil? || !follow.account.local?)' do
|
||||
let(:_nil) { false }
|
||||
let(:local) { true }
|
||||
|
||||
context 'limit_reached?' do
|
||||
let(:limit_reached) { true }
|
||||
|
||||
it 'calls errors.add' do
|
||||
expect(errors).to have_received(:add)
|
||||
.with(:base, I18n.t('users.follow_limit_reached', limit: FollowLimitValidator::LIMIT))
|
||||
end
|
||||
end
|
||||
|
||||
context '!limit_reached?' do
|
||||
let(:limit_reached) { false }
|
||||
|
||||
it 'not calls errors.add' do
|
||||
expect(errors).not_to have_received(:add).with(:base, any_args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,8 +4,17 @@ require 'rails_helper'
|
||||
|
||||
describe StatusLengthValidator do
|
||||
describe '#validate' do
|
||||
it 'does not add errors onto remote statuses'
|
||||
it 'does not add errors onto local reblogs'
|
||||
it 'does not add errors onto remote statuses' do
|
||||
status = double(local?: false)
|
||||
subject.validate(status)
|
||||
expect(status).not_to receive(:errors)
|
||||
end
|
||||
|
||||
it 'does not add errors onto local reblogs' do
|
||||
status = double(local?: false, reblog?: true)
|
||||
subject.validate(status)
|
||||
expect(status).not_to receive(:errors)
|
||||
end
|
||||
|
||||
it 'adds an error when content warning is over MAX_CHARS characters' do
|
||||
chars = StatusLengthValidator::MAX_CHARS + 1
|
||||
|
||||
57
spec/validators/status_pin_validator_spec.rb
Normal file
57
spec/validators/status_pin_validator_spec.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe StatusPinValidator, type: :validator do
|
||||
describe '#validate' do
|
||||
before do
|
||||
subject.validate(pin)
|
||||
end
|
||||
|
||||
let(:pin) { double(account: account, errors: errors, status: status, account_id: pin_account_id) }
|
||||
let(:status) { double(reblog?: reblog, account_id: status_account_id, visibility: visibility) }
|
||||
let(:account) { double(status_pins: status_pins, local?: local) }
|
||||
let(:status_pins) { double(count: count) }
|
||||
let(:errors) { double(add: nil) }
|
||||
let(:pin_account_id) { 1 }
|
||||
let(:status_account_id) { 1 }
|
||||
let(:visibility) { 'public' }
|
||||
let(:local) { false }
|
||||
let(:reblog) { false }
|
||||
let(:count) { 0 }
|
||||
|
||||
context 'pin.status.reblog?' do
|
||||
let(:reblog) { true }
|
||||
|
||||
it 'calls errors.add' do
|
||||
expect(errors).to have_received(:add).with(:base, I18n.t('statuses.pin_errors.reblog'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'pin.account_id != pin.status.account_id' do
|
||||
let(:pin_account_id) { 1 }
|
||||
let(:status_account_id) { 2 }
|
||||
|
||||
it 'calls errors.add' do
|
||||
expect(errors).to have_received(:add).with(:base, I18n.t('statuses.pin_errors.ownership'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'unless %w(public unlisted).include?(pin.status.visibility)' do
|
||||
let(:visibility) { '' }
|
||||
|
||||
it 'calls errors.add' do
|
||||
expect(errors).to have_received(:add).with(:base, I18n.t('statuses.pin_errors.private'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'pin.account.status_pins.count > 4 && pin.account.local?' do
|
||||
let(:count) { 5 }
|
||||
let(:local) { true }
|
||||
|
||||
it 'calls errors.add' do
|
||||
expect(errors).to have_received(:add).with(:base, I18n.t('statuses.pin_errors.limit'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user