commit
65cbcce997
76 changed files with 2122 additions and 1497 deletions
@ -0,0 +1,41 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
class MailSubscriptionsController < ApplicationController |
||||||
|
layout 'auth' |
||||||
|
|
||||||
|
skip_before_action :require_functional! |
||||||
|
|
||||||
|
before_action :set_body_classes |
||||||
|
before_action :set_user |
||||||
|
before_action :set_type |
||||||
|
|
||||||
|
def show; end |
||||||
|
|
||||||
|
def create |
||||||
|
@user.settings[email_type_from_param] = false |
||||||
|
@user.save! |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def set_user |
||||||
|
@user = GlobalID::Locator.locate_signed(params[:token], for: 'unsubscribe') |
||||||
|
end |
||||||
|
|
||||||
|
def set_body_classes |
||||||
|
@body_classes = 'lighter' |
||||||
|
end |
||||||
|
|
||||||
|
def set_type |
||||||
|
@type = email_type_from_param |
||||||
|
end |
||||||
|
|
||||||
|
def email_type_from_param |
||||||
|
case params[:type] |
||||||
|
when 'follow', 'reblog', 'favourite', 'mention', 'follow_request' |
||||||
|
"notification_emails.#{params[:type]}" |
||||||
|
else |
||||||
|
raise ArgumentError |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,27 @@ |
|||||||
|
interface Props { |
||||||
|
size: number; |
||||||
|
strokeWidth: number; |
||||||
|
} |
||||||
|
|
||||||
|
export const CircularProgress: React.FC<Props> = ({ size, strokeWidth }) => { |
||||||
|
const viewBox = `0 0 ${size} ${size}`; |
||||||
|
const radius = (size - strokeWidth) / 2; |
||||||
|
|
||||||
|
return ( |
||||||
|
<svg |
||||||
|
width={size} |
||||||
|
height={size} |
||||||
|
viewBox={viewBox} |
||||||
|
className='circular-progress' |
||||||
|
role='progressbar' |
||||||
|
> |
||||||
|
<circle |
||||||
|
fill='none' |
||||||
|
cx={size / 2} |
||||||
|
cy={size / 2} |
||||||
|
r={radius} |
||||||
|
strokeWidth={`${strokeWidth}px`} |
||||||
|
/> |
||||||
|
</svg> |
||||||
|
); |
||||||
|
}; |
@ -1,23 +0,0 @@ |
|||||||
import PropTypes from 'prop-types'; |
|
||||||
import { PureComponent } from 'react'; |
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl'; |
|
||||||
|
|
||||||
export default class LoadPending extends PureComponent { |
|
||||||
|
|
||||||
static propTypes = { |
|
||||||
onClick: PropTypes.func, |
|
||||||
count: PropTypes.number, |
|
||||||
}; |
|
||||||
|
|
||||||
render() { |
|
||||||
const { count } = this.props; |
|
||||||
|
|
||||||
return ( |
|
||||||
<button className='load-more load-gap' onClick={this.props.onClick}> |
|
||||||
<FormattedMessage id='load_pending' defaultMessage='{count, plural, one {# new item} other {# new items}}' values={{ count }} /> |
|
||||||
</button> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,18 @@ |
|||||||
|
import { FormattedMessage } from 'react-intl'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
onClick: (event: React.MouseEvent) => void; |
||||||
|
count: number; |
||||||
|
} |
||||||
|
|
||||||
|
export const LoadPending: React.FC<Props> = ({ onClick, count }) => { |
||||||
|
return ( |
||||||
|
<button className='load-more load-gap' onClick={onClick}> |
||||||
|
<FormattedMessage |
||||||
|
id='load_pending' |
||||||
|
defaultMessage='{count, plural, one {# new item} other {# new items}}' |
||||||
|
values={{ count }} |
||||||
|
/> |
||||||
|
</button> |
||||||
|
); |
||||||
|
}; |
@ -1,31 +0,0 @@ |
|||||||
import PropTypes from 'prop-types'; |
|
||||||
|
|
||||||
export const CircularProgress = ({ size, strokeWidth }) => { |
|
||||||
const viewBox = `0 0 ${size} ${size}`; |
|
||||||
const radius = (size - strokeWidth) / 2; |
|
||||||
|
|
||||||
return ( |
|
||||||
<svg width={size} height={size} viewBox={viewBox} className='circular-progress' role='progressbar'> |
|
||||||
<circle |
|
||||||
fill='none' |
|
||||||
cx={size / 2} |
|
||||||
cy={size / 2} |
|
||||||
r={radius} |
|
||||||
strokeWidth={`${strokeWidth}px`} |
|
||||||
/> |
|
||||||
</svg> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
CircularProgress.propTypes = { |
|
||||||
size: PropTypes.number.isRequired, |
|
||||||
strokeWidth: PropTypes.number.isRequired, |
|
||||||
}; |
|
||||||
|
|
||||||
const LoadingIndicator = () => ( |
|
||||||
<div className='loading-indicator'> |
|
||||||
<CircularProgress size={50} strokeWidth={6} /> |
|
||||||
</div> |
|
||||||
); |
|
||||||
|
|
||||||
export default LoadingIndicator; |
|
@ -0,0 +1,7 @@ |
|||||||
|
import { CircularProgress } from './circular_progress'; |
||||||
|
|
||||||
|
export const LoadingIndicator: React.FC = () => ( |
||||||
|
<div className='loading-indicator'> |
||||||
|
<CircularProgress size={50} strokeWidth={6} /> |
||||||
|
</div> |
||||||
|
); |
@ -0,0 +1,51 @@ |
|||||||
|
import type { BaseEmoji, EmojiData, NimbleEmojiIndex } from 'emoji-mart'; |
||||||
|
import type { Category, Data, Emoji } from 'emoji-mart/dist-es/utils/data'; |
||||||
|
|
||||||
|
/* |
||||||
|
* The 'search' property, although not defined in the [`Emoji`]{@link node_modules/@types/emoji-mart/dist-es/utils/data.d.ts#Emoji} type, |
||||||
|
* is used in the application. |
||||||
|
* This could be due to an oversight by the library maintainer. |
||||||
|
* The `search` property is defined and used [here]{@link node_modules/emoji-mart/dist/utils/data.js#uncompress}. |
||||||
|
*/ |
||||||
|
export type Search = string; |
||||||
|
/* |
||||||
|
* The 'skins' property does not exist in the application data. |
||||||
|
* This could be a potential area of refactoring or error handling. |
||||||
|
* The non-existence of 'skins' property is evident at [this location]{@link app/javascript/mastodon/features/emoji/emoji_compressed.js:121}. |
||||||
|
*/ |
||||||
|
export type Skins = null; |
||||||
|
|
||||||
|
export type FilenameData = string[] | string[][]; |
||||||
|
export type ShortCodesToEmojiDataKey = |
||||||
|
| EmojiData['id'] |
||||||
|
| BaseEmoji['native'] |
||||||
|
| keyof NimbleEmojiIndex['emojis']; |
||||||
|
|
||||||
|
export type SearchData = [ |
||||||
|
BaseEmoji['native'], |
||||||
|
Emoji['short_names'], |
||||||
|
Search, |
||||||
|
Emoji['unified'] |
||||||
|
]; |
||||||
|
|
||||||
|
export interface ShortCodesToEmojiData { |
||||||
|
[key: ShortCodesToEmojiDataKey]: [FilenameData, SearchData]; |
||||||
|
} |
||||||
|
export type EmojisWithoutShortCodes = FilenameData[]; |
||||||
|
|
||||||
|
export type EmojiCompressed = [ |
||||||
|
ShortCodesToEmojiData, |
||||||
|
Skins, |
||||||
|
Category[], |
||||||
|
Data['aliases'], |
||||||
|
EmojisWithoutShortCodes |
||||||
|
]; |
||||||
|
|
||||||
|
/* |
||||||
|
* `emoji_compressed.js` uses `babel-plugin-preval`, which makes it difficult to convert to TypeScript. |
||||||
|
* As a temporary solution, we are allowing a default export here to apply the TypeScript type `EmojiCompressed` to the JS file export. |
||||||
|
* - {@link app/javascript/mastodon/features/emoji/emoji_compressed.js} |
||||||
|
*/ |
||||||
|
declare const emojiCompressed: EmojiCompressed; |
||||||
|
|
||||||
|
export default emojiCompressed; // eslint-disable-line import/no-default-export
|
@ -1,43 +0,0 @@ |
|||||||
// The output of this module is designed to mimic emoji-mart's
|
|
||||||
// "data" object, such that we can use it for a light version of emoji-mart's
|
|
||||||
// emojiIndex.search functionality.
|
|
||||||
import emojiCompressed from './emoji_compressed'; |
|
||||||
import { unicodeToUnifiedName } from './unicode_to_unified_name'; |
|
||||||
|
|
||||||
const [ shortCodesToEmojiData, skins, categories, short_names ] = emojiCompressed; |
|
||||||
|
|
||||||
const emojis = {}; |
|
||||||
|
|
||||||
// decompress
|
|
||||||
Object.keys(shortCodesToEmojiData).forEach((shortCode) => { |
|
||||||
let [ |
|
||||||
filenameData, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
||||||
searchData, |
|
||||||
] = shortCodesToEmojiData[shortCode]; |
|
||||||
let [ |
|
||||||
native, |
|
||||||
short_names, |
|
||||||
search, |
|
||||||
unified, |
|
||||||
] = searchData; |
|
||||||
|
|
||||||
if (!unified) { |
|
||||||
// unified name can be derived from unicodeToUnifiedName
|
|
||||||
unified = unicodeToUnifiedName(native); |
|
||||||
} |
|
||||||
|
|
||||||
short_names = [shortCode].concat(short_names); |
|
||||||
emojis[shortCode] = { |
|
||||||
native, |
|
||||||
search, |
|
||||||
short_names, |
|
||||||
unified, |
|
||||||
}; |
|
||||||
}); |
|
||||||
|
|
||||||
export { |
|
||||||
emojis, |
|
||||||
skins, |
|
||||||
categories, |
|
||||||
short_names, |
|
||||||
}; |
|
@ -0,0 +1,52 @@ |
|||||||
|
// The output of this module is designed to mimic emoji-mart's
|
||||||
|
// "data" object, such that we can use it for a light version of emoji-mart's
|
||||||
|
// emojiIndex.search functionality.
|
||||||
|
import type { BaseEmoji } from 'emoji-mart'; |
||||||
|
import type { Emoji } from 'emoji-mart/dist-es/utils/data'; |
||||||
|
|
||||||
|
import type { Search, ShortCodesToEmojiData } from './emoji_compressed'; |
||||||
|
import emojiCompressed from './emoji_compressed'; |
||||||
|
import { unicodeToUnifiedName } from './unicode_to_unified_name'; |
||||||
|
|
||||||
|
type Emojis = { |
||||||
|
[key in keyof ShortCodesToEmojiData]: { |
||||||
|
native: BaseEmoji['native']; |
||||||
|
search: Search; |
||||||
|
short_names: Emoji['short_names']; |
||||||
|
unified: Emoji['unified']; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
const [ |
||||||
|
shortCodesToEmojiData, |
||||||
|
skins, |
||||||
|
categories, |
||||||
|
short_names, |
||||||
|
_emojisWithoutShortCodes, |
||||||
|
] = emojiCompressed; |
||||||
|
|
||||||
|
const emojis: Emojis = {}; |
||||||
|
|
||||||
|
// decompress
|
||||||
|
Object.keys(shortCodesToEmojiData).forEach((shortCode) => { |
||||||
|
const [_filenameData, searchData] = shortCodesToEmojiData[shortCode]; |
||||||
|
const native = searchData[0]; |
||||||
|
let short_names = searchData[1]; |
||||||
|
const search = searchData[2]; |
||||||
|
let unified = searchData[3]; |
||||||
|
|
||||||
|
if (!unified) { |
||||||
|
// unified name can be derived from unicodeToUnifiedName
|
||||||
|
unified = unicodeToUnifiedName(native); |
||||||
|
} |
||||||
|
|
||||||
|
if (short_names) short_names = [shortCode].concat(short_names); |
||||||
|
emojis[shortCode] = { |
||||||
|
native, |
||||||
|
search, |
||||||
|
short_names, |
||||||
|
unified, |
||||||
|
}; |
||||||
|
}); |
||||||
|
|
||||||
|
export { emojis, skins, categories, short_names }; |
@ -0,0 +1,9 @@ |
|||||||
|
- content_for :page_title do |
||||||
|
= t('mail_subscriptions.unsubscribe.title') |
||||||
|
|
||||||
|
.simple_form |
||||||
|
%h1.title= t('mail_subscriptions.unsubscribe.complete') |
||||||
|
%p.lead |
||||||
|
= t('mail_subscriptions.unsubscribe.success_html', domain: content_tag(:strong, site_hostname), type: content_tag(:strong, I18n.t(@type, scope: 'mail_subscriptions.unsubscribe.emails')), email: content_tag(:strong, @user.email)) |
||||||
|
%p.lead |
||||||
|
= t('mail_subscriptions.unsubscribe.resubscribe_html', settings_path: settings_preferences_notifications_path) |
@ -0,0 +1,12 @@ |
|||||||
|
- content_for :page_title do |
||||||
|
= t('mail_subscriptions.unsubscribe.title') |
||||||
|
|
||||||
|
.simple_form |
||||||
|
%h1.title= t('mail_subscriptions.unsubscribe.title') |
||||||
|
%p.lead |
||||||
|
= t('mail_subscriptions.unsubscribe.confirmation_html', domain: content_tag(:strong, site_hostname), type: content_tag(:strong, I18n.t(@type, scope: 'mail_subscriptions.unsubscribe.emails')), email: content_tag(:strong, @user.email), settings_path: settings_preferences_notifications_path) |
||||||
|
|
||||||
|
= form_tag unsubscribe_path, method: :post do |
||||||
|
= hidden_field_tag :token, params[:token] |
||||||
|
= hidden_field_tag :type, params[:type] |
||||||
|
= button_tag t('mail_subscriptions.unsubscribe.action'), type: :submit |
@ -1,358 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
require 'rails_helper' |
|
||||||
|
|
||||||
describe Api::V1::Admin::CanonicalEmailBlocksController do |
|
||||||
render_views |
|
||||||
|
|
||||||
let(:role) { UserRole.find_by(name: 'Admin') } |
|
||||||
let(:user) { Fabricate(:user, role: role) } |
|
||||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
|
||||||
let(:scopes) { 'admin:read:canonical_email_blocks admin:write:canonical_email_blocks' } |
|
||||||
|
|
||||||
before do |
|
||||||
allow(controller).to receive(:doorkeeper_token) { token } |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples 'forbidden for wrong scope' do |wrong_scope| |
|
||||||
let(:scopes) { wrong_scope } |
|
||||||
|
|
||||||
it 'returns http forbidden' do |
|
||||||
expect(response).to have_http_status(403) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples 'forbidden for wrong role' do |wrong_role| |
|
||||||
let(:role) { UserRole.find_by(name: wrong_role) } |
|
||||||
|
|
||||||
it 'returns http forbidden' do |
|
||||||
expect(response).to have_http_status(403) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #index' do |
|
||||||
context 'with wrong scope' do |
|
||||||
before do |
|
||||||
get :index |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses' |
|
||||||
end |
|
||||||
|
|
||||||
context 'with wrong role' do |
|
||||||
before do |
|
||||||
get :index |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
get :index |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
context 'when there is no canonical email block' do |
|
||||||
it 'returns an empty list' do |
|
||||||
get :index |
|
||||||
|
|
||||||
body = body_as_json |
|
||||||
|
|
||||||
expect(body).to be_empty |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when there are canonical email blocks' do |
|
||||||
let!(:canonical_email_blocks) { Fabricate.times(5, :canonical_email_block) } |
|
||||||
let(:expected_email_hashes) { canonical_email_blocks.pluck(:canonical_email_hash) } |
|
||||||
|
|
||||||
it 'returns the correct canonical email hashes' do |
|
||||||
get :index |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json.pluck(:canonical_email_hash)).to match_array(expected_email_hashes) |
|
||||||
end |
|
||||||
|
|
||||||
context 'with limit param' do |
|
||||||
let(:params) { { limit: 2 } } |
|
||||||
|
|
||||||
it 'returns only the requested number of canonical email blocks' do |
|
||||||
get :index, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json.size).to eq(params[:limit]) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'with since_id param' do |
|
||||||
let(:params) { { since_id: canonical_email_blocks[1].id } } |
|
||||||
|
|
||||||
it 'returns only the canonical email blocks after since_id' do |
|
||||||
get :index, params: params |
|
||||||
|
|
||||||
canonical_email_blocks_ids = canonical_email_blocks.pluck(:id).map(&:to_s) |
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json.pluck(:id)).to match_array(canonical_email_blocks_ids[2..]) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'with max_id param' do |
|
||||||
let(:params) { { max_id: canonical_email_blocks[3].id } } |
|
||||||
|
|
||||||
it 'returns only the canonical email blocks before max_id' do |
|
||||||
get :index, params: params |
|
||||||
|
|
||||||
canonical_email_blocks_ids = canonical_email_blocks.pluck(:id).map(&:to_s) |
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json.pluck(:id)).to match_array(canonical_email_blocks_ids[..2]) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #show' do |
|
||||||
let!(:canonical_email_block) { Fabricate(:canonical_email_block) } |
|
||||||
let(:params) { { id: canonical_email_block.id } } |
|
||||||
|
|
||||||
context 'with wrong scope' do |
|
||||||
before do |
|
||||||
get :show, params: params |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses' |
|
||||||
end |
|
||||||
|
|
||||||
context 'with wrong role' do |
|
||||||
before do |
|
||||||
get :show, params: params |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
end |
|
||||||
|
|
||||||
context 'when canonical email block exists' do |
|
||||||
it 'returns http success' do |
|
||||||
get :show, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns canonical email block data correctly' do |
|
||||||
get :show, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json[:id]).to eq(canonical_email_block.id.to_s) |
|
||||||
expect(json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when canonical block does not exist' do |
|
||||||
it 'returns http not found' do |
|
||||||
get :show, params: { id: 0 } |
|
||||||
|
|
||||||
expect(response).to have_http_status(404) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'POST #test' do |
|
||||||
context 'with wrong scope' do |
|
||||||
before do |
|
||||||
post :test |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses' |
|
||||||
end |
|
||||||
|
|
||||||
context 'with wrong role' do |
|
||||||
before do |
|
||||||
post :test, params: { email: 'whatever@email.com' } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
end |
|
||||||
|
|
||||||
context 'when required email is not provided' do |
|
||||||
it 'returns http bad request' do |
|
||||||
post :test |
|
||||||
|
|
||||||
expect(response).to have_http_status(400) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when required email is provided' do |
|
||||||
let(:params) { { email: 'example@email.com' } } |
|
||||||
|
|
||||||
context 'when there is a matching canonical email block' do |
|
||||||
let!(:canonical_email_block) { CanonicalEmailBlock.create(params) } |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
post :test, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns expected canonical email hash' do |
|
||||||
post :test, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json[0][:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when there is no matching canonical email block' do |
|
||||||
it 'returns http success' do |
|
||||||
post :test, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns an empty list' do |
|
||||||
post :test, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json).to be_empty |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'POST #create' do |
|
||||||
let(:params) { { email: 'example@email.com' } } |
|
||||||
let(:canonical_email_block) { CanonicalEmailBlock.new(email: params[:email]) } |
|
||||||
|
|
||||||
context 'with wrong scope' do |
|
||||||
before do |
|
||||||
post :create, params: params |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses' |
|
||||||
end |
|
||||||
|
|
||||||
context 'with wrong role' do |
|
||||||
before do |
|
||||||
post :create, params: params |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns canonical_email_hash correctly' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) |
|
||||||
end |
|
||||||
|
|
||||||
context 'when required email param is not provided' do |
|
||||||
it 'returns http unprocessable entity' do |
|
||||||
post :create |
|
||||||
|
|
||||||
expect(response).to have_http_status(422) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when canonical_email_hash param is provided instead of email' do |
|
||||||
let(:params) { { canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } } |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns correct canonical_email_hash' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json[:canonical_email_hash]).to eq(params[:canonical_email_hash]) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when both email and canonical_email_hash params are provided' do |
|
||||||
let(:params) { { email: 'example@email.com', canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } } |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'ignores canonical_email_hash param' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when canonical email was already blocked' do |
|
||||||
before do |
|
||||||
canonical_email_block.save |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns http unprocessable entity' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(422) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'DELETE #destroy' do |
|
||||||
let!(:canonical_email_block) { Fabricate(:canonical_email_block) } |
|
||||||
let(:params) { { id: canonical_email_block.id } } |
|
||||||
|
|
||||||
context 'with wrong scope' do |
|
||||||
before do |
|
||||||
delete :destroy, params: params |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'read:statuses' |
|
||||||
end |
|
||||||
|
|
||||||
context 'with wrong role' do |
|
||||||
before do |
|
||||||
delete :destroy, params: params |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
delete :destroy, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
context 'when canonical email block is not found' do |
|
||||||
it 'returns http not found' do |
|
||||||
delete :destroy, params: { id: 0 } |
|
||||||
|
|
||||||
expect(response).to have_http_status(404) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,140 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
require 'rails_helper' |
|
||||||
|
|
||||||
RSpec.describe Api::V1::Admin::DomainAllowsController do |
|
||||||
render_views |
|
||||||
|
|
||||||
let(:role) { UserRole.find_by(name: 'Admin') } |
|
||||||
let(:user) { Fabricate(:user, role: role) } |
|
||||||
let(:scopes) { 'admin:read admin:write' } |
|
||||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
|
||||||
|
|
||||||
before do |
|
||||||
allow(controller).to receive(:doorkeeper_token) { token } |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples 'forbidden for wrong scope' do |wrong_scope| |
|
||||||
let(:scopes) { wrong_scope } |
|
||||||
|
|
||||||
it 'returns http forbidden' do |
|
||||||
expect(response).to have_http_status(403) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples 'forbidden for wrong role' do |wrong_role| |
|
||||||
let(:role) { UserRole.find_by(name: wrong_role) } |
|
||||||
|
|
||||||
it 'returns http forbidden' do |
|
||||||
expect(response).to have_http_status(403) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #index' do |
|
||||||
let!(:domain_allow) { Fabricate(:domain_allow) } |
|
||||||
|
|
||||||
before do |
|
||||||
get :index |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns the expected domain allows' do |
|
||||||
json = body_as_json |
|
||||||
expect(json.length).to eq 1 |
|
||||||
expect(json[0][:id].to_i).to eq domain_allow.id |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #show' do |
|
||||||
let!(:domain_allow) { Fabricate(:domain_allow) } |
|
||||||
|
|
||||||
before do |
|
||||||
get :show, params: { id: domain_allow.id } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns expected domain name' do |
|
||||||
json = body_as_json |
|
||||||
expect(json[:domain]).to eq domain_allow.domain |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'DELETE #destroy' do |
|
||||||
let!(:domain_allow) { Fabricate(:domain_allow) } |
|
||||||
|
|
||||||
before do |
|
||||||
delete :destroy, params: { id: domain_allow.id } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'deletes the block' do |
|
||||||
expect(DomainAllow.find_by(id: domain_allow.id)).to be_nil |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'POST #create' do |
|
||||||
let!(:domain_allow) { Fabricate(:domain_allow, domain: 'example.com') } |
|
||||||
|
|
||||||
context 'with a valid domain' do |
|
||||||
before do |
|
||||||
post :create, params: { domain: 'foo.bar.com' } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns expected domain name' do |
|
||||||
json = body_as_json |
|
||||||
expect(json[:domain]).to eq 'foo.bar.com' |
|
||||||
end |
|
||||||
|
|
||||||
it 'creates a domain block' do |
|
||||||
expect(DomainAllow.find_by(domain: 'foo.bar.com')).to_not be_nil |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'with invalid domain name' do |
|
||||||
before do |
|
||||||
post :create, params: { domain: 'foo bar' } |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns http unprocessable entity' do |
|
||||||
expect(response).to have_http_status(422) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when domain name is not specified' do |
|
||||||
it 'returns http unprocessable entity' do |
|
||||||
post :create |
|
||||||
|
|
||||||
expect(response).to have_http_status(422) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,180 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
require 'rails_helper' |
|
||||||
|
|
||||||
RSpec.describe Api::V1::Admin::DomainBlocksController do |
|
||||||
render_views |
|
||||||
|
|
||||||
let(:role) { UserRole.find_by(name: 'Admin') } |
|
||||||
let(:user) { Fabricate(:user, role: role) } |
|
||||||
let(:scopes) { 'admin:read admin:write' } |
|
||||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
|
||||||
|
|
||||||
before do |
|
||||||
allow(controller).to receive(:doorkeeper_token) { token } |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples 'forbidden for wrong scope' do |wrong_scope| |
|
||||||
let(:scopes) { wrong_scope } |
|
||||||
|
|
||||||
it 'returns http forbidden' do |
|
||||||
expect(response).to have_http_status(403) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples 'forbidden for wrong role' do |wrong_role| |
|
||||||
let(:role) { UserRole.find_by(name: wrong_role) } |
|
||||||
|
|
||||||
it 'returns http forbidden' do |
|
||||||
expect(response).to have_http_status(403) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #index' do |
|
||||||
let!(:block) { Fabricate(:domain_block) } |
|
||||||
|
|
||||||
before do |
|
||||||
get :index |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns the expected domain blocks' do |
|
||||||
json = body_as_json |
|
||||||
expect(json.length).to eq 1 |
|
||||||
expect(json[0][:id].to_i).to eq block.id |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #show' do |
|
||||||
let!(:block) { Fabricate(:domain_block) } |
|
||||||
|
|
||||||
before do |
|
||||||
get :show, params: { id: block.id } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns expected domain name' do |
|
||||||
json = body_as_json |
|
||||||
expect(json[:domain]).to eq block.domain |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'PUT #update' do |
|
||||||
let!(:remote_account) { Fabricate(:account, domain: 'example.com') } |
|
||||||
let(:subject) do |
|
||||||
post :update, params: { id: domain_block.id, domain: 'example.com', severity: new_severity } |
|
||||||
end |
|
||||||
let(:domain_block) { Fabricate(:domain_block, domain: 'example.com', severity: original_severity) } |
|
||||||
|
|
||||||
before do |
|
||||||
BlockDomainService.new.call(domain_block) |
|
||||||
end |
|
||||||
|
|
||||||
context 'when downgrading a domain suspension to silence' do |
|
||||||
let(:original_severity) { 'suspend' } |
|
||||||
let(:new_severity) { 'silence' } |
|
||||||
|
|
||||||
it 'changes the block severity' do |
|
||||||
expect { subject }.to change { domain_block.reload.severity }.from('suspend').to('silence') |
|
||||||
end |
|
||||||
|
|
||||||
it 'undoes individual suspensions' do |
|
||||||
expect { subject }.to change { remote_account.reload.suspended? }.from(true).to(false) |
|
||||||
end |
|
||||||
|
|
||||||
it 'performs individual silences' do |
|
||||||
expect { subject }.to change { remote_account.reload.silenced? }.from(false).to(true) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when upgrading a domain silence to suspend' do |
|
||||||
let(:original_severity) { 'silence' } |
|
||||||
let(:new_severity) { 'suspend' } |
|
||||||
|
|
||||||
it 'changes the block severity' do |
|
||||||
expect { subject }.to change { domain_block.reload.severity }.from('silence').to('suspend') |
|
||||||
end |
|
||||||
|
|
||||||
it 'undoes individual silences' do |
|
||||||
expect { subject }.to change { remote_account.reload.silenced? }.from(true).to(false) |
|
||||||
end |
|
||||||
|
|
||||||
it 'performs individual suspends' do |
|
||||||
expect { subject }.to change { remote_account.reload.suspended? }.from(false).to(true) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'DELETE #destroy' do |
|
||||||
let!(:block) { Fabricate(:domain_block) } |
|
||||||
|
|
||||||
before do |
|
||||||
delete :destroy, params: { id: block.id } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'deletes the block' do |
|
||||||
expect(DomainBlock.find_by(id: block.id)).to be_nil |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'POST #create' do |
|
||||||
let(:existing_block_domain) { 'example.com' } |
|
||||||
let!(:block) { Fabricate(:domain_block, domain: existing_block_domain, severity: :suspend) } |
|
||||||
|
|
||||||
before do |
|
||||||
post :create, params: { domain: 'foo.bar.com', severity: :silence } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns expected domain name' do |
|
||||||
json = body_as_json |
|
||||||
expect(json[:domain]).to eq 'foo.bar.com' |
|
||||||
end |
|
||||||
|
|
||||||
it 'creates a domain block' do |
|
||||||
expect(DomainBlock.find_by(domain: 'foo.bar.com')).to_not be_nil |
|
||||||
end |
|
||||||
|
|
||||||
context 'when a stricter domain block already exists' do |
|
||||||
let(:existing_block_domain) { 'bar.com' } |
|
||||||
|
|
||||||
it 'returns http unprocessable entity' do |
|
||||||
expect(response).to have_http_status(422) |
|
||||||
end |
|
||||||
|
|
||||||
it 'renders existing domain block in error' do |
|
||||||
json = body_as_json |
|
||||||
expect(json[:existing_domain_block][:domain]).to eq existing_block_domain |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,309 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
require 'rails_helper' |
|
||||||
|
|
||||||
describe Api::V1::Admin::IpBlocksController do |
|
||||||
render_views |
|
||||||
|
|
||||||
let(:role) { UserRole.find_by(name: 'Admin') } |
|
||||||
let(:user) { Fabricate(:user, role: role) } |
|
||||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
|
||||||
let(:scopes) { 'admin:read:ip_blocks admin:write:ip_blocks' } |
|
||||||
|
|
||||||
before do |
|
||||||
allow(controller).to receive(:doorkeeper_token) { token } |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples 'forbidden for wrong scope' do |wrong_scope| |
|
||||||
let(:scopes) { wrong_scope } |
|
||||||
|
|
||||||
it 'returns http forbidden' do |
|
||||||
expect(response).to have_http_status(403) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples 'forbidden for wrong role' do |wrong_role| |
|
||||||
let(:role) { UserRole.find_by(name: wrong_role) } |
|
||||||
|
|
||||||
it 'returns http forbidden' do |
|
||||||
expect(response).to have_http_status(403) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #index' do |
|
||||||
context 'with wrong scope' do |
|
||||||
before do |
|
||||||
get :index |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'admin:write:ip_blocks' |
|
||||||
end |
|
||||||
|
|
||||||
context 'with wrong role' do |
|
||||||
before do |
|
||||||
get :index |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
get :index |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
context 'when there is no ip block' do |
|
||||||
it 'returns an empty body' do |
|
||||||
get :index |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json).to be_empty |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when there are ip blocks' do |
|
||||||
let!(:ip_blocks) do |
|
||||||
[ |
|
||||||
IpBlock.create(ip: '192.0.2.0/24', severity: :no_access), |
|
||||||
IpBlock.create(ip: '172.16.0.1', severity: :sign_up_requires_approval, comment: 'Spam'), |
|
||||||
IpBlock.create(ip: '2001:0db8::/32', severity: :sign_up_block, expires_in: 10.days), |
|
||||||
] |
|
||||||
end |
|
||||||
let(:expected_response) do |
|
||||||
ip_blocks.map do |ip_block| |
|
||||||
{ |
|
||||||
id: ip_block.id.to_s, |
|
||||||
ip: ip_block.ip, |
|
||||||
severity: ip_block.severity.to_s, |
|
||||||
comment: ip_block.comment, |
|
||||||
created_at: ip_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'), |
|
||||||
expires_at: ip_block.expires_at&.strftime('%Y-%m-%dT%H:%M:%S.%LZ'), |
|
||||||
} |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns the correct blocked ips' do |
|
||||||
get :index |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json).to match_array(expected_response) |
|
||||||
end |
|
||||||
|
|
||||||
context 'with limit param' do |
|
||||||
let(:params) { { limit: 2 } } |
|
||||||
|
|
||||||
it 'returns only the requested number of ip blocks' do |
|
||||||
get :index, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json.size).to eq(params[:limit]) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #show' do |
|
||||||
let!(:ip_block) { IpBlock.create(ip: '192.0.2.0/24', severity: :no_access) } |
|
||||||
let(:params) { { id: ip_block.id } } |
|
||||||
|
|
||||||
context 'with wrong scope' do |
|
||||||
before do |
|
||||||
get :show, params: params |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'admin:write:ip_blocks' |
|
||||||
end |
|
||||||
|
|
||||||
context 'with wrong role' do |
|
||||||
before do |
|
||||||
get :show, params: params |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
get :show, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns the correct ip block' do |
|
||||||
get :show, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json[:ip]).to eq("#{ip_block.ip}/#{ip_block.ip.prefix}") |
|
||||||
expect(json[:severity]).to eq(ip_block.severity.to_s) |
|
||||||
end |
|
||||||
|
|
||||||
context 'when ip block does not exist' do |
|
||||||
it 'returns http not found' do |
|
||||||
get :show, params: { id: 0 } |
|
||||||
|
|
||||||
expect(response).to have_http_status(404) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'POST #create' do |
|
||||||
let(:params) { { ip: '151.0.32.55', severity: 'no_access', comment: 'Spam' } } |
|
||||||
|
|
||||||
context 'with wrong scope' do |
|
||||||
before do |
|
||||||
post :create, params: params |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'admin:read:ip_blocks' |
|
||||||
end |
|
||||||
|
|
||||||
context 'with wrong role' do |
|
||||||
before do |
|
||||||
post :create, params: params |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
it_behaves_like 'forbidden for wrong role', 'Moderator' |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns the correct ip block' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json[:ip]).to eq("#{params[:ip]}/32") |
|
||||||
expect(json[:severity]).to eq(params[:severity]) |
|
||||||
expect(json[:comment]).to eq(params[:comment]) |
|
||||||
end |
|
||||||
|
|
||||||
context 'when ip is not provided' do |
|
||||||
let(:params) { { ip: '', severity: 'no_access' } } |
|
||||||
|
|
||||||
it 'returns http unprocessable entity' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(422) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when severity is not provided' do |
|
||||||
let(:params) { { ip: '173.65.23.1', severity: '' } } |
|
||||||
|
|
||||||
it 'returns http unprocessable entity' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(422) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when provided ip is already blocked' do |
|
||||||
before do |
|
||||||
IpBlock.create(params) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns http unprocessable entity' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(422) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when provided ip address is invalid' do |
|
||||||
let(:params) { { ip: '520.13.54.120', severity: 'no_access' } } |
|
||||||
|
|
||||||
it 'returns http unprocessable entity' do |
|
||||||
post :create, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(422) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'PUT #update' do |
|
||||||
context 'when ip block exists' do |
|
||||||
let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access', comment: 'Spam', expires_in: 48.hours) } |
|
||||||
let(:params) { { id: ip_block.id, severity: 'sign_up_requires_approval', comment: 'Decreasing severity' } } |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
put :update, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns the correct ip block' do |
|
||||||
put :update, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json).to match(hash_including({ |
|
||||||
ip: "#{ip_block.ip}/#{ip_block.ip.prefix}", |
|
||||||
severity: 'sign_up_requires_approval', |
|
||||||
comment: 'Decreasing severity', |
|
||||||
})) |
|
||||||
end |
|
||||||
|
|
||||||
it 'updates the severity correctly' do |
|
||||||
expect { put :update, params: params }.to change { ip_block.reload.severity }.from('no_access').to('sign_up_requires_approval') |
|
||||||
end |
|
||||||
|
|
||||||
it 'updates the comment correctly' do |
|
||||||
expect { put :update, params: params }.to change { ip_block.reload.comment }.from('Spam').to('Decreasing severity') |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when ip block does not exist' do |
|
||||||
it 'returns http not found' do |
|
||||||
put :update, params: { id: 0 } |
|
||||||
|
|
||||||
expect(response).to have_http_status(404) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'DELETE #destroy' do |
|
||||||
context 'when ip block exists' do |
|
||||||
let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access') } |
|
||||||
let(:params) { { id: ip_block.id } } |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
delete :destroy, params: params |
|
||||||
|
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns an empty body' do |
|
||||||
delete :destroy, params: params |
|
||||||
|
|
||||||
json = body_as_json |
|
||||||
|
|
||||||
expect(json).to be_empty |
|
||||||
end |
|
||||||
|
|
||||||
it 'deletes the ip block' do |
|
||||||
delete :destroy, params: params |
|
||||||
|
|
||||||
expect(IpBlock.find_by(id: ip_block.id)).to be_nil |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when ip block does not exist' do |
|
||||||
it 'returns http not found' do |
|
||||||
delete :destroy, params: { id: 0 } |
|
||||||
|
|
||||||
expect(response).to have_http_status(404) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,111 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
require 'rails_helper' |
|
||||||
|
|
||||||
RSpec.describe Api::V1::Admin::ReportsController do |
|
||||||
render_views |
|
||||||
|
|
||||||
let(:role) { UserRole.find_by(name: 'Moderator') } |
|
||||||
let(:user) { Fabricate(:user, role: role) } |
|
||||||
let(:scopes) { 'admin:read admin:write' } |
|
||||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
|
||||||
let(:report) { Fabricate(:report) } |
|
||||||
|
|
||||||
before do |
|
||||||
allow(controller).to receive(:doorkeeper_token) { token } |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples 'forbidden for wrong scope' do |wrong_scope| |
|
||||||
let(:scopes) { wrong_scope } |
|
||||||
|
|
||||||
it 'returns http forbidden' do |
|
||||||
expect(response).to have_http_status(403) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples 'forbidden for wrong role' do |wrong_role| |
|
||||||
let(:role) { UserRole.find_by(name: wrong_role) } |
|
||||||
|
|
||||||
it 'returns http forbidden' do |
|
||||||
expect(response).to have_http_status(403) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #index' do |
|
||||||
before do |
|
||||||
get :index |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #show' do |
|
||||||
before do |
|
||||||
get :show, params: { id: report.id } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'POST #resolve' do |
|
||||||
before do |
|
||||||
post :resolve, params: { id: report.id } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'POST #reopen' do |
|
||||||
before do |
|
||||||
post :reopen, params: { id: report.id } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'POST #assign_to_self' do |
|
||||||
before do |
|
||||||
post :assign_to_self, params: { id: report.id } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'POST #unassign' do |
|
||||||
before do |
|
||||||
post :unassign, params: { id: report.id } |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
|
||||||
it_behaves_like 'forbidden for wrong role', '' |
|
||||||
|
|
||||||
it 'returns http success' do |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,5 +1,5 @@ |
|||||||
# frozen_string_literal: true |
# frozen_string_literal: true |
||||||
|
|
||||||
Fabricator(:domain_allow) do |
Fabricator(:domain_allow) do |
||||||
domain 'MyString' |
domain { sequence(:domain) { |i| "example#{i}.com" } } |
||||||
end |
end |
||||||
|
@ -0,0 +1,8 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
Fabricator(:status_stat) do |
||||||
|
status |
||||||
|
replies_count '123' |
||||||
|
reblogs_count '456' |
||||||
|
favourites_count '789' |
||||||
|
end |
@ -0,0 +1,53 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
require 'rails_helper' |
||||||
|
|
||||||
|
describe 'GET /api/v1/accounts/{account_id}' do |
||||||
|
it 'returns account entity as 200 OK' do |
||||||
|
account = Fabricate(:account) |
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}" |
||||||
|
|
||||||
|
aggregate_failures do |
||||||
|
expect(response).to have_http_status(200) |
||||||
|
expect(body_as_json[:id]).to eq(account.id.to_s) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns 404 if account not found' do |
||||||
|
get '/api/v1/accounts/1' |
||||||
|
|
||||||
|
aggregate_failures do |
||||||
|
expect(response).to have_http_status(404) |
||||||
|
expect(body_as_json[:error]).to eq('Record not found') |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when with token' do |
||||||
|
it 'returns account entity as 200 OK if token is valid' do |
||||||
|
account = Fabricate(:account) |
||||||
|
user = Fabricate(:user, account: account) |
||||||
|
token = Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts').token |
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}", headers: { Authorization: "Bearer #{token}" } |
||||||
|
|
||||||
|
aggregate_failures do |
||||||
|
expect(response).to have_http_status(200) |
||||||
|
expect(body_as_json[:id]).to eq(account.id.to_s) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns 403 if scope of token is invalid' do |
||||||
|
account = Fabricate(:account) |
||||||
|
user = Fabricate(:user, account: account) |
||||||
|
token = Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:statuses').token |
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}", headers: { Authorization: "Bearer #{token}" } |
||||||
|
|
||||||
|
aggregate_failures do |
||||||
|
expect(response).to have_http_status(403) |
||||||
|
expect(body_as_json[:error]).to eq('This action is outside the authorized scopes') |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,305 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
require 'rails_helper' |
||||||
|
|
||||||
|
RSpec.describe 'Canonical Email Blocks' do |
||||||
|
let(:role) { UserRole.find_by(name: 'Admin') } |
||||||
|
let(:user) { Fabricate(:user, role: role) } |
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
||||||
|
let(:scopes) { 'admin:read:canonical_email_blocks admin:write:canonical_email_blocks' } |
||||||
|
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } |
||||||
|
|
||||||
|
shared_examples 'forbidden for wrong scope' do |wrong_scope| |
||||||
|
let(:scopes) { wrong_scope } |
||||||
|
|
||||||
|
it 'returns http forbidden' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(403) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples 'forbidden for wrong role' do |wrong_role| |
||||||
|
let(:role) { UserRole.find_by(name: wrong_role) } |
||||||
|
|
||||||
|
it 'returns http forbidden' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(403) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'GET /api/v1/admin/canonical_email_blocks' do |
||||||
|
subject do |
||||||
|
get '/api/v1/admin/canonical_email_blocks', headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let(:params) { {} } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'read:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there is no canonical email block' do |
||||||
|
it 'returns an empty list' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to be_empty |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there are canonical email blocks' do |
||||||
|
let!(:canonical_email_blocks) { Fabricate.times(5, :canonical_email_block) } |
||||||
|
let(:expected_email_hashes) { canonical_email_blocks.pluck(:canonical_email_hash) } |
||||||
|
|
||||||
|
it 'returns the correct canonical email hashes' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json.pluck(:canonical_email_hash)).to match_array(expected_email_hashes) |
||||||
|
end |
||||||
|
|
||||||
|
context 'with limit param' do |
||||||
|
let(:params) { { limit: 2 } } |
||||||
|
|
||||||
|
it 'returns only the requested number of canonical email blocks' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json.size).to eq(params[:limit]) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with since_id param' do |
||||||
|
let(:params) { { since_id: canonical_email_blocks[1].id } } |
||||||
|
|
||||||
|
it 'returns only the canonical email blocks after since_id' do |
||||||
|
subject |
||||||
|
|
||||||
|
canonical_email_blocks_ids = canonical_email_blocks.pluck(:id).map(&:to_s) |
||||||
|
|
||||||
|
expect(body_as_json.pluck(:id)).to match_array(canonical_email_blocks_ids[2..]) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with max_id param' do |
||||||
|
let(:params) { { max_id: canonical_email_blocks[3].id } } |
||||||
|
|
||||||
|
it 'returns only the canonical email blocks before max_id' do |
||||||
|
subject |
||||||
|
|
||||||
|
canonical_email_blocks_ids = canonical_email_blocks.pluck(:id).map(&:to_s) |
||||||
|
|
||||||
|
expect(body_as_json.pluck(:id)).to match_array(canonical_email_blocks_ids[..2]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'GET /api/v1/admin/canonical_email_blocks/:id' do |
||||||
|
subject do |
||||||
|
get "/api/v1/admin/canonical_email_blocks/#{canonical_email_block.id}", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let!(:canonical_email_block) { Fabricate(:canonical_email_block) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'read:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
context 'when the requested canonical email block exists' do |
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the requested canonical email block data correctly' do |
||||||
|
subject |
||||||
|
|
||||||
|
json = body_as_json |
||||||
|
|
||||||
|
expect(json[:id]).to eq(canonical_email_block.id.to_s) |
||||||
|
expect(json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the requested canonical block does not exist' do |
||||||
|
it 'returns http not found' do |
||||||
|
get '/api/v1/admin/canonical_email_blocks/-1', headers: headers |
||||||
|
|
||||||
|
expect(response).to have_http_status(404) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'POST /api/v1/admin/canonical_email_blocks/test' do |
||||||
|
subject do |
||||||
|
post '/api/v1/admin/canonical_email_blocks/test', headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let(:params) { { email: 'email@example.com' } } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'read:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
context 'when the required email param is not provided' do |
||||||
|
let(:params) { {} } |
||||||
|
|
||||||
|
it 'returns http bad request' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(400) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the required email param is provided' do |
||||||
|
context 'when there is a matching canonical email block' do |
||||||
|
let!(:canonical_email_block) { CanonicalEmailBlock.create(params) } |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the expected canonical email hash' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json[0][:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there is no matching canonical email block' do |
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns an empty list' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to be_empty |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'POST /api/v1/admin/canonical_email_blocks' do |
||||||
|
subject do |
||||||
|
post '/api/v1/admin/canonical_email_blocks', headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let(:params) { { email: 'example@email.com' } } |
||||||
|
let(:canonical_email_block) { CanonicalEmailBlock.new(email: params[:email]) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'read:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the canonical_email_hash correctly' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the required email param is not provided' do |
||||||
|
let(:params) { {} } |
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(422) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the canonical_email_hash param is provided instead of email' do |
||||||
|
let(:params) { { canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } } |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the correct canonical_email_hash' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json[:canonical_email_hash]).to eq(params[:canonical_email_hash]) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when both email and canonical_email_hash params are provided' do |
||||||
|
let(:params) { { email: 'example@email.com', canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } } |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'ignores the canonical_email_hash param' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the given canonical email was already blocked' do |
||||||
|
before do |
||||||
|
canonical_email_block.save |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(422) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'DELETE /api/v1/admin/canonical_email_blocks/:id' do |
||||||
|
subject do |
||||||
|
delete "/api/v1/admin/canonical_email_blocks/#{canonical_email_block.id}", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let!(:canonical_email_block) { Fabricate(:canonical_email_block) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'read:statuses' |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'deletes the canonical email block' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(CanonicalEmailBlock.find_by(id: canonical_email_block.id)).to be_nil |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the canonical email block is not found' do |
||||||
|
it 'returns http not found' do |
||||||
|
delete '/api/v1/admin/canonical_email_blocks/0', headers: headers |
||||||
|
|
||||||
|
expect(response).to have_http_status(404) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,214 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
require 'rails_helper' |
||||||
|
|
||||||
|
RSpec.describe 'Domain Allows' do |
||||||
|
let(:role) { UserRole.find_by(name: 'Admin') } |
||||||
|
let(:user) { Fabricate(:user, role: role) } |
||||||
|
let(:scopes) { 'admin:read admin:write' } |
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
||||||
|
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } |
||||||
|
|
||||||
|
shared_examples 'forbidden for wrong scope' do |wrong_scope| |
||||||
|
let(:scopes) { wrong_scope } |
||||||
|
|
||||||
|
it 'returns http forbidden' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(403) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples 'forbidden for wrong role' do |wrong_role| |
||||||
|
let(:role) { UserRole.find_by(name: wrong_role) } |
||||||
|
|
||||||
|
it 'returns http forbidden' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(403) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'GET /api/v1/admin/domain_allows' do |
||||||
|
subject do |
||||||
|
get '/api/v1/admin/domain_allows', headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let(:params) { {} } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there is no allowed domains' do |
||||||
|
it 'returns an empty body' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to be_empty |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there are allowed domains' do |
||||||
|
let!(:domain_allows) { Fabricate.times(5, :domain_allow) } |
||||||
|
let(:expected_response) do |
||||||
|
domain_allows.map do |domain_allow| |
||||||
|
{ |
||||||
|
id: domain_allow.id.to_s, |
||||||
|
domain: domain_allow.domain, |
||||||
|
created_at: domain_allow.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'), |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the correct allowed domains' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to match_array(expected_response) |
||||||
|
end |
||||||
|
|
||||||
|
context 'with limit param' do |
||||||
|
let(:params) { { limit: 2 } } |
||||||
|
|
||||||
|
it 'returns only the requested number of allowed domains' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json.size).to eq(params[:limit]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'GET /api/v1/admin/domain_allows/:id' do |
||||||
|
subject do |
||||||
|
get "/api/v1/admin/domain_allows/#{domain_allow.id}", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let!(:domain_allow) { Fabricate(:domain_allow) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the expected allowed domain name' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json[:domain]).to eq domain_allow.domain |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the requested allowed domain does not exist' do |
||||||
|
it 'returns http not found' do |
||||||
|
get '/api/v1/admin/domain_allows/-1', headers: headers |
||||||
|
|
||||||
|
expect(response).to have_http_status(404) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'POST /api/v1/admin/domain_allows' do |
||||||
|
subject do |
||||||
|
post '/api/v1/admin/domain_allows', headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let(:params) { { domain: 'foo.bar.com' } } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
context 'with a valid domain name' do |
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the expected domain name' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json[:domain]).to eq 'foo.bar.com' |
||||||
|
end |
||||||
|
|
||||||
|
it 'creates a domain allow' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(DomainAllow.find_by(domain: 'foo.bar.com')).to be_present |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with invalid domain name' do |
||||||
|
let(:params) { 'foo bar' } |
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(422) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when domain name is not specified' do |
||||||
|
let(:params) { {} } |
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(422) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the domain is already allowed' do |
||||||
|
before do |
||||||
|
DomainAllow.create(params) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the existing allowed domain name' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json[:domain]).to eq(params[:domain]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'DELETE /api/v1/admin/domain_allows/:id' do |
||||||
|
subject do |
||||||
|
delete "/api/v1/admin/domain_allows/#{domain_allow.id}", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let!(:domain_allow) { Fabricate(:domain_allow) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'deletes the allowed domain' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(DomainAllow.find_by(id: domain_allow.id)).to be_nil |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the allowed domain does not exist' do |
||||||
|
it 'returns http not found' do |
||||||
|
delete '/api/v1/admin/domain_allows/-1', headers: headers |
||||||
|
|
||||||
|
expect(response).to have_http_status(404) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,284 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
require 'rails_helper' |
||||||
|
|
||||||
|
RSpec.describe 'Domain Blocks' do |
||||||
|
let(:role) { UserRole.find_by(name: 'Admin') } |
||||||
|
let(:user) { Fabricate(:user, role: role) } |
||||||
|
let(:scopes) { 'admin:read:domain_blocks admin:write:domain_blocks' } |
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
||||||
|
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } |
||||||
|
|
||||||
|
shared_examples 'forbidden for wrong scope' do |wrong_scope| |
||||||
|
let(:scopes) { wrong_scope } |
||||||
|
|
||||||
|
it 'returns http forbidden' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(403) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples 'forbidden for wrong role' do |wrong_role| |
||||||
|
let(:role) { UserRole.find_by(name: wrong_role) } |
||||||
|
|
||||||
|
it 'returns http forbidden' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(403) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'GET /api/v1/admin/domain_blocks' do |
||||||
|
subject do |
||||||
|
get '/api/v1/admin/domain_blocks', headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let(:params) { {} } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there are no domain blocks' do |
||||||
|
it 'returns an empty list' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to be_empty |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there are domain blocks' do |
||||||
|
let!(:domain_blocks) do |
||||||
|
[ |
||||||
|
Fabricate(:domain_block, severity: :silence, reject_media: true), |
||||||
|
Fabricate(:domain_block, severity: :suspend, obfuscate: true), |
||||||
|
Fabricate(:domain_block, severity: :noop, reject_reports: true), |
||||||
|
Fabricate(:domain_block, public_comment: 'Spam'), |
||||||
|
Fabricate(:domain_block, private_comment: 'Spam'), |
||||||
|
] |
||||||
|
end |
||||||
|
let(:expected_responde) do |
||||||
|
domain_blocks.map do |domain_block| |
||||||
|
{ |
||||||
|
id: domain_block.id.to_s, |
||||||
|
domain: domain_block.domain, |
||||||
|
created_at: domain_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'), |
||||||
|
severity: domain_block.severity.to_s, |
||||||
|
reject_media: domain_block.reject_media, |
||||||
|
reject_reports: domain_block.reject_reports, |
||||||
|
private_comment: domain_block.private_comment, |
||||||
|
public_comment: domain_block.public_comment, |
||||||
|
obfuscate: domain_block.obfuscate, |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the expected domain blocks' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to match_array(expected_responde) |
||||||
|
end |
||||||
|
|
||||||
|
context 'with limit param' do |
||||||
|
let(:params) { { limit: 2 } } |
||||||
|
|
||||||
|
it 'returns only the requested number of domain blocks' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json.size).to eq(params[:limit]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'GET /api/v1/admin/domain_blocks/:id' do |
||||||
|
subject do |
||||||
|
get "/api/v1/admin/domain_blocks/#{domain_block.id}", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let!(:domain_block) { Fabricate(:domain_block) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the expected domain block content' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to eq( |
||||||
|
{ |
||||||
|
id: domain_block.id.to_s, |
||||||
|
domain: domain_block.domain, |
||||||
|
created_at: domain_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'), |
||||||
|
severity: domain_block.severity.to_s, |
||||||
|
reject_media: domain_block.reject_media, |
||||||
|
reject_reports: domain_block.reject_reports, |
||||||
|
private_comment: domain_block.private_comment, |
||||||
|
public_comment: domain_block.public_comment, |
||||||
|
obfuscate: domain_block.obfuscate, |
||||||
|
} |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the requested domain block does not exist' do |
||||||
|
it 'returns http not found' do |
||||||
|
get '/api/v1/admin/domain_blocks/-1', headers: headers |
||||||
|
|
||||||
|
expect(response).to have_http_status(404) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'POST /api/v1/admin/domain_blocks' do |
||||||
|
subject do |
||||||
|
post '/api/v1/admin/domain_blocks', headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let(:params) { { domain: 'foo.bar.com', severity: :silence } } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns expected domain name and severity' do |
||||||
|
subject |
||||||
|
|
||||||
|
body = body_as_json |
||||||
|
|
||||||
|
expect(body).to match a_hash_including( |
||||||
|
{ |
||||||
|
domain: 'foo.bar.com', |
||||||
|
severity: 'silence', |
||||||
|
} |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
it 'creates a domain block' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(DomainBlock.find_by(domain: 'foo.bar.com')).to be_present |
||||||
|
end |
||||||
|
|
||||||
|
context 'when a stricter domain block already exists' do |
||||||
|
before do |
||||||
|
Fabricate(:domain_block, domain: 'bar.com', severity: :suspend) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(422) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns existing domain block in error' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json[:existing_domain_block][:domain]).to eq('bar.com') |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when given domain name is invalid' do |
||||||
|
let(:params) { { domain: 'foo bar', severity: :silence } } |
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(422) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'PUT /api/v1/admin/domain_blocks/:id' do |
||||||
|
subject do |
||||||
|
put "/api/v1/admin/domain_blocks/#{domain_block.id}", headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let!(:domain_block) { Fabricate(:domain_block, domain: 'example.com', severity: :silence) } |
||||||
|
let(:params) { { domain: 'example.com', severity: 'suspend' } } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the updated domain block' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to match a_hash_including( |
||||||
|
{ |
||||||
|
id: domain_block.id.to_s, |
||||||
|
domain: domain_block.domain, |
||||||
|
severity: 'suspend', |
||||||
|
} |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
it 'updates the block severity' do |
||||||
|
expect { subject }.to change { domain_block.reload.severity }.from('silence').to('suspend') |
||||||
|
end |
||||||
|
|
||||||
|
context 'when domain block does not exist' do |
||||||
|
it 'returns http not found' do |
||||||
|
put '/api/v1/admin/domain_blocks/-1', headers: headers |
||||||
|
|
||||||
|
expect(response).to have_http_status(404) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'DELETE /api/v1/admin/domain_blocks/:id' do |
||||||
|
subject do |
||||||
|
delete "/api/v1/admin/domain_blocks/#{domain_block.id}", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let!(:domain_block) { Fabricate(:domain_block) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'deletes the domain block' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(DomainBlock.find_by(id: domain_block.id)).to be_nil |
||||||
|
end |
||||||
|
|
||||||
|
context 'when domain block does not exist' do |
||||||
|
it 'returns http not found' do |
||||||
|
delete '/api/v1/admin/domain_blocks/-1', headers: headers |
||||||
|
|
||||||
|
expect(response).to have_http_status(404) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,275 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
require 'rails_helper' |
||||||
|
|
||||||
|
RSpec.describe 'IP Blocks' do |
||||||
|
let(:role) { UserRole.find_by(name: 'Admin') } |
||||||
|
let(:user) { Fabricate(:user, role: role) } |
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
||||||
|
let(:scopes) { 'admin:read:ip_blocks admin:write:ip_blocks' } |
||||||
|
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } |
||||||
|
|
||||||
|
shared_examples 'forbidden for wrong scope' do |wrong_scope| |
||||||
|
let(:scopes) { wrong_scope } |
||||||
|
|
||||||
|
it 'returns http forbidden' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(403) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples 'forbidden for wrong role' do |wrong_role| |
||||||
|
let(:role) { UserRole.find_by(name: wrong_role) } |
||||||
|
|
||||||
|
it 'returns http forbidden' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(403) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'GET /api/v1/admin/ip_blocks' do |
||||||
|
subject do |
||||||
|
get '/api/v1/admin/ip_blocks', headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let(:params) { {} } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'admin:write:ip_blocks' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there is no ip block' do |
||||||
|
it 'returns an empty body' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to be_empty |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there are ip blocks' do |
||||||
|
let!(:ip_blocks) do |
||||||
|
[ |
||||||
|
IpBlock.create(ip: '192.0.2.0/24', severity: :no_access), |
||||||
|
IpBlock.create(ip: '172.16.0.1', severity: :sign_up_requires_approval, comment: 'Spam'), |
||||||
|
IpBlock.create(ip: '2001:0db8::/32', severity: :sign_up_block, expires_in: 10.days), |
||||||
|
] |
||||||
|
end |
||||||
|
let(:expected_response) do |
||||||
|
ip_blocks.map do |ip_block| |
||||||
|
{ |
||||||
|
id: ip_block.id.to_s, |
||||||
|
ip: ip_block.ip, |
||||||
|
severity: ip_block.severity.to_s, |
||||||
|
comment: ip_block.comment, |
||||||
|
created_at: ip_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'), |
||||||
|
expires_at: ip_block.expires_at&.strftime('%Y-%m-%dT%H:%M:%S.%LZ'), |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the correct blocked ips' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to match_array(expected_response) |
||||||
|
end |
||||||
|
|
||||||
|
context 'with limit param' do |
||||||
|
let(:params) { { limit: 2 } } |
||||||
|
|
||||||
|
it 'returns only the requested number of ip blocks' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json.size).to eq(params[:limit]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'GET /api/v1/admin/ip_blocks/:id' do |
||||||
|
subject do |
||||||
|
get "/api/v1/admin/ip_blocks/#{ip_block.id}", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let!(:ip_block) { IpBlock.create(ip: '192.0.2.0/24', severity: :no_access) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'admin:write:ip_blocks' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the correct ip block' do |
||||||
|
subject |
||||||
|
|
||||||
|
json = body_as_json |
||||||
|
|
||||||
|
expect(json[:ip]).to eq("#{ip_block.ip}/#{ip_block.ip.prefix}") |
||||||
|
expect(json[:severity]).to eq(ip_block.severity.to_s) |
||||||
|
end |
||||||
|
|
||||||
|
context 'when ip block does not exist' do |
||||||
|
it 'returns http not found' do |
||||||
|
get '/api/v1/admin/ip_blocks/-1', headers: headers |
||||||
|
|
||||||
|
expect(response).to have_http_status(404) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'POST /api/v1/admin/ip_blocks' do |
||||||
|
subject do |
||||||
|
post '/api/v1/admin/ip_blocks', headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let(:params) { { ip: '151.0.32.55', severity: 'no_access', comment: 'Spam' } } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'admin:read:ip_blocks' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
it_behaves_like 'forbidden for wrong role', 'Moderator' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the correct ip block' do |
||||||
|
subject |
||||||
|
|
||||||
|
json = body_as_json |
||||||
|
|
||||||
|
expect(json[:ip]).to eq("#{params[:ip]}/32") |
||||||
|
expect(json[:severity]).to eq(params[:severity]) |
||||||
|
expect(json[:comment]).to eq(params[:comment]) |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the required ip param is not provided' do |
||||||
|
let(:params) { { ip: '', severity: 'no_access' } } |
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(422) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the required severity param is not provided' do |
||||||
|
let(:params) { { ip: '173.65.23.1', severity: '' } } |
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(422) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the given ip address is already blocked' do |
||||||
|
before do |
||||||
|
IpBlock.create(params) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(422) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when the given ip address is invalid' do |
||||||
|
let(:params) { { ip: '520.13.54.120', severity: 'no_access' } } |
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(422) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'PUT /api/v1/admin/ip_blocks/:id' do |
||||||
|
subject do |
||||||
|
put "/api/v1/admin/ip_blocks/#{ip_block.id}", headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access', comment: 'Spam', expires_in: 48.hours) } |
||||||
|
let(:params) { { severity: 'sign_up_requires_approval', comment: 'Decreasing severity' } } |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the correct ip block' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to match(hash_including({ |
||||||
|
ip: "#{ip_block.ip}/#{ip_block.ip.prefix}", |
||||||
|
severity: 'sign_up_requires_approval', |
||||||
|
comment: 'Decreasing severity', |
||||||
|
})) |
||||||
|
end |
||||||
|
|
||||||
|
it 'updates the severity correctly' do |
||||||
|
expect { subject }.to change { ip_block.reload.severity }.from('no_access').to('sign_up_requires_approval') |
||||||
|
end |
||||||
|
|
||||||
|
it 'updates the comment correctly' do |
||||||
|
expect { subject }.to change { ip_block.reload.comment }.from('Spam').to('Decreasing severity') |
||||||
|
end |
||||||
|
|
||||||
|
context 'when ip block does not exist' do |
||||||
|
it 'returns http not found' do |
||||||
|
put '/api/v1/admin/ip_blocks/-1', headers: headers, params: params |
||||||
|
|
||||||
|
expect(response).to have_http_status(404) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'DELETE /api/v1/admin/ip_blocks/:id' do |
||||||
|
subject do |
||||||
|
delete "/api/v1/admin/ip_blocks/#{ip_block.id}", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access') } |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns an empty body' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to be_empty |
||||||
|
end |
||||||
|
|
||||||
|
it 'deletes the ip block' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(IpBlock.find_by(id: ip_block.id)).to be_nil |
||||||
|
end |
||||||
|
|
||||||
|
context 'when ip block does not exist' do |
||||||
|
it 'returns http not found' do |
||||||
|
delete '/api/v1/admin/ip_blocks/-1', headers: headers |
||||||
|
|
||||||
|
expect(response).to have_http_status(404) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,292 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
require 'rails_helper' |
||||||
|
|
||||||
|
RSpec.describe 'Reports' do |
||||||
|
let(:role) { UserRole.find_by(name: 'Admin') } |
||||||
|
let(:user) { Fabricate(:user, role: role) } |
||||||
|
let(:scopes) { 'admin:read:reports admin:write:reports' } |
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
||||||
|
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } |
||||||
|
|
||||||
|
shared_examples 'forbidden for wrong scope' do |wrong_scope| |
||||||
|
let(:scopes) { wrong_scope } |
||||||
|
|
||||||
|
it 'returns http forbidden' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(403) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples 'forbidden for wrong role' do |wrong_role| |
||||||
|
let(:role) { UserRole.find_by(name: wrong_role) } |
||||||
|
|
||||||
|
it 'returns http forbidden' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(403) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'GET /api/v1/admin/reports' do |
||||||
|
subject do |
||||||
|
get '/api/v1/admin/reports', headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let(:params) { {} } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there are no reports' do |
||||||
|
it 'returns an empty list' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to be_empty |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when there are reports' do |
||||||
|
let!(:reporter) { Fabricate(:account) } |
||||||
|
let!(:spammer) { Fabricate(:account) } |
||||||
|
let(:expected_response) do |
||||||
|
scope.map do |report| |
||||||
|
hash_including({ |
||||||
|
id: report.id.to_s, |
||||||
|
action_taken: report.action_taken?, |
||||||
|
category: report.category, |
||||||
|
comment: report.comment, |
||||||
|
account: hash_including(id: report.account.id.to_s), |
||||||
|
target_account: hash_including(id: report.target_account.id.to_s), |
||||||
|
statuses: report.statuses, |
||||||
|
rules: report.rules, |
||||||
|
forwarded: report.forwarded, |
||||||
|
}) |
||||||
|
end |
||||||
|
end |
||||||
|
let(:scope) { Report.unresolved } |
||||||
|
|
||||||
|
before do |
||||||
|
Fabricate(:report) |
||||||
|
Fabricate(:report, target_account: spammer) |
||||||
|
Fabricate(:report, account: reporter, target_account: spammer) |
||||||
|
Fabricate(:report, action_taken_at: 4.days.ago, account: reporter) |
||||||
|
Fabricate(:report, action_taken_at: 20.days.ago) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns all unresolved reports' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to match_array(expected_response) |
||||||
|
end |
||||||
|
|
||||||
|
context 'with resolved param' do |
||||||
|
let(:params) { { resolved: true } } |
||||||
|
let(:scope) { Report.resolved } |
||||||
|
|
||||||
|
it 'returns only the resolved reports' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to match_array(expected_response) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with account_id param' do |
||||||
|
let(:params) { { account_id: reporter.id } } |
||||||
|
let(:scope) { Report.unresolved.where(account: reporter) } |
||||||
|
|
||||||
|
it 'returns all unresolved reports filed by the specified account' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to match_array(expected_response) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with target_account_id param' do |
||||||
|
let(:params) { { target_account_id: spammer.id } } |
||||||
|
let(:scope) { Report.unresolved.where(target_account: spammer) } |
||||||
|
|
||||||
|
it 'returns all unresolved reports targeting the specified account' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to match_array(expected_response) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with limit param' do |
||||||
|
let(:params) { { limit: 1 } } |
||||||
|
|
||||||
|
it 'returns only the requested number of reports' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json.size).to eq(1) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'GET /api/v1/admin/reports/:id' do |
||||||
|
subject do |
||||||
|
get "/api/v1/admin/reports/#{report.id}", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let(:report) { Fabricate(:report) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the requested report content' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(body_as_json).to include( |
||||||
|
{ |
||||||
|
id: report.id.to_s, |
||||||
|
action_taken: report.action_taken?, |
||||||
|
category: report.category, |
||||||
|
comment: report.comment, |
||||||
|
account: a_hash_including(id: report.account.id.to_s), |
||||||
|
target_account: a_hash_including(id: report.target_account.id.to_s), |
||||||
|
statuses: report.statuses, |
||||||
|
rules: report.rules, |
||||||
|
forwarded: report.forwarded, |
||||||
|
} |
||||||
|
) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'PUT /api/v1/admin/reports/:id' do |
||||||
|
subject do |
||||||
|
put "/api/v1/admin/reports/#{report.id}", headers: headers, params: params |
||||||
|
end |
||||||
|
|
||||||
|
let!(:report) { Fabricate(:report, category: :other) } |
||||||
|
let(:params) { { category: 'spam' } } |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'updates the report category' do |
||||||
|
expect { subject }.to change { report.reload.category }.from('other').to('spam') |
||||||
|
end |
||||||
|
|
||||||
|
it 'returns the updated report content' do |
||||||
|
subject |
||||||
|
|
||||||
|
report.reload |
||||||
|
|
||||||
|
expect(body_as_json).to include( |
||||||
|
{ |
||||||
|
id: report.id.to_s, |
||||||
|
action_taken: report.action_taken?, |
||||||
|
category: report.category, |
||||||
|
comment: report.comment, |
||||||
|
account: a_hash_including(id: report.account.id.to_s), |
||||||
|
target_account: a_hash_including(id: report.target_account.id.to_s), |
||||||
|
statuses: report.statuses, |
||||||
|
rules: report.rules, |
||||||
|
forwarded: report.forwarded, |
||||||
|
} |
||||||
|
) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'POST #resolve' do |
||||||
|
subject do |
||||||
|
post "/api/v1/admin/reports/#{report.id}/resolve", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let(:report) { Fabricate(:report, action_taken_at: nil) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'marks report as resolved' do |
||||||
|
expect { subject }.to change { report.reload.unresolved? }.from(true).to(false) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'POST #reopen' do |
||||||
|
subject do |
||||||
|
post "/api/v1/admin/reports/#{report.id}/reopen", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let(:report) { Fabricate(:report, action_taken_at: 10.days.ago) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'marks report as unresolved' do |
||||||
|
expect { subject }.to change { report.reload.unresolved? }.from(false).to(true) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'POST #assign_to_self' do |
||||||
|
subject do |
||||||
|
post "/api/v1/admin/reports/#{report.id}/assign_to_self", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let(:report) { Fabricate(:report) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'assigns report to the requesting user' do |
||||||
|
expect { subject }.to change { report.reload.assigned_account_id }.from(nil).to(user.account.id) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'POST #unassign' do |
||||||
|
subject do |
||||||
|
post "/api/v1/admin/reports/#{report.id}/unassign", headers: headers |
||||||
|
end |
||||||
|
|
||||||
|
let(:report) { Fabricate(:report, assigned_account_id: user.account.id) } |
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||||
|
it_behaves_like 'forbidden for wrong role', '' |
||||||
|
|
||||||
|
it 'returns http success' do |
||||||
|
subject |
||||||
|
|
||||||
|
expect(response).to have_http_status(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'unassigns report from assignee' do |
||||||
|
expect { subject }.to change { report.reload.assigned_account_id }.from(user.account.id).to(nil) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -1,74 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
shared_examples 'ScopedSettings' do |
|
||||||
describe '[]' do |
|
||||||
it 'inherits default settings' do |
|
||||||
expect(Setting.boost_modal).to be false |
|
||||||
expect(Setting.interactions['must_be_follower']).to be false |
|
||||||
|
|
||||||
settings = create! |
|
||||||
|
|
||||||
expect(settings['boost_modal']).to be false |
|
||||||
expect(settings['interactions']['must_be_follower']).to be false |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'all_as_records' do |
|
||||||
# expecting [] and []= works |
|
||||||
|
|
||||||
it 'returns records merged with default values except hashes' do |
|
||||||
expect(Setting.boost_modal).to be false |
|
||||||
expect(Setting.delete_modal).to be true |
|
||||||
|
|
||||||
settings = create! |
|
||||||
settings['boost_modal'] = true |
|
||||||
|
|
||||||
records = settings.all_as_records |
|
||||||
|
|
||||||
expect(records['boost_modal'].value).to be true |
|
||||||
expect(records['delete_modal'].value).to be true |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'missing methods' do |
|
||||||
# expecting [] and []= works. |
|
||||||
|
|
||||||
it 'reads settings' do |
|
||||||
expect(Setting.boost_modal).to be false |
|
||||||
settings = create! |
|
||||||
expect(settings.boost_modal).to be false |
|
||||||
end |
|
||||||
|
|
||||||
it 'updates settings' do |
|
||||||
settings = fabricate |
|
||||||
settings.boost_modal = true |
|
||||||
expect(settings['boost_modal']).to be true |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
it 'can update settings with [] and can read with []=' do |
|
||||||
settings = fabricate |
|
||||||
|
|
||||||
settings['boost_modal'] = true |
|
||||||
settings['interactions'] = settings['interactions'].merge('must_be_follower' => true) |
|
||||||
|
|
||||||
Setting.save! |
|
||||||
|
|
||||||
expect(settings['boost_modal']).to be true |
|
||||||
expect(settings['interactions']['must_be_follower']).to be true |
|
||||||
|
|
||||||
Rails.cache.clear |
|
||||||
|
|
||||||
expect(settings['boost_modal']).to be true |
|
||||||
expect(settings['interactions']['must_be_follower']).to be true |
|
||||||
end |
|
||||||
|
|
||||||
xit 'does not mutate defaults via the cache' do |
|
||||||
fabricate['interactions']['must_be_follower'] = true |
|
||||||
# TODO |
|
||||||
# This mutates the global settings default such that future |
|
||||||
# instances will inherit the incorrect starting values |
|
||||||
|
|
||||||
expect(fabricate.settings['interactions']['must_be_follower']).to be false |
|
||||||
end |
|
||||||
end |
|
@ -1,15 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
shared_examples 'Settings-extended' do |
|
||||||
describe 'settings' do |
|
||||||
def fabricate |
|
||||||
super.settings |
|
||||||
end |
|
||||||
|
|
||||||
def create! |
|
||||||
super.settings |
|
||||||
end |
|
||||||
|
|
||||||
it_behaves_like 'ScopedSettings' |
|
||||||
end |
|
||||||
end |
|
Loading…
Reference in new issue