Merge tag 'v3.1.1' into instance_only_statuses
This commit is contained in:
@ -85,10 +85,7 @@ describe Api::ProofsController do
|
||||
end
|
||||
|
||||
it 'has the correct avatar url' do
|
||||
first_part = 'https://cb6e6126.ngrok.io/system/accounts/avatars/'
|
||||
last_part = 'original/avatar.gif'
|
||||
|
||||
expect(body_as_json[:avatar]).to match /#{Regexp.quote(first_part)}(?:\d{3,5}\/){3}#{Regexp.quote(last_part)}/
|
||||
expect(body_as_json[:avatar]).to match "https://cb6e6126.ngrok.io#{alice.avatar.url}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -59,6 +59,19 @@ describe Api::V1::Accounts::CredentialsController do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with empty source list' do
|
||||
before do
|
||||
patch :update, params: {
|
||||
display_name: "I'm a cat",
|
||||
source: {},
|
||||
}, as: :json
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with invalid data' do
|
||||
before do
|
||||
patch :update, params: { note: 'This is too long. ' * 30 }
|
||||
|
@ -3,19 +3,38 @@ require 'rails_helper'
|
||||
describe Api::V1::Accounts::FollowerAccountsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:alice) { Fabricate(:account) }
|
||||
let(:bob) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
Fabricate(:follow, target_account: user.account)
|
||||
alice.follow!(account)
|
||||
bob.follow!(account)
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
it 'returns http success' do
|
||||
get :index, params: { account_id: user.account.id, limit: 1 }
|
||||
get :index, params: { account_id: account.id, limit: 2 }
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns accounts following the given account' do
|
||||
get :index, params: { account_id: account.id, limit: 2 }
|
||||
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
|
||||
end
|
||||
|
||||
it 'does not return blocked users' do
|
||||
user.account.block!(bob)
|
||||
get :index, params: { account_id: account.id, limit: 2 }
|
||||
|
||||
expect(body_as_json.size).to eq 1
|
||||
expect(body_as_json[0][:id]).to eq alice.id.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -3,19 +3,38 @@ require 'rails_helper'
|
||||
describe Api::V1::Accounts::FollowingAccountsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:alice) { Fabricate(:account) }
|
||||
let(:bob) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
Fabricate(:follow, account: user.account)
|
||||
account.follow!(alice)
|
||||
account.follow!(bob)
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
it 'returns http success' do
|
||||
get :index, params: { account_id: user.account.id, limit: 1 }
|
||||
get :index, params: { account_id: account.id, limit: 2 }
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns accounts followed by the given account' do
|
||||
get :index, params: { account_id: account.id, limit: 2 }
|
||||
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
|
||||
end
|
||||
|
||||
it 'does not return blocked users' do
|
||||
user.account.block!(bob)
|
||||
get :index, params: { account_id: account.id, limit: 2 }
|
||||
|
||||
expect(body_as_json.size).to eq 1
|
||||
expect(body_as_json[0][:id]).to eq alice.id.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::Announcements::ReactionsController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'write:favourites' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
|
||||
let!(:announcement) { Fabricate(:announcement) }
|
||||
|
||||
describe 'PUT #update' do
|
||||
context 'without token' do
|
||||
it 'returns http unauthorized' do
|
||||
put :update, params: { announcement_id: announcement.id, id: '😂' }
|
||||
expect(response).to have_http_status :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
put :update, params: { announcement_id: announcement.id, id: '😂' }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'creates reaction' do
|
||||
expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
before do
|
||||
announcement.announcement_reactions.create!(account: user.account, name: '😂')
|
||||
end
|
||||
|
||||
context 'without token' do
|
||||
it 'returns http unauthorized' do
|
||||
delete :destroy, params: { announcement_id: announcement.id, id: '😂' }
|
||||
expect(response).to have_http_status :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
delete :destroy, params: { announcement_id: announcement.id, id: '😂' }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'creates reaction' do
|
||||
expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
59
spec/controllers/api/v1/announcements_controller_spec.rb
Normal file
59
spec/controllers/api/v1/announcements_controller_spec.rb
Normal file
@ -0,0 +1,59 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::AnnouncementsController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'read' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
|
||||
let!(:announcement) { Fabricate(:announcement) }
|
||||
|
||||
describe 'GET #index' do
|
||||
context 'without token' do
|
||||
it 'returns http unprocessable entity' do
|
||||
get :index
|
||||
expect(response).to have_http_status :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
get :index
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #dismiss' do
|
||||
context 'without token' do
|
||||
it 'returns http unauthorized' do
|
||||
post :dismiss, params: { id: announcement.id }
|
||||
expect(response).to have_http_status :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
let(:scopes) { 'write:accounts' }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
post :dismiss, params: { id: announcement.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'dismisses announcement' do
|
||||
expect(announcement.announcement_mutes.find_by(account: user.account)).to_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
78
spec/controllers/api/v1/bookmarks_controller_spec.rb
Normal file
78
spec/controllers/api/v1/bookmarks_controller_spec.rb
Normal file
@ -0,0 +1,78 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::BookmarksController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:bookmarks') }
|
||||
|
||||
describe 'GET #index' do
|
||||
context 'without token' do
|
||||
it 'returns http unauthorized' do
|
||||
get :index
|
||||
expect(response).to have_http_status :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
context 'without read scope' do
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) do
|
||||
Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: '')
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
get :index
|
||||
expect(response).to have_http_status :forbidden
|
||||
end
|
||||
end
|
||||
|
||||
context 'without valid resource owner' do
|
||||
before do
|
||||
token = Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read')
|
||||
user.destroy!
|
||||
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
get :index
|
||||
expect(response).to have_http_status :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
context 'with read scope and valid resource owner' do
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) do
|
||||
Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read')
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows bookmarks owned by the user' do
|
||||
bookmarked_by_user = Fabricate(:bookmark, account: user.account)
|
||||
bookmarked_by_others = Fabricate(:bookmark)
|
||||
|
||||
get :index
|
||||
|
||||
expect(assigns(:statuses)).to match_array [bookmarked_by_user.status]
|
||||
end
|
||||
|
||||
it 'adds pagination headers if necessary' do
|
||||
bookmark = Fabricate(:bookmark, account: user.account)
|
||||
|
||||
get :index, params: { limit: 1 }
|
||||
|
||||
expect(response.headers['Link'].find_link(['rel', 'next']).href).to eq "http://test.host/api/v1/bookmarks?limit=1&max_id=#{bookmark.id}"
|
||||
expect(response.headers['Link'].find_link(['rel', 'prev']).href).to eq "http://test.host/api/v1/bookmarks?limit=1&min_id=#{bookmark.id}"
|
||||
end
|
||||
|
||||
it 'does not add pagination headers if not necessary' do
|
||||
get :index
|
||||
|
||||
expect(response.headers['Link']).to eq nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Api::V1::Statuses::BookmarksController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:bookmarks', application: app) }
|
||||
|
||||
context 'with an oauth token' do
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
post :create, params: { status_id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
it 'updates the bookmarked attribute' do
|
||||
expect(user.account.bookmarked?(status)).to be true
|
||||
end
|
||||
|
||||
it 'return json with updated attributes' do
|
||||
hash_body = body_as_json
|
||||
|
||||
expect(hash_body[:id]).to eq status.id.to_s
|
||||
expect(hash_body[:bookmarked]).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #destroy' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
Bookmark.find_or_create_by!(account: user.account, status: status)
|
||||
post :destroy, params: { status_id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
it 'updates the bookmarked attribute' do
|
||||
expect(user.account.bookmarked?(status)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -6,6 +6,8 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: 'read:accounts') }
|
||||
let(:alice) { Fabricate(:account) }
|
||||
let(:bob) { Fabricate(:account) }
|
||||
|
||||
context 'with an oauth token' do
|
||||
before do
|
||||
@ -16,14 +18,28 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
Fabricate(:favourite, status: status)
|
||||
Favourite.create!(account: alice, status: status)
|
||||
Favourite.create!(account: bob, status: status)
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get :index, params: { status_id: status.id, limit: 1 }
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers['Link'].links.size).to eq(2)
|
||||
end
|
||||
|
||||
it 'returns accounts who favorited the status' do
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
|
||||
end
|
||||
|
||||
it 'does not return blocked users' do
|
||||
user.account.block!(bob)
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
expect(body_as_json.size).to eq 1
|
||||
expect(body_as_json[0][:id]).to eq alice.id.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -6,6 +6,8 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: 'read:accounts') }
|
||||
let(:alice) { Fabricate(:account) }
|
||||
let(:bob) { Fabricate(:account) }
|
||||
|
||||
context 'with an oauth token' do
|
||||
before do
|
||||
@ -16,14 +18,28 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
Fabricate(:status, reblog_of_id: status.id)
|
||||
Fabricate(:status, account: alice, reblog_of_id: status.id)
|
||||
Fabricate(:status, account: bob, reblog_of_id: status.id)
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get :index, params: { status_id: status.id, limit: 1 }
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers['Link'].links.size).to eq(2)
|
||||
end
|
||||
|
||||
it 'returns accounts who reblogged the status' do
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
|
||||
end
|
||||
|
||||
it 'does not return blocked users' do
|
||||
user.account.block!(bob)
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
expect(body_as_json.size).to eq 1
|
||||
expect(body_as_json[0][:id]).to eq alice.id.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
18
spec/controllers/api/v1/trends_controller_spec.rb
Normal file
18
spec/controllers/api/v1/trends_controller_spec.rb
Normal file
@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::TrendsController, type: :controller do
|
||||
render_views
|
||||
|
||||
describe 'GET #index' do
|
||||
before do
|
||||
allow(TrendingTags).to receive(:get).and_return(Fabricate.times(10, :tag))
|
||||
get :index
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
@ -22,11 +22,6 @@ describe ApplicationController, type: :controller do
|
||||
end
|
||||
|
||||
shared_examples 'respond_with_error' do |code|
|
||||
it "returns http #{code} for any" do
|
||||
subject
|
||||
expect(response).to have_http_status(code)
|
||||
end
|
||||
|
||||
it "returns http #{code} for http" do
|
||||
subject
|
||||
expect(response).to have_http_status(code)
|
||||
|
@ -1,30 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe ApplicationController, type: :controller do
|
||||
controller do
|
||||
include ObfuscateFilename
|
||||
|
||||
obfuscate_filename :file
|
||||
|
||||
def file
|
||||
render plain: params[:file]&.original_filename
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
routes.draw { get 'file' => 'anonymous#file' }
|
||||
end
|
||||
|
||||
it 'obfusticates filename if the given parameter is specified' do
|
||||
file = fixture_file_upload('files/imports.txt', 'text/plain')
|
||||
post 'file', params: { file: file }
|
||||
expect(response.body).to end_with '.txt'
|
||||
expect(response.body).not_to include 'imports'
|
||||
end
|
||||
|
||||
it 'does nothing if the given parameter is not specified' do
|
||||
post 'file'
|
||||
end
|
||||
end
|
@ -97,6 +97,33 @@ describe ApplicationController, type: :controller do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with inaccessible key' do
|
||||
before do
|
||||
get :success
|
||||
|
||||
author = Fabricate(:account, domain: 'localhost:5000', uri: 'http://localhost:5000/actor')
|
||||
fake_request = Request.new(:get, request.url)
|
||||
fake_request.on_behalf_of(author)
|
||||
author.destroy
|
||||
|
||||
request.headers.merge!(fake_request.headers)
|
||||
|
||||
stub_request(:get, 'http://localhost:5000/actor#main-key').to_raise(Mastodon::HostValidationError)
|
||||
end
|
||||
|
||||
describe '#signed_request?' do
|
||||
it 'returns true' do
|
||||
expect(controller.signed_request?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#signed_request_account' do
|
||||
it 'returns nil' do
|
||||
expect(controller.signed_request_account).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with body' do
|
||||
before do
|
||||
post :success, body: 'Hello world'
|
||||
|
@ -22,6 +22,18 @@ describe FollowerAccountsController do
|
||||
expect(assigned[0]).to eq follow1
|
||||
expect(assigned[1]).to eq follow0
|
||||
end
|
||||
|
||||
it 'does not assign blocked users' do
|
||||
user = Fabricate(:user)
|
||||
user.account.block!(follower0)
|
||||
sign_in(user)
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
assigned = assigns(:follows).to_a
|
||||
expect(assigned.size).to eq 1
|
||||
expect(assigned[0]).to eq follow1
|
||||
end
|
||||
end
|
||||
|
||||
context 'when format is json' do
|
||||
|
@ -22,6 +22,18 @@ describe FollowingAccountsController do
|
||||
expect(assigned[0]).to eq follow1
|
||||
expect(assigned[1]).to eq follow0
|
||||
end
|
||||
|
||||
it 'does not assign blocked users' do
|
||||
user = Fabricate(:user)
|
||||
user.account.block!(followee0)
|
||||
sign_in(user)
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
assigned = assigns(:follows).to_a
|
||||
expect(assigned.size).to eq 1
|
||||
expect(assigned[0]).to eq follow1
|
||||
end
|
||||
end
|
||||
|
||||
context 'when format is json' do
|
||||
|
43
spec/controllers/settings/featured_tags_controller_spec.rb
Normal file
43
spec/controllers/settings/featured_tags_controller_spec.rb
Normal file
@ -0,0 +1,43 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Settings::FeaturedTagsController do
|
||||
render_views
|
||||
|
||||
shared_examples 'authenticate user' do
|
||||
it 'redirects to sign_in page' do
|
||||
is_expected.to redirect_to new_user_session_path
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'when user is not sign in' do
|
||||
subject { post :create }
|
||||
|
||||
it_behaves_like 'authenticate user'
|
||||
end
|
||||
|
||||
context 'when user is sign in' do
|
||||
subject { post :create, params: { featured_tag: params } }
|
||||
|
||||
let(:user) { Fabricate(:user, password: '12345678') }
|
||||
|
||||
before { sign_in user, scope: :user }
|
||||
|
||||
context 'when parameter is valid' do
|
||||
let(:params) { { name: 'test' } }
|
||||
|
||||
it 'creates featured tag' do
|
||||
expect { subject }.to change { user.account.featured_tags.count }.by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parameter is invalid' do
|
||||
let(:params) { { name: 'test, #foo !bleh' } }
|
||||
|
||||
it 'renders new' do
|
||||
expect(subject).to render_template :index
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
6
spec/fabricators/announcement_fabricator.rb
Normal file
6
spec/fabricators/announcement_fabricator.rb
Normal file
@ -0,0 +1,6 @@
|
||||
Fabricator(:announcement) do
|
||||
text { Faker::Lorem.paragraph(sentence_count: 2) }
|
||||
published true
|
||||
starts_at nil
|
||||
ends_at nil
|
||||
end
|
4
spec/fabricators/announcement_mute_fabricator.rb
Normal file
4
spec/fabricators/announcement_mute_fabricator.rb
Normal file
@ -0,0 +1,4 @@
|
||||
Fabricator(:announcement_mute) do
|
||||
account
|
||||
announcement
|
||||
end
|
5
spec/fabricators/announcement_reaction_fabricator.rb
Normal file
5
spec/fabricators/announcement_reaction_fabricator.rb
Normal file
@ -0,0 +1,5 @@
|
||||
Fabricator(:announcement_reaction) do
|
||||
account
|
||||
announcement
|
||||
name '🌿'
|
||||
end
|
4
spec/fabricators/bookmark_fabricator.rb
Normal file
4
spec/fabricators/bookmark_fabricator.rb
Normal file
@ -0,0 +1,4 @@
|
||||
Fabricator(:bookmark) do
|
||||
account
|
||||
status
|
||||
end
|
@ -1,16 +1,12 @@
|
||||
Fabricator(:media_attachment) do
|
||||
account
|
||||
|
||||
file do |attrs|
|
||||
[
|
||||
case attrs[:type]
|
||||
when :gifv
|
||||
attachment_fixture ['attachment.gif', 'attachment.webm'].sample
|
||||
when :image
|
||||
attachment_fixture 'attachment.jpg'
|
||||
when nil
|
||||
attachment_fixture ['attachment.gif', 'attachment.jpg', 'attachment.webm'].sample
|
||||
end,
|
||||
nil
|
||||
].sample
|
||||
case attrs[:type]
|
||||
when :gifv, :video
|
||||
attachment_fixture('attachment.webm')
|
||||
else
|
||||
attachment_fixture('attachment.jpg')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,47 +1,51 @@
|
||||
require "rails_helper"
|
||||
# frozen_string_literal: true
|
||||
|
||||
feature "Log in" do
|
||||
given(:email) { "test@examle.com" }
|
||||
require 'rails_helper'
|
||||
|
||||
feature 'Log in' do
|
||||
include ProfileStories
|
||||
|
||||
given(:email) { "test@example.com" }
|
||||
given(:password) { "password" }
|
||||
given(:confirmed_at) { Time.zone.now }
|
||||
|
||||
background do
|
||||
Fabricate(:user, email: email, password: password, confirmed_at: confirmed_at)
|
||||
as_a_registered_user
|
||||
visit new_user_session_path
|
||||
end
|
||||
|
||||
subject { page }
|
||||
|
||||
scenario "A valid email and password user is able to log in" do
|
||||
fill_in "user_email", with: email
|
||||
fill_in "user_password", with: password
|
||||
scenario 'A valid email and password user is able to log in' do
|
||||
fill_in 'user_email', with: email
|
||||
fill_in 'user_password', with: password
|
||||
click_on I18n.t('auth.login')
|
||||
|
||||
is_expected.to have_css("div.app-holder")
|
||||
is_expected.to have_css('div.app-holder')
|
||||
end
|
||||
|
||||
scenario "A invalid email and password user is not able to log in" do
|
||||
fill_in "user_email", with: "invalid_email"
|
||||
fill_in "user_password", with: "invalid_password"
|
||||
scenario 'A invalid email and password user is not able to log in' do
|
||||
fill_in 'user_email', with: 'invalid_email'
|
||||
fill_in 'user_password', with: 'invalid_password'
|
||||
click_on I18n.t('auth.login')
|
||||
|
||||
is_expected.to have_css(".flash-message", text: failure_message("invalid"))
|
||||
is_expected.to have_css('.flash-message', text: failure_message('invalid'))
|
||||
end
|
||||
|
||||
context do
|
||||
given(:confirmed_at) { nil }
|
||||
|
||||
scenario "A unconfirmed user is able to log in" do
|
||||
fill_in "user_email", with: email
|
||||
fill_in "user_password", with: password
|
||||
scenario 'A unconfirmed user is able to log in' do
|
||||
fill_in 'user_email', with: email
|
||||
fill_in 'user_password', with: password
|
||||
click_on I18n.t('auth.login')
|
||||
|
||||
is_expected.to have_css("div.admin-wrapper")
|
||||
is_expected.to have_css('div.admin-wrapper')
|
||||
end
|
||||
end
|
||||
|
||||
def failure_message(message)
|
||||
keys = User.authentication_keys.map { |key| User.human_attribute_name(key) }
|
||||
I18n.t("devise.failure.#{message}", authentication_keys: keys.join("support.array.words_connector"))
|
||||
I18n.t("devise.failure.#{message}", authentication_keys: keys.join('support.array.words_connector'))
|
||||
end
|
||||
end
|
||||
|
53
spec/features/profile_spec.rb
Normal file
53
spec/features/profile_spec.rb
Normal file
@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
feature 'Profile' do
|
||||
include ProfileStories
|
||||
|
||||
given(:local_domain) { ENV['LOCAL_DOMAIN'] }
|
||||
|
||||
background do
|
||||
as_a_logged_in_user
|
||||
with_alice_as_local_user
|
||||
end
|
||||
|
||||
subject { page }
|
||||
|
||||
scenario 'I can view Annes public account' do
|
||||
visit account_path('alice')
|
||||
|
||||
is_expected.to have_title("alice (@alice@#{local_domain})")
|
||||
|
||||
within('.public-account-header h1') do
|
||||
is_expected.to have_content("alice @alice@#{local_domain}")
|
||||
end
|
||||
|
||||
bio_elem = first('.public-account-bio')
|
||||
expect(bio_elem).to have_content(alice_bio)
|
||||
# The bio has hashtags made clickable
|
||||
expect(bio_elem).to have_link('cryptology')
|
||||
expect(bio_elem).to have_link('science')
|
||||
# Nicknames are make clickable
|
||||
expect(bio_elem).to have_link('@alice')
|
||||
expect(bio_elem).to have_link('@bob')
|
||||
# Nicknames not on server are not clickable
|
||||
expect(bio_elem).not_to have_link('@pepe')
|
||||
end
|
||||
|
||||
scenario 'I can change my account' do
|
||||
visit settings_profile_path
|
||||
fill_in 'Display name', with: 'Bob'
|
||||
fill_in 'Bio', with: 'Bob is silent'
|
||||
click_on 'Save changes'
|
||||
is_expected.to have_content 'Changes successfully saved!'
|
||||
|
||||
# View my own public profile and see the changes
|
||||
click_link "Bob @bob@#{local_domain}"
|
||||
|
||||
within('.public-account-header h1') do
|
||||
is_expected.to have_content("Bob @bob@#{local_domain}")
|
||||
end
|
||||
expect(first('.public-account-bio')).to have_content('Bob is silent')
|
||||
end
|
||||
end
|
4
spec/fixtures/xml/mastodon.atom
vendored
4
spec/fixtures/xml/mastodon.atom
vendored
@ -123,7 +123,7 @@
|
||||
<published>2016-10-10T00:41:31Z</published>
|
||||
<updated>2016-10-10T00:41:31Z</updated>
|
||||
<title>Social media needs MOAR cats! http://kickass.zone/media/3</title>
|
||||
<content type="html"><p>Social media needs MOAR cats! <a rel="nofollow noopener" href="http://kickass.zone/media/3">http://kickass.zone/media/3</a></p></content>
|
||||
<content type="html"><p>Social media needs MOAR cats! <a rel="nofollow noopener noreferrer" href="http://kickass.zone/media/3">http://kickass.zone/media/3</a></p></content>
|
||||
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
||||
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/9.atom"/>
|
||||
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/9"/>
|
||||
@ -135,7 +135,7 @@
|
||||
<published>2016-10-10T00:38:39Z</published>
|
||||
<updated>2016-10-10T00:38:39Z</updated>
|
||||
<title>http://kickass.zone/media/2</title>
|
||||
<content type="html"><p><a rel="nofollow noopener" href="http://kickass.zone/media/2">http://kickass.zone/media/2</a></p></content>
|
||||
<content type="html"><p><a rel="nofollow noopener noreferrer" href="http://kickass.zone/media/2">http://kickass.zone/media/2</a></p></content>
|
||||
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
||||
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/8.atom"/>
|
||||
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/8"/>
|
||||
|
67
spec/helpers/accounts_helper_spec.rb
Normal file
67
spec/helpers/accounts_helper_spec.rb
Normal file
@ -0,0 +1,67 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountsHelper, type: :helper do
|
||||
def set_not_embedded_view
|
||||
params[:controller] = "not_#{StatusesHelper::EMBEDDED_CONTROLLER}"
|
||||
params[:action] = "not_#{StatusesHelper::EMBEDDED_ACTION}"
|
||||
end
|
||||
|
||||
def set_embedded_view
|
||||
params[:controller] = StatusesHelper::EMBEDDED_CONTROLLER
|
||||
params[:action] = StatusesHelper::EMBEDDED_ACTION
|
||||
end
|
||||
|
||||
describe '#display_name' do
|
||||
it 'uses the display name when it exists' do
|
||||
account = Account.new(display_name: "Display", username: "Username")
|
||||
|
||||
expect(helper.display_name(account)).to eq "Display"
|
||||
end
|
||||
|
||||
it 'uses the username when display name is nil' do
|
||||
account = Account.new(display_name: nil, username: "Username")
|
||||
|
||||
expect(helper.display_name(account)).to eq "Username"
|
||||
end
|
||||
end
|
||||
|
||||
describe '#acct' do
|
||||
it 'is fully qualified for embedded local accounts' do
|
||||
allow(Rails.configuration.x).to receive(:local_domain).and_return('local_domain')
|
||||
set_embedded_view
|
||||
account = Account.new(domain: nil, username: 'user')
|
||||
|
||||
acct = helper.acct(account)
|
||||
|
||||
expect(acct).to eq '@user@local_domain'
|
||||
end
|
||||
|
||||
it 'is fully qualified for embedded foreign accounts' do
|
||||
set_embedded_view
|
||||
account = Account.new(domain: 'foreign_server.com', username: 'user')
|
||||
|
||||
acct = helper.acct(account)
|
||||
|
||||
expect(acct).to eq '@user@foreign_server.com'
|
||||
end
|
||||
|
||||
it 'is fully qualified for non embedded foreign accounts' do
|
||||
set_not_embedded_view
|
||||
account = Account.new(domain: 'foreign_server.com', username: 'user')
|
||||
|
||||
acct = helper.acct(account)
|
||||
|
||||
expect(acct).to eq '@user@foreign_server.com'
|
||||
end
|
||||
|
||||
it 'is fully qualified for non embedded local accounts' do
|
||||
allow(Rails.configuration.x).to receive(:local_domain).and_return('local_domain')
|
||||
set_not_embedded_view
|
||||
account = Account.new(domain: nil, username: 'user')
|
||||
|
||||
acct = helper.acct(account)
|
||||
|
||||
expect(acct).to eq '@user@local_domain'
|
||||
end
|
||||
end
|
||||
end
|
@ -3,7 +3,7 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::AccountModerationNotesHelper, type: :helper do
|
||||
include StatusesHelper
|
||||
include AccountsHelper
|
||||
|
||||
describe '#admin_account_link_to' do
|
||||
context 'account is nil' do
|
||||
|
@ -1,20 +1,6 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe StatusesHelper, type: :helper do
|
||||
describe '#display_name' do
|
||||
it 'uses the display name when it exists' do
|
||||
account = Account.new(display_name: "Display", username: "Username")
|
||||
|
||||
expect(helper.display_name(account)).to eq "Display"
|
||||
end
|
||||
|
||||
it 'uses the username when display name is nil' do
|
||||
account = Account.new(display_name: nil, username: "Username")
|
||||
|
||||
expect(helper.display_name(account)).to eq "Username"
|
||||
end
|
||||
end
|
||||
|
||||
describe '#stream_link_target' do
|
||||
it 'returns nil if it is not an embedded view' do
|
||||
set_not_embedded_view
|
||||
@ -29,46 +15,6 @@ RSpec.describe StatusesHelper, type: :helper do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#acct' do
|
||||
it 'is fully qualified for embedded local accounts' do
|
||||
allow(Rails.configuration.x).to receive(:local_domain).and_return('local_domain')
|
||||
set_embedded_view
|
||||
account = Account.new(domain: nil, username: 'user')
|
||||
|
||||
acct = helper.acct(account)
|
||||
|
||||
expect(acct).to eq '@user@local_domain'
|
||||
end
|
||||
|
||||
it 'is fully qualified for embedded foreign accounts' do
|
||||
set_embedded_view
|
||||
account = Account.new(domain: 'foreign_server.com', username: 'user')
|
||||
|
||||
acct = helper.acct(account)
|
||||
|
||||
expect(acct).to eq '@user@foreign_server.com'
|
||||
end
|
||||
|
||||
it 'is fully qualified for non embedded foreign accounts' do
|
||||
set_not_embedded_view
|
||||
account = Account.new(domain: 'foreign_server.com', username: 'user')
|
||||
|
||||
acct = helper.acct(account)
|
||||
|
||||
expect(acct).to eq '@user@foreign_server.com'
|
||||
end
|
||||
|
||||
it 'is fully qualified for non embedded local accounts' do
|
||||
allow(Rails.configuration.x).to receive(:local_domain).and_return('local_domain')
|
||||
set_not_embedded_view
|
||||
account = Account.new(domain: nil, username: 'user')
|
||||
|
||||
acct = helper.acct(account)
|
||||
|
||||
expect(acct).to eq '@user@local_domain'
|
||||
end
|
||||
end
|
||||
|
||||
def set_not_embedded_view
|
||||
params[:controller] = "not_#{StatusesHelper::EMBEDDED_CONTROLLER}"
|
||||
params[:action] = "not_#{StatusesHelper::EMBEDDED_ACTION}"
|
||||
|
@ -261,6 +261,32 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context 'with media attachments with long description' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
mediaType: 'image/png',
|
||||
url: 'http://example.com/attachment.png',
|
||||
name: '*' * 1500,
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.media_attachments.map(&:description)).to include('*' * 1500)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media attachments with focal points' do
|
||||
let(:object_json) do
|
||||
{
|
||||
@ -352,6 +378,28 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with hashtags invalid name' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Hashtag',
|
||||
href: 'http://example.com/blah',
|
||||
name: 'foo, #eh !',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with emojis' do
|
||||
let(:object_json) do
|
||||
{
|
||||
|
@ -242,6 +242,30 @@ RSpec.describe Formatter do
|
||||
is_expected.to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a stand-alone xmpp: URI' do
|
||||
let(:text) { 'xmpp:user@instance.com' }
|
||||
|
||||
it 'matches the full URI' do
|
||||
is_expected.to include 'href="xmpp:user@instance.com"'
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a an xmpp: URI with a query-string' do
|
||||
let(:text) { 'please join xmpp:muc@instance.com?join right now' }
|
||||
|
||||
it 'matches the full URI' do
|
||||
is_expected.to include 'href="xmpp:muc@instance.com?join"'
|
||||
end
|
||||
end
|
||||
|
||||
context 'given text containing a magnet: URI' do
|
||||
let(:text) { 'wikipedia gives this example of a magnet uri: magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a' }
|
||||
|
||||
it 'matches the full URI' do
|
||||
is_expected.to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#format_spoiler' do
|
||||
|
@ -24,7 +24,23 @@ describe Sanitize::Config do
|
||||
end
|
||||
|
||||
it 'keep links in lists' do
|
||||
expect(Sanitize.fragment('<p>Check out:</p><ul><li><a href="https://joinmastodon.org" rel="nofollow noopener" target="_blank">joinmastodon.org</a></li><li>Bar</li></ul>', subject)).to eq '<p>Check out:</p><p><a href="https://joinmastodon.org" rel="nofollow noopener" target="_blank">joinmastodon.org</a><br>Bar</p>'
|
||||
expect(Sanitize.fragment('<p>Check out:</p><ul><li><a href="https://joinmastodon.org" rel="nofollow noopener noreferrer" target="_blank">joinmastodon.org</a></li><li>Bar</li></ul>', subject)).to eq '<p>Check out:</p><p><a href="https://joinmastodon.org" rel="nofollow noopener noreferrer" target="_blank">joinmastodon.org</a><br>Bar</p>'
|
||||
end
|
||||
|
||||
it 'removes a without href' do
|
||||
expect(Sanitize.fragment('<a>Test</a>', subject)).to eq 'Test'
|
||||
end
|
||||
|
||||
it 'removes a without href and only keeps text content' do
|
||||
expect(Sanitize.fragment('<a><span class="invisible">foo&</span><span>Test</span></a>', subject)).to eq 'foo&Test'
|
||||
end
|
||||
|
||||
it 'removes a with unsupported scheme in href' do
|
||||
expect(Sanitize.fragment('<a href="foo://bar">Test</a>', subject)).to eq 'Test'
|
||||
end
|
||||
|
||||
it 'keeps a with href' do
|
||||
expect(Sanitize.fragment('<a href="http://example.com">Test</a>', subject)).to eq '<a href="http://example.com" rel="nofollow noopener noreferrer" target="_blank">Test</a>'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -215,13 +215,6 @@ RSpec.describe Account, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#subscription' do
|
||||
it 'returns an OStatus subscription' do
|
||||
account = Fabricate(:account)
|
||||
expect(account.subscription('')).to be_instance_of OStatus2::Subscription
|
||||
end
|
||||
end
|
||||
|
||||
describe '#object_type' do
|
||||
it 'is always a person' do
|
||||
account = Fabricate(:account)
|
||||
@ -626,18 +619,18 @@ RSpec.describe Account, type: :model do
|
||||
end
|
||||
|
||||
context 'when is remote' do
|
||||
it 'is invalid if the username is not unique in case-sensitive comparison among accounts in the same normalized domain' do
|
||||
it 'is invalid if the username is same among accounts in the same normalized domain' do
|
||||
Fabricate(:account, domain: 'にゃん', username: 'username')
|
||||
account = Fabricate.build(:account, domain: 'xn--r9j5b5b', username: 'username')
|
||||
account.valid?
|
||||
expect(account).to model_have_error_on_field(:username)
|
||||
end
|
||||
|
||||
it 'is valid even if the username is unique only in case-sensitive comparison among accounts in the same normalized domain' do
|
||||
it 'is invalid if the username is not unique in case-insensitive comparison among accounts in the same normalized domain' do
|
||||
Fabricate(:account, domain: 'にゃん', username: 'username')
|
||||
account = Fabricate.build(:account, domain: 'xn--r9j5b5b', username: 'Username')
|
||||
account.valid?
|
||||
expect(account).not_to model_have_error_on_field(:username)
|
||||
expect(account).to model_have_error_on_field(:username)
|
||||
end
|
||||
|
||||
it 'is valid even if the username contains hyphens' do
|
||||
@ -823,4 +816,5 @@ RSpec.describe Account, type: :model do
|
||||
end
|
||||
|
||||
include_examples 'AccountAvatar', :account
|
||||
include_examples 'AccountHeader', :account
|
||||
end
|
||||
|
4
spec/models/announcement_mute_spec.rb
Normal file
4
spec/models/announcement_mute_spec.rb
Normal file
@ -0,0 +1,4 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AnnouncementMute, type: :model do
|
||||
end
|
4
spec/models/announcement_reaction_spec.rb
Normal file
4
spec/models/announcement_reaction_spec.rb
Normal file
@ -0,0 +1,4 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AnnouncementReaction, type: :model do
|
||||
end
|
4
spec/models/announcement_spec.rb
Normal file
4
spec/models/announcement_spec.rb
Normal file
@ -0,0 +1,4 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Announcement, type: :model do
|
||||
end
|
@ -52,6 +52,16 @@ RSpec.describe DomainBlock, type: :model do
|
||||
block = Fabricate(:domain_block, domain: 'sub.example.com')
|
||||
expect(DomainBlock.rule_for('sub.example.com')).to eq block
|
||||
end
|
||||
|
||||
it 'returns a rule matching a blocked TLD' do
|
||||
block = Fabricate(:domain_block, domain: 'google')
|
||||
expect(DomainBlock.rule_for('google')).to eq block
|
||||
end
|
||||
|
||||
it 'returns a rule matching a subdomain of a blocked TLD' do
|
||||
block = Fabricate(:domain_block, domain: 'google')
|
||||
expect(DomainBlock.rule_for('maps.google')).to eq block
|
||||
end
|
||||
end
|
||||
|
||||
describe '#stricter_than?' do
|
||||
|
@ -31,14 +31,6 @@ RSpec.describe MediaAttachment, type: :model do
|
||||
context 'file is blank' do
|
||||
let(:file) { nil }
|
||||
|
||||
context 'remote_url is blank' do
|
||||
let(:remote_url) { '' }
|
||||
|
||||
it 'returns false' do
|
||||
is_expected.to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'remote_url is present' do
|
||||
let(:remote_url) { 'remote_url' }
|
||||
|
||||
@ -133,13 +125,36 @@ RSpec.describe MediaAttachment, type: :model do
|
||||
expect(media.file.meta["small"]["height"]).to eq 327
|
||||
expect(media.file.meta["small"]["aspect"]).to eq 490.0 / 327
|
||||
end
|
||||
|
||||
it 'gives the file a random name' do
|
||||
expect(media.file_file_name).to_not eq 'attachment.jpg'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'base64-encoded jpeg' do
|
||||
let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" }
|
||||
let(:media) { MediaAttachment.create(account: Fabricate(:account), file: base64_attachment) }
|
||||
|
||||
it 'saves media attachment' do
|
||||
expect(media.persisted?).to be true
|
||||
expect(media.file).to_not be_nil
|
||||
end
|
||||
|
||||
it 'gives the file a file name' do
|
||||
expect(media.file_file_name).to_not be_blank
|
||||
end
|
||||
end
|
||||
|
||||
it 'is invalid without file' do
|
||||
media = MediaAttachment.new(account: Fabricate(:account))
|
||||
expect(media.valid?).to be false
|
||||
end
|
||||
|
||||
describe 'descriptions for remote attachments' do
|
||||
it 'are cut off at 140 characters' do
|
||||
it 'are cut off at 1500 characters' do
|
||||
media = Fabricate(:media_attachment, description: 'foo' * 1000, remote_url: 'http://example.com/blah.jpg')
|
||||
|
||||
expect(media.description.size).to be <= 420
|
||||
expect(media.description.size).to be <= 1_500
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -34,32 +34,6 @@ RSpec.describe Notification, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#browserable?' do
|
||||
let(:notification) { Fabricate(:notification) }
|
||||
|
||||
subject { notification.browserable? }
|
||||
|
||||
context 'type is :follow_request' do
|
||||
before do
|
||||
allow(notification).to receive(:type).and_return(:follow_request)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
is_expected.to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'type is not :follow_request' do
|
||||
before do
|
||||
allow(notification).to receive(:type).and_return(:else)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
is_expected.to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#type' do
|
||||
it 'returns :reblog for a Status' do
|
||||
notification = Notification.new(activity: Status.new)
|
||||
|
@ -96,16 +96,20 @@ RSpec.describe Status, type: :model do
|
||||
|
||||
context 'unless destroyed?' do
|
||||
context 'if reblog?' do
|
||||
it 'returns "#{account.acct} shared a status by #{reblog.account.acct}"' do
|
||||
it 'returns "#{account.acct} shared #{reblog.account.acct}\'s: #{preview}"' do
|
||||
reblog = subject.reblog = other
|
||||
expect(subject.title).to eq "#{account.acct} shared a status by #{reblog.account.acct}"
|
||||
preview = subject.text.slice(0, 10).split("\n")[0]
|
||||
expect(subject.title).to(
|
||||
eq "#{account.acct} shared #{reblog.account.acct}'s: #{preview}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'unless reblog?' do
|
||||
it 'returns "New status by #{account.acct}"' do
|
||||
it 'returns "#{account.acct}: #{preview}"' do
|
||||
subject.reblog = nil
|
||||
expect(subject.title).to eq "New status by #{account.acct}"
|
||||
preview = subject.text.slice(0, 20).split("\n")[0]
|
||||
expect(subject.title).to eq "#{account.acct}: #{preview}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -322,20 +322,7 @@ RSpec.describe User, type: :model do
|
||||
end
|
||||
|
||||
it 'disables user' do
|
||||
expect(user).to have_attributes(disabled: true, current_sign_in_at: nil, last_sign_in_at: current_sign_in_at)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#disable!' do
|
||||
subject(:user) { Fabricate(:user, disabled: false, current_sign_in_at: current_sign_in_at, last_sign_in_at: nil) }
|
||||
let(:current_sign_in_at) { Time.zone.now }
|
||||
|
||||
before do
|
||||
user.disable!
|
||||
end
|
||||
|
||||
it 'disables user' do
|
||||
expect(user).to have_attributes(disabled: true, current_sign_in_at: nil, last_sign_in_at: current_sign_in_at)
|
||||
expect(user).to have_attributes(disabled: true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,6 +26,7 @@ describe 'Localization' do
|
||||
I18n.t('about.tagline', locale: 'es')
|
||||
)
|
||||
end
|
||||
|
||||
it 'falls back to english when locale is missing' do
|
||||
headers = { 'Accept-Language' => '12-FAKE' }
|
||||
|
||||
|
@ -104,6 +104,26 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Event object' do
|
||||
let(:object) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: "https://#{valid_domain}/@foo/1234",
|
||||
type: 'Event',
|
||||
name: "Let's change the world",
|
||||
attributedTo: ActivityPub::TagManager.instance.uri_for(sender)
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.url).to eq "https://#{valid_domain}/@foo/1234"
|
||||
expect(strip_tags(status.text)).to eq "Let's change the world https://#{valid_domain}/@foo/1234"
|
||||
end
|
||||
end
|
||||
|
||||
context 'with wrong id' do
|
||||
let(:note) do
|
||||
{
|
||||
|
@ -80,7 +80,7 @@ RSpec.describe FetchLinkCardService, type: :service do
|
||||
end
|
||||
|
||||
context 'in a remote status' do
|
||||
let(:status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com'), text: 'Habt ihr ein paar gute Links zu #<span class="tag"><a href="https://quitter.se/tag/wannacry" target="_blank" rel="tag noopener" title="https://quitter.se/tag/wannacry">Wannacry</a></span> herumfliegen? Ich will mal unter <br> <a href="https://github.com/qbi/WannaCry" target="_blank" rel="noopener" title="https://github.com/qbi/WannaCry">https://github.com/qbi/WannaCry</a> was sammeln. !<a href="http://sn.jonkman.ca/group/416/id" target="_blank" rel="noopener" title="http://sn.jonkman.ca/group/416/id">security</a> ') }
|
||||
let(:status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com'), text: 'Habt ihr ein paar gute Links zu <a>foo</a> #<span class="tag"><a href="https://quitter.se/tag/wannacry" target="_blank" rel="tag noopener noreferrer" title="https://quitter.se/tag/wannacry">Wannacry</a></span> herumfliegen? Ich will mal unter <br> <a href="https://github.com/qbi/WannaCry" target="_blank" rel="noopener noreferrer" title="https://github.com/qbi/WannaCry">https://github.com/qbi/WannaCry</a> was sammeln. !<a href="http://sn.jonkman.ca/group/416/id" target="_blank" rel="noopener noreferrer" title="http://sn.jonkman.ca/group/416/id">security</a> ') }
|
||||
|
||||
it 'parses out URLs' do
|
||||
expect(a_request(:get, 'https://github.com/qbi/WannaCry')).to have_been_made.at_least_once
|
||||
|
@ -113,6 +113,24 @@ describe FetchOEmbedService, type: :service do
|
||||
|
||||
end
|
||||
|
||||
context 'when endpoint is cached' do
|
||||
before do
|
||||
stub_request(:get, 'http://www.youtube.com/oembed?format=json&url=https://www.youtube.com/watch?v=dqwpQarrDwk').to_return(
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'text/html' },
|
||||
body: request_fixture('oembed_json_empty.html')
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns new provider without fetching original URL first' do
|
||||
subject.call('https://www.youtube.com/watch?v=dqwpQarrDwk', cached_endpoint: { endpoint: 'http://www.youtube.com/oembed?format=json&url={url}', format: :json })
|
||||
expect(a_request(:get, 'https://www.youtube.com/watch?v=dqwpQarrDwk')).to_not have_been_made
|
||||
expect(subject.endpoint_url).to eq 'http://www.youtube.com/oembed?format=json&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdqwpQarrDwk'
|
||||
expect(subject.format).to eq :json
|
||||
expect(a_request(:get, 'http://www.youtube.com/oembed?format=json&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdqwpQarrDwk')).to have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status code is not 200' do
|
||||
before do
|
||||
stub_request(:get, 'https://host.test/oembed.html').to_return(
|
||||
|
@ -1,50 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe FetchRemoteAccountService, type: :service do
|
||||
let(:url) { 'https://example.com/alice' }
|
||||
let(:prefetched_body) { nil }
|
||||
let(:protocol) { :ostatus }
|
||||
|
||||
subject { FetchRemoteAccountService.new.call(url, prefetched_body, protocol) }
|
||||
|
||||
let(:actor) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'https://example.com/alice',
|
||||
type: 'Person',
|
||||
preferredUsername: 'alice',
|
||||
name: 'Alice',
|
||||
summary: 'Foo bar',
|
||||
inbox: 'http://example.com/alice/inbox',
|
||||
}
|
||||
end
|
||||
|
||||
let(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
|
||||
let(:xml) { File.read(Rails.root.join('spec', 'fixtures', 'xml', 'mastodon.atom')) }
|
||||
|
||||
shared_examples 'return Account' do
|
||||
it { is_expected.to be_an Account }
|
||||
end
|
||||
|
||||
context 'protocol is :activitypub' do
|
||||
let(:prefetched_body) { Oj.dump(actor) }
|
||||
let(:protocol) { :activitypub }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
include_examples 'return Account'
|
||||
end
|
||||
|
||||
context 'when prefetched_body is nil' do
|
||||
context 'protocol is :activitypub' do
|
||||
before do
|
||||
stub_request(:get, url).to_return(status: 200, body: Oj.dump(actor), headers: { 'Content-Type' => 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
include_examples 'return Account'
|
||||
end
|
||||
end
|
||||
end
|
@ -16,9 +16,8 @@ RSpec.describe FetchRemoteStatusService, type: :service do
|
||||
end
|
||||
|
||||
context 'protocol is :activitypub' do
|
||||
subject { described_class.new.call(note[:id], prefetched_body, protocol) }
|
||||
subject { described_class.new.call(note[:id], prefetched_body) }
|
||||
let(:prefetched_body) { Oj.dump(note) }
|
||||
let(:protocol) { :activitypub }
|
||||
|
||||
before do
|
||||
account.update(uri: ActivityPub::TagManager.instance.uri_for(account))
|
||||
@ -59,7 +58,7 @@ RSpec.describe FetchRemoteStatusService, type: :service do
|
||||
</entry>
|
||||
XML
|
||||
|
||||
expect(subject.call('https://fake.domain/foo', status_body, :ostatus)).to be_nil
|
||||
expect(subject.call('https://fake.domain/foo', status_body)).to be_nil
|
||||
end
|
||||
|
||||
it 'does not create status with wrong id when id uses http format' do
|
||||
@ -81,7 +80,7 @@ RSpec.describe FetchRemoteStatusService, type: :service do
|
||||
</entry>
|
||||
XML
|
||||
|
||||
expect(subject.call('https://real.domain/statuses/456', status_body, :ostatus)).to be_nil
|
||||
expect(subject.call('https://real.domain/statuses/456', status_body)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -71,14 +71,14 @@ RSpec.describe FetchResourceService, type: :service do
|
||||
let(:content_type) { 'application/activity+json; charset=utf-8' }
|
||||
let(:body) { json }
|
||||
|
||||
it { is_expected.to eq [1, { prefetched_body: body, id: true }, :activitypub] }
|
||||
it { is_expected.to eq [1, { prefetched_body: body, id: true }] }
|
||||
end
|
||||
|
||||
context 'when content type is ld+json with profile' do
|
||||
let(:content_type) { 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' }
|
||||
let(:body) { json }
|
||||
|
||||
it { is_expected.to eq [1, { prefetched_body: body, id: true }, :activitypub] }
|
||||
it { is_expected.to eq [1, { prefetched_body: body, id: true }] }
|
||||
end
|
||||
|
||||
before do
|
||||
@ -89,14 +89,14 @@ RSpec.describe FetchResourceService, type: :service do
|
||||
context 'when link header is present' do
|
||||
let(:headers) { { 'Link' => '<http://example.com/foo>; rel="alternate"; type="application/activity+json"', } }
|
||||
|
||||
it { is_expected.to eq [1, { prefetched_body: json, id: true }, :activitypub] }
|
||||
it { is_expected.to eq [1, { prefetched_body: json, id: true }] }
|
||||
end
|
||||
|
||||
context 'when content type is text/html' do
|
||||
let(:content_type) { 'text/html' }
|
||||
let(:body) { '<html><head><link rel="alternate" href="http://example.com/foo" type="application/activity+json"/></head></html>' }
|
||||
|
||||
it { is_expected.to eq [1, { prefetched_body: json, id: true }, :activitypub] }
|
||||
it { is_expected.to eq [1, { prefetched_body: json, id: true }] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -212,14 +212,18 @@ RSpec.describe PostStatusService, type: :service do
|
||||
|
||||
it 'does not allow attaching both videos and images' do
|
||||
account = Fabricate(:account)
|
||||
video = Fabricate(:media_attachment, type: :video, account: account)
|
||||
image = Fabricate(:media_attachment, type: :image, account: account)
|
||||
|
||||
video.update(type: :video)
|
||||
|
||||
expect do
|
||||
subject.call(
|
||||
account,
|
||||
text: "test status update",
|
||||
media_ids: [
|
||||
Fabricate(:media_attachment, type: :video, account: account),
|
||||
Fabricate(:media_attachment, type: :image, account: account),
|
||||
video,
|
||||
image,
|
||||
].map(&:id),
|
||||
)
|
||||
end.to raise_error(
|
||||
|
@ -5,11 +5,11 @@ RSpec.describe ProcessMentionsService, type: :service do
|
||||
let(:visibility) { :public }
|
||||
let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}", visibility: visibility) }
|
||||
|
||||
subject { ProcessMentionsService.new }
|
||||
|
||||
context 'OStatus with public toot' do
|
||||
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') }
|
||||
|
||||
subject { ProcessMentionsService.new }
|
||||
|
||||
before do
|
||||
stub_request(:post, remote_user.salmon_url)
|
||||
subject.call(status)
|
||||
@ -24,8 +24,6 @@ RSpec.describe ProcessMentionsService, type: :service do
|
||||
let(:visibility) { :private }
|
||||
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') }
|
||||
|
||||
subject { ProcessMentionsService.new }
|
||||
|
||||
before do
|
||||
stub_request(:post, remote_user.salmon_url)
|
||||
subject.call(status)
|
||||
@ -41,29 +39,45 @@ RSpec.describe ProcessMentionsService, type: :service do
|
||||
end
|
||||
|
||||
context 'ActivityPub' do
|
||||
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
|
||||
context do
|
||||
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
|
||||
|
||||
subject { ProcessMentionsService.new }
|
||||
before do
|
||||
stub_request(:post, remote_user.inbox_url)
|
||||
subject.call(status)
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:post, remote_user.inbox_url)
|
||||
subject.call(status)
|
||||
it 'creates a mention' do
|
||||
expect(remote_user.mentions.where(status: status).count).to eq 1
|
||||
end
|
||||
|
||||
it 'sends activity to the inbox' do
|
||||
expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates a mention' do
|
||||
expect(remote_user.mentions.where(status: status).count).to eq 1
|
||||
end
|
||||
context 'with an IDN domain' do
|
||||
let(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') }
|
||||
let(:status) { Fabricate(:status, account: account, text: "Hello @sneak@hæresiar.ch") }
|
||||
|
||||
it 'sends activity to the inbox' do
|
||||
expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once
|
||||
before do
|
||||
stub_request(:post, remote_user.inbox_url)
|
||||
subject.call(status)
|
||||
end
|
||||
|
||||
it 'creates a mention' do
|
||||
expect(remote_user.mentions.where(status: status).count).to eq 1
|
||||
end
|
||||
|
||||
it 'sends activity to the inbox' do
|
||||
expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'Temporarily-unreachable ActivityPub user' do
|
||||
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) }
|
||||
|
||||
subject { ProcessMentionsService.new }
|
||||
|
||||
before do
|
||||
stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
|
||||
stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com").to_return(status: 500)
|
||||
|
@ -28,12 +28,12 @@ RSpec.describe VerifyLinkService, type: :service do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a link contains an <a rel="noopener"> back' do
|
||||
context 'when a link contains an <a rel="noopener noreferrer"> back' do
|
||||
let(:html) do
|
||||
<<-HTML
|
||||
<!doctype html>
|
||||
<body>
|
||||
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="noopener me" target="_blank">Follow me on Mastodon</a>
|
||||
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="me noopener noreferrer" target="_blank">Follow me on Mastodon</a>
|
||||
</body>
|
||||
HTML
|
||||
end
|
||||
|
@ -12,6 +12,7 @@ end
|
||||
gc_counter = -1
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.example_status_persistence_file_path = "tmp/rspec/examples.txt"
|
||||
config.expect_with :rspec do |expectations|
|
||||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||
end
|
||||
|
@ -16,4 +16,24 @@ shared_examples 'AccountAvatar' do |fabricator|
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'base64-encoded files' do
|
||||
let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" }
|
||||
let(:account) { Fabricate(fabricator, avatar: base64_attachment) }
|
||||
|
||||
it 'saves avatar' do
|
||||
expect(account.persisted?).to be true
|
||||
expect(account.avatar).to_not be_nil
|
||||
end
|
||||
|
||||
it 'gives the avatar a file name' do
|
||||
expect(account.avatar_file_name).to_not be_blank
|
||||
end
|
||||
|
||||
it 'saves a new avatar under a different file name' do
|
||||
previous_file_name = account.avatar_file_name
|
||||
account.update(avatar: base64_attachment)
|
||||
expect(account.avatar_file_name).to_not eq previous_file_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
23
spec/support/examples/models/concerns/account_header.rb
Normal file
23
spec/support/examples/models/concerns/account_header.rb
Normal file
@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
shared_examples 'AccountHeader' do |fabricator|
|
||||
describe 'base64-encoded files' do
|
||||
let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" }
|
||||
let(:account) { Fabricate(fabricator, header: base64_attachment) }
|
||||
|
||||
it 'saves header' do
|
||||
expect(account.persisted?).to be true
|
||||
expect(account.header).to_not be_nil
|
||||
end
|
||||
|
||||
it 'gives the header a file name' do
|
||||
expect(account.header_file_name).to_not be_blank
|
||||
end
|
||||
|
||||
it 'saves a new header under a different file name' do
|
||||
previous_file_name = account.header_file_name
|
||||
account.update(header: base64_attachment)
|
||||
expect(account.header_file_name).to_not eq previous_file_name
|
||||
end
|
||||
end
|
||||
end
|
45
spec/support/stories/profile_stories.rb
Normal file
45
spec/support/stories/profile_stories.rb
Normal file
@ -0,0 +1,45 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ProfileStories
|
||||
attr_reader :bob, :alice, :alice_bio
|
||||
|
||||
def as_a_registered_user
|
||||
@bob = Fabricate(
|
||||
:user,
|
||||
email: email, password: password, confirmed_at: confirmed_at,
|
||||
account: Fabricate(:account, username: 'bob')
|
||||
)
|
||||
end
|
||||
|
||||
def as_a_logged_in_user
|
||||
as_a_registered_user
|
||||
visit new_user_session_path
|
||||
fill_in 'user_email', with: email
|
||||
fill_in 'user_password', with: password
|
||||
click_on I18n.t('auth.login')
|
||||
end
|
||||
|
||||
def with_alice_as_local_user
|
||||
@alice_bio = '@alice and @bob are fictional characters commonly used as'\
|
||||
'placeholder names in #cryptology, as well as #science and'\
|
||||
'engineering 📖 literature. Not affilated with @pepe.'
|
||||
|
||||
@alice = Fabricate(
|
||||
:user,
|
||||
email: 'alice@example.com', password: password, confirmed_at: confirmed_at,
|
||||
account: Fabricate(:account, username: 'alice', note: @alice_bio)
|
||||
)
|
||||
end
|
||||
|
||||
def confirmed_at
|
||||
@confirmed_at ||= Time.zone.now
|
||||
end
|
||||
|
||||
def email
|
||||
@email ||= 'test@example.com'
|
||||
end
|
||||
|
||||
def password
|
||||
@password ||= 'password'
|
||||
end
|
||||
end
|
42
spec/validators/reaction_validator_spec.rb
Normal file
42
spec/validators/reaction_validator_spec.rb
Normal file
@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe ReactionValidator do
|
||||
let(:announcement) { Fabricate(:announcement) }
|
||||
|
||||
describe '#validate' do
|
||||
it 'adds error when not a valid unicode emoji' do
|
||||
reaction = announcement.announcement_reactions.build(name: 'F')
|
||||
subject.validate(reaction)
|
||||
expect(reaction.errors).to_not be_empty
|
||||
end
|
||||
|
||||
it 'does not add error when non-unicode emoji is a custom emoji' do
|
||||
custom_emoji = Fabricate(:custom_emoji)
|
||||
reaction = announcement.announcement_reactions.build(name: custom_emoji.shortcode, custom_emoji_id: custom_emoji.id)
|
||||
subject.validate(reaction)
|
||||
expect(reaction.errors).to be_empty
|
||||
end
|
||||
|
||||
it 'adds error when 8 reactions already exist' do
|
||||
%w(🐘 ❤️ 🙉 😍 😋 😂 😞 👍).each do |name|
|
||||
announcement.announcement_reactions.create!(name: name, account: Fabricate(:account))
|
||||
end
|
||||
|
||||
reaction = announcement.announcement_reactions.build(name: '😘')
|
||||
subject.validate(reaction)
|
||||
expect(reaction.errors).to_not be_empty
|
||||
end
|
||||
|
||||
it 'does not add error when new reaction is part of the existing ones' do
|
||||
%w(🐘 ❤️ 🙉 😍 😋 😂 😞 👍).each do |name|
|
||||
announcement.announcement_reactions.create!(name: name, account: Fabricate(:account))
|
||||
end
|
||||
|
||||
reaction = announcement.announcement_reactions.build(name: '😋')
|
||||
subject.validate(reaction)
|
||||
expect(reaction.errors).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
@ -4,22 +4,65 @@ require 'rails_helper'
|
||||
|
||||
describe UniqueUsernameValidator do
|
||||
describe '#validate' do
|
||||
context 'when local account' do
|
||||
it 'does not add errors if username is nil' do
|
||||
account = double(username: nil, domain: nil, persisted?: false, errors: double(add: nil))
|
||||
subject.validate(account)
|
||||
expect(account.errors).to_not have_received(:add)
|
||||
end
|
||||
|
||||
it 'does not add errors when existing one is subject itself' do
|
||||
account = Fabricate(:account, username: 'abcdef')
|
||||
expect(account).to be_valid
|
||||
end
|
||||
|
||||
it 'adds an error when the username is already used with ignoring cases' do
|
||||
Fabricate(:account, username: 'ABCdef')
|
||||
account = double(username: 'abcDEF', domain: nil, persisted?: false, errors: double(add: nil))
|
||||
subject.validate(account)
|
||||
expect(account.errors).to have_received(:add)
|
||||
end
|
||||
|
||||
it 'does not add errors when same username remote account exists' do
|
||||
Fabricate(:account, username: 'abcdef', domain: 'example.com')
|
||||
account = double(username: 'abcdef', domain: nil, persisted?: false, errors: double(add: nil))
|
||||
subject.validate(account)
|
||||
expect(account.errors).to_not have_received(:add)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when remote account' do
|
||||
it 'does not add errors if username is nil' do
|
||||
account = double(username: nil, persisted?: false, errors: double(add: nil))
|
||||
account = double(username: nil, domain: 'example.com', persisted?: false, errors: double(add: nil))
|
||||
subject.validate(account)
|
||||
expect(account.errors).to_not have_received(:add)
|
||||
end
|
||||
|
||||
it 'does not add errors when existing one is subject itself' do
|
||||
account = Fabricate(:account, username: 'abcdef')
|
||||
account = Fabricate(:account, username: 'abcdef', domain: 'example.com')
|
||||
expect(account).to be_valid
|
||||
end
|
||||
|
||||
it 'adds an error when the username is already used with ignoring cases' do
|
||||
Fabricate(:account, username: 'ABCdef')
|
||||
account = double(username: 'abcDEF', persisted?: false, errors: double(add: nil))
|
||||
Fabricate(:account, username: 'ABCdef', domain: 'example.com')
|
||||
account = double(username: 'abcDEF', domain: 'example.com', persisted?: false, errors: double(add: nil))
|
||||
subject.validate(account)
|
||||
expect(account.errors).to have_received(:add)
|
||||
end
|
||||
|
||||
it 'adds an error when the domain is already used with ignoring cases' do
|
||||
Fabricate(:account, username: 'ABCdef', domain: 'example.com')
|
||||
account = double(username: 'ABCdef', domain: 'EXAMPLE.COM', persisted?: false, errors: double(add: nil))
|
||||
subject.validate(account)
|
||||
expect(account.errors).to have_received(:add)
|
||||
end
|
||||
|
||||
it 'does not add errors when account with the same username and another domain exists' do
|
||||
Fabricate(:account, username: 'abcdef', domain: 'example.com')
|
||||
account = double(username: 'abcdef', domain: 'example2.com', persisted?: false, errors: double(add: nil))
|
||||
subject.validate(account)
|
||||
expect(account.errors).to_not have_received(:add)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
63
spec/workers/move_worker_spec.rb
Normal file
63
spec/workers/move_worker_spec.rb
Normal file
@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
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') }
|
||||
|
||||
subject { described_class.new }
|
||||
|
||||
before do
|
||||
local_follower.follow!(source_account)
|
||||
end
|
||||
|
||||
context 'both accounts are distant' do
|
||||
describe 'perform' do
|
||||
it 'calls UnfollowFollowWorker' do
|
||||
allow(UnfollowFollowWorker).to receive(:push_bulk)
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(UnfollowFollowWorker).to have_received(:push_bulk).with([local_follower.id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'target account is local' do
|
||||
let(:target_account) { Fabricate(:user, email: 'alice@example.com', account: Fabricate(:account, username: 'alice')).account }
|
||||
|
||||
describe 'perform' do
|
||||
it 'calls UnfollowFollowWorker' do
|
||||
allow(UnfollowFollowWorker).to receive(:push_bulk)
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(UnfollowFollowWorker).to have_received(:push_bulk).with([local_follower.id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'both target and source accounts are local' do
|
||||
let(:target_account) { Fabricate(:user, email: 'alice@example.com', account: Fabricate(:account, username: 'alice')).account }
|
||||
let(:source_account) { Fabricate(:user, email: 'alice_@example.com', account: Fabricate(:account, username: 'alice_')).account }
|
||||
|
||||
describe 'perform' do
|
||||
it 'calls makes local followers follow the target account' do
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(local_follower.following?(target_account)).to be true
|
||||
end
|
||||
|
||||
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)
|
||||
double_follower.follow!(target_account)
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(local_follower.following?(target_account)).to be true
|
||||
end
|
||||
|
||||
it 'does not allow the moved account to follow themselves' do
|
||||
source_account.follow!(target_account)
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(target_account.following?(target_account)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
30
spec/workers/refollow_worker_spec.rb
Normal file
30
spec/workers/refollow_worker_spec.rb
Normal file
@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe RefollowWorker do
|
||||
subject { described_class.new }
|
||||
let(:account) { Fabricate(:account, domain: 'example.org', protocol: :activitypub) }
|
||||
let(:alice) { Fabricate(:account, domain: nil, username: 'alice') }
|
||||
let(:bob) { Fabricate(:account, domain: nil, username: 'bob') }
|
||||
|
||||
describe 'perform' do
|
||||
let(:service) { double }
|
||||
|
||||
before do
|
||||
allow(FollowService).to receive(:new).and_return(service)
|
||||
allow(service).to receive(:call)
|
||||
|
||||
alice.follow!(account, reblogs: true)
|
||||
bob.follow!(account, reblogs: false)
|
||||
end
|
||||
|
||||
it 'calls FollowService for local followers' do
|
||||
result = subject.perform(account.id)
|
||||
|
||||
expect(result).to be_nil
|
||||
expect(service).to have_received(:call).with(alice, account, reblogs: true)
|
||||
expect(service).to have_received(:call).with(bob, account, reblogs: false)
|
||||
end
|
||||
end
|
||||
end
|
50
spec/workers/unfollow_follow_worker_spec.rb
Normal file
50
spec/workers/unfollow_follow_worker_spec.rb
Normal file
@ -0,0 +1,50 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe UnfollowFollowWorker do
|
||||
let(:local_follower) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
|
||||
let(:source_account) { Fabricate(:account) }
|
||||
let(:target_account) { Fabricate(:account) }
|
||||
let(:show_reblogs) { true }
|
||||
|
||||
subject { described_class.new }
|
||||
|
||||
before do
|
||||
local_follower.follow!(source_account, reblogs: show_reblogs)
|
||||
end
|
||||
|
||||
context 'when show_reblogs is true' do
|
||||
let(:show_reblogs) { true }
|
||||
|
||||
describe 'perform' do
|
||||
it 'unfollows source account and follows target account' do
|
||||
subject.perform(local_follower.id, source_account.id, target_account.id)
|
||||
expect(local_follower.following?(source_account)).to be false
|
||||
expect(local_follower.following?(target_account)).to be true
|
||||
end
|
||||
|
||||
it 'preserves show_reblogs' do
|
||||
subject.perform(local_follower.id, source_account.id, target_account.id)
|
||||
expect(Follow.find_by(account: local_follower, target_account: target_account).show_reblogs?).to be show_reblogs
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when show_reblogs is false' do
|
||||
let(:show_reblogs) { false }
|
||||
|
||||
describe 'perform' do
|
||||
it 'unfollows source account and follows target account' do
|
||||
subject.perform(local_follower.id, source_account.id, target_account.id)
|
||||
expect(local_follower.following?(source_account)).to be false
|
||||
expect(local_follower.following?(target_account)).to be true
|
||||
end
|
||||
|
||||
it 'preserves show_reblogs' do
|
||||
subject.perform(local_follower.id, source_account.id, target_account.id)
|
||||
expect(Follow.find_by(account: local_follower, target_account: target_account).show_reblogs?).to be show_reblogs
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user