Merge commit 'c91c0175db1cc8b954a977d29472886234ce9586' into glitch-soc/merge-upstream

Conflicts:
- `spec/controllers/api/v1/timelines/tag_controller_spec.rb`:
  Glitch-soc had a few extra lines in this file to account for a different
  default setting. This file got replaced by
  `spec/requests/api/v1/timelines/tag_spec.rb`, into which the glitch-soc
  additions were moved too.

Additional changes:
- `spec/requests/api/v1/statuses/sources_spec.rb`:
  Add glitch-soc-only attribute `content_type`.
th-downstream
Claire 1 year ago
commit b867d4581e

@ -180,9 +180,7 @@ RSpec/LetSetup:
- 'spec/controllers/admin/reports/actions_controller_spec.rb' - 'spec/controllers/admin/reports/actions_controller_spec.rb'
- 'spec/controllers/admin/statuses_controller_spec.rb' - 'spec/controllers/admin/statuses_controller_spec.rb'
- 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb' - 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb'
- 'spec/controllers/api/v1/admin/accounts_controller_spec.rb'
- 'spec/controllers/api/v1/filters_controller_spec.rb' - 'spec/controllers/api/v1/filters_controller_spec.rb'
- 'spec/controllers/api/v1/followed_tags_controller_spec.rb'
- 'spec/controllers/api/v2/admin/accounts_controller_spec.rb' - 'spec/controllers/api/v2/admin/accounts_controller_spec.rb'
- 'spec/controllers/api/v2/filters/keywords_controller_spec.rb' - 'spec/controllers/api/v2/filters/keywords_controller_spec.rb'
- 'spec/controllers/api/v2/filters/statuses_controller_spec.rb' - 'spec/controllers/api/v2/filters/statuses_controller_spec.rb'
@ -418,7 +416,6 @@ Rails/SkipsModelValidations:
- 'lib/mastodon/cli/accounts.rb' - 'lib/mastodon/cli/accounts.rb'
- 'lib/mastodon/cli/main.rb' - 'lib/mastodon/cli/main.rb'
- 'lib/mastodon/cli/maintenance.rb' - 'lib/mastodon/cli/maintenance.rb'
- 'spec/controllers/api/v1/admin/accounts_controller_spec.rb'
- 'spec/lib/activitypub/activity/follow_spec.rb' - 'spec/lib/activitypub/activity/follow_spec.rb'
- 'spec/services/follow_service_spec.rb' - 'spec/services/follow_service_spec.rb'
- 'spec/services/update_account_service_spec.rb' - 'spec/services/update_account_service_spec.rb'

@ -151,7 +151,7 @@ GEM
erubi (>= 1.0.0) erubi (>= 1.0.0)
rack (>= 0.9.0) rack (>= 0.9.0)
rouge (>= 1.0.0) rouge (>= 1.0.0)
better_html (2.0.1) better_html (2.0.2)
actionview (>= 6.0) actionview (>= 6.0)
activesupport (>= 6.0) activesupport (>= 6.0)
ast (~> 2.0) ast (~> 2.0)
@ -345,14 +345,14 @@ GEM
rainbow (>= 2.0.0) rainbow (>= 2.0.0)
i18n (1.14.1) i18n (1.14.1)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
i18n-tasks (1.0.12) i18n-tasks (1.0.13)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
ast (>= 2.1.0) ast (>= 2.1.0)
better_html (>= 1.0, < 3.0) better_html (>= 1.0, < 3.0)
erubi erubi
highline (>= 2.0.0) highline (>= 2.0.0)
i18n i18n
parser (>= 2.2.3.0) parser (>= 3.2.2.1)
rails-i18n rails-i18n
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
terminal-table (>= 1.5.1) terminal-table (>= 1.5.1)
@ -561,7 +561,7 @@ GEM
rails-html-sanitizer (1.6.0) rails-html-sanitizer (1.6.0)
loofah (~> 2.21) loofah (~> 2.21)
nokogiri (~> 1.14) nokogiri (~> 1.14)
rails-i18n (7.0.7) rails-i18n (7.0.8)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 6.0.0, < 8)
railties (7.0.8) railties (7.0.8)

@ -613,7 +613,7 @@
"sign_in_banner.create_account": "Sortu kontua", "sign_in_banner.create_account": "Sortu kontua",
"sign_in_banner.sign_in": "Hasi saioa", "sign_in_banner.sign_in": "Hasi saioa",
"sign_in_banner.sso_redirect": "Hasi saioa edo izena eman", "sign_in_banner.sso_redirect": "Hasi saioa edo izena eman",
"sign_in_banner.text": "Hasi saioa profilak edo traolak jarraitzeko, bidalketak gogokoetara gehitzeko, partekatzeko edo erantzuteko. Zure kontutik ere komunika zaitezke beste zerbitzari ezberdin vatean.", "sign_in_banner.text": "Hasi saioa profilak edo traolak jarraitzeko, bidalketak gogokoetara gehitzeko, partekatzeko edo erantzuteko. Zure kontutik ere komunika zaitezke beste zerbitzari ezberdin batean.",
"status.admin_account": "Ireki @{name} erabiltzailearen moderazio interfazea", "status.admin_account": "Ireki @{name} erabiltzailearen moderazio interfazea",
"status.admin_domain": "{domain}-(r)en moderazio-interfazea ireki", "status.admin_domain": "{domain}-(r)en moderazio-interfazea ireki",
"status.admin_status": "Ireki bidalketa hau moderazio interfazean", "status.admin_status": "Ireki bidalketa hau moderazio interfazean",

@ -71,8 +71,8 @@
"account.unmute_notifications_short": "Poista ilmoitusten mykistys", "account.unmute_notifications_short": "Poista ilmoitusten mykistys",
"account.unmute_short": "Poista mykistys", "account.unmute_short": "Poista mykistys",
"account_note.placeholder": "Lisää muistiinpano napsauttamalla", "account_note.placeholder": "Lisää muistiinpano napsauttamalla",
"admin.dashboard.daily_retention": "Käyttäjän säilyminen rekisteröitymisen jälkeiseen päivään mennessä", "admin.dashboard.daily_retention": "Käyttäjän pysyminen rekisteröitymisen jälkeiseen päivään mennessä",
"admin.dashboard.monthly_retention": "Käyttäjän säilyminen rekisteröitymisen jälkeiseen kuukauteen mennessä", "admin.dashboard.monthly_retention": "Käyttäjän pysyminen rekisteröitymisen jälkeiseen kuukauteen mennessä",
"admin.dashboard.retention.average": "Keskimäärin", "admin.dashboard.retention.average": "Keskimäärin",
"admin.dashboard.retention.cohort": "Kirjautumiset", "admin.dashboard.retention.cohort": "Kirjautumiset",
"admin.dashboard.retention.cohort_size": "Uudet käyttäjät", "admin.dashboard.retention.cohort_size": "Uudet käyttäjät",
@ -101,7 +101,7 @@
"bundle_modal_error.close": "Sulje", "bundle_modal_error.close": "Sulje",
"bundle_modal_error.message": "Jotain meni pieleen komponenttia ladattaessa.", "bundle_modal_error.message": "Jotain meni pieleen komponenttia ladattaessa.",
"bundle_modal_error.retry": "Yritä uudelleen", "bundle_modal_error.retry": "Yritä uudelleen",
"closed_registrations.other_server_instructions": "Koska Mastodon on hajautettu, voit luoda tilin toiselle palvelimelle ja silti olla vuorovaikutuksessa tämän kanssa.", "closed_registrations.other_server_instructions": "Koska Mastodon on hajautettu, voit luoda tilin toiselle palvelimelle ja olla silti vuorovaikutuksessa tämän kanssa.",
"closed_registrations_modal.description": "Tilin luonti palveluun {domain} ei tällä hetkellä ole mahdollista, mutta huomioi, ettei Mastodonin käyttö edellytä juuri kyseisen palvelun tiliä.", "closed_registrations_modal.description": "Tilin luonti palveluun {domain} ei tällä hetkellä ole mahdollista, mutta huomioi, ettei Mastodonin käyttö edellytä juuri kyseisen palvelun tiliä.",
"closed_registrations_modal.find_another_server": "Etsi toinen palvelin", "closed_registrations_modal.find_another_server": "Etsi toinen palvelin",
"closed_registrations_modal.preamble": "Mastodon on hajautettu, joten riippumatta siitä, missä luot tilisi, voit seurata ja olla vuorovaikutuksessa kenen tahansa kanssa tällä palvelimella. Voit jopa isännöidä palvelinta!", "closed_registrations_modal.preamble": "Mastodon on hajautettu, joten riippumatta siitä, missä luot tilisi, voit seurata ja olla vuorovaikutuksessa kenen tahansa kanssa tällä palvelimella. Voit jopa isännöidä palvelinta!",
@ -154,9 +154,9 @@
"compose_form.publish_form": "Uusi julkaisu", "compose_form.publish_form": "Uusi julkaisu",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Tallenna muutokset", "compose_form.save_changes": "Tallenna muutokset",
"compose_form.sensitive.hide": "{count, plural, one {Merkitse media arkaluontoiseksi} other {Merkitse mediat arkaluontoiseksi}}", "compose_form.sensitive.hide": "{count, plural, one {Merkitse media arkaluonteiseksi} other {Merkitse mediat arkaluonteisiksi}}",
"compose_form.sensitive.marked": "{count, plural, one {Media on merkitty arkaluontoiseksi} other {Mediat on merkitty arkaluontoiseksi}}", "compose_form.sensitive.marked": "{count, plural, one {Media on merkitty arkaluonteiseksi} other {Mediat on merkitty arkaluonteisiksi}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Mediaa ei ole merkitty arkaluontoiseksi} other {Medioja ei ole merkitty arkaluontoiseksi}}", "compose_form.sensitive.unmarked": "{count, plural, one {Mediaa ei ole merkitty arkaluonteiseksi} other {Medioita ei ole merkitty arkaluonteisiksi}}",
"compose_form.spoiler.marked": "Poista sisältövaroitus", "compose_form.spoiler.marked": "Poista sisältövaroitus",
"compose_form.spoiler.unmarked": "Lisää sisältövaroitus", "compose_form.spoiler.unmarked": "Lisää sisältövaroitus",
"compose_form.spoiler_placeholder": "Kirjoita varoituksesi tähän", "compose_form.spoiler_placeholder": "Kirjoita varoituksesi tähän",
@ -592,7 +592,7 @@
"search.search_or_paste": "Hae tai kirjoita URL-osoite", "search.search_or_paste": "Hae tai kirjoita URL-osoite",
"search_popout.full_text_search_disabled_message": "Ei saatavilla palvelimella {domain}.", "search_popout.full_text_search_disabled_message": "Ei saatavilla palvelimella {domain}.",
"search_popout.language_code": "ISO-kielikoodi", "search_popout.language_code": "ISO-kielikoodi",
"search_popout.options": "Haun asetukset", "search_popout.options": "Hakuvalinnat",
"search_popout.quick_actions": "Pikatoiminnot", "search_popout.quick_actions": "Pikatoiminnot",
"search_popout.recent": "Viimeaikaiset haut", "search_popout.recent": "Viimeaikaiset haut",
"search_popout.specific_date": "tietty päivämäärä", "search_popout.specific_date": "tietty päivämäärä",
@ -686,7 +686,7 @@
"timeline_hint.resources.followers": "Seuraajat", "timeline_hint.resources.followers": "Seuraajat",
"timeline_hint.resources.follows": "seurattua", "timeline_hint.resources.follows": "seurattua",
"timeline_hint.resources.statuses": "Vanhemmat julkaisut", "timeline_hint.resources.statuses": "Vanhemmat julkaisut",
"trends.counter_by_accounts": "{count, plural, one {{counter} henkilö} other {{counter} henkilöä}} {days, plural, one {viimeisen päivän} other {viimeisten {days} päivän}} aikana", "trends.counter_by_accounts": "{count, plural, one {{counter} henkilö} other {{counter} henkilöä}} {days, plural, one {viime päivänä} other {viimeisenä {days} päivänä}}",
"trends.trending_now": "Suosittua nyt", "trends.trending_now": "Suosittua nyt",
"ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.", "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.",
"units.short.billion": "{count} mrd.", "units.short.billion": "{count} mrd.",

@ -47,7 +47,7 @@
"account.locked_info": "このアカウントは承認制アカウントです。相手が承認するまでフォローは完了しません。", "account.locked_info": "このアカウントは承認制アカウントです。相手が承認するまでフォローは完了しません。",
"account.media": "メディア", "account.media": "メディア",
"account.mention": "@{name}さんにメンション", "account.mention": "@{name}さんにメンション",
"account.moved_to": "{name}さんがアカウントを引っ越しました:", "account.moved_to": "{name}さんはこちらのアカウントに引っ越しました:",
"account.mute": "@{name}さんをミュート", "account.mute": "@{name}さんをミュート",
"account.mute_notifications_short": "通知をオフにする", "account.mute_notifications_short": "通知をオフにする",
"account.mute_short": "ミュート", "account.mute_short": "ミュート",

@ -61,7 +61,7 @@
"account.requested_follow": "{name} ti poslal žiadosť na sledovanie", "account.requested_follow": "{name} ti poslal žiadosť na sledovanie",
"account.share": "Zdieľaj @{name} profil", "account.share": "Zdieľaj @{name} profil",
"account.show_reblogs": "Ukáž vyzdvihnutia od @{name}", "account.show_reblogs": "Ukáž vyzdvihnutia od @{name}",
"account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.statuses_counter": "{count, plural, one {{counter} príspevok} other {{counter} príspevkov}}",
"account.unblock": "Odblokuj @{name}", "account.unblock": "Odblokuj @{name}",
"account.unblock_domain": "Prestaň skrývať {domain}", "account.unblock_domain": "Prestaň skrývať {domain}",
"account.unblock_short": "Odblokuj", "account.unblock_short": "Odblokuj",

@ -204,7 +204,7 @@
"dismissable_banner.explore_links": "這些新聞故事正在被此伺服器以及去中心化網路上的人們熱烈討論著。越多不同人所嘟出的新聞排名更高。", "dismissable_banner.explore_links": "這些新聞故事正在被此伺服器以及去中心化網路上的人們熱烈討論著。越多不同人所嘟出的新聞排名更高。",
"dismissable_banner.explore_statuses": "這些於此伺服器以及去中心化網路中其他伺服器發出的嘟文正在被此伺服器上的人們熱烈討論著。越多不同人轉嘟及最愛排名更高。", "dismissable_banner.explore_statuses": "這些於此伺服器以及去中心化網路中其他伺服器發出的嘟文正在被此伺服器上的人們熱烈討論著。越多不同人轉嘟及最愛排名更高。",
"dismissable_banner.explore_tags": "這些主題標籤正在被此伺服器以及去中心化網路上的人們熱烈討論著。越多不同人所嘟出的主題標籤排名更高。", "dismissable_banner.explore_tags": "這些主題標籤正在被此伺服器以及去中心化網路上的人們熱烈討論著。越多不同人所嘟出的主題標籤排名更高。",
"dismissable_banner.public_timeline": "這些是來自 {domain} 上人們於社群網站中跟隨者所發表之最近公開嘟文。", "dismissable_banner.public_timeline": "這些是來自 {domain} 使用者們跟隨中帳號所發表之最新公開嘟文。",
"embed.instructions": "要在您的網站嵌入此嘟文,請複製以下程式碼。", "embed.instructions": "要在您的網站嵌入此嘟文,請複製以下程式碼。",
"embed.preview": "它將顯示成這樣:", "embed.preview": "它將顯示成這樣:",
"emoji_button.activity": "活動", "emoji_button.activity": "活動",
@ -271,8 +271,8 @@
"filter_modal.select_filter.title": "過濾此嘟文", "filter_modal.select_filter.title": "過濾此嘟文",
"filter_modal.title.status": "過濾一則嘟文", "filter_modal.title.status": "過濾一則嘟文",
"firehose.all": "全部", "firehose.all": "全部",
"firehose.local": "此伺服器", "firehose.local": "本站",
"firehose.remote": "其他伺服器", "firehose.remote": "聯邦宇宙",
"follow_request.authorize": "授權", "follow_request.authorize": "授權",
"follow_request.reject": "拒絕", "follow_request.reject": "拒絕",
"follow_requests.unlocked_explanation": "即便您的帳號未被鎖定,{domain} 的管理員認為您可能想要自己審核這些帳號的跟隨請求。", "follow_requests.unlocked_explanation": "即便您的帳號未被鎖定,{domain} 的管理員認為您可能想要自己審核這些帳號的跟隨請求。",

@ -36,7 +36,8 @@ class LinkDetailsExtractor
end end
def language def language
json['inLanguage'] lang = json['inLanguage']
lang.is_a?(Hash) ? (lang['alternateName'] || lang['name']) : lang
end end
def type def type

@ -2,25 +2,40 @@
class ExistingUsernameValidator < ActiveModel::EachValidator class ExistingUsernameValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value) def validate_each(record, attribute, value)
return if value.blank? @value = value
return if @value.blank?
usernames_and_domains = value.split(',').filter_map do |str| if options[:multiple]
username, domain = str.strip.gsub(/\A@/, '').split('@', 2) record.errors.add(attribute, not_found_multiple_message) if usernames_with_no_accounts.any?
elsif usernames_with_no_accounts.any? || usernames_and_domains.size > 1
record.errors.add(attribute, not_found_message)
end
end
private
def usernames_and_domains
@value.split(',').filter_map do |string|
username, domain = string.strip.gsub(/\A@/, '').split('@', 2)
domain = nil if TagManager.instance.local_domain?(domain) domain = nil if TagManager.instance.local_domain?(domain)
next if username.blank? next if username.blank?
[str, username, domain] [string, username, domain]
end end
end
usernames_with_no_accounts = usernames_and_domains.filter_map do |(str, username, domain)| def usernames_with_no_accounts
str unless Account.find_remote(username, domain) usernames_and_domains.filter_map do |(string, username, domain)|
string unless Account.find_remote(username, domain)
end end
end
if options[:multiple] def not_found_multiple_message
record.errors.add(attribute, I18n.t('existing_username_validator.not_found_multiple', usernames: usernames_with_no_accounts.join(', '))) if usernames_with_no_accounts.any? I18n.t('existing_username_validator.not_found_multiple', usernames: usernames_with_no_accounts.join(', '))
elsif usernames_with_no_accounts.any? || usernames_and_domains.size > 1 end
record.errors.add(attribute, I18n.t('existing_username_validator.not_found'))
end def not_found_message
I18n.t('existing_username_validator.not_found')
end end
end end

@ -0,0 +1,38 @@
.strike-card
- unless strike.none_action?
%p= t "user_mailer.warning.explanation.#{strike.action}", instance: Rails.configuration.x.local_domain
- if strike.text.present?
= linkify(strike.text)
- if strike.report && !strike.report.other?
%p
%strong= t('user_mailer.warning.reason')
= t("user_mailer.warning.categories.#{strike.report.category}")
- if strike.report.violation? && strike.report.rule_ids.present?
%ul.strike-card__rules
- strike.report.rules.each do |rule|
%li
%span.strike-card__rules__text= rule.text
- if strike.status_ids.present? && !strike.status_ids.empty?
%p
%strong= t('user_mailer.warning.statuses')
.strike-card__statuses-list
- status_map = strike.statuses.includes(:application, :media_attachments).index_by(&:id)
- strike.status_ids.each do |status_id|
.strike-card__statuses-list__item
- if (status = status_map[status_id.to_i])
.one-liner
.emojify= one_line_preview(status)
- status.ordered_media_attachments.each do |media_attachment|
%abbr{ title: media_attachment.description }
= fa_icon 'link'
= media_attachment.file_file_name
.strike-card__statuses-list__item__meta
= link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank', rel: 'noopener noreferrer' do
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
- unless status.application.nil?
·
= status.application.name
- else
.one-liner= t('disputes.strikes.status', id: status_id)
.strike-card__statuses-list__item__meta
= t('disputes.strikes.status_removed')

@ -21,51 +21,7 @@
.report-header .report-header
.report-header__card .report-header__card
.strike-card = render 'card', strike: @strike
- unless @strike.none_action?
%p= t "user_mailer.warning.explanation.#{@strike.action}", instance: Rails.configuration.x.local_domain
- if @strike.text.present?
= linkify(@strike.text)
- if @strike.report && !@strike.report.other?
%p
%strong= t('user_mailer.warning.reason')
= t("user_mailer.warning.categories.#{@strike.report.category}")
- if @strike.report.violation? && @strike.report.rule_ids.present?
%ul.strike-card__rules
- @strike.report.rules.each do |rule|
%li
%span.strike-card__rules__text= rule.text
- if @strike.status_ids.present? && !@strike.status_ids.empty?
%p
%strong= t('user_mailer.warning.statuses')
.strike-card__statuses-list
- status_map = @strike.statuses.includes(:application, :media_attachments).index_by(&:id)
- @strike.status_ids.each do |status_id|
.strike-card__statuses-list__item
- if (status = status_map[status_id.to_i])
.one-liner
.emojify= one_line_preview(status)
- status.ordered_media_attachments.each do |media_attachment|
%abbr{ title: media_attachment.description }
= fa_icon 'link'
= media_attachment.file_file_name
.strike-card__statuses-list__item__meta
= link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank', rel: 'noopener noreferrer' do
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
- unless status.application.nil?
·
= status.application.name
- else
.one-liner= t('disputes.strikes.status', id: status_id)
.strike-card__statuses-list__item__meta
= t('disputes.strikes.status_removed')
.report-header__details .report-header__details
.report-header__details__item .report-header__details__item

@ -3,9 +3,9 @@
enabled = ENV['ES_ENABLED'] == 'true' enabled = ENV['ES_ENABLED'] == 'true'
host = ENV.fetch('ES_HOST') { 'localhost' } host = ENV.fetch('ES_HOST') { 'localhost' }
port = ENV.fetch('ES_PORT') { 9200 } port = ENV.fetch('ES_PORT') { 9200 }
user = ENV.fetch('ES_USER') { nil } user = ENV.fetch('ES_USER', nil).presence
password = ENV.fetch('ES_PASS') { nil } password = ENV.fetch('ES_PASS', nil).presence
fallback_prefix = ENV.fetch('REDIS_NAMESPACE') { nil } fallback_prefix = ENV.fetch('REDIS_NAMESPACE', nil).presence
prefix = ENV.fetch('ES_PREFIX') { fallback_prefix } prefix = ENV.fetch('ES_PREFIX') { fallback_prefix }
Chewy.settings = { Chewy.settings = {

@ -14,6 +14,7 @@ Rails.application.config.middleware.insert_before 0, Rack::Cors do
with_options headers: :any, credentials: false do with_options headers: :any, credentials: false do
with_options methods: [:get] do with_options methods: [:get] do
resource '/.well-known/*' resource '/.well-known/*'
resource '/nodeinfo/*'
resource '/@:username' resource '/@:username'
resource '/users/:username' resource '/users/:username'
end end

@ -394,7 +394,7 @@ Devise.setup do |config|
config.check_at_sign = true config.check_at_sign = true
config.pam_default_suffix = ENV.fetch('PAM_EMAIL_DOMAIN') { ENV['LOCAL_DOMAIN'] } config.pam_default_suffix = ENV.fetch('PAM_EMAIL_DOMAIN') { ENV['LOCAL_DOMAIN'] }
config.pam_default_service = ENV.fetch('PAM_DEFAULT_SERVICE') { 'rpam' } config.pam_default_service = ENV.fetch('PAM_DEFAULT_SERVICE') { 'rpam' }
config.pam_controlled_service = ENV.fetch('PAM_CONTROLLED_SERVICE') { nil } config.pam_controlled_service = ENV.fetch('PAM_CONTROLLED_SERVICE', nil).presence
end end
if ENV['LDAP_ENABLED'] == 'true' if ENV['LDAP_ENABLED'] == 'true'

@ -4,7 +4,7 @@ zh-TW:
confirmations: confirmations:
confirmed: 您的電子郵件地址已確認成功。 confirmed: 您的電子郵件地址已確認成功。
send_instructions: 幾分鐘後您將收到確認信件。若未收到此信件,請檢查垃圾郵件資料夾。 send_instructions: 幾分鐘後您將收到確認信件。若未收到此信件,請檢查垃圾郵件資料夾。
send_paranoid_instructions: 如果您的電子郵件存在於我們的資料庫,您將會在幾分鐘內收到確認信。若未收到請檢查垃圾郵件資料夾。 send_paranoid_instructions: 如果您的電子郵件存在於我們的資料庫,您將幾分鐘內收到確認信。若未收到請檢查垃圾郵件資料夾。
failure: failure:
already_authenticated: 您已登入。 already_authenticated: 您已登入。
inactive: 您的帳號尚未啟用。 inactive: 您的帳號尚未啟用。
@ -43,7 +43,7 @@ zh-TW:
reset_password_instructions: reset_password_instructions:
action: 變更密碼 action: 變更密碼
explanation: 您已請求帳號的新密碼。 explanation: 您已請求帳號的新密碼。
extra: 若您並未請求,請忽略此信件。您的密碼存取上方連結並建立新密碼前不會變更。 extra: 若您並未請求,請忽略此信件。您的密碼存取上方連結並建立新密碼前不會變更。
subject: Mastodon重設密碼指引 subject: Mastodon重設密碼指引
title: 重設密碼 title: 重設密碼
two_factor_disabled: two_factor_disabled:

@ -12,7 +12,7 @@ fi:
one: seuraaja one: seuraaja
other: seuraajaa other: seuraajaa
following: seurattu(a) following: seurattu(a)
instance_actor_flash: Tämä tili on virtuaalinen toimija, jota käytetään edustamaan itse palvelinta eikä yksittäistä käyttäjää. Sitä käytetään federointitarkoituksiin, eikä sitä tule jäädyttää. instance_actor_flash: Tämä tili on virtuaalinen toimija, jota käytetään edustamaan itse palvelinta eikä yksittäistä käyttäjää. Sitä käytetään liittoutumistarkoituksiin, eikä sitä tule jäädyttää.
last_active: viimeksi aktiivinen last_active: viimeksi aktiivinen
link_verified_on: Tämän linkin omistus on tarkastettu %{date} link_verified_on: Tämän linkin omistus on tarkastettu %{date}
nothing_here: Täällä ei ole mitään! nothing_here: Täällä ei ole mitään!
@ -138,7 +138,7 @@ fi:
security_measures: security_measures:
only_password: Vain salasana only_password: Vain salasana
password_and_2fa: Salasana ja kaksivaiheinen tunnistautuminen password_and_2fa: Salasana ja kaksivaiheinen tunnistautuminen
sensitive: Pakotus arkaluonteiseksi sensitive: Pakota arkaluonteiseksi
sensitized: Merkitty arkaluonteiseksi sensitized: Merkitty arkaluonteiseksi
shared_inbox_url: Jaetun saapuvan postilaatikon osoite shared_inbox_url: Jaetun saapuvan postilaatikon osoite
show: show:
@ -157,7 +157,7 @@ fi:
unblock_email: Poista sähköpostiosoitteen esto unblock_email: Poista sähköpostiosoitteen esto
unblocked_email_msg: Käyttäjän %{username} sähköpostiosoitteen esto kumottiin unblocked_email_msg: Käyttäjän %{username} sähköpostiosoitteen esto kumottiin
unconfirmed_email: Sähköpostia ei vahvistettu unconfirmed_email: Sähköpostia ei vahvistettu
undo_sensitized: Kumoa pakotus arkaluonteiseksi tiliksi undo_sensitized: Kumoa pakotus arkaluonteiseksi
undo_silenced: Kumoa rajoitus undo_silenced: Kumoa rajoitus
undo_suspension: Peru jäähy undo_suspension: Peru jäähy
unsilenced_msg: Tilin %{username} rajoituksen kumoaminen onnistui unsilenced_msg: Tilin %{username} rajoituksen kumoaminen onnistui
@ -167,7 +167,7 @@ fi:
view_domain: Näytä verkkotunnuksen yhteenveto view_domain: Näytä verkkotunnuksen yhteenveto
warn: Varoita warn: Varoita
web: Verkko web: Verkko
whitelisted: Sallittu federoimaan whitelisted: Sallittu liittoutua
action_logs: action_logs:
action_types: action_types:
approve_appeal: Hyväksy valitus approve_appeal: Hyväksy valitus
@ -214,12 +214,12 @@ fi:
resend_user: Lähetä vahvistusviesti uudelleen resend_user: Lähetä vahvistusviesti uudelleen
reset_password_user: Nollaa salasana reset_password_user: Nollaa salasana
resolve_report: Selvitä raportti resolve_report: Selvitä raportti
sensitive_account: Pakotus arkaluontoiseksi tiliksi sensitive_account: Pakotus arkaluonteiseksi tiliksi
silence_account: Rajoita tiliä silence_account: Rajoita tiliä
suspend_account: Jäädytä tili suspend_account: Jäädytä tili
unassigned_report: Peruuta raportin määritys unassigned_report: Peruuta raportin määritys
unblock_email_account: Poista sähköpostiosoitteen esto unblock_email_account: Poista sähköpostiosoitteen esto
unsensitive_account: Kumoa pakotus arkaluontoiseksi tiliksi unsensitive_account: Kumoa pakotus arkaluonteiseksi tiliksi
unsilence_account: Kumoa tilin rajoitus unsilence_account: Kumoa tilin rajoitus
unsuspend_account: Kumoa tilin jäädytys unsuspend_account: Kumoa tilin jäädytys
update_announcement: Päivitä tiedote update_announcement: Päivitä tiedote
@ -239,7 +239,7 @@ fi:
create_announcement_html: "%{name} loi uuden tiedotteen %{target}" create_announcement_html: "%{name} loi uuden tiedotteen %{target}"
create_canonical_email_block_html: "%{name} esti sähköpostin tiivisteellä %{target}" create_canonical_email_block_html: "%{name} esti sähköpostin tiivisteellä %{target}"
create_custom_emoji_html: "%{name} lähetti uuden emojin %{target}" create_custom_emoji_html: "%{name} lähetti uuden emojin %{target}"
create_domain_allow_html: "%{name} salli federoinnin verkkotunnuksen %{target} kanssa" create_domain_allow_html: "%{name} salli liittoutumisen verkkotunnuksen %{target} kanssa"
create_domain_block_html: "%{name} esti verkkotunnuksen %{target}" create_domain_block_html: "%{name} esti verkkotunnuksen %{target}"
create_email_domain_block_html: "%{name} esti sähköpostiverkkotunnuksen %{target}" create_email_domain_block_html: "%{name} esti sähköpostiverkkotunnuksen %{target}"
create_ip_block_html: "%{name} loi IP-säännön %{target}" create_ip_block_html: "%{name} loi IP-säännön %{target}"
@ -249,7 +249,7 @@ fi:
destroy_announcement_html: "%{name} poisti tiedotteen %{target}" destroy_announcement_html: "%{name} poisti tiedotteen %{target}"
destroy_canonical_email_block_html: "%{name} poisti sähköpostin eston tiivisteellä %{target}" destroy_canonical_email_block_html: "%{name} poisti sähköpostin eston tiivisteellä %{target}"
destroy_custom_emoji_html: "%{name} poisti emojin %{target}" destroy_custom_emoji_html: "%{name} poisti emojin %{target}"
destroy_domain_allow_html: "%{name} kielsi federoinnin verkkotunnuksen %{target} kanssa" destroy_domain_allow_html: "%{name} kielsi liittoutumisen verkkotunnuksen %{target} kanssa"
destroy_domain_block_html: "%{name} poisti verkkotunnuksen %{target} eston" destroy_domain_block_html: "%{name} poisti verkkotunnuksen %{target} eston"
destroy_email_domain_block_html: "%{name} poisti sähköpostiverkkotunnuksen %{target} eston" destroy_email_domain_block_html: "%{name} poisti sähköpostiverkkotunnuksen %{target} eston"
destroy_instance_html: "%{name} tyhjensi verkkotunnuksen %{target}" destroy_instance_html: "%{name} tyhjensi verkkotunnuksen %{target}"
@ -278,7 +278,7 @@ fi:
suspend_account_html: "%{name} jäädytti käyttäjän %{target} tilin" suspend_account_html: "%{name} jäädytti käyttäjän %{target} tilin"
unassigned_report_html: "%{name} peruutti raportin määrityksen %{target}" unassigned_report_html: "%{name} peruutti raportin määrityksen %{target}"
unblock_email_account_html: "%{name} poisti käyttäjän %{target} sähköpostiosoitteen eston" unblock_email_account_html: "%{name} poisti käyttäjän %{target} sähköpostiosoitteen eston"
unsensitive_account_html: "%{name} poisti käyttäjän %{target} median arkaluonteisen merkinnän" unsensitive_account_html: "%{name} kumosi käyttäjän %{target} median arkaluonteisuusmerkinnän"
unsilence_account_html: "%{name} kumosi käyttäjän %{target} rajoituksen" unsilence_account_html: "%{name} kumosi käyttäjän %{target} rajoituksen"
unsuspend_account_html: "%{name} kumosi käyttäjän %{target} tilin jäädytyksen" unsuspend_account_html: "%{name} kumosi käyttäjän %{target} tilin jäädytyksen"
update_announcement_html: "%{name} päivitti tiedotteen %{target}" update_announcement_html: "%{name} päivitti tiedotteen %{target}"
@ -375,12 +375,12 @@ fi:
empty: Valituksia ei löytynyt. empty: Valituksia ei löytynyt.
title: Valitukset title: Valitukset
domain_allows: domain_allows:
add_new: Salli liitto verkkotunnuksella add_new: Salli liittoutuminen tämän verkkotunnuksen kanssa
created_msg: Verkkotunnus on onnistuneesti sallittu federoinnille created_msg: Verkkotunnuksen on onnistuneesti sallittu liittoutua
destroyed_msg: Verkkotunnusta on kielletty federoimasta destroyed_msg: Verkkotunnusta on kielletty liittoutumasta
export: Vie export: Vie
import: Tuo import: Tuo
undo: Estä liitto verkkotunnukselle undo: Kiellä liittoutuminen tämän verkkotunnuksen kanssa
domain_blocks: domain_blocks:
add_new: Lisää uusi verkkotunnuksen esto add_new: Lisää uusi verkkotunnuksen esto
confirm_suspension: confirm_suspension:
@ -527,7 +527,7 @@ fi:
public_comment: Julkinen kommentti public_comment: Julkinen kommentti
purge: Tyhjennä purge: Tyhjennä
purge_description_html: Jos uskot, että tämä verkkotunnus on offline-tilassa tarkoituksella, voit poistaa kaikki verkkotunnuksen tilitietueet ja niihin liittyvät tiedot tallennustilastasi. Tämä voi kestää jonkin aikaa. purge_description_html: Jos uskot, että tämä verkkotunnus on offline-tilassa tarkoituksella, voit poistaa kaikki verkkotunnuksen tilitietueet ja niihin liittyvät tiedot tallennustilastasi. Tämä voi kestää jonkin aikaa.
title: Federointi title: Liittoutuminen
total_blocked_by_us: Estämämme total_blocked_by_us: Estämämme
total_followed_by_them: Heidän seuraama total_followed_by_them: Heidän seuraama
total_followed_by_us: Meidän seuraama total_followed_by_us: Meidän seuraama
@ -562,7 +562,7 @@ fi:
relays: relays:
add_new: Lisää uusi välittäjä add_new: Lisää uusi välittäjä
delete: Poista delete: Poista
description_html: "<strong>Federointivälittäjä</strong> on välityspalvelin, joka siirtää suuria määriä julkisia julkaisuja siihen liittyneiden palvelinten välillä. <strong>Se voi auttaa pieniä ja keskisuuria palvelimia löytämään fediversumin sisältöä</strong>, mikä muutoin vaatisi paikallisia käyttäjiä seuraamaan etäpalvalinten käyttäjiä manuaalisesti." description_html: "<strong>Liittoutumisvälittäjä</strong> on välityspalvelin, joka siirtää suuria määriä julkisia julkaisuja siihen liittyneiden palvelinten välillä. <strong>Se voi auttaa pieniä ja keskisuuria palvelimia löytämään fediversumin sisältöä</strong>, mikä muutoin vaatisi paikallisia käyttäjiä seuraamaan etäpalvalinten käyttäjiä manuaalisesti."
disable: Poista käytöstä disable: Poista käytöstä
disabled: Poissa käytöstä disabled: Poissa käytöstä
enable: Ota käyttöön enable: Ota käyttöön
@ -671,56 +671,56 @@ fi:
devops: DevOps devops: DevOps
invites: Kutsut invites: Kutsut
moderation: Valvonta moderation: Valvonta
special: Erikois special: Erityistä
delete: Poista delete: Poista
description_html: "<strong>Käyttäjärooleilla</strong> voit muokata, mihin toimintoihin ja alueisiin käyttäjäsi pääsevät käsiksi." description_html: "<strong>Käyttäjärooleilla</strong> voit muokata, mihin toimintoihin ja alueisiin käyttäjäsi pääsevät käsiksi."
edit: Muokkaa "%{name}" roolia edit: Muokkaa roolia ”%{name}”
everyone: Oletus käyttöoikeudet everyone: Oletuskäyttöoikeudet
everyone_full_description_html: Tämä on <strong>perusrooli</strong> joka vaikuttaa <strong>kaikkiin käyttäjiin</strong>, jopa ilman määrättyä roolia. Kaikki muut roolit perivät sen käyttöoikeudet. everyone_full_description_html: Tämä on <strong>perusrooli</strong>, joka vaikuttaa <strong>kaikkiin käyttäjiin</strong>, jopa ilman määrättyä roolia. Kaikki muut roolit perivät sen käyttöoikeudet.
permissions_count: permissions_count:
one: "%{count} käyttöoikeus" one: "%{count} käyttöoikeus"
other: "%{count} käyttöoikeutta" other: "%{count} käyttöoikeutta"
privileges: privileges:
administrator: Ylläpitäjä administrator: Ylläpitäjä
administrator_description: Käyttäjät, joilla on tämä käyttöoikeus, ohittavat jokaisen käyttöoikeuden administrator_description: Käyttäjät, joilla on tämä käyttöoikeus, ohittavat jokaisen käyttöoikeuden
delete_user_data: Poista käyttäjän tiedot delete_user_data: Poistaa käyttäjän tiedot
delete_user_data_description: Salli käyttäjien poistaa muiden käyttäjien tiedot viipymättä delete_user_data_description: Salli käyttäjien poistaa muiden käyttäjien tiedot viipymättä
invite_users: Kutsu käyttäjiä invite_users: Kutsua käyttäjiä
invite_users_description: Sallii käyttäjien kutsua uusia ihmisiä palvelimelle invite_users_description: Sallii käyttäjien kutsua uusia ihmisiä palvelimelle
manage_announcements: Hallitse tiedotteita manage_announcements: Hallita tiedotteita
manage_announcements_description: Sallii käyttäjien hallita tiedotteita palvelimella manage_announcements_description: Sallii käyttäjien hallita tiedotteita palvelimella
manage_appeals: Hallitse valituksia manage_appeals: Hallita valituksia
manage_appeals_description: Sallii käyttäjien tarkistaa valvontatoimia koskevia valituksia manage_appeals_description: Sallii käyttäjien tarkistaa valvontatoimia koskevia valituksia
manage_blocks: Hallitse estoja manage_blocks: Hallita estoja
manage_blocks_description: Sallii käyttäjien estää sähköpostipalveluntarjoajia ja IP-osoitteita manage_blocks_description: Sallii käyttäjien estää sähköpostipalveluntarjoajia ja IP-osoitteita
manage_custom_emojis: Hallitse mukautettuja emojeita manage_custom_emojis: Hallita mukautettuja emojeita
manage_custom_emojis_description: Sallii käyttäjien hallita mukautettuja emojeita palvelimella manage_custom_emojis_description: Sallii käyttäjien hallita mukautettuja emojeita palvelimella
manage_federation: Hallitse federointia manage_federation: Hallita liittoutumista
manage_federation_description: Sallii käyttäjien estää tai sallia federointi muiden verkkotunnusten kanssa ja hallita toimitusta manage_federation_description: Sallii käyttäjien estää tai sallia liittoutuminen muiden verkkotunnusten kanssa ja hallita toimitusta
manage_invites: Hallitse kutsuja manage_invites: Hallita kutsuja
manage_invites_description: Sallii käyttäjien selata ja poistaa kutsulinkkejä käytöstä manage_invites_description: Sallii käyttäjien selata ja poistaa kutsulinkkejä käytöstä
manage_reports: Hallitse raportteja manage_reports: Hallita raportteja
manage_reports_description: Sallii käyttäjien tarkistaa raportteja ja suorittaa valvontatoimia niitä vastaan manage_reports_description: Sallii käyttäjien tarkistaa raportteja ja suorittaa valvontatoimia niitä vastaan
manage_roles: Hallitse rooleja manage_roles: Hallita rooleja
manage_roles_description: Sallii käyttäjien hallita ja määrittää rooleja heidän alapuolellaan manage_roles_description: Sallii käyttäjien hallita ja määrittää rooleja heidän alapuolellaan
manage_rules: Hallitse sääntöjä manage_rules: Hallita sääntöjä
manage_rules_description: Sallii käyttäjien muuttaa palvelimen sääntöjä manage_rules_description: Sallii käyttäjien muuttaa palvelimen sääntöjä
manage_settings: Hallitse asetuksia manage_settings: Hallita asetuksia
manage_settings_description: Sallii käyttäjien muuttaa sivuston asetuksia manage_settings_description: Sallii käyttäjien muuttaa sivuston asetuksia
manage_taxonomies: Hallitse luokittelua manage_taxonomies: Hallita luokittelua
manage_taxonomies_description: Sallii käyttäjien tarkistaa suositun sisällön ja päivittää aihetunnisteiden asetuksia manage_taxonomies_description: Sallii käyttäjien tarkistaa suositun sisällön ja päivittää aihetunnisteiden asetuksia
manage_user_access: Hallitse käyttäjäoikeuksia manage_user_access: Hallita käyttäjäoikeuksia
manage_user_access_description: Sallii käyttäjien poistaa muiden käyttäjien kaksivaiheinen todennus käytöstä, vaihtaa heidän sähköpostiosoitteensa ja nollata heidän salasanansa manage_user_access_description: Sallii käyttäjien poistaa muiden käyttäjien kaksivaiheinen todennus käytöstä, vaihtaa heidän sähköpostiosoitteensa ja nollata heidän salasanansa
manage_users: Hallitse käyttäjiä manage_users: Hallita käyttäjiä
manage_users_description: Sallii käyttäjien tarkastella muiden käyttäjien tietoja ja suorittaa valvontatoimia heitä kohtaan manage_users_description: Sallii käyttäjien tarkastella muiden käyttäjien tietoja ja suorittaa valvontatoimia heitä kohtaan
manage_webhooks: Hallitse webhookeja manage_webhooks: Hallita webhookeja
manage_webhooks_description: Sallii käyttäjien luoda webhookeja hallinnollisiin tapahtumiin manage_webhooks_description: Sallii käyttäjien luoda webhookeja hallinnollisiin tapahtumiin
view_audit_log: Katsoa valvontalokia view_audit_log: Katsoa valvontalokia
view_audit_log_description: Sallii käyttäjien nähdä palvelimen hallinnollisten toimien historian view_audit_log_description: Sallii käyttäjien nähdä palvelimen hallinnollisten toimien historian
view_dashboard: Näytä koontinäyttö view_dashboard: Katsoa koontinäyttöä
view_dashboard_description: Sallii käyttäjien käyttää kojelautaa ja erilaisia mittareita view_dashboard_description: Sallii käyttäjien käyttää kojelautaa ja erilaisia mittareita
view_devops: DevOps view_devops: DevOps
view_devops_description: Sallii käyttäjille oikeuden käyttää Sidekiq ja pgHero dashboardeja view_devops_description: Sallii käyttäjille pääsyn Sidekiq- ja pgHero-hallintapaneeleihin
title: Roolit title: Roolit
rules: rules:
add_new: Lisää sääntö add_new: Lisää sääntö
@ -732,7 +732,7 @@ fi:
settings: settings:
about: about:
manage_rules: Hallitse palvelimen sääntöjä manage_rules: Hallitse palvelimen sääntöjä
preamble: Anna perusteellista tietoa siitä, miten palvelinta käytetään, valvotaan, rahoitetaan. preamble: Anna perusteellista tietoa siitä, miten palvelinta käytetään, valvotaan ja rahoitetaan.
rules_hint: On olemassa erityinen alue sääntöjä, joita käyttäjien odotetaan noudattavan. rules_hint: On olemassa erityinen alue sääntöjä, joita käyttäjien odotetaan noudattavan.
title: Tietoja title: Tietoja
appearance: appearance:
@ -752,7 +752,7 @@ fi:
title: Jätä käyttäjät oletusarvoisesti hakukoneindeksoinnin ulkopuolelle title: Jätä käyttäjät oletusarvoisesti hakukoneindeksoinnin ulkopuolelle
discovery: discovery:
follow_recommendations: Seuraamissuositukset follow_recommendations: Seuraamissuositukset
preamble: Mielenkiintoisen sisällön esille tuominen auttaa saamaan uusia käyttäjiä, jotka eivät ehkä tunne ketään Mastodonista. Määrittele, kuinka erilaiset etsintäominaisuudet toimivat palvelimellasi. preamble: Mielenkiintoisen sisällön esille tuominen auttaa saamaan uusia käyttäjiä, jotka eivät ehkä tunne ketään Mastodonista. Määrittele, kuinka erilaiset löytämisominaisuudet toimivat palvelimellasi.
profile_directory: Profiilihakemisto profile_directory: Profiilihakemisto
public_timelines: Julkiset aikajanat public_timelines: Julkiset aikajanat
publish_discovered_servers: Julkaise löydetyt palvelimet publish_discovered_servers: Julkaise löydetyt palvelimet
@ -765,17 +765,17 @@ fi:
users: Kirjautuneille paikallisille käyttäjille users: Kirjautuneille paikallisille käyttäjille
registrations: registrations:
preamble: Määritä, kuka voi luoda tilin palvelimellesi. preamble: Määritä, kuka voi luoda tilin palvelimellesi.
title: Rekisteröinnit title: Rekisteröityminen
registrations_mode: registrations_mode:
modes: modes:
approved: Rekisteröinti vaatii hyväksynnän approved: Rekisteröinti vaatii hyväksynnän
none: Kukaan ei voi rekisteröityä none: Kukaan ei voi rekisteröityä
open: Kaikki voivat rekisteröityä open: Kaikki voivat rekisteröityä
security: security:
authorized_fetch: Vaadi todennus yhdistetyiltä palvelimilta authorized_fetch: Vaadi todennus liittoutuvilta palvelimilta
authorized_fetch_hint: Todennuksen vaatiminen federoiduilta palvelimilta mahdollistaa sekä käyttäjä- että palvelintason estojen tiukemman valvonnan. Tämä tapahtuu kuitenkin suorituskyvyn kustannuksella, vähentää vastauksiesi tavoittavuutta ja voi aiheuttaa yhteensopivuusongelmia joidenkin federoitujen palvelujen kanssa. Tämä ei myöskään estä omistautuneita toimijoita hakemasta julkisia julkaisujasi ja tilejäsi. authorized_fetch_hint: Todennuksen vaatiminen liittoutuvilta palvelimilta mahdollistaa sekä käyttäjä- että palvelintason estojen tiukemman valvonnan. Tämä tapahtuu kuitenkin suorituskyvyn kustannuksella, vähentää vastauksiesi tavoittavuutta ja voi aiheuttaa yhteensopivuusongelmia joidenkin liittoutuvien palvelujen kanssa. Tämä ei myöskään estä omistautuneita toimijoita hakemasta julkisia julkaisujasi ja tilejäsi.
authorized_fetch_overridden_hint: Et voi tällä hetkellä muuttaa tätä asetusta, koska se on ohitettu ympäristömuuttujalla. authorized_fetch_overridden_hint: Et voi tällä hetkellä muuttaa tätä asetusta, koska se on ohitettu ympäristömuuttujalla.
federation_authentication: Yhdistettyjen palvelinten todentamisen täytäntöönpano federation_authentication: Liittoutumisen todentamisen täytäntöönpano
title: Palvelimen asetukset title: Palvelimen asetukset
site_uploads: site_uploads:
delete: Poista ladattu tiedosto delete: Poista ladattu tiedosto
@ -1239,7 +1239,7 @@ fi:
deprecated_api_multiple_keywords: Näitä parametreja ei voi muuttaa tästä sovelluksesta, koska ne koskevat useampaa kuin yhtä suodattimen avainsanaa. Käytä uudempaa sovellusta tai selainkäyttöliittymää. deprecated_api_multiple_keywords: Näitä parametreja ei voi muuttaa tästä sovelluksesta, koska ne koskevat useampaa kuin yhtä suodattimen avainsanaa. Käytä uudempaa sovellusta tai selainkäyttöliittymää.
invalid_context: Ei sisältöä tai se on virheellinen invalid_context: Ei sisältöä tai se on virheellinen
index: index:
contexts: Suodattimet %{contexts} contexts: Suodattaa kontektissa %{contexts}
delete: Poista delete: Poista
empty: Sinulla ei ole suodattimia. empty: Sinulla ei ole suodattimia.
expires_in: Vanhenee %{distance} expires_in: Vanhenee %{distance}
@ -1414,8 +1414,8 @@ fi:
not_found: ei voitu löytää not_found: ei voitu löytää
on_cooldown: Sinä olet jäähyllä on_cooldown: Sinä olet jäähyllä
followers_count: Seuraajat muuton aikana followers_count: Seuraajat muuton aikana
incoming_migrations: Siirtyminen toiselta tililtä incoming_migrations: Muutto toiselta tililtä
incoming_migrations_html: Siirtyäksesi toisesta tilistä tähän, sinun täytyy ensin <a href="%{path}">luoda tilin alias</a>. incoming_migrations_html: Muuttaaksesi toisesta tilistä tähän, sinun täytyy ensin <a href="%{path}">luoda tilin alias</a>.
moved_msg: Tilisi ohjaa nyt kohteeseen %{acct} ja seuraajiasi siirretään. moved_msg: Tilisi ohjaa nyt kohteeseen %{acct} ja seuraajiasi siirretään.
not_redirecting: Tilisi ei ohjaa tällä hetkellä mihinkään muuhun tiliin. not_redirecting: Tilisi ei ohjaa tällä hetkellä mihinkään muuhun tiliin.
on_cooldown: Olet siirtänyt tilisi äskettäin. Tämä toiminto tulee saataville uudelleen %{count} päivän kuluttua. on_cooldown: Olet siirtänyt tilisi äskettäin. Tämä toiminto tulee saataville uudelleen %{count} päivän kuluttua.
@ -1707,7 +1707,7 @@ fi:
keep_self_bookmark: Säilytä kirjanmerkkeihin lisäämäsi julkaisut keep_self_bookmark: Säilytä kirjanmerkkeihin lisäämäsi julkaisut
keep_self_bookmark_hint: Ei poista julkaisujasi, jos olet lisännyt ne kirjanmerkkeihin keep_self_bookmark_hint: Ei poista julkaisujasi, jos olet lisännyt ne kirjanmerkkeihin
keep_self_fav: Säilytä suosikkeihin lisäämäsi julkaisut keep_self_fav: Säilytä suosikkeihin lisäämäsi julkaisut
keep_self_fav_hint: Ei poista julkaisujasi, jos olet lisännyt ne suosikkeihin keep_self_fav_hint: Ei poista julkaisujasi, jos olet lisännyt ne suosikkeihisi
min_age: min_age:
'1209600': 2 viikkoa '1209600': 2 viikkoa
'15778476': 6 kuukautta '15778476': 6 kuukautta
@ -1786,7 +1786,7 @@ fi:
spam: Roskaposti spam: Roskaposti
violation: Sisältö rikkoo seuraavia yhteisön sääntöjä violation: Sisältö rikkoo seuraavia yhteisön sääntöjä
explanation: explanation:
delete_statuses: Joidenkin julkaisuistasi on havaittu rikkovan ainakin yhtä yhteisön sääntöä, ja instanssin %{instance} valvojat ovat poistaneet ne. delete_statuses: Joidenkin julkaisuistasi on havaittu rikkovan ainakin yhtä yhteisön sääntöä, joten instanssin %{instance} valvojat ovat poistaneet ne.
disable: Et voi enää käyttää tiliäsi, mutta profiilisi ja muut tiedot pysyvät muuttumattomina. Voit pyytää varmuuskopiota tiedoistasi, vaihtaa tilin asetuksia tai poistaa tilisi. disable: Et voi enää käyttää tiliäsi, mutta profiilisi ja muut tiedot pysyvät muuttumattomina. Voit pyytää varmuuskopiota tiedoistasi, vaihtaa tilin asetuksia tai poistaa tilisi.
mark_statuses_as_sensitive: Palvelimen %{instance} valvojat ovat merkinneet osan julkaisuistasi arkaluonteisiksi. Tämä tarkoittaa sitä, että ihmisten täytyy napauttaa mediaa ennen kuin sen esikatselu näytetään. Voit merkitä median itse arkaluonteiseksi, kun julkaiset tulevaisuudessa. mark_statuses_as_sensitive: Palvelimen %{instance} valvojat ovat merkinneet osan julkaisuistasi arkaluonteisiksi. Tämä tarkoittaa sitä, että ihmisten täytyy napauttaa mediaa ennen kuin sen esikatselu näytetään. Voit merkitä median itse arkaluonteiseksi, kun julkaiset tulevaisuudessa.
sensitive: Tästä lähtien kaikki ladatut mediatiedostot merkitään arkaluonteisiksi ja piilotetaan napsautusvaroituksen taakse. sensitive: Tästä lähtien kaikki ladatut mediatiedostot merkitään arkaluonteisiksi ja piilotetaan napsautusvaroituksen taakse.
@ -1807,7 +1807,7 @@ fi:
disable: Tili jäädytetty disable: Tili jäädytetty
mark_statuses_as_sensitive: Julkaisut merkitty arkaluonteisiksi mark_statuses_as_sensitive: Julkaisut merkitty arkaluonteisiksi
none: Varoitus none: Varoitus
sensitive: Tili on merkitty arkaluonteiseksi sensitive: Tili merkitty arkaluonteiseksi
silence: Tiliä rajoitettu silence: Tiliä rajoitettu
suspend: Tili jäädytetty suspend: Tili jäädytetty
welcome: welcome:

@ -791,9 +791,9 @@ si:
guide_link_text: පරිවර්තකයින්ට දායක වීමට හැකිය. guide_link_text: පරිවර්තකයින්ට දායක වීමට හැකිය.
sensitive_content: සංවේදී අන්තර්ගත sensitive_content: සංවේදී අන්තර්ගත
application_mailer: application_mailer:
notification_preferences: ඊමේල් මනාප වෙනස් කරන්න notification_preferences: වි-තැපැල් අභිප්‍රේත වෙනස් කරන්න
salutation: "%{name}," salutation: "%{name},"
settings: 'ඊමේල් මනාප වෙනස් කරන්න: %{link}' settings: 'වි-තැපැල් අභිප්‍රේත වෙනස් කරන්න: %{link}'
view: 'දැක්ම:' view: 'දැක්ම:'
view_profile: පැතිකඩ බලන්න view_profile: පැතිකඩ බලන්න
view_status: ලිපිය බලන්න view_status: ලිපිය බලන්න
@ -1405,11 +1405,11 @@ si:
title: සංරක්ෂිත රැගෙන යාම title: සංරක්ෂිත රැගෙන යාම
suspicious_sign_in: suspicious_sign_in:
change_password: මුරපදය වෙනස් කරන්න change_password: මුරපදය වෙනස් කරන්න
details: 'පුරනය වීමේ විස්තර මෙන්න:' details: 'ප්‍රවේශයට අදාළ විස්තර:'
explanation: අපි නව IP ලිපිනයකින් ඔබගේ ගිණුමට පුරනය වීමක් අනාවරණය කරගෙන ඇත. explanation: ඔබගේ ගිණුමට නව අ.ජා.කෙ. (IP) ලිපිනයකින් ප්‍රවේශයක් අනාවරණය වී ඇත.
further_actions_html: ෙය ඔබ නොවේ නම්, අපි ඔබට වහාම %{action} ලෙස නිර්දේශ කර ඔබගේ ගිණුම සුරක්ෂිතව තබා ගැනීමට සාධක දෙකක සත්‍යාපනය සබල කරන්න. further_actions_html: ේ ඔබ නොවේ නම්, වහාම %{action}. ඔබගේ ගිණුම සුරක්‍ෂිතව තබා ගැනීමට ද්වි-සාධකය සබල කරන්න.
subject: ඔබගේ ගිණුම නව IP ලිපිනයකින් ප්‍රවේශ වී ඇත subject: ඔබගේ ගිණුම නව අ.ජා.කෙ. (IP) ලිපිනයකින් ප්‍රවේශ වී ඇත
title: නව පුරනය වීමක් title: නව ප්‍රවේශයක්
warning: warning:
appeal: අභියාචනයක් ඉදිරිපත් කරන්න appeal: අභියාචනයක් ඉදිරිපත් කරන්න
appeal_description: මෙය දෝෂයක් බව ඔබ විශ්වාස කරන්නේ නම්, ඔබට %{instance}හි කාර්ය මණ්ඩලයට අභියාචනයක් ඉදිරිපත් කළ හැක. appeal_description: මෙය දෝෂයක් බව ඔබ විශ්වාස කරන්නේ නම්, ඔබට %{instance}හි කාර්ය මණ්ඩලයට අභියාචනයක් ඉදිරිපත් කළ හැක.

@ -25,7 +25,7 @@ fi:
types: types:
disable: Estä käyttäjää käyttämästä tiliään, mutta älä poista tai piilota sen sisältöä. disable: Estä käyttäjää käyttämästä tiliään, mutta älä poista tai piilota sen sisältöä.
none: Käytä tätä lähettääksesi varoituksen käyttäjälle käynnistämättä mitään muita toimintoja. none: Käytä tätä lähettääksesi varoituksen käyttäjälle käynnistämättä mitään muita toimintoja.
sensitive: Pakota kaikki tämän käyttäjän mediatiedostot arkaluontoisiksi. sensitive: Pakota kaikki tämän käyttäjän mediatiedostot arkaluonteisiksi.
silence: Estä käyttäjää lähettämästä viestejä julkisesti, piilota hänen viestinsä ja ilmoituksensa ihmisiltä, jotka eivät seuraa häntä. Sulkee kaikki tämän tilin raportit. silence: Estä käyttäjää lähettämästä viestejä julkisesti, piilota hänen viestinsä ja ilmoituksensa ihmisiltä, jotka eivät seuraa häntä. Sulkee kaikki tämän tilin raportit.
suspend: Estä kaikki vuorovaikutus tältä -tai tälle tilille ja poista sen kaikki sisältö. Päätös voidaan peruuttaa 30 päivän aikana. Sulkee kaikki raportit tätä tiliä vasten. suspend: Estä kaikki vuorovaikutus tältä -tai tälle tilille ja poista sen kaikki sisältö. Päätös voidaan peruuttaa 30 päivän aikana. Sulkee kaikki raportit tätä tiliä vasten.
warning_preset_id: Valinnainen. Voit silti lisätä mukautetun tekstin esiasetuksen loppuun warning_preset_id: Valinnainen. Voit silti lisätä mukautetun tekstin esiasetuksen loppuun
@ -79,14 +79,14 @@ fi:
activity_api_enabled: Paikallisesti julkaistujen julkaisujen, aktiivisten käyttäjien ja rekisteröitymisten viikoittainen määrä activity_api_enabled: Paikallisesti julkaistujen julkaisujen, aktiivisten käyttäjien ja rekisteröitymisten viikoittainen määrä
backups_retention_period: Säilytä luodut arkistot määritetyn määrän päiviä. backups_retention_period: Säilytä luodut arkistot määritetyn määrän päiviä.
bootstrap_timeline_accounts: Nämä tilit kiinnitetään uusien käyttäjien seuraamissuositusten yläpuolelle. bootstrap_timeline_accounts: Nämä tilit kiinnitetään uusien käyttäjien seuraamissuositusten yläpuolelle.
closed_registrations_message: Näkyy, kun ilmoittautuminen on suljettu closed_registrations_message: Näkyy, kun rekisteröityminen on suljettu
content_cache_retention_period: Viestit muilta palvelimilta poistetaan määritetyn määrän päiviä jälkeen, kun arvo on asetettu positiiviseksi. Tämä voi olla peruuttamatonta. content_cache_retention_period: Kaikki julkaisut ja tehostukset muilta palvelimilta poistetaan, kun määritelty määrä päiviä on kulunut. Osaa julkaisuista voi olla mahdoton palauttaa. Kaikki julkaisuihin liittyvät kirjanmerkit, suosikit ja tehostukset menetetään, eikä niitä voi palauttaa.
custom_css: Voit käyttää mukautettuja tyylejä Mastodonin verkkoversiossa. custom_css: Voit käyttää mukautettuja tyylejä Mastodonin verkkoversiossa.
mascot: Ohittaa kuvituksen edistyneessä selainkäyttöliittymässä. mascot: Ohittaa kuvituksen edistyneessä selainkäyttöliittymässä.
media_cache_retention_period: Ladatut mediatiedostot poistetaan määritetyn määrän päiviä jälkeen, kun arvo on positiivinen ja ladataan uudelleen pyynnöstä. media_cache_retention_period: Ladatut mediatiedostot poistetaan määritetyn määrän päiviä jälkeen, kun arvo on positiivinen ja ladataan uudelleen pyynnöstä.
peers_api_enabled: Luettelo verkkotunnuksista, jotka tämä palvelin on kohdannut fediversumissa. Se ei kerro, oletko liitossa tietyn palvelimen kanssa, vaan että palvelimesi on ylipäätään tietoinen siitä. Tätä tietoa käytetään palveluissa, jotka keräävät tilastoja liittoutumisesta yleisellä tasolla. peers_api_enabled: Luettelo verkkotunnuksista, jotka tämä palvelin on kohdannut fediversumissa. Se ei kerro, oletko liitossa tietyn palvelimen kanssa, vaan että palvelimesi on ylipäätään tietoinen siitä. Tätä tietoa käytetään palveluissa, jotka keräävät tilastoja federoinnista yleisellä tasolla.
profile_directory: Profiilihakemisto lueteloi kaikki käyttäjät, jotka ovat ilmoittaneet olevansa löydettävissä. profile_directory: Profiilihakemisto lueteloi kaikki käyttäjät, jotka ovat ilmoittaneet olevansa löydettävissä.
require_invite_text: Kun kirjautuminen vaatii manuaalisen hyväksynnän, tee ”Miksi haluat liittyä?” teksti syötetään pakolliseksi eikä vapaaehtoiseksi require_invite_text: Kun rekisteröityminen vaatii manuaalisen hyväksynnän, tee ”Miksi haluat liittyä?” -tekstikentästä pakollinen vapaaehtoisen sijaan
site_contact_email: Kuinka ihmiset voivat tavoittaa sinut oikeudellisissa tai tukikysymyksissä. site_contact_email: Kuinka ihmiset voivat tavoittaa sinut oikeudellisissa tai tukikysymyksissä.
site_contact_username: Miten ihmiset voivat tavoittaa sinut Mastodonissa. site_contact_username: Miten ihmiset voivat tavoittaa sinut Mastodonissa.
site_extended_description: Kaikki lisätiedot, jotka voivat olla hyödyllisiä kävijöille ja käyttäjille. Voidaan jäsentää Markdown-syntaksilla. site_extended_description: Kaikki lisätiedot, jotka voivat olla hyödyllisiä kävijöille ja käyttäjille. Voidaan jäsentää Markdown-syntaksilla.
@ -96,10 +96,10 @@ fi:
status_page_url: URL-osoite sivulle, jonka kautta tämän palvelimen tila voidaan ongelmatilanteissa tarkastaa status_page_url: URL-osoite sivulle, jonka kautta tämän palvelimen tila voidaan ongelmatilanteissa tarkastaa
theme: Teema, jonka uloskirjautuneet vierailijat ja uudet käyttäjät näkevät. theme: Teema, jonka uloskirjautuneet vierailijat ja uudet käyttäjät näkevät.
thumbnail: Noin 2:1 kuva näytetään palvelimen tietojen rinnalla. thumbnail: Noin 2:1 kuva näytetään palvelimen tietojen rinnalla.
timeline_preview: Uloskirjautuneet vierailijat voivat selata uusimpia julkisia viestejä, jotka ovat saatavilla palvelimella. timeline_preview: Uloskirjautuneet vierailijat voivat selata uusimpia julkisia julkaisuja, jotka ovat saatavilla palvelimella.
trendable_by_default: Ohita suositun sisällön manuaalinen tarkistus. Yksittäisiä kohteita voidaan edelleen poistaa jälkikäteen. trendable_by_default: Ohita suositun sisällön manuaalinen tarkistus. Yksittäisiä kohteita voidaan edelleen poistaa jälkikäteen.
trends: Trendit osoittavat, mitkä julkaisut, aihetunnisteet ja uutiset ovat saamassa vetoa palvelimellasi. trends: Trendit osoittavat, mitkä julkaisut, aihetunnisteet ja uutiset ovat saamassa vetoa palvelimellasi.
trends_as_landing_page: Näytä vierailijoille ja uloskirjautuneille käyttäjille suosittu sisältö palvelininstanssin kuvaustekstin sijaan. Edellytyksenä on, että suosittu sisältö -ominaisuus on käytössä. trends_as_landing_page: Näytä vierailijoille ja uloskirjautuneille käyttäjille suosittua sisältöä palvelimen kuvauksen sijaan. Edellyttää, että trendit on otettu käyttöön.
form_challenge: form_challenge:
current_password: Olet menossa suojatulle alueelle current_password: Olet menossa suojatulle alueelle
imports: imports:
@ -129,9 +129,9 @@ fi:
chosen_languages: Kun valittu, vain valituilla kielillä kirjoitetut julkaisut näkyvät julkisilla aikajanoilla chosen_languages: Kun valittu, vain valituilla kielillä kirjoitetut julkaisut näkyvät julkisilla aikajanoilla
role: Rooli määrää, mitkä käyttöoikeudet käyttäjällä on role: Rooli määrää, mitkä käyttöoikeudet käyttäjällä on
user_role: user_role:
color: Väri, jota käytetään roolin koko käyttöliittymässä, RGB heksamuodossa color: Väri, jota käytetään roolille kaikkialla käyttöliittymässä, RGB-heksadesimaalimuodossa
highlighted: Tämä tekee roolista julkisesti näkyvän highlighted: Tämä tekee roolista julkisesti näkyvän
name: Roolin julkinen nimi, jos rooli on asetettu näytettäväksi mekkinä name: Roolin julkinen nimi, jos rooli on asetettu näytettäväksi merkkinä
permissions_as_keys: Käyttäjillä, joilla on tämä rooli, on käyttöoikeus... permissions_as_keys: Käyttäjillä, joilla on tämä rooli, on käyttöoikeus...
position: Korkeampi rooli ratkaisee konfliktit tietyissä tilanteissa. Tiettyjä toimintoja voidaan suorittaa vain rooleille, joiden prioriteetti on pienempi position: Korkeampi rooli ratkaisee konfliktit tietyissä tilanteissa. Tiettyjä toimintoja voidaan suorittaa vain rooleille, joiden prioriteetti on pienempi
webhook: webhook:
@ -238,10 +238,10 @@ fi:
hide: Piilota kokonaan hide: Piilota kokonaan
warn: Piilota ja näytä varoitus warn: Piilota ja näytä varoitus
form_admin_settings: form_admin_settings:
activity_api_enabled: Julkaise yhteenlasketut tilastot käyttäjätoiminnasta rajapinnassa activity_api_enabled: Julkaise yhteenlasketut tilastot käyttäjätoiminnasta ohjelmointirajapinnassa
backups_retention_period: Käyttäjän arkiston säilytysaika backups_retention_period: Käyttäjän arkiston säilytysaika
bootstrap_timeline_accounts: Suosittele aina näitä tilejä uusille käyttäjille bootstrap_timeline_accounts: Suosittele aina näitä tilejä uusille käyttäjille
closed_registrations_message: Mukautettu viesti, kun kirjautumisia ei ole saatavilla closed_registrations_message: Mukautettu viesti, kun rekisteröityminen ei ole saatavilla
content_cache_retention_period: Sisällön välimuistin säilytysaika content_cache_retention_period: Sisällön välimuistin säilytysaika
custom_css: Mukautettu CSS custom_css: Mukautettu CSS
mascot: Mukautettu maskotti (legacy) mascot: Mukautettu maskotti (legacy)
@ -251,7 +251,7 @@ fi:
registrations_mode: Kuka voi rekisteröityä registrations_mode: Kuka voi rekisteröityä
require_invite_text: Vaadi syy liittyä require_invite_text: Vaadi syy liittyä
show_domain_blocks: Näytä verkkotunnusten estot show_domain_blocks: Näytä verkkotunnusten estot
show_domain_blocks_rationale: Näytä miksi verkkotunnukset on estetty show_domain_blocks_rationale: Näytä, miksi verkkotunnukset on estetty
site_contact_email: Ota yhteyttä sähköpostilla site_contact_email: Ota yhteyttä sähköpostilla
site_contact_username: Yhteyshenkilön käyttäjänimi site_contact_username: Yhteyshenkilön käyttäjänimi
site_extended_description: Laajennettu kuvaus site_extended_description: Laajennettu kuvaus
@ -261,10 +261,10 @@ fi:
status_page_url: Tilasivun URL-osoite status_page_url: Tilasivun URL-osoite
theme: Oletusteema theme: Oletusteema
thumbnail: Palvelimen pikkukuva thumbnail: Palvelimen pikkukuva
timeline_preview: Salli todentamaton pääsy julkiselle aikajanalle timeline_preview: Salli todentamaton pääsy julkisille aikajanoille
trendable_by_default: Salli trendit ilman ennakkotarkastusta trendable_by_default: Salli trendit ilman ennakkotarkastusta
trends: Trendit käyttöön trends: Ota trendit käyttöön
trends_as_landing_page: Käytä suosittua sisältöä aloitussivuna trends_as_landing_page: Käytä trendejä aloitussivuna
interactions: interactions:
must_be_follower: Estä ilmoitukset käyttäjiltä, jotka eivät seuraa sinua must_be_follower: Estä ilmoitukset käyttäjiltä, jotka eivät seuraa sinua
must_be_following: Estä ilmoitukset käyttäjiltä, joita et seuraa must_be_following: Estä ilmoitukset käyttäjiltä, joita et seuraa
@ -313,7 +313,7 @@ fi:
time_zone: Aikavyöhyke time_zone: Aikavyöhyke
user_role: user_role:
color: Merkin väri color: Merkin väri
highlighted: Näyttä rooli merkkinä käyttäjäprofiileissa highlighted: Näytä rooli merkkinä käyttäjäprofiileissa
name: Nimi name: Nimi
permissions_as_keys: Oikeudet permissions_as_keys: Oikeudet
position: Prioriteetti position: Prioriteetti

@ -5,7 +5,7 @@ zh-TW:
account: account:
discoverable: 公開嘟文及個人檔案可能於各 Mastodon 功能中被推薦,並且您的個人檔案可能被推薦至其他使用者。 discoverable: 公開嘟文及個人檔案可能於各 Mastodon 功能中被推薦,並且您的個人檔案可能被推薦至其他使用者。
display_name: 完整名稱或暱稱。 display_name: 完整名稱或暱稱。
fields: 烘培雞,自我認同代稱,年齡,及任何您想分享的。 fields: 烘培雞、自我認同代稱、年齡,及任何您想分享的。
indexable: 您的公開嘟文可能會顯示於 Mastodon 之搜尋結果中。曾與您嘟文互動過的人可能無論如何都能搜尋它們。 indexable: 您的公開嘟文可能會顯示於 Mastodon 之搜尋結果中。曾與您嘟文互動過的人可能無論如何都能搜尋它們。
note: '您可以 @mention 其他人或者使用 #主題標籤。' note: '您可以 @mention 其他人或者使用 #主題標籤。'
show_collections: 人們將能瀏覽您跟隨中及跟隨者帳號。您所跟隨之人能得知您正在跟隨其帳號。 show_collections: 人們將能瀏覽您跟隨中及跟隨者帳號。您所跟隨之人能得知您正在跟隨其帳號。
@ -31,7 +31,7 @@ zh-TW:
warning_preset_id: 選用。您仍可在預設的結尾新增自訂文字 warning_preset_id: 選用。您仍可在預設的結尾新增自訂文字
announcement: announcement:
all_day: 核取後,只會顯示出時間範圍中的日期部分 all_day: 核取後,只會顯示出時間範圍中的日期部分
ends_at: 可選的,公告會該時間點自動取消發布 ends_at: 可選的,公告會該時間點自動取消發布
scheduled_at: 空白則立即發布公告 scheduled_at: 空白則立即發布公告
starts_at: 可選的,讓公告在特定時間範圍內顯示 starts_at: 可選的,讓公告在特定時間範圍內顯示
text: 您可以使用嘟文語法,但請小心別讓公告太鴨霸而佔據使用者的整個版面。 text: 您可以使用嘟文語法,但請小心別讓公告太鴨霸而佔據使用者的整個版面。
@ -59,8 +59,8 @@ zh-TW:
setting_display_media_default: 隱藏標為敏感內容的媒體 setting_display_media_default: 隱藏標為敏感內容的媒體
setting_display_media_hide_all: 總是隱藏所有媒體 setting_display_media_hide_all: 總是隱藏所有媒體
setting_display_media_show_all: 總是顯示標為敏感內容的媒體 setting_display_media_show_all: 總是顯示標為敏感內容的媒體
setting_use_blurhash: 彩色漸層圖樣是基於隱藏媒體內容顏色產生,所有細節變得模糊 setting_use_blurhash: 彩色漸層圖樣是基於隱藏媒體內容顏色產生,所有細節變得模糊
setting_use_pending_items: 關閉自動捲動更新,時間軸只會點擊後更新 setting_use_pending_items: 關閉自動捲動更新,時間軸只會點擊後更新
username: 您可以使用字幕、數字與底線 username: 您可以使用字幕、數字與底線
whole_word: 如果關鍵字或詞組僅有字母與數字,則其將只在符合整個單字的時候才會套用 whole_word: 如果關鍵字或詞組僅有字母與數字,則其將只在符合整個單字的時候才會套用
domain_allow: domain_allow:
@ -126,7 +126,7 @@ zh-TW:
tag: tag:
name: 您只能變更大小寫,例如,以使其更易讀。 name: 您只能變更大小寫,例如,以使其更易讀。
user: user:
chosen_languages: 當選取時,只有選取語言之嘟文會公開時間軸中顯示 chosen_languages: 當選取時,只有選取語言之嘟文會公開時間軸中顯示
role: 角色控制使用者有哪些權限 role: 角色控制使用者有哪些權限
user_role: user_role:
color: 在整個使用者介面中用於角色的顏色,十六進位格式的 RGB color: 在整個使用者介面中用於角色的顏色,十六進位格式的 RGB

@ -353,6 +353,7 @@ sk:
silence: Obmedz silence: Obmedz
suspend: Vylúč suspend: Vylúč
title: Nové blokovanie domény title: Nové blokovanie domény
not_permitted: Nemáš povolenie na vykonanie tohto kroku
obfuscate: Zatemniť názov domény obfuscate: Zatemniť názov domény
private_comment: Súkromný komentár private_comment: Súkromný komentár
private_comment_hint: Odôvodni toto doménové obmedzenie, pre vnútorné vyrozumenie moderátorov. private_comment_hint: Odôvodni toto doménové obmedzenie, pre vnútorné vyrozumenie moderátorov.

@ -390,7 +390,7 @@ zh-TW:
domain: 站點 domain: 站點
edit: 更改封鎖的站台 edit: 更改封鎖的站台
existing_domain_block: 您已對 %{name} 施加更嚴格的限制。 existing_domain_block: 您已對 %{name} 施加更嚴格的限制。
existing_domain_block_html: 您已對 %{name} 施加更嚴格的限制,您需要先 <a href="%{unblock_url}">解除封鎖</a>。 existing_domain_block_html: 您已對 %{name} 施加更嚴格的限制,您需要先<a href="%{unblock_url}">解除封鎖</a>。
export: 匯出 export: 匯出
import: 匯入 import: 匯入
new: new:
@ -451,9 +451,7 @@ zh-TW:
title: 匯入網域黑名單 title: 匯入網域黑名單
no_file: 尚未選擇檔案 no_file: 尚未選擇檔案
follow_recommendations: follow_recommendations:
description_html: |- description_html: "<strong>跟隨建議幫助新使用者們快速找到有趣的內容</strong>。當使用者沒有與其他帳號有足夠多的互動以建立個人化跟隨建議時,這些帳號將會被推薦。這些帳號將基於某選定語言之高互動和高本地跟隨者數量帳號而每日重新更新。"
<strong>跟隨建議幫助新使用者們快速找到有趣的內容</strong>。當使用者沒有與其他帳號有足夠多的互動以建立個人化跟隨建議時,這些帳號將會被推薦。這些帳號將基於某選定語言之高互動和高本地跟隨者數量帳號而
每日重新更新。
language: 對於語言 language: 對於語言
status: 狀態 status: 狀態
suppress: 取消跟隨建議 suppress: 取消跟隨建議
@ -553,7 +551,7 @@ zh-TW:
relays: relays:
add_new: 新增中繼站 add_new: 新增中繼站
delete: 刪除 delete: 刪除
description_html: "<strong>聯邦中繼站</strong> 是種中繼伺服器,會訂閱並推送至此中繼站的伺服器之間交換大量的公開嘟文。<strong>中繼站也能協助小型或中型伺服器從聯邦宇宙中探索內容</strong>,而無須本地使用者手動跟隨遠端伺服器的其他使用者。" description_html: "<strong>聯邦中繼站</strong> 是種中繼伺服器,會訂閱並推送至此中繼站的伺服器之間交換大量的公開嘟文。<strong>中繼站也能協助小型或中型伺服器從聯邦宇宙中探索內容</strong>,而無須本地使用者手動跟隨遠端伺服器的其他使用者。"
disable: 停用 disable: 停用
disabled: 停用 disabled: 停用
enable: 啟用 enable: 啟用
@ -988,7 +986,7 @@ zh-TW:
aliases: aliases:
add_new: 建立別名 add_new: 建立別名
created_msg: 成功建立別名。您可以自舊帳號開始轉移。 created_msg: 成功建立別名。您可以自舊帳號開始轉移。
deleted_msg: 成功移除別名。您將無法再由舊帳號轉移目前的帳號。 deleted_msg: 成功移除別名。您將無法再由舊帳號轉移目前的帳號。
empty: 您目前沒有任何別名。 empty: 您目前沒有任何別名。
hint_html: 如果想由其他帳號轉移至此帳號,您可以在此處新增別名,稍後系統將容許您將跟隨者由舊帳號轉移至此。此項作業是<strong>無害且可復原的</strong>。 <strong>帳號的遷移程序需要在舊帳號啟動</strong>。 hint_html: 如果想由其他帳號轉移至此帳號,您可以在此處新增別名,稍後系統將容許您將跟隨者由舊帳號轉移至此。此項作業是<strong>無害且可復原的</strong>。 <strong>帳號的遷移程序需要在舊帳號啟動</strong>。
remove: 取消連結別名 remove: 取消連結別名
@ -999,7 +997,7 @@ zh-TW:
confirmation_dialogs: 確認對話框 confirmation_dialogs: 確認對話框
discovery: 探索 discovery: 探索
localization: localization:
body: Mastodon 是由志願者翻譯 body: Mastodon 是由志願者翻譯。
guide_link: https://crowdin.com/project/mastodon guide_link: https://crowdin.com/project/mastodon
guide_link_text: 每個人都能貢獻。 guide_link_text: 每個人都能貢獻。
sensitive_content: 敏感內容 sensitive_content: 敏感內容
@ -1042,8 +1040,8 @@ zh-TW:
log_in_with: 登入,使用 log_in_with: 登入,使用
login: 登入 login: 登入
logout: 登出 logout: 登出
migrate_account: 轉移另一個帳號 migrate_account: 轉移另一個帳號
migrate_account_html: 如果您希望引導他人跟隨另一個帳號,請 <a href="%{path}">到這裡設定</a>。 migrate_account_html: 如果您希望引導他人跟隨另一個帳號,請<a href="%{path}">至這裡設定</a>。
or_log_in_with: 或透過其他方式登入 or_log_in_with: 或透過其他方式登入
privacy_policy_agreement_html: 我已閱讀且同意 <a href="%{privacy_policy_path}" target="_blank">隱私權政策</a> privacy_policy_agreement_html: 我已閱讀且同意 <a href="%{privacy_policy_path}" target="_blank">隱私權政策</a>
progress: progress:
@ -1072,7 +1070,7 @@ zh-TW:
email_below_hint_html: 請檢查您的垃圾郵件資料夾,或是請求另一個。如果是錯的,您可以更正您的電子郵件地址。 email_below_hint_html: 請檢查您的垃圾郵件資料夾,或是請求另一個。如果是錯的,您可以更正您的電子郵件地址。
email_settings_hint_html: 請點擊我們寄給您連結以驗證 %{email}。我們將於此稍候。 email_settings_hint_html: 請點擊我們寄給您連結以驗證 %{email}。我們將於此稍候。
link_not_received: 無法取得連結嗎? link_not_received: 無法取得連結嗎?
new_confirmation_instructions_sent: 您將會在幾分鐘之內收到新的包含確認連結的電子郵件! new_confirmation_instructions_sent: 您將幾分鐘之內收到新的包含確認連結的電子郵件!
title: 請檢查您的收件匣 title: 請檢查您的收件匣
sign_in: sign_in:
preamble_html: 請使用您於 <strong>%{domain}</strong> 的帳號密碼登入。若您的帳號託管於其他伺服器,您將無法在此登入。 preamble_html: 請使用您於 <strong>%{domain}</strong> 的帳號密碼登入。若您的帳號託管於其他伺服器,您將無法在此登入。
@ -1084,7 +1082,7 @@ zh-TW:
status: status:
account_status: 帳號狀態 account_status: 帳號狀態
confirming: 等待電子郵件確認完成。 confirming: 等待電子郵件確認完成。
functional: 您的帳號可以正常使用了。 functional: "您的帳號可以正常使用了。🎉"
pending: 管管們正在處理您的申請,這可能需要一點時間處理。我們將於申請通過後以電子郵件方式通知您。 pending: 管管們正在處理您的申請,這可能需要一點時間處理。我們將於申請通過後以電子郵件方式通知您。
redirecting_to: 您的帳號因目前重定向至 %{acct} 而被停用。 redirecting_to: 您的帳號因目前重定向至 %{acct} 而被停用。
view_strikes: 檢視針對您帳號過去的警示 view_strikes: 檢視針對您帳號過去的警示
@ -1410,7 +1408,7 @@ zh-TW:
followers: 此動作將會將目前帳號的所有跟隨者轉移至新帳號 followers: 此動作將會將目前帳號的所有跟隨者轉移至新帳號
only_redirect_html: 或者,您也可以<a href="%{path}">僅在您的個人檔案中設定重新導向</a>。 only_redirect_html: 或者,您也可以<a href="%{path}">僅在您的個人檔案中設定重新導向</a>。
other_data: 其他資料並不會自動轉移 other_data: 其他資料並不會自動轉移
redirect: 您目前的帳號將會在個人檔案頁面新增重新導向公告,並會被排除在搜尋結果之外 redirect: 您目前的帳號將個人檔案頁面新增重新導向公告,並會被排除在搜尋結果之外
moderation: moderation:
title: 站務 title: 站務
move_handler: move_handler:
@ -1499,9 +1497,7 @@ zh-TW:
posting_defaults: 嘟文預設值 posting_defaults: 嘟文預設值
public_timelines: 公開時間軸 public_timelines: 公開時間軸
privacy: privacy:
hint_html: |- hint_html: "<strong>自訂您希望如何讓您的個人檔案及嘟文被發現。</strong>藉由啟用一系列 Mastodon 功能以幫助您觸及更廣的受眾。煩請花些時間確認您是否欲啟用這些設定。"
<strong>自訂您希望如何讓您的個人檔案及嘟文被找到。</strong>
藉由啟用一系列 Mastodon 功能以幫助您觸及更廣的受眾。煩請花些時間確認您是否欲啟用這些設定。
privacy: 隱私權 privacy: 隱私權
privacy_hint_html: 控制您希望向其他人揭露之內容。人們透過瀏覽其他人的跟隨者與其發嘟之應用程式發現有趣的個人檔案和酷炫的 Mastodon 應用程式,但您能選擇將其隱藏。 privacy_hint_html: 控制您希望向其他人揭露之內容。人們透過瀏覽其他人的跟隨者與其發嘟之應用程式發現有趣的個人檔案和酷炫的 Mastodon 應用程式,但您能選擇將其隱藏。
reach: 觸及 reach: 觸及
@ -1665,7 +1661,7 @@ zh-TW:
enabled: 自動刪除舊嘟文 enabled: 自動刪除舊嘟文
enabled_hint: 一旦達到指定的保存期限,就會自動刪除您的嘟文,除非該嘟文符合下列例外 enabled_hint: 一旦達到指定的保存期限,就會自動刪除您的嘟文,除非該嘟文符合下列例外
exceptions: 例外 exceptions: 例外
explanation: 因為刪除嘟文是耗費資源的操作,當伺服器不那麼忙碌時才會慢慢完成。因此,您的嘟文會在到達保存期限後一段時間才會被刪除。 explanation: 因為刪除嘟文是耗費資源的操作,當伺服器不那麼忙碌時才會慢慢完成。因此,您的嘟文將於到達保存期限後一段時間才會被刪除。
ignore_favs: 忽略最愛數 ignore_favs: 忽略最愛數
ignore_reblogs: 忽略轉嘟數 ignore_reblogs: 忽略轉嘟數
interaction_exceptions: 基於互動的例外規則 interaction_exceptions: 基於互動的例外規則

@ -16,37 +16,63 @@ describe Rack::Attack, type: :request do
# https://github.com/rack/rack-attack/blob/v6.6.1/lib/rack/attack/cache.rb#L64-L66 # https://github.com/rack/rack-attack/blob/v6.6.1/lib/rack/attack/cache.rb#L64-L66
# So we want to minimize `Time.now.to_i % period` # So we want to minimize `Time.now.to_i % period`
travel_to Time.zone.at((Time.now.to_i / period.seconds).to_i * period.seconds) travel_to Time.zone.at(counter_prefix * period.seconds)
end end
context 'when the number of requests is lower than the limit' do context 'when the number of requests is lower than the limit' do
before do
below_limit.times { increment_counter }
end
it 'does not change the request status' do it 'does not change the request status' do
limit.times do expect { request.call }.to change { throttle_count }.by(1)
request.call
expect(response).to_not have_http_status(429) expect(response).to_not have_http_status(429)
end
end end
end end
context 'when the number of requests is higher than the limit' do context 'when the number of requests is higher than the limit' do
before do
above_limit.times { increment_counter }
end
it 'returns http too many requests after limit and returns to normal status after period' do it 'returns http too many requests after limit and returns to normal status after period' do
(limit * 2).times do |i| expect { request.call }.to change { throttle_count }.by(1)
request.call expect(response).to have_http_status(429)
expect(response).to have_http_status(429) if i > limit
end
travel period travel period
request.call expect { request.call }.to change { throttle_count }.by(1)
expect(response).to_not have_http_status(429) expect(response).to_not have_http_status(429)
end end
end end
def below_limit
limit - 1
end
def above_limit
limit * 2
end
def throttle_count
described_class.cache.read("#{counter_prefix}:#{throttle}:#{remote_ip}") || 0
end
def counter_prefix
(Time.now.to_i / period.seconds).to_i
end
def increment_counter
described_class.cache.count("#{throttle}:#{remote_ip}", period)
end
end end
let(:remote_ip) { '1.2.3.5' } let(:remote_ip) { '1.2.3.5' }
describe 'throttle excessive sign-up requests by IP address' do describe 'throttle excessive sign-up requests by IP address' do
context 'when accessed through the website' do context 'when accessed through the website' do
let(:throttle) { 'throttle_sign_up_attempts/ip' }
let(:limit) { 25 } let(:limit) { 25 }
let(:period) { 5.minutes } let(:period) { 5.minutes }
let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } } let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } }
@ -65,6 +91,7 @@ describe Rack::Attack, type: :request do
end end
context 'when accessed through the API' do context 'when accessed through the API' do
let(:throttle) { 'throttle_api_sign_up' }
let(:limit) { 5 } let(:limit) { 5 }
let(:period) { 30.minutes } let(:period) { 30.minutes }
let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } } let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } }
@ -87,6 +114,7 @@ describe Rack::Attack, type: :request do
end end
describe 'throttle excessive sign-in requests by IP address' do describe 'throttle excessive sign-in requests by IP address' do
let(:throttle) { 'throttle_login_attempts/ip' }
let(:limit) { 25 } let(:limit) { 25 }
let(:period) { 5.minutes } let(:period) { 5.minutes }
let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } } let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } }

@ -14,11 +14,8 @@ RSpec.describe Api::OEmbedController do
get :show, params: { url: short_account_status_url(alice, status) }, format: :json get :show, params: { url: short_account_status_url(alice, status) }, format: :json
end end
it 'returns http success' do it 'returns private cache control headers', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store') expect(response.headers['Cache-Control']).to include('private, no-store')
end end
end end

@ -41,11 +41,9 @@ describe Api::V1::Accounts::CredentialsController do
} }
end end
it 'returns http success' do it 'updates account info', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'updates account info' do
user.reload user.reload
user.account.reload user.account.reload
@ -55,9 +53,7 @@ describe Api::V1::Accounts::CredentialsController do
expect(user.account.header).to exist expect(user.account.header).to exist
expect(user.setting_default_privacy).to eq('unlisted') expect(user.setting_default_privacy).to eq('unlisted')
expect(user.setting_default_sensitive).to be(true) expect(user.setting_default_sensitive).to be(true)
end
it 'queues up an account update distribution' do
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(user.account_id) expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(user.account_id)
end end
end end

@ -18,23 +18,19 @@ describe Api::V1::Accounts::FollowerAccountsController do
end end
describe 'GET #index' do describe 'GET #index' do
it 'returns http success' do it 'returns accounts following the given account', :aggregate_failures do
get :index, params: { account_id: account.id, limit: 2 } get :index, params: { account_id: account.id, limit: 2 }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns accounts following the given account' do
get :index, params: { account_id: account.id, limit: 2 }
expect(body_as_json.size).to eq 2 expect(body_as_json.size).to eq 2
expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s) expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
end end
it 'does not return blocked users' do it 'does not return blocked users', :aggregate_failures do
user.account.block!(bob) user.account.block!(bob)
get :index, params: { account_id: account.id, limit: 2 } get :index, params: { account_id: account.id, limit: 2 }
expect(response).to have_http_status(200)
expect(body_as_json.size).to eq 1 expect(body_as_json.size).to eq 1
expect(body_as_json[0][:id]).to eq alice.id.to_s expect(body_as_json[0][:id]).to eq alice.id.to_s
end end

@ -18,23 +18,19 @@ describe Api::V1::Accounts::FollowingAccountsController do
end end
describe 'GET #index' do describe 'GET #index' do
it 'returns http success' do it 'returns accounts followed by the given account', :aggregate_failures do
get :index, params: { account_id: account.id, limit: 2 } get :index, params: { account_id: account.id, limit: 2 }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns accounts followed by the given account' do
get :index, params: { account_id: account.id, limit: 2 }
expect(body_as_json.size).to eq 2 expect(body_as_json.size).to eq 2
expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s) expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
end end
it 'does not return blocked users' do it 'does not return blocked users', :aggregate_failures do
user.account.block!(bob) user.account.block!(bob)
get :index, params: { account_id: account.id, limit: 2 } get :index, params: { account_id: account.id, limit: 2 }
expect(response).to have_http_status(200)
expect(body_as_json.size).to eq 1 expect(body_as_json.size).to eq 1
expect(body_as_json[0][:id]).to eq alice.id.to_s expect(body_as_json[0][:id]).to eq alice.id.to_s
end end

@ -19,30 +19,24 @@ describe Api::V1::Accounts::NotesController do
post :create, params: { account_id: account.id, comment: comment } post :create, params: { account_id: account.id, comment: comment }
end end
context 'when account note has reasonable length' do context 'when account note has reasonable length', :aggregate_failures do
let(:comment) { 'foo' } let(:comment) { 'foo' }
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
it 'updates account note' do it 'updates account note' do
subject subject
expect(response).to have_http_status(200)
expect(AccountNote.find_by(account_id: user.account.id, target_account_id: account.id).comment).to eq comment expect(AccountNote.find_by(account_id: user.account.id, target_account_id: account.id).comment).to eq comment
end end
end end
context 'when account note exceeds allowed length' do context 'when account note exceeds allowed length', :aggregate_failures do
let(:comment) { 'a' * 2_001 } let(:comment) { 'a' * 2_001 }
it 'returns 422' do
subject
expect(response).to have_http_status(422)
end
it 'does not create account note' do it 'does not create account note' do
subject subject
expect(response).to have_http_status(422)
expect(AccountNote.where(account_id: user.account.id, target_account_id: account.id)).to_not exist expect(AccountNote.where(account_id: user.account.id, target_account_id: account.id)).to_not exist
end end
end end

@ -15,14 +15,11 @@ RSpec.describe Api::V1::Accounts::PinsController do
describe 'POST #create' do describe 'POST #create' do
subject { post :create, params: { account_id: kevin.account.id } } subject { post :create, params: { account_id: kevin.account.id } }
it 'returns 200' do it 'creates account_pin', :aggregate_failures do
expect(response).to have_http_status(200)
end
it 'creates account_pin' do
expect do expect do
subject subject
end.to change { AccountPin.where(account: john.account, target_account: kevin.account).count }.by(1) end.to change { AccountPin.where(account: john.account, target_account: kevin.account).count }.by(1)
expect(response).to have_http_status(200)
end end
end end
@ -33,14 +30,11 @@ RSpec.describe Api::V1::Accounts::PinsController do
Fabricate(:account_pin, account: john.account, target_account: kevin.account) Fabricate(:account_pin, account: john.account, target_account: kevin.account)
end end
it 'returns 200' do it 'destroys account_pin', :aggregate_failures do
expect(response).to have_http_status(200)
end
it 'destroys account_pin' do
expect do expect do
subject subject
end.to change { AccountPin.where(account: john.account, target_account: kevin.account).count }.by(-1) end.to change { AccountPin.where(account: john.account, target_account: kevin.account).count }.by(-1)
expect(response).to have_http_status(200)
end end
end end
end end

@ -26,13 +26,10 @@ describe Api::V1::Accounts::RelationshipsController do
get :index, params: { id: simon.id } get :index, params: { id: simon.id }
end end
it 'returns http success' do it 'returns JSON with correct data', :aggregate_failures do
expect(response).to have_http_status(200)
end
it 'returns JSON with correct data' do
json = body_as_json json = body_as_json
expect(response).to have_http_status(200)
expect(json).to be_a Enumerable expect(json).to be_a Enumerable
expect(json.first[:following]).to be true expect(json.first[:following]).to be true
expect(json.first[:followed_by]).to be false expect(json.first[:followed_by]).to be false
@ -51,11 +48,14 @@ describe Api::V1::Accounts::RelationshipsController do
context 'when there is returned JSON data' do context 'when there is returned JSON data' do
let(:json) { body_as_json } let(:json) { body_as_json }
it 'returns an enumerable json' do it 'returns an enumerable json with correct elements', :aggregate_failures do
expect(json).to be_a Enumerable expect(json).to be_a Enumerable
expect_simon_item_one
expect_lewis_item_two
end end
it 'returns a correct first element' do def expect_simon_item_one
expect(json.first[:id]).to eq simon.id.to_s expect(json.first[:id]).to eq simon.id.to_s
expect(json.first[:following]).to be true expect(json.first[:following]).to be true
expect(json.first[:showing_reblogs]).to be true expect(json.first[:showing_reblogs]).to be true
@ -65,7 +65,7 @@ describe Api::V1::Accounts::RelationshipsController do
expect(json.first[:domain_blocking]).to be false expect(json.first[:domain_blocking]).to be false
end end
it 'returns a correct second element' do def expect_lewis_item_two
expect(json.second[:id]).to eq lewis.id.to_s expect(json.second[:id]).to eq lewis.id.to_s
expect(json.second[:following]).to be false expect(json.second[:following]).to be false
expect(json.second[:showing_reblogs]).to be false expect(json.second[:showing_reblogs]).to be false

@ -14,15 +14,10 @@ describe Api::V1::Accounts::StatusesController do
end end
describe 'GET #index' do describe 'GET #index' do
it 'returns http success' do it 'returns expected headers', :aggregate_failures do
get :index, params: { account_id: user.account.id, limit: 1 } get :index, params: { account_id: user.account.id, limit: 1 }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns expected headers' do
get :index, params: { account_id: user.account.id, limit: 1 }
expect(response.headers['Link'].links.size).to eq(2) expect(response.headers['Link'].links.size).to eq(2)
end end
@ -44,14 +39,11 @@ describe Api::V1::Accounts::StatusesController do
get :index, params: { account_id: user.account.id, exclude_replies: true } get :index, params: { account_id: user.account.id, exclude_replies: true }
end end
it 'returns http success' do it 'returns posts along with self replies', :aggregate_failures do
expect(response).to have_http_status(200)
end
it 'returns posts along with self replies' do
json = body_as_json json = body_as_json
post_ids = json.map { |item| item[:id].to_i }.sort post_ids = json.map { |item| item[:id].to_i }.sort
expect(response).to have_http_status(200)
expect(post_ids).to eq [status.id, status_self_reply.id] expect(post_ids).to eq [status.id, status_self_reply.id]
end end
end end

@ -25,15 +25,10 @@ RSpec.describe Api::V1::AccountsController do
context 'when given truthy agreement' do context 'when given truthy agreement' do
let(:agreement) { 'true' } let(:agreement) { 'true' }
it 'returns http success' do it 'creates a user', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns a new access token as JSON' do
expect(body_as_json[:access_token]).to_not be_blank expect(body_as_json[:access_token]).to_not be_blank
end
it 'creates a user' do
user = User.find_by(email: 'hello@world.tld') user = User.find_by(email: 'hello@world.tld')
expect(user).to_not be_nil expect(user).to_not be_nil
expect(user.created_by_application_id).to eq app.id expect(user.created_by_application_id).to eq app.id
@ -59,18 +54,14 @@ RSpec.describe Api::V1::AccountsController do
context 'with unlocked account' do context 'with unlocked account' do
let(:locked) { false } let(:locked) { false }
it 'returns http success' do it 'creates a following relation between user and target user', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns JSON with following=true and requested=false' do
json = body_as_json json = body_as_json
expect(json[:following]).to be true expect(json[:following]).to be true
expect(json[:requested]).to be false expect(json[:requested]).to be false
end
it 'creates a following relation between user and target user' do
expect(user.account.following?(other_account)).to be true expect(user.account.following?(other_account)).to be true
end end
@ -80,18 +71,14 @@ RSpec.describe Api::V1::AccountsController do
context 'with locked account' do context 'with locked account' do
let(:locked) { true } let(:locked) { true }
it 'returns http success' do it 'creates a follow request relation between user and target user', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns JSON with following=false and requested=true' do
json = body_as_json json = body_as_json
expect(json[:following]).to be false expect(json[:following]).to be false
expect(json[:requested]).to be true expect(json[:requested]).to be true
end
it 'creates a follow request relation between user and target user' do
expect(user.account.requested?(other_account)).to be true expect(user.account.requested?(other_account)).to be true
end end
@ -148,11 +135,8 @@ RSpec.describe Api::V1::AccountsController do
post :unfollow, params: { id: other_account.id } post :unfollow, params: { id: other_account.id }
end end
it 'returns http success' do it 'removes the following relation between user and target user', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the following relation between user and target user' do
expect(user.account.following?(other_account)).to be false expect(user.account.following?(other_account)).to be false
end end
@ -168,11 +152,8 @@ RSpec.describe Api::V1::AccountsController do
post :remove_from_followers, params: { id: other_account.id } post :remove_from_followers, params: { id: other_account.id }
end end
it 'returns http success' do it 'removes the followed relation between user and target user', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the followed relation between user and target user' do
expect(user.account.followed_by?(other_account)).to be false expect(user.account.followed_by?(other_account)).to be false
end end
@ -188,15 +169,9 @@ RSpec.describe Api::V1::AccountsController do
post :block, params: { id: other_account.id } post :block, params: { id: other_account.id }
end end
it 'returns http success' do it 'creates a blocking relation', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the following relation between user and target user' do
expect(user.account.following?(other_account)).to be false expect(user.account.following?(other_account)).to be false
end
it 'creates a blocking relation' do
expect(user.account.blocking?(other_account)).to be true expect(user.account.blocking?(other_account)).to be true
end end
@ -212,11 +187,8 @@ RSpec.describe Api::V1::AccountsController do
post :unblock, params: { id: other_account.id } post :unblock, params: { id: other_account.id }
end end
it 'returns http success' do it 'removes the blocking relation between user and target user', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the blocking relation between user and target user' do
expect(user.account.blocking?(other_account)).to be false expect(user.account.blocking?(other_account)).to be false
end end
@ -232,19 +204,10 @@ RSpec.describe Api::V1::AccountsController do
post :mute, params: { id: other_account.id } post :mute, params: { id: other_account.id }
end end
it 'returns http success' do it 'mutes notifications', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'does not remove the following relation between user and target user' do
expect(user.account.following?(other_account)).to be true expect(user.account.following?(other_account)).to be true
end
it 'creates a muting relation' do
expect(user.account.muting?(other_account)).to be true expect(user.account.muting?(other_account)).to be true
end
it 'mutes notifications' do
expect(user.account.muting_notifications?(other_account)).to be true expect(user.account.muting_notifications?(other_account)).to be true
end end
@ -260,19 +223,10 @@ RSpec.describe Api::V1::AccountsController do
post :mute, params: { id: other_account.id, notifications: false } post :mute, params: { id: other_account.id, notifications: false }
end end
it 'returns http success' do it 'does not mute notifications', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'does not remove the following relation between user and target user' do
expect(user.account.following?(other_account)).to be true expect(user.account.following?(other_account)).to be true
end
it 'creates a muting relation' do
expect(user.account.muting?(other_account)).to be true expect(user.account.muting?(other_account)).to be true
end
it 'does not mute notifications' do
expect(user.account.muting_notifications?(other_account)).to be false expect(user.account.muting_notifications?(other_account)).to be false
end end
@ -288,19 +242,10 @@ RSpec.describe Api::V1::AccountsController do
post :mute, params: { id: other_account.id, duration: 300 } post :mute, params: { id: other_account.id, duration: 300 }
end end
it 'returns http success' do it 'mutes notifications', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'does not remove the following relation between user and target user' do
expect(user.account.following?(other_account)).to be true expect(user.account.following?(other_account)).to be true
end
it 'creates a muting relation' do
expect(user.account.muting?(other_account)).to be true expect(user.account.muting?(other_account)).to be true
end
it 'mutes notifications' do
expect(user.account.muting_notifications?(other_account)).to be true expect(user.account.muting_notifications?(other_account)).to be true
end end
@ -316,11 +261,8 @@ RSpec.describe Api::V1::AccountsController do
post :unmute, params: { id: other_account.id } post :unmute, params: { id: other_account.id }
end end
it 'returns http success' do it 'removes the muting relation between user and target user', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the muting relation between user and target user' do
expect(user.account.muting?(other_account)).to be false expect(user.account.muting?(other_account)).to be false
end end

@ -1,198 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Api::V1::Admin::AccountsController do
render_views
let(:role) { UserRole.find_by(name: 'Moderator') }
let(:user) { Fabricate(:user, role: role) }
let(:scopes) { 'admin:read admin:write' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:account) { Fabricate(:account) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
let!(:remote_account) { Fabricate(:account, domain: 'example.org') }
let!(:other_remote_account) { Fabricate(:account, domain: 'foo.bar') }
let!(:suspended_account) { Fabricate(:account, suspended: true) }
let!(:suspended_remote) { Fabricate(:account, domain: 'foo.bar', suspended: true) }
let!(:disabled_account) { Fabricate(:user, disabled: true).account }
let!(:pending_account) { Fabricate(:user, approved: false).account }
let!(:admin_account) { user.account }
let(:params) { {} }
before do
pending_account.user.update(approved: false)
get :index, params: params
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
[
[{ active: 'true', local: 'true', staff: 'true' }, [:admin_account]],
[{ by_domain: 'example.org', remote: 'true' }, [:remote_account]],
[{ suspended: 'true' }, [:suspended_account]],
[{ disabled: 'true' }, [:disabled_account]],
[{ pending: 'true' }, [:pending_account]],
].each do |params, expected_results|
context "when called with #{params.inspect}" do
let(:params) { params }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it "returns the correct accounts (#{expected_results.inspect})" do
json = body_as_json
expect(json.map { |a| a[:id].to_i }).to eq(expected_results.map { |symbol| send(symbol).id })
end
end
end
end
describe 'GET #show' do
before do
get :show, params: { id: account.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
describe 'POST #approve' do
before do
account.user.update(approved: false)
post :approve, params: { id: account.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'approves user' do
expect(account.reload.user_approved?).to be true
end
it 'logs action' do
log_item = Admin::ActionLog.last
expect(log_item).to_not be_nil
expect(log_item.action).to eq :approve
expect(log_item.account_id).to eq user.account_id
expect(log_item.target_id).to eq account.user.id
end
end
describe 'POST #reject' do
before do
account.user.update(approved: false)
post :reject, params: { id: account.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'removes user' do
expect(User.where(id: account.user.id).count).to eq 0
end
it 'logs action' do
log_item = Admin::ActionLog.last
expect(log_item).to_not be_nil
expect(log_item.action).to eq :reject
expect(log_item.account_id).to eq user.account_id
expect(log_item.target_id).to eq account.user.id
end
end
describe 'POST #enable' do
before do
account.user.update(disabled: true)
post :enable, params: { id: account.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'enables user' do
expect(account.reload.user_disabled?).to be false
end
end
describe 'POST #unsuspend' do
before do
account.suspend!
post :unsuspend, params: { id: account.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'unsuspends account' do
expect(account.reload.suspended?).to be false
end
end
describe 'POST #unsensitive' do
before do
account.touch(:sensitized_at)
post :unsensitive, params: { id: account.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'unsensitizes account' do
expect(account.reload.sensitized?).to be false
end
end
describe 'POST #unsilence' do
before do
account.touch(:silenced_at)
post :unsilence, params: { id: account.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'unsilences account' do
expect(account.reload.silenced?).to be false
end
end
end

@ -1,52 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Api::V1::Admin::Trends::LinksController do
render_views
let(:role) { UserRole.find_by(name: 'Admin') }
let(:user) { Fabricate(:user, role: role) }
let(:scopes) { 'admin:read admin:write' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:account) { Fabricate(:account) }
let(:preview_card) { Fabricate(:preview_card) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 }
expect(response).to have_http_status(200)
end
end
describe 'POST #approve' do
before do
post :approve, params: { id: preview_card.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
describe 'POST #reject' do
before do
post :reject, params: { id: preview_card.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
end

@ -25,11 +25,8 @@ RSpec.describe Api::V1::Announcements::ReactionsController do
put :update, params: { announcement_id: announcement.id, id: '😂' } put :update, params: { announcement_id: announcement.id, id: '😂' }
end end
it 'returns http success' do it 'creates reaction', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'creates reaction' do
expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to_not be_nil expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to_not be_nil
end end
end end
@ -53,11 +50,8 @@ RSpec.describe Api::V1::Announcements::ReactionsController do
delete :destroy, params: { announcement_id: announcement.id, id: '😂' } delete :destroy, params: { announcement_id: announcement.id, id: '😂' }
end end
it 'returns http success' do it 'creates reaction', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'creates reaction' do
expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to be_nil expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to be_nil
end end
end end

@ -47,11 +47,8 @@ RSpec.describe Api::V1::AnnouncementsController do
post :dismiss, params: { id: announcement.id } post :dismiss, params: { id: announcement.id }
end end
it 'returns http success' do it 'dismisses announcement', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'dismisses announcement' do
expect(announcement.announcement_mutes.find_by(account: user.account)).to_not be_nil expect(announcement.announcement_mutes.find_by(account: user.account)).to_not be_nil
end end
end end

@ -1,65 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Api::V1::BlocksController do
render_views
let(:user) { Fabricate(:user) }
let(:scopes) { 'read:blocks' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
before { allow(controller).to receive(:doorkeeper_token) { token } }
describe 'GET #index' do
it 'limits according to limit parameter' do
Array.new(2) { Fabricate(:block, account: user.account) }
get :index, params: { limit: 1 }
expect(body_as_json.size).to eq 1
end
it 'queries blocks in range according to max_id' do
blocks = Array.new(2) { Fabricate(:block, account: user.account) }
get :index, params: { max_id: blocks[1] }
expect(body_as_json.size).to eq 1
expect(body_as_json[0][:id]).to eq blocks[0].target_account_id.to_s
end
it 'queries blocks in range according to since_id' do
blocks = Array.new(2) { Fabricate(:block, account: user.account) }
get :index, params: { since_id: blocks[0] }
expect(body_as_json.size).to eq 1
expect(body_as_json[0][:id]).to eq blocks[1].target_account_id.to_s
end
it 'sets pagination header for next path' do
blocks = Array.new(2) { Fabricate(:block, account: user.account) }
get :index, params: { limit: 1, since_id: blocks[0] }
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq api_v1_blocks_url(limit: 1, max_id: blocks[1])
end
it 'sets pagination header for previous path' do
block = Fabricate(:block, account: user.account)
get :index
expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq api_v1_blocks_url(since_id: block)
end
it 'returns http success' do
get :index
expect(response).to have_http_status(200)
end
context 'with wrong scopes' do
let(:scopes) { 'write:blocks' }
it 'returns http forbidden' do
get :index
expect(response).to have_http_status(403)
end
end
end
end

@ -21,17 +21,14 @@ RSpec.describe Api::V1::ConversationsController do
PostStatusService.new.call(user.account, text: 'Hey, nobody here', visibility: 'direct') PostStatusService.new.call(user.account, text: 'Hey, nobody here', visibility: 'direct')
end end
it 'returns http success' do it 'returns pagination headers', :aggregate_failures do
get :index
expect(response).to have_http_status(200)
end
it 'returns pagination headers' do
get :index, params: { limit: 1 } get :index, params: { limit: 1 }
expect(response).to have_http_status(200)
expect(response.headers['Link'].links.size).to eq(2) expect(response.headers['Link'].links.size).to eq(2)
end end
it 'returns conversations' do it 'returns conversations', :aggregate_failures do
get :index get :index
json = body_as_json json = body_as_json
expect(json.size).to eq 2 expect(json.size).to eq 2

@ -1,80 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Api::V1::FavouritesController do
render_views
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
describe 'GET #index' do
context 'without token' do
it 'returns http unauthorized' do
get :index
expect(response).to have_http_status 401
end
end
context 'with token' do
context 'without read scope' do
before do
allow(controller).to receive(:doorkeeper_token) do
Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: '')
end
end
it 'returns http forbidden' do
get :index
expect(response).to have_http_status 403
end
end
context 'without valid resource owner' do
before do
token = Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read')
user.destroy!
allow(controller).to receive(:doorkeeper_token) { token }
end
it 'returns http unprocessable entity' do
get :index
expect(response).to have_http_status 422
end
end
context 'with read scope and valid resource owner' do
before do
allow(controller).to receive(:doorkeeper_token) do
Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:favourites')
end
end
it 'shows favourites owned by the user' do
favourite_by_user = Fabricate(:favourite, account: user.account)
favourite_by_others = Fabricate(:favourite)
get :index
expect(assigns(:statuses)).to contain_exactly(favourite_by_user.status)
end
it 'adds pagination headers if necessary' do
favourite = Fabricate(:favourite, account: user.account)
get :index, params: { limit: 1 }
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq "http://test.host/api/v1/favourites?limit=1&max_id=#{favourite.id}"
expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq "http://test.host/api/v1/favourites?limit=1&min_id=#{favourite.id}"
end
it 'does not add pagination headers if not necessary' do
get :index
expect(response.headers['Link']).to be_nil
end
end
end
end
end

@ -31,12 +31,10 @@ RSpec.describe Api::V1::FiltersController do
post :create, params: { phrase: 'magic', context: %w(home), irreversible: irreversible, whole_word: whole_word } post :create, params: { phrase: 'magic', context: %w(home), irreversible: irreversible, whole_word: whole_word }
end end
it 'returns http success' do it 'creates a filter', :aggregate_failures do
expect(response).to have_http_status(200)
end
it 'creates a filter' do
filter = user.account.custom_filters.first filter = user.account.custom_filters.first
expect(response).to have_http_status(200)
expect(filter).to_not be_nil expect(filter).to_not be_nil
expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]] expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]]
expect(filter.context).to eq %w(home) expect(filter.context).to eq %w(home)
@ -48,12 +46,10 @@ RSpec.describe Api::V1::FiltersController do
let(:irreversible) { false } let(:irreversible) { false }
let(:whole_word) { true } let(:whole_word) { true }
it 'returns http success' do it 'creates a filter', :aggregate_failures do
expect(response).to have_http_status(200)
end
it 'creates a filter' do
filter = user.account.custom_filters.first filter = user.account.custom_filters.first
expect(response).to have_http_status(200)
expect(filter).to_not be_nil expect(filter).to_not be_nil
expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]] expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]]
expect(filter.context).to eq %w(home) expect(filter.context).to eq %w(home)
@ -83,11 +79,8 @@ RSpec.describe Api::V1::FiltersController do
put :update, params: { id: keyword.id, phrase: 'updated' } put :update, params: { id: keyword.id, phrase: 'updated' }
end end
it 'returns http success' do it 'updates the filter', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'updates the filter' do
expect(keyword.reload.phrase).to eq 'updated' expect(keyword.reload.phrase).to eq 'updated'
end end
end end
@ -101,11 +94,8 @@ RSpec.describe Api::V1::FiltersController do
delete :destroy, params: { id: keyword.id } delete :destroy, params: { id: keyword.id }
end end
it 'returns http success' do it 'removes the filter', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the filter' do
expect { keyword.reload }.to raise_error ActiveRecord::RecordNotFound expect { keyword.reload }.to raise_error ActiveRecord::RecordNotFound
end end
end end

@ -1,25 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Api::V1::FollowedTagsController do
render_views
let(:user) { Fabricate(:user) }
let(:scopes) { 'read:follows' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
before { allow(controller).to receive(:doorkeeper_token) { token } }
describe 'GET #index' do
let!(:tag_follows) { Fabricate.times(5, :tag_follow, account: user.account) }
before do
get :index, params: { limit: 1 }
end
it 'returns http success' do
expect(response).to have_http_status(:success)
end
end
end

@ -5,7 +5,7 @@ require 'rails_helper'
describe Api::V1::Instances::TranslationLanguagesController do describe Api::V1::Instances::TranslationLanguagesController do
describe 'GET #show' do describe 'GET #show' do
context 'when no translation service is configured' do context 'when no translation service is configured' do
it 'returns empty language matrix' do it 'returns empty language matrix', :aggregate_failures do
get :show get :show
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
@ -19,7 +19,7 @@ describe Api::V1::Instances::TranslationLanguagesController do
allow(TranslationService).to receive_messages(configured?: true, configured: service) allow(TranslationService).to receive_messages(configured?: true, configured: service)
end end
it 'returns language matrix' do it 'returns language matrix', :aggregate_failures do
get :show get :show
expect(response).to have_http_status(200) expect(response).to have_http_status(200)

@ -1,92 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Api::V1::Lists::AccountsController do
render_views
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:list) { Fabricate(:list, account: user.account) }
before do
follow = Fabricate(:follow, account: user.account)
list.accounts << follow.target_account
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
let(:scopes) { 'read:lists' }
it 'returns http success' do
get :show, params: { list_id: list.id }
expect(response).to have_http_status(200)
end
end
describe 'POST #create' do
let(:scopes) { 'write:lists' }
let(:bob) { Fabricate(:account, username: 'bob') }
context 'when the added account is followed' do
before do
user.account.follow!(bob)
post :create, params: { list_id: list.id, account_ids: [bob.id] }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'adds account to the list' do
expect(list.accounts.include?(bob)).to be true
end
end
context 'when the added account has been sent a follow request' do
before do
user.account.follow_requests.create!(target_account: bob)
post :create, params: { list_id: list.id, account_ids: [bob.id] }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'adds account to the list' do
expect(list.accounts.include?(bob)).to be true
end
end
context 'when the added account is not followed' do
before do
post :create, params: { list_id: list.id, account_ids: [bob.id] }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
it 'does not add the account to the list' do
expect(list.accounts.include?(bob)).to be false
end
end
end
describe 'DELETE #destroy' do
let(:scopes) { 'write:lists' }
before do
delete :destroy, params: { list_id: list.id, account_ids: [list.accounts.first.id] }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'removes account from the list' do
expect(list.accounts.count).to eq 0
end
end
end

@ -18,13 +18,10 @@ RSpec.describe Api::V1::MarkersController do
get :index, params: { timeline: %w(home notifications) } get :index, params: { timeline: %w(home notifications) }
end end
it 'returns http success' do it 'returns markers', :aggregate_failures do
expect(response).to have_http_status(200)
end
it 'returns markers' do
json = body_as_json json = body_as_json
expect(response).to have_http_status(200)
expect(json.key?(:home)).to be true expect(json.key?(:home)).to be true
expect(json[:home][:last_read_id]).to eq '123' expect(json[:home][:last_read_id]).to eq '123'
expect(json.key?(:notifications)).to be true expect(json.key?(:notifications)).to be true
@ -38,11 +35,8 @@ RSpec.describe Api::V1::MarkersController do
post :create, params: { home: { last_read_id: '69420' } } post :create, params: { home: { last_read_id: '69420' } }
end end
it 'returns http success' do it 'creates a marker', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'creates a marker' do
expect(user.markers.first.timeline).to eq 'home' expect(user.markers.first.timeline).to eq 'home'
expect(user.markers.first.last_read_id).to eq 69_420 expect(user.markers.first.last_read_id).to eq 69_420
end end
@ -54,11 +48,8 @@ RSpec.describe Api::V1::MarkersController do
post :create, params: { home: { last_read_id: '70120' } } post :create, params: { home: { last_read_id: '70120' } }
end end
it 'returns http success' do it 'updates a marker', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'updates a marker' do
expect(user.markers.first.timeline).to eq 'home' expect(user.markers.first.timeline).to eq 'home'
expect(user.markers.first.last_read_id).to eq 70_120 expect(user.markers.first.last_read_id).to eq 70_120
end end

@ -38,19 +38,10 @@ RSpec.describe Api::V1::MediaController do
post :create, params: { file: fixture_file_upload('attachment.jpg', 'image/jpeg') } post :create, params: { file: fixture_file_upload('attachment.jpg', 'image/jpeg') }
end end
it 'returns http success' do it 'creates a media attachment', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'creates a media attachment' do
expect(MediaAttachment.first).to_not be_nil expect(MediaAttachment.first).to_not be_nil
end
it 'uploads a file' do
expect(MediaAttachment.first).to have_attached_file(:file) expect(MediaAttachment.first).to have_attached_file(:file)
end
it 'returns media ID in JSON' do
expect(body_as_json[:id]).to eq MediaAttachment.first.id.to_s expect(body_as_json[:id]).to eq MediaAttachment.first.id.to_s
end end
end end
@ -60,19 +51,10 @@ RSpec.describe Api::V1::MediaController do
post :create, params: { file: fixture_file_upload('attachment.gif', 'image/gif') } post :create, params: { file: fixture_file_upload('attachment.gif', 'image/gif') }
end end
it 'returns http success' do it 'creates a media attachment', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'creates a media attachment' do
expect(MediaAttachment.first).to_not be_nil expect(MediaAttachment.first).to_not be_nil
end
it 'uploads a file' do
expect(MediaAttachment.first).to have_attached_file(:file) expect(MediaAttachment.first).to have_attached_file(:file)
end
it 'returns media ID in JSON' do
expect(body_as_json[:id]).to eq MediaAttachment.first.id.to_s expect(body_as_json[:id]).to eq MediaAttachment.first.id.to_s
end end
end end
@ -82,17 +64,10 @@ RSpec.describe Api::V1::MediaController do
post :create, params: { file: fixture_file_upload('attachment.webm', 'video/webm') } post :create, params: { file: fixture_file_upload('attachment.webm', 'video/webm') }
end end
it do it 'creates a media attachment', :aggregate_failures do
# returns http success
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
# creates a media attachment
expect(MediaAttachment.first).to_not be_nil expect(MediaAttachment.first).to_not be_nil
# uploads a file
expect(MediaAttachment.first).to have_attached_file(:file) expect(MediaAttachment.first).to have_attached_file(:file)
# returns media ID in JSON
expect(body_as_json[:id]).to eq MediaAttachment.first.id.to_s expect(body_as_json[:id]).to eq MediaAttachment.first.id.to_s
end end
end end

@ -18,18 +18,13 @@ RSpec.describe Api::V1::Polls::VotesController do
post :create, params: { poll_id: poll.id, choices: %w(1) } post :create, params: { poll_id: poll.id, choices: %w(1) }
end end
it 'returns http success' do it 'creates a vote', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'creates a vote' do
vote = poll.votes.where(account: user.account).first vote = poll.votes.where(account: user.account).first
expect(vote).to_not be_nil expect(vote).to_not be_nil
expect(vote.choice).to eq 1 expect(vote.choice).to eq 1
end
it 'updates poll tallies' do
expect(poll.reload.cached_tallies).to eq [0, 1] expect(poll.reload.cached_tallies).to eq [0, 1]
end end
end end

@ -1,75 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Api::V1::ReportsController do
render_views
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'POST #create' do
let!(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
let(:scopes) { 'write:reports' }
let(:status) { Fabricate(:status) }
let(:target_account) { status.account }
let(:category) { nil }
let(:forward) { nil }
let(:rule_ids) { nil }
before do
post :create, params: { status_ids: [status.id], account_id: target_account.id, comment: 'reasons', category: category, rule_ids: rule_ids, forward: forward }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'creates a report' do
expect(target_account.targeted_reports).to_not be_empty
end
it 'saves comment' do
expect(target_account.targeted_reports.first.comment).to eq 'reasons'
end
it 'sends e-mails to admins' do
expect(ActionMailer::Base.deliveries.first.to).to eq([admin.email])
end
context 'when a status does not belong to the reported account' do
let(:target_account) { Fabricate(:account) }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when a category is chosen' do
let(:category) { 'spam' }
it 'saves category' do
expect(target_account.targeted_reports.first.spam?).to be true
end
end
context 'when violated rules are chosen' do
let(:rule) { Fabricate(:rule) }
let(:category) { 'violation' }
let(:rule_ids) { [rule.id] }
it 'saves category' do
expect(target_account.targeted_reports.first.violation?).to be true
end
it 'saves rule_ids' do
expect(target_account.targeted_reports.first.rule_ids).to contain_exactly(rule.id)
end
end
end
end

@ -21,11 +21,8 @@ describe Api::V1::Statuses::MutesController do
post :create, params: { status_id: status.id } post :create, params: { status_id: status.id }
end end
it 'returns http success' do it 'creates a conversation mute', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'creates a conversation mute' do
expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to_not be_nil expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to_not be_nil
end end
end end
@ -38,11 +35,8 @@ describe Api::V1::Statuses::MutesController do
post :destroy, params: { status_id: status.id } post :destroy, params: { status_id: status.id }
end end
it 'returns http success' do it 'destroys the conversation mute', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'destroys the conversation mute' do
expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to be_nil expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to be_nil
end end
end end

@ -24,14 +24,12 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController do
Fabricate(:status, account: bob, reblog_of_id: status.id) Fabricate(:status, account: bob, reblog_of_id: status.id)
end end
it 'returns http success' do it 'returns accounts who reblogged the status', :aggregate_failures do
get :index, params: { status_id: status.id, limit: 2 } get :index, params: { status_id: status.id, limit: 2 }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response.headers['Link'].links.size).to eq(2) expect(response.headers['Link'].links.size).to eq(2)
end
it 'returns accounts who reblogged the status' do
get :index, params: { status_id: status.id, limit: 2 }
expect(body_as_json.size).to eq 2 expect(body_as_json.size).to eq 2
expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s) expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
end end

@ -28,19 +28,13 @@ describe Api::V1::Statuses::ReblogsController do
end end
context 'with public status' do context 'with public status' do
it 'returns http success' do it 'reblogs the status', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'updates the reblogs count' do
expect(status.reblogs.count).to eq 1 expect(status.reblogs.count).to eq 1
end
it 'updates the reblogged attribute' do
expect(user.account.reblogged?(status)).to be true expect(user.account.reblogged?(status)).to be true
end
it 'returns json with updated attributes' do
hash_body = body_as_json hash_body = body_as_json
expect(hash_body[:reblog][:id]).to eq status.id.to_s expect(hash_body[:reblog][:id]).to eq status.id.to_s
@ -67,19 +61,13 @@ describe Api::V1::Statuses::ReblogsController do
post :destroy, params: { status_id: status.id } post :destroy, params: { status_id: status.id }
end end
it 'returns http success' do it 'destroys the reblog', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'updates the reblogs count' do
expect(status.reblogs.count).to eq 0 expect(status.reblogs.count).to eq 0
end
it 'updates the reblogged attribute' do
expect(user.account.reblogged?(status)).to be false expect(user.account.reblogged?(status)).to be false
end
it 'returns json with updated attributes' do
hash_body = body_as_json hash_body = body_as_json
expect(hash_body[:id]).to eq status.id.to_s expect(hash_body[:id]).to eq status.id.to_s
@ -97,19 +85,13 @@ describe Api::V1::Statuses::ReblogsController do
post :destroy, params: { status_id: status.id } post :destroy, params: { status_id: status.id }
end end
it 'returns http success' do it 'destroys the reblog', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'updates the reblogs count' do
expect(status.reblogs.count).to eq 0 expect(status.reblogs.count).to eq 0
end
it 'updates the reblogged attribute' do
expect(user.account.reblogged?(status)).to be false expect(user.account.reblogged?(status)).to be false
end
it 'returns json with updated attributes' do
hash_body = body_as_json hash_body = body_as_json
expect(hash_body[:id]).to eq status.id.to_s expect(hash_body[:id]).to eq status.id.to_s

@ -1,29 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Api::V1::Statuses::SourcesController do
render_views
let(:user) { Fabricate(:user) }
let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses', application: app) }
context 'with an oauth token' do
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #show' do
let(:status) { Fabricate(:status, account: user.account) }
before do
get :show, params: { status_id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
end
end

@ -30,14 +30,11 @@ RSpec.describe Api::V1::StatusesController do
user.account.custom_filters.create!(phrase: 'filter1', context: %w(home), action: :hide, keywords_attributes: [{ keyword: 'banned' }, { keyword: 'irrelevant' }]) user.account.custom_filters.create!(phrase: 'filter1', context: %w(home), action: :hide, keywords_attributes: [{ keyword: 'banned' }, { keyword: 'irrelevant' }])
end end
it 'returns http success' do it 'returns filter information', :aggregate_failures do
get :show, params: { id: status.id }
expect(response).to have_http_status(200)
end
it 'returns filter information' do
get :show, params: { id: status.id } get :show, params: { id: status.id }
json = body_as_json json = body_as_json
expect(response).to have_http_status(200)
expect(json[:filtered][0]).to include({ expect(json[:filtered][0]).to include({
filter: a_hash_including({ filter: a_hash_including({
id: user.account.custom_filters.first.id.to_s, id: user.account.custom_filters.first.id.to_s,
@ -57,14 +54,11 @@ RSpec.describe Api::V1::StatusesController do
filter.statuses.create!(status_id: status.id) filter.statuses.create!(status_id: status.id)
end end
it 'returns http success' do it 'returns filter information', :aggregate_failures do
get :show, params: { id: status.id }
expect(response).to have_http_status(200)
end
it 'returns filter information' do
get :show, params: { id: status.id } get :show, params: { id: status.id }
json = body_as_json json = body_as_json
expect(response).to have_http_status(200)
expect(json[:filtered][0]).to include({ expect(json[:filtered][0]).to include({
filter: a_hash_including({ filter: a_hash_including({
id: user.account.custom_filters.first.id.to_s, id: user.account.custom_filters.first.id.to_s,
@ -83,14 +77,11 @@ RSpec.describe Api::V1::StatusesController do
user.account.custom_filters.create!(phrase: 'filter1', context: %w(home), action: :hide, keywords_attributes: [{ keyword: 'banned' }, { keyword: 'irrelevant' }]) user.account.custom_filters.create!(phrase: 'filter1', context: %w(home), action: :hide, keywords_attributes: [{ keyword: 'banned' }, { keyword: 'irrelevant' }])
end end
it 'returns http success' do it 'returns filter information', :aggregate_failures do
get :show, params: { id: status.id }
expect(response).to have_http_status(200)
end
it 'returns filter information' do
get :show, params: { id: status.id } get :show, params: { id: status.id }
json = body_as_json json = body_as_json
expect(response).to have_http_status(200)
expect(json[:reblog][:filtered][0]).to include({ expect(json[:reblog][:filtered][0]).to include({
filter: a_hash_including({ filter: a_hash_including({
id: user.account.custom_filters.first.id.to_s, id: user.account.custom_filters.first.id.to_s,
@ -125,11 +116,8 @@ RSpec.describe Api::V1::StatusesController do
post :create, params: { status: 'Hello world' } post :create, params: { status: 'Hello world' }
end end
it 'returns http success' do it 'returns rate limit headers', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns rate limit headers' do
expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s
expect(response.headers['X-RateLimit-Remaining']).to eq (RateLimiter::FAMILIES[:statuses][:limit] - 1).to_s expect(response.headers['X-RateLimit-Remaining']).to eq (RateLimiter::FAMILIES[:statuses][:limit] - 1).to_s
end end
@ -143,11 +131,8 @@ RSpec.describe Api::V1::StatusesController do
post :create, params: { status: '@alice hm, @bob is really annoying lately', allowed_mentions: [alice.id] } post :create, params: { status: '@alice hm, @bob is really annoying lately', allowed_mentions: [alice.id] }
end end
it 'returns http unprocessable entity' do it 'returns serialized extra accounts in body', :aggregate_failures do
expect(response).to have_http_status(422) expect(response).to have_http_status(422)
end
it 'returns serialized extra accounts in body' do
expect(body_as_json[:unexpected_accounts].map { |a| a.slice(:id, :acct) }).to eq [{ id: bob.id.to_s, acct: bob.acct }] expect(body_as_json[:unexpected_accounts].map { |a| a.slice(:id, :acct) }).to eq [{ id: bob.id.to_s, acct: bob.acct }]
end end
end end
@ -157,11 +142,8 @@ RSpec.describe Api::V1::StatusesController do
post :create, params: {} post :create, params: {}
end end
it 'returns http unprocessable entity' do it 'returns rate limit headers', :aggregate_failures do
expect(response).to have_http_status(422) expect(response).to have_http_status(422)
end
it 'returns rate limit headers' do
expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s
end end
end end
@ -173,11 +155,8 @@ RSpec.describe Api::V1::StatusesController do
post :create, params: { status: 'Hello world' } post :create, params: { status: 'Hello world' }
end end
it 'returns http too many requests' do it 'returns rate limit headers', :aggregate_failures do
expect(response).to have_http_status(429) expect(response).to have_http_status(429)
end
it 'returns rate limit headers' do
expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s
expect(response.headers['X-RateLimit-Remaining']).to eq '0' expect(response.headers['X-RateLimit-Remaining']).to eq '0'
end end
@ -192,11 +171,8 @@ RSpec.describe Api::V1::StatusesController do
post :destroy, params: { id: status.id } post :destroy, params: { id: status.id }
end end
it 'returns http success' do it 'removes the status', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the status' do
expect(Status.find_by(id: status.id)).to be_nil expect(Status.find_by(id: status.id)).to be_nil
end end
end end
@ -209,11 +185,8 @@ RSpec.describe Api::V1::StatusesController do
put :update, params: { id: status.id, status: 'I am updated' } put :update, params: { id: status.id, status: 'I am updated' }
end end
it 'returns http success' do it 'updates the status', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'updates the status' do
expect(status.reload.text).to eq 'I am updated' expect(status.reload.text).to eq 'I am updated'
end end
end end

@ -1,75 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Api::V1::Timelines::TagController do
render_views
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #show' do
subject do
get :show, params: { id: 'test' }
end
before do
PostStatusService.new.call(user.account, text: 'It is a #test')
end
context 'when the instance allows public preview' do
before do
Setting.timeline_preview = true
end
context 'when the user is not authenticated' do
let(:token) { nil }
it 'returns http success', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(response.headers['Link'].links.size).to eq(2)
end
end
context 'when the user is authenticated' do
it 'returns http success', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(response.headers['Link'].links.size).to eq(2)
end
end
end
context 'when the instance does not allow public preview' do
before do
Form::AdminSettings.new(timeline_preview: false).save
end
context 'when the user is not authenticated' do
let(:token) { nil }
it 'returns http unauthorized' do
subject
expect(response).to have_http_status(401)
end
end
context 'when the user is authenticated' do
it 'returns http success', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(response.headers['Link'].links.size).to eq(2)
end
end
end
end
end

@ -44,14 +44,14 @@ RSpec.describe Api::V2::Admin::AccountsController do
context "when called with #{params.inspect}" do context "when called with #{params.inspect}" do
let(:params) { params } let(:params) { params }
it 'returns http success' do it "returns the correct accounts (#{expected_results.inspect})" do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it "returns the correct accounts (#{expected_results.inspect})" do expect(body_json_ids).to eq(expected_results.map { |symbol| send(symbol).id })
json = body_as_json end
expect(json.map { |a| a[:id].to_i }).to eq(expected_results.map { |symbol| send(symbol).id }) def body_json_ids
body_as_json.map { |a| a[:id].to_i }
end end
end end
end end

@ -40,17 +40,13 @@ RSpec.describe Api::V2::Filters::KeywordsController do
post :create, params: { filter_id: filter_id, keyword: 'magic', whole_word: false } post :create, params: { filter_id: filter_id, keyword: 'magic', whole_word: false }
end end
it 'returns http success' do it 'creates a filter', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns a keyword' do
json = body_as_json json = body_as_json
expect(json[:keyword]).to eq 'magic' expect(json[:keyword]).to eq 'magic'
expect(json[:whole_word]).to be false expect(json[:whole_word]).to be false
end
it 'creates a keyword' do
filter = user.account.custom_filters.first filter = user.account.custom_filters.first
expect(filter).to_not be_nil expect(filter).to_not be_nil
expect(filter.keywords.pluck(:keyword)).to eq ['magic'] expect(filter.keywords.pluck(:keyword)).to eq ['magic']
@ -73,11 +69,9 @@ RSpec.describe Api::V2::Filters::KeywordsController do
get :show, params: { id: keyword.id } get :show, params: { id: keyword.id }
end end
it 'returns http success' do it 'responds with the keyword', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns expected data' do
json = body_as_json json = body_as_json
expect(json[:keyword]).to eq 'foo' expect(json[:keyword]).to eq 'foo'
expect(json[:whole_word]).to be false expect(json[:whole_word]).to be false
@ -100,11 +94,9 @@ RSpec.describe Api::V2::Filters::KeywordsController do
get :update, params: { id: keyword.id, keyword: 'updated' } get :update, params: { id: keyword.id, keyword: 'updated' }
end end
it 'returns http success' do it 'updates the keyword', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'updates the keyword' do
expect(keyword.reload.keyword).to eq 'updated' expect(keyword.reload.keyword).to eq 'updated'
end end
@ -125,11 +117,9 @@ RSpec.describe Api::V2::Filters::KeywordsController do
delete :destroy, params: { id: keyword.id } delete :destroy, params: { id: keyword.id }
end end
it 'returns http success' do it 'destroys the keyword', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the filter' do
expect { keyword.reload }.to raise_error ActiveRecord::RecordNotFound expect { keyword.reload }.to raise_error ActiveRecord::RecordNotFound
end end

@ -41,16 +41,12 @@ RSpec.describe Api::V2::Filters::StatusesController do
post :create, params: { filter_id: filter_id, status_id: status.id } post :create, params: { filter_id: filter_id, status_id: status.id }
end end
it 'returns http success' do it 'creates a filter', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns a status filter' do
json = body_as_json json = body_as_json
expect(json[:status_id]).to eq status.id.to_s expect(json[:status_id]).to eq status.id.to_s
end
it 'creates a status filter' do
filter = user.account.custom_filters.first filter = user.account.custom_filters.first
expect(filter).to_not be_nil expect(filter).to_not be_nil
expect(filter.statuses.pluck(:status_id)).to eq [status.id] expect(filter.statuses.pluck(:status_id)).to eq [status.id]
@ -73,11 +69,9 @@ RSpec.describe Api::V2::Filters::StatusesController do
get :show, params: { id: status_filter.id } get :show, params: { id: status_filter.id }
end end
it 'returns http success' do it 'responds with the filter', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns expected data' do
json = body_as_json json = body_as_json
expect(json[:status_id]).to eq status_filter.status_id.to_s expect(json[:status_id]).to eq status_filter.status_id.to_s
end end
@ -99,11 +93,9 @@ RSpec.describe Api::V2::Filters::StatusesController do
delete :destroy, params: { id: status_filter.id } delete :destroy, params: { id: status_filter.id }
end end
it 'returns http success' do it 'destroys the filter', :aggregate_failures do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the filter' do
expect { status_filter.reload }.to raise_error ActiveRecord::RecordNotFound expect { status_filter.reload }.to raise_error ActiveRecord::RecordNotFound
end end

@ -1,391 +0,0 @@
HTTP/1.1 200 OK
Date: Tue, 01 May 2018 23:25:57 GMT
Content-Location: activitystreams.jsonld
Vary: negotiate,accept
TCN: choice
Last-Modified: Mon, 16 Apr 2018 00:28:23 GMT
ETag: "1eb0-569ec4caa97c0;d3-540ee27e0eec0"
Accept-Ranges: bytes
Content-Length: 7856
Cache-Control: max-age=21600
Expires: Wed, 02 May 2018 05:25:57 GMT
P3P: policyref="http://www.w3.org/2014/08/p3p.xml"
Access-Control-Allow-Origin: *
Content-Type: application/ld+json
Strict-Transport-Security: max-age=15552000; includeSubdomains; preload
Content-Security-Policy: upgrade-insecure-requests
{
"@context": {
"@vocab": "_:",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"as": "https://www.w3.org/ns/activitystreams#",
"ldp": "http://www.w3.org/ns/ldp#",
"id": "@id",
"type": "@type",
"Accept": "as:Accept",
"Activity": "as:Activity",
"IntransitiveActivity": "as:IntransitiveActivity",
"Add": "as:Add",
"Announce": "as:Announce",
"Application": "as:Application",
"Arrive": "as:Arrive",
"Article": "as:Article",
"Audio": "as:Audio",
"Block": "as:Block",
"Collection": "as:Collection",
"CollectionPage": "as:CollectionPage",
"Relationship": "as:Relationship",
"Create": "as:Create",
"Delete": "as:Delete",
"Dislike": "as:Dislike",
"Document": "as:Document",
"Event": "as:Event",
"Follow": "as:Follow",
"Flag": "as:Flag",
"Group": "as:Group",
"Ignore": "as:Ignore",
"Image": "as:Image",
"Invite": "as:Invite",
"Join": "as:Join",
"Leave": "as:Leave",
"Like": "as:Like",
"Link": "as:Link",
"Mention": "as:Mention",
"Note": "as:Note",
"Object": "as:Object",
"Offer": "as:Offer",
"OrderedCollection": "as:OrderedCollection",
"OrderedCollectionPage": "as:OrderedCollectionPage",
"Organization": "as:Organization",
"Page": "as:Page",
"Person": "as:Person",
"Place": "as:Place",
"Profile": "as:Profile",
"Question": "as:Question",
"Reject": "as:Reject",
"Remove": "as:Remove",
"Service": "as:Service",
"TentativeAccept": "as:TentativeAccept",
"TentativeReject": "as:TentativeReject",
"Tombstone": "as:Tombstone",
"Undo": "as:Undo",
"Update": "as:Update",
"Video": "as:Video",
"View": "as:View",
"Listen": "as:Listen",
"Read": "as:Read",
"Move": "as:Move",
"Travel": "as:Travel",
"IsFollowing": "as:IsFollowing",
"IsFollowedBy": "as:IsFollowedBy",
"IsContact": "as:IsContact",
"IsMember": "as:IsMember",
"subject": {
"@id": "as:subject",
"@type": "@id"
},
"relationship": {
"@id": "as:relationship",
"@type": "@id"
},
"actor": {
"@id": "as:actor",
"@type": "@id"
},
"attributedTo": {
"@id": "as:attributedTo",
"@type": "@id"
},
"attachment": {
"@id": "as:attachment",
"@type": "@id"
},
"bcc": {
"@id": "as:bcc",
"@type": "@id"
},
"bto": {
"@id": "as:bto",
"@type": "@id"
},
"cc": {
"@id": "as:cc",
"@type": "@id"
},
"context": {
"@id": "as:context",
"@type": "@id"
},
"current": {
"@id": "as:current",
"@type": "@id"
},
"first": {
"@id": "as:first",
"@type": "@id"
},
"generator": {
"@id": "as:generator",
"@type": "@id"
},
"icon": {
"@id": "as:icon",
"@type": "@id"
},
"image": {
"@id": "as:image",
"@type": "@id"
},
"inReplyTo": {
"@id": "as:inReplyTo",
"@type": "@id"
},
"items": {
"@id": "as:items",
"@type": "@id"
},
"instrument": {
"@id": "as:instrument",
"@type": "@id"
},
"orderedItems": {
"@id": "as:items",
"@type": "@id",
"@container": "@list"
},
"last": {
"@id": "as:last",
"@type": "@id"
},
"location": {
"@id": "as:location",
"@type": "@id"
},
"next": {
"@id": "as:next",
"@type": "@id"
},
"object": {
"@id": "as:object",
"@type": "@id"
},
"oneOf": {
"@id": "as:oneOf",
"@type": "@id"
},
"anyOf": {
"@id": "as:anyOf",
"@type": "@id"
},
"closed": {
"@id": "as:closed",
"@type": "xsd:dateTime"
},
"origin": {
"@id": "as:origin",
"@type": "@id"
},
"accuracy": {
"@id": "as:accuracy",
"@type": "xsd:float"
},
"prev": {
"@id": "as:prev",
"@type": "@id"
},
"preview": {
"@id": "as:preview",
"@type": "@id"
},
"replies": {
"@id": "as:replies",
"@type": "@id"
},
"result": {
"@id": "as:result",
"@type": "@id"
},
"audience": {
"@id": "as:audience",
"@type": "@id"
},
"partOf": {
"@id": "as:partOf",
"@type": "@id"
},
"tag": {
"@id": "as:tag",
"@type": "@id"
},
"target": {
"@id": "as:target",
"@type": "@id"
},
"to": {
"@id": "as:to",
"@type": "@id"
},
"url": {
"@id": "as:url",
"@type": "@id"
},
"altitude": {
"@id": "as:altitude",
"@type": "xsd:float"
},
"content": "as:content",
"contentMap": {
"@id": "as:content",
"@container": "@language"
},
"name": "as:name",
"nameMap": {
"@id": "as:name",
"@container": "@language"
},
"duration": {
"@id": "as:duration",
"@type": "xsd:duration"
},
"endTime": {
"@id": "as:endTime",
"@type": "xsd:dateTime"
},
"height": {
"@id": "as:height",
"@type": "xsd:nonNegativeInteger"
},
"href": {
"@id": "as:href",
"@type": "@id"
},
"hreflang": "as:hreflang",
"latitude": {
"@id": "as:latitude",
"@type": "xsd:float"
},
"longitude": {
"@id": "as:longitude",
"@type": "xsd:float"
},
"mediaType": "as:mediaType",
"published": {
"@id": "as:published",
"@type": "xsd:dateTime"
},
"radius": {
"@id": "as:radius",
"@type": "xsd:float"
},
"rel": "as:rel",
"startIndex": {
"@id": "as:startIndex",
"@type": "xsd:nonNegativeInteger"
},
"startTime": {
"@id": "as:startTime",
"@type": "xsd:dateTime"
},
"summary": "as:summary",
"summaryMap": {
"@id": "as:summary",
"@container": "@language"
},
"totalItems": {
"@id": "as:totalItems",
"@type": "xsd:nonNegativeInteger"
},
"units": "as:units",
"updated": {
"@id": "as:updated",
"@type": "xsd:dateTime"
},
"width": {
"@id": "as:width",
"@type": "xsd:nonNegativeInteger"
},
"describes": {
"@id": "as:describes",
"@type": "@id"
},
"formerType": {
"@id": "as:formerType",
"@type": "@id"
},
"deleted": {
"@id": "as:deleted",
"@type": "xsd:dateTime"
},
"inbox": {
"@id": "ldp:inbox",
"@type": "@id"
},
"outbox": {
"@id": "as:outbox",
"@type": "@id"
},
"following": {
"@id": "as:following",
"@type": "@id"
},
"followers": {
"@id": "as:followers",
"@type": "@id"
},
"streams": {
"@id": "as:streams",
"@type": "@id"
},
"preferredUsername": "as:preferredUsername",
"endpoints": {
"@id": "as:endpoints",
"@type": "@id"
},
"uploadMedia": {
"@id": "as:uploadMedia",
"@type": "@id"
},
"proxyUrl": {
"@id": "as:proxyUrl",
"@type": "@id"
},
"liked": {
"@id": "as:liked",
"@type": "@id"
},
"oauthAuthorizationEndpoint": {
"@id": "as:oauthAuthorizationEndpoint",
"@type": "@id"
},
"oauthTokenEndpoint": {
"@id": "as:oauthTokenEndpoint",
"@type": "@id"
},
"provideClientKey": {
"@id": "as:provideClientKey",
"@type": "@id"
},
"signClientKey": {
"@id": "as:signClientKey",
"@type": "@id"
},
"sharedInbox": {
"@id": "as:sharedInbox",
"@type": "@id"
},
"Public": {
"@id": "as:Public",
"@type": "@id"
},
"source": "as:source",
"likes": {
"@id": "as:likes",
"@type": "@id"
},
"shares": {
"@id": "as:shares",
"@type": "@id"
}
}
}

@ -1,100 +0,0 @@
HTTP/1.1 200 OK
Accept-Ranges: bytes
Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept-Encoding
Access-Control-Allow-Origin: *
Content-Type: application/ld+json
Date: Tue, 01 May 2018 23:28:21 GMT
Etag: "e26-547a6fc75b04a-gzip"
Last-Modified: Fri, 03 Feb 2017 21:30:09 GMT
Server: Apache/2.4.7 (Ubuntu)
Vary: Accept-Encoding
Transfer-Encoding: chunked
{
"@context": {
"id": "@id",
"type": "@type",
"cred": "https://w3id.org/credentials#",
"dc": "http://purl.org/dc/terms/",
"identity": "https://w3id.org/identity#",
"perm": "https://w3id.org/permissions#",
"ps": "https://w3id.org/payswarm#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"sec": "https://w3id.org/security#",
"schema": "http://schema.org/",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"Group": "https://www.w3.org/ns/activitystreams#Group",
"claim": {"@id": "cred:claim", "@type": "@id"},
"credential": {"@id": "cred:credential", "@type": "@id"},
"issued": {"@id": "cred:issued", "@type": "xsd:dateTime"},
"issuer": {"@id": "cred:issuer", "@type": "@id"},
"recipient": {"@id": "cred:recipient", "@type": "@id"},
"Credential": "cred:Credential",
"CryptographicKeyCredential": "cred:CryptographicKeyCredential",
"about": {"@id": "schema:about", "@type": "@id"},
"address": {"@id": "schema:address", "@type": "@id"},
"addressCountry": "schema:addressCountry",
"addressLocality": "schema:addressLocality",
"addressRegion": "schema:addressRegion",
"comment": "rdfs:comment",
"created": {"@id": "dc:created", "@type": "xsd:dateTime"},
"creator": {"@id": "dc:creator", "@type": "@id"},
"description": "schema:description",
"email": "schema:email",
"familyName": "schema:familyName",
"givenName": "schema:givenName",
"image": {"@id": "schema:image", "@type": "@id"},
"label": "rdfs:label",
"name": "schema:name",
"postalCode": "schema:postalCode",
"streetAddress": "schema:streetAddress",
"title": "dc:title",
"url": {"@id": "schema:url", "@type": "@id"},
"Person": "schema:Person",
"PostalAddress": "schema:PostalAddress",
"Organization": "schema:Organization",
"identityService": {"@id": "identity:identityService", "@type": "@id"},
"idp": {"@id": "identity:idp", "@type": "@id"},
"Identity": "identity:Identity",
"paymentProcessor": "ps:processor",
"preferences": {"@id": "ps:preferences", "@type": "@vocab"},
"cipherAlgorithm": "sec:cipherAlgorithm",
"cipherData": "sec:cipherData",
"cipherKey": "sec:cipherKey",
"digestAlgorithm": "sec:digestAlgorithm",
"digestValue": "sec:digestValue",
"domain": "sec:domain",
"expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"initializationVector": "sec:initializationVector",
"member": {"@id": "schema:member", "@type": "@id"},
"memberOf": {"@id": "schema:memberOf", "@type": "@id"},
"nonce": "sec:nonce",
"normalizationAlgorithm": "sec:normalizationAlgorithm",
"owner": {"@id": "sec:owner", "@type": "@id"},
"password": "sec:password",
"privateKey": {"@id": "sec:privateKey", "@type": "@id"},
"privateKeyPem": "sec:privateKeyPem",
"publicKey": {"@id": "sec:publicKey", "@type": "@id"},
"publicKeyPem": "sec:publicKeyPem",
"publicKeyService": {"@id": "sec:publicKeyService", "@type": "@id"},
"revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"},
"signature": "sec:signature",
"signatureAlgorithm": "sec:signatureAlgorithm",
"signatureValue": "sec:signatureValue",
"CryptographicKey": "sec:Key",
"EncryptedMessage": "sec:EncryptedMessage",
"GraphSignature2012": "sec:GraphSignature2012",
"LinkedDataSignature2015": "sec:LinkedDataSignature2015",
"accessControl": {"@id": "perm:accessControl", "@type": "@id"},
"writePermission": {"@id": "perm:writePermission", "@type": "@id"}
}
}

@ -1,61 +0,0 @@
HTTP/1.1 200 OK
Accept-Ranges: bytes
Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept-Encoding
Access-Control-Allow-Origin: *
Content-Type: application/ld+json
Date: Wed, 02 May 2018 16:25:32 GMT
Etag: "7e3-5651ec0f7c5ed-gzip"
Last-Modified: Tue, 13 Feb 2018 21:34:04 GMT
Server: Apache/2.4.7 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 2019
{
"@context": {
"id": "@id",
"type": "@type",
"dc": "http://purl.org/dc/terms/",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"EcdsaKoblitzSignature2016": "sec:EcdsaKoblitzSignature2016",
"Ed25519Signature2018": "sec:Ed25519Signature2018",
"EncryptedMessage": "sec:EncryptedMessage",
"GraphSignature2012": "sec:GraphSignature2012",
"LinkedDataSignature2015": "sec:LinkedDataSignature2015",
"LinkedDataSignature2016": "sec:LinkedDataSignature2016",
"CryptographicKey": "sec:Key",
"authenticationTag": "sec:authenticationTag",
"canonicalizationAlgorithm": "sec:canonicalizationAlgorithm",
"cipherAlgorithm": "sec:cipherAlgorithm",
"cipherData": "sec:cipherData",
"cipherKey": "sec:cipherKey",
"created": {"@id": "dc:created", "@type": "xsd:dateTime"},
"creator": {"@id": "dc:creator", "@type": "@id"},
"digestAlgorithm": "sec:digestAlgorithm",
"digestValue": "sec:digestValue",
"domain": "sec:domain",
"encryptionKey": "sec:encryptionKey",
"expiration": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"initializationVector": "sec:initializationVector",
"iterationCount": "sec:iterationCount",
"nonce": "sec:nonce",
"normalizationAlgorithm": "sec:normalizationAlgorithm",
"owner": {"@id": "sec:owner", "@type": "@id"},
"password": "sec:password",
"privateKey": {"@id": "sec:privateKey", "@type": "@id"},
"privateKeyPem": "sec:privateKeyPem",
"publicKey": {"@id": "sec:publicKey", "@type": "@id"},
"publicKeyBase58": "sec:publicKeyBase58",
"publicKeyPem": "sec:publicKeyPem",
"publicKeyService": {"@id": "sec:publicKeyService", "@type": "@id"},
"revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"},
"salt": "sec:salt",
"signature": "sec:signature",
"signatureAlgorithm": "sec:signingAlgorithm",
"signatureValue": "sec:signatureValue"
}
}

@ -18,10 +18,6 @@ RSpec.describe ActivityPub::LinkedDataSignature do
let(:json) { raw_json.merge('signature' => signature) } let(:json) { raw_json.merge('signature' => signature) }
before do
stub_jsonld_contexts!
end
describe '#verify_actor!' do describe '#verify_actor!' do
context 'when signature matches' do context 'when signature matches' do
let(:raw_signature) do let(:raw_signature) do

@ -82,6 +82,10 @@ RSpec.describe LinkDetailsExtractor do
'name' => 'Pet News', 'name' => 'Pet News',
'url' => 'https://example.com', 'url' => 'https://example.com',
}, },
'inLanguage' => {
name: 'English',
alternateName: 'en',
},
}.to_json }.to_json
end end
@ -115,6 +119,12 @@ RSpec.describe LinkDetailsExtractor do
expect(subject.provider_name).to eq 'Pet News' expect(subject.provider_name).to eq 'Pet News'
end end
end end
describe '#language' do
it 'returns the language from structured data' do
expect(subject.language).to eq 'en'
end
end
end end
context 'when is wrapped in CDATA tags' do context 'when is wrapped in CDATA tags' do

@ -4,9 +4,31 @@ require 'rails_helper'
require 'mastodon/cli/statuses' require 'mastodon/cli/statuses'
describe Mastodon::CLI::Statuses do describe Mastodon::CLI::Statuses do
let(:cli) { described_class.new }
describe '.exit_on_failure?' do describe '.exit_on_failure?' do
it 'returns true' do it 'returns true' do
expect(described_class.exit_on_failure?).to be true expect(described_class.exit_on_failure?).to be true
end end
end end
describe '#remove', use_transactional_tests: false do
context 'with small batch size' do
let(:options) { { batch_size: 0 } }
it 'exits with error message' do
expect { cli.invoke :remove, [], options }.to output(
a_string_including('Cannot run')
).to_stdout.and raise_error(SystemExit)
end
end
context 'with default batch size' do
it 'removes unreferenced statuses' do
expect { cli.invoke :remove }.to output(
a_string_including('Done after')
).to_stdout
end
end
end
end end

@ -54,26 +54,6 @@ Devise::Test::ControllerHelpers.module_eval do
end end
end end
module SignedRequestHelpers
def get(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'] = ENV.fetch('LOCAL_DOMAIN')
signed_headers = headers.merge('(request-target)' => "get #{path}").slice('(request-target)', 'Host', 'Date')
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
RSpec.configure do |config| RSpec.configure do |config|
# This is set before running spec:system, see lib/tasks/tests.rake # This is set before running spec:system, see lib/tasks/tests.rake
config.filter_run_excluding type: lambda { |type| config.filter_run_excluding type: lambda { |type|
@ -105,6 +85,12 @@ RSpec.configure do |config|
config.include Redisable config.include Redisable
config.include SignedRequestHelpers, type: :request config.include SignedRequestHelpers, type: :request
config.around(:each, use_transactional_tests: false) do |example|
self.use_transactional_tests = false
example.run
self.use_transactional_tests = true
end
config.before :each, type: :cli do config.before :each, type: :cli do
stub_stdout stub_stdout
stub_reset_connection_pools stub_reset_connection_pools
@ -114,14 +100,6 @@ RSpec.configure do |config|
Capybara.current_driver = :rack_test Capybara.current_driver = :rack_test
end end
config.before :each, type: :controller do
stub_jsonld_contexts!
end
config.before :each, type: :service do
stub_jsonld_contexts!
end
config.before :suite do config.before :suite do
if RUN_SYSTEM_SPECS if RUN_SYSTEM_SPECS
Webpacker.compile Webpacker.compile
@ -212,9 +190,3 @@ def stub_reset_connection_pools
allow(ActiveRecord::Base).to receive(:establish_connection) allow(ActiveRecord::Base).to receive(:establish_connection)
allow(RedisConfiguration).to receive(:establish_pool) allow(RedisConfiguration).to receive(:establish_pool)
end end
def stub_jsonld_contexts!
stub_request(:get, 'https://www.w3.org/ns/activitystreams').to_return(request_fixture('json-ld.activitystreams.txt'))
stub_request(:get, 'https://w3id.org/identity/v1').to_return(request_fixture('json-ld.identity.txt'))
stub_request(:get, 'https://w3id.org/security/v1').to_return(request_fixture('json-ld.security.txt'))
end

@ -51,14 +51,9 @@ RSpec.describe 'Account actions' do
it_behaves_like 'a successful notification delivery' it_behaves_like 'a successful notification delivery'
it_behaves_like 'a successful logged action', :disable, :user it_behaves_like 'a successful logged action', :disable, :user
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
it 'disables the target account' do it 'disables the target account' do
expect { subject }.to change { target_account.reload.user_disabled? }.from(false).to(true) expect { subject }.to change { target_account.reload.user_disabled? }.from(false).to(true)
expect(response).to have_http_status(200)
end end
end end
@ -70,14 +65,9 @@ RSpec.describe 'Account actions' do
it_behaves_like 'a successful notification delivery' it_behaves_like 'a successful notification delivery'
it_behaves_like 'a successful logged action', :sensitive, :account it_behaves_like 'a successful logged action', :sensitive, :account
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
it 'marks the target account as sensitive' do it 'marks the target account as sensitive' do
expect { subject }.to change { target_account.reload.sensitized? }.from(false).to(true) expect { subject }.to change { target_account.reload.sensitized? }.from(false).to(true)
expect(response).to have_http_status(200)
end end
end end
@ -89,14 +79,9 @@ RSpec.describe 'Account actions' do
it_behaves_like 'a successful notification delivery' it_behaves_like 'a successful notification delivery'
it_behaves_like 'a successful logged action', :silence, :account it_behaves_like 'a successful logged action', :silence, :account
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
it 'marks the target account as silenced' do it 'marks the target account as silenced' do
expect { subject }.to change { target_account.reload.silenced? }.from(false).to(true) expect { subject }.to change { target_account.reload.silenced? }.from(false).to(true)
expect(response).to have_http_status(200)
end end
end end
@ -108,14 +93,9 @@ RSpec.describe 'Account actions' do
it_behaves_like 'a successful notification delivery' it_behaves_like 'a successful notification delivery'
it_behaves_like 'a successful logged action', :suspend, :account it_behaves_like 'a successful logged action', :suspend, :account
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
it 'marks the target account as suspended' do it 'marks the target account as suspended' do
expect { subject }.to change { target_account.reload.suspended? }.from(false).to(true) expect { subject }.to change { target_account.reload.suspended? }.from(false).to(true)
expect(response).to have_http_status(200)
end end
end end

@ -0,0 +1,401 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Accounts' do
let(:role) { UserRole.find_by(name: 'Admin') }
let(:user) { Fabricate(:user, role: role) }
let(:scopes) { 'admin:read:accounts admin:write:accounts' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
describe 'GET /api/v1/admin/accounts' do
subject do
get '/api/v1/admin/accounts', headers: headers, params: params
end
shared_examples 'a successful request' do
it 'returns the correct accounts', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(body_as_json.pluck(:id)).to match_array(expected_results.map { |a| a.id.to_s })
end
end
let!(:remote_account) { Fabricate(:account, domain: 'example.org') }
let!(:suspended_account) { Fabricate(:account, suspended: true) }
let!(:disabled_account) { Fabricate(:user, disabled: true).account }
let!(:pending_account) { Fabricate(:user, approved: false).account }
let!(:admin_account) { user.account }
let(:params) { {} }
it_behaves_like 'forbidden for wrong scope', 'read read:accounts admin:write admin:write:accounts'
it_behaves_like 'forbidden for wrong role', ''
context 'when requesting active local staff accounts' do
let(:expected_results) { [admin_account] }
let(:params) { { active: 'true', local: 'true', staff: 'true' } }
it_behaves_like 'a successful request'
end
context 'when requesting remote accounts from a specified domain' do
let(:expected_results) { [remote_account] }
let(:params) { { by_domain: 'example.org', remote: 'true' } }
before do
Fabricate(:account, domain: 'foo.bar')
end
it_behaves_like 'a successful request'
end
context 'when requesting suspended accounts' do
let(:expected_results) { [suspended_account] }
let(:params) { { suspended: 'true' } }
before do
Fabricate(:account, domain: 'foo.bar', suspended: true)
end
it_behaves_like 'a successful request'
end
context 'when requesting disabled accounts' do
let(:expected_results) { [disabled_account] }
let(:params) { { disabled: 'true' } }
it_behaves_like 'a successful request'
end
context 'when requesting pending accounts' do
let(:expected_results) { [pending_account] }
let(:params) { { pending: 'true' } }
before do
pending_account.user.update(approved: false)
end
it_behaves_like 'a successful request'
end
context 'when no parameter is given' do
let(:expected_results) { [disabled_account, pending_account, admin_account] }
it_behaves_like 'a successful request'
end
context 'with limit param' do
let(:params) { { limit: 2 } }
it 'returns only the requested number of accounts', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(body_as_json.size).to eq(params[:limit])
end
end
end
describe 'GET /api/v1/admin/accounts/:id' do
subject do
get "/api/v1/admin/accounts/#{account.id}", headers: headers
end
let(:account) { Fabricate(:account) }
it_behaves_like 'forbidden for wrong scope', 'read read:accounts admin:write admin:write:accounts'
it_behaves_like 'forbidden for wrong role', ''
it 'returns the requested account successfully', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(body_as_json).to match(
a_hash_including(id: account.id.to_s, username: account.username, email: account.user.email)
)
end
context 'when the account is not found' do
it 'returns http not found' do
get '/api/v1/admin/accounts/-1', headers: headers
expect(response).to have_http_status(404)
end
end
end
describe 'POST /api/v1/admin/accounts/:id/approve' do
subject do
post "/api/v1/admin/accounts/#{account.id}/approve", headers: headers
end
let(:account) { Fabricate(:account) }
context 'when the account is pending' do
before do
account.user.update(approved: false)
end
it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read'
it_behaves_like 'forbidden for wrong role', ''
it 'approves the user successfully', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(account.reload.user_approved?).to be(true)
end
it 'logs action', :aggregate_failures do
subject
log_item = Admin::ActionLog.last
expect(log_item).to be_present
expect(log_item.action).to eq :approve
expect(log_item.account_id).to eq user.account_id
expect(log_item.target_id).to eq account.user.id
end
end
context 'when the account is already approved' do
it 'returns http forbidden' do
subject
expect(response).to have_http_status(403)
end
end
context 'when the account is not found' do
it 'returns http not found' do
post '/api/v1/admin/accounts/-1/approve', headers: headers
expect(response).to have_http_status(404)
end
end
end
describe 'POST /api/v1/admin/accounts/:id/reject' do
subject do
post "/api/v1/admin/accounts/#{account.id}/reject", headers: headers
end
let(:account) { Fabricate(:account) }
context 'when the account is pending' do
before do
account.user.update(approved: false)
end
it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read'
it_behaves_like 'forbidden for wrong role', ''
it 'removes the user successfully', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(User.where(id: account.user.id)).to_not exist
end
it 'logs action', :aggregate_failures do
subject
log_item = Admin::ActionLog.last
expect(log_item).to be_present
expect(log_item.action).to eq :reject
expect(log_item.account_id).to eq user.account_id
expect(log_item.target_id).to eq account.user.id
end
end
context 'when account is already approved' do
it 'returns http forbidden' do
subject
expect(response).to have_http_status(403)
end
end
context 'when the account is not found' do
it 'returns http not found' do
post '/api/v1/admin/accounts/-1/reject', headers: headers
expect(response).to have_http_status(404)
end
end
end
describe 'POST /api/v1/admin/accounts/:id/enable' do
subject do
post "/api/v1/admin/accounts/#{account.id}/enable", headers: headers
end
let(:account) { Fabricate(:account) }
before do
account.user.update(disabled: true)
end
it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read'
it_behaves_like 'forbidden for wrong role', ''
it 'enables the user successfully', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(account.reload.user_disabled?).to be false
end
context 'when the account is not found' do
it 'returns http not found' do
post '/api/v1/admin/accounts/-1/enable', headers: headers
expect(response).to have_http_status(404)
end
end
end
describe 'POST /api/v1/admin/accounts/:id/unsuspend' do
subject do
post "/api/v1/admin/accounts/#{account.id}/unsuspend", headers: headers
end
let(:account) { Fabricate(:account) }
context 'when the account is suspended' do
before do
account.suspend!
end
it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read'
it_behaves_like 'forbidden for wrong role', ''
it 'unsuspends the account successfully', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(account.reload.suspended?).to be false
end
end
context 'when the account is not suspended' do
it 'returns http forbidden' do
subject
expect(response).to have_http_status(403)
end
end
context 'when the account is not found' do
it 'returns http not found' do
post '/api/v1/admin/accounts/-1/unsuspend', headers: headers
expect(response).to have_http_status(404)
end
end
end
describe 'POST /api/v1/admin/accounts/:id/unsensitive' do
subject do
post "/api/v1/admin/accounts/#{account.id}/unsensitive", headers: headers
end
let(:account) { Fabricate(:account) }
before do
account.update(sensitized_at: 10.days.ago)
end
it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read'
it_behaves_like 'forbidden for wrong role', ''
it 'unsensitizes the account successfully', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(account.reload.sensitized?).to be false
end
context 'when the account is not found' do
it 'returns http not found' do
post '/api/v1/admin/accounts/-1/unsensitive', headers: headers
expect(response).to have_http_status(404)
end
end
end
describe 'POST /api/v1/admin/accounts/:id/unsilence' do
subject do
post "/api/v1/admin/accounts/#{account.id}/unsilence", headers: headers
end
let(:account) { Fabricate(:account) }
before do
account.update(silenced_at: 3.days.ago)
end
it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read'
it_behaves_like 'forbidden for wrong role', ''
it 'unsilences the account successfully', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(account.reload.silenced?).to be false
end
context 'when the account is not found' do
it 'returns http not found' do
post '/api/v1/admin/accounts/-1/unsilence', headers: headers
expect(response).to have_http_status(404)
end
end
end
describe 'DELETE /api/v1/admin/accounts/:id' do
subject do
delete "/api/v1/admin/accounts/#{account.id}", headers: headers
end
let(:account) { Fabricate(:account) }
context 'when account is suspended' do
before do
account.suspend!
end
it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read'
it_behaves_like 'forbidden for wrong role', ''
it 'deletes the account successfully', :aggregate_failures do
allow(Admin::AccountDeletionWorker).to receive(:perform_async)
subject
expect(response).to have_http_status(200)
expect(Admin::AccountDeletionWorker).to have_received(:perform_async).with(account.id).once
end
end
context 'when account is not suspended' do
it 'returns http forbidden' do
subject
expect(response).to have_http_status(403)
end
end
context 'when the account is not found' do
it 'returns http not found' do
delete '/api/v1/admin/accounts/-1', headers: headers
expect(response).to have_http_status(404)
end
end
end
end

@ -92,15 +92,10 @@ RSpec.describe 'Canonical Email Blocks' do
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
context 'when the requested canonical email block exists' do context 'when the requested canonical email block exists' do
it 'returns http success' do it 'returns the requested canonical email block data correctly', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the requested canonical email block data correctly' do
subject
json = body_as_json json = body_as_json
expect(json[:id]).to eq(canonical_email_block.id.to_s) expect(json[:id]).to eq(canonical_email_block.id.to_s)
@ -142,29 +137,19 @@ RSpec.describe 'Canonical Email Blocks' do
context 'when there is a matching canonical email block' do context 'when there is a matching canonical email block' do
let!(:canonical_email_block) { CanonicalEmailBlock.create(params) } let!(:canonical_email_block) { CanonicalEmailBlock.create(params) }
it 'returns http success' do it 'returns the expected canonical email hash', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the expected canonical email hash' do
subject
expect(body_as_json[0][:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) expect(body_as_json[0][:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
end end
end end
context 'when there is no matching canonical email block' do context 'when there is no matching canonical email block' do
it 'returns http success' do it 'returns an empty list', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns an empty list' do
subject
expect(body_as_json).to be_empty expect(body_as_json).to be_empty
end end
end end
@ -183,15 +168,10 @@ RSpec.describe 'Canonical Email Blocks' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'returns the canonical_email_hash correctly', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the canonical_email_hash correctly' do
subject
expect(body_as_json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) expect(body_as_json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
end end
@ -208,15 +188,10 @@ RSpec.describe 'Canonical Email Blocks' do
context 'when the canonical_email_hash param is provided instead of email' do context 'when the canonical_email_hash param is provided instead of email' do
let(:params) { { canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } } let(:params) { { canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } }
it 'returns http success' do it 'returns the correct canonical_email_hash', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the correct canonical_email_hash' do
subject
expect(body_as_json[:canonical_email_hash]).to eq(params[:canonical_email_hash]) expect(body_as_json[:canonical_email_hash]).to eq(params[:canonical_email_hash])
end end
end end
@ -224,15 +199,10 @@ RSpec.describe 'Canonical Email Blocks' do
context 'when both email and canonical_email_hash params are provided' do context 'when both email and canonical_email_hash params are provided' do
let(:params) { { email: 'example@email.com', canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } } let(:params) { { email: 'example@email.com', canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } }
it 'returns http success' do it 'ignores the canonical_email_hash param', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'ignores the canonical_email_hash param' do
subject
expect(body_as_json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) expect(body_as_json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
end end
end end
@ -262,15 +232,10 @@ RSpec.describe 'Canonical Email Blocks' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'deletes the canonical email block', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'deletes the canonical email block' do
subject
expect(CanonicalEmailBlock.find_by(id: canonical_email_block.id)).to be_nil expect(CanonicalEmailBlock.find_by(id: canonical_email_block.id)).to be_nil
end end

@ -75,15 +75,10 @@ RSpec.describe 'Domain Allows' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'returns the expected allowed domain name', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the expected allowed domain name' do
subject
expect(body_as_json[:domain]).to eq domain_allow.domain expect(body_as_json[:domain]).to eq domain_allow.domain
end end
@ -108,21 +103,11 @@ RSpec.describe 'Domain Allows' do
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
context 'with a valid domain name' do context 'with a valid domain name' do
it 'returns http success' do it 'returns the expected domain name', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the expected domain name' do
subject
expect(body_as_json[:domain]).to eq 'foo.bar.com' expect(body_as_json[:domain]).to eq 'foo.bar.com'
end
it 'creates a domain allow' do
subject
expect(DomainAllow.find_by(domain: 'foo.bar.com')).to be_present expect(DomainAllow.find_by(domain: 'foo.bar.com')).to be_present
end end
end end
@ -171,15 +156,10 @@ RSpec.describe 'Domain Allows' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'deletes the allowed domain', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'deletes the allowed domain' do
subject
expect(DomainAllow.find_by(id: domain_allow.id)).to be_nil expect(DomainAllow.find_by(id: domain_allow.id)).to be_nil
end end

@ -89,15 +89,10 @@ RSpec.describe 'Domain Blocks' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'returns the expected domain block content', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the expected domain block content' do
subject
expect(body_as_json).to eq( expect(body_as_json).to eq(
{ {
id: domain_block.id.to_s, id: domain_block.id.to_s,
@ -133,27 +128,18 @@ RSpec.describe 'Domain Blocks' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'returns expected domain name and severity', :aggregate_failures do
subject
expect(response).to have_http_status(200)
end
it 'returns expected domain name and severity' do
subject subject
body = body_as_json body = body_as_json
expect(response).to have_http_status(200)
expect(body).to match a_hash_including( expect(body).to match a_hash_including(
{ {
domain: 'foo.bar.com', domain: 'foo.bar.com',
severity: 'silence', severity: 'silence',
} }
) )
end
it 'creates a domain block' do
subject
expect(DomainBlock.find_by(domain: 'foo.bar.com')).to be_present expect(DomainBlock.find_by(domain: 'foo.bar.com')).to be_present
end end
@ -163,15 +149,10 @@ RSpec.describe 'Domain Blocks' do
Fabricate(:domain_block, domain: 'bar.com', severity: :suspend) Fabricate(:domain_block, domain: 'bar.com', severity: :suspend)
end end
it 'returns http unprocessable entity' do it 'returns existing domain block in error', :aggregate_failures do
subject subject
expect(response).to have_http_status(422) expect(response).to have_http_status(422)
end
it 'returns existing domain block in error' do
subject
expect(body_as_json[:existing_domain_block][:domain]).to eq('bar.com') expect(body_as_json[:existing_domain_block][:domain]).to eq('bar.com')
end end
end end
@ -199,15 +180,10 @@ RSpec.describe 'Domain Blocks' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'returns the updated domain block', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the updated domain block' do
subject
expect(body_as_json).to match a_hash_including( expect(body_as_json).to match a_hash_including(
{ {
id: domain_block.id.to_s, id: domain_block.id.to_s,
@ -241,15 +217,10 @@ RSpec.describe 'Domain Blocks' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'deletes the domain block', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'deletes the domain block' do
subject
expect(DomainBlock.find_by(id: domain_block.id)).to be_nil expect(DomainBlock.find_by(id: domain_block.id)).to be_nil
end end

@ -93,15 +93,10 @@ RSpec.describe 'Email Domain Blocks' do
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
context 'when email domain block exists' do context 'when email domain block exists' do
it 'returns http success' do it 'returns the correct blocked domain', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the correct blocked domain' do
subject
expect(body_as_json[:domain]).to eq(email_domain_block.domain) expect(body_as_json[:domain]).to eq(email_domain_block.domain)
end end
end end
@ -126,15 +121,10 @@ RSpec.describe 'Email Domain Blocks' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'returns the correct blocked email domain', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the correct blocked email domain' do
subject
expect(body_as_json[:domain]).to eq(params[:domain]) expect(body_as_json[:domain]).to eq(params[:domain])
end end
@ -182,21 +172,11 @@ RSpec.describe 'Email Domain Blocks' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'deletes email domain block', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns an empty body' do
subject
expect(body_as_json).to be_empty expect(body_as_json).to be_empty
end
it 'deletes email domain block' do
subject
expect(EmailDomainBlock.find_by(id: email_domain_block.id)).to be_nil expect(EmailDomainBlock.find_by(id: email_domain_block.id)).to be_nil
end end

@ -84,15 +84,10 @@ RSpec.describe 'IP Blocks' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'returns the correct ip block', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the correct ip block' do
subject
json = body_as_json json = body_as_json
expect(json[:ip]).to eq("#{ip_block.ip}/#{ip_block.ip.prefix}") expect(json[:ip]).to eq("#{ip_block.ip}/#{ip_block.ip.prefix}")
@ -119,15 +114,10 @@ RSpec.describe 'IP Blocks' do
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator' it_behaves_like 'forbidden for wrong role', 'Moderator'
it 'returns http success' do it 'returns the correct ip block', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the correct ip block' do
subject
json = body_as_json json = body_as_json
expect(json[:ip]).to eq("#{params[:ip]}/32") expect(json[:ip]).to eq("#{params[:ip]}/32")
@ -186,15 +176,10 @@ RSpec.describe 'IP Blocks' do
let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access', comment: 'Spam', expires_in: 48.hours) } let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access', comment: 'Spam', expires_in: 48.hours) }
let(:params) { { severity: 'sign_up_requires_approval', comment: 'Decreasing severity' } } let(:params) { { severity: 'sign_up_requires_approval', comment: 'Decreasing severity' } }
it 'returns http success' do it 'returns the correct ip block', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the correct ip block' do
subject
expect(body_as_json).to match(hash_including({ expect(body_as_json).to match(hash_including({
ip: "#{ip_block.ip}/#{ip_block.ip.prefix}", ip: "#{ip_block.ip}/#{ip_block.ip.prefix}",
severity: 'sign_up_requires_approval', severity: 'sign_up_requires_approval',
@ -226,21 +211,11 @@ RSpec.describe 'IP Blocks' do
let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access') } let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access') }
it 'returns http success' do it 'deletes the ip block', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns an empty body' do
subject
expect(body_as_json).to be_empty expect(body_as_json).to be_empty
end
it 'deletes the ip block' do
subject
expect(IpBlock.find_by(id: ip_block.id)).to be_nil expect(IpBlock.find_by(id: ip_block.id)).to be_nil
end end

@ -122,15 +122,10 @@ RSpec.describe 'Reports' do
it_behaves_like 'forbidden for wrong scope', 'write:statuses' it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do it 'returns the requested report content', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the requested report content' do
subject
expect(body_as_json).to include( expect(body_as_json).to include(
{ {
id: report.id.to_s, id: report.id.to_s,
@ -155,18 +150,10 @@ RSpec.describe 'Reports' do
let!(:report) { Fabricate(:report, category: :other) } let!(:report) { Fabricate(:report, category: :other) }
let(:params) { { category: 'spam' } } let(:params) { { category: 'spam' } }
it 'returns http success' do it 'updates the report category', :aggregate_failures do
subject
expect(response).to have_http_status(200)
end
it 'updates the report category' do
expect { subject }.to change { report.reload.category }.from('other').to('spam') expect { subject }.to change { report.reload.category }.from('other').to('spam')
end
it 'returns the updated report content' do expect(response).to have_http_status(200)
subject
report.reload report.reload
@ -196,14 +183,9 @@ RSpec.describe 'Reports' do
it_behaves_like 'forbidden for wrong scope', 'write:statuses' it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do it 'marks report as resolved', :aggregate_failures do
subject
expect(response).to have_http_status(200)
end
it 'marks report as resolved' do
expect { subject }.to change { report.reload.unresolved? }.from(true).to(false) expect { subject }.to change { report.reload.unresolved? }.from(true).to(false)
expect(response).to have_http_status(200)
end end
end end
@ -217,14 +199,9 @@ RSpec.describe 'Reports' do
it_behaves_like 'forbidden for wrong scope', 'write:statuses' it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do it 'marks report as unresolved', :aggregate_failures do
subject
expect(response).to have_http_status(200)
end
it 'marks report as unresolved' do
expect { subject }.to change { report.reload.unresolved? }.from(false).to(true) expect { subject }.to change { report.reload.unresolved? }.from(false).to(true)
expect(response).to have_http_status(200)
end end
end end
@ -238,14 +215,9 @@ RSpec.describe 'Reports' do
it_behaves_like 'forbidden for wrong scope', 'write:statuses' it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do it 'assigns report to the requesting user', :aggregate_failures do
subject
expect(response).to have_http_status(200)
end
it 'assigns report to the requesting user' do
expect { subject }.to change { report.reload.assigned_account_id }.from(nil).to(user.account.id) expect { subject }.to change { report.reload.assigned_account_id }.from(nil).to(user.account.id)
expect(response).to have_http_status(200)
end end
end end
@ -259,14 +231,9 @@ RSpec.describe 'Reports' do
it_behaves_like 'forbidden for wrong scope', 'write:statuses' it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do it 'unassigns report from assignee', :aggregate_failures do
subject
expect(response).to have_http_status(200)
end
it 'unassigns report from assignee' do
expect { subject }.to change { report.reload.assigned_account_id }.from(user.account.id).to(nil) expect { subject }.to change { report.reload.assigned_account_id }.from(user.account.id).to(nil)
expect(response).to have_http_status(200)
end end
end end
end end

@ -0,0 +1,129 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Links' do
let(:role) { UserRole.find_by(name: 'Admin') }
let(:user) { Fabricate(:user, role: role) }
let(:scopes) { 'admin:read admin:write' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
describe 'GET /api/v1/admin/trends/links' do
subject do
get '/api/v1/admin/trends/links', headers: headers
end
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
end
describe 'POST /api/v1/admin/trends/links/:id/approve' do
subject do
post "/api/v1/admin/trends/links/#{preview_card.id}/approve", headers: headers
end
let(:preview_card) { Fabricate(:preview_card, trendable: false) }
it_behaves_like 'forbidden for wrong scope', 'read write'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
it 'sets the link as trendable' do
expect { subject }.to change { preview_card.reload.trendable }.from(false).to(true)
end
it 'returns the link data' do
subject
expect(body_as_json).to match(
a_hash_including(
url: preview_card.url,
title: preview_card.title,
description: preview_card.description,
type: 'link',
requires_review: false
)
)
end
context 'when the link does not exist' do
it 'returns http not found' do
post '/api/v1/admin/trends/links/-1/approve', headers: headers
expect(response).to have_http_status(404)
end
end
context 'without an authorization header' do
let(:headers) { {} }
it 'returns http forbidden' do
subject
expect(response).to have_http_status(403)
end
end
end
describe 'POST /api/v1/admin/trends/links/:id/reject' do
subject do
post "/api/v1/admin/trends/links/#{preview_card.id}/reject", headers: headers
end
let(:preview_card) { Fabricate(:preview_card, trendable: false) }
it_behaves_like 'forbidden for wrong scope', 'read write'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
it 'does not set the link as trendable' do
expect { subject }.to_not(change { preview_card.reload.trendable })
end
it 'returns the link data' do
subject
expect(body_as_json).to match(
a_hash_including(
url: preview_card.url,
title: preview_card.title,
description: preview_card.description,
type: 'link',
requires_review: false
)
)
end
context 'when the link does not exist' do
it 'returns http not found' do
post '/api/v1/admin/trends/links/-1/reject', headers: headers
expect(response).to have_http_status(404)
end
end
context 'without an authorization header' do
let(:headers) { {} }
it 'returns http forbidden' do
subject
expect(response).to have_http_status(403)
end
end
end
end

@ -12,14 +12,10 @@ describe 'Credentials' do
let(:token) { Fabricate(:accessible_access_token, scopes: 'read', application: Fabricate(:application)) } let(:token) { Fabricate(:accessible_access_token, scopes: 'read', application: Fabricate(:application)) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
it 'returns http success' do it 'returns the app information correctly', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the app information correctly' do
subject
expect(body_as_json).to match( expect(body_as_json).to match(
a_hash_including( a_hash_including(

@ -23,20 +23,11 @@ RSpec.describe 'Apps' do
end end
context 'with valid params' do context 'with valid params' do
it 'returns http success' do it 'creates an OAuth app', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'creates an OAuth app' do
subject
expect(Doorkeeper::Application.find_by(name: client_name)).to be_present expect(Doorkeeper::Application.find_by(name: client_name)).to be_present
end
it 'returns client ID and client secret' do
subject
body = body_as_json body = body_as_json
@ -58,15 +49,10 @@ RSpec.describe 'Apps' do
context 'with many duplicate scopes' do context 'with many duplicate scopes' do
let(:scopes) { (%w(read) * 40).join(' ') } let(:scopes) { (%w(read) * 40).join(' ') }
it 'returns http success' do it 'only saves the scope once', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'only saves the scope once' do
subject
expect(Doorkeeper::Application.find_by(name: client_name).scopes.to_s).to eq 'read' expect(Doorkeeper::Application.find_by(name: client_name).scopes.to_s).to eq 'read'
end end
end end

@ -0,0 +1,80 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Blocks' do
let(:user) { Fabricate(:user) }
let(:scopes) { 'read:blocks' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
describe 'GET /api/v1/blocks' do
subject do
get '/api/v1/blocks', headers: headers, params: params
end
let!(:blocks) { Fabricate.times(3, :block, account: user.account) }
let(:params) { {} }
let(:expected_response) do
blocks.map { |block| a_hash_including(id: block.target_account.id.to_s, username: block.target_account.username) }
end
it_behaves_like 'forbidden for wrong scope', 'write write:blocks'
it 'returns the blocked accounts', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(body_as_json).to match_array(expected_response)
end
context 'with limit param' do
let(:params) { { limit: 2 } }
it 'returns only the requested number of blocked accounts' do
subject
expect(body_as_json.size).to eq(params[:limit])
end
it 'sets the correct pagination header for the prev path' do
subject
expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_blocks_url(limit: params[:limit], since_id: blocks.last.id))
end
it 'sets the correct pagination header for the next path' do
subject
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_blocks_url(limit: params[:limit], max_id: blocks[1].id))
end
end
context 'with max_id param' do
let(:params) { { max_id: blocks[1].id } }
it 'queries the blocks in range according to max_id', :aggregate_failures do
subject
response_body = body_as_json
expect(response_body.size).to be 1
expect(response_body[0][:id]).to eq(blocks[0].target_account.id.to_s)
end
end
context 'with since_id param' do
let(:params) { { since_id: blocks[1].id } }
it 'queries the blocks in range according to since_id', :aggregate_failures do
subject
response_body = body_as_json
expect(response_body.size).to be 1
expect(response_body[0][:id]).to eq(blocks[2].target_account.id.to_s)
end
end
end
end

@ -22,15 +22,10 @@ RSpec.describe 'Domain blocks' do
it_behaves_like 'forbidden for wrong scope', 'write:blocks' it_behaves_like 'forbidden for wrong scope', 'write:blocks'
it 'returns http success' do it 'returns the domains blocked by the requesting user', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the domains blocked by the requesting user' do
subject
expect(body_as_json).to match_array(blocked_domains) expect(body_as_json).to match_array(blocked_domains)
end end
@ -54,15 +49,10 @@ RSpec.describe 'Domain blocks' do
it_behaves_like 'forbidden for wrong scope', 'read read:blocks' it_behaves_like 'forbidden for wrong scope', 'read read:blocks'
it 'returns http success' do it 'creates a domain block', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'creates a domain block' do
subject
expect(user.account.domain_blocking?(params[:domain])).to be(true) expect(user.account.domain_blocking?(params[:domain])).to be(true)
end end
@ -100,15 +90,10 @@ RSpec.describe 'Domain blocks' do
it_behaves_like 'forbidden for wrong scope', 'read read:blocks' it_behaves_like 'forbidden for wrong scope', 'read read:blocks'
it 'returns http success' do it 'deletes the specified domain block', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'deletes the specified domain block' do
subject
expect(user.account.domain_blocking?('example.com')).to be(false) expect(user.account.domain_blocking?('example.com')).to be(false)
end end

@ -0,0 +1,71 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Favourites' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'read:favourites' }
let(:headers) { { Authorization: "Bearer #{token.token}" } }
describe 'GET /api/v1/favourites' do
subject do
get '/api/v1/favourites', headers: headers, params: params
end
let(:params) { {} }
let!(:favourites) { Fabricate.times(3, :favourite, account: user.account) }
let(:expected_response) do
favourites.map do |favourite|
a_hash_including(id: favourite.status.id.to_s, account: a_hash_including(id: favourite.status.account.id.to_s))
end
end
it_behaves_like 'forbidden for wrong scope', 'write'
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
it 'returns the favourites' do
subject
expect(body_as_json).to match_array(expected_response)
end
context 'with limit param' do
let(:params) { { limit: 2 } }
it 'returns only the requested number of favourites' do
subject
expect(body_as_json.size).to eq(params[:limit])
end
it 'sets the correct pagination header for the prev path' do
subject
expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_favourites_url(limit: params[:limit], min_id: favourites.last.id))
end
it 'sets the correct pagination header for the next path' do
subject
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_favourites_url(limit: params[:limit], max_id: favourites[1].id))
end
end
context 'without an authorization header' do
let(:headers) { {} }
it 'returns http unauthorized' do
subject
expect(response).to have_http_status(401)
end
end
end
end

@ -32,15 +32,10 @@ RSpec.describe 'Follow requests' do
it_behaves_like 'forbidden for wrong scope', 'write write:follows' it_behaves_like 'forbidden for wrong scope', 'write write:follows'
it 'returns http success' do it 'returns the expected content from accounts requesting to follow', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the expected content from accounts requesting to follow' do
subject
expect(body_as_json).to match_array(expected_response) expect(body_as_json).to match_array(expected_response)
end end
@ -68,19 +63,9 @@ RSpec.describe 'Follow requests' do
it_behaves_like 'forbidden for wrong scope', 'read read:follows' it_behaves_like 'forbidden for wrong scope', 'read read:follows'
it 'returns http success' do it 'allows the requesting follower to follow', :aggregate_failures do
subject
expect(response).to have_http_status(200)
end
it 'allows the requesting follower to follow' do
expect { subject }.to change { follower.following?(user.account) }.from(false).to(true) expect { subject }.to change { follower.following?(user.account) }.from(false).to(true)
end expect(response).to have_http_status(200)
it 'returns JSON with followed_by set to true' do
subject
expect(body_as_json[:followed_by]).to be true expect(body_as_json[:followed_by]).to be true
end end
end end
@ -98,21 +83,11 @@ RSpec.describe 'Follow requests' do
it_behaves_like 'forbidden for wrong scope', 'read read:follows' it_behaves_like 'forbidden for wrong scope', 'read read:follows'
it 'returns http success' do it 'removes the follow request', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the follow request' do
subject
expect(FollowRequest.where(target_account: user.account, account: follower)).to_not exist expect(FollowRequest.where(target_account: user.account, account: follower)).to_not exist
end
it 'returns JSON with followed_by set to false' do
subject
expect(body_as_json[:followed_by]).to be false expect(body_as_json[:followed_by]).to be false
end end
end end

@ -0,0 +1,65 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Followed tags' do
let(:user) { Fabricate(:user) }
let(:scopes) { 'read:follows' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
describe 'GET /api/v1/followed_tags' do
subject do
get '/api/v1/followed_tags', headers: headers, params: params
end
let!(:tag_follows) { Fabricate.times(5, :tag_follow, account: user.account) }
let(:params) { {} }
let(:expected_response) do
tag_follows.map do |tag_follow|
a_hash_including(name: tag_follow.tag.name, following: true)
end
end
before do
Fabricate(:tag_follow)
end
it_behaves_like 'forbidden for wrong scope', 'write write:follows'
it 'returns http success' do
subject
expect(response).to have_http_status(:success)
end
it 'returns the followed tags correctly' do
subject
expect(body_as_json).to match_array(expected_response)
end
context 'with limit param' do
let(:params) { { limit: 3 } }
it 'returns only the requested number of follow tags' do
subject
expect(body_as_json.size).to eq(params[:limit])
end
it 'sets the correct pagination header for the prev path' do
subject
expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_followed_tags_url(limit: params[:limit], since_id: tag_follows.last.id))
end
it 'sets the correct pagination header for the next path' do
subject
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_followed_tags_url(limit: params[:limit], max_id: tag_follows[2].id))
end
end
end
end

@ -0,0 +1,178 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Accounts' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'read:lists write:lists' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
describe 'GET /api/v1/lists/:id/accounts' do
subject do
get "/api/v1/lists/#{list.id}/accounts", headers: headers, params: params
end
let(:params) { { limit: 0 } }
let(:list) { Fabricate(:list, account: user.account) }
let(:accounts) { Fabricate.times(3, :account) }
let(:expected_response) do
accounts.map do |account|
a_hash_including(id: account.id.to_s, username: account.username, acct: account.acct)
end
end
before do
accounts.each { |account| user.account.follow!(account) }
list.accounts << accounts
end
it_behaves_like 'forbidden for wrong scope', 'write write:lists'
it 'returns the accounts in the requested list', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(body_as_json).to match_array(expected_response)
end
context 'with limit param' do
let(:params) { { limit: 1 } }
it 'returns only the requested number of accounts' do
subject
expect(body_as_json.size).to eq(params[:limit])
end
end
end
describe 'POST /api/v1/lists/:id/accounts' do
subject do
post "/api/v1/lists/#{list.id}/accounts", headers: headers, params: params
end
let(:list) { Fabricate(:list, account: user.account) }
let(:bob) { Fabricate(:account, username: 'bob') }
let(:params) { { account_ids: [bob.id] } }
it_behaves_like 'forbidden for wrong scope', 'read read:lists'
context 'when the added account is followed' do
before do
user.account.follow!(bob)
end
it 'adds account to the list', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(list.accounts).to include(bob)
end
end
context 'when the added account has been sent a follow request' do
before do
user.account.follow_requests.create!(target_account: bob)
end
it 'adds account to the list', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(list.accounts).to include(bob)
end
end
context 'when the added account is not followed' do
it 'does not add the account to the list', :aggregate_failures do
subject
expect(response).to have_http_status(404)
expect(list.accounts).to_not include(bob)
end
end
context 'when the list is not owned by the requesting user' do
let(:list) { Fabricate(:list) }
before do
user.account.follow!(bob)
end
it 'returns http not found' do
subject
expect(response).to have_http_status(404)
end
end
context 'when account is already in the list' do
before do
user.account.follow!(bob)
list.accounts << bob
end
it 'returns http unprocessable entity' do
subject
expect(response).to have_http_status(422)
end
end
end
describe 'DELETE /api/v1/lists/:id/accounts' do
subject do
delete "/api/v1/lists/#{list.id}/accounts", headers: headers, params: params
end
context 'when the list is owned by the requesting user' do
let(:list) { Fabricate(:list, account: user.account) }
let(:bob) { Fabricate(:account, username: 'bob') }
let(:peter) { Fabricate(:account, username: 'peter') }
let(:params) { { account_ids: [bob.id] } }
before do
user.account.follow!(bob)
user.account.follow!(peter)
list.accounts << [bob, peter]
end
it 'removes the specified account from the list', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(list.accounts).to_not include(bob)
end
it 'does not remove any other account from the list' do
subject
expect(list.accounts).to include(peter)
end
context 'when the specified account is not in the list' do
let(:params) { { account_ids: [0] } }
it 'does not remove any account from the list', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(list.accounts).to contain_exactly(bob, peter)
end
end
end
context 'when the list is not owned by the requesting user' do
let(:list) { Fabricate(:list) }
let(:params) { {} }
it 'returns http not found' do
subject
expect(response).to have_http_status(404)
end
end
end
end

@ -39,15 +39,10 @@ RSpec.describe 'Lists' do
it_behaves_like 'forbidden for wrong scope', 'write write:lists' it_behaves_like 'forbidden for wrong scope', 'write write:lists'
it 'returns http success' do it 'returns the expected lists', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the expected lists' do
subject
expect(body_as_json).to match_array(expected_response) expect(body_as_json).to match_array(expected_response)
end end
end end
@ -61,15 +56,10 @@ RSpec.describe 'Lists' do
it_behaves_like 'forbidden for wrong scope', 'write write:lists' it_behaves_like 'forbidden for wrong scope', 'write write:lists'
it 'returns http success' do it 'returns the requested list correctly', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the requested list correctly' do
subject
expect(body_as_json).to eq({ expect(body_as_json).to eq({
id: list.id.to_s, id: list.id.to_s,
title: list.title, title: list.title,
@ -106,21 +96,11 @@ RSpec.describe 'Lists' do
it_behaves_like 'forbidden for wrong scope', 'read read:lists' it_behaves_like 'forbidden for wrong scope', 'read read:lists'
it 'returns http success' do it 'returns the new list', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the new list' do
subject
expect(body_as_json).to match(a_hash_including(title: 'my list', replies_policy: 'none', exclusive: true)) expect(body_as_json).to match(a_hash_including(title: 'my list', replies_policy: 'none', exclusive: true))
end
it 'creates a list' do
subject
expect(List.where(account: user.account).count).to eq(1) expect(List.where(account: user.account).count).to eq(1)
end end
@ -155,15 +135,10 @@ RSpec.describe 'Lists' do
it_behaves_like 'forbidden for wrong scope', 'read read:lists' it_behaves_like 'forbidden for wrong scope', 'read read:lists'
it 'returns http success' do it 'returns the updated list', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the updated list' do
subject
list.reload list.reload
expect(body_as_json).to eq({ expect(body_as_json).to eq({
@ -214,15 +189,10 @@ RSpec.describe 'Lists' do
it_behaves_like 'forbidden for wrong scope', 'read read:lists' it_behaves_like 'forbidden for wrong scope', 'read read:lists'
it 'returns http success' do it 'deletes the list', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'deletes the list' do
subject
expect(List.where(id: list.id)).to_not exist expect(List.where(id: list.id)).to_not exist
end end

@ -0,0 +1,89 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Reports' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'write:reports' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
describe 'POST /api/v1/reports' do
subject do
post '/api/v1/reports', headers: headers, params: params
end
let!(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
let(:status) { Fabricate(:status) }
let(:target_account) { status.account }
let(:category) { 'other' }
let(:forward) { nil }
let(:rule_ids) { nil }
let(:params) do
{
status_ids: [status.id],
account_id: target_account.id,
comment: 'reasons',
category: category,
rule_ids: rule_ids,
forward: forward,
}
end
it_behaves_like 'forbidden for wrong scope', 'read read:reports'
it 'creates a report', :aggregate_failures do
perform_enqueued_jobs do
subject
expect(response).to have_http_status(200)
expect(body_as_json).to match(
a_hash_including(
status_ids: [status.id.to_s],
category: category,
comment: 'reasons'
)
)
expect(target_account.targeted_reports).to_not be_empty
expect(target_account.targeted_reports.first.comment).to eq 'reasons'
expect(ActionMailer::Base.deliveries.first.to).to eq([admin.email])
end
end
context 'when a status does not belong to the reported account' do
let(:target_account) { Fabricate(:account) }
it 'returns http not found' do
subject
expect(response).to have_http_status(404)
end
end
context 'when a category is chosen' do
let(:category) { 'spam' }
it 'saves category' do
subject
expect(target_account.targeted_reports.first.spam?).to be true
end
end
context 'when violated rules are chosen' do
let(:rule) { Fabricate(:rule) }
let(:category) { 'violation' }
let(:rule_ids) { [rule.id] }
it 'saves category and rule_ids' do
subject
expect(target_account.targeted_reports.first.violation?).to be true
expect(target_account.targeted_reports.first.rule_ids).to contain_exactly(rule.id)
end
end
end
end

@ -0,0 +1,74 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Sources' do
let(:user) { Fabricate(:user) }
let(:scopes) { 'read:statuses' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
describe 'GET /api/v1/statuses/:status_id/source' do
subject do
get "/api/v1/statuses/#{status.id}/source", headers: headers
end
let(:status) { Fabricate(:status) }
it_behaves_like 'forbidden for wrong scope', 'write write:statuses'
context 'with public status' do
it 'returns the source properties of the status', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(body_as_json).to eq({
id: status.id.to_s,
text: status.text,
spoiler_text: status.spoiler_text,
content_type: nil,
})
end
end
context 'with private status of non-followed account' do
let(:status) { Fabricate(:status, visibility: :private) }
it 'returns http not found' do
subject
expect(response).to have_http_status(404)
end
end
context 'with private status of followed account' do
let(:status) { Fabricate(:status, visibility: :private) }
before do
user.account.follow!(status.account)
end
it 'returns the source properties of the status', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(body_as_json).to eq({
id: status.id.to_s,
text: status.text,
spoiler_text: status.spoiler_text,
content_type: nil,
})
end
end
context 'without an authorization header' do
let(:headers) { {} }
it 'returns http unauthorized' do
subject
expect(response).to have_http_status(401)
end
end
end
end

@ -17,15 +17,10 @@ RSpec.describe 'Tags' do
let!(:tag) { Fabricate(:tag) } let!(:tag) { Fabricate(:tag) }
let(:name) { tag.name } let(:name) { tag.name }
it 'returns http success' do it 'returns the tag', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'returns the tag' do
subject
expect(body_as_json[:name]).to eq(name) expect(body_as_json[:name]).to eq(name)
end end
end end
@ -62,15 +57,10 @@ RSpec.describe 'Tags' do
it_behaves_like 'forbidden for wrong scope', 'read read:follows' it_behaves_like 'forbidden for wrong scope', 'read read:follows'
context 'when the tag exists' do context 'when the tag exists' do
it 'returns http success' do it 'creates follow', :aggregate_failures do
subject subject
expect(response).to have_http_status(:success) expect(response).to have_http_status(:success)
end
it 'creates follow' do
subject
expect(TagFollow.where(tag: tag, account: user.account)).to exist expect(TagFollow.where(tag: tag, account: user.account)).to exist
end end
end end
@ -78,21 +68,11 @@ RSpec.describe 'Tags' do
context 'when the tag does not exist' do context 'when the tag does not exist' do
let(:name) { 'hoge' } let(:name) { 'hoge' }
it 'returns http success' do it 'creates a new tag with the specified name', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'creates a new tag with the specified name' do
subject
expect(Tag.where(name: name)).to exist expect(Tag.where(name: name)).to exist
end
it 'creates follow' do
subject
expect(TagFollow.where(tag: Tag.find_by(name: name), account: user.account)).to exist expect(TagFollow.where(tag: Tag.find_by(name: name), account: user.account)).to exist
end end
end end
@ -133,15 +113,10 @@ RSpec.describe 'Tags' do
it_behaves_like 'forbidden for wrong scope', 'read read:follows' it_behaves_like 'forbidden for wrong scope', 'read read:follows'
it 'returns http success' do it 'removes the follow', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
it 'removes the follow' do
subject
expect(TagFollow.where(tag: tag, account: user.account)).to_not exist expect(TagFollow.where(tag: tag, account: user.account)).to_not exist
end end

@ -0,0 +1,116 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Tag' do
let(:user) { Fabricate(:user) }
let(:scopes) { 'read:statuses' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
shared_examples 'a successful request to the tag timeline' do
it 'returns the expected statuses', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(body_as_json.pluck(:id)).to match_array(expected_statuses.map { |status| status.id.to_s })
end
end
describe 'GET /api/v1/timelines/tag/:hashtag' do
subject do
get "/api/v1/timelines/tag/#{hashtag}", headers: headers, params: params
end
before do
Setting.timeline_preview = true
end
let(:account) { Fabricate(:account) }
let!(:private_status) { PostStatusService.new.call(account, visibility: :private, text: '#life could be a dream') } # rubocop:disable RSpec/LetSetup
let!(:life_status) { PostStatusService.new.call(account, text: 'tell me what is my #life without your #love') }
let!(:war_status) { PostStatusService.new.call(user.account, text: '#war, war never changes') }
let!(:love_status) { PostStatusService.new.call(account, text: 'what is #love?') }
let(:params) { {} }
let(:hashtag) { 'life' }
context 'when given only one hashtag' do
let(:expected_statuses) { [life_status] }
it_behaves_like 'a successful request to the tag timeline'
end
context 'with any param' do
let(:expected_statuses) { [life_status, love_status] }
let(:params) { { any: %(love) } }
it_behaves_like 'a successful request to the tag timeline'
end
context 'with all param' do
let(:expected_statuses) { [life_status] }
let(:params) { { all: %w(love) } }
it_behaves_like 'a successful request to the tag timeline'
end
context 'with none param' do
let(:expected_statuses) { [war_status] }
let(:hashtag) { 'war' }
let(:params) { { none: %w(life love) } }
it_behaves_like 'a successful request to the tag timeline'
end
context 'with limit param' do
let(:hashtag) { 'love' }
let(:params) { { limit: 1 } }
it 'returns only the requested number of statuses' do
subject
expect(body_as_json.size).to eq(params[:limit])
end
it 'sets the correct pagination headers', :aggregate_failures do
subject
headers = response.headers['Link']
expect(headers.find_link(%w(rel prev)).href).to eq(api_v1_timelines_tag_url(limit: 1, min_id: love_status.id.to_s))
expect(headers.find_link(%w(rel next)).href).to eq(api_v1_timelines_tag_url(limit: 1, max_id: love_status.id.to_s))
end
end
context 'when the instance allows public preview' do
context 'when the user is not authenticated' do
let(:headers) { {} }
let(:expected_statuses) { [life_status] }
it_behaves_like 'a successful request to the tag timeline'
end
end
context 'when the instance does not allow public preview' do
before do
Form::AdminSettings.new(timeline_preview: false).save
end
context 'when the user is not authenticated' do
let(:headers) { {} }
it 'returns http unauthorized' do
subject
expect(response).to have_http_status(401)
end
end
context 'when the user is authenticated' do
let(:expected_statuses) { [life_status] }
it_behaves_like 'a successful request to the tag timeline'
end
end
end
end

@ -9,7 +9,6 @@ RSpec.describe AfterBlockDomainFromAccountService, type: :service do
let!(:alice) { Fabricate(:account, username: 'alice') } let!(:alice) { Fabricate(:account, username: 'alice') }
before do before do
stub_jsonld_contexts!
allow(ActivityPub::DeliveryWorker).to receive(:perform_async) allow(ActivityPub::DeliveryWorker).to receive(:perform_async)
end end

@ -0,0 +1,21 @@
# frozen_string_literal: true
module SignedRequestHelpers
def get(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'] = ENV.fetch('LOCAL_DOMAIN')
signed_headers = headers.merge('(request-target)' => "get #{path}").slice('(request-target)', 'Host', 'Date')
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

@ -0,0 +1,83 @@
# frozen_string_literal: true
require 'rails_helper'
describe ExistingUsernameValidator do
let(:record_class) do
Class.new do
include ActiveModel::Validations
attr_accessor :contact, :friends
def self.name
'Record'
end
validates :contact, existing_username: true
validates :friends, existing_username: { multiple: true }
end
end
let(:record) { record_class.new }
describe '#validate_each' do
context 'with a nil value' do
it 'does not add errors' do
record.contact = nil
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'when there are no accounts' do
it 'adds errors to the record' do
record.contact = 'user@example.com'
expect(record).to_not be_valid
expect(record.errors.first.attribute).to eq(:contact)
expect(record.errors.first.type).to eq I18n.t('existing_username_validator.not_found')
end
end
context 'when there are accounts' do
before { Fabricate(:account, domain: 'example.com', username: 'user') }
context 'when the value does not match' do
it 'adds errors to the record' do
record.contact = 'friend@other.host'
expect(record).to_not be_valid
expect(record.errors.first.attribute).to eq(:contact)
expect(record.errors.first.type).to eq I18n.t('existing_username_validator.not_found')
end
context 'when multiple is true' do
it 'adds errors to the record' do
record.friends = 'friend@other.host'
expect(record).to_not be_valid
expect(record.errors.first.attribute).to eq(:friends)
expect(record.errors.first.type).to eq I18n.t('existing_username_validator.not_found_multiple', usernames: 'friend@other.host')
end
end
end
context 'when the value does match' do
it 'does not add errors to the record' do
record.contact = 'user@example.com'
expect(record).to be_valid
expect(record.errors).to be_empty
end
context 'when multiple is true' do
it 'does not add errors to the record' do
record.friends = 'user@example.com'
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
end
end
end
end

@ -67,39 +67,31 @@ describe MoveWorker do
end end
shared_examples 'block and mute handling' do shared_examples 'block and mute handling' do
it 'makes blocks carry over and add a note' do it 'makes blocks and mutes carry over and adds a note' do
subject.perform(source_account.id, target_account.id) subject.perform(source_account.id, target_account.id)
expect(block_service).to have_received(:call).with(blocking_account, target_account) expect(block_service).to have_received(:call).with(blocking_account, target_account)
expect(AccountNote.find_by(account: blocking_account, target_account: target_account).comment).to include(source_account.acct) expect(AccountNote.find_by(account: blocking_account, target_account: target_account).comment).to include(source_account.acct)
end
it 'makes mutes carry over and add a note' do
subject.perform(source_account.id, target_account.id)
expect(muting_account.muting?(target_account)).to be true expect(muting_account.muting?(target_account)).to be true
expect(AccountNote.find_by(account: muting_account, target_account: target_account).comment).to include(source_account.acct) expect(AccountNote.find_by(account: muting_account, target_account: target_account).comment).to include(source_account.acct)
end end
end end
shared_examples 'followers count handling' do shared_examples 'followers count handling' do
it 'updates the source account followers count' do it 'updates the source and target account followers counts' do
subject.perform(source_account.id, target_account.id) subject.perform(source_account.id, target_account.id)
expect(source_account.reload.followers_count).to eq(source_account.passive_relationships.count)
end
it 'updates the target account followers count' do expect(source_account.reload.followers_count).to eq(source_account.passive_relationships.count)
subject.perform(source_account.id, target_account.id)
expect(target_account.reload.followers_count).to eq(target_account.passive_relationships.count) expect(target_account.reload.followers_count).to eq(target_account.passive_relationships.count)
end end
end end
shared_examples 'lists handling' do shared_examples 'lists handling' do
it 'puts the new account on the list' do it 'puts the new account on the list and makes valid lists', sidekiq: :inline do
subject.perform(source_account.id, target_account.id) subject.perform(source_account.id, target_account.id)
expect(list.accounts.include?(target_account)).to be true
end
it 'does not create invalid list memberships' do expect(list.accounts.include?(target_account)).to be true
subject.perform(source_account.id, target_account.id)
expect(ListAccount.all).to all be_valid expect(ListAccount.all).to all be_valid
end end
end end

@ -3825,9 +3825,9 @@ buffer@^6.0.3:
ieee754 "^1.2.1" ieee754 "^1.2.1"
bufferutil@^4.0.7: bufferutil@^4.0.7:
version "4.0.7" version "4.0.8"
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea"
integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw== integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==
dependencies: dependencies:
node-gyp-build "^4.3.0" node-gyp-build "^4.3.0"
@ -8781,9 +8781,9 @@ node-forge@^0.10.0:
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
node-gyp-build@^4.3.0: node-gyp-build@^4.3.0:
version "4.6.0" version "4.6.1"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.1.tgz#24b6d075e5e391b8d5539d98c7fc5c210cac8a3e"
integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== integrity sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==
node-int64@^0.4.0: node-int64@^0.4.0:
version "0.4.0" version "0.4.0"
@ -11488,6 +11488,7 @@ stringz@^2.1.0:
char-regex "^1.0.2" char-regex "^1.0.2"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
name strip-ansi-cjs
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==

Loading…
Cancel
Save