Merge commit '877090518682b6c77ba9bdfa0231afd56daec44d' into glitch-soc/merge-upstream

Conflicts:
- `app/models/concerns/user/has_settings.rb`:
  Not a real conflict, upstream added a setting textually close to a glitch-soc one.
  Added upstream's new setting.
- `app/views/settings/preferences/appearance/show.html.haml`:
  Not a real conflict, upstream added a setting textually close to a glitch-soc one.
  Added upstream's new setting.
- `config/routes.rb`:
  Upstream moved web app routes to `config/routes/web_app.rb`, while glitch-soc had
  an extra route.
  Moved the extra route to `config/routes/web_app.rb`.
- `spec/controllers/settings/preferences/appearance_controller_spec.rb`:
  This spec got converted to a system spec upstream.
  However, the theme setting works differently in glitch-soc, so the spec had been
  changed.
  Changed the corresponding system spec as well.
This commit is contained in:
Claire
2024-12-25 18:01:37 +01:00
759 changed files with 13447 additions and 7221 deletions

View File

@@ -1,6 +0,0 @@
inherit_from: ../../.rubocop.yml
# Anonymous controllers in specs cannot access `described_class`, explanation:
# https://github.com/rubocop/rubocop-rspec/blob/v2.26.1/lib/rubocop/cop/rspec/described_class.rb#L36-L56
RSpec/DescribedClass:
SkipBlocks: true

View File

@@ -1,57 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::AccountModerationNotesController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
let(:target_account) { Fabricate(:account) }
before do
sign_in user, scope: :user
end
describe 'POST #create' do
subject { post :create, params: params }
context 'when parameters are valid' do
let(:params) { { account_moderation_note: { target_account_id: target_account.id, content: 'test content' } } }
it 'successfully creates a note' do
expect { subject }.to change(AccountModerationNote, :count).by(1)
expect(response).to redirect_to admin_account_path(target_account.id)
end
end
context 'when the content is too short' do
let(:params) { { account_moderation_note: { target_account_id: target_account.id, content: '' } } }
it 'fails to create a note' do
expect { subject }.to_not change(AccountModerationNote, :count)
expect(response).to render_template 'admin/accounts/show'
end
end
context 'when the content is too long' do
let(:params) { { account_moderation_note: { target_account_id: target_account.id, content: 'test' * AccountModerationNote::CONTENT_SIZE_LIMIT } } }
it 'fails to create a note' do
expect { subject }.to_not change(AccountModerationNote, :count)
expect(response).to render_template 'admin/accounts/show'
end
end
end
describe 'DELETE #destroy' do
subject { delete :destroy, params: { id: note.id } }
let!(:note) { Fabricate(:account_moderation_note, account: account, target_account: target_account) }
let(:account) { Fabricate(:account) }
it 'destroys note' do
expect { subject }.to change(AccountModerationNote, :count).by(-1)
expect(response).to redirect_to admin_account_path(target_account.id)
end
end
end

View File

@@ -1,57 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::CustomEmojisController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
before do
Fabricate(:custom_emoji)
end
it 'renders index page' do
get :index
expect(response).to have_http_status 200
expect(response).to render_template :index
end
end
describe 'GET #new' do
it 'renders new page' do
get :new
expect(response).to have_http_status 200
expect(response).to render_template :new
end
end
describe 'POST #create' do
subject { post :create, params: { custom_emoji: params } }
let(:image) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'emojo.png'), 'image/png') }
context 'when parameter is valid' do
let(:params) { { shortcode: 'test', image: image } }
it 'creates custom emoji' do
expect { subject }.to change(CustomEmoji, :count).by(1)
end
end
context 'when parameter is invalid' do
let(:params) { { shortcode: 't', image: image } }
it 'renders new' do
expect(subject).to render_template :new
end
end
end
end

View File

@@ -1,49 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::DomainAllowsController do
render_views
before do
sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
end
describe 'GET #new' do
it 'assigns a new domain allow' do
get :new
expect(response).to have_http_status(200)
end
end
describe 'POST #create' do
it 'blocks the domain when succeeded to save' do
post :create, params: { domain_allow: { domain: 'example.com' } }
expect(flash[:notice]).to eq I18n.t('admin.domain_allows.created_msg')
expect(response).to redirect_to(admin_instances_path)
end
it 'renders new when failed to save' do
Fabricate(:domain_allow, domain: 'example.com')
post :create, params: { domain_allow: { domain: 'example.com' } }
expect(response).to render_template :new
end
end
describe 'DELETE #destroy' do
it 'disallows the domain' do
service = instance_double(UnallowDomainService, call: true)
allow(UnallowDomainService).to receive(:new).and_return(service)
domain_allow = Fabricate(:domain_allow)
delete :destroy, params: { id: domain_allow.id }
expect(service).to have_received(:call).with(domain_allow)
expect(flash[:notice]).to eq I18n.t('admin.domain_allows.destroyed_msg')
expect(response).to redirect_to(admin_instances_path)
end
end
end

View File

@@ -32,15 +32,16 @@ RSpec.describe Admin::ExportDomainAllowsController do
it 'allows imported domains' do
post :import, params: { admin_import: { data: fixture_file_upload('domain_allows.csv') } }
expect(response).to redirect_to(admin_instances_path)
expect(response)
.to redirect_to(admin_instances_path)
# Header should not be imported
expect(DomainAllow.where(domain: '#domain').present?).to be(false)
# Domains should now be added
get :export, params: { format: :csv }
expect(response).to have_http_status(200)
expect(response.body).to eq(domain_allows_csv_file)
# Header row should not be imported, but domains should
expect(DomainAllow)
.to_not exist(domain: '#domain')
expect(DomainAllow)
.to exist(domain: 'good.domain')
expect(DomainAllow)
.to exist(domain: 'better.domain')
end
it 'displays error on no file selected' do

View File

@@ -1,54 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::IpBlocksController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
it 'returns http success' do
get :index
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

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::TermsOfService::DistributionsController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
let(:terms_of_service) { Fabricate(:terms_of_service, notification_sent_at: nil) }
before do
sign_in user, scope: :user
end
describe 'POST #create' do
it 'returns http success' do
post :create, params: { terms_of_service_id: terms_of_service.id }
expect(response).to redirect_to(admin_terms_of_service_index_path)
end
end
end

View File

@@ -0,0 +1,66 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::TermsOfService::DraftsController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
it 'returns http success' do
get :show
expect(response).to have_http_status(:success)
end
end
describe 'PUT #update' do
subject { put :update, params: params }
let!(:terms) { Fabricate :terms_of_service, published_at: nil }
context 'with publishing params' do
let(:params) { { terms_of_service: { text: 'new' }, action_type: 'publish' } }
it 'publishes the record' do
expect { subject }
.to change(Admin::ActionLog, :count).by(1)
expect(response)
.to redirect_to(admin_terms_of_service_index_path)
expect(terms.reload.published_at)
.to_not be_nil
end
end
context 'with non publishing params' do
let(:params) { { terms_of_service: { text: 'new' }, action_type: 'save_draft' } }
it 'updates but does not publish the record' do
expect { subject }
.to_not change(Admin::ActionLog, :count)
expect(response)
.to redirect_to(admin_terms_of_service_draft_path)
expect(terms.reload.published_at)
.to be_nil
end
end
context 'with invalid params' do
let(:params) { { terms_of_service: { text: '' }, action_type: 'save_draft' } }
it 'does not update the record' do
subject
expect(response)
.to have_http_status(:success)
end
end
end
end

View File

@@ -0,0 +1,65 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::TermsOfService::GeneratesController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
it 'returns http success' do
get :show
expect(response).to have_http_status(:success)
end
end
describe 'POST #create' do
subject { post :create, params: params }
context 'with valid params' do
let(:params) do
{
terms_of_service_generator: {
admin_email: 'test@host.example',
arbitration_address: '123 Main Street',
arbitration_website: 'https://host.example',
dmca_address: '123 DMCA Ave',
dmca_email: 'dmca@host.example',
domain: 'host.example',
jurisdiction: 'Europe',
},
}
end
it 'saves new record' do
expect { subject }
.to change(TermsOfService, :count).by(1)
expect(response)
.to redirect_to(admin_terms_of_service_draft_path)
end
end
context 'with invalid params' do
let(:params) do
{
terms_of_service_generator: {
admin_email: 'what the',
},
}
end
it 'does not save new record' do
expect { subject }
.to_not change(TermsOfService, :count)
expect(response)
.to have_http_status(200)
end
end
end
end

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::TermsOfService::HistoriesController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
it 'returns http success' do
get :show
expect(response).to have_http_status(:success)
end
end
end

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::TermsOfService::PreviewsController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
let(:terms_of_service) { Fabricate(:terms_of_service, notification_sent_at: nil) }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
it 'returns http success' do
get :show, params: { terms_of_service_id: terms_of_service.id }
expect(response).to have_http_status(:success)
end
end
end

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::TermsOfService::TestsController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
let(:terms_of_service) { Fabricate(:terms_of_service, notification_sent_at: nil) }
before do
sign_in user, scope: :user
end
describe 'POST #create' do
it 'returns http success' do
post :create, params: { terms_of_service_id: terms_of_service.id }
expect(response).to redirect_to(admin_terms_of_service_preview_path(terms_of_service))
end
end
end

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::TermsOfServiceController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
it 'returns http success' do
get :index
expect(response).to have_http_status(:success)
end
end
end

View File

@@ -1,117 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::WebhooksController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
it 'returns http success' do
get :index
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, events: ['account.created', 'report.created']) }
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(:edit)
end
end
describe 'POST #enable' do
it 'enables the webhook' do
post :enable, params: { id: webhook.id }
expect(webhook.reload).to be_enabled
expect(response).to redirect_to(admin_webhook_path(webhook))
end
end
describe 'POST #disable' do
it 'disables the webhook' do
post :disable, params: { id: webhook.id }
expect(webhook.reload).to_not be_enabled
expect(response).to redirect_to(admin_webhook_path(webhook))
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

View File

@@ -4,7 +4,7 @@ require 'rails_helper'
RSpec.describe AccountControllerConcern do
controller(ApplicationController) do
include AccountControllerConcern
include AccountControllerConcern # rubocop:disable RSpec/DescribedClass
def success
render plain: @account.username # rubocop:disable RSpec/InstanceVariable

View File

@@ -4,7 +4,7 @@ require 'rails_helper'
RSpec.describe Api::RateLimitHeaders do
controller(ApplicationController) do
include Api::RateLimitHeaders
include Api::RateLimitHeaders # rubocop:disable RSpec/DescribedClass
def show
head 200

View File

@@ -4,7 +4,7 @@ require 'rails_helper'
RSpec.describe ChallengableConcern do
controller(ApplicationController) do
include ChallengableConcern
include ChallengableConcern # rubocop:disable RSpec/DescribedClass
before_action :require_challenge!

View File

@@ -4,7 +4,7 @@ require 'rails_helper'
RSpec.describe Localized do
controller(ApplicationController) do
include Localized
include Localized # rubocop:disable RSpec/DescribedClass
def success
render plain: I18n.locale, status: 200

View File

@@ -4,7 +4,7 @@ require 'rails_helper'
RSpec.describe PreloadingConcern do
controller(ApplicationController) do
include PreloadingConcern
include PreloadingConcern # rubocop:disable RSpec/DescribedClass
def empty_array
render plain: preload_collection([], Status).size

View File

@@ -4,7 +4,7 @@ require 'rails_helper'
RSpec.describe Settings::ExportControllerConcern do
controller(ApplicationController) do
include Settings::ExportControllerConcern
include Settings::ExportControllerConcern # rubocop:disable RSpec/DescribedClass
def index
send_export_file

View File

@@ -4,7 +4,7 @@ require 'rails_helper'
RSpec.describe UserTrackingConcern do
controller(ApplicationController) do
include UserTrackingConcern
include UserTrackingConcern # rubocop:disable RSpec/DescribedClass
def show
render plain: 'show'

View File

@@ -1,34 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe FiltersController do
render_views
describe 'GET #index' do
context 'with signed out user' do
before do
get :index
end
it 'redirects' do
expect(response).to be_redirect
end
end
context 'with a signed in user' do
before do
sign_in(Fabricate(:user))
get :index
end
it 'returns http success' do
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')
end
end
end
end

View File

@@ -1,29 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::LoginActivitiesController do
render_views
let!(:user) { Fabricate(:user) }
let!(:login_activity) { Fabricate :login_activity, user: user }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
before do
get :index
end
it 'returns http success with private cache control headers', :aggregate_failures do
expect(response).to have_http_status(200)
expect(response.headers['Cache-Control']).to include('private, no-store')
expect(response.body)
.to include(login_activity.user_agent)
.and include(login_activity.authentication_method)
.and include(login_activity.ip.to_s)
end
end
end

View File

@@ -1,35 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::Preferences::AppearanceController do
render_views
let!(:user) { Fabricate(:user) }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
before do
get :show
end
it 'returns http success with private cache control headers', :aggregate_failures do
expect(response).to have_http_status(200)
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'PUT #update' do
subject { put :update, params: { user: { settings_attributes: { skin: 'contrast' } } } }
it 'redirects correctly' do
expect { subject }
.to change { user.reload.settings.skin }.to('contrast')
expect(response).to redirect_to(settings_preferences_appearance_path)
end
end
end

View File

@@ -1,43 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::Preferences::NotificationsController do
render_views
let(:user) { Fabricate(:user) }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
before do
get :show
end
it 'returns http success with private cache control headers', :aggregate_failures do
expect(response).to have_http_status(200)
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'PUT #update' do
it 'updates notifications settings' do
user.settings.update('notification_emails.follow': false)
user.save
put :update, params: {
user: {
settings_attributes: {
'notification_emails.follow': '1',
},
},
}
expect(response).to redirect_to(settings_preferences_notifications_path)
user.reload
expect(user.settings['notification_emails.follow']).to be true
end
end
end

View File

@@ -1,54 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::Preferences::OtherController do
render_views
let(:user) { Fabricate(:user, chosen_languages: []) }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
before do
get :show
end
it 'returns http success with private cache control headers', :aggregate_failures do
expect(response).to have_http_status(200)
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'PUT #update' do
it 'updates the user record' do
put :update, params: { user: { locale: 'en', chosen_languages: ['es', 'fr', ''] } }
expect(response).to redirect_to(settings_preferences_other_path)
user.reload
expect(user.locale).to eq 'en'
expect(user.chosen_languages).to eq %w(es fr)
end
it 'updates user settings' do
user.settings.update('web.reblog_modal': false, 'web.delete_modal': true)
user.save
put :update, params: {
user: {
settings_attributes: {
'web.reblog_modal': '1',
'web.delete_modal': '0',
},
},
}
expect(response).to redirect_to(settings_preferences_other_path)
user.reload
expect(user.settings['web.reblog_modal']).to be true
expect(user.settings['web.delete_modal']).to be false
end
end
end

View File

@@ -1,51 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::ProfilesController do
render_views
let!(:user) { Fabricate(:user) }
let(:account) { user.account }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
before do
get :show
end
it 'returns http success with private cache control headers', :aggregate_failures do
expect(response).to have_http_status(200)
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'PUT #update' do
before do
user.account.update(display_name: 'Old name')
end
it 'updates the user profile' do
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async)
put :update, params: { account: { display_name: 'New name' } }
expect(account.reload.display_name).to eq 'New name'
expect(response).to redirect_to(settings_profile_path)
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id)
end
end
describe 'PUT #update with new profile image' do
it 'updates profile image' do
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async)
expect(account.avatar.instance.avatar_file_name).to be_nil
put :update, params: { account: { avatar: fixture_file_upload('avatar.gif', 'image/gif') } }
expect(response).to redirect_to(settings_profile_path)
expect(account.reload.avatar.instance.avatar_file_name).to_not be_nil
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id)
end
end
end

View File

@@ -1,29 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::VerificationsController do
render_views
let!(:user) { Fabricate(:user) }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
before do
get :show
end
it 'returns http success with private cache control headers', :aggregate_failures do
expect(response)
.to have_http_status(200)
.and have_attributes(
headers: include(
'Cache-Control' => 'private, no-store'
)
)
end
end
end

View File

@@ -1,43 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe StatusesCleanupController do
render_views
let!(:user) { Fabricate(:user) }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
before do
get :show
end
it 'returns http success' do
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')
end
end
describe 'PUT #update' do
before do
put :update, params: { account_statuses_cleanup_policy: { enabled: true, min_status_age: 2.weeks.seconds, keep_direct: false, keep_polls: true } }
end
it 'updates the account status cleanup policy' do
expect(user.account.statuses_cleanup_policy.enabled).to be true
expect(user.account.statuses_cleanup_policy.keep_direct).to be false
expect(user.account.statuses_cleanup_policy.keep_polls).to be true
end
it 'redirects' do
expect(response).to redirect_to(statuses_cleanup_path)
end
end
end

View File

@@ -527,213 +527,4 @@ RSpec.describe StatusesController do
end
end
end
describe 'GET #activity' do
let(:account) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: account) }
context 'when account is permanently suspended' do
before do
account.suspend!
account.deletion_request.destroy
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http gone' do
expect(response).to have_http_status(410)
end
end
context 'when account is temporarily suspended' do
before do
account.suspend!
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
context 'when status is public' do
before do
status.update(visibility: :public)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(:success)
end
end
context 'when status is private' do
before do
status.update(visibility: :private)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
before do
status.update(visibility: :direct)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end
context 'when signed-in' do
let(:user) { Fabricate(:user) }
before do
sign_in(user)
end
context 'when status is public' do
before do
status.update(visibility: :public)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(:success)
end
end
context 'when status is private' do
before do
status.update(visibility: :private)
end
context 'when user is authorized to see it' do
before do
user.account.follow!(account)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
context 'when user is not authorized to see it' do
before do
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end
end
context 'when status is direct' do
before do
status.update(visibility: :direct)
end
context 'when user is authorized to see it' do
before do
Fabricate(:mention, account: user.account, status: status)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
context 'when user is not authorized to see it' do
before do
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end
end
end
context 'with signature' do
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
before do
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
end
context 'when status is public' do
before do
status.update(visibility: :public)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(:success)
end
end
context 'when status is private' do
before do
status.update(visibility: :private)
end
context 'when user is authorized to see it' do
before do
remote_account.follow!(account)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
context 'when user is not authorized to see it' do
before do
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end
end
context 'when status is direct' do
before do
status.update(visibility: :direct)
end
context 'when user is authorized to see it' do
before do
Fabricate(:mention, account: remote_account, status: status)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
context 'when user is not authorized to see it' do
before do
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end
end
end
end
end

View File

@@ -1,5 +1,5 @@
# frozen_string_literal: true
Fabricator(:setting) do
var 'var'
var { sequence(:var) { |n| "var_#{n}" } }
end

View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true
Fabricator(:status_edit) do
status { Fabricate.build(:status) }
end

View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true
Fabricator(:tag_trend) do
tag
end

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
Fabricator(:terms_of_service) do
text { Faker::Lorem.paragraph }
changelog { Faker::Lorem.paragraph }
published_at { Time.zone.now }
notification_sent_at { Time.zone.now }
end

View File

@@ -160,10 +160,6 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when fetching' do
subject { described_class.new(json, sender) }
before do
subject.perform
end
context 'when object publication date is below ISO8601 range' do
let(:object_json) do
{
@@ -175,6 +171,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status with a valid creation date', :aggregate_failures do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -195,6 +193,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status with a valid creation date', :aggregate_failures do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -216,6 +216,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status with appropriate creation and edition dates', :aggregate_failures do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -239,17 +241,13 @@ RSpec.describe ActivityPub::Activity::Create do
}
end
it 'creates status' do
it 'creates status and does not mark it as edited' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.text).to eq 'Lorem ipsum'
end
it 'does not mark status as edited' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.edited?).to be false
end
end
@@ -264,7 +262,7 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'does not create a status' do
expect(sender.statuses.count).to be_zero
expect { subject.perform }.to_not change(sender.statuses, :count)
end
end
@@ -278,6 +276,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -285,6 +285,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'missing to/cc defaults to direct privacy' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -303,6 +305,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -321,6 +325,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -339,6 +345,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -357,6 +365,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -375,6 +385,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -393,6 +405,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -411,6 +425,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -433,6 +449,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -452,15 +470,13 @@ RSpec.describe ActivityPub::Activity::Create do
}
end
it 'creates status' do
it 'creates status with a silent mention' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'limited'
end
it 'creates silent mention' do
status = sender.statuses.first
expect(status.mentions.first).to be_silent
end
end
@@ -483,6 +499,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status with limited visibility' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -507,6 +525,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status with direct visibility' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -528,6 +548,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -548,6 +570,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -576,6 +600,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -598,6 +624,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
end
@@ -625,6 +653,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status with correctly-ordered media attachments' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -651,6 +681,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -676,6 +708,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -701,6 +735,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -724,6 +760,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
end
@@ -746,6 +784,42 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.tags.map(&:name)).to include('test')
end
end
context 'with featured hashtags' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
to: 'https://www.w3.org/ns/activitystreams#Public',
tag: [
{
type: 'Hashtag',
href: 'http://example.com/blah',
name: '#test',
},
],
}
end
before do
sender.featured_tags.create!(name: 'test')
end
it 'creates status and updates featured tag' do
expect { subject.perform }
.to change(sender.statuses, :count).by(1)
.and change { sender.featured_tags.first.reload.statuses_count }.by(1)
.and change { sender.featured_tags.first.reload.last_status_at }.from(nil).to(be_within(0.1).of(Time.now.utc))
status = sender.statuses.first
expect(status).to_not be_nil
@@ -769,6 +843,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
end
@@ -791,6 +867,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
end
@@ -815,6 +893,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -841,6 +921,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
@@ -866,6 +948,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
end
@@ -887,6 +971,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
end
@@ -917,13 +1003,13 @@ RSpec.describe ActivityPub::Activity::Create do
}
end
it 'creates status' do
it 'creates status with a poll' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.poll).to_not be_nil
end
it 'creates a poll' do
poll = sender.polls.first
expect(poll).to_not be_nil
expect(poll.status).to_not be_nil
@@ -946,6 +1032,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'adds a vote to the poll with correct uri' do
expect { subject.perform }.to change(poll.votes, :count).by(1)
vote = poll.votes.first
expect(vote).to_not be_nil
expect(vote.uri).to eq object_json[:id]
@@ -955,9 +1043,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when a vote to an expired local poll' do
let(:poll) do
poll = Fabricate.build(:poll, options: %w(Yellow Blue), expires_at: 1.day.ago)
poll.save(validate: false)
poll
travel_to 2.days.ago do
Fabricate(:poll, options: %w(Yellow Blue), expires_at: 1.day.from_now)
end
end
let!(:local_status) { Fabricate(:status, poll: poll) }
@@ -971,6 +1059,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'does not add a vote to the poll' do
expect { subject.perform }.to_not change(poll.votes, :count)
expect(poll.votes.first).to be_nil
end
end
@@ -995,6 +1085,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
it 'uses the counts from the created object' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
status = sender.statuses.first
expect(status.untrusted_favourites_count).to eq 50
expect(status.untrusted_reblogs_count).to eq 100

View File

@@ -37,15 +37,15 @@ RSpec.describe Sanitize::Config do
end
it 'keeps a with href' do
expect(Sanitize.fragment('<a href="http://example.com">Test</a>', subject)).to eq '<a href="http://example.com" rel="nofollow noopener noreferrer" target="_blank">Test</a>'
expect(Sanitize.fragment('<a href="http://example.com">Test</a>', subject)).to eq '<a href="http://example.com" rel="nofollow noopener" target="_blank">Test</a>'
end
it 'keeps a with translate="no"' do
expect(Sanitize.fragment('<a href="http://example.com" translate="no">Test</a>', subject)).to eq '<a href="http://example.com" translate="no" rel="nofollow noopener noreferrer" target="_blank">Test</a>'
expect(Sanitize.fragment('<a href="http://example.com" translate="no">Test</a>', subject)).to eq '<a href="http://example.com" translate="no" rel="nofollow noopener" target="_blank">Test</a>'
end
it 'removes "translate" attribute with invalid value' do
expect(Sanitize.fragment('<a href="http://example.com" translate="foo">Test</a>', subject)).to eq '<a href="http://example.com" rel="nofollow noopener noreferrer" target="_blank">Test</a>'
expect(Sanitize.fragment('<a href="http://example.com" translate="foo">Test</a>', subject)).to eq '<a href="http://example.com" rel="nofollow noopener" target="_blank">Test</a>'
end
it 'removes a with unparsable href' do
@@ -53,7 +53,7 @@ RSpec.describe Sanitize::Config do
end
it 'keeps a with supported scheme and no host' do
expect(Sanitize.fragment('<a href="dweb:/a/foo">Test</a>', subject)).to eq '<a href="dweb:/a/foo" rel="nofollow noopener noreferrer" target="_blank">Test</a>'
expect(Sanitize.fragment('<a href="dweb:/a/foo">Test</a>', subject)).to eq '<a href="dweb:/a/foo" rel="nofollow noopener" target="_blank">Test</a>'
end
it 'keeps title in abbr' do

View File

@@ -50,7 +50,7 @@ RSpec.describe TextFormatter do
end
end
context 'when given a stand-alone google URL' do
context 'when given a stand-alone Google URL' do
let(:text) { 'http://google.com' }
it 'matches the full URL' do
@@ -280,6 +280,26 @@ RSpec.describe TextFormatter do
end
end
context 'when given a lengthy URL' do
let(:text) { 'lorem https://prepitaph.org/wip/web-dovespair/ ipsum' }
it 'truncates the URL' do
expect(subject).to include '<span class="invisible">https://</span>'
expect(subject).to include '<span class="ellipsis">prepitaph.org/wip/web-dovespai</span>'
expect(subject).to include '<span class="invisible">r/</span>'
end
end
context 'when given a sufficiently short URL' do
let(:text) { 'lorem https://prepitaph.org/wip/web-devspair/ ipsum' }
it 'does not truncate the URL' do
expect(subject).to include '<span class="invisible">https://</span>'
expect(subject).to include '<span class="">prepitaph.org/wip/web-devspair/</span>'
expect(subject).to include '<span class="invisible"></span>'
end
end
context 'when given text containing a hashtag' do
let(:text) { '#hashtag' }

View File

@@ -71,6 +71,7 @@ RSpec.describe AdminMailer do
before do
PreviewCardTrend.create!(preview_card: link)
StatusTrend.create!(status: status, account: Fabricate(:account))
TagTrend.create!(tag: tag)
recipient.user.update(locale: :en)
end

View File

@@ -98,4 +98,9 @@ class UserMailerPreview < ActionMailer::Preview
def failed_2fa
UserMailer.failed_2fa(User.first, '127.0.0.1', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0', Time.now.utc)
end
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/terms_of_service_changed
def terms_of_service_changed
UserMailer.terms_of_service_changed(User.first, TermsOfService.live.first)
end
end

View File

@@ -3,6 +3,17 @@
require 'rails_helper'
RSpec.describe UserMailer do
shared_examples 'delivery to memorialized user' do
context 'when the account is memorialized' do
before { receiver.account.update(memorial: true) }
it 'does not deliver mail' do
emails = capture_emails { mail.deliver_now }
expect(emails).to be_empty
end
end
end
let(:receiver) { Fabricate(:user) }
describe '#confirmation_instructions' do
@@ -21,6 +32,7 @@ RSpec.describe UserMailer do
include_examples 'localized subject',
'devise.mailer.confirmation_instructions.subject',
instance: Rails.configuration.x.local_domain
include_examples 'delivery to memorialized user'
end
describe '#reconfirmation_instructions' do
@@ -39,6 +51,7 @@ RSpec.describe UserMailer do
include_examples 'localized subject',
'devise.mailer.confirmation_instructions.subject',
instance: Rails.configuration.x.local_domain
include_examples 'delivery to memorialized user'
end
describe '#reset_password_instructions' do
@@ -55,6 +68,7 @@ RSpec.describe UserMailer do
include_examples 'localized subject',
'devise.mailer.reset_password_instructions.subject'
include_examples 'delivery to memorialized user'
end
describe '#password_change' do
@@ -70,6 +84,7 @@ RSpec.describe UserMailer do
include_examples 'localized subject',
'devise.mailer.password_change.subject'
include_examples 'delivery to memorialized user'
end
describe '#email_changed' do
@@ -85,6 +100,7 @@ RSpec.describe UserMailer do
include_examples 'localized subject',
'devise.mailer.email_changed.subject'
include_examples 'delivery to memorialized user'
end
describe '#warning' do
@@ -115,6 +131,7 @@ RSpec.describe UserMailer do
include_examples 'localized subject',
'devise.mailer.webauthn_credential.deleted.subject'
include_examples 'delivery to memorialized user'
end
describe '#suspicious_sign_in' do
@@ -186,6 +203,8 @@ RSpec.describe UserMailer do
.and(have_subject(I18n.t('devise.mailer.two_factor_enabled.subject')))
.and(have_body_text(I18n.t('devise.mailer.two_factor_enabled.explanation')))
end
include_examples 'delivery to memorialized user'
end
describe '#two_factor_disabled' do
@@ -197,6 +216,8 @@ RSpec.describe UserMailer do
.and(have_subject(I18n.t('devise.mailer.two_factor_disabled.subject')))
.and(have_body_text(I18n.t('devise.mailer.two_factor_disabled.explanation')))
end
include_examples 'delivery to memorialized user'
end
describe '#webauthn_enabled' do
@@ -208,6 +229,8 @@ RSpec.describe UserMailer do
.and(have_subject(I18n.t('devise.mailer.webauthn_enabled.subject')))
.and(have_body_text(I18n.t('devise.mailer.webauthn_enabled.explanation')))
end
include_examples 'delivery to memorialized user'
end
describe '#webauthn_disabled' do
@@ -219,6 +242,8 @@ RSpec.describe UserMailer do
.and(have_subject(I18n.t('devise.mailer.webauthn_disabled.subject')))
.and(have_body_text(I18n.t('devise.mailer.webauthn_disabled.explanation')))
end
include_examples 'delivery to memorialized user'
end
describe '#two_factor_recovery_codes_changed' do
@@ -230,6 +255,8 @@ RSpec.describe UserMailer do
.and(have_subject(I18n.t('devise.mailer.two_factor_recovery_codes_changed.subject')))
.and(have_body_text(I18n.t('devise.mailer.two_factor_recovery_codes_changed.explanation')))
end
include_examples 'delivery to memorialized user'
end
describe '#webauthn_credential_added' do
@@ -242,6 +269,8 @@ RSpec.describe UserMailer do
.and(have_subject(I18n.t('devise.mailer.webauthn_credential.added.subject')))
.and(have_body_text(I18n.t('devise.mailer.webauthn_credential.added.explanation')))
end
include_examples 'delivery to memorialized user'
end
describe '#welcome' do
@@ -259,6 +288,8 @@ RSpec.describe UserMailer do
.and(have_subject(I18n.t('user_mailer.welcome.subject')))
.and(have_body_text(I18n.t('user_mailer.welcome.explanation')))
end
include_examples 'delivery to memorialized user'
end
describe '#backup_ready' do
@@ -271,5 +302,19 @@ RSpec.describe UserMailer do
.and(have_subject(I18n.t('user_mailer.backup_ready.subject')))
.and(have_body_text(I18n.t('user_mailer.backup_ready.explanation')))
end
include_examples 'delivery to memorialized user'
end
describe '#terms_of_service_changed' do
let(:terms) { Fabricate :terms_of_service }
let(:mail) { described_class.terms_of_service_changed(receiver, terms) }
it 'renders terms_of_service_changed mail' do
expect(mail)
.to be_present
.and(have_subject(I18n.t('user_mailer.terms_of_service_changed.subject')))
.and(have_body_text(I18n.t('user_mailer.terms_of_service_changed.changelog')))
end
end
end

View File

@@ -0,0 +1,33 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe AccountPin do
describe 'Associations' do
it { is_expected.to belong_to(:account).required }
it { is_expected.to belong_to(:target_account).required }
end
describe 'Validations' do
describe 'the follow relationship' do
subject { Fabricate.build :account_pin, account: account }
let(:account) { Fabricate :account }
let(:target_account) { Fabricate :account }
context 'when account is following target account' do
before { account.follow!(target_account) }
it { is_expected.to allow_value(target_account).for(:target_account).against(:base) }
end
context 'when account is not following target account' do
it { is_expected.to_not allow_value(target_account).for(:target_account).against(:base).with_message(not_following_message) }
def not_following_message
I18n.t('accounts.pin_errors.following')
end
end
end
end
end

View File

@@ -129,8 +129,8 @@ RSpec.describe AccountStatusesCleanupPolicy do
let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
it 'records the given id' do
account_statuses_cleanup_policy.record_last_inspected(42)
expect(account_statuses_cleanup_policy.last_inspected).to eq 42
expect { account_statuses_cleanup_policy.record_last_inspected(42) }
.to change(account_statuses_cleanup_policy, :last_inspected).from(nil).to(42)
end
end
@@ -153,8 +153,8 @@ RSpec.describe AccountStatusesCleanupPolicy do
end
it 'does not change the recorded id' do
subject
expect(account_statuses_cleanup_policy.last_inspected).to eq 42
expect { subject }
.to_not change(account_statuses_cleanup_policy, :last_inspected).from(42)
end
end
@@ -164,8 +164,8 @@ RSpec.describe AccountStatusesCleanupPolicy do
end
it 'records the older id' do
subject
expect(account_statuses_cleanup_policy.last_inspected).to eq 10
expect { subject }
.to change(account_statuses_cleanup_policy, :last_inspected).from(42).to(10)
end
end
end
@@ -179,8 +179,8 @@ RSpec.describe AccountStatusesCleanupPolicy do
end
it 'does not change the recorded id' do
subject
expect(account_statuses_cleanup_policy.last_inspected).to eq 42
expect { subject }
.to_not change(account_statuses_cleanup_policy, :last_inspected).from(42)
end
end
@@ -190,8 +190,8 @@ RSpec.describe AccountStatusesCleanupPolicy do
end
it 'records the older id' do
subject
expect(account_statuses_cleanup_policy.last_inspected).to eq 10
expect { subject }
.to change(account_statuses_cleanup_policy, :last_inspected).from(42).to(10)
end
end
end
@@ -205,8 +205,8 @@ RSpec.describe AccountStatusesCleanupPolicy do
end
it 'does not change the recorded id' do
subject
expect(account_statuses_cleanup_policy.last_inspected).to eq 42
expect { subject }
.to_not change(account_statuses_cleanup_policy, :last_inspected).from(42)
end
end
@@ -216,8 +216,8 @@ RSpec.describe AccountStatusesCleanupPolicy do
end
it 'records the older id' do
subject
expect(account_statuses_cleanup_policy.last_inspected).to eq 10
expect { subject }
.to change(account_statuses_cleanup_policy, :last_inspected).from(42).to(10)
end
end
end
@@ -227,8 +227,8 @@ RSpec.describe AccountStatusesCleanupPolicy do
let(:status) { Fabricate(:status, account: account) }
it 'does not change the recorded id' do
subject
expect(account_statuses_cleanup_policy.last_inspected).to eq 42
expect { subject }
.to_not change(account_statuses_cleanup_policy, :last_inspected).from(42)
end
end
end

View File

@@ -3,23 +3,14 @@
require 'rails_helper'
RSpec.describe CustomFilter do
include_examples 'Expireable'
describe 'Validations' do
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:context) }
it 'requires non-empty of context' do
record = described_class.new(context: [])
record.valid?
expect(record).to model_have_error_on_field(:context)
end
it 'requires valid context value' do
record = described_class.new(context: ['invalid'])
record.valid?
expect(record).to model_have_error_on_field(:context)
end
it { is_expected.to_not allow_values([], %w(invalid)).for(:context) }
it { is_expected.to allow_values(%w(home)).for(:context) }
end
describe 'Normalizations' do
@@ -27,4 +18,28 @@ RSpec.describe CustomFilter do
it { is_expected.to normalize(:context).from(['home', 'notifications', 'public ', '']).to(%w(home notifications public)) }
end
end
describe '#expires_in' do
subject { custom_filter.expires_in }
let(:custom_filter) { Fabricate.build(:custom_filter, expires_at: expires_at) }
context 'when expires_at is nil' do
let(:expires_at) { nil }
it { is_expected.to be_nil }
end
context 'when expires is beyond the end of the range' do
let(:expires_at) { described_class::EXPIRATION_DURATIONS.last.from_now + 2.days }
it { is_expected.to be_nil }
end
context 'when expires is before the start of the range' do
let(:expires_at) { described_class::EXPIRATION_DURATIONS.first.from_now - 10.minutes }
it { is_expected.to eq(described_class::EXPIRATION_DURATIONS.first) }
end
end
end

View File

@@ -61,10 +61,7 @@ RSpec.describe Form::Import do
let(:import_type) { 'following' }
let(:import_file) { 'boop.ogg' }
it 'has errors' do
# NOTE: not testing more specific error because we don't know the string to match
expect(subject).to model_have_error_on_field(:data)
end
it { is_expected.to_not allow_value(data).for(:data) }
end
context 'when importing more follows than allowed' do

View File

@@ -3,6 +3,8 @@
require 'rails_helper'
RSpec.describe Invite do
include_examples 'Expireable'
describe '#valid_for_use?' do
it 'returns true when there are no limitations' do
invite = Fabricate(:invite, max_uses: nil, expires_at: nil)

View File

@@ -3,6 +3,8 @@
require 'rails_helper'
RSpec.describe IpBlock do
include_examples 'Expireable'
describe 'Validations' do
subject { Fabricate.build :ip_block }

View File

@@ -3,8 +3,14 @@
require 'rails_helper'
RSpec.describe Mention do
describe 'validations' do
describe 'Associations' do
it { is_expected.to belong_to(:account).required }
it { is_expected.to belong_to(:status).required }
end
describe 'Validations' do
subject { Fabricate.build :mention }
it { is_expected.to validate_uniqueness_of(:account_id).scoped_to(:status_id) }
end
end

18
spec/models/mute_spec.rb Normal file
View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Mute do
include_examples 'Expireable'
describe 'Associations' do
it { is_expected.to belong_to(:account).required }
it { is_expected.to belong_to(:target_account).required }
end
describe 'Validations' do
subject { Fabricate.build :mute }
it { is_expected.to validate_uniqueness_of(:account_id).scoped_to(:target_account_id) }
end
end

View File

@@ -3,32 +3,7 @@
require 'rails_helper'
RSpec.describe Poll do
describe 'Scopes' do
let(:status) { Fabricate(:status) }
let(:attached_poll) { Fabricate(:poll, status: status) }
let(:not_attached_poll) do
Fabricate(:poll).tap do |poll|
poll.status = nil
poll.save(validate: false)
end
end
describe '.attached' do
it 'finds the correct records' do
results = described_class.attached
expect(results).to eq([attached_poll])
end
end
describe '.unattached' do
it 'finds the correct records' do
results = described_class.unattached
expect(results).to eq([not_attached_poll])
end
end
end
include_examples 'Expireable'
describe '#reset_votes!' do
let(:poll) { Fabricate :poll, cached_tallies: [2, 3], votes_count: 5, voters_count: 5 }

View File

@@ -105,35 +105,64 @@ RSpec.describe Report do
describe 'history' do
subject(:action_logs) { report.history }
let(:report) { Fabricate(:report, target_account_id: target_account.id, status_ids: [status.id], created_at: 3.days.ago, updated_at: 1.day.ago) }
let(:report) { Fabricate(:report, target_account_id: target_account.id, status_ids: [status.id]) }
let(:target_account) { Fabricate(:account) }
let(:status) { Fabricate(:status) }
let(:account_warning) { Fabricate(:account_warning, report_id: report.id) }
before do
Fabricate(:action_log, target_type: 'Report', account_id: target_account.id, target_id: report.id, created_at: 2.days.ago)
Fabricate(:action_log, target_type: 'Account', account_id: target_account.id, target_id: report.target_account_id, created_at: 2.days.ago)
Fabricate(:action_log, target_type: 'Status', account_id: target_account.id, target_id: status.id, created_at: 2.days.ago)
Fabricate(:action_log, target_type: 'AccountWarning', account_id: target_account.id, target_id: account_warning.id, created_at: 2.days.ago)
end
let!(:matched_type_account_warning) { Fabricate(:action_log, target_type: 'AccountWarning', target_id: account_warning.id) }
let!(:matched_type_account) { Fabricate(:action_log, target_type: 'Account', target_id: report.target_account_id) }
let!(:matched_type_report) { Fabricate(:action_log, target_type: 'Report', target_id: report.id) }
let!(:matched_type_status) { Fabricate(:action_log, target_type: 'Status', target_id: status.id) }
let!(:unmatched_type_account_warning) { Fabricate(:action_log, target_type: 'AccountWarning') }
let!(:unmatched_type_account) { Fabricate(:action_log, target_type: 'Account') }
let!(:unmatched_type_report) { Fabricate(:action_log, target_type: 'Report') }
let!(:unmatched_type_status) { Fabricate(:action_log, target_type: 'Status') }
it 'returns expected logs' do
expect(action_logs)
.to have_attributes(count: 4)
.and include(have_attributes(target_type: 'Account'))
.and include(have_attributes(target_type: 'AccountWarning'))
.and include(have_attributes(target_type: 'Report'))
.and include(have_attributes(target_type: 'Status'))
.and include(matched_type_account_warning)
.and include(matched_type_account)
.and include(matched_type_report)
.and include(matched_type_status)
.and not_include(unmatched_type_account_warning)
.and not_include(unmatched_type_account)
.and not_include(unmatched_type_report)
.and not_include(unmatched_type_status)
end
end
describe 'validations' do
describe '#unresolved_siblings?' do
subject { Fabricate :report }
context 'when the target account has other unresolved reports' do
before { Fabricate :report, action_taken_at: nil, target_account: subject.target_account }
it { is_expected.to be_unresolved_siblings }
end
context 'when the target account has a resolved report' do
before { Fabricate :report, action_taken_at: 3.days.ago, target_account: subject.target_account }
it { is_expected.to_not be_unresolved_siblings }
end
context 'when the target account has no other reports' do
before { described_class.where(target_account: subject.target_account).destroy_all }
it { is_expected.to_not be_unresolved_siblings }
end
end
describe 'Validations' do
let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') }
it 'is invalid if comment is longer than character limit and reporter is local' do
report = Fabricate.build(:report, comment: comment_over_limit)
expect(report.valid?).to be false
expect(report).to model_have_error_on_field(:comment)
report = Fabricate.build(:report)
expect(report).to_not allow_value(comment_over_limit).for(:comment)
end
it 'is valid if comment is longer than character limit and reporter is not local' do
@@ -142,16 +171,16 @@ RSpec.describe Report do
end
it 'is invalid if it references invalid rules' do
report = Fabricate.build(:report, category: :violation, rule_ids: [-1])
expect(report.valid?).to be false
expect(report).to model_have_error_on_field(:rule_ids)
report = Fabricate.build(:report, category: :violation)
expect(report).to_not allow_value([-1]).for(:rule_ids)
end
it 'is invalid if it references rules but category is not "violation"' do
rule = Fabricate(:rule)
report = Fabricate.build(:report, category: :spam, rule_ids: rule.id)
expect(report.valid?).to be false
expect(report).to model_have_error_on_field(:rule_ids)
report = Fabricate.build(:report, category: :spam)
expect(report).to_not allow_value(rule.id).for(:rule_ids)
end
def comment_over_limit

View File

@@ -5,7 +5,39 @@ require 'rails_helper'
RSpec.describe Tag do
include_examples 'Reviewable'
describe 'validations' do
describe 'Validations' do
describe 'name' do
context 'with a new record' do
subject { Fabricate.build :tag, name: 'original' }
it { is_expected.to allow_value('changed').for(:name) }
end
context 'with an existing record' do
subject { Fabricate :tag, name: 'original' }
it { is_expected.to_not allow_value('changed').for(:name).with_message(previous_name_error_message) }
end
end
describe 'display_name' do
context 'with a new record' do
subject { Fabricate.build :tag, name: 'original', display_name: 'OriginalDisplayName' }
it { is_expected.to allow_value('ChangedDisplayName').for(:display_name) }
end
context 'with an existing record' do
subject { Fabricate :tag, name: 'original', display_name: 'OriginalDisplayName' }
it { is_expected.to_not allow_value('ChangedDisplayName').for(:display_name).with_message(previous_name_error_message) }
end
end
def previous_name_error_message
I18n.t('tags.does_not_match_previous_name')
end
it 'invalid with #' do
expect(described_class.new(name: '#hello_world')).to_not be_valid
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe TagTrend do
include_examples 'RankedTrend'
describe 'Associations' do
it { is_expected.to belong_to(:tag).required }
end
end

View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe TermsOfService do
describe '#scope_for_notification' do
subject { terms_of_service.scope_for_notification }
let(:published_at) { Time.now.utc }
let(:terms_of_service) { Fabricate(:terms_of_service, published_at: published_at) }
let(:user_before) { Fabricate(:user, created_at: published_at - 2.days) }
let(:user_before_unconfirmed) { Fabricate(:user, created_at: published_at - 2.days, confirmed_at: nil) }
let(:user_before_suspended) { Fabricate(:user, created_at: published_at - 2.days) }
let(:user_after) { Fabricate(:user, created_at: published_at + 1.hour) }
before do
user_before_suspended.account.suspend!
user_before_unconfirmed
user_before
user_after
end
it 'includes only users created before the terms of service were published' do
expect(subject.pluck(:id)).to match_array(user_before.id)
end
end
end

View File

@@ -61,10 +61,10 @@ RSpec.describe Trends::Tags do
it 'decays scores' do
subject.refresh(yesterday + 12.hours)
original_score = subject.score(tag_ocs.id)
original_score = TagTrend.find_by(tag: tag_ocs).score
expect(original_score).to eq 144.0
subject.refresh(yesterday + 12.hours + subject.options[:max_score_halflife])
decayed_score = subject.score(tag_ocs.id)
decayed_score = TagTrend.find_by(tag: tag_ocs).score
expect(decayed_score).to be <= original_score / 2
end
end

View File

@@ -18,6 +18,14 @@ RSpec.describe UserRole do
end
end
describe 'position' do
subject { Fabricate.build :user_role }
let(:limit) { described_class::POSITION_LIMIT }
it { is_expected.to validate_numericality_of(:position).is_in(-limit..limit) }
end
describe 'color' do
it { is_expected.to allow_values('#112233', '#aabbcc', '').for(:color) }
it { is_expected.to_not allow_values('x', '112233445566', '#xxyyzz').for(:color) }

View File

@@ -0,0 +1,73 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe TermsOfServicePolicy do
subject { described_class }
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
let(:john) { Fabricate(:account) }
permissions :index?, :create? do
it { is_expected.to permit(admin, TermsOfService) }
it { is_expected.to_not permit(john, TermsOfService) }
end
permissions :update?, :destroy? do
let(:terms) { Fabricate(:terms_of_service, published_at: published) }
context 'with an unpublished terms' do
let(:published) { nil }
it { is_expected.to permit(admin, terms) }
it { is_expected.to_not permit(john, terms) }
end
context 'with a published terms' do
let(:published) { 5.days.ago }
it { is_expected.to_not permit(admin, terms) }
it { is_expected.to_not permit(john, terms) }
end
end
permissions :distribute? do
let(:terms) { Fabricate(:terms_of_service, published_at: published, notification_sent_at: notification) }
context 'with notification already sent' do
let(:notification) { 3.days.ago }
context 'with published true' do
let(:published) { 5.days.ago }
it { is_expected.to_not permit(admin, terms) }
it { is_expected.to_not permit(john, terms) }
end
context 'with published false' do
let(:published) { nil }
it { is_expected.to_not permit(admin, terms) }
it { is_expected.to_not permit(john, terms) }
end
end
context 'with notification not yet sent' do
let(:notification) { nil }
context 'with published true' do
let(:published) { 5.days.ago }
it { is_expected.to permit(admin, terms) }
it { is_expected.to_not permit(john, terms) }
end
context 'with published false' do
let(:published) { nil }
it { is_expected.to_not permit(admin, terms) }
it { is_expected.to_not permit(john, terms) }
end
end
end
end

View File

@@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Terms of Service' do
describe 'GET /api/v1/instance/terms_of_service' do
before do
Fabricate(:terms_of_service)
end
it 'returns http success' do
get api_v1_instance_terms_of_service_path
expect(response)
.to have_http_status(200)
expect(response.content_type)
.to start_with('application/json')
expect(response.parsed_body)
.to be_present
.and include(:content)
end
end
end

View File

@@ -55,6 +55,9 @@ RSpec.describe 'Instances' do
max_characters: StatusLengthValidator::MAX_CHARS,
max_media_attachments: Status::MEDIA_ATTACHMENTS_LIMIT
),
media_attachments: include(
description_limit: MediaAttachment::MAX_DESCRIPTION_LENGTH
),
polls: include(
max_options: PollValidator::MAX_OPTIONS
)

View File

@@ -10,8 +10,14 @@ RSpec.describe 'Custom CSS' do
it 'returns empty stylesheet' do
get '/custom.css'
expect(response.content_type).to include('text/css')
expect(response.body.presence).to be_nil
expect(response)
.to have_http_status(200)
.and have_cacheable_headers
.and have_attributes(
content_type: match('text/css')
)
expect(response.body.presence)
.to be_nil
end
end
@@ -23,8 +29,14 @@ RSpec.describe 'Custom CSS' do
it 'returns stylesheet from settings' do
get '/custom.css'
expect(response.content_type).to include('text/css')
expect(response.body.strip).to eq(expected_css)
expect(response)
.to have_http_status(200)
.and have_cacheable_headers
.and have_attributes(
content_type: match('text/css')
)
expect(response.body.strip)
.to eq(expected_css)
end
def expected_css
@@ -44,8 +56,14 @@ RSpec.describe 'Custom CSS' do
it 'returns stylesheet from settings' do
get '/custom.css'
expect(response.content_type).to include('text/css')
expect(response.body.strip).to eq(expected_css)
expect(response)
.to have_http_status(200)
.and have_cacheable_headers
.and have_attributes(
content_type: match('text/css')
)
expect(response.body.strip)
.to eq(expected_css)
end
def expected_css

View File

@@ -1,18 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Custom stylesheets' do
describe 'GET /custom.css' do
before { get '/custom.css' }
it 'returns http success' do
expect(response)
.to have_http_status(200)
.and have_cacheable_headers
.and have_attributes(
content_type: match('text/css')
)
end
end
end

View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Filters' do
describe 'GET /filters' do
context 'with signed out user' do
it 'redirects to sign in page' do
get filters_path
expect(response)
.to redirect_to(new_user_session_path)
end
end
end
end

View File

@@ -0,0 +1,218 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Status Activity' do
describe 'GET /users/:account_username/statuses/:id/activity' do
let(:account) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: account) }
context 'when signed out' do
subject { get activity_account_status_path(account.username, status) }
context 'when account is permanently suspended' do
before do
account.suspend!
account.deletion_request.destroy
end
it 'returns http gone' do
subject
expect(response)
.to have_http_status(410)
end
end
context 'when account is temporarily suspended' do
before { account.suspend! }
it 'returns http forbidden' do
subject
expect(response)
.to have_http_status(403)
end
end
context 'when status is public' do
before { status.update(visibility: :public) }
it 'returns http success' do
subject
expect(response)
.to have_http_status(:success)
expect(response.content_type)
.to start_with('application/activity+json')
end
end
context 'when status is private' do
before { status.update(visibility: :private) }
it 'returns http not_found' do
subject
expect(response)
.to have_http_status(404)
end
end
context 'when status is direct' do
before { status.update(visibility: :direct) }
it 'returns http not_found' do
subject
expect(response)
.to have_http_status(404)
end
end
end
context 'when signed in' do
subject { get activity_account_status_path(account.username, status) }
let(:user) { Fabricate(:user) }
before { sign_in(user) }
context 'when status is public' do
before { status.update(visibility: :public) }
it 'returns http success' do
subject
expect(response)
.to have_http_status(:success)
expect(response.content_type)
.to start_with('application/activity+json')
end
end
context 'when status is private' do
before { status.update(visibility: :private) }
context 'when user is authorized to see it' do
before { user.account.follow!(account) }
it 'returns http success' do
subject
expect(response)
.to have_http_status(200)
expect(response.content_type)
.to start_with('application/activity+json')
end
end
context 'when user is not authorized to see it' do
it 'returns http not_found' do
subject
expect(response)
.to have_http_status(404)
end
end
end
context 'when status is direct' do
before { status.update(visibility: :direct) }
context 'when user is authorized to see it' do
before { Fabricate(:mention, account: user.account, status: status) }
it 'returns http success' do
subject
expect(response)
.to have_http_status(200)
expect(response.content_type)
.to start_with('application/activity+json')
end
end
context 'when user is not authorized to see it' do
it 'returns http not_found' do
subject
expect(response)
.to have_http_status(404)
end
end
end
end
context 'with signature' do
subject { get activity_account_status_path(account.username, status), headers: nil, sign_with: remote_account }
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
context 'when status is public' do
before { status.update(visibility: :public) }
it 'returns http success' do
subject
expect(response)
.to have_http_status(:success)
expect(response.content_type)
.to start_with('application/activity+json')
end
end
context 'when status is private' do
before { status.update(visibility: :private) }
context 'when user is authorized to see it' do
before { remote_account.follow!(account) }
it 'returns http success' do
subject
expect(response)
.to have_http_status(200)
expect(response.content_type)
.to start_with('application/activity+json')
end
end
context 'when user is not authorized to see it' do
it 'returns http not_found' do
subject
expect(response)
.to have_http_status(404)
end
end
end
context 'when status is direct' do
before { status.update(visibility: :direct) }
context 'when user is authorized to see it' do
before { Fabricate(:mention, account: remote_account, status: status) }
it 'returns http success' do
subject
expect(response)
.to have_http_status(200)
expect(response.content_type)
.to start_with('application/activity+json')
end
end
context 'when user is not authorized to see it' do
it 'returns http not_found' do
subject
expect(response)
.to have_http_status(404)
end
end
end
end
end
end

View File

@@ -7,6 +7,29 @@ RSpec.describe 'Tags' do
context 'when tag exists' do
let(:tag) { Fabricate :tag }
context 'with HTML format' do
before { get tag_path(tag) }
it 'returns page with links to alternate resources' do
expect(rss_links.first[:href])
.to eq(tag_url(tag))
expect(activity_json_links.first[:href])
.to eq(tag_url(tag))
end
def rss_links
alternate_links.css('[type="application/rss+xml"]')
end
def activity_json_links
alternate_links.css('[type="application/activity+json"]')
end
def alternate_links
response.parsed_body.css('link[rel=alternate]')
end
end
context 'with JSON format' do
before { get tag_path(tag, format: :json) }

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::AcceptFollowSerializer do
subject { serialized_record_json(record, described_class, adapter: ActivityPub::Adapter) }
describe 'serializing an object' do
let(:record) { Fabricate :follow_request }
it 'returns expected attributes' do
expect(subject.deep_symbolize_keys)
.to include(
actor: eq(ActivityPub::TagManager.instance.uri_for(record.target_account)),
id: match("#accepts/follows/#{record.id}"),
object: include(type: 'Follow'),
type: 'Accept'
)
end
end
end

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::RejectFollowSerializer do
subject { serialized_record_json(record, described_class, adapter: ActivityPub::Adapter) }
describe 'serializing an object' do
let(:record) { Fabricate :follow_request }
it 'returns expected attributes' do
expect(subject.deep_symbolize_keys)
.to include(
actor: eq(ActivityPub::TagManager.instance.uri_for(record.target_account)),
id: match("#rejects/follows/#{record.id}"),
object: include(type: 'Follow'),
type: 'Reject'
)
end
end
end

View File

@@ -5,13 +5,14 @@ require 'rails_helper'
RSpec.describe REST::AccountRelationshipSeveranceEventSerializer do
subject { serialized_record_json(record, described_class) }
let(:record) { Fabricate.build :account_relationship_severance_event, id: 123 }
let(:record) { Fabricate.build :account_relationship_severance_event, id: 123, created_at: DateTime.new(2024, 11, 28, 16, 20, 0) }
describe 'serialization' do
it 'returns expected values' do
expect(subject)
.to include(
'id' => be_a(String).and(eq('123'))
'id' => be_a(String).and(eq('123')),
'created_at' => match_api_datetime_format
)
end
end

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::AccountSerializer::FieldSerializer do
subject { serialized_record_json(field, described_class) }
let(:default_datetime) { DateTime.new(2024, 11, 28, 16, 20, 0) }
let(:account) { Fabricate.build :account }
context 'when verified_at is populated' do
let(:field) { Account::Field.new(account, 'name' => 'Foo', 'value' => 'Bar', 'verified_at' => default_datetime) }
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'verified_at' => match_api_datetime_format
)
end
end
end

View File

@@ -5,6 +5,7 @@ require 'rails_helper'
RSpec.describe REST::AccountSerializer do
subject { serialized_record_json(account, described_class) }
let(:default_datetime) { DateTime.new(2024, 11, 28, 16, 20, 0) }
let(:role) { Fabricate(:user_role, name: 'Role', highlighted: true) }
let(:user) { Fabricate(:user, role: role) }
let(:account) { user.account }
@@ -44,4 +45,27 @@ RSpec.describe REST::AccountSerializer do
expect(subject['memorial']).to be true
end
end
context 'when created_at is populated' do
before do
account.account_stat.update!(created_at: default_datetime)
end
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format
)
end
end
context 'when last_status_at is populated' do
before do
account.account_stat.update!(last_status_at: default_datetime)
end
it 'is serialized as yyyy-mm-dd' do
expect(subject['last_status_at']).to eq('2024-11-28')
end
end
end

View File

@@ -12,7 +12,8 @@ RSpec.describe REST::AccountWarningSerializer do
expect(subject)
.to include(
'id' => be_a(String).and(eq('123')),
'status_ids' => be_a(Array).and(eq(['456', '789']))
'status_ids' => be_a(Array).and(eq(['456', '789'])),
'created_at' => match_api_datetime_format
)
end
end

View File

@@ -5,6 +5,17 @@ require 'rails_helper'
RSpec.describe REST::Admin::AccountSerializer do
subject { serialized_record_json(record, described_class) }
context 'when created_at is populated' do
let(:record) { Fabricate :account, user: Fabricate(:user) }
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format
)
end
end
describe 'created_by_application_id' do
context 'when account is application-created' do
let(:record) { Fabricate :account, user: Fabricate(:user, created_by_application: application) }

View File

@@ -11,8 +11,10 @@ RSpec.describe REST::Admin::CohortSerializer do
it 'returns expected values' do
expect(subject)
.to include(
'data' => be_a(Array),
'period' => /2024-01-01/
'data' => be_a(Array).and(
all(include('date' => match_api_datetime_format))
),
'period' => match(/2024-01-01/).and(match_api_datetime_format)
)
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::Admin::DomainAllowSerializer do
subject { serialized_record_json(record, described_class) }
let(:record) { Fabricate(:domain_allow) }
context 'when created_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::Admin::DomainBlockSerializer do
subject { serialized_record_json(record, described_class) }
let(:record) { Fabricate(:domain_block) }
context 'when created_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::Admin::EmailDomainBlockSerializer do
subject { serialized_record_json(record, described_class) }
let(:record) { Fabricate(:email_domain_block) }
context 'when created_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::Admin::IpBlockSerializer do
subject { serialized_record_json(record, described_class) }
context 'when timestamps are populated' do
let(:record) { Fabricate(:ip_block, expires_at: DateTime.new(2024, 11, 28, 16, 20, 0)) }
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format,
'expires_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::Admin::IpSerializer do
subject { serialized_record_json(record, described_class) }
let(:record) { UserIp.new(used_at: 3.days.ago) }
context 'when used_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'used_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::Admin::MeasureSerializer do
subject { serialized_record_json(record, described_class) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
let(:params) { ActionController::Parameters.new({ instance_accounts: [123] }) }
let(:record) { Admin::Metrics::Measure::ActiveUsersMeasure.new(start_at, end_at, params) }
context 'when start_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'data' => all(
include('date' => match_api_datetime_format)
)
)
end
end
end

View File

@@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::Admin::ReportSerializer do
subject { serialized_record_json(report, described_class) }
context 'with timestamps' do
let(:report) { Fabricate(:report, action_taken_at: 3.days.ago) }
it 'is serialized as RFC 3339 datetime' do
expect(subject)
.to include(
'action_taken_at' => match_api_datetime_format,
'created_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,31 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::AnnouncementSerializer do
subject do
serialized_record_json(
announcement,
described_class,
options: {
scope: current_user,
scope_name: :current_user,
}
)
end
let(:current_user) { Fabricate(:user) }
let(:announcement) { Fabricate(:announcement, starts_at: 10.days.ago, published_at: 10.days.ago, ends_at: 5.days.from_now) }
context 'when date fields are populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'starts_at' => match_api_datetime_format,
'ends_at' => match_api_datetime_format,
'published_at' => match_api_datetime_format,
'updated_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::AnnualReportEventSerializer do
subject { serialized_record_json(record, described_class) }
describe 'serializing an object' do
let(:record) { Fabricate.build :generated_annual_report, year: 2024 }
it 'returns expected attributes' do
expect(subject.deep_symbolize_keys)
.to include(
year: '2024'
)
end
end
end

View File

@@ -5,9 +5,11 @@ require 'rails_helper'
RSpec.describe REST::ExtendedDescriptionSerializer do
subject { serialized_record_json(record, described_class) }
let(:default_datetime) { DateTime.new(2024, 11, 28, 16, 20, 0) }
describe 'serialization' do
context 'with text present' do
let(:record) { ExtendedDescription.new text: 'Hello world', updated_at: Date.new(2024, 1, 1) }
let(:record) { ExtendedDescription.new text: 'Hello world', updated_at: default_datetime }
it 'returns expected values' do
expect(subject)
@@ -15,19 +17,19 @@ RSpec.describe REST::ExtendedDescriptionSerializer do
'content' => eq(<<~HTML),
<p>Hello world</p>
HTML
'updated_at' => eq('2024-01-01')
'updated_at' => eq('2024-11-28T16:20:00+00:00')
)
end
end
context 'with text missing' do
let(:record) { ExtendedDescription.new text: nil, updated_at: Date.new(2024, 1, 1) }
let(:record) { ExtendedDescription.new text: nil, updated_at: default_datetime }
it 'returns expected values' do
expect(subject)
.to include(
'content' => eq(''),
'updated_at' => eq('2024-01-01')
'updated_at' => eq('2024-11-28T16:20:00+00:00')
)
end
end

View File

@@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::FeaturedTagSerializer do
subject do
serialized_record_json(
featured_tag,
described_class
)
end
let(:featured_tag) { Fabricate :featured_tag }
context 'when last_status_at is populated' do
before do
featured_tag.increment(DateTime.new(2024, 11, 28, 16, 20, 0))
end
it 'is serialized as yyyy-mm-dd' do
expect(subject['last_status_at']).to eq('2024-11-28')
end
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::FilterSerializer do
subject do
serialized_record_json(
filter,
described_class
)
end
let(:filter) { Fabricate.build :custom_filter, expires_at: DateTime.new(2024, 11, 28, 16, 20, 0) }
context 'when expires_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'expires_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::MarkerSerializer do
subject do
serialized_record_json(
marker,
described_class
)
end
let(:marker) { Fabricate :marker }
context 'when updated_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'updated_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,45 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::MutedAccountSerializer do
subject do
serialized_record_json(
mutee,
described_class,
options: {
scope: current_user,
scope_name: :current_user,
}
)
end
let(:current_user) { Fabricate(:user) }
let(:account) { current_user.account }
let(:mutee) { Fabricate(:account, username: 'mutee') }
context 'when mute_expires_at is populated and non-nil' do
before do
account.follow!(mutee)
account.mute!(mutee, duration: 1.day)
end
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'mute_expires_at' => match_api_datetime_format
)
end
end
context 'when mute has no duration' do
before do
account.follow!(mutee)
account.mute!(mutee)
end
it 'has a nil mute_expires_at' do
expect(subject['mute_expires_at']).to be_nil
end
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::NotificationGroupSerializer do
subject do
serialized_record_json(
notification_group,
described_class
)
end
let(:notification_group) { NotificationGroup.new pagination_data: { latest_notification_at: 3.days.ago }, notification: Fabricate(:notification), sample_accounts: [] }
context 'when latest_page_notification_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'latest_page_notification_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,29 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::NotificationRequestSerializer do
subject do
serialized_record_json(
notification_request,
described_class,
options: {
scope: current_user,
scope_name: :current_user,
}
)
end
let(:current_user) { Fabricate(:user) }
let(:notification_request) { Fabricate :notification_request }
context 'when timestampts are populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format,
'updated_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::NotificationSerializer do
subject do
serialized_record_json(
notification,
described_class
)
end
let(:notification) { Fabricate :notification }
context 'when created_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,28 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::PollSerializer do
subject do
serialized_record_json(
poll,
described_class,
options: {
scope: current_user,
scope_name: :current_user,
}
)
end
let(:current_user) { Fabricate(:user) }
let(:poll) { Fabricate.build :poll, expires_at: 5.days.from_now }
context 'when expires_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'expires_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::PreviewCardSerializer do
subject do
serialized_record_json(
preview_card,
described_class
)
end
context 'when preview card does not have author data' do
let(:preview_card) { Fabricate.build :preview_card }
it 'includes empty authors array' do
expect(subject.deep_symbolize_keys)
.to include(
authors: be_an(Array).and(be_empty)
)
end
end
context 'when preview card has author data' do
let(:preview_card) { Fabricate.build :preview_card, author_name: 'Name', author_url: 'https://host.example/123' }
it 'includes populated authors array' do
expect(subject.deep_symbolize_keys)
.to include(
authors: be_an(Array).and(
contain_exactly(
include(
name: 'Name',
url: 'https://host.example/123'
)
)
)
)
end
end
end

View File

@@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::ReportSerializer do
subject do
serialized_record_json(
report,
described_class
)
end
context 'with timestamps' do
let(:report) { Fabricate(:report, action_taken_at: 3.days.ago) }
it 'is serialized as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format,
'action_taken_at' => match_api_datetime_format
)
end
end
end

View File

@@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::ScheduledStatusSerializer do
subject do
serialized_record_json(
scheduled_status,
described_class
)
end
let(:scheduled_status) { Fabricate.build(:scheduled_status, scheduled_at: 4.minutes.from_now, params: { application_id: 123 }) }
describe 'serialization' do
it 'returns expected values and removes application_id from params' do
expect(subject.deep_symbolize_keys)
.to include(
scheduled_at: be_a(String).and(match_api_datetime_format),
params: not_include(:application_id)
)
end
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe REST::StatusEditSerializer do
subject do
serialized_record_json(
status_edit,
described_class
)
end
let(:status_edit) { Fabricate(:status_edit) }
context 'when created_at is populated' do
it 'parses as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format
)
end
end
end

View File

@@ -51,5 +51,25 @@ RSpec.describe REST::StatusSerializer do
expect(subject['favourites_count']).to eq(40)
end
end
context 'with created_at' do
it 'is serialized as RFC 3339 datetime' do
expect(subject)
.to include(
'created_at' => match_api_datetime_format
)
end
end
context 'when edited_at is populated' do
let(:status) { Fabricate.build :status, edited_at: 3.days.ago }
it 'is serialized as RFC 3339 datetime' do
expect(subject)
.to include(
'edited_at' => match_api_datetime_format
)
end
end
end
end

View File

@@ -6,6 +6,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
subject { described_class.new }
let!(:status) { Fabricate(:status, text: 'Hello world', account: Fabricate(:account, domain: 'example.com')) }
let(:bogus_mention) { 'https://example.com/users/erroringuser' }
let(:payload) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
@@ -17,6 +18,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
tag: [
{ type: 'Hashtag', name: 'hoge' },
{ type: 'Mention', href: ActivityPub::TagManager.instance.uri_for(alice) },
{ type: 'Mention', href: bogus_mention },
],
}
end
@@ -30,19 +32,21 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:media_attachments) { [] }
before do
mentions.each { |a| Fabricate(:mention, status: status, account: a) }
mentions.each { |(account, silent)| Fabricate(:mention, status: status, account: account, silent: silent) }
tags.each { |t| status.tags << t }
media_attachments.each { |m| status.media_attachments << m }
stub_request(:get, bogus_mention).to_raise(HTTP::ConnectionError)
end
describe '#call' do
it 'updates text and content warning' do
it 'updates text and content warning, and schedules re-fetching broken mention' do
subject.call(status, json, json)
expect(status.reload)
.to have_attributes(
text: eq('Hello universe'),
spoiler_text: eq('Show more')
)
expect(MentionResolveWorker).to have_enqueued_sidekiq_job(status.id, bogus_mention, anything)
end
context 'when the changes are only in sanitized-out HTML' do
@@ -252,16 +256,22 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
updated: '2021-09-08T22:39:25Z',
tag: [
{ type: 'Hashtag', name: 'foo' },
{ type: 'Hashtag', name: 'bar' },
],
}
end
before do
subject.call(status, json, json)
status.account.featured_tags.create!(name: 'bar')
status.account.featured_tags.create!(name: 'test')
end
it 'updates tags' do
expect(status.tags.reload.map(&:name)).to eq %w(foo)
it 'updates tags and featured tags' do
expect { subject.call(status, json, json) }
.to change { status.tags.reload.pluck(:name) }.from(%w(test foo)).to(%w(foo bar))
.and change { status.account.featured_tags.find_by(name: 'test').statuses_count }.by(-1)
.and change { status.account.featured_tags.find_by(name: 'bar').statuses_count }.by(1)
.and change { status.account.featured_tags.find_by(name: 'bar').last_status_at }.from(nil).to(be_within(0.1).of(Time.now.utc))
end
end
@@ -276,7 +286,19 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
context 'when originally with mentions' do
let(:mentions) { [alice, bob] }
let(:mentions) { [[alice, false], [bob, false]] }
before do
subject.call(status, json, json)
end
it 'updates mentions' do
expect(status.active_mentions.reload.map(&:account_id)).to eq [alice.id]
end
end
context 'when originally with silent mentions' do
let(:mentions) { [[alice, true], [bob, true]] }
before do
subject.call(status, json, json)

View File

@@ -70,7 +70,7 @@ RSpec.describe PostStatusService do
subject.call(account, text: 'Hi future!', scheduled_at: invalid_scheduled_time)
end.to raise_error(
ActiveRecord::RecordInvalid,
'Validation failed: Scheduled at The scheduled date must be in the future'
'Validation failed: Scheduled at date must be in the future'
)
end
end

View File

@@ -46,7 +46,7 @@ RSpec.describe ReblogService do
Status
.where(id: reblog_of_id)
.where(text: 'discard-status-text')
.update_all(deleted_at: Time.now.utc)
.touch_all(:deleted_at)
end
end
end

View File

@@ -150,6 +150,14 @@ RSpec.describe UpdateStatusService do
.to eq [bob.id]
expect(status.mentions.pluck(:account_id))
.to contain_exactly(alice.id, bob.id)
# Going back when a mention was switched to silence should still be possible
subject.call(status, status.account_id, text: 'Hello @alice')
expect(status.active_mentions.pluck(:account_id))
.to eq [alice.id]
expect(status.mentions.pluck(:account_id))
.to contain_exactly(alice.id, bob.id)
end
end

View File

@@ -0,0 +1,110 @@
# frozen_string_literal: true
RSpec.shared_examples 'Expireable' do
subject { described_class.new(expires_at: expires_at) }
let(:expires_at) { nil }
describe 'Scopes' do
let!(:expired_record) do
travel_to 2.days.ago do
Fabricate factory_name, expires_at: 1.day.from_now
end
end
let!(:un_expired_record) { Fabricate factory_name, expires_at: 10.days.from_now }
describe '.expired' do
it 'returns expired records' do
expect(described_class.expired)
.to include(expired_record)
.and not_include(un_expired_record)
end
end
end
describe '#expires_in' do
context 'when expires at is nil' do
let(:expires_at) { nil }
it 'returns nil' do
expect(subject.expires_in)
.to be_nil
end
end
end
describe '#expires_in=' do
let(:record) { Fabricate.build factory_name }
context 'when set to nil' do
it 'sets expires_at to nil' do
record.expires_in = nil
expect(record.expires_at)
.to be_nil
end
end
context 'when set to empty' do
it 'sets expires_at to nil' do
record.expires_in = ''
expect(record.expires_at)
.to be_nil
end
end
context 'when set to a value' do
it 'sets expires_at to expected future time' do
record.expires_in = 60
expect(record.expires_at)
.to be_within(0.1).of(60.seconds.from_now)
end
end
end
describe '#expired?' do
context 'when expires_at is nil' do
let(:expires_at) { nil }
it { is_expected.to_not be_expired }
end
context 'when expires_at is in the past' do
let(:expires_at) { 5.days.ago }
it { is_expected.to be_expired }
end
context 'when expires_at is in the future' do
let(:expires_at) { 5.days.from_now }
it { is_expected.to_not be_expired }
end
describe '#expire!' do
subject { Fabricate factory_name }
it 'updates the timestamp' do
expect { subject.expire! }
.to change(subject, :expires_at)
end
end
describe '#expires?' do
context 'when value is missing' do
let(:expires_at) { nil }
it { is_expected.to_not be_expires }
end
context 'when value is present' do
let(:expires_at) { 3.days.from_now }
it { is_expected.to be_expires }
end
end
end
def factory_name
described_class.name.underscore.to_sym
end
end

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
RSpec::Matchers.define :match_api_datetime_format do
match(notify_expectation_failures: true) do |value|
expect { DateTime.rfc3339(value) }
.to_not raise_error
end
end

Some files were not shown because too many files have changed in this diff Show More