Merge remote-tracking branch 'parent/main' into upstream-20240117
This commit is contained in:
commit
5d79bd078c
150 changed files with 2982 additions and 1485 deletions
|
@ -1,21 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::FollowRecommendationsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:admin_user) }
|
||||
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns http success' do
|
||||
get :show
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,21 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::TermsOfService::HistoriesController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:admin_user) }
|
||||
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns http success' do
|
||||
get :show
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ApplicationController do
|
||||
render_views
|
||||
|
||||
controller do
|
||||
def success
|
||||
head 200
|
||||
|
@ -23,9 +25,22 @@ RSpec.describe ApplicationController do
|
|||
|
||||
shared_examples 'respond_with_error' do |code|
|
||||
it "returns http #{code} for http and renders template" do
|
||||
expect(subject).to render_template("errors/#{code}", layout: 'error')
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(code)
|
||||
expect(response)
|
||||
.to have_http_status(code)
|
||||
expect(response.parsed_body)
|
||||
.to have_css('body[class=error]')
|
||||
expect(response.parsed_body.css('h1').to_s)
|
||||
.to include(error_content(code))
|
||||
end
|
||||
|
||||
def error_content(code)
|
||||
if code == 422
|
||||
I18n.t('errors.422.content')
|
||||
else
|
||||
I18n.t("errors.#{code}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Auth::SetupController do
|
||||
render_views
|
||||
|
||||
describe 'GET #show' do
|
||||
context 'with a signed out request' do
|
||||
it 'returns http redirect' do
|
||||
get :show
|
||||
expect(response).to be_redirect
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an unconfirmed signed in user' do
|
||||
before { sign_in Fabricate(:user, confirmed_at: nil) }
|
||||
|
||||
it 'returns http success' do
|
||||
get :show
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -80,18 +80,37 @@ RSpec.describe ThemeHelper do
|
|||
end
|
||||
|
||||
describe '#custom_stylesheet' do
|
||||
let(:custom_css) { 'body {}' }
|
||||
let(:custom_digest) { Digest::SHA256.hexdigest(custom_css) }
|
||||
|
||||
before do
|
||||
Setting.custom_css = custom_css
|
||||
end
|
||||
|
||||
context 'when custom css setting value digest is present' do
|
||||
before { Rails.cache.write(:setting_digest_custom_css, '1a2s3d4f1a2s3d4f') }
|
||||
before { Rails.cache.write(:setting_digest_custom_css, custom_digest) }
|
||||
|
||||
it 'returns value from settings' do
|
||||
expect(custom_stylesheet)
|
||||
.to match('/css/custom-1a2s3d4f.css')
|
||||
.to match("/css/custom-#{custom_digest[...8]}.css")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom css setting value digest is not present' do
|
||||
context 'when custom css setting value digest is expired' do
|
||||
before { Rails.cache.delete(:setting_digest_custom_css) }
|
||||
|
||||
it 'returns value from settings' do
|
||||
expect(custom_stylesheet)
|
||||
.to match("/css/custom-#{custom_digest[...8]}.css")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom css setting is not present' do
|
||||
before do
|
||||
Setting.custom_css = nil
|
||||
Rails.cache.delete(:setting_digest_custom_css)
|
||||
end
|
||||
|
||||
it 'returns default value' do
|
||||
expect(custom_stylesheet)
|
||||
.to be_blank
|
||||
|
|
|
@ -182,12 +182,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when object publication date is below ISO8601 range' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
published: '-0977-11-03T08:31:22Z',
|
||||
}
|
||||
build_object(
|
||||
published: '-0977-11-03T08:31:22Z'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status with a valid creation date', :aggregate_failures do
|
||||
|
@ -204,12 +201,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when object publication date is above ISO8601 range' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
published: '10000-11-03T08:31:22Z',
|
||||
}
|
||||
build_object(
|
||||
published: '10000-11-03T08:31:22Z'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status with a valid creation date', :aggregate_failures do
|
||||
|
@ -226,13 +220,10 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when object has been edited' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
published: '2022-01-22T15:00:00Z',
|
||||
updated: '2022-01-22T16:00:00Z',
|
||||
}
|
||||
updated: '2022-01-22T16:00:00Z'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status with appropriate creation and edition dates', :aggregate_failures do
|
||||
|
@ -252,13 +243,10 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when object has update date equal to creation date' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
published: '2022-01-22T15:00:00Z',
|
||||
updated: '2022-01-22T15:00:00Z',
|
||||
}
|
||||
updated: '2022-01-22T15:00:00Z'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status and does not mark it as edited' do
|
||||
|
@ -274,11 +262,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with an unknown object type' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Banana',
|
||||
content: 'Lorem ipsum',
|
||||
}
|
||||
build_object(
|
||||
type: 'Banana'
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not create a status' do
|
||||
|
@ -287,13 +273,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
end
|
||||
|
||||
context 'with a standalone' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
}
|
||||
end
|
||||
let(:object_json) { build_object }
|
||||
|
||||
it 'creates status' do
|
||||
expect { subject.perform }.to change(sender.statuses, :count).by(1)
|
||||
|
@ -316,12 +296,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when public with explicit public address' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
}
|
||||
build_object(
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -336,12 +313,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when public with as:Public' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'as:Public',
|
||||
}
|
||||
build_object(
|
||||
to: 'as:Public'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -356,12 +330,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when public with Public' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'Public',
|
||||
}
|
||||
build_object(
|
||||
to: 'Public'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -376,12 +347,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when unlisted with explicit public address' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
cc: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
}
|
||||
build_object(
|
||||
cc: 'https://www.w3.org/ns/activitystreams#Public'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -396,12 +364,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when unlisted with as:Public' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
cc: 'as:Public',
|
||||
}
|
||||
build_object(
|
||||
cc: 'as:Public'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -416,12 +381,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when unlisted with Public' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
cc: 'Public',
|
||||
}
|
||||
build_object(
|
||||
cc: 'Public'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -479,12 +441,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when private' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'http://example.com/followers',
|
||||
}
|
||||
build_object(
|
||||
to: 'http://example.com/followers'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -499,16 +458,13 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'when private with inlined Collection in audience' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
to: {
|
||||
type: 'OrderedCollection',
|
||||
id: 'http://example.com/followers',
|
||||
first: 'http://example.com/followers?page=true',
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -525,12 +481,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
}
|
||||
build_object(
|
||||
to: ActivityPub::TagManager.instance.uri_for(recipient)
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status with a silent mention' do
|
||||
|
@ -597,16 +550,13 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
to: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
tag: {
|
||||
type: 'Mention',
|
||||
href: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -921,12 +871,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
let(:original_status) { Fabricate(:status) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
|
||||
}
|
||||
build_object(
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status)
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -946,17 +893,14 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
tag: [
|
||||
{
|
||||
type: 'Mention',
|
||||
href: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -971,16 +915,13 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with mentions missing href' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
tag: [
|
||||
{
|
||||
type: 'Mention',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1301,10 +1242,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with media attachments' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
|
@ -1316,8 +1254,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
mediaType: 'image/png',
|
||||
url: 'http://example.com/emoji.png',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status with correctly-ordered media attachments' do
|
||||
|
@ -1333,10 +1271,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
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',
|
||||
build_object(
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
|
@ -1344,8 +1279,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
url: 'http://example.com/attachment.png',
|
||||
name: '*' * MediaAttachment::MAX_DESCRIPTION_LENGTH,
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1360,10 +1295,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with media attachments with long description as summary' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
|
@ -1371,8 +1303,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
url: 'http://example.com/attachment.png',
|
||||
summary: '*' * MediaAttachment::MAX_DESCRIPTION_LENGTH,
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1387,10 +1319,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with media attachments with focal points' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
|
@ -1398,8 +1327,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
url: 'http://example.com/attachment.png',
|
||||
focalPoint: [0.5, -0.7],
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1414,17 +1343,14 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with media attachments missing url' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
mediaType: 'image/png',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1437,18 +1363,15 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with hashtags' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
tag: [
|
||||
{
|
||||
type: 'Hashtag',
|
||||
href: 'http://example.com/blah',
|
||||
name: '#test',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1463,10 +1386,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with featured hashtags' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
tag: [
|
||||
{
|
||||
|
@ -1474,8 +1394,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
href: 'http://example.com/blah',
|
||||
name: '#test',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
|
@ -1512,17 +1432,14 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with hashtags missing name' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
tag: [
|
||||
{
|
||||
type: 'Hashtag',
|
||||
href: 'http://example.com/blah',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1535,18 +1452,15 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with hashtags invalid name' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
tag: [
|
||||
{
|
||||
type: 'Hashtag',
|
||||
href: 'http://example.com/blah',
|
||||
name: 'foo, #eh !',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1559,9 +1473,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with emojis' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
build_object(
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
{
|
||||
|
@ -1571,8 +1483,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
},
|
||||
name: 'tinking',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1587,9 +1499,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with emojis served with invalid content-type' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
build_object(
|
||||
content: 'Lorem ipsum :tinkong:',
|
||||
tag: [
|
||||
{
|
||||
|
@ -1599,8 +1509,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
},
|
||||
name: 'tinkong',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1615,9 +1525,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with emojis missing name' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
build_object(
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
{
|
||||
|
@ -1626,8 +1534,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
url: 'http://example.com/emoji.png',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1640,17 +1548,15 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with emojis missing icon' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
build_object(
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
{
|
||||
type: 'Emoji',
|
||||
name: 'tinking',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
|
@ -1663,8 +1569,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with poll' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
build_object(
|
||||
type: 'Question',
|
||||
content: 'Which color was the submarine?',
|
||||
oneOf: [
|
||||
|
@ -1682,8 +1587,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
totalItems: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status with a poll' do
|
||||
|
@ -1706,12 +1611,10 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
let!(:local_status) { Fabricate(:status, poll: poll) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
build_object(
|
||||
name: 'Yellow',
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
|
||||
}
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
|
||||
).except(:content)
|
||||
end
|
||||
|
||||
it 'adds a vote to the poll with correct uri' do
|
||||
|
@ -1759,12 +1662,10 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
let!(:local_status) { Fabricate(:status, poll: poll) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
build_object(
|
||||
name: 'Yellow',
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
|
||||
}
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
|
||||
).except(:content)
|
||||
end
|
||||
|
||||
it 'does not add a vote to the poll' do
|
||||
|
@ -2509,10 +2410,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
context 'with counts' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
build_object(
|
||||
likes: {
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar', '/likes'].join,
|
||||
type: 'Collection',
|
||||
|
@ -2522,8 +2420,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar', '/shares'].join,
|
||||
type: 'Collection',
|
||||
totalItems: 100,
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it 'uses the counts from the created object' do
|
||||
|
@ -2552,12 +2450,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
end
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
}
|
||||
build_object(
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public'
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
|
@ -2623,13 +2518,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
subject.perform
|
||||
end
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
}
|
||||
end
|
||||
let(:object_json) { build_object }
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
@ -2644,12 +2533,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
let!(:local_status) { Fabricate(:status) }
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
|
||||
}
|
||||
build_object(
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
|
@ -2715,13 +2601,11 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
subject { described_class.new(json, sender, delivery: true) }
|
||||
|
||||
let!(:local_account) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: ActivityPub::TagManager.instance.uri_for(local_account),
|
||||
}
|
||||
build_object(
|
||||
to: ActivityPub::TagManager.instance.uri_for(local_account)
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
|
@ -2741,12 +2625,9 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
|
||||
let!(:local_account) { Fabricate(:account) }
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
cc: ActivityPub::TagManager.instance.uri_for(local_account),
|
||||
}
|
||||
build_object(
|
||||
cc: ActivityPub::TagManager.instance.uri_for(local_account)
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
|
@ -2797,17 +2678,19 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
subject.perform
|
||||
end
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
}
|
||||
end
|
||||
let(:object_json) { build_object }
|
||||
|
||||
it 'does not create anything' do
|
||||
expect(sender.statuses.count).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
def build_object(options = {})
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
}.merge(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,17 +7,50 @@ RSpec.describe ActivityPub::TagManager do
|
|||
|
||||
subject { described_class.instance }
|
||||
|
||||
let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http'}://#{Rails.configuration.x.web_domain}" }
|
||||
|
||||
describe '#public_collection?' do
|
||||
it 'returns true for the special public collection and common shorthands' do
|
||||
expect(subject.public_collection?('https://www.w3.org/ns/activitystreams#Public')).to be true
|
||||
expect(subject.public_collection?('as:Public')).to be true
|
||||
expect(subject.public_collection?('Public')).to be true
|
||||
end
|
||||
|
||||
it 'returns false for other URIs' do
|
||||
expect(subject.public_collection?('https://example.com/foo/bar')).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#url_for' do
|
||||
it 'returns a string' do
|
||||
it 'returns a string starting with web domain' do
|
||||
account = Fabricate(:account)
|
||||
expect(subject.url_for(account)).to be_a String
|
||||
expect(subject.url_for(account)).to be_a(String)
|
||||
.and start_with(domain)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#uri_for' do
|
||||
it 'returns a string' do
|
||||
it 'returns a string starting with web domain' do
|
||||
account = Fabricate(:account)
|
||||
expect(subject.uri_for(account)).to be_a String
|
||||
expect(subject.uri_for(account)).to be_a(String)
|
||||
.and start_with(domain)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#activity_uri_for' do
|
||||
context 'when given an account' do
|
||||
it 'raises an exception' do
|
||||
account = Fabricate(:account)
|
||||
expect { subject.activity_uri_for(account) }.to raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given a local activity' do
|
||||
it 'returns a string starting with web domain' do
|
||||
status = Fabricate(:status)
|
||||
expect(subject.uri_for(status)).to be_a(String)
|
||||
.and start_with(domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -13,4 +13,13 @@ RSpec.describe AnnualReport do
|
|||
.to change(GeneratedAnnualReport, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.prepare' do
|
||||
before { Fabricate :status }
|
||||
|
||||
it 'generates records from source class which prepare data' do
|
||||
expect { described_class.prepare(Time.current.year) }
|
||||
.to change(AnnualReport::StatusesPerAccountCount, :count).by(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,8 +42,8 @@ RSpec.describe DeliveryFailureTracker do
|
|||
Fabricate(:unavailable_domain, domain: 'foo.bar')
|
||||
end
|
||||
|
||||
it 'removes URLs that are unavailable' do
|
||||
results = described_class.without_unavailable(['http://example.com/good/inbox', 'http://foo.bar/unavailable/inbox'])
|
||||
it 'removes URLs that are bogus or unavailable' do
|
||||
results = described_class.without_unavailable(['http://example.com/good/inbox', 'http://foo.bar/unavailable/inbox', '{foo:'])
|
||||
|
||||
expect(results).to include('http://example.com/good/inbox')
|
||||
expect(results).to_not include('http://foo.bar/unavailable/inbox')
|
||||
|
|
|
@ -89,10 +89,8 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :accounts, name: :index_accounts_on_username_and_domain_lower
|
||||
_remote_account = Fabricate(:account, username: duplicate_account_username, domain: duplicate_account_domain)
|
||||
_remote_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: duplicate_account_domain).save(validate: false)
|
||||
_local_account = Fabricate(:account, username: duplicate_account_username, domain: nil)
|
||||
_local_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: nil).save(validate: false)
|
||||
duplicate_record(:account, username: duplicate_account_username, domain: duplicate_account_domain)
|
||||
duplicate_record(:account, username: duplicate_account_username, domain: nil)
|
||||
end
|
||||
|
||||
def choose_local_account_to_keep
|
||||
|
@ -127,8 +125,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :users, :email
|
||||
Fabricate(:user, email: duplicate_email)
|
||||
Fabricate.build(:user, email: duplicate_email).save(validate: false)
|
||||
duplicate_record(:user, email: duplicate_email)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -156,8 +153,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :users, :confirmation_token
|
||||
Fabricate(:user, confirmation_token: duplicate_confirmation_token)
|
||||
Fabricate.build(:user, confirmation_token: duplicate_confirmation_token).save(validate: false)
|
||||
duplicate_record(:user, confirmation_token: duplicate_confirmation_token)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -185,8 +181,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :users, :reset_password_token
|
||||
Fabricate(:user, reset_password_token: duplicate_reset_password_token)
|
||||
Fabricate.build(:user, reset_password_token: duplicate_reset_password_token).save(validate: false)
|
||||
duplicate_record(:user, reset_password_token: duplicate_reset_password_token)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -214,8 +209,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :account_domain_blocks, [:account_id, :domain]
|
||||
Fabricate(:account_domain_block, account: account, domain: duplicate_domain)
|
||||
Fabricate.build(:account_domain_block, account: account, domain: duplicate_domain).save(validate: false)
|
||||
duplicate_record(:account_domain_block, account: account, domain: duplicate_domain)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -244,8 +238,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :announcement_reactions, [:account_id, :announcement_id, :name]
|
||||
Fabricate(:announcement_reaction, account: account, announcement: announcement, name: name)
|
||||
Fabricate.build(:announcement_reaction, account: account, announcement: announcement, name: name).save(validate: false)
|
||||
duplicate_record(:announcement_reaction, account: account, announcement: announcement, name: name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -272,8 +265,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :conversations, :uri
|
||||
Fabricate(:conversation, uri: uri)
|
||||
Fabricate.build(:conversation, uri: uri).save(validate: false)
|
||||
duplicate_record(:conversation, uri: uri)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -301,8 +293,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :custom_emojis, [:shortcode, :domain]
|
||||
Fabricate(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain)
|
||||
Fabricate.build(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain).save(validate: false)
|
||||
duplicate_record(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -329,8 +320,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :custom_emoji_categories, :name
|
||||
Fabricate(:custom_emoji_category, name: duplicate_name)
|
||||
Fabricate.build(:custom_emoji_category, name: duplicate_name).save(validate: false)
|
||||
duplicate_record(:custom_emoji_category, name: duplicate_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -357,8 +347,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :domain_allows, :domain
|
||||
Fabricate(:domain_allow, domain: domain)
|
||||
Fabricate.build(:domain_allow, domain: domain).save(validate: false)
|
||||
duplicate_record(:domain_allow, domain: domain)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -385,8 +374,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :domain_blocks, :domain
|
||||
Fabricate(:domain_block, domain: domain)
|
||||
Fabricate.build(:domain_block, domain: domain).save(validate: false)
|
||||
duplicate_record(:domain_block, domain: domain)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -413,8 +401,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :email_domain_blocks, :domain
|
||||
Fabricate(:email_domain_block, domain: domain)
|
||||
Fabricate.build(:email_domain_block, domain: domain).save(validate: false)
|
||||
duplicate_record(:email_domain_block, domain: domain)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -441,8 +428,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :media_attachments, :shortcode
|
||||
Fabricate(:media_attachment, shortcode: shortcode)
|
||||
Fabricate.build(:media_attachment, shortcode: shortcode).save(validate: false)
|
||||
duplicate_record(:media_attachment, shortcode: shortcode)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -469,8 +455,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :preview_cards, :url
|
||||
Fabricate(:preview_card, url: url)
|
||||
Fabricate.build(:preview_card, url: url).save(validate: false)
|
||||
duplicate_record(:preview_card, url: url)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -530,8 +515,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :tags, name: 'index_tags_on_name_lower_btree'
|
||||
Fabricate(:tag, name: name)
|
||||
Fabricate.build(:tag, name: name).save(validate: false)
|
||||
duplicate_record(:tag, name: name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -558,8 +542,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :webauthn_credentials, :external_id
|
||||
Fabricate(:webauthn_credential, external_id: external_id)
|
||||
Fabricate.build(:webauthn_credential, external_id: external_id).save(validate: false)
|
||||
duplicate_record(:webauthn_credential, external_id: external_id)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -586,11 +569,15 @@ RSpec.describe Mastodon::CLI::Maintenance do
|
|||
|
||||
def prepare_duplicate_data
|
||||
ActiveRecord::Base.connection.remove_index :webhooks, :url
|
||||
Fabricate(:webhook, url: url)
|
||||
Fabricate.build(:webhook, url: url).save(validate: false)
|
||||
duplicate_record(:webhook, url: url)
|
||||
end
|
||||
end
|
||||
|
||||
def duplicate_record(fabricator, options = {})
|
||||
Fabricate(fabricator, options)
|
||||
Fabricate.build(fabricator, options).save(validate: false)
|
||||
end
|
||||
|
||||
def agree_to_backup_warning
|
||||
allow(cli.shell)
|
||||
.to receive(:yes?)
|
||||
|
|
|
@ -4,7 +4,9 @@ require 'rails_helper'
|
|||
require 'securerandom'
|
||||
|
||||
RSpec.describe Request do
|
||||
subject { described_class.new(:get, 'http://example.com') }
|
||||
subject { described_class.new(:get, 'http://example.com', **options) }
|
||||
|
||||
let(:options) { {} }
|
||||
|
||||
describe '#headers' do
|
||||
it 'returns user agent' do
|
||||
|
@ -39,8 +41,8 @@ RSpec.describe Request do
|
|||
end
|
||||
|
||||
describe '#perform' do
|
||||
context 'with valid host' do
|
||||
before { stub_request(:get, 'http://example.com') }
|
||||
context 'with valid host and non-persistent connection' do
|
||||
before { stub_request(:get, 'http://example.com').to_return(body: 'lorem ipsum') }
|
||||
|
||||
it 'executes a HTTP request' do
|
||||
expect { |block| subject.perform(&block) }.to yield_control
|
||||
|
@ -71,9 +73,9 @@ RSpec.describe Request do
|
|||
expect(subject.send(:http_client)).to have_received(:close)
|
||||
end
|
||||
|
||||
it 'returns response which implements body_with_limit' do
|
||||
it 'yields response' do
|
||||
subject.perform do |response|
|
||||
expect(response).to respond_to :body_with_limit
|
||||
expect(response.body_with_limit).to eq 'lorem ipsum'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -95,6 +97,43 @@ RSpec.describe Request do
|
|||
expect { subject.perform }.to raise_error Mastodon::ValidationError
|
||||
end
|
||||
end
|
||||
|
||||
context 'with persistent connection' do
|
||||
before { stub_request(:get, 'http://example.com').to_return(body: SecureRandom.random_bytes(2.megabytes)) }
|
||||
|
||||
let(:http_client) { described_class.http_client.persistent('http://example.com') }
|
||||
let(:options) { { http_client: http_client } }
|
||||
|
||||
it 'leaves connection open after completely consumed response' do
|
||||
allow(http_client).to receive(:close)
|
||||
|
||||
subject.perform { |response| response.truncated_body(3.megabytes) }
|
||||
|
||||
expect(http_client).to_not have_received(:close)
|
||||
end
|
||||
|
||||
it 'leaves connection open after nearly consumed response' do
|
||||
allow(http_client).to receive(:close)
|
||||
|
||||
subject.perform { |response| response.truncated_body(1.8.megabytes) }
|
||||
|
||||
expect(http_client).to_not have_received(:close)
|
||||
end
|
||||
|
||||
it 'closes connection after unconsumed response' do
|
||||
allow(http_client).to receive(:close)
|
||||
|
||||
subject.perform
|
||||
|
||||
expect(http_client).to have_received(:close)
|
||||
end
|
||||
|
||||
it 'yields response' do
|
||||
subject.perform do |response|
|
||||
expect(response.body_with_limit(2.megabytes).size).to eq 2.megabytes
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "response's body_with_limit method" do
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Account do
|
||||
include_examples 'Account::Search'
|
||||
include_examples 'Reviewable'
|
||||
|
||||
context 'with an account record' do
|
||||
|
@ -48,14 +49,16 @@ RSpec.describe Account do
|
|||
end
|
||||
|
||||
describe '#local?' do
|
||||
it 'returns true when domain is null' do
|
||||
account = Fabricate(:account, domain: nil)
|
||||
expect(account).to be_local
|
||||
context 'when the domain is null' do
|
||||
subject { Fabricate.build :account, domain: nil }
|
||||
|
||||
it { is_expected.to be_local }
|
||||
end
|
||||
|
||||
it 'returns false when domain is present' do
|
||||
account = Fabricate(:account, domain: 'foreign.tld')
|
||||
expect(account).to_not be_local
|
||||
context 'when the domain is present' do
|
||||
subject { Fabricate.build :account, domain: 'host.example' }
|
||||
|
||||
it { is_expected.to_not be_local }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -66,12 +69,6 @@ RSpec.describe Account do
|
|||
it { is_expected.to_not be_remote }
|
||||
end
|
||||
|
||||
context 'when the domain is blank' do
|
||||
subject { Fabricate.build :account, domain: '' }
|
||||
|
||||
it { is_expected.to_not be_remote }
|
||||
end
|
||||
|
||||
context 'when the domain is present' do
|
||||
subject { Fabricate.build :account, domain: 'host.example' }
|
||||
|
||||
|
@ -79,6 +76,20 @@ RSpec.describe Account do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#actor_type_application?' do
|
||||
context 'when the actor is not of type application' do
|
||||
subject { Fabricate.build :account, actor_type: 'Person' }
|
||||
|
||||
it { is_expected.to_not be_actor_type_application }
|
||||
end
|
||||
|
||||
context 'when the actor is of type application' do
|
||||
subject { Fabricate.build :account, actor_type: 'Application' }
|
||||
|
||||
it { is_expected.to be_actor_type_application }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Local domain user methods' do
|
||||
subject { Fabricate(:account, domain: nil, username: 'alice') }
|
||||
|
||||
|
@ -540,298 +551,6 @@ RSpec.describe Account do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.search_for' do
|
||||
before do
|
||||
_missing = Fabricate(
|
||||
:account,
|
||||
display_name: 'Missing',
|
||||
username: 'missing',
|
||||
domain: 'missing.com'
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not return suspended users' do
|
||||
Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com',
|
||||
suspended: true
|
||||
)
|
||||
|
||||
results = described_class.search_for('username')
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unapproved users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(approved: false)
|
||||
|
||||
results = described_class.search_for('username')
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unconfirmed users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(confirmed_at: nil)
|
||||
|
||||
results = described_class.search_for('username')
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'accepts ?, \, : and space as delimiter' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.search_for('A?l\i:c e')
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'finds accounts with matching display_name' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.search_for('display')
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'finds accounts with matching username' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.search_for('username')
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'finds accounts with matching domain' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.search_for('example')
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'limits via constant by default' do
|
||||
stub_const('Account::Search::DEFAULT_LIMIT', 1)
|
||||
2.times.each { Fabricate(:account, display_name: 'Display Name') }
|
||||
results = described_class.search_for('display')
|
||||
expect(results.size).to eq 1
|
||||
end
|
||||
|
||||
it 'accepts arbitrary limits' do
|
||||
2.times.each { Fabricate(:account, display_name: 'Display Name') }
|
||||
results = described_class.search_for('display', limit: 1)
|
||||
expect(results.size).to eq 1
|
||||
end
|
||||
|
||||
it 'ranks multiple matches higher' do
|
||||
matches = [
|
||||
{ username: 'username', display_name: 'username' },
|
||||
{ display_name: 'Display Name', username: 'username', domain: 'example.com' },
|
||||
].map(&method(:Fabricate).curry(2).call(:account))
|
||||
|
||||
results = described_class.search_for('username')
|
||||
expect(results).to eq matches
|
||||
end
|
||||
end
|
||||
|
||||
describe '.advanced_search_for' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
context 'when limiting search to followed accounts' do
|
||||
it 'accepts ?, \, : and space as delimiter' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
account.follow!(match)
|
||||
|
||||
results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'does not return non-followed accounts' do
|
||||
Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return suspended users' do
|
||||
Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com',
|
||||
suspended: true
|
||||
)
|
||||
|
||||
results = described_class.advanced_search_for('username', account, limit: 10, following: true)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unapproved users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(approved: false)
|
||||
|
||||
results = described_class.advanced_search_for('username', account, limit: 10, following: true)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unconfirmed users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(confirmed_at: nil)
|
||||
|
||||
results = described_class.advanced_search_for('username', account, limit: 10, following: true)
|
||||
expect(results).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context 'when limiting search to follower accounts' do
|
||||
it 'accepts ?, \, : and space as delimiter' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
match.follow!(account)
|
||||
|
||||
results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, follower: true)
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'does not return non-follower accounts' do
|
||||
Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, follower: true)
|
||||
expect(results).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not return suspended users' do
|
||||
Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com',
|
||||
suspended: true
|
||||
)
|
||||
|
||||
results = described_class.advanced_search_for('username', account)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unapproved users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(approved: false)
|
||||
|
||||
results = described_class.advanced_search_for('username', account)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unconfirmed users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(confirmed_at: nil)
|
||||
|
||||
results = described_class.advanced_search_for('username', account)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'accepts ?, \, : and space as delimiter' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.advanced_search_for('A?l\i:c e', account)
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'limits result count by default value' do
|
||||
stub_const('Account::Search::DEFAULT_LIMIT', 1)
|
||||
2.times { Fabricate(:account, display_name: 'Display Name') }
|
||||
results = described_class.advanced_search_for('display', account)
|
||||
expect(results.size).to eq 1
|
||||
end
|
||||
|
||||
it 'accepts arbitrary limits' do
|
||||
2.times { Fabricate(:account, display_name: 'Display Name') }
|
||||
results = described_class.advanced_search_for('display', account, limit: 1)
|
||||
expect(results.size).to eq 1
|
||||
end
|
||||
|
||||
it 'ranks followed accounts higher' do
|
||||
match = Fabricate(:account, username: 'Matching')
|
||||
followed_match = Fabricate(:account, username: 'Matcher')
|
||||
Fabricate(:follow, account: account, target_account: followed_match)
|
||||
|
||||
results = described_class.advanced_search_for('match', account)
|
||||
expect(results).to eq [followed_match, match]
|
||||
expect(results.first.rank).to be > results.last.rank
|
||||
end
|
||||
end
|
||||
|
||||
describe '#statuses_count' do
|
||||
subject { Fabricate(:account) }
|
||||
|
||||
|
@ -1030,6 +749,8 @@ RSpec.describe Account do
|
|||
describe 'Validations' do
|
||||
it { is_expected.to validate_presence_of(:username) }
|
||||
|
||||
it { is_expected.to_not allow_value('').for(:domain) }
|
||||
|
||||
context 'when account is local' do
|
||||
subject { Fabricate.build :account, domain: nil }
|
||||
|
||||
|
@ -1068,6 +789,11 @@ RSpec.describe Account do
|
|||
|
||||
it { is_expected.to allow_value(fields_empty_name_value).for(:fields) }
|
||||
it { is_expected.to_not allow_values(fields_over_limit, fields_empty_name).for(:fields) }
|
||||
|
||||
it { is_expected.to validate_absence_of(:followers_url).on(:create) }
|
||||
it { is_expected.to validate_absence_of(:inbox_url).on(:create) }
|
||||
it { is_expected.to validate_absence_of(:shared_inbox_url).on(:create) }
|
||||
it { is_expected.to validate_absence_of(:uri).on(:create) }
|
||||
end
|
||||
|
||||
context 'when account is remote' do
|
||||
|
|
|
@ -8,4 +8,18 @@ RSpec.describe AccountWarning do
|
|||
it { is_expected.to normalize(:text).from(nil).to('') }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#appeal_eligible?' do
|
||||
context 'when created too long ago' do
|
||||
subject { Fabricate.build :account_warning, created_at: (described_class::APPEAL_WINDOW * 2).ago }
|
||||
|
||||
it { is_expected.to_not be_appeal_eligible }
|
||||
end
|
||||
|
||||
context 'when created recently' do
|
||||
subject { Fabricate.build :account_warning, created_at: (described_class::APPEAL_WINDOW - 2.days).ago }
|
||||
|
||||
it { is_expected.to be_appeal_eligible }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ RSpec.describe Appeal do
|
|||
it { is_expected.to validate_length_of(:text).is_at_most(described_class::TEXT_LENGTH_LIMIT) }
|
||||
|
||||
context 'with a strike created too long ago' do
|
||||
let(:strike) { Fabricate.build :account_warning, created_at: (described_class::MAX_STRIKE_AGE * 2).ago }
|
||||
let(:strike) { Fabricate.build :account_warning, created_at: (AccountWarning::APPEAL_WINDOW * 2).ago }
|
||||
|
||||
it { is_expected.to_not allow_values(strike).for(:strike).against(:base).on(:create) }
|
||||
end
|
||||
|
|
|
@ -5,6 +5,29 @@ require 'rails_helper'
|
|||
RSpec.describe Invite do
|
||||
include_examples 'Expireable'
|
||||
|
||||
describe 'Associations' do
|
||||
it { is_expected.to belong_to(:user).inverse_of(:invites) }
|
||||
it { is_expected.to have_many(:users).inverse_of(:invite) }
|
||||
end
|
||||
|
||||
describe 'Validations' do
|
||||
it { is_expected.to validate_length_of(:comment).is_at_most(described_class::COMMENT_SIZE_LIMIT) }
|
||||
end
|
||||
|
||||
describe 'Scopes' do
|
||||
describe '.available' do
|
||||
let!(:no_expires) { Fabricate :invite, expires_at: nil }
|
||||
let!(:past_expires) { Fabricate :invite, expires_at: 2.days.ago }
|
||||
let!(:future_expires) { Fabricate :invite, expires_at: 2.days.from_now }
|
||||
|
||||
it 'returns future and non-epiring records' do
|
||||
expect(described_class.available)
|
||||
.to include(no_expires, future_expires)
|
||||
.and not_include(past_expires)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid_for_use?' do
|
||||
it 'returns true when there are no limitations' do
|
||||
invite = Fabricate(:invite, max_uses: nil, expires_at: nil)
|
||||
|
@ -37,4 +60,26 @@ RSpec.describe Invite do
|
|||
expect(invite.valid_for_use?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Callbacks' do
|
||||
describe 'Setting the invite code' do
|
||||
context 'when creating a new record' do
|
||||
subject { Fabricate.build :invite }
|
||||
|
||||
it 'sets a code value' do
|
||||
expect { subject.save }
|
||||
.to change(subject, :code).from(be_blank).to(be_present)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when updating a record' do
|
||||
subject { Fabricate :invite }
|
||||
|
||||
it 'does not change the code value' do
|
||||
expect { subject.update(max_uses: 123_456) }
|
||||
.to not_change(subject, :code)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,8 @@ RSpec.describe IpBlock do
|
|||
it { is_expected.to validate_presence_of(:severity) }
|
||||
|
||||
it { is_expected.to validate_uniqueness_of(:ip) }
|
||||
|
||||
it { is_expected.to allow_values(:sign_up_requires_approval, :sign_up_block, :no_access).for(:severity) }
|
||||
end
|
||||
|
||||
describe '#to_log_human_identifier' do
|
||||
|
|
|
@ -9,6 +9,8 @@ RSpec.describe Status do
|
|||
let(:bob) { Fabricate(:account, username: 'bob') }
|
||||
let(:other) { Fabricate(:status, account: bob, text: 'Skulls for the skull god! The enemy\'s gates are sideways!') }
|
||||
|
||||
include_examples 'Status::Visibility'
|
||||
|
||||
describe '#local?' do
|
||||
it 'returns true when no remote URI is set' do
|
||||
expect(subject.local?).to be true
|
||||
|
@ -84,178 +86,6 @@ RSpec.describe Status do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#hidden?' do
|
||||
context 'when private_visibility?' do
|
||||
it 'returns true' do
|
||||
subject.visibility = :private
|
||||
expect(subject.hidden?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct_visibility?' do
|
||||
it 'returns true' do
|
||||
subject.visibility = :direct
|
||||
expect(subject.hidden?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public_visibility?' do
|
||||
it 'returns false' do
|
||||
subject.visibility = :public
|
||||
expect(subject.hidden?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when unlisted_visibility?' do
|
||||
it 'returns false' do
|
||||
subject.visibility = :unlisted
|
||||
expect(subject.hidden?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#compute_searchability' do
|
||||
subject { Fabricate(:status, account: account, searchability: status_searchability) }
|
||||
|
||||
let(:account_searchability) { :public }
|
||||
let(:status_searchability) { :public }
|
||||
let(:account_domain) { 'example.com' }
|
||||
let(:silenced_at) { nil }
|
||||
let(:account) { Fabricate(:account, domain: account_domain, searchability: account_searchability, silenced_at: silenced_at) }
|
||||
|
||||
context 'when public-public' do
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-public but silenced' do
|
||||
let(:silenced_at) { Time.now.utc }
|
||||
|
||||
it 'returns private' do
|
||||
expect(subject.compute_searchability).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-public_unlisted but silenced' do
|
||||
let(:silenced_at) { Time.now.utc }
|
||||
let(:status_searchability) { :public_unlisted }
|
||||
|
||||
it 'returns private' do
|
||||
expect(subject.compute_searchability).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-public_unlisted' do
|
||||
let(:status_searchability) { :public_unlisted }
|
||||
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
|
||||
it 'returns public_unlisted for local' do
|
||||
expect(subject.compute_searchability_local).to eq 'public_unlisted'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-private' do
|
||||
let(:status_searchability) { :private }
|
||||
|
||||
it 'returns private' do
|
||||
expect(subject.compute_searchability).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-direct' do
|
||||
let(:status_searchability) { :direct }
|
||||
|
||||
it 'returns direct' do
|
||||
expect(subject.compute_searchability).to eq 'direct'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when private-public' do
|
||||
let(:account_searchability) { :private }
|
||||
|
||||
it 'returns private' do
|
||||
expect(subject.compute_searchability).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct-public' do
|
||||
let(:account_searchability) { :direct }
|
||||
|
||||
it 'returns direct' do
|
||||
expect(subject.compute_searchability).to eq 'direct'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when limited-public' do
|
||||
let(:account_searchability) { :limited }
|
||||
|
||||
it 'returns limited' do
|
||||
expect(subject.compute_searchability).to eq 'limited'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when private-limited' do
|
||||
let(:account_searchability) { :private }
|
||||
let(:status_searchability) { :limited }
|
||||
|
||||
it 'returns limited' do
|
||||
expect(subject.compute_searchability).to eq 'limited'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when private-public of local account' do
|
||||
let(:account_searchability) { :private }
|
||||
let(:account_domain) { nil }
|
||||
let(:status_searchability) { :public }
|
||||
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct-public of local account' do
|
||||
let(:account_searchability) { :direct }
|
||||
let(:account_domain) { nil }
|
||||
let(:status_searchability) { :public }
|
||||
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when limited-public of local account' do
|
||||
let(:account_searchability) { :limited }
|
||||
let(:account_domain) { nil }
|
||||
let(:status_searchability) { :public }
|
||||
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-public_unlisted of local account' do
|
||||
let(:account_searchability) { :public }
|
||||
let(:account_domain) { nil }
|
||||
let(:status_searchability) { :public_unlisted }
|
||||
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
|
||||
it 'returns public_unlisted for local' do
|
||||
expect(subject.compute_searchability_local).to eq 'public_unlisted'
|
||||
end
|
||||
|
||||
it 'returns private for activitypub' do
|
||||
expect(subject.compute_searchability_activitypub).to eq 'private'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#quote' do
|
||||
let(:target_status) { Fabricate(:status) }
|
||||
let(:quote) { true }
|
||||
|
|
44
spec/models/trends/links_spec.rb
Normal file
44
spec/models/trends/links_spec.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Trends::Links do
|
||||
describe 'Trends::Links::Query' do
|
||||
subject { described_class.new.query }
|
||||
|
||||
describe '#records' do
|
||||
context 'with scored cards' do
|
||||
let!(:higher_score) { Fabricate :preview_card_trend, score: 10, language: 'en' }
|
||||
let!(:lower_score) { Fabricate :preview_card_trend, score: 1, language: 'es' }
|
||||
|
||||
it 'returns higher score first' do
|
||||
expect(subject.records)
|
||||
.to eq([higher_score.preview_card, lower_score.preview_card])
|
||||
end
|
||||
|
||||
context 'with preferred locale' do
|
||||
before { subject.in_locale!('es') }
|
||||
|
||||
it 'returns in language order' do
|
||||
expect(subject.records)
|
||||
.to eq([lower_score.preview_card, higher_score.preview_card])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account has chosen languages' do
|
||||
let!(:lang_match_higher_score) { Fabricate :preview_card_trend, score: 10, language: 'is' }
|
||||
let!(:lang_match_lower_score) { Fabricate :preview_card_trend, score: 1, language: 'da' }
|
||||
let(:user) { Fabricate :user, chosen_languages: %w(da is) }
|
||||
let(:account) { Fabricate :account, user: user }
|
||||
|
||||
before { subject.filtered_for!(account) }
|
||||
|
||||
it 'returns results' do
|
||||
expect(subject.records)
|
||||
.to eq([lang_match_higher_score.preview_card, lang_match_lower_score.preview_card, higher_score.preview_card, lower_score.preview_card])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -45,6 +45,45 @@ RSpec.describe Trends::Statuses do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'Trends::Statuses::Query methods' do
|
||||
subject { described_class.new.query }
|
||||
|
||||
describe '#records' do
|
||||
context 'with scored cards' do
|
||||
let!(:higher_score) { Fabricate :status_trend, score: 10, language: 'en' }
|
||||
let!(:lower_score) { Fabricate :status_trend, score: 1, language: 'es' }
|
||||
|
||||
it 'returns higher score first' do
|
||||
expect(subject.records)
|
||||
.to eq([higher_score.status, lower_score.status])
|
||||
end
|
||||
|
||||
context 'with preferred locale' do
|
||||
before { subject.in_locale!('es') }
|
||||
|
||||
it 'returns in language order' do
|
||||
expect(subject.records)
|
||||
.to eq([lower_score.status, higher_score.status])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account has chosen languages' do
|
||||
let!(:lang_match_higher_score) { Fabricate :status_trend, score: 10, language: 'is' }
|
||||
let!(:lang_match_lower_score) { Fabricate :status_trend, score: 1, language: 'da' }
|
||||
let(:user) { Fabricate :user, chosen_languages: %w(da is) }
|
||||
let(:account) { Fabricate :account, user: user }
|
||||
|
||||
before { subject.filtered_for!(account) }
|
||||
|
||||
it 'returns results' do
|
||||
expect(subject.records)
|
||||
.to eq([lang_match_higher_score.status, lang_match_lower_score.status, higher_score.status, lower_score.status])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#add' do
|
||||
let(:status) { Fabricate(:status) }
|
||||
|
||||
|
|
|
@ -56,6 +56,45 @@ RSpec.describe Trends::Tags do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'Trends::Tags::Query' do
|
||||
subject { described_class.new.query }
|
||||
|
||||
describe '#records' do
|
||||
context 'with scored cards' do
|
||||
let!(:higher_score) { Fabricate :tag_trend, score: 10, language: 'en' }
|
||||
let!(:lower_score) { Fabricate :tag_trend, score: 1, language: 'es' }
|
||||
|
||||
it 'returns higher score first' do
|
||||
expect(subject.records)
|
||||
.to eq([higher_score.tag, lower_score.tag])
|
||||
end
|
||||
|
||||
context 'with preferred locale' do
|
||||
before { subject.in_locale!('es') }
|
||||
|
||||
it 'returns in language order' do
|
||||
expect(subject.records)
|
||||
.to eq([lower_score.tag, higher_score.tag])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account has chosen languages' do
|
||||
let!(:lang_match_higher_score) { Fabricate :tag_trend, score: 10, language: 'is' }
|
||||
let!(:lang_match_lower_score) { Fabricate :tag_trend, score: 1, language: 'da' }
|
||||
let(:user) { Fabricate :user, chosen_languages: %w(da is) }
|
||||
let(:account) { Fabricate :account, user: user }
|
||||
|
||||
before { subject.filtered_for!(account) }
|
||||
|
||||
it 'returns results' do
|
||||
expect(subject.records)
|
||||
.to eq([lang_match_higher_score.tag, lang_match_lower_score.tag, higher_score.tag, lower_score.tag])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#refresh' do
|
||||
let!(:today) { at_time }
|
||||
let!(:yesterday) { today - 1.day }
|
||||
|
|
|
@ -31,11 +31,11 @@ RSpec.describe AccountWarningPolicy do
|
|||
|
||||
context 'when account is target' do
|
||||
context 'when record is appealable' do
|
||||
it { is_expected.to permit(account, AccountWarning.new(target_account_id: account.id, created_at: Appeal::MAX_STRIKE_AGE.ago + 1.hour)) }
|
||||
it { is_expected.to permit(account, AccountWarning.new(target_account_id: account.id, created_at: AccountWarning::APPEAL_WINDOW.ago + 1.hour)) }
|
||||
end
|
||||
|
||||
context 'when record is not appealable' do
|
||||
it { is_expected.to_not permit(account, AccountWarning.new(target_account_id: account.id, created_at: Appeal::MAX_STRIKE_AGE.ago - 1.hour)) }
|
||||
it { is_expected.to_not permit(account, AccountWarning.new(target_account_id: account.id, created_at: AccountWarning::APPEAL_WINDOW.ago - 1.hour)) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -68,6 +68,7 @@ RSpec.describe InstancePresenter do
|
|||
context 'with the GITHUB_REPOSITORY env variable set' do
|
||||
around do |example|
|
||||
ClimateControl.modify GITHUB_REPOSITORY: 'other/repo' do
|
||||
reload_configuration
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
@ -80,6 +81,7 @@ RSpec.describe InstancePresenter do
|
|||
context 'without the GITHUB_REPOSITORY env variable set' do
|
||||
around do |example|
|
||||
ClimateControl.modify GITHUB_REPOSITORY: nil do
|
||||
reload_configuration
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
@ -88,6 +90,10 @@ RSpec.describe InstancePresenter do
|
|||
expect(instance_presenter.source_url).to eq('https://github.com/kmycode/mastodon')
|
||||
end
|
||||
end
|
||||
|
||||
def reload_configuration
|
||||
Rails.configuration.x.mastodon = Rails.application.config_for(:mastodon)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#thumbnail' do
|
||||
|
|
|
@ -53,8 +53,9 @@ RSpec.describe 'Accounts show response' do
|
|||
it 'returns a standard HTML response', :aggregate_failures do
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and render_template(:show)
|
||||
.and have_http_link_header(ActivityPub::TagManager.instance.uri_for(account)).for(rel: 'alternate')
|
||||
expect(response.parsed_body.at('title').content)
|
||||
.to include(account.username)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -258,6 +258,19 @@ RSpec.describe 'Domain Blocks' do
|
|||
.to start_with('application/json')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when severity is invalid' do
|
||||
let(:params) { { domain: 'bar.com', severity: :bar } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
expect(response.content_type)
|
||||
.to start_with('application/json')
|
||||
expect(response.parsed_body[:error]).to eq('Validation failed: Severity is not included in the list')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v1/admin/domain_blocks/:id' do
|
||||
|
|
|
@ -187,6 +187,16 @@ RSpec.describe 'IP Blocks' do
|
|||
.to start_with('application/json')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the given severity is invalid' do
|
||||
let(:params) { { ip: '151.0.32.55', severity: 'invalid' } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v1/admin/ip_blocks/:id' do
|
||||
|
|
|
@ -9,10 +9,21 @@ RSpec.describe 'Languages' do
|
|||
end
|
||||
|
||||
it 'returns http success and includes supported languages' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.content_type)
|
||||
.to start_with('application/json')
|
||||
expect(response.parsed_body.pluck(:code)).to match_array LanguagesHelper::SUPPORTED_LOCALES.keys.map(&:to_s)
|
||||
expect(response.parsed_body)
|
||||
.to match_array(supported_locale_expectations)
|
||||
end
|
||||
|
||||
def supported_locale_expectations
|
||||
LanguagesHelper::SUPPORTED_LOCALES.map do |key, values|
|
||||
include(
|
||||
code: key.to_s,
|
||||
name: values.first
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,6 +36,31 @@ RSpec.describe 'Polls' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when poll is remote and needs refresh' do
|
||||
let(:poll) { Fabricate(:poll, last_fetched_at: nil, account: remote_account, status: status) }
|
||||
let(:remote_account) { Fabricate :account, domain: 'host.example' }
|
||||
let(:service) { instance_double(ActivityPub::FetchRemotePollService, call: nil) }
|
||||
let(:status) { Fabricate(:status, visibility: 'public', account: remote_account) }
|
||||
|
||||
before { allow(ActivityPub::FetchRemotePollService).to receive(:new).and_return(service) }
|
||||
|
||||
it 'returns poll data and calls fetch remote service' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.content_type)
|
||||
.to start_with('application/json')
|
||||
expect(response.parsed_body).to match(
|
||||
a_hash_including(
|
||||
id: poll.id.to_s
|
||||
)
|
||||
)
|
||||
expect(service)
|
||||
.to have_received(:call).with(poll, user.account)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parent status is private' do
|
||||
let(:visibility) { 'private' }
|
||||
|
||||
|
|
|
@ -143,6 +143,55 @@ RSpec.describe 'Notifications' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when there are numerous notifications for the same final group' do
|
||||
before do
|
||||
user.account.notifications.destroy_all
|
||||
5.times.each { FavouriteService.new.call(Fabricate(:account), user.account.statuses.first) }
|
||||
end
|
||||
|
||||
context 'with no options' do
|
||||
it 'returns a notification group covering all notifications' do
|
||||
subject
|
||||
|
||||
notification_ids = user.account.notifications.reload.pluck(:id)
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type)
|
||||
.to start_with('application/json')
|
||||
expect(response.parsed_body[:notification_groups]).to contain_exactly(
|
||||
a_hash_including(
|
||||
type: 'favourite',
|
||||
sample_account_ids: have_attributes(size: 5),
|
||||
page_min_id: notification_ids.first.to_s,
|
||||
page_max_id: notification_ids.last.to_s
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with min_id param' do
|
||||
let(:params) { { min_id: user.account.notifications.reload.first.id - 1 } }
|
||||
|
||||
it 'returns a notification group covering all notifications' do
|
||||
subject
|
||||
|
||||
notification_ids = user.account.notifications.reload.pluck(:id)
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type)
|
||||
.to start_with('application/json')
|
||||
expect(response.parsed_body[:notification_groups]).to contain_exactly(
|
||||
a_hash_including(
|
||||
type: 'favourite',
|
||||
sample_account_ids: have_attributes(size: 5),
|
||||
page_min_id: notification_ids.first.to_s,
|
||||
page_max_id: notification_ids.last.to_s
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no options' do
|
||||
it 'returns expected notification types', :aggregate_failures do
|
||||
subject
|
||||
|
|
27
spec/requests/auth/setup_spec.rb
Normal file
27
spec/requests/auth/setup_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Auth Setup' do
|
||||
describe 'GET /auth/setup' do
|
||||
context 'with a signed out request' do
|
||||
it 'redirects to root' do
|
||||
get '/auth/setup'
|
||||
|
||||
expect(response)
|
||||
.to redirect_to(new_user_session_url)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a confirmed signed in user' do
|
||||
before { sign_in Fabricate(:user, confirmed_at: 2.days.ago) }
|
||||
|
||||
it 'redirects to root' do
|
||||
get '/auth/setup'
|
||||
|
||||
expect(response)
|
||||
.to redirect_to(root_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,7 +9,6 @@ RSpec.describe 'Remote Interaction Helper' do
|
|||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and render_template(:index, layout: 'helper_frame')
|
||||
.and have_attributes(
|
||||
headers: include(
|
||||
'X-Frame-Options' => 'SAMEORIGIN',
|
||||
|
@ -17,6 +16,8 @@ RSpec.describe 'Remote Interaction Helper' do
|
|||
'Content-Security-Policy' => expected_csp_headers
|
||||
)
|
||||
)
|
||||
expect(response.body)
|
||||
.to match(/remote_interaction_helper/)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ RSpec.describe REST::ScheduledStatusSerializer do
|
|||
expect(subject.deep_symbolize_keys)
|
||||
.to include(
|
||||
scheduled_at: be_a(String).and(match_api_datetime_format),
|
||||
params: not_include(:application_id)
|
||||
params: include(:application_id)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -204,7 +204,7 @@ RSpec.describe ImportService, :inline_jobs do
|
|||
subject { described_class.new }
|
||||
|
||||
let(:csv) { attachment_fixture('bookmark-imports.txt') }
|
||||
let(:local_account) { Fabricate(:account, username: 'foo', domain: '') }
|
||||
let(:local_account) { Fabricate(:account, username: 'foo', domain: nil) }
|
||||
let!(:remote_status) { Fabricate(:status, uri: 'https://example.com/statuses/1312') }
|
||||
let!(:direct_status) { Fabricate(:status, uri: 'https://example.com/statuses/direct', visibility: :direct) }
|
||||
|
||||
|
|
295
spec/support/examples/models/concerns/account/search.rb
Normal file
295
spec/support/examples/models/concerns/account/search.rb
Normal file
|
@ -0,0 +1,295 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'Account::Search' do
|
||||
describe '.search_for' do
|
||||
before do
|
||||
_missing = Fabricate(
|
||||
:account,
|
||||
display_name: 'Missing',
|
||||
username: 'missing',
|
||||
domain: 'missing.com'
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not return suspended users' do
|
||||
Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com',
|
||||
suspended: true
|
||||
)
|
||||
|
||||
results = described_class.search_for('username')
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unapproved users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(approved: false)
|
||||
|
||||
results = described_class.search_for('username')
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unconfirmed users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(confirmed_at: nil)
|
||||
|
||||
results = described_class.search_for('username')
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'accepts ?, \, : and space as delimiter' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.search_for('A?l\i:c e')
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'finds accounts with matching display_name' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.search_for('display')
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'finds accounts with matching username' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.search_for('username')
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'finds accounts with matching domain' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.search_for('example')
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'limits via constant by default' do
|
||||
stub_const('Account::Search::DEFAULT_LIMIT', 1)
|
||||
2.times.each { Fabricate(:account, display_name: 'Display Name') }
|
||||
results = described_class.search_for('display')
|
||||
expect(results.size).to eq 1
|
||||
end
|
||||
|
||||
it 'accepts arbitrary limits' do
|
||||
2.times.each { Fabricate(:account, display_name: 'Display Name') }
|
||||
results = described_class.search_for('display', limit: 1)
|
||||
expect(results.size).to eq 1
|
||||
end
|
||||
|
||||
it 'ranks multiple matches higher' do
|
||||
matches = [
|
||||
{ username: 'username', display_name: 'username' },
|
||||
{ display_name: 'Display Name', username: 'username', domain: 'example.com' },
|
||||
].map(&method(:Fabricate).curry(2).call(:account))
|
||||
|
||||
results = described_class.search_for('username')
|
||||
expect(results).to eq matches
|
||||
end
|
||||
end
|
||||
|
||||
describe '.advanced_search_for' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
context 'when limiting search to followed accounts' do
|
||||
it 'accepts ?, \, : and space as delimiter' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
account.follow!(match)
|
||||
|
||||
results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'does not return non-followed accounts' do
|
||||
Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return suspended users' do
|
||||
Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com',
|
||||
suspended: true
|
||||
)
|
||||
|
||||
results = described_class.advanced_search_for('username', account, limit: 10, following: true)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unapproved users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(approved: false)
|
||||
|
||||
results = described_class.advanced_search_for('username', account, limit: 10, following: true)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unconfirmed users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(confirmed_at: nil)
|
||||
|
||||
results = described_class.advanced_search_for('username', account, limit: 10, following: true)
|
||||
expect(results).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context 'when limiting search to follower accounts' do
|
||||
it 'accepts ?, \, : and space as delimiter' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
match.follow!(account)
|
||||
|
||||
results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, follower: true)
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'does not return non-follower accounts' do
|
||||
Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, follower: true)
|
||||
expect(results).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not return suspended users' do
|
||||
Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username',
|
||||
domain: 'example.com',
|
||||
suspended: true
|
||||
)
|
||||
|
||||
results = described_class.advanced_search_for('username', account)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unapproved users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(approved: false)
|
||||
|
||||
results = described_class.advanced_search_for('username', account)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'does not return unconfirmed users' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'Display Name',
|
||||
username: 'username'
|
||||
)
|
||||
|
||||
match.user.update(confirmed_at: nil)
|
||||
|
||||
results = described_class.advanced_search_for('username', account)
|
||||
expect(results).to eq []
|
||||
end
|
||||
|
||||
it 'accepts ?, \, : and space as delimiter' do
|
||||
match = Fabricate(
|
||||
:account,
|
||||
display_name: 'A & l & i & c & e',
|
||||
username: 'username',
|
||||
domain: 'example.com'
|
||||
)
|
||||
|
||||
results = described_class.advanced_search_for('A?l\i:c e', account)
|
||||
expect(results).to eq [match]
|
||||
end
|
||||
|
||||
it 'limits result count by default value' do
|
||||
stub_const('Account::Search::DEFAULT_LIMIT', 1)
|
||||
2.times { Fabricate(:account, display_name: 'Display Name') }
|
||||
results = described_class.advanced_search_for('display', account)
|
||||
expect(results.size).to eq 1
|
||||
end
|
||||
|
||||
it 'accepts arbitrary limits' do
|
||||
2.times { Fabricate(:account, display_name: 'Display Name') }
|
||||
results = described_class.advanced_search_for('display', account, limit: 1)
|
||||
expect(results.size).to eq 1
|
||||
end
|
||||
|
||||
it 'ranks followed accounts higher' do
|
||||
match = Fabricate(:account, username: 'Matching')
|
||||
followed_match = Fabricate(:account, username: 'Matcher')
|
||||
Fabricate(:follow, account: account, target_account: followed_match)
|
||||
|
||||
results = described_class.advanced_search_for('match', account)
|
||||
expect(results).to eq [followed_match, match]
|
||||
expect(results.first.rank).to be > results.last.rank
|
||||
end
|
||||
end
|
||||
end
|
326
spec/support/examples/models/concerns/status/visibility.rb
Normal file
326
spec/support/examples/models/concerns/status/visibility.rb
Normal file
|
@ -0,0 +1,326 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.shared_examples 'Status::Visibility' do
|
||||
describe 'Validations' do
|
||||
context 'when status is a reblog' do
|
||||
subject { Fabricate.build :status, reblog: Fabricate(:status) }
|
||||
|
||||
it { is_expected.to allow_values('public', 'unlisted', 'private').for(:visibility) }
|
||||
it { is_expected.to_not allow_values('direct', 'limited').for(:visibility) }
|
||||
end
|
||||
|
||||
context 'when status is not reblog' do
|
||||
subject { Fabricate.build :status, reblog_of_id: nil }
|
||||
|
||||
it { is_expected.to allow_values('public', 'unlisted', 'private', 'direct', 'limited').for(:visibility) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Scopes' do
|
||||
let!(:direct_status) { Fabricate :status, visibility: :direct }
|
||||
let!(:limited_status) { Fabricate :status, visibility: :limited }
|
||||
let!(:private_status) { Fabricate :status, visibility: :private }
|
||||
let!(:public_status) { Fabricate :status, visibility: :public }
|
||||
let!(:unlisted_status) { Fabricate :status, visibility: :unlisted }
|
||||
|
||||
describe '.list_eligible_visibility' do
|
||||
it 'returns appropriate records' do
|
||||
expect(Status.list_eligible_visibility)
|
||||
.to include(
|
||||
private_status,
|
||||
public_status,
|
||||
unlisted_status
|
||||
)
|
||||
.and not_include(direct_status)
|
||||
.and not_include(limited_status)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.distributable_visibility' do
|
||||
it 'returns appropriate records' do
|
||||
expect(Status.distributable_visibility)
|
||||
.to include(
|
||||
public_status,
|
||||
unlisted_status
|
||||
)
|
||||
.and not_include(private_status)
|
||||
.and not_include(direct_status)
|
||||
.and not_include(limited_status)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.not_direct_visibility' do
|
||||
it 'returns appropriate records' do
|
||||
expect(Status.not_direct_visibility)
|
||||
.to include(
|
||||
limited_status,
|
||||
private_status,
|
||||
public_status,
|
||||
unlisted_status
|
||||
)
|
||||
.and not_include(direct_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Callbacks' do
|
||||
describe 'Setting visibility in before validation' do
|
||||
subject { Fabricate.build :status, visibility: nil }
|
||||
|
||||
context 'when explicit value is set' do
|
||||
before { subject.visibility = :public }
|
||||
|
||||
it 'does not change' do
|
||||
expect { subject.valid? }
|
||||
.to_not change(subject, :visibility)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is a reblog' do
|
||||
before { subject.reblog = Fabricate(:status, visibility: :public) }
|
||||
|
||||
it 'changes to match the reblog' do
|
||||
expect { subject.valid? }
|
||||
.to change(subject, :visibility).to('public')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is locked' do
|
||||
before { subject.account = Fabricate.build(:account, locked: true) }
|
||||
|
||||
it 'changes to private' do
|
||||
expect { subject.valid? }
|
||||
.to change(subject, :visibility).to('private')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is not locked' do
|
||||
before { subject.account = Fabricate.build(:account, locked: false) }
|
||||
|
||||
it 'changes to public' do
|
||||
expect { subject.valid? }
|
||||
.to change(subject, :visibility).to('public')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.selectable_visibilities' do
|
||||
it 'returns options available for default privacy selection' do
|
||||
expect(Status.selectable_visibilities)
|
||||
.to match(%w(public unlisted private))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#hidden?' do
|
||||
subject { Status.new }
|
||||
|
||||
context 'when visibility is private' do
|
||||
before { subject.visibility = :private }
|
||||
|
||||
it { is_expected.to be_hidden }
|
||||
end
|
||||
|
||||
context 'when visibility is direct' do
|
||||
before { subject.visibility = :direct }
|
||||
|
||||
it { is_expected.to be_hidden }
|
||||
end
|
||||
|
||||
context 'when visibility is limited' do
|
||||
before { subject.visibility = :limited }
|
||||
|
||||
it { is_expected.to be_hidden }
|
||||
end
|
||||
|
||||
context 'when visibility is public' do
|
||||
before { subject.visibility = :public }
|
||||
|
||||
it { is_expected.to_not be_hidden }
|
||||
end
|
||||
|
||||
context 'when visibility is unlisted' do
|
||||
before { subject.visibility = :unlisted }
|
||||
|
||||
it { is_expected.to_not be_hidden }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#distributable?' do
|
||||
subject { Status.new }
|
||||
|
||||
context 'when visibility is public' do
|
||||
before { subject.visibility = :public }
|
||||
|
||||
it { is_expected.to be_distributable }
|
||||
end
|
||||
|
||||
context 'when visibility is unlisted' do
|
||||
before { subject.visibility = :unlisted }
|
||||
|
||||
it { is_expected.to be_distributable }
|
||||
end
|
||||
|
||||
context 'when visibility is private' do
|
||||
before { subject.visibility = :private }
|
||||
|
||||
it { is_expected.to_not be_distributable }
|
||||
end
|
||||
|
||||
context 'when visibility is direct' do
|
||||
before { subject.visibility = :direct }
|
||||
|
||||
it { is_expected.to_not be_distributable }
|
||||
end
|
||||
|
||||
context 'when visibility is limited' do
|
||||
before { subject.visibility = :limited }
|
||||
|
||||
it { is_expected.to_not be_distributable }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#compute_searchability' do
|
||||
subject { Fabricate(:status, account: account, searchability: status_searchability) }
|
||||
|
||||
let(:account_searchability) { :public }
|
||||
let(:status_searchability) { :public }
|
||||
let(:account_domain) { 'example.com' }
|
||||
let(:silenced_at) { nil }
|
||||
let(:account) { Fabricate(:account, domain: account_domain, searchability: account_searchability, silenced_at: silenced_at) }
|
||||
|
||||
context 'when public-public' do
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-public but silenced' do
|
||||
let(:silenced_at) { Time.now.utc }
|
||||
|
||||
it 'returns private' do
|
||||
expect(subject.compute_searchability).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-public_unlisted but silenced' do
|
||||
let(:silenced_at) { Time.now.utc }
|
||||
let(:status_searchability) { :public_unlisted }
|
||||
|
||||
it 'returns private' do
|
||||
expect(subject.compute_searchability).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-public_unlisted' do
|
||||
let(:status_searchability) { :public_unlisted }
|
||||
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
|
||||
it 'returns public_unlisted for local' do
|
||||
expect(subject.compute_searchability_local).to eq 'public_unlisted'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-private' do
|
||||
let(:status_searchability) { :private }
|
||||
|
||||
it 'returns private' do
|
||||
expect(subject.compute_searchability).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-direct' do
|
||||
let(:status_searchability) { :direct }
|
||||
|
||||
it 'returns direct' do
|
||||
expect(subject.compute_searchability).to eq 'direct'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when private-public' do
|
||||
let(:account_searchability) { :private }
|
||||
|
||||
it 'returns private' do
|
||||
expect(subject.compute_searchability).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct-public' do
|
||||
let(:account_searchability) { :direct }
|
||||
|
||||
it 'returns direct' do
|
||||
expect(subject.compute_searchability).to eq 'direct'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when limited-public' do
|
||||
let(:account_searchability) { :limited }
|
||||
|
||||
it 'returns limited' do
|
||||
expect(subject.compute_searchability).to eq 'limited'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when private-limited' do
|
||||
let(:account_searchability) { :private }
|
||||
let(:status_searchability) { :limited }
|
||||
|
||||
it 'returns limited' do
|
||||
expect(subject.compute_searchability).to eq 'limited'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when private-public of local account' do
|
||||
let(:account_searchability) { :private }
|
||||
let(:account_domain) { nil }
|
||||
let(:status_searchability) { :public }
|
||||
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct-public of local account' do
|
||||
let(:account_searchability) { :direct }
|
||||
let(:account_domain) { nil }
|
||||
let(:status_searchability) { :public }
|
||||
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when limited-public of local account' do
|
||||
let(:account_searchability) { :limited }
|
||||
let(:account_domain) { nil }
|
||||
let(:status_searchability) { :public }
|
||||
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when public-public_unlisted of local account' do
|
||||
let(:account_searchability) { :public }
|
||||
let(:account_domain) { nil }
|
||||
let(:status_searchability) { :public_unlisted }
|
||||
|
||||
it 'returns public' do
|
||||
expect(subject.compute_searchability).to eq 'public'
|
||||
end
|
||||
|
||||
it 'returns public_unlisted for local' do
|
||||
expect(subject.compute_searchability_local).to eq 'public_unlisted'
|
||||
end
|
||||
|
||||
it 'returns private for activitypub' do
|
||||
expect(subject.compute_searchability_activitypub).to eq 'private'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,9 +5,7 @@ require 'rails_helper'
|
|||
RSpec.describe 'Admin::EmailDomainBlocks' do
|
||||
let(:current_user) { Fabricate(:admin_user) }
|
||||
|
||||
before do
|
||||
sign_in current_user
|
||||
end
|
||||
before { sign_in current_user }
|
||||
|
||||
describe 'Performing batch updates' do
|
||||
before do
|
||||
|
@ -22,6 +20,27 @@ RSpec.describe 'Admin::EmailDomainBlocks' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with a selected block' do
|
||||
let!(:email_domain_block) { Fabricate :email_domain_block }
|
||||
|
||||
it 'deletes the block' do
|
||||
visit admin_email_domain_blocks_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_delete }
|
||||
.to change(EmailDomainBlock, :count).by(-1)
|
||||
expect { email_domain_block.reload }
|
||||
.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
def check_item
|
||||
within '.batch-table__row' do
|
||||
find('input[type=checkbox]').check
|
||||
end
|
||||
end
|
||||
|
||||
def button_for_delete
|
||||
I18n.t('admin.email_domain_blocks.delete')
|
||||
end
|
||||
|
|
18
spec/system/admin/follow_recommendations_spec.rb
Normal file
18
spec/system/admin/follow_recommendations_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Admin Follow Recommendations' do
|
||||
let(:user) { Fabricate(:admin_user) }
|
||||
|
||||
before { sign_in(user) }
|
||||
|
||||
describe 'Viewing follow recommendations details' do
|
||||
it 'shows a list of accounts' do
|
||||
visit admin_follow_recommendations_path
|
||||
|
||||
expect(page)
|
||||
.to have_content(I18n.t('admin.follow_recommendations.title'))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -48,6 +48,27 @@ RSpec.describe 'Admin::IpBlocks' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with a selected block' do
|
||||
let!(:ip_block) { Fabricate :ip_block }
|
||||
|
||||
it 'deletes the block' do
|
||||
visit admin_ip_blocks_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_delete }
|
||||
.to change(IpBlock, :count).by(-1)
|
||||
expect { ip_block.reload }
|
||||
.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
def check_item
|
||||
within '.batch-table__row' do
|
||||
find('input[type=checkbox]').check
|
||||
end
|
||||
end
|
||||
|
||||
def button_for_delete
|
||||
I18n.t('admin.ip_blocks.delete')
|
||||
end
|
||||
|
|
21
spec/system/admin/terms_of_service/histories_spec.rb
Normal file
21
spec/system/admin/terms_of_service/histories_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Admin Terms of Service Histories' do
|
||||
let(:current_user) { Fabricate(:admin_user) }
|
||||
|
||||
before { sign_in(current_user) }
|
||||
|
||||
describe 'Viewing TOS histories' do
|
||||
before { Fabricate :terms_of_service, changelog: 'The changelog notes from v1 are here' }
|
||||
|
||||
it 'shows previous terms versions' do
|
||||
visit admin_terms_of_service_history_path
|
||||
|
||||
expect(page)
|
||||
.to have_content(I18n.t('admin.terms_of_service.history'))
|
||||
.and have_content(/changelog notes from v1/)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,20 +5,49 @@ require 'rails_helper'
|
|||
RSpec.describe 'Admin::Trends::Links::PreviewCardProviders' do
|
||||
let(:current_user) { Fabricate(:admin_user) }
|
||||
|
||||
before do
|
||||
sign_in current_user
|
||||
end
|
||||
before { sign_in current_user }
|
||||
|
||||
describe 'Performing batch updates' do
|
||||
before do
|
||||
visit admin_trends_links_preview_card_providers_path
|
||||
end
|
||||
|
||||
context 'without selecting any records' do
|
||||
it 'displays a notice about selection' do
|
||||
visit admin_trends_links_preview_card_providers_path
|
||||
|
||||
click_on button_for_allow
|
||||
|
||||
expect(page).to have_content(selection_error_text)
|
||||
expect(page)
|
||||
.to have_content(selection_error_text)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with providers that are not trendable' do
|
||||
let!(:provider) { Fabricate :preview_card_provider, trendable: false }
|
||||
|
||||
it 'allows the providers' do
|
||||
visit admin_trends_links_preview_card_providers_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_allow }
|
||||
.to change { provider.reload.trendable? }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with providers that are trendable' do
|
||||
let!(:provider) { Fabricate :preview_card_provider, trendable: true }
|
||||
|
||||
it 'disallows the providers' do
|
||||
visit admin_trends_links_preview_card_providers_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_disallow }
|
||||
.to change { provider.reload.trendable? }.from(true).to(false)
|
||||
end
|
||||
end
|
||||
|
||||
def check_item
|
||||
within '.batch-table__row' do
|
||||
find('input[type=checkbox]').check
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,6 +55,10 @@ RSpec.describe 'Admin::Trends::Links::PreviewCardProviders' do
|
|||
I18n.t('admin.trends.allow')
|
||||
end
|
||||
|
||||
def button_for_disallow
|
||||
I18n.t('admin.trends.disallow')
|
||||
end
|
||||
|
||||
def selection_error_text
|
||||
I18n.t('admin.trends.links.publishers.no_publisher_selected')
|
||||
end
|
||||
|
|
|
@ -5,20 +5,77 @@ require 'rails_helper'
|
|||
RSpec.describe 'Admin::Trends::Links' do
|
||||
let(:current_user) { Fabricate(:admin_user) }
|
||||
|
||||
before do
|
||||
sign_in current_user
|
||||
end
|
||||
before { sign_in current_user }
|
||||
|
||||
describe 'Performing batch updates' do
|
||||
before do
|
||||
visit admin_trends_links_path
|
||||
end
|
||||
|
||||
context 'without selecting any records' do
|
||||
it 'displays a notice about selection' do
|
||||
visit admin_trends_links_path
|
||||
|
||||
click_on button_for_allow
|
||||
|
||||
expect(page).to have_content(selection_error_text)
|
||||
expect(page)
|
||||
.to have_content(selection_error_text)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with links that are not trendable' do
|
||||
let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, trendable: false) }
|
||||
|
||||
it 'allows the links' do
|
||||
visit admin_trends_links_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_allow }
|
||||
.to change { preview_card_trend.preview_card.reload.trendable? }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with links whose providers are not trendable' do
|
||||
let(:preview_card_provider) { Fabricate :preview_card_provider, trendable: false }
|
||||
let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, url: "https://#{preview_card_provider.domain}/page") }
|
||||
|
||||
it 'allows the providers of the links' do
|
||||
visit admin_trends_links_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_allow_providers }
|
||||
.to change { preview_card_trend.preview_card.provider.reload.trendable? }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with links that are trendable' do
|
||||
let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, trendable: true) }
|
||||
|
||||
it 'disallows the links' do
|
||||
visit admin_trends_links_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_disallow }
|
||||
.to change { preview_card_trend.preview_card.reload.trendable? }.from(true).to(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with links whose providers are trendable' do
|
||||
let(:preview_card_provider) { Fabricate :preview_card_provider, trendable: true }
|
||||
let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, url: "https://#{preview_card_provider.domain}/page") }
|
||||
|
||||
it 'disallows the links' do
|
||||
visit admin_trends_links_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_disallow_providers }
|
||||
.to change { preview_card_trend.preview_card.provider.reload.trendable? }.from(true).to(false)
|
||||
end
|
||||
end
|
||||
|
||||
def check_item
|
||||
within '.batch-table__row' do
|
||||
find('input[type=checkbox]').check
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,6 +83,18 @@ RSpec.describe 'Admin::Trends::Links' do
|
|||
I18n.t('admin.trends.links.allow')
|
||||
end
|
||||
|
||||
def button_for_allow_providers
|
||||
I18n.t('admin.trends.links.allow_provider')
|
||||
end
|
||||
|
||||
def button_for_disallow
|
||||
I18n.t('admin.trends.links.disallow')
|
||||
end
|
||||
|
||||
def button_for_disallow_providers
|
||||
I18n.t('admin.trends.links.disallow_provider')
|
||||
end
|
||||
|
||||
def selection_error_text
|
||||
I18n.t('admin.trends.links.no_link_selected')
|
||||
end
|
||||
|
|
|
@ -5,20 +5,75 @@ require 'rails_helper'
|
|||
RSpec.describe 'Admin::Trends::Statuses' do
|
||||
let(:current_user) { Fabricate(:admin_user) }
|
||||
|
||||
before do
|
||||
sign_in current_user
|
||||
end
|
||||
before { sign_in current_user }
|
||||
|
||||
describe 'Performing batch updates' do
|
||||
before do
|
||||
visit admin_trends_statuses_path
|
||||
end
|
||||
|
||||
context 'without selecting any records' do
|
||||
it 'displays a notice about selection' do
|
||||
visit admin_trends_statuses_path
|
||||
|
||||
click_on button_for_allow
|
||||
|
||||
expect(page).to have_content(selection_error_text)
|
||||
expect(page)
|
||||
.to have_content(selection_error_text)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with statuses that are not trendable' do
|
||||
let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, trendable: false) }
|
||||
|
||||
it 'allows the statuses' do
|
||||
visit admin_trends_statuses_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_allow }
|
||||
.to change { status_trend.status.reload.trendable? }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with statuses whose accounts are not trendable' do
|
||||
let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, account: Fabricate(:account, trendable: false)) }
|
||||
|
||||
it 'allows the accounts of the statuses' do
|
||||
visit admin_trends_statuses_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_allow_accounts }
|
||||
.to change { status_trend.status.account.reload.trendable? }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with statuses that are trendable' do
|
||||
let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, trendable: true) }
|
||||
|
||||
it 'disallows the statuses' do
|
||||
visit admin_trends_statuses_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_disallow }
|
||||
.to change { status_trend.status.reload.trendable? }.from(true).to(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with statuses whose accounts are trendable' do
|
||||
let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, account: Fabricate(:account, trendable: true)) }
|
||||
|
||||
it 'disallows the statuses' do
|
||||
visit admin_trends_statuses_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_disallow_accounts }
|
||||
.to change { status_trend.status.reload.trendable? }.from(true).to(false)
|
||||
end
|
||||
end
|
||||
|
||||
def check_item
|
||||
within '.batch-table__row' do
|
||||
find('input[type=checkbox]').check
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,6 +81,18 @@ RSpec.describe 'Admin::Trends::Statuses' do
|
|||
I18n.t('admin.trends.statuses.allow')
|
||||
end
|
||||
|
||||
def button_for_allow_accounts
|
||||
I18n.t('admin.trends.statuses.allow_account')
|
||||
end
|
||||
|
||||
def button_for_disallow
|
||||
I18n.t('admin.trends.statuses.disallow')
|
||||
end
|
||||
|
||||
def button_for_disallow_accounts
|
||||
I18n.t('admin.trends.statuses.disallow_account')
|
||||
end
|
||||
|
||||
def selection_error_text
|
||||
I18n.t('admin.trends.statuses.no_status_selected')
|
||||
end
|
||||
|
|
|
@ -5,27 +5,59 @@ require 'rails_helper'
|
|||
RSpec.describe 'Admin::Trends::Tags' do
|
||||
let(:current_user) { Fabricate(:admin_user) }
|
||||
|
||||
before do
|
||||
sign_in current_user
|
||||
end
|
||||
before { sign_in current_user }
|
||||
|
||||
describe 'Performing batch updates' do
|
||||
before do
|
||||
visit admin_trends_tags_path
|
||||
end
|
||||
|
||||
context 'without selecting any records' do
|
||||
it 'displays a notice about selection' do
|
||||
visit admin_trends_tags_path
|
||||
|
||||
click_on button_for_allow
|
||||
|
||||
expect(page).to have_content(selection_error_text)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with tags that are not trendable' do
|
||||
let!(:tag_trend) { Fabricate :tag_trend, tag: Fabricate(:tag, trendable: false) }
|
||||
|
||||
it 'allows the tags' do
|
||||
visit admin_trends_tags_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_allow }
|
||||
.to change { tag_trend.tag.reload.trendable? }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with tags that are trendable' do
|
||||
let!(:tag_trend) { Fabricate :tag_trend, tag: Fabricate(:tag, trendable: true) }
|
||||
|
||||
it 'disallows the tags' do
|
||||
visit admin_trends_tags_path
|
||||
|
||||
check_item
|
||||
|
||||
expect { click_on button_for_disallow }
|
||||
.to change { tag_trend.tag.reload.trendable? }.from(true).to(false)
|
||||
end
|
||||
end
|
||||
|
||||
def check_item
|
||||
within '.batch-table__row' do
|
||||
find('input[type=checkbox]').check
|
||||
end
|
||||
end
|
||||
|
||||
def button_for_allow
|
||||
I18n.t('admin.trends.allow')
|
||||
end
|
||||
|
||||
def button_for_disallow
|
||||
I18n.t('admin.trends.disallow')
|
||||
end
|
||||
|
||||
def selection_error_text
|
||||
I18n.t('admin.trends.tags.no_tag_selected')
|
||||
end
|
||||
|
|
30
spec/system/auth/setup_spec.rb
Normal file
30
spec/system/auth/setup_spec.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Auth Setup' do
|
||||
context 'with an unconfirmed signed in user' do
|
||||
let(:user) { Fabricate(:user, confirmed_at: nil) }
|
||||
|
||||
before { sign_in(user) }
|
||||
|
||||
it 'can update email address' do
|
||||
visit auth_setup_path
|
||||
|
||||
expect(page)
|
||||
.to have_content(I18n.t('auth.setup.title'))
|
||||
|
||||
find('summary.lead').click
|
||||
fill_in 'user_email', with: 'new-email@example.host'
|
||||
|
||||
expect { submit_form }
|
||||
.to(change { user.reload.unconfirmed_email })
|
||||
expect(page)
|
||||
.to have_content(I18n.t('auth.setup.new_confirmation_instructions_sent'))
|
||||
end
|
||||
|
||||
def submit_form
|
||||
find('[name=button]').click
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,21 +5,36 @@ require 'rails_helper'
|
|||
RSpec.describe Web::PushNotificationWorker do
|
||||
subject { described_class.new }
|
||||
|
||||
let(:p256dh) { 'BN4GvZtEZiZuqFxSKVZfSfluwKBD7UxHNBmWkfiZfCtgDE8Bwh-_MtLXbBxTBAWH9r7IPKL0lhdcaqtL1dfxU5E=' }
|
||||
let(:auth) { 'Q2BoAjC09xH3ywDLNJr-dA==' }
|
||||
let(:endpoint) { 'https://updates.push.services.mozilla.com/push/v1/subscription-id' }
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:notification) { Fabricate(:notification) }
|
||||
let(:subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: p256dh, key_auth: auth, endpoint: endpoint, data: { alerts: { notification.type => true } }) }
|
||||
let(:vapid_public_key) { 'BB37UCyc8LLX4PNQSe-04vSFvpUWGrENubUaslVFM_l5TxcGVMY0C3RXPeUJAQHKYlcOM2P4vTYmkoo0VZGZTM4=' }
|
||||
let(:vapid_private_key) { 'OPrw1Sum3gRoL4-DXfSCC266r-qfFSRZrnj8MgIhRHg=' }
|
||||
let(:vapid_key) { Webpush::VapidKey.from_keys(vapid_public_key, vapid_private_key) }
|
||||
let(:contact_email) { 'sender@example.com' }
|
||||
let(:ciphertext) { "+\xB8\xDBT}\x13\xB6\xDD.\xF9\xB0\xA7\xC8\xD2\x80\xFD\x99#\xF7\xAC\x83\xA4\xDB,\x1F\xB5\xB9w\x85>\xF7\xADr" }
|
||||
let(:salt) { "X\x97\x953\xE4X\xF8_w\xE7T\x95\xC51q\xFE" }
|
||||
let(:server_public_key) { "\x04\b-RK9w\xDD$\x16lFz\xF9=\xB4~\xC6\x12k\xF3\xF40t\xA9\xC1\fR\xC3\x81\x80\xAC\f\x7F\xE4\xCC\x8E\xC2\x88 n\x8BB\xF1\x9C\x14\a\xFA\x8D\xC9\x80\xA1\xDDyU\\&c\x01\x88#\x118Ua" }
|
||||
let(:shared_secret) { "\t\xA7&\x85\t\xC5m\b\xA8\xA7\xF8B{1\xADk\xE1y'm\xEDE\xEC\xDD\xEDj\xB3$s\xA9\xDA\xF0" }
|
||||
let(:payload) { { ciphertext: ciphertext, salt: salt, server_public_key: server_public_key, shared_secret: shared_secret } }
|
||||
|
||||
# Legacy values
|
||||
let(:p256dh) { 'BN4GvZtEZiZuqFxSKVZfSfluwKBD7UxHNBmWkfiZfCtgDE8Bwh-_MtLXbBxTBAWH9r7IPKL0lhdcaqtL1dfxU5E=' }
|
||||
let(:auth) { 'Q2BoAjC09xH3ywDLNJr-dA==' }
|
||||
let(:legacy_subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: p256dh, key_auth: auth, endpoint: endpoint, data: { alerts: { notification.type => true } }) }
|
||||
let(:legacy_payload) do
|
||||
{
|
||||
ciphertext: "+\xB8\xDBT}\x13\xB6\xDD.\xF9\xB0\xA7\xC8\xD2\x80\xFD\x99#\xF7\xAC\x83\xA4\xDB,\x1F\xB5\xB9w\x85>\xF7\xADr",
|
||||
salt: "X\x97\x953\xE4X\xF8_w\xE7T\x95\xC51q\xFE",
|
||||
server_public_key: "\x04\b-RK9w\xDD$\x16lFz\xF9=\xB4~\xC6\x12k\xF3\xF40t\xA9\xC1\fR\xC3\x81\x80\xAC\f\x7F\xE4\xCC\x8E\xC2\x88 n\x8BB\xF1\x9C\x14\a\xFA\x8D\xC9\x80\xA1\xDDyU\\&c\x01\x88#\x118Ua",
|
||||
shared_secret: "\t\xA7&\x85\t\xC5m\b\xA8\xA7\xF8B{1\xADk\xE1y'm\xEDE\xEC\xDD\xEDj\xB3$s\xA9\xDA\xF0",
|
||||
}
|
||||
end
|
||||
|
||||
# Standard values, from RFC8291
|
||||
let(:std_p256dh) { 'BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4' }
|
||||
let(:std_auth) { 'BTBZMqHH6r4Tts7J_aSIgg' }
|
||||
let(:std_as_public) { 'BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8' }
|
||||
let(:std_as_private) { 'yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw' }
|
||||
let(:std_salt) { 'DGv6ra1nlYgDCS1FRnbzlw' }
|
||||
let(:std_subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: std_p256dh, key_auth: std_auth, endpoint: endpoint, standard: true, data: { alerts: { notification.type => true } }) }
|
||||
let(:std_input) { 'When I grow up, I want to be a watermelon' }
|
||||
let(:std_ciphertext) { 'DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPTpK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN' }
|
||||
|
||||
describe 'perform' do
|
||||
around do |example|
|
||||
|
@ -35,20 +50,40 @@ RSpec.describe Web::PushNotificationWorker do
|
|||
before do
|
||||
Setting.site_contact_email = contact_email
|
||||
|
||||
allow(Webpush::Encryption).to receive(:encrypt).and_return(payload)
|
||||
allow(JWT).to receive(:encode).and_return('jwt.encoded.payload')
|
||||
|
||||
stub_request(:post, endpoint).to_return(status: 201, body: '')
|
||||
end
|
||||
|
||||
it 'calls the relevant service with the correct headers' do
|
||||
subject.perform(subscription.id, notification.id)
|
||||
it 'Legacy push calls the relevant service with the legacy headers' do
|
||||
allow(Webpush::Legacy::Encryption).to receive(:encrypt).and_return(legacy_payload)
|
||||
|
||||
expect(web_push_endpoint_request)
|
||||
subject.perform(legacy_subscription.id, notification.id)
|
||||
|
||||
expect(legacy_web_push_endpoint_request)
|
||||
.to have_been_made
|
||||
end
|
||||
|
||||
def web_push_endpoint_request
|
||||
# We allow subject stub to encrypt the same input than the RFC8291 example
|
||||
# rubocop:disable RSpec/SubjectStub
|
||||
it 'Standard push calls the relevant service with the standard headers' do
|
||||
# Mock server keys to match RFC example
|
||||
allow(OpenSSL::PKey::EC).to receive(:generate).and_return(std_as_keys)
|
||||
# Mock the random salt to match RFC example
|
||||
rand = Random.new
|
||||
allow(Random).to receive(:new).and_return(rand)
|
||||
allow(rand).to receive(:bytes).and_return(Webpush.decode64(std_salt))
|
||||
# Mock input to match RFC example
|
||||
allow(subject).to receive(:push_notification_json).and_return(std_input)
|
||||
|
||||
subject.perform(std_subscription.id, notification.id)
|
||||
|
||||
expect(standard_web_push_endpoint_request)
|
||||
.to have_been_made
|
||||
end
|
||||
# rubocop:enable RSpec/SubjectStub
|
||||
|
||||
def legacy_web_push_endpoint_request
|
||||
a_request(
|
||||
:post,
|
||||
endpoint
|
||||
|
@ -66,5 +101,28 @@ RSpec.describe Web::PushNotificationWorker do
|
|||
body: "+\xB8\xDBT}\u0013\xB6\xDD.\xF9\xB0\xA7\xC8Ҁ\xFD\x99#\xF7\xAC\x83\xA4\xDB,\u001F\xB5\xB9w\x85>\xF7\xADr"
|
||||
)
|
||||
end
|
||||
|
||||
def standard_web_push_endpoint_request
|
||||
a_request(
|
||||
:post,
|
||||
endpoint
|
||||
).with(
|
||||
headers: {
|
||||
'Content-Encoding' => 'aes128gcm',
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Ttl' => '172800',
|
||||
'Urgency' => 'normal',
|
||||
'Authorization' => "vapid t=jwt.encoded.payload,k=#{vapid_public_key.delete('=')}",
|
||||
'Unsubscribe-URL' => %r{/api/web/push_subscriptions/},
|
||||
},
|
||||
body: Webpush.decode64(std_ciphertext)
|
||||
)
|
||||
end
|
||||
|
||||
def std_as_keys
|
||||
# VapidKey contains a method to retrieve EC keypair from
|
||||
# B64 raw keys, the keypair is stored in curve field
|
||||
Webpush::VapidKey.from_keys(std_as_public, std_as_private).curve
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue