mirror of
https://github.com/glitch-soc/mastodon
synced 2025-04-24 20:04:51 +00:00
Merge pull request #3017 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to e28b64ac2d
This commit is contained in:
commit
51a8c6c3f7
18 changed files with 439 additions and 259 deletions
|
@ -504,6 +504,7 @@
|
|||
"navigation_bar.follows_and_followers": "Падпіскі і падпісчыкі",
|
||||
"navigation_bar.lists": "Спісы",
|
||||
"navigation_bar.logout": "Выйсці",
|
||||
"navigation_bar.moderation": "Мадэрацыя",
|
||||
"navigation_bar.mutes": "Ігнараваныя карыстальнікі",
|
||||
"navigation_bar.opened_in_classic_interface": "Допісы, уліковыя запісы і іншыя спецыфічныя старонкі па змоўчанні адчыняюцца ў класічным вэб-інтэрфейсе.",
|
||||
"navigation_bar.personal": "Асабістае",
|
||||
|
|
|
@ -669,7 +669,7 @@
|
|||
"notifications_permission_banner.title": "Nenechte si nic uniknout",
|
||||
"onboarding.follows.back": "Zpět",
|
||||
"onboarding.follows.done": "Hotovo",
|
||||
"onboarding.follows.empty": "Bohužel, žádné výsledky nelze momentálně zobrazit. Můžete zkusit vyhledat nebo procházet stránku s průzkumem a najít lidi, kteří budou sledovat, nebo to zkuste znovu později.",
|
||||
"onboarding.follows.empty": "Bohužel, žádné výsledky nelze momentálně zobrazit. Můžete zkusit najít uživatele ke sledování za pomocí vyhledávání nebo na stránce „Objevit“, nebo to zkuste znovu později.",
|
||||
"onboarding.follows.search": "Hledat",
|
||||
"onboarding.follows.title": "Sledujte lidi a začněte",
|
||||
"onboarding.profile.discoverable": "Udělat svůj profil vyhledatelným",
|
||||
|
|
|
@ -10,7 +10,7 @@ class AccountReachFinder
|
|||
end
|
||||
|
||||
def inboxes
|
||||
(followers_inboxes + reporters_inboxes + recently_mentioned_inboxes + relay_inboxes).uniq
|
||||
(followers_inboxes + reporters_inboxes + recently_mentioned_inboxes + recently_followed_inboxes + recently_requested_inboxes + relay_inboxes).uniq
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -31,13 +31,32 @@ class AccountReachFinder
|
|||
.take(RECENT_LIMIT)
|
||||
end
|
||||
|
||||
def recently_followed_inboxes
|
||||
@account
|
||||
.following
|
||||
.where(follows: { created_at: recent_date_cutoff... })
|
||||
.inboxes
|
||||
.take(RECENT_LIMIT)
|
||||
end
|
||||
|
||||
def recently_requested_inboxes
|
||||
Account
|
||||
.where(id: @account.follow_requests.where({ created_at: recent_date_cutoff... }).select(:target_account_id))
|
||||
.inboxes
|
||||
.take(RECENT_LIMIT)
|
||||
end
|
||||
|
||||
def relay_inboxes
|
||||
Relay.enabled.pluck(:inbox_url)
|
||||
end
|
||||
|
||||
def oldest_status_id
|
||||
Mastodon::Snowflake
|
||||
.id_at(STATUS_SINCE.ago, with_random: false)
|
||||
.id_at(recent_date_cutoff, with_random: false)
|
||||
end
|
||||
|
||||
def recent_date_cutoff
|
||||
@account.suspended? && @account.suspension_origin_local? ? @account.suspended_at - STATUS_SINCE : STATUS_SINCE.ago
|
||||
end
|
||||
|
||||
def recent_statuses
|
||||
|
|
|
@ -429,7 +429,7 @@ lv:
|
|||
obfuscate: Apslēpt domēna vārdu
|
||||
obfuscate_hint: Daļēji apslēpt domēna nosaukumu sarakstā, ja ir iespējota domēna ierobežojumu saraksta reklamēšana
|
||||
private_comment: Privāts komentārs
|
||||
private_comment_hint: Atstāj komentāru par šo domēna ierobežojumu moderatoru iekšējai lietošanai.
|
||||
private_comment_hint: Atstāt piebildi par šo domēna ierobežojumu satura pārraudzītāju iekšējai lietošanai.
|
||||
public_comment: Publisks komentārs
|
||||
public_comment_hint: Piebilde par šo domēna ierobežojumu vispārējai sabiedrībai, ja ir iespējota domēnu ierobežojumu saraksta reklamēšana.
|
||||
reject_media: Noraidīt multivides failus
|
||||
|
@ -647,7 +647,7 @@ lv:
|
|||
delete: Dzēst
|
||||
placeholder: Jāapraksta veiktās darbības vai jebkuri citi saistītie atjauninājumi...
|
||||
title: Piezīmes
|
||||
notes_description_html: Skati un atstāj piezīmes citiem moderatoriem un sev nākotnei
|
||||
notes_description_html: Apskatīt un atstāt piezīmes citiem satura pārraudzītājiem un sev nākotnei
|
||||
processed_msg: 'Pārskats #%{id} veiksmīgi apstrādāts'
|
||||
quick_actions_description_html: 'Veic ātro darbību vai ritini uz leju, lai skatītu saturu, par kuru ziņots:'
|
||||
remote_user_placeholder: attālais lietotājs no %{instance}
|
||||
|
@ -1048,6 +1048,8 @@ lv:
|
|||
title: Tīmekļa āķi
|
||||
webhook: Tīmekļa āķis
|
||||
admin_mailer:
|
||||
auto_close_registrations:
|
||||
body: Nesenu satura pārraudzības darbību trūkuma dēļ reģistrācija %{instance} ir automātiski pārslēgta nepieciešamība pēc pašrocīgas izskatīšanas, lai novērstu %{instance} izmantošana kā platformu iespējami sliktiem dalībniekiem. Jebkurā brīdī var ieslēgt atpakaļ atvērtu reģistrēšanos.
|
||||
new_appeal:
|
||||
actions:
|
||||
delete_statuses: lai izdzēstu viņu ierakstus
|
||||
|
@ -1166,8 +1168,8 @@ lv:
|
|||
accept: Pieņemt
|
||||
back: Atpakaļ
|
||||
invited_by: 'Tu vari pievienoties %{domain}, pateicoties uzaicinājumam, ko saņēmi no:'
|
||||
preamble: Tos iestata un ievieš %{domain} moderatori.
|
||||
preamble_invited: Pirms turpināt, lūdzu, apsver galvenos noteikumus, ko noteikuši %{domain} moderatori.
|
||||
preamble: Tos iestata un ievieš %{domain} satura pārraudzītāji.
|
||||
preamble_invited: Pirms turpināt, lūgums apsvērt pamatnoteikumus, kurus norādījuši %{domain} satura pārraudzītāji.
|
||||
title: Daži pamatnoteikumi.
|
||||
title_invited: Tu esi uzaicināts.
|
||||
security: Drošība
|
||||
|
@ -1181,7 +1183,7 @@ lv:
|
|||
preamble_html: Jāpiesakās ar saviem <strong>%{domain}</strong> piekļuves datiem. Ja konts tiek mitināts citā serverī, šeit nevarēs pieteikties.
|
||||
title: Pieteikties %{domain}
|
||||
sign_up:
|
||||
manual_review: Reģistrācijas domēnā %{domain} manuāli pārbauda mūsu moderatori. Lai palīdzētu mums apstrādāt tavu reģistrāciju, uzraksti mazliet par sevi un to, kāpēc vēlies kontu %{domain}.
|
||||
manual_review: Reģistrāciju %{domain} pašrocīgi izskata mūsu satura pārraudzītāji. Lai palīdzētu mums apstrādāt Tavu reģistrāciju, uzraksti mazliet par sevi un to, kāpēc vēlies kontu %{domain}!
|
||||
title: Atļauj tevi iestatīt %{domain}.
|
||||
status:
|
||||
account_status: Konta statuss
|
||||
|
@ -1730,6 +1732,7 @@ lv:
|
|||
user_domain_block: Jūs bloķējāt %{target_name}
|
||||
lost_followers: Zaudētie sekotāji
|
||||
lost_follows: Zaudētie sekojumi
|
||||
preamble: Tu vari zaudēt sekojamos un sekotājus, kad liedz domēnu vai kad satura pārraudzītāji izlemj apturēt attālu serveri. Kad t as notiek, būs iespējams lejupielādēt sarakstus ar pārtrauktajām saiknēm, kurus tad var izpētīt un, iespējams, ievietot citā serverī.
|
||||
type: Notikums
|
||||
statuses:
|
||||
attached:
|
||||
|
@ -1878,9 +1881,9 @@ lv:
|
|||
spam: Spams
|
||||
violation: Saturs pārkāpj šādas kopienas pamatnostādnes
|
||||
explanation:
|
||||
delete_statuses: Tika konstatēts, ka dažas no tavām ziņām pārkāpj vienu vai vairākas kopienas vadlīnijas, un rezultātā %{instance} moderatori tās noņēma.
|
||||
delete_statuses: Tika noteikts, ka daži no Taviem ierakstiem pārkāpj vienu vai vairākas kopienas vadlīnijas, tādējādi tos noņēma %{instance} satura pārraudzītāji.
|
||||
disable: Tu vairs nevari izmantot savu kontu, taču tavs profils un citi dati paliek neskarti. Tu vari pieprasīt savu datu dublējumu, mainīt konta iestatījumus vai dzēst kontu.
|
||||
mark_statuses_as_sensitive: "%{instance} satura pārraudzītāji dažus no Taviem ierakstiem ir atzīmējuši kā jūtīgus. Tas nozīmē, ka cilvēkiem būs jāpiesit ierakstos esošajiem informāijas nesējiem, pirms tiek attēlots priekšskatījums. Tu pats vari atzīmēt informācijas nesēju kā jūtīgu, kad nākotnē tādu ievietosi."
|
||||
mark_statuses_as_sensitive: "%{instance} satura pārraudzītāji dažus no Taviem ierakstiem ir atzīmējuši kā jūtīgus. Tas nozīmē, ka cilvēkiem būs jāpiesit ierakstos esošajiem informāijas nesējiem, pirms tiek attēlots to priekšskatījums. Tu pats vari atzīmēt informācijas nesēju kā jūtīgu, kad nākotnē tādu ievietosi."
|
||||
sensitive: Turpmāk visi augšupielādētās informācijas nesēju datnes tiks atzīmētas kā jūtīgas un paslēptas aiz klikšķināma brīdinājuma.
|
||||
silence: Tu joprojām vari izmantot savu kontu, taču tikai tie cilvēki, kuri jau tev seko, redzēs tavas ziņas šajā serverī, un tev var tikt liegtas dažādas atklāšanas funkcijas. Tomēr citi joprojām var tev manuāli sekot.
|
||||
suspend: Tu vairs nevari izmantot savu kontu, un tavs profils un citi dati vairs nav pieejami. Tu joprojām vari pieteikties, lai pieprasītu savu datu dublēšanu, līdz dati tiks pilnībā noņemti aptuveni 30 dienu laikā, taču mēs saglabāsim dažus pamata datus, lai neļautu tev izvairīties no apturēšanas.
|
||||
|
|
|
@ -75,6 +75,7 @@ fr-CA:
|
|||
filters:
|
||||
action: Choisir l'action à effectuer quand un message correspond au filtre
|
||||
actions:
|
||||
blur: Cacher les médias derrière un avertissement, sans cacher le texte
|
||||
hide: Cacher complètement le contenu filtré, faire comme s'il n'existait pas
|
||||
warn: Cacher le contenu filtré derrière un avertissement mentionnant le nom du filtre
|
||||
form_admin_settings:
|
||||
|
@ -260,6 +261,7 @@ fr-CA:
|
|||
name: Mot-clic
|
||||
filters:
|
||||
actions:
|
||||
blur: Masquer les médias derrière un avertissement
|
||||
hide: Cacher complètement
|
||||
warn: Cacher derrière un avertissement
|
||||
form_admin_settings:
|
||||
|
|
|
@ -75,6 +75,7 @@ fr:
|
|||
filters:
|
||||
action: Choisir l'action à effectuer quand un message correspond au filtre
|
||||
actions:
|
||||
blur: Cacher les médias derrière un avertissement, sans cacher le texte
|
||||
hide: Cacher complètement le contenu filtré, faire comme s'il n'existait pas
|
||||
warn: Cacher le contenu filtré derrière un avertissement mentionnant le nom du filtre
|
||||
form_admin_settings:
|
||||
|
@ -260,6 +261,7 @@ fr:
|
|||
name: Hashtag
|
||||
filters:
|
||||
actions:
|
||||
blur: Masquer les médias derrière un avertissement
|
||||
hide: Cacher complètement
|
||||
warn: Cacher derrière un avertissement
|
||||
form_admin_settings:
|
||||
|
|
|
@ -75,6 +75,7 @@ gl:
|
|||
filters:
|
||||
action: Elixe a acción a realizar cando algunha publicación coincida co filtro
|
||||
actions:
|
||||
blur: Ocultar multimedia detrás dun aviso, sen ocultar o texto que se inclúa
|
||||
hide: Agochar todo o contido filtrado, facer coma se non existise
|
||||
warn: Agochar o contido filtrado tras un aviso que conteña o nome do filtro
|
||||
form_admin_settings:
|
||||
|
@ -260,6 +261,7 @@ gl:
|
|||
name: Cancelo
|
||||
filters:
|
||||
actions:
|
||||
blur: Ocultar multimedia cun aviso
|
||||
hide: Agochar completamente
|
||||
warn: Agochar tras un aviso
|
||||
form_admin_settings:
|
||||
|
|
|
@ -26,7 +26,7 @@ lv:
|
|||
types:
|
||||
disable: Neļauj lietotājam izmantot savu kontu, bet neizdzēs vai neslēp tā saturu.
|
||||
none: Izmanto šo, lai nosūtītu lietotājam brīdinājumu, neradot nekādas citas darbības.
|
||||
sensitive: Piespiest visus šī lietotāja multivides pielikumus atzīmēt kā sensitīvus.
|
||||
sensitive: Visus šī lietotāja informācijas nesēju pielikumus uzspiesti atzīmēt kā jūtīgus.
|
||||
silence: Neļaut lietotājam veikt ierakstus ar publisku redzamību, paslēpt viņa ierakstus un paziņojumus no cilvēkiem, kas tam neseko. Tiek aizvērti visi ziņojumi par šo kontu.
|
||||
suspend: Novērs jebkādu mijiedarbību no šī konta vai uz to un dzēs tā saturu. Atgriežams 30 dienu laikā. Tiek aizvērti visi šī konta pārskati.
|
||||
warning_preset_id: Neobligāts. Tu joprojām vari pievienot pielāgotu tekstu sākotnējās iestatīšanas beigās
|
||||
|
@ -56,8 +56,8 @@ lv:
|
|||
scopes: Kuriem API lietotnei būs ļauts piekļūt. Ja atlasa augstākā līmeņa tvērumu, nav nepieciešamas atlasīt atsevišķus.
|
||||
setting_aggregate_reblogs: Nerādīt jaunus izcēlumus ziņām, kas nesen tika palielinātas (ietekmē tikai nesen saņemtos palielinājumus)
|
||||
setting_always_send_emails: Parasti e-pasta paziņojumi netiek sūtīti, kad aktīvi izmantojat Mastodon
|
||||
setting_default_sensitive: Sensitīva multivide pēc noklusējuma ir paslēpti, un tos var atklāt, noklikšķinot
|
||||
setting_display_media_default: Paslēpt multividi, kas atzīmēta kā sensitīva
|
||||
setting_default_sensitive: Pēc noklusējuma jūtīgi informācijas nesēji ir paslēpti, un tos var atklāt ar klikšķi
|
||||
setting_display_media_default: Paslēpt informācijas nesējus, kas atzīmēti kā jūtīgi
|
||||
setting_display_media_hide_all: Vienmēr slēpt multividi
|
||||
setting_display_media_show_all: Vienmēr rādīt multividi
|
||||
setting_system_scrollbars_ui: Attiecas tikai uz darbvirsmas pārlūkiem, kuru pamatā ir Safari vai Chrome
|
||||
|
@ -179,7 +179,7 @@ lv:
|
|||
types:
|
||||
disable: Iesaldēt
|
||||
none: Nosūtīt brīdinājumu
|
||||
sensitive: Sensitīvs
|
||||
sensitive: Jūtīgs
|
||||
silence: Ierobežot
|
||||
suspend: Apturēt
|
||||
warning_preset_id: Lietot iepriekš iestatītus brīdinājumus
|
||||
|
@ -223,7 +223,7 @@ lv:
|
|||
setting_boost_modal: Rādīt apstiprinājuma dialogu pirms izcelšanas
|
||||
setting_default_language: Publicēšanas valoda
|
||||
setting_default_privacy: Publicēšanas privātums
|
||||
setting_default_sensitive: Atļaut atzīmēt multividi kā sensitīvu
|
||||
setting_default_sensitive: Vienmēr atzīmēt informācijas nesējus kā jūtīgus
|
||||
setting_delete_modal: Parādīt apstiprinājuma dialogu pirms ziņas dzēšanas
|
||||
setting_disable_hover_cards: Atspējot profila priekšskatījumu pēc kursora novietošanas
|
||||
setting_disable_swiping: Atspējot vilkšanas kustības
|
||||
|
|
|
@ -75,6 +75,7 @@ pt-PT:
|
|||
filters:
|
||||
action: Escolha qual a ação a executar quando uma publicação corresponde ao filtro
|
||||
actions:
|
||||
blur: Esconder multimédia com um aviso à frente, sem esconder o texto
|
||||
hide: Ocultar completamente o conteúdo filtrado, comportando-se como se não existisse
|
||||
warn: Ocultar o conteúdo filtrado por trás de um aviso mencionando o título do filtro
|
||||
form_admin_settings:
|
||||
|
@ -260,6 +261,7 @@ pt-PT:
|
|||
name: Etiqueta
|
||||
filters:
|
||||
actions:
|
||||
blur: Esconder multimédia com um aviso
|
||||
hide: Ocultar por completo
|
||||
warn: Ocultar com um aviso
|
||||
form_admin_settings:
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::InboxesController do
|
||||
let(:remote_account) { nil }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'with signature' do
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub) }
|
||||
|
||||
before do
|
||||
post :create, body: '{}'
|
||||
end
|
||||
|
||||
it 'returns http accepted' do
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
|
||||
context 'with a specific account' do
|
||||
subject(:response) { post :create, params: { account_username: account.username }, body: '{}' }
|
||||
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http accepted' do
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Collection-Synchronization header' do
|
||||
let(:remote_account) { Fabricate(:account, followers_url: 'https://example.com/followers', domain: 'example.com', uri: 'https://example.com/actor', protocol: :activitypub) }
|
||||
let(:synchronization_collection) { remote_account.followers_url }
|
||||
let(:synchronization_url) { 'https://example.com/followers-for-domain' }
|
||||
let(:synchronization_hash) { 'somehash' }
|
||||
let(:synchronization_header) { "collectionId=\"#{synchronization_collection}\", digest=\"#{synchronization_hash}\", url=\"#{synchronization_url}\"" }
|
||||
|
||||
before do
|
||||
allow(ActivityPub::FollowersSynchronizationWorker).to receive(:perform_async).and_return(nil)
|
||||
allow(remote_account).to receive(:local_followers_hash).and_return('somehash')
|
||||
|
||||
request.headers['Collection-Synchronization'] = synchronization_header
|
||||
post :create, body: '{}'
|
||||
end
|
||||
|
||||
context 'with mismatching target collection' do
|
||||
let(:synchronization_collection) { 'https://example.com/followers2' }
|
||||
|
||||
it 'does not start a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).to_not have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mismatching domain in partial collection attribute' do
|
||||
let(:synchronization_url) { 'https://example.org/followers' }
|
||||
|
||||
it 'does not start a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).to_not have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with matching digest' do
|
||||
it 'does not start a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).to_not have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mismatching digest' do
|
||||
let(:synchronization_hash) { 'wronghash' }
|
||||
|
||||
it 'starts a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).to have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns http accepted' do
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without signature' do
|
||||
before do
|
||||
post :create, body: '{}'
|
||||
end
|
||||
|
||||
it 'returns http not authorized' do
|
||||
expect(response).to have_http_status(401)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,13 +13,28 @@ RSpec.describe AccountReachFinder do
|
|||
let(:ap_mentioned_example_com) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-3', domain: 'example.com') }
|
||||
let(:ap_mentioned_example_org) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.org/inbox-4', domain: 'example.org') }
|
||||
|
||||
let(:ap_followed_example_com) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-5', domain: 'example.com') }
|
||||
let(:ap_followed_example_org) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-6', domain: 'example.org') }
|
||||
|
||||
let(:ap_requested_example_com) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-7', domain: 'example.com') }
|
||||
let(:ap_requested_example_org) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-8', domain: 'example.org') }
|
||||
|
||||
let(:unrelated_account) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/unrelated-inbox', domain: 'example.com') }
|
||||
let(:old_followed_account) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/old-followed-inbox', domain: 'example.com') }
|
||||
|
||||
before do
|
||||
travel_to(2.months.ago) { account.follow!(old_followed_account) }
|
||||
|
||||
ap_follower_example_com.follow!(account)
|
||||
ap_follower_example_org.follow!(account)
|
||||
ap_follower_with_shared.follow!(account)
|
||||
|
||||
account.follow!(ap_followed_example_com)
|
||||
account.follow!(ap_followed_example_org)
|
||||
|
||||
account.request_follow!(ap_requested_example_com)
|
||||
account.request_follow!(ap_requested_example_org)
|
||||
|
||||
Fabricate(:status, account: account).tap do |status|
|
||||
status.mentions << Mention.new(account: ap_follower_example_com)
|
||||
status.mentions << Mention.new(account: ap_mentioned_with_shared)
|
||||
|
@ -44,7 +59,10 @@ RSpec.describe AccountReachFinder do
|
|||
expect(subject)
|
||||
.to include(*follower_inbox_urls)
|
||||
.and include(*mentioned_account_inbox_urls)
|
||||
.and include(*recently_followed_inbox_urls)
|
||||
.and include(*recently_requested_inbox_urls)
|
||||
.and not_include(unrelated_account.preferred_inbox_url)
|
||||
.and not_include(old_followed_account.preferred_inbox_url)
|
||||
end
|
||||
|
||||
def follower_inbox_urls
|
||||
|
@ -56,5 +74,15 @@ RSpec.describe AccountReachFinder do
|
|||
[ap_mentioned_with_shared, ap_mentioned_example_com, ap_mentioned_example_org]
|
||||
.map(&:preferred_inbox_url)
|
||||
end
|
||||
|
||||
def recently_followed_inbox_urls
|
||||
[ap_followed_example_com, ap_followed_example_org]
|
||||
.map(&:preferred_inbox_url)
|
||||
end
|
||||
|
||||
def recently_requested_inbox_urls
|
||||
[ap_requested_example_com, ap_requested_example_org]
|
||||
.map(&:preferred_inbox_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,22 +2,19 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::CollectionsController do
|
||||
RSpec.describe 'ActivityPub Collections' do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
let!(:private_pinned) { Fabricate(:status, account: account, text: 'secret private stuff', visibility: :private) }
|
||||
let(:remote_account) { nil }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
|
||||
|
||||
Fabricate(:status_pin, account: account)
|
||||
Fabricate(:status_pin, account: account)
|
||||
Fabricate.times(2, :status_pin, account: account)
|
||||
Fabricate(:status_pin, account: account, status: private_pinned)
|
||||
Fabricate(:status, account: account, visibility: :private)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
subject(:response) { get :show, params: { id: id, account_username: account.username } }
|
||||
subject { get account_collection_path(id: id, account_username: account.username), headers: nil, sign_with: remote_account }
|
||||
|
||||
context 'when id is "featured"' do
|
||||
let(:id) { 'featured' }
|
||||
|
@ -26,10 +23,13 @@ RSpec.describe ActivityPub::CollectionsController do
|
|||
let(:remote_account) { nil }
|
||||
|
||||
it 'returns http success and correct media type and correct items' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_cacheable_headers
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.media_type)
|
||||
.to eq 'application/activity+json'
|
||||
|
||||
expect(response.parsed_body[:orderedItems])
|
||||
.to be_an(Array)
|
||||
|
@ -45,17 +45,21 @@ RSpec.describe ActivityPub::CollectionsController do
|
|||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
before { account.suspend! }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -65,11 +69,14 @@ RSpec.describe ActivityPub::CollectionsController do
|
|||
|
||||
context 'when getting a featured resource' do
|
||||
it 'returns http success and correct media type and expected items' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_cacheable_headers
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.media_type)
|
||||
.to eq 'application/activity+json'
|
||||
|
||||
expect(response.parsed_body[:orderedItems])
|
||||
.to be_an(Array)
|
||||
|
@ -80,39 +87,45 @@ RSpec.describe ActivityPub::CollectionsController do
|
|||
end
|
||||
|
||||
context 'with authorized fetch mode' do
|
||||
before do
|
||||
allow(controller).to receive(:authorized_fetch_mode?).and_return(true)
|
||||
end
|
||||
before { Setting.authorized_fetch = true }
|
||||
|
||||
context 'when signed request account is blocked' do
|
||||
before do
|
||||
account.block!(remote_account)
|
||||
end
|
||||
before { account.block!(remote_account) }
|
||||
|
||||
it 'returns http success and correct media type and cache headers and empty items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to include 'private'
|
||||
subject
|
||||
|
||||
expect(response.parsed_body[:orderedItems])
|
||||
.to be_an(Array)
|
||||
.and be_empty
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.media_type)
|
||||
.to eq('application/activity+json')
|
||||
expect(response.headers['Cache-Control'])
|
||||
.to include('private')
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
orderedItems: be_an(Array).and(be_empty)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed request account is domain blocked' do
|
||||
before do
|
||||
account.block_domain!(remote_account.domain)
|
||||
end
|
||||
before { account.block_domain!(remote_account.domain) }
|
||||
|
||||
it 'returns http success and correct media type and cache headers and empty items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to include 'private'
|
||||
subject
|
||||
|
||||
expect(response.parsed_body[:orderedItems])
|
||||
.to be_an(Array)
|
||||
.and be_empty
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.media_type)
|
||||
.to eq('application/activity+json')
|
||||
expect(response.headers['Cache-Control'])
|
||||
.to include('private')
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
orderedItems: be_an(Array).and(be_empty)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -123,7 +136,10 @@ RSpec.describe ActivityPub::CollectionsController do
|
|||
let(:id) { 'hoge' }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::FollowersSynchronizationsController do
|
||||
RSpec.describe 'ActivityPub Follower Synchronizations' do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
let!(:follower_example_com_user_a) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/a') }
|
||||
let!(:follower_example_com_user_b) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/b') }
|
||||
|
@ -14,32 +14,34 @@ RSpec.describe ActivityPub::FollowersSynchronizationsController do
|
|||
follower_example_com_user_b.follow!(account)
|
||||
follower_foo_com_user_a.follow!(account)
|
||||
follower_example_com_instance_actor.follow!(account)
|
||||
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
context 'without signature' do
|
||||
let(:remote_account) { nil }
|
||||
|
||||
before do
|
||||
get :show, params: { account_username: account.username }
|
||||
end
|
||||
subject { get account_followers_synchronization_path(account_username: account.username) }
|
||||
|
||||
it 'returns http not authorized' do
|
||||
expect(response).to have_http_status(401)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(401)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with signature from example.com' do
|
||||
subject(:response) { get :show, params: { account_username: account.username } }
|
||||
subject { get account_followers_synchronization_path(account_username: account.username), headers: nil, sign_with: remote_account }
|
||||
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/instance') }
|
||||
|
||||
it 'returns http success and cache control and activity json types and correct items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.headers['Cache-Control'])
|
||||
.to eq 'max-age=0, private'
|
||||
expect(response.media_type)
|
||||
.to eq 'application/activity+json'
|
||||
|
||||
expect(response.parsed_body[:orderedItems])
|
||||
.to be_an(Array)
|
||||
|
@ -57,17 +59,21 @@ RSpec.describe ActivityPub::FollowersSynchronizationsController do
|
|||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
before { account.suspend! }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
148
spec/requests/activitypub/inboxes_spec.rb
Normal file
148
spec/requests/activitypub/inboxes_spec.rb
Normal file
|
@ -0,0 +1,148 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'ActivityPub Inboxes' do
|
||||
let(:remote_account) { nil }
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'with signature' do
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub) }
|
||||
|
||||
context 'without a named account' do
|
||||
subject { post inbox_path, params: {}.to_json, sign_with: remote_account }
|
||||
|
||||
it 'returns http accepted' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(202)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a specific account' do
|
||||
subject { post account_inbox_path(account_username: account.username), params: {}.to_json, sign_with: remote_account }
|
||||
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before { account.suspend! }
|
||||
|
||||
it 'returns http accepted' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(202)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Collection-Synchronization header' do
|
||||
subject { post inbox_path, params: {}.to_json, headers: { 'Collection-Synchronization' => synchronization_header }, sign_with: remote_account }
|
||||
|
||||
let(:remote_account) { Fabricate(:account, followers_url: 'https://example.com/followers', domain: 'example.com', uri: 'https://example.com/actor', protocol: :activitypub) }
|
||||
let(:synchronization_collection) { remote_account.followers_url }
|
||||
let(:synchronization_url) { 'https://example.com/followers-for-domain' }
|
||||
let(:synchronization_hash) { 'somehash' }
|
||||
let(:synchronization_header) { "collectionId=\"#{synchronization_collection}\", digest=\"#{synchronization_hash}\", url=\"#{synchronization_url}\"" }
|
||||
|
||||
before do
|
||||
stub_follow_sync_worker
|
||||
stub_followers_hash
|
||||
end
|
||||
|
||||
context 'with mismatching target collection' do
|
||||
let(:synchronization_collection) { 'https://example.com/followers2' }
|
||||
|
||||
it 'does not start a synchronization job' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(202)
|
||||
expect(ActivityPub::FollowersSynchronizationWorker)
|
||||
.to_not have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mismatching domain in partial collection attribute' do
|
||||
let(:synchronization_url) { 'https://example.org/followers' }
|
||||
|
||||
it 'does not start a synchronization job' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(202)
|
||||
expect(ActivityPub::FollowersSynchronizationWorker)
|
||||
.to_not have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with matching digest' do
|
||||
it 'does not start a synchronization job' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(202)
|
||||
expect(ActivityPub::FollowersSynchronizationWorker)
|
||||
.to_not have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mismatching digest' do
|
||||
let(:synchronization_hash) { 'wronghash' }
|
||||
|
||||
it 'starts a synchronization job' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(202)
|
||||
expect(ActivityPub::FollowersSynchronizationWorker)
|
||||
.to have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns http accepted' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(202)
|
||||
end
|
||||
|
||||
def stub_follow_sync_worker
|
||||
allow(ActivityPub::FollowersSynchronizationWorker)
|
||||
.to receive(:perform_async)
|
||||
.and_return(nil)
|
||||
end
|
||||
|
||||
def stub_followers_hash
|
||||
Rails.cache.write("followers_hash:#{remote_account.id}:local", 'somehash') # Populate value to match request
|
||||
end
|
||||
end
|
||||
|
||||
context 'without signature' do
|
||||
subject { post inbox_path, params: {}.to_json }
|
||||
|
||||
it 'returns http not authorized' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(401)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::OutboxesController do
|
||||
RSpec.describe 'ActivityPub Outboxes' do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
|
@ -11,13 +11,11 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
Fabricate(:status, account: account, visibility: :private)
|
||||
Fabricate(:status, account: account, visibility: :direct)
|
||||
Fabricate(:status, account: account, visibility: :limited)
|
||||
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
context 'without signature' do
|
||||
subject(:response) { get :show, params: { account_username: account.username, page: page } }
|
||||
subject { get account_outbox_path(account_username: account.username, page: page) }
|
||||
|
||||
let(:remote_account) { nil }
|
||||
|
||||
|
@ -25,13 +23,18 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
let(:page) { nil }
|
||||
|
||||
it 'returns http success and correct media type and headers and items count' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_cacheable_headers
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Vary']).to be_nil
|
||||
expect(response.parsed_body[:totalItems]).to eq 4
|
||||
expect(response.media_type)
|
||||
.to eq 'application/activity+json'
|
||||
expect(response.headers['Vary'])
|
||||
.to be_nil
|
||||
expect(response.parsed_body[:totalItems])
|
||||
.to eq 4
|
||||
end
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
|
@ -41,17 +44,21 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
before { account.suspend! }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -60,12 +67,16 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
let(:page) { 'true' }
|
||||
|
||||
it 'returns http success and correct media type and vary header and items' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_cacheable_headers
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Vary']).to include 'Signature'
|
||||
expect(response.media_type)
|
||||
.to eq 'application/activity+json'
|
||||
expect(response.headers['Vary'])
|
||||
.to include 'Signature'
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
|
@ -82,35 +93,42 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
before { account.suspend! }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with signature' do
|
||||
subject { get account_outbox_path(account_username: account.username, page: page), headers: nil, sign_with: remote_account }
|
||||
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||
let(:page) { 'true' }
|
||||
|
||||
context 'when signed request account does not follow account' do
|
||||
before do
|
||||
get :show, params: { account_username: account.username, page: page }
|
||||
end
|
||||
|
||||
it 'returns http success and correct media type and headers and items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.media_type)
|
||||
.to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control'])
|
||||
.to eq 'private, no-store'
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
|
@ -122,15 +140,17 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
end
|
||||
|
||||
context 'when signed request account follows account' do
|
||||
before do
|
||||
remote_account.follow!(account)
|
||||
get :show, params: { account_username: account.username, page: page }
|
||||
end
|
||||
before { remote_account.follow!(account) }
|
||||
|
||||
it 'returns http success and correct media type and headers and items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.media_type)
|
||||
.to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control'])
|
||||
.to eq 'private, no-store'
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
|
@ -142,15 +162,17 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
end
|
||||
|
||||
context 'when signed request account is blocked' do
|
||||
before do
|
||||
account.block!(remote_account)
|
||||
get :show, params: { account_username: account.username, page: page }
|
||||
end
|
||||
before { account.block!(remote_account) }
|
||||
|
||||
it 'returns http success and correct media type and headers and items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.media_type)
|
||||
.to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control'])
|
||||
.to eq 'private, no-store'
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
|
@ -160,15 +182,17 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
end
|
||||
|
||||
context 'when signed request account is domain blocked' do
|
||||
before do
|
||||
account.block_domain!(remote_account.domain)
|
||||
get :show, params: { account_username: account.username, page: page }
|
||||
end
|
||||
before { account.block_domain!(remote_account.domain) }
|
||||
|
||||
it 'returns http success and correct media type and headers and items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.media_type)
|
||||
.to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control'])
|
||||
.to eq 'private, no-store'
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::RepliesController do
|
||||
RSpec.describe 'ActivityPub Replies' do
|
||||
let(:status) { Fabricate(:status, visibility: parent_visibility) }
|
||||
let(:remote_account) { Fabricate(:account, domain: 'foobar.com') }
|
||||
let(:remote_account) { Fabricate(:account, domain: 'foobar.com') }
|
||||
let(:remote_reply_id) { 'https://foobar.com/statuses/1234' }
|
||||
let(:remote_querier) { nil }
|
||||
|
||||
|
@ -13,7 +13,10 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
let(:parent_visibility) { :private }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -21,7 +24,10 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
let(:parent_visibility) { :direct }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -31,7 +37,10 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
let(:parent_visibility) { :public }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -48,19 +57,23 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
let(:parent_visibility) { :public }
|
||||
|
||||
before do
|
||||
status.account.suspend!
|
||||
end
|
||||
before { status.account.suspend! }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -68,15 +81,20 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
let(:parent_visibility) { :public }
|
||||
|
||||
it 'returns http success and correct media type' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_cacheable_headers
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.media_type)
|
||||
.to eq 'application/activity+json'
|
||||
end
|
||||
|
||||
context 'without only_other_accounts' do
|
||||
context 'without `only_other_accounts` param' do
|
||||
it "returns items with thread author's replies" do
|
||||
subject
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(
|
||||
|
@ -91,6 +109,8 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
|
||||
context 'when there are few self-replies' do
|
||||
it 'points next to replies from other people' do
|
||||
subject
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(
|
||||
|
@ -108,6 +128,8 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
end
|
||||
|
||||
it 'points next to other self-replies' do
|
||||
subject
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(
|
||||
|
@ -120,31 +142,33 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with only_other_accounts' do
|
||||
context 'with `only_other_accounts` param' do
|
||||
let(:only_other_accounts) { 'true' }
|
||||
|
||||
it 'returns items with other public or unlisted replies' do
|
||||
it 'returns items with other public or unlisted replies and correctly inlines replies and uses IDs' do
|
||||
subject
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(
|
||||
include(items: be_an(Array).and(have_attributes(size: 3)))
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
it 'only inlines items that are local and public or unlisted replies' do
|
||||
# Only inline replies that are local and public, or unlisted
|
||||
expect(inlined_replies)
|
||||
.to all(satisfy { |item| targets_public_collection?(item) })
|
||||
.and all(satisfy { |item| ActivityPub::TagManager.instance.local_uri?(item[:id]) })
|
||||
end
|
||||
|
||||
it 'uses ids for remote toots' do
|
||||
# Use ids for remote replies
|
||||
expect(remote_replies)
|
||||
.to all(satisfy { |item| item.is_a?(String) && !ActivityPub::TagManager.instance.local_uri?(item) })
|
||||
end
|
||||
|
||||
context 'when there are few replies' do
|
||||
it 'does not have a next page' do
|
||||
subject
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(not_include(next: be_present))
|
||||
|
@ -158,6 +182,8 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
end
|
||||
|
||||
it 'points next to other replies' do
|
||||
subject
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(
|
||||
|
@ -176,10 +202,8 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
|
||||
before do
|
||||
stub_const 'ActivityPub::RepliesController::DESCENDANTS_LIMIT', 5
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_querier)
|
||||
|
||||
Fabricate(:status, thread: status, visibility: :public)
|
||||
Fabricate(:status, thread: status, visibility: :public)
|
||||
Fabricate.times(2, :status, thread: status, visibility: :public)
|
||||
Fabricate(:status, thread: status, visibility: :private)
|
||||
Fabricate(:status, account: status.account, thread: status, visibility: :public)
|
||||
Fabricate(:status, account: status.account, thread: status, visibility: :private)
|
||||
|
@ -188,31 +212,29 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
subject(:response) { get :index, params: { account_username: status.account.username, status_id: status.id, only_other_accounts: only_other_accounts } }
|
||||
|
||||
let(:only_other_accounts) { nil }
|
||||
|
||||
context 'with no signature' do
|
||||
subject { get account_status_replies_path(account_username: status.account.username, status_id: status.id, only_other_accounts: only_other_accounts) }
|
||||
|
||||
it_behaves_like 'allowed access'
|
||||
end
|
||||
|
||||
context 'with signature' do
|
||||
subject { get account_status_replies_path(account_username: status.account.username, status_id: status.id, only_other_accounts: only_other_accounts), headers: nil, sign_with: remote_querier }
|
||||
|
||||
let(:remote_querier) { Fabricate(:account, domain: 'example.com') }
|
||||
|
||||
it_behaves_like 'allowed access'
|
||||
|
||||
context 'when signed request account is blocked' do
|
||||
before do
|
||||
status.account.block!(remote_querier)
|
||||
end
|
||||
before { status.account.block!(remote_querier) }
|
||||
|
||||
it_behaves_like 'disallowed access'
|
||||
end
|
||||
|
||||
context 'when signed request account is domain blocked' do
|
||||
before do
|
||||
status.account.block_domain!(remote_querier.domain)
|
||||
end
|
||||
before { status.account.block_domain!(remote_querier.domain) }
|
||||
|
||||
it_behaves_like 'disallowed access'
|
||||
end
|
|
@ -18,4 +18,24 @@ module SignedRequestHelpers
|
|||
|
||||
super(path, headers: headers, **args)
|
||||
end
|
||||
|
||||
def post(path, headers: nil, sign_with: nil, **args)
|
||||
return super(path, headers: headers, **args) if sign_with.nil?
|
||||
|
||||
headers ||= {}
|
||||
headers['Date'] = Time.now.utc.httpdate
|
||||
headers['Host'] = Rails.configuration.x.local_domain
|
||||
headers['Digest'] = "SHA-256=#{Digest::SHA256.base64digest(args[:params].to_s)}"
|
||||
|
||||
signed_headers = headers.merge('(request-target)' => "post #{path}").slice('(request-target)', 'Host', 'Date', 'Digest')
|
||||
|
||||
key_id = ActivityPub::TagManager.instance.key_uri_for(sign_with)
|
||||
keypair = sign_with.keypair
|
||||
signed_string = signed_headers.map { |key, value| "#{key.downcase}: #{value}" }.join("\n")
|
||||
signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string))
|
||||
|
||||
headers['Signature'] = "keyId=\"#{key_id}\",algorithm=\"rsa-sha256\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\""
|
||||
|
||||
super(path, headers: headers, **args)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,9 +24,6 @@ RSpec.describe 'Account notes', :inline_jobs, :js, :streaming do
|
|||
# The easiest way is to send ctrl+enter ourselves
|
||||
find_field(class: 'account__header__account-note__content').send_keys [:control, :enter]
|
||||
|
||||
expect(page)
|
||||
.to have_css('.account__header__account-note .inline-alert', text: 'SAVED')
|
||||
|
||||
expect(page)
|
||||
.to have_css('.account__header__account-note__content', text: note_text)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue