Merge commit 'c15c8c7aa8
' into kb_migration
This commit is contained in:
commit
093cfdbb7b
208 changed files with 2976 additions and 5888 deletions
|
@ -40,42 +40,135 @@ RSpec.describe Admin::DomainBlocksController do
|
|||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
it 'blocks the domain when succeeded to save' do
|
||||
before do
|
||||
allow(DomainBlockWorker).to receive(:perform_async).and_return(true)
|
||||
|
||||
post :create, params: { domain_block: { domain: 'example.com', severity: 'silence' } }
|
||||
|
||||
expect(DomainBlockWorker).to have_received(:perform_async)
|
||||
expect(flash[:notice]).to eq I18n.t('admin.domain_blocks.created_msg')
|
||||
expect(response).to redirect_to(admin_instances_path(limited: '1'))
|
||||
end
|
||||
|
||||
it 'renders new when failed to save' do
|
||||
Fabricate(:domain_block, domain: 'example.com', severity: 'suspend')
|
||||
allow(DomainBlockWorker).to receive(:perform_async).and_return(true)
|
||||
context 'with "silence" severity and no conflict' do
|
||||
before do
|
||||
post :create, params: { domain_block: { domain: 'example.com', severity: 'silence' } }
|
||||
end
|
||||
|
||||
post :create, params: { domain_block: { domain: 'example.com', severity: 'silence' } }
|
||||
it 'records a block' do
|
||||
expect(DomainBlock.exists?(domain: 'example.com', severity: 'silence')).to be true
|
||||
end
|
||||
|
||||
expect(DomainBlockWorker).to_not have_received(:perform_async)
|
||||
expect(response).to render_template :new
|
||||
it 'calls DomainBlockWorker' do
|
||||
expect(DomainBlockWorker).to have_received(:perform_async)
|
||||
end
|
||||
|
||||
it 'redirects with a success message' do
|
||||
expect(flash[:notice]).to eq I18n.t('admin.domain_blocks.created_msg')
|
||||
expect(response).to redirect_to(admin_instances_path(limited: '1'))
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows upgrading a block' do
|
||||
Fabricate(:domain_block, domain: 'example.com', severity: 'silence')
|
||||
allow(DomainBlockWorker).to receive(:perform_async).and_return(true)
|
||||
context 'when the new domain block conflicts with an existing one' do
|
||||
before do
|
||||
Fabricate(:domain_block, domain: 'example.com', severity: 'suspend')
|
||||
post :create, params: { domain_block: { domain: 'example.com', severity: 'silence' } }
|
||||
end
|
||||
|
||||
post :create, params: { domain_block: { domain: 'example.com', severity: 'silence', reject_media: true, reject_reports: true } }
|
||||
it 'does not record a block' do
|
||||
expect(DomainBlock.exists?(domain: 'example.com', severity: 'silence')).to be false
|
||||
end
|
||||
|
||||
expect(DomainBlockWorker).to have_received(:perform_async)
|
||||
expect(flash[:notice]).to eq I18n.t('admin.domain_blocks.created_msg')
|
||||
expect(response).to redirect_to(admin_instances_path(limited: '1'))
|
||||
it 'does not call DomainBlockWorker' do
|
||||
expect(DomainBlockWorker).to_not have_received(:perform_async)
|
||||
end
|
||||
|
||||
it 'renders new' do
|
||||
expect(response).to render_template :new
|
||||
end
|
||||
end
|
||||
|
||||
context 'with "suspend" severity and no conflict' do
|
||||
context 'without a confirmation' do
|
||||
before do
|
||||
post :create, params: { domain_block: { domain: 'example.com', severity: 'suspend', reject_media: true, reject_reports: true } }
|
||||
end
|
||||
|
||||
it 'does not record a block' do
|
||||
expect(DomainBlock.exists?(domain: 'example.com', severity: 'suspend')).to be false
|
||||
end
|
||||
|
||||
it 'does not call DomainBlockWorker' do
|
||||
expect(DomainBlockWorker).to_not have_received(:perform_async)
|
||||
end
|
||||
|
||||
it 'renders confirm_suspension' do
|
||||
expect(response).to render_template :confirm_suspension
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a confirmation' do
|
||||
before do
|
||||
post :create, params: { :domain_block => { domain: 'example.com', severity: 'suspend', reject_media: true, reject_reports: true }, 'confirm' => '' }
|
||||
end
|
||||
|
||||
it 'records a block' do
|
||||
expect(DomainBlock.exists?(domain: 'example.com', severity: 'suspend')).to be true
|
||||
end
|
||||
|
||||
it 'calls DomainBlockWorker' do
|
||||
expect(DomainBlockWorker).to have_received(:perform_async)
|
||||
end
|
||||
|
||||
it 'redirects with a success message' do
|
||||
expect(flash[:notice]).to eq I18n.t('admin.domain_blocks.created_msg')
|
||||
expect(response).to redirect_to(admin_instances_path(limited: '1'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when upgrading an existing block' do
|
||||
before do
|
||||
Fabricate(:domain_block, domain: 'example.com', severity: 'silence')
|
||||
end
|
||||
|
||||
context 'without a confirmation' do
|
||||
before do
|
||||
post :create, params: { domain_block: { domain: 'example.com', severity: 'suspend', reject_media: true, reject_reports: true } }
|
||||
end
|
||||
|
||||
it 'does not record a block' do
|
||||
expect(DomainBlock.exists?(domain: 'example.com', severity: 'suspend')).to be false
|
||||
end
|
||||
|
||||
it 'does not call DomainBlockWorker' do
|
||||
expect(DomainBlockWorker).to_not have_received(:perform_async)
|
||||
end
|
||||
|
||||
it 'renders confirm_suspension' do
|
||||
expect(response).to render_template :confirm_suspension
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a confirmation' do
|
||||
before do
|
||||
post :create, params: { :domain_block => { domain: 'example.com', severity: 'suspend', reject_media: true, reject_reports: true }, 'confirm' => '' }
|
||||
end
|
||||
|
||||
it 'updates the record' do
|
||||
expect(DomainBlock.exists?(domain: 'example.com', severity: 'suspend')).to be true
|
||||
end
|
||||
|
||||
it 'calls DomainBlockWorker' do
|
||||
expect(DomainBlockWorker).to have_received(:perform_async)
|
||||
end
|
||||
|
||||
it 'redirects with a success message' do
|
||||
expect(flash[:notice]).to eq I18n.t('admin.domain_blocks.created_msg')
|
||||
expect(response).to redirect_to(admin_instances_path(limited: '1'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
let!(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||
let(:subject) do
|
||||
post :update, params: { id: domain_block.id, domain_block: { domain: 'example.com', severity: new_severity } }
|
||||
post :update, params: { :id => domain_block.id, :domain_block => { domain: 'example.com', severity: new_severity }, 'confirm' => '' }
|
||||
end
|
||||
let(:domain_block) { Fabricate(:domain_block, domain: 'example.com', severity: original_severity) }
|
||||
|
||||
|
|
|
@ -18,4 +18,37 @@ describe Admin::IpBlocksController do
|
|||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
it 'returns http success and renders view' do
|
||||
get :new
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response).to render_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'with valid data' do
|
||||
it 'creates a new ip block and redirects' do
|
||||
expect do
|
||||
post :create, params: { ip_block: { ip: '1.1.1.1', severity: 'no_access', expires_in: 1.day.to_i.to_s } }
|
||||
end.to change(IpBlock, :count).by(1)
|
||||
|
||||
expect(response).to redirect_to(admin_ip_blocks_path)
|
||||
expect(flash.notice).to match(I18n.t('admin.ip_blocks.created_msg'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid data' do
|
||||
it 'does not create new a ip block and renders new' do
|
||||
expect do
|
||||
post :create, params: { ip_block: { ip: '1.1.1.1' } }
|
||||
end.to_not change(IpBlock, :count)
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response).to render_template(:new)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,4 +18,42 @@ describe Admin::RelaysController do
|
|||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
it 'returns http success and renders view' do
|
||||
get :new
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response).to render_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'with valid data' do
|
||||
let(:inbox_url) { 'https://example.com/inbox' }
|
||||
|
||||
before do
|
||||
stub_request(:post, inbox_url).to_return(status: 200)
|
||||
end
|
||||
|
||||
it 'creates a new relay and redirects' do
|
||||
expect do
|
||||
post :create, params: { relay: { inbox_url: inbox_url } }
|
||||
end.to change(Relay, :count).by(1)
|
||||
|
||||
expect(response).to redirect_to(admin_relays_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid data' do
|
||||
it 'does not create new a relay and renders new' do
|
||||
expect do
|
||||
post :create, params: { relay: { inbox_url: 'invalid' } }
|
||||
end.to_not change(Relay, :count)
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response).to render_template(:new)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,4 +18,68 @@ describe Admin::RulesController do
|
|||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #edit' do
|
||||
let(:rule) { Fabricate(:rule) }
|
||||
|
||||
it 'returns http success and renders edit' do
|
||||
get :edit, params: { id: rule.id }
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response).to render_template(:edit)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'with valid data' do
|
||||
it 'creates a new rule and redirects' do
|
||||
expect do
|
||||
post :create, params: { rule: { text: 'The rule text.' } }
|
||||
end.to change(Rule, :count).by(1)
|
||||
|
||||
expect(response).to redirect_to(admin_rules_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid data' do
|
||||
it 'does creates a new rule and renders index' do
|
||||
expect do
|
||||
post :create, params: { rule: { text: '' } }
|
||||
end.to_not change(Rule, :count)
|
||||
|
||||
expect(response).to render_template(:index)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
let(:rule) { Fabricate(:rule, text: 'Original text') }
|
||||
|
||||
context 'with valid data' do
|
||||
it 'updates the rule and redirects' do
|
||||
put :update, params: { id: rule.id, rule: { text: 'Updated text.' } }
|
||||
|
||||
expect(response).to redirect_to(admin_rules_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid data' do
|
||||
it 'does not update the rule and renders index' do
|
||||
put :update, params: { id: rule.id, rule: { text: '' } }
|
||||
|
||||
expect(response).to render_template(:edit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
let!(:rule) { Fabricate(:rule) }
|
||||
|
||||
it 'destroys the rule and redirects' do
|
||||
delete :destroy, params: { id: rule.id }
|
||||
|
||||
expect(rule.reload).to be_discarded
|
||||
expect(response).to redirect_to(admin_rules_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,4 +18,82 @@ describe Admin::WebhooksController do
|
|||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
it 'returns http success and renders view' do
|
||||
get :new
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response).to render_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
it 'creates a new webhook record with valid data' do
|
||||
expect do
|
||||
post :create, params: { webhook: { url: 'https://example.com/hook', events: ['account.approved'] } }
|
||||
end.to change(Webhook, :count).by(1)
|
||||
|
||||
expect(response).to be_redirect
|
||||
end
|
||||
|
||||
it 'does not create a new webhook record with invalid data' do
|
||||
expect do
|
||||
post :create, params: { webhook: { url: 'https://example.com/hook', events: [] } }
|
||||
end.to_not change(Webhook, :count)
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response).to render_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an existing record' do
|
||||
let!(:webhook) { Fabricate :webhook }
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns http success and renders view' do
|
||||
get :show, params: { id: webhook.id }
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response).to render_template(:show)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #edit' do
|
||||
it 'returns http success and renders view' do
|
||||
get :edit, params: { id: webhook.id }
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response).to render_template(:edit)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
it 'updates the record with valid data' do
|
||||
put :update, params: { id: webhook.id, webhook: { url: 'https://example.com/new/location' } }
|
||||
|
||||
expect(webhook.reload.url).to match(%r{new/location})
|
||||
expect(response).to redirect_to(admin_webhook_path(webhook))
|
||||
end
|
||||
|
||||
it 'does not update the record with invalid data' do
|
||||
expect do
|
||||
put :update, params: { id: webhook.id, webhook: { url: '' } }
|
||||
end.to_not change(webhook, :url)
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response).to render_template(:show)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
it 'destroys the record' do
|
||||
expect do
|
||||
delete :destroy, params: { id: webhook.id }
|
||||
end.to change(Webhook, :count).by(-1)
|
||||
|
||||
expect(response).to redirect_to(admin_webhooks_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ describe Api::V1::Statuses::TranslationsController do
|
|||
|
||||
before do
|
||||
translation = TranslationService::Translation.new(text: 'Hello')
|
||||
service = instance_double(TranslationService::DeepL, translate: translation)
|
||||
service = instance_double(TranslationService::DeepL, translate: [translation])
|
||||
allow(TranslationService).to receive(:configured?).and_return(true)
|
||||
allow(TranslationService).to receive(:configured).and_return(service)
|
||||
Rails.cache.write('translation_service/languages', { 'es' => ['en'] })
|
||||
|
|
78
spec/features/admin/domain_blocks_spec.rb
Normal file
78
spec/features/admin/domain_blocks_spec.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'blocking domains through the moderation interface' do
|
||||
before do
|
||||
sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
|
||||
end
|
||||
|
||||
context 'when silencing a new domain' do
|
||||
it 'adds a new domain block' do
|
||||
visit new_admin_domain_block_path
|
||||
|
||||
fill_in 'domain_block_domain', with: 'example.com'
|
||||
select I18n.t('admin.domain_blocks.new.severity.silence'), from: 'domain_block_severity'
|
||||
click_on I18n.t('admin.domain_blocks.new.create')
|
||||
|
||||
expect(DomainBlock.exists?(domain: 'example.com', severity: 'silence')).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when suspending a new domain' do
|
||||
it 'presents a confirmation screen before suspending the domain' do
|
||||
visit new_admin_domain_block_path
|
||||
|
||||
fill_in 'domain_block_domain', with: 'example.com'
|
||||
select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity'
|
||||
click_on I18n.t('admin.domain_blocks.new.create')
|
||||
|
||||
# It presents a confirmation screen
|
||||
expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com'))
|
||||
|
||||
# Confirming creates a block
|
||||
click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm')
|
||||
|
||||
expect(DomainBlock.exists?(domain: 'example.com', severity: 'suspend')).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when suspending a domain that is already silenced' do
|
||||
it 'presents a confirmation screen before suspending the domain' do
|
||||
domain_block = Fabricate(:domain_block, domain: 'example.com', severity: 'silence')
|
||||
|
||||
visit new_admin_domain_block_path
|
||||
|
||||
fill_in 'domain_block_domain', with: 'example.com'
|
||||
select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity'
|
||||
click_on I18n.t('admin.domain_blocks.new.create')
|
||||
|
||||
# It presents a confirmation screen
|
||||
expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com'))
|
||||
|
||||
# Confirming updates the block
|
||||
click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm')
|
||||
|
||||
expect(domain_block.reload.severity).to eq 'silence'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when editing a domain block' do
|
||||
it 'presents a confirmation screen before suspending the domain' do
|
||||
domain_block = Fabricate(:domain_block, domain: 'example.com', severity: 'silence')
|
||||
|
||||
visit edit_admin_domain_block_path(domain_block)
|
||||
|
||||
select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity'
|
||||
click_on I18n.t('generic.save_changes')
|
||||
|
||||
# It presents a confirmation screen
|
||||
expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com'))
|
||||
|
||||
# Confirming updates the block
|
||||
click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm')
|
||||
|
||||
expect(domain_block.reload.severity).to eq 'silence'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -208,6 +208,18 @@ describe ApplicationHelper do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when S3 alias includes a path component' do
|
||||
around do |example|
|
||||
ClimateControl.modify S3_ALIAS_HOST: 's3.alias/path' do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns a correct URL' do
|
||||
expect(helper.storage_host).to eq('https://s3.alias/path')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when S3 cloudfront is present' do
|
||||
around do |example|
|
||||
ClimateControl.modify S3_CLOUDFRONT_HOST: 's3.cloudfront' do
|
||||
|
@ -219,12 +231,6 @@ describe ApplicationHelper do
|
|||
expect(helper.storage_host).to eq('https://s3.cloudfront')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when neither env value is present' do
|
||||
it 'returns false' do
|
||||
expect(helper.storage_host).to eq('https:')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'storage_host?' do
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::Metrics::Measure::InstanceAccountsMeasure do
|
||||
subject(:measure) { described_class.new(start_at, end_at, params) }
|
||||
|
||||
let(:domain) { 'example.com' }
|
||||
|
||||
let(:start_at) { 2.days.ago }
|
||||
let(:end_at) { Time.now.utc }
|
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) }
|
||||
|
||||
before do
|
||||
Fabricate(:account, domain: domain, created_at: 1.year.ago)
|
||||
Fabricate(:account, domain: domain, created_at: 1.month.ago)
|
||||
Fabricate(:account, domain: domain)
|
||||
|
||||
Fabricate(:account, domain: "foo.#{domain}", created_at: 1.year.ago)
|
||||
Fabricate(:account, domain: "foo.#{domain}")
|
||||
Fabricate(:account, domain: "bar.#{domain}")
|
||||
end
|
||||
|
||||
describe 'total' do
|
||||
context 'without include_subdomains' do
|
||||
it 'returns the expected number of accounts' do
|
||||
expect(measure.total).to eq 3
|
||||
end
|
||||
end
|
||||
|
||||
context 'with include_subdomains' do
|
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
|
||||
|
||||
it 'returns the expected number of accounts' do
|
||||
expect(measure.total).to eq 6
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::Metrics::Measure::InstanceFollowersMeasure do
|
||||
subject(:measure) { described_class.new(start_at, end_at, params) }
|
||||
|
||||
let(:domain) { 'example.com' }
|
||||
|
||||
let(:start_at) { 2.days.ago }
|
||||
let(:end_at) { Time.now.utc }
|
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) }
|
||||
|
||||
before do
|
||||
local_account = Fabricate(:account)
|
||||
|
||||
Fabricate(:account, domain: domain).follow!(local_account)
|
||||
Fabricate(:account, domain: domain).follow!(local_account)
|
||||
Fabricate(:account, domain: domain)
|
||||
|
||||
Fabricate(:account, domain: "foo.#{domain}").follow!(local_account)
|
||||
Fabricate(:account, domain: "foo.#{domain}").follow!(local_account)
|
||||
Fabricate(:account, domain: "bar.#{domain}")
|
||||
end
|
||||
|
||||
describe 'total' do
|
||||
context 'without include_subdomains' do
|
||||
it 'returns the expected number of accounts' do
|
||||
expect(measure.total).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'with include_subdomains' do
|
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
|
||||
|
||||
it 'returns the expected number of accounts' do
|
||||
expect(measure.total).to eq 4
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::Metrics::Measure::InstanceFollowsMeasure do
|
||||
subject(:measure) { described_class.new(start_at, end_at, params) }
|
||||
|
||||
let(:domain) { 'example.com' }
|
||||
|
||||
let(:start_at) { 2.days.ago }
|
||||
let(:end_at) { Time.now.utc }
|
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) }
|
||||
|
||||
before do
|
||||
local_account = Fabricate(:account)
|
||||
|
||||
local_account.follow!(Fabricate(:account, domain: domain))
|
||||
local_account.follow!(Fabricate(:account, domain: domain))
|
||||
Fabricate(:account, domain: domain)
|
||||
|
||||
local_account.follow!(Fabricate(:account, domain: "foo.#{domain}"))
|
||||
local_account.follow!(Fabricate(:account, domain: "foo.#{domain}"))
|
||||
Fabricate(:account, domain: "bar.#{domain}")
|
||||
end
|
||||
|
||||
describe 'total' do
|
||||
context 'without include_subdomains' do
|
||||
it 'returns the expected number of accounts' do
|
||||
expect(measure.total).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'with include_subdomains' do
|
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
|
||||
|
||||
it 'returns the expected number of accounts' do
|
||||
expect(measure.total).to eq 4
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure do
|
||||
subject(:measure) { described_class.new(start_at, end_at, params) }
|
||||
|
||||
let(:domain) { 'example.com' }
|
||||
|
||||
let(:start_at) { 2.days.ago }
|
||||
let(:end_at) { Time.now.utc }
|
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) }
|
||||
|
||||
let(:remote_account) { Fabricate(:account, domain: domain) }
|
||||
let(:remote_account_on_subdomain) { Fabricate(:account, domain: "foo.#{domain}") }
|
||||
|
||||
before do
|
||||
remote_account.media_attachments.create!(file: attachment_fixture('attachment.jpg'))
|
||||
remote_account_on_subdomain.media_attachments.create!(file: attachment_fixture('attachment.jpg'))
|
||||
end
|
||||
|
||||
describe 'total' do
|
||||
context 'without include_subdomains' do
|
||||
it 'returns the expected number of accounts' do
|
||||
expected_total = remote_account.media_attachments.sum(:file_file_size) + remote_account.media_attachments.sum(:thumbnail_file_size)
|
||||
expect(measure.total).to eq expected_total
|
||||
end
|
||||
end
|
||||
|
||||
context 'with include_subdomains' do
|
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
|
||||
|
||||
it 'returns the expected number of accounts' do
|
||||
expected_total = [remote_account, remote_account_on_subdomain].sum do |account|
|
||||
account.media_attachments.sum(:file_file_size) + account.media_attachments.sum(:thumbnail_file_size)
|
||||
end
|
||||
|
||||
expect(measure.total).to eq expected_total
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::Metrics::Measure::InstanceReportsMeasure do
|
||||
subject(:measure) { described_class.new(start_at, end_at, params) }
|
||||
|
||||
let(:domain) { 'example.com' }
|
||||
|
||||
let(:start_at) { 2.days.ago }
|
||||
let(:end_at) { Time.now.utc }
|
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) }
|
||||
|
||||
before do
|
||||
Fabricate(:report, target_account: Fabricate(:account, domain: domain))
|
||||
Fabricate(:report, target_account: Fabricate(:account, domain: domain))
|
||||
|
||||
Fabricate(:report, target_account: Fabricate(:account, domain: "foo.#{domain}"))
|
||||
Fabricate(:report, target_account: Fabricate(:account, domain: "foo.#{domain}"))
|
||||
Fabricate(:report, target_account: Fabricate(:account, domain: "bar.#{domain}"))
|
||||
end
|
||||
|
||||
describe 'total' do
|
||||
context 'without include_subdomains' do
|
||||
it 'returns the expected number of accounts' do
|
||||
expect(measure.total).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'with include_subdomains' do
|
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
|
||||
|
||||
it 'returns the expected number of accounts' do
|
||||
expect(measure.total).to eq 5
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::Metrics::Measure::InstanceStatusesMeasure do
|
||||
subject(:measure) { described_class.new(start_at, end_at, params) }
|
||||
|
||||
let(:domain) { 'example.com' }
|
||||
|
||||
let(:start_at) { 2.days.ago }
|
||||
let(:end_at) { Time.now.utc }
|
||||
|
||||
let(:params) { ActionController::Parameters.new(domain: domain) }
|
||||
|
||||
before do
|
||||
Fabricate(:status, account: Fabricate(:account, domain: domain))
|
||||
Fabricate(:status, account: Fabricate(:account, domain: domain))
|
||||
|
||||
Fabricate(:status, account: Fabricate(:account, domain: "foo.#{domain}"))
|
||||
Fabricate(:status, account: Fabricate(:account, domain: "foo.#{domain}"))
|
||||
Fabricate(:status, account: Fabricate(:account, domain: "bar.#{domain}"))
|
||||
end
|
||||
|
||||
describe 'total' do
|
||||
context 'without include_subdomains' do
|
||||
it 'returns the expected number of accounts' do
|
||||
expect(measure.total).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'with include_subdomains' do
|
||||
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
|
||||
|
||||
it 'returns the expected number of accounts' do
|
||||
expect(measure.total).to eq 5
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe HashObject do
|
||||
it 'has methods corresponding to hash properties' do
|
||||
expect(HashObject.new(key: 'value').key).to eq 'value'
|
||||
end
|
||||
end
|
|
@ -577,4 +577,351 @@ describe Mastodon::CLI::Accounts do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#unfollow' do
|
||||
context 'when the given username is not found' do
|
||||
let(:arguments) { ['non_existent_username'] }
|
||||
|
||||
it 'exits with an error message indicating that no account with the given username was found' do
|
||||
expect { cli.invoke(:unfollow, arguments) }.to output(
|
||||
a_string_including('No such account')
|
||||
).to_stdout
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the given username is found' do
|
||||
let!(:target_account) { Fabricate(:account) }
|
||||
let!(:follower_chris) { Fabricate(:account, username: 'chris') }
|
||||
let!(:follower_rambo) { Fabricate(:account, username: 'rambo') }
|
||||
let!(:follower_ana) { Fabricate(:account, username: 'ana') }
|
||||
let(:unfollow_service) { instance_double(UnfollowService, call: nil) }
|
||||
let(:scope) { target_account.followers.local }
|
||||
|
||||
before do
|
||||
accounts = [follower_chris, follower_rambo, follower_ana]
|
||||
accounts.each { |account| target_account.follow!(account) }
|
||||
allow(cli).to receive(:parallelize_with_progress).and_yield(follower_chris)
|
||||
.and_yield(follower_rambo)
|
||||
.and_yield(follower_ana)
|
||||
.and_return([3, nil])
|
||||
allow(UnfollowService).to receive(:new).and_return(unfollow_service)
|
||||
end
|
||||
|
||||
it 'makes all local accounts unfollow the target account' do
|
||||
cli.unfollow(target_account.username)
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(unfollow_service).to have_received(:call).with(follower_chris, target_account).once
|
||||
expect(unfollow_service).to have_received(:call).with(follower_rambo, target_account).once
|
||||
expect(unfollow_service).to have_received(:call).with(follower_ana, target_account).once
|
||||
end
|
||||
|
||||
it 'displays a successful message' do
|
||||
expect { cli.unfollow(target_account.username) }.to output(
|
||||
a_string_including('OK, unfollowed target from 3 accounts')
|
||||
).to_stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#backup' do
|
||||
context 'when the given username is not found' do
|
||||
let(:arguments) { ['non_existent_username'] }
|
||||
|
||||
it 'exits with an error message indicating that there is no such account' do
|
||||
expect { cli.invoke(:backup, arguments) }.to output(
|
||||
a_string_including('No user with such username')
|
||||
).to_stdout
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the given username is found' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:user) { account.user }
|
||||
let(:arguments) { [account.username] }
|
||||
|
||||
it 'creates a new backup for the specified user' do
|
||||
expect { cli.invoke(:backup, arguments) }.to change { user.backups.count }.by(1)
|
||||
end
|
||||
|
||||
it 'creates a backup job' do
|
||||
allow(BackupWorker).to receive(:perform_async)
|
||||
|
||||
cli.invoke(:backup, arguments)
|
||||
latest_backup = user.backups.last
|
||||
|
||||
expect(BackupWorker).to have_received(:perform_async).with(latest_backup.id).once
|
||||
end
|
||||
|
||||
it 'displays a successful message' do
|
||||
expect { cli.invoke(:backup, arguments) }.to output(
|
||||
a_string_including('OK')
|
||||
).to_stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#refresh' do
|
||||
context 'with --all option' do
|
||||
let!(:local_account) { Fabricate(:account, domain: nil) }
|
||||
let!(:remote_account_example_com) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:account_example_net) { Fabricate(:account, domain: 'example.net') }
|
||||
let(:scope) { Account.remote }
|
||||
|
||||
before do
|
||||
allow(cli).to receive(:parallelize_with_progress).and_yield(remote_account_example_com)
|
||||
.and_yield(account_example_net)
|
||||
.and_return([2, nil])
|
||||
cli.options = { all: true }
|
||||
end
|
||||
|
||||
it 'refreshes the avatar for all remote accounts' do
|
||||
allow(remote_account_example_com).to receive(:reset_avatar!)
|
||||
allow(account_example_net).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(remote_account_example_com).to have_received(:reset_avatar!).once
|
||||
expect(account_example_net).to have_received(:reset_avatar!).once
|
||||
end
|
||||
|
||||
it 'does not refresh avatar for local accounts' do
|
||||
allow(local_account).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(local_account).to_not have_received(:reset_avatar!)
|
||||
end
|
||||
|
||||
it 'refreshes the header for all remote accounts' do
|
||||
allow(remote_account_example_com).to receive(:reset_header!)
|
||||
allow(account_example_net).to receive(:reset_header!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(remote_account_example_com).to have_received(:reset_header!).once
|
||||
expect(account_example_net).to have_received(:reset_header!).once
|
||||
end
|
||||
|
||||
it 'does not refresh the header for local accounts' do
|
||||
allow(local_account).to receive(:reset_header!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(local_account).to_not have_received(:reset_header!)
|
||||
end
|
||||
|
||||
it 'displays a successful message' do
|
||||
expect { cli.refresh }.to output(
|
||||
a_string_including('Refreshed 2 accounts')
|
||||
).to_stdout
|
||||
end
|
||||
|
||||
context 'with --dry-run option' do
|
||||
before do
|
||||
cli.options = { all: true, dry_run: true }
|
||||
end
|
||||
|
||||
it 'does not refresh the avatar for any account' do
|
||||
allow(local_account).to receive(:reset_avatar!)
|
||||
allow(remote_account_example_com).to receive(:reset_avatar!)
|
||||
allow(account_example_net).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(local_account).to_not have_received(:reset_avatar!)
|
||||
expect(remote_account_example_com).to_not have_received(:reset_avatar!)
|
||||
expect(account_example_net).to_not have_received(:reset_avatar!)
|
||||
end
|
||||
|
||||
it 'does not refresh the header for any account' do
|
||||
allow(local_account).to receive(:reset_header!)
|
||||
allow(remote_account_example_com).to receive(:reset_header!)
|
||||
allow(account_example_net).to receive(:reset_header!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(local_account).to_not have_received(:reset_header!)
|
||||
expect(remote_account_example_com).to_not have_received(:reset_header!)
|
||||
expect(account_example_net).to_not have_received(:reset_header!)
|
||||
end
|
||||
|
||||
it 'displays a successful message with (DRY RUN)' do
|
||||
expect { cli.refresh }.to output(
|
||||
a_string_including('Refreshed 2 accounts (DRY RUN)')
|
||||
).to_stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a list of accts' do
|
||||
let!(:account_example_com_a) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:account_example_com_b) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:account_example_net) { Fabricate(:account, domain: 'example.net') }
|
||||
let(:arguments) { [account_example_com_a.acct, account_example_com_b.acct] }
|
||||
|
||||
before do
|
||||
allow(Account).to receive(:find_remote).with(account_example_com_a.username, account_example_com_a.domain).and_return(account_example_com_a)
|
||||
allow(Account).to receive(:find_remote).with(account_example_com_b.username, account_example_com_b.domain).and_return(account_example_com_b)
|
||||
allow(Account).to receive(:find_remote).with(account_example_net.username, account_example_net.domain).and_return(account_example_net)
|
||||
end
|
||||
|
||||
it 'resets the avatar for the specified accounts' do
|
||||
allow(account_example_com_a).to receive(:reset_avatar!)
|
||||
allow(account_example_com_b).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_com_a).to have_received(:reset_avatar!).once
|
||||
expect(account_example_com_b).to have_received(:reset_avatar!).once
|
||||
end
|
||||
|
||||
it 'does not reset the avatar for unspecified accounts' do
|
||||
allow(account_example_net).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_net).to_not have_received(:reset_avatar!)
|
||||
end
|
||||
|
||||
it 'resets the header for the specified accounts' do
|
||||
allow(account_example_com_a).to receive(:reset_header!)
|
||||
allow(account_example_com_b).to receive(:reset_header!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_com_a).to have_received(:reset_header!).once
|
||||
expect(account_example_com_b).to have_received(:reset_header!).once
|
||||
end
|
||||
|
||||
it 'does not reset the header for unspecified accounts' do
|
||||
allow(account_example_net).to receive(:reset_header!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_net).to_not have_received(:reset_header!)
|
||||
end
|
||||
|
||||
context 'when an UnexpectedResponseError is raised' do
|
||||
it 'displays a failure message' do
|
||||
allow(account_example_com_a).to receive(:reset_avatar!).and_raise(Mastodon::UnexpectedResponseError)
|
||||
|
||||
expect { cli.refresh(*arguments) }
|
||||
.to output(
|
||||
a_string_including("Account failed: #{account_example_com_a.username}@#{account_example_com_a.domain}")
|
||||
).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a specified account is not found' do
|
||||
it 'exits with an error message' do
|
||||
allow(Account).to receive(:find_remote).with(account_example_com_b.username, account_example_com_b.domain).and_return(nil)
|
||||
|
||||
expect { cli.refresh(*arguments) }.to output(
|
||||
a_string_including('No such account')
|
||||
).to_stdout
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with --dry-run option' do
|
||||
before do
|
||||
cli.options = { dry_run: true }
|
||||
end
|
||||
|
||||
it 'does not refresh the avatar for any account' do
|
||||
allow(account_example_com_a).to receive(:reset_avatar!)
|
||||
allow(account_example_com_b).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_com_a).to_not have_received(:reset_avatar!)
|
||||
expect(account_example_com_b).to_not have_received(:reset_avatar!)
|
||||
end
|
||||
|
||||
it 'does not refresh the header for any account' do
|
||||
allow(account_example_com_a).to receive(:reset_header!)
|
||||
allow(account_example_com_b).to receive(:reset_header!)
|
||||
|
||||
cli.refresh(*arguments)
|
||||
|
||||
expect(account_example_com_a).to_not have_received(:reset_header!)
|
||||
expect(account_example_com_b).to_not have_received(:reset_header!)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with --domain option' do
|
||||
let!(:account_example_com_a) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:account_example_com_b) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:account_example_net) { Fabricate(:account, domain: 'example.net') }
|
||||
let(:domain) { 'example.com' }
|
||||
let(:scope) { Account.remote.where(domain: domain) }
|
||||
|
||||
before do
|
||||
allow(cli).to receive(:parallelize_with_progress).and_yield(account_example_com_a)
|
||||
.and_yield(account_example_com_b)
|
||||
.and_return([2, nil])
|
||||
|
||||
cli.options = { domain: domain }
|
||||
end
|
||||
|
||||
it 'refreshes the avatar for all accounts on specified domain' do
|
||||
allow(account_example_com_a).to receive(:reset_avatar!)
|
||||
allow(account_example_com_b).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(account_example_com_a).to have_received(:reset_avatar!).once
|
||||
expect(account_example_com_b).to have_received(:reset_avatar!).once
|
||||
end
|
||||
|
||||
it 'does not refresh the avatar for accounts outside specified domain' do
|
||||
allow(account_example_net).to receive(:reset_avatar!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(account_example_net).to_not have_received(:reset_avatar!)
|
||||
end
|
||||
|
||||
it 'refreshes the header for all accounts on specified domain' do
|
||||
allow(account_example_com_a).to receive(:reset_header!)
|
||||
allow(account_example_com_b).to receive(:reset_header!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope)
|
||||
expect(account_example_com_a).to have_received(:reset_header!).once
|
||||
expect(account_example_com_b).to have_received(:reset_header!).once
|
||||
end
|
||||
|
||||
it 'does not refresh the header for accounts outside specified domain' do
|
||||
allow(account_example_net).to receive(:reset_header!)
|
||||
|
||||
cli.refresh
|
||||
|
||||
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
|
||||
expect(account_example_net).to_not have_received(:reset_header!)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when neither a list of accts nor options are provided' do
|
||||
it 'exits with an error message' do
|
||||
expect { cli.refresh }.to output(
|
||||
a_string_including('No account(s) given')
|
||||
).to_stdout
|
||||
.and raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,10 @@ RSpec.describe TranslationService::DeepL do
|
|||
.with(body: 'text=Hasta+la+vista&source_lang=ES&target_lang=en&tag_handling=html')
|
||||
.to_return(body: '{"translations":[{"detected_source_language":"ES","text":"See you soon"}]}')
|
||||
|
||||
translation = service.translate('Hasta la vista', 'es', 'en')
|
||||
translations = service.translate(['Hasta la vista'], 'es', 'en')
|
||||
expect(translations.size).to eq 1
|
||||
|
||||
translation = translations.first
|
||||
expect(translation.detected_source_language).to eq 'es'
|
||||
expect(translation.provider).to eq 'DeepL.com'
|
||||
expect(translation.text).to eq 'See you soon'
|
||||
|
@ -31,12 +34,27 @@ RSpec.describe TranslationService::DeepL do
|
|||
it 'returns translation with auto-detected source language' do
|
||||
stub_request(:post, 'https://api.deepl.com/v2/translate')
|
||||
.with(body: 'text=Guten+Tag&source_lang&target_lang=en&tag_handling=html')
|
||||
.to_return(body: '{"translations":[{"detected_source_language":"DE","text":"Good Morning"}]}')
|
||||
.to_return(body: '{"translations":[{"detected_source_language":"DE","text":"Good morning"}]}')
|
||||
|
||||
translation = service.translate('Guten Tag', nil, 'en')
|
||||
translations = service.translate(['Guten Tag'], nil, 'en')
|
||||
expect(translations.size).to eq 1
|
||||
|
||||
translation = translations.first
|
||||
expect(translation.detected_source_language).to eq 'de'
|
||||
expect(translation.provider).to eq 'DeepL.com'
|
||||
expect(translation.text).to eq 'Good Morning'
|
||||
expect(translation.text).to eq 'Good morning'
|
||||
end
|
||||
|
||||
it 'returns translation of multiple texts' do
|
||||
stub_request(:post, 'https://api.deepl.com/v2/translate')
|
||||
.with(body: 'text=Guten+Morgen&text=Gute+Nacht&source_lang=DE&target_lang=en&tag_handling=html')
|
||||
.to_return(body: '{"translations":[{"detected_source_language":"DE","text":"Good morning"},{"detected_source_language":"DE","text":"Good night"}]}')
|
||||
|
||||
translations = service.translate(['Guten Morgen', 'Gute Nacht'], 'de', 'en')
|
||||
expect(translations.size).to eq 2
|
||||
|
||||
expect(translations.first.text).to eq 'Good morning'
|
||||
expect(translations.last.text).to eq 'Good night'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -31,24 +31,42 @@ RSpec.describe TranslationService::LibreTranslate do
|
|||
describe '#translate' do
|
||||
it 'returns translation with specified source language' do
|
||||
stub_request(:post, 'https://libretranslate.example.com/translate')
|
||||
.with(body: '{"q":"Hasta la vista","source":"es","target":"en","format":"html","api_key":"my-api-key"}')
|
||||
.to_return(body: '{"translatedText": "See you"}')
|
||||
.with(body: '{"q":["Hasta la vista"],"source":"es","target":"en","format":"html","api_key":"my-api-key"}')
|
||||
.to_return(body: '{"translatedText": ["See you"]}')
|
||||
|
||||
translation = service.translate('Hasta la vista', 'es', 'en')
|
||||
expect(translation.detected_source_language).to eq 'es'
|
||||
translations = service.translate(['Hasta la vista'], 'es', 'en')
|
||||
expect(translations.size).to eq 1
|
||||
|
||||
translation = translations.first
|
||||
expect(translation.detected_source_language).to be 'es'
|
||||
expect(translation.provider).to eq 'LibreTranslate'
|
||||
expect(translation.text).to eq 'See you'
|
||||
end
|
||||
|
||||
it 'returns translation with auto-detected source language' do
|
||||
stub_request(:post, 'https://libretranslate.example.com/translate')
|
||||
.with(body: '{"q":"Guten Morgen","source":"auto","target":"en","format":"html","api_key":"my-api-key"}')
|
||||
.to_return(body: '{"detectedLanguage":{"confidence":92,"language":"de"},"translatedText":"Good morning"}')
|
||||
.with(body: '{"q":["Guten Morgen"],"source":"auto","target":"en","format":"html","api_key":"my-api-key"}')
|
||||
.to_return(body: '{"detectedLanguage": [{"confidence": 92, "language": "de"}], "translatedText": ["Good morning"]}')
|
||||
|
||||
translation = service.translate('Guten Morgen', nil, 'en')
|
||||
expect(translation.detected_source_language).to be_nil
|
||||
translations = service.translate(['Guten Morgen'], nil, 'en')
|
||||
expect(translations.size).to eq 1
|
||||
|
||||
translation = translations.first
|
||||
expect(translation.detected_source_language).to eq 'de'
|
||||
expect(translation.provider).to eq 'LibreTranslate'
|
||||
expect(translation.text).to eq 'Good morning'
|
||||
end
|
||||
|
||||
it 'returns translation of multiple texts' do
|
||||
stub_request(:post, 'https://libretranslate.example.com/translate')
|
||||
.with(body: '{"q":["Guten Morgen","Gute Nacht"],"source":"de","target":"en","format":"html","api_key":"my-api-key"}')
|
||||
.to_return(body: '{"translatedText": ["Good morning", "Good night"]}')
|
||||
|
||||
translations = service.translate(['Guten Morgen', 'Gute Nacht'], 'de', 'en')
|
||||
expect(translations.size).to eq 2
|
||||
|
||||
expect(translations.first.text).to eq 'Good morning'
|
||||
expect(translations.last.text).to eq 'Good night'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -62,6 +62,10 @@ RSpec.configure do |config|
|
|||
config.infer_spec_type_from_file_location!
|
||||
config.filter_rails_from_backtrace!
|
||||
|
||||
config.define_derived_metadata(file_path: Regexp.new('spec/lib/mastodon/cli')) do |metadata|
|
||||
metadata[:type] = :cli
|
||||
end
|
||||
|
||||
config.include Devise::Test::ControllerHelpers, type: :controller
|
||||
config.include Devise::Test::ControllerHelpers, type: :helper
|
||||
config.include Devise::Test::ControllerHelpers, type: :view
|
||||
|
@ -73,6 +77,10 @@ RSpec.configure do |config|
|
|||
config.include Redisable
|
||||
config.include SignedRequestHelpers, type: :request
|
||||
|
||||
config.before :each, type: :cli do
|
||||
stub_stdout
|
||||
end
|
||||
|
||||
config.before :each, type: :feature do
|
||||
https = ENV['LOCAL_HTTPS'] == 'true'
|
||||
Capybara.app_host = "http#{https ? 's' : ''}://#{ENV.fetch('LOCAL_DOMAIN')}"
|
||||
|
@ -106,6 +114,10 @@ def attachment_fixture(name)
|
|||
Rails.root.join('spec', 'fixtures', 'files', name).open
|
||||
end
|
||||
|
||||
def stub_stdout
|
||||
allow($stdout).to receive(:write)
|
||||
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'))
|
||||
|
|
226
spec/services/translate_status_service_spec.rb
Normal file
226
spec/services/translate_status_service_spec.rb
Normal file
|
@ -0,0 +1,226 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe TranslateStatusService, type: :service do
|
||||
subject(:service) { described_class.new }
|
||||
|
||||
let(:status) { Fabricate(:status, text: text, spoiler_text: spoiler_text, language: 'en', preloadable_poll: poll, media_attachments: media_attachments) }
|
||||
let(:text) { 'Hello' }
|
||||
let(:spoiler_text) { '' }
|
||||
let(:poll) { nil }
|
||||
let(:media_attachments) { [] }
|
||||
|
||||
before do
|
||||
Fabricate(:custom_emoji, shortcode: 'highfive')
|
||||
end
|
||||
|
||||
describe '#call' do
|
||||
before do
|
||||
translation_service = TranslationService.new
|
||||
allow(translation_service).to receive(:languages).and_return({ 'en' => ['es'] })
|
||||
allow(translation_service).to receive(:translate) do |texts|
|
||||
texts.map do |text|
|
||||
TranslationService::Translation.new(
|
||||
text: text.gsub('Hello', 'Hola').gsub('higfive', 'cincoaltos'),
|
||||
detected_source_language: 'en',
|
||||
provider: 'Dummy'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
allow(TranslationService).to receive(:configured?).and_return(true)
|
||||
allow(TranslationService).to receive(:configured).and_return(translation_service)
|
||||
end
|
||||
|
||||
it 'returns translated status content' do
|
||||
expect(service.call(status, 'es').content).to eq '<p>Hola</p>'
|
||||
end
|
||||
|
||||
it 'returns source language' do
|
||||
expect(service.call(status, 'es').detected_source_language).to eq 'en'
|
||||
end
|
||||
|
||||
it 'returns translation provider' do
|
||||
expect(service.call(status, 'es').provider).to eq 'Dummy'
|
||||
end
|
||||
|
||||
it 'returns original status' do
|
||||
expect(service.call(status, 'es').status).to eq status
|
||||
end
|
||||
|
||||
describe 'status has content with custom emoji' do
|
||||
let(:text) { 'Hello & :highfive:' }
|
||||
|
||||
it 'does not translate shortcode' do
|
||||
expect(service.call(status, 'es').content).to eq '<p>Hola & :highfive:</p>'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has no spoiler_text' do
|
||||
it 'returns an empty string' do
|
||||
expect(service.call(status, 'es').spoiler_text).to eq ''
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has spoiler_text' do
|
||||
let(:spoiler_text) { 'Hello & Hello!' }
|
||||
|
||||
it 'translates the spoiler text' do
|
||||
expect(service.call(status, 'es').spoiler_text).to eq 'Hola & Hola!'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has spoiler_text with custom emoji' do
|
||||
let(:spoiler_text) { 'Hello :highfive:' }
|
||||
|
||||
it 'does not translate shortcode' do
|
||||
expect(service.call(status, 'es').spoiler_text).to eq 'Hola :highfive:'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has spoiler_text with unmatched custom emoji' do
|
||||
let(:spoiler_text) { 'Hello :Hello:' }
|
||||
|
||||
it 'translates the invalid shortcode' do
|
||||
expect(service.call(status, 'es').spoiler_text).to eq 'Hola :Hola:'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has poll' do
|
||||
let(:poll) { Fabricate(:poll, options: ['Hello 1', 'Hello 2']) }
|
||||
|
||||
it 'translates the poll option title' do
|
||||
status_translation = service.call(status, 'es')
|
||||
expect(status_translation.poll_options.size).to eq 2
|
||||
expect(status_translation.poll_options.first.title).to eq 'Hola 1'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has media attachment' do
|
||||
let(:media_attachments) { [Fabricate(:media_attachment, description: 'Hello & :highfive:')] }
|
||||
|
||||
it 'translates the media attachment description' do
|
||||
status_translation = service.call(status, 'es')
|
||||
|
||||
media_attachment = status_translation.media_attachments.first
|
||||
expect(media_attachment.id).to eq media_attachments.first.id
|
||||
expect(media_attachment.description).to eq 'Hola & :highfive:'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#source_texts' do
|
||||
before do
|
||||
service.instance_variable_set(:@status, status)
|
||||
end
|
||||
|
||||
describe 'status only has content' do
|
||||
it 'returns formatted content' do
|
||||
expect(service.send(:source_texts)).to eq({ content: '<p>Hello</p>' })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status content contains custom emoji' do
|
||||
let(:status) { Fabricate(:status, text: 'Hello :highfive:') }
|
||||
|
||||
it 'returns formatted content' do
|
||||
source_texts = service.send(:source_texts)
|
||||
expect(source_texts[:content]).to eq '<p>Hello <span translate="no">:highfive:</span></p>'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status content contains tags' do
|
||||
let(:status) { Fabricate(:status, text: 'Hello #hola') }
|
||||
|
||||
it 'returns formatted content' do
|
||||
source_texts = service.send(:source_texts)
|
||||
expect(source_texts[:content]).to include '<p>Hello <a'
|
||||
expect(source_texts[:content]).to include '/tags/hola'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has spoiler text' do
|
||||
let(:status) { Fabricate(:status, spoiler_text: 'Hello :highfive:') }
|
||||
|
||||
it 'returns formatted spoiler text' do
|
||||
source_texts = service.send(:source_texts)
|
||||
expect(source_texts[:spoiler_text]).to eq 'Hello <span translate="no">:highfive:</span>'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has poll' do
|
||||
let(:poll) { Fabricate(:poll, options: %w(Blue Green)) }
|
||||
|
||||
it 'returns formatted poll options' do
|
||||
source_texts = service.send(:source_texts)
|
||||
expect(source_texts.size).to eq 3
|
||||
expect(source_texts.values).to eq %w(<p>Hello</p> Blue Green)
|
||||
|
||||
expect(source_texts.keys.first).to eq :content
|
||||
|
||||
option1 = source_texts.keys.second
|
||||
expect(option1).to be_a Poll::Option
|
||||
expect(option1.id).to eq '0'
|
||||
expect(option1.title).to eq 'Blue'
|
||||
|
||||
option2 = source_texts.keys.third
|
||||
expect(option2).to be_a Poll::Option
|
||||
expect(option2.id).to eq '1'
|
||||
expect(option2.title).to eq 'Green'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has poll with custom emoji' do
|
||||
let(:poll) { Fabricate(:poll, options: ['Blue', 'Green :highfive:']) }
|
||||
|
||||
it 'returns formatted poll options' do
|
||||
html = service.send(:source_texts).values.last
|
||||
expect(html).to eq 'Green <span translate="no">:highfive:</span>'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has media attachments' do
|
||||
let(:text) { '' }
|
||||
let(:media_attachments) { [Fabricate(:media_attachment, description: 'Hello :highfive:')] }
|
||||
|
||||
it 'returns media attachments without custom emoji rendering' do
|
||||
source_texts = service.send(:source_texts)
|
||||
expect(source_texts.size).to eq 1
|
||||
|
||||
key, text = source_texts.first
|
||||
expect(key).to eq media_attachments.first
|
||||
expect(text).to eq 'Hello :highfive:'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#wrap_emoji_shortcodes' do
|
||||
before do
|
||||
service.instance_variable_set(:@status, status)
|
||||
end
|
||||
|
||||
describe 'string contains custom emoji' do
|
||||
let(:text) { ':highfive:' }
|
||||
|
||||
it 'renders the emoji' do
|
||||
html = service.send(:wrap_emoji_shortcodes, 'Hello :highfive:'.html_safe)
|
||||
expect(html).to eq 'Hello <span translate="no">:highfive:</span>'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#unwrap_emoji_shortcodes' do
|
||||
describe 'string contains custom emoji' do
|
||||
it 'inserts the shortcode' do
|
||||
fragment = service.send(:unwrap_emoji_shortcodes, '<p>Hello <span translate="no">:highfive:</span>!</p>')
|
||||
expect(fragment.to_html).to eq '<p>Hello :highfive:!</p>'
|
||||
end
|
||||
|
||||
it 'preserves other attributes than translate=no' do
|
||||
fragment = service.send(:unwrap_emoji_shortcodes, '<p>Hello <span translate="no" class="foo">:highfive:</span>!</p>')
|
||||
expect(fragment.to_html).to eq '<p>Hello <span class="foo">:highfive:</span>!</p>'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue