mirror of
https://git.kescher.at/CatCatNya/catstodon.git
synced 2024-11-25 17:51:36 +01:00
Merge pull request #2885 from ClearlyClaire/glitch-soc/backports-4.3
Merge upstream changes (stable-4.3)
This commit is contained in:
commit
3a5e83b91a
79 changed files with 861 additions and 543 deletions
|
@ -73,6 +73,15 @@ DB_PORT=5432
|
||||||
SECRET_KEY_BASE=
|
SECRET_KEY_BASE=
|
||||||
OTP_SECRET=
|
OTP_SECRET=
|
||||||
|
|
||||||
|
# Encryption secrets
|
||||||
|
# ------------------
|
||||||
|
# Must be available (and set to same values) for all server processes
|
||||||
|
# These are private/secret values, do not share outside hosting environment
|
||||||
|
# Use `bin/rails db:encryption:init` to generate fresh secrets
|
||||||
|
# ------------------
|
||||||
|
# ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=
|
||||||
|
# ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=
|
||||||
|
# ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=
|
||||||
|
|
||||||
# Web Push
|
# Web Push
|
||||||
# --------
|
# --------
|
||||||
|
|
2
.github/workflows/test-migrations.yml
vendored
2
.github/workflows/test-migrations.yml
vendored
|
@ -32,6 +32,8 @@ jobs:
|
||||||
postgres:
|
postgres:
|
||||||
- 14-alpine
|
- 14-alpine
|
||||||
- 15-alpine
|
- 15-alpine
|
||||||
|
- 16-alpine
|
||||||
|
- 17-alpine
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
|
|
8
.github/workflows/test-ruby.yml
vendored
8
.github/workflows/test-ruby.yml
vendored
|
@ -143,7 +143,7 @@ jobs:
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg libpam-dev
|
additional-system-dependencies: ffmpeg imagemagick libpam-dev
|
||||||
|
|
||||||
- name: Load database schema
|
- name: Load database schema
|
||||||
run: |
|
run: |
|
||||||
|
@ -245,7 +245,7 @@ jobs:
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg libpam-dev libyaml-dev
|
additional-system-dependencies: ffmpeg libpam-dev
|
||||||
|
|
||||||
- name: Load database schema
|
- name: Load database schema
|
||||||
run: './bin/rails db:create db:schema:load db:seed'
|
run: './bin/rails db:create db:schema:load db:seed'
|
||||||
|
@ -325,7 +325,7 @@ jobs:
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg
|
additional-system-dependencies: ffmpeg imagemagick
|
||||||
|
|
||||||
- name: Set up Javascript environment
|
- name: Set up Javascript environment
|
||||||
uses: ./.github/actions/setup-javascript
|
uses: ./.github/actions/setup-javascript
|
||||||
|
@ -445,7 +445,7 @@ jobs:
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg
|
additional-system-dependencies: ffmpeg imagemagick
|
||||||
|
|
||||||
- name: Set up Javascript environment
|
- name: Set up Javascript environment
|
||||||
uses: ./.github/actions/setup-javascript
|
uses: ./.github/actions/setup-javascript
|
||||||
|
|
|
@ -23,6 +23,6 @@ class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseControl
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_translation
|
def set_translation
|
||||||
@translation = TranslateStatusService.new.call(@status, content_locale)
|
@translation = TranslateStatusService.new.call(@status, I18n.locale.to_s)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -129,8 +129,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getComputedStyle(bodyRef.current).direction === 'rtl') {
|
||||||
|
setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
|
||||||
|
setCanScrollRight(bodyRef.current.scrollLeft < 0);
|
||||||
|
} else {
|
||||||
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
|
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
|
||||||
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
|
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
|
||||||
|
}
|
||||||
}, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]);
|
}, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]);
|
||||||
|
|
||||||
const handleLeftNav = useCallback(() => {
|
const handleLeftNav = useCallback(() => {
|
||||||
|
@ -146,8 +151,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getComputedStyle(bodyRef.current).direction === 'rtl') {
|
||||||
|
setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
|
||||||
|
setCanScrollRight(bodyRef.current.scrollLeft < 0);
|
||||||
|
} else {
|
||||||
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
|
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
|
||||||
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
|
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
|
||||||
|
}
|
||||||
}, [setCanScrollRight, setCanScrollLeft, bodyRef]);
|
}, [setCanScrollRight, setCanScrollLeft, bodyRef]);
|
||||||
|
|
||||||
const handleDismiss = useCallback(() => {
|
const handleDismiss = useCallback(() => {
|
||||||
|
|
|
@ -14,6 +14,8 @@ import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||||
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
||||||
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
||||||
import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
||||||
|
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
|
||||||
|
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
|
||||||
import { replyCompose } from 'flavours/glitch/actions/compose';
|
import { replyCompose } from 'flavours/glitch/actions/compose';
|
||||||
import { toggleReblog, toggleFavourite } from 'flavours/glitch/actions/interactions';
|
import { toggleReblog, toggleFavourite } from 'flavours/glitch/actions/interactions';
|
||||||
import { openModal } from 'flavours/glitch/actions/modal';
|
import { openModal } from 'flavours/glitch/actions/modal';
|
||||||
|
@ -161,16 +163,20 @@ class Footer extends ImmutablePureComponent {
|
||||||
replyTitle = intl.formatMessage(messages.replyAll);
|
replyTitle = intl.formatMessage(messages.replyAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reblogTitle = '';
|
let reblogTitle, reblogIconComponent;
|
||||||
|
|
||||||
if (status.get('reblogged')) {
|
if (status.get('reblogged')) {
|
||||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||||
|
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
|
||||||
} else if (publicStatus) {
|
} else if (publicStatus) {
|
||||||
reblogTitle = intl.formatMessage(messages.reblog);
|
reblogTitle = intl.formatMessage(messages.reblog);
|
||||||
|
reblogIconComponent = RepeatIcon;
|
||||||
} else if (reblogPrivate) {
|
} else if (reblogPrivate) {
|
||||||
reblogTitle = intl.formatMessage(messages.reblog_private);
|
reblogTitle = intl.formatMessage(messages.reblog_private);
|
||||||
|
reblogIconComponent = RepeatPrivateIcon;
|
||||||
} else {
|
} else {
|
||||||
reblogTitle = intl.formatMessage(messages.cannot_reblog);
|
reblogTitle = intl.formatMessage(messages.cannot_reblog);
|
||||||
|
reblogIconComponent = RepeatDisabledIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
let replyButton = null;
|
let replyButton = null;
|
||||||
|
@ -201,7 +207,7 @@ class Footer extends ImmutablePureComponent {
|
||||||
return (
|
return (
|
||||||
<div className='picture-in-picture__footer'>
|
<div className='picture-in-picture__footer'>
|
||||||
{replyButton}
|
{replyButton}
|
||||||
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
|
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
|
||||||
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={StarIcon} onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
|
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={StarIcon} onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
|
||||||
{withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' iconComponent={OpenInNewIcon} onClick={this.handleOpenClick} href={status.get('url')} />}
|
{withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' iconComponent={OpenInNewIcon} onClick={this.handleOpenClick} href={status.get('url')} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -116,6 +116,7 @@ export const MuteModal = ({ accountId, acct }) => {
|
||||||
<div className='safety-action-modal__bottom__collapsible'>
|
<div className='safety-action-modal__bottom__collapsible'>
|
||||||
<div className='safety-action-modal__field-group'>
|
<div className='safety-action-modal__field-group'>
|
||||||
<RadioButtonLabel name='duration' value='0' label={intl.formatMessage(messages.indefinite)} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
<RadioButtonLabel name='duration' value='0' label={intl.formatMessage(messages.indefinite)} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
||||||
|
<RadioButtonLabel name='duration' value='21600' label={intl.formatMessage(messages.hours, { number: 6 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
||||||
<RadioButtonLabel name='duration' value='86400' label={intl.formatMessage(messages.hours, { number: 24 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
<RadioButtonLabel name='duration' value='86400' label={intl.formatMessage(messages.hours, { number: 24 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
||||||
<RadioButtonLabel name='duration' value='604800' label={intl.formatMessage(messages.days, { number: 7 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
<RadioButtonLabel name='duration' value='604800' label={intl.formatMessage(messages.days, { number: 7 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
||||||
<RadioButtonLabel name='duration' value='2592000' label={intl.formatMessage(messages.days, { number: 30 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
<RadioButtonLabel name='duration' value='2592000' label={intl.formatMessage(messages.days, { number: 30 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
||||||
|
|
|
@ -11354,21 +11354,17 @@ noscript {
|
||||||
color: $darker-text-color;
|
color: $darker-text-color;
|
||||||
-webkit-line-clamp: 4;
|
-webkit-line-clamp: 4;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
max-height: 4 * 22px;
|
max-height: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
p {
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
display: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
p,
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.reply-indicator__attachments {
|
.reply-indicator__attachments {
|
||||||
|
|
|
@ -90,6 +90,10 @@ body.rtl {
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column-back-button__icon {
|
||||||
|
transform: scale(-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
.simple_form select {
|
.simple_form select {
|
||||||
background: $ui-base-color
|
background: $ui-base-color
|
||||||
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 12%))}'/></svg>")
|
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 12%))}'/></svg>")
|
||||||
|
|
|
@ -129,8 +129,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getComputedStyle(bodyRef.current).direction === 'rtl') {
|
||||||
|
setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
|
||||||
|
setCanScrollRight(bodyRef.current.scrollLeft < 0);
|
||||||
|
} else {
|
||||||
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
|
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
|
||||||
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
|
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
|
||||||
|
}
|
||||||
}, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]);
|
}, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]);
|
||||||
|
|
||||||
const handleLeftNav = useCallback(() => {
|
const handleLeftNav = useCallback(() => {
|
||||||
|
@ -146,8 +151,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getComputedStyle(bodyRef.current).direction === 'rtl') {
|
||||||
|
setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
|
||||||
|
setCanScrollRight(bodyRef.current.scrollLeft < 0);
|
||||||
|
} else {
|
||||||
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
|
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
|
||||||
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
|
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
|
||||||
|
}
|
||||||
}, [setCanScrollRight, setCanScrollLeft, bodyRef]);
|
}, [setCanScrollRight, setCanScrollLeft, bodyRef]);
|
||||||
|
|
||||||
const handleDismiss = useCallback(() => {
|
const handleDismiss = useCallback(() => {
|
||||||
|
|
|
@ -14,6 +14,8 @@ import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||||
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
||||||
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
||||||
import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
||||||
|
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
|
||||||
|
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
|
||||||
import { replyCompose } from 'mastodon/actions/compose';
|
import { replyCompose } from 'mastodon/actions/compose';
|
||||||
import { toggleReblog, toggleFavourite } from 'mastodon/actions/interactions';
|
import { toggleReblog, toggleFavourite } from 'mastodon/actions/interactions';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
|
@ -159,22 +161,26 @@ class Footer extends ImmutablePureComponent {
|
||||||
replyTitle = intl.formatMessage(messages.replyAll);
|
replyTitle = intl.formatMessage(messages.replyAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reblogTitle = '';
|
let reblogTitle, reblogIconComponent;
|
||||||
|
|
||||||
if (status.get('reblogged')) {
|
if (status.get('reblogged')) {
|
||||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||||
|
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
|
||||||
} else if (publicStatus) {
|
} else if (publicStatus) {
|
||||||
reblogTitle = intl.formatMessage(messages.reblog);
|
reblogTitle = intl.formatMessage(messages.reblog);
|
||||||
|
reblogIconComponent = RepeatIcon;
|
||||||
} else if (reblogPrivate) {
|
} else if (reblogPrivate) {
|
||||||
reblogTitle = intl.formatMessage(messages.reblog_private);
|
reblogTitle = intl.formatMessage(messages.reblog_private);
|
||||||
|
reblogIconComponent = RepeatPrivateIcon;
|
||||||
} else {
|
} else {
|
||||||
reblogTitle = intl.formatMessage(messages.cannot_reblog);
|
reblogTitle = intl.formatMessage(messages.cannot_reblog);
|
||||||
|
reblogIconComponent = RepeatDisabledIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='picture-in-picture__footer'>
|
<div className='picture-in-picture__footer'>
|
||||||
<IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
|
<IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
|
||||||
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
|
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
|
||||||
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={StarIcon} onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
|
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={StarIcon} onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
|
||||||
{withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' iconComponent={OpenInNewIcon} onClick={this.handleOpenClick} href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} />}
|
{withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' iconComponent={OpenInNewIcon} onClick={this.handleOpenClick} href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -116,6 +116,7 @@ export const MuteModal = ({ accountId, acct }) => {
|
||||||
<div className='safety-action-modal__bottom__collapsible'>
|
<div className='safety-action-modal__bottom__collapsible'>
|
||||||
<div className='safety-action-modal__field-group'>
|
<div className='safety-action-modal__field-group'>
|
||||||
<RadioButtonLabel name='duration' value='0' label={intl.formatMessage(messages.indefinite)} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
<RadioButtonLabel name='duration' value='0' label={intl.formatMessage(messages.indefinite)} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
||||||
|
<RadioButtonLabel name='duration' value='21600' label={intl.formatMessage(messages.hours, { number: 6 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
||||||
<RadioButtonLabel name='duration' value='86400' label={intl.formatMessage(messages.hours, { number: 24 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
<RadioButtonLabel name='duration' value='86400' label={intl.formatMessage(messages.hours, { number: 24 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
||||||
<RadioButtonLabel name='duration' value='604800' label={intl.formatMessage(messages.days, { number: 7 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
<RadioButtonLabel name='duration' value='604800' label={intl.formatMessage(messages.days, { number: 7 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
||||||
<RadioButtonLabel name='duration' value='2592000' label={intl.formatMessage(messages.days, { number: 30 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
<RadioButtonLabel name='duration' value='2592000' label={intl.formatMessage(messages.days, { number: 30 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
|
||||||
|
|
|
@ -10804,21 +10804,17 @@ noscript {
|
||||||
color: $darker-text-color;
|
color: $darker-text-color;
|
||||||
-webkit-line-clamp: 4;
|
-webkit-line-clamp: 4;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
max-height: 4 * 22px;
|
max-height: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
p {
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
display: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
p,
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.reply-indicator__attachments {
|
.reply-indicator__attachments {
|
||||||
|
|
|
@ -35,6 +35,10 @@ body.rtl {
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column-back-button__icon {
|
||||||
|
transform: scale(-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
.simple_form select {
|
.simple_form select {
|
||||||
background: $ui-base-color
|
background: $ui-base-color
|
||||||
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 12%))}'/></svg>")
|
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 12%))}'/></svg>")
|
||||||
|
|
|
@ -40,7 +40,7 @@ class ContentSecurityPolicy
|
||||||
end
|
end
|
||||||
|
|
||||||
def cdn_host_value
|
def cdn_host_value
|
||||||
s3_alias_host || s3_cloudfront_host || azure_alias_host || s3_hostname_host
|
s3_alias_host || s3_cloudfront_host || azure_alias_host || s3_hostname_host || swift_object_url
|
||||||
end
|
end
|
||||||
|
|
||||||
def paperclip_root_url
|
def paperclip_root_url
|
||||||
|
@ -76,6 +76,14 @@ class ContentSecurityPolicy
|
||||||
host_to_url ENV.fetch('S3_HOSTNAME', nil)
|
host_to_url ENV.fetch('S3_HOSTNAME', nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def swift_object_url
|
||||||
|
url = ENV.fetch('SWIFT_OBJECT_URL', nil)
|
||||||
|
return if url.blank? || !url.start_with?('https://')
|
||||||
|
|
||||||
|
url += '/' unless url.end_with?('/')
|
||||||
|
url
|
||||||
|
end
|
||||||
|
|
||||||
def uri_from_configuration_and_string(host_string)
|
def uri_from_configuration_and_string(host_string)
|
||||||
Addressable::URI.parse("#{host_protocol}://#{host_string}").tap do |uri|
|
Addressable::URI.parse("#{host_protocol}://#{host_string}").tap do |uri|
|
||||||
uri.path += '/' unless uri.path.blank? || uri.path.end_with?('/')
|
uri.path += '/' unless uri.path.blank? || uri.path.end_with?('/')
|
||||||
|
|
|
@ -18,5 +18,6 @@ class FollowRecommendation < ApplicationRecord
|
||||||
belongs_to :account_summary, foreign_key: :account_id, inverse_of: false
|
belongs_to :account_summary, foreign_key: :account_id, inverse_of: false
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|
||||||
scope :localized, ->(locale) { joins(:account_summary).merge(AccountSummary.localized(locale)) }
|
scope :unsupressed, -> { where.not(FollowRecommendationSuppression.where(FollowRecommendationSuppression.arel_table[:account_id].eq(arel_table[:account_id])).select(1).arel.exists) }
|
||||||
|
scope :localized, ->(locale) { unsupressed.joins(:account_summary).merge(AccountSummary.localized(locale)) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,8 @@ class TranslateStatusService < BaseService
|
||||||
def call(status, target_language)
|
def call(status, target_language)
|
||||||
@status = status
|
@status = status
|
||||||
@source_texts = source_texts
|
@source_texts = source_texts
|
||||||
|
|
||||||
|
target_language = target_language.split(/[_-]/).first unless target_languages.include?(target_language)
|
||||||
@target_language = target_language
|
@target_language = target_language
|
||||||
|
|
||||||
raise Mastodon::NotPermittedError unless permitted?
|
raise Mastodon::NotPermittedError unless permitted?
|
||||||
|
@ -32,11 +34,15 @@ class TranslateStatusService < BaseService
|
||||||
def permitted?
|
def permitted?
|
||||||
return false unless @status.distributable? && TranslationService.configured?
|
return false unless @status.distributable? && TranslationService.configured?
|
||||||
|
|
||||||
languages[@status.language]&.include?(@target_language)
|
target_languages.include?(@target_language)
|
||||||
end
|
end
|
||||||
|
|
||||||
def languages
|
def languages
|
||||||
Rails.cache.fetch('translation_service/languages', expires_in: 7.days, race_condition_ttl: 1.hour) { TranslationService.configured.languages }
|
Rails.cache.fetch('translation_service/languages', expires_in: 7.days, race_condition_ttl: 1.hour) { translation_backend.languages }
|
||||||
|
end
|
||||||
|
|
||||||
|
def target_languages
|
||||||
|
languages[@status.language] || []
|
||||||
end
|
end
|
||||||
|
|
||||||
def content_hash
|
def content_hash
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
%tr
|
%tr{ id: dom_id(invite) }
|
||||||
%td
|
%td
|
||||||
.input-copy
|
.input-copy
|
||||||
.input-copy__wrapper
|
.input-copy__wrapper
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
- ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
|
- ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
|
||||||
|
|
||||||
Run `bin/rails db:encryption:init` to generate new secrets and then assign the environment variables.
|
Run `bin/rails db:encryption:init` to generate new secrets and then assign the environment variables.
|
||||||
|
Do not change the secrets once they are set, as doing so may cause data loss and other issues that will be difficult or impossible to recover from.
|
||||||
MESSAGE
|
MESSAGE
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,17 @@ namespace :db do
|
||||||
namespace :encryption do
|
namespace :encryption do
|
||||||
desc 'Generate a set of keys for configuring Active Record encryption in a given environment'
|
desc 'Generate a set of keys for configuring Active Record encryption in a given environment'
|
||||||
task :init do # rubocop:disable Rails/RakeEnvironment
|
task :init do # rubocop:disable Rails/RakeEnvironment
|
||||||
|
if %w(
|
||||||
|
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
|
||||||
|
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
|
||||||
|
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
|
||||||
|
).any? { |key| ENV.key?(key) }
|
||||||
|
pastel = Pastel.new
|
||||||
|
puts pastel.red(<<~MSG)
|
||||||
|
WARNING: It looks like encryption secrets have already been set. Please ensure you are not changing secrets for a Mastodon installation that already uses them, as this will cause data loss and other issues that are difficult to recover from.
|
||||||
|
MSG
|
||||||
|
end
|
||||||
|
|
||||||
puts <<~MSG
|
puts <<~MSG
|
||||||
Add the following secret environment variables to your Mastodon environment (e.g. .env.production), ensure they are shared across all your nodes and do not change them after they are set:#{' '}
|
Add the following secret environment variables to your Mastodon environment (e.g. .env.production), ensure they are shared across all your nodes and do not change them after they are set:#{' '}
|
||||||
|
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Admin::InvitesController do
|
|
||||||
render_views
|
|
||||||
|
|
||||||
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
sign_in user, scope: :user
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET #index' do
|
|
||||||
subject { get :index, params: { available: true } }
|
|
||||||
|
|
||||||
let!(:invite) { Fabricate(:invite) }
|
|
||||||
|
|
||||||
it 'renders index page' do
|
|
||||||
expect(subject).to render_template :index
|
|
||||||
expect(response.body)
|
|
||||||
.to include(invite.code)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'POST #create' do
|
|
||||||
subject { post :create, params: { invite: { max_uses: '10', expires_in: 1800 } } }
|
|
||||||
|
|
||||||
it 'succeeds to create a invite' do
|
|
||||||
expect { subject }.to change(Invite, :count).by(1)
|
|
||||||
expect(subject).to redirect_to admin_invites_path
|
|
||||||
expect(Invite.last).to have_attributes(user_id: user.id, max_uses: 10)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'DELETE #destroy' do
|
|
||||||
subject { delete :destroy, params: { id: invite.id } }
|
|
||||||
|
|
||||||
let!(:invite) { Fabricate(:invite, expires_at: nil) }
|
|
||||||
|
|
||||||
it 'expires invite' do
|
|
||||||
expect(subject).to redirect_to admin_invites_path
|
|
||||||
expect(invite.reload).to be_expired
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'POST #deactivate_all' do
|
|
||||||
before { Fabricate(:invite, expires_at: nil) }
|
|
||||||
|
|
||||||
it 'expires all invites, then redirects to admin_invites_path' do
|
|
||||||
expect { post :deactivate_all }
|
|
||||||
.to change { Invite.exists?(expires_at: nil) }
|
|
||||||
.from(true)
|
|
||||||
.to(false)
|
|
||||||
|
|
||||||
expect(response).to redirect_to admin_invites_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,82 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Admin::TagsController do
|
|
||||||
render_views
|
|
||||||
|
|
||||||
before do
|
|
||||||
sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin'))
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET #index' do
|
|
||||||
before do
|
|
||||||
Fabricate(:tag)
|
|
||||||
|
|
||||||
tag_filter = instance_double(Admin::TagFilter, results: Tag.all)
|
|
||||||
allow(Admin::TagFilter).to receive(:new).and_return(tag_filter)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:params) { { order: 'newest' } }
|
|
||||||
|
|
||||||
it 'returns http success' do
|
|
||||||
get :index
|
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
expect(response).to render_template(:index)
|
|
||||||
|
|
||||||
expect(Admin::TagFilter)
|
|
||||||
.to have_received(:new)
|
|
||||||
.with(hash_including(params))
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'with filters' do
|
|
||||||
let(:params) { { order: 'newest', name: 'test' } }
|
|
||||||
|
|
||||||
it 'returns http success' do
|
|
||||||
get :index, params: { name: 'test' }
|
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
expect(response).to render_template(:index)
|
|
||||||
|
|
||||||
expect(Admin::TagFilter)
|
|
||||||
.to have_received(:new)
|
|
||||||
.with(hash_including(params))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET #show' do
|
|
||||||
let!(:tag) { Fabricate(:tag) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
get :show, params: { id: tag.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns status 200' do
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'PUT #update' do
|
|
||||||
let!(:tag) { Fabricate(:tag, listable: false) }
|
|
||||||
|
|
||||||
context 'with valid params' do
|
|
||||||
it 'updates the tag' do
|
|
||||||
put :update, params: { id: tag.id, tag: { listable: '1' } }
|
|
||||||
|
|
||||||
expect(response).to redirect_to(admin_tag_path(tag.id))
|
|
||||||
expect(tag.reload).to be_listable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with invalid params' do
|
|
||||||
it 'does not update the tag' do
|
|
||||||
put :update, params: { id: tag.id, tag: { name: 'cant-change-name' } }
|
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
expect(response).to render_template(:show)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -736,76 +736,4 @@ RSpec.describe StatusesController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #embed' do
|
|
||||||
let(:account) { Fabricate(:account) }
|
|
||||||
let(:status) { Fabricate(:status, account: account) }
|
|
||||||
|
|
||||||
context 'when account is suspended' do
|
|
||||||
let(:account) { Fabricate(:account, suspended: true) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
get :embed, params: { account_username: account.username, id: status.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns http gone' do
|
|
||||||
expect(response).to have_http_status(410)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when status is a reblog' do
|
|
||||||
let(:original_account) { Fabricate(:account, domain: 'example.com') }
|
|
||||||
let(:original_status) { Fabricate(:status, account: original_account, url: 'https://example.com/123') }
|
|
||||||
let(:status) { Fabricate(:status, account: account, reblog: original_status) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
get :embed, params: { account_username: status.account.username, id: status.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns http not found' do
|
|
||||||
expect(response).to have_http_status(404)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when status is public' do
|
|
||||||
before do
|
|
||||||
get :embed, params: { account_username: status.account.username, id: status.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'renders status successfully', :aggregate_failures do
|
|
||||||
expect(response)
|
|
||||||
.to have_http_status(200)
|
|
||||||
.and render_template(:embed)
|
|
||||||
expect(response.headers).to include(
|
|
||||||
'Vary' => 'Accept, Accept-Language, Cookie',
|
|
||||||
'Cache-Control' => include('public'),
|
|
||||||
'Link' => include('activity+json')
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when status is private' do
|
|
||||||
let(:status) { Fabricate(:status, account: account, visibility: :private) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
get :embed, params: { account_username: status.account.username, id: status.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns http not found' do
|
|
||||||
expect(response).to have_http_status(404)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when status is direct' do
|
|
||||||
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
get :embed, params: { account_username: status.account.username, id: status.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns http not found' do
|
|
||||||
expect(response).to have_http_status(404)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
6
spec/fabricators/ip_block_fabricator.rb
Normal file
6
spec/fabricators/ip_block_fabricator.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
Fabricator(:ip_block) do
|
||||||
|
severity { :sign_up_requires_approval }
|
||||||
|
ip { sequence(:ip) { |n| "10.0.0.#{n}" } }
|
||||||
|
end
|
|
@ -38,16 +38,23 @@ RSpec.describe AccountReachFinder do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#inboxes' do
|
describe '#inboxes' do
|
||||||
it 'includes the preferred inbox URL of followers' do
|
subject { described_class.new(account).inboxes }
|
||||||
expect(described_class.new(account).inboxes).to include(*[ap_follower_example_com, ap_follower_example_org, ap_follower_with_shared].map(&:preferred_inbox_url))
|
|
||||||
|
it 'includes the preferred inbox URL of followers and recently mentioned accounts but not unrelated users' do
|
||||||
|
expect(subject)
|
||||||
|
.to include(*follower_inbox_urls)
|
||||||
|
.and include(*mentioned_account_inbox_urls)
|
||||||
|
.and not_include(unrelated_account.preferred_inbox_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes the preferred inbox URL of recently-mentioned accounts' do
|
def follower_inbox_urls
|
||||||
expect(described_class.new(account).inboxes).to include(*[ap_mentioned_with_shared, ap_mentioned_example_com, ap_mentioned_example_org].map(&:preferred_inbox_url))
|
[ap_follower_example_com, ap_follower_example_org, ap_follower_with_shared]
|
||||||
|
.map(&:preferred_inbox_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not include the inbox of unrelated users' do
|
def mentioned_account_inbox_urls
|
||||||
expect(described_class.new(account).inboxes).to_not include(unrelated_account.preferred_inbox_url)
|
[ap_mentioned_with_shared, ap_mentioned_example_com, ap_mentioned_example_org]
|
||||||
|
.map(&:preferred_inbox_url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe ActivityPub::Activity::Block do
|
RSpec.describe ActivityPub::Activity::Block do
|
||||||
|
subject { described_class.new(json, sender) }
|
||||||
|
|
||||||
let(:sender) { Fabricate(:account) }
|
let(:sender) { Fabricate(:account) }
|
||||||
let(:recipient) { Fabricate(:account) }
|
let(:recipient) { Fabricate(:account) }
|
||||||
|
|
||||||
|
@ -16,61 +18,39 @@ RSpec.describe ActivityPub::Activity::Block do
|
||||||
}.with_indifferent_access
|
}.with_indifferent_access
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the recipient does not follow the sender' do
|
|
||||||
describe '#perform' do
|
describe '#perform' do
|
||||||
subject { described_class.new(json, sender) }
|
context 'when the recipient does not follow the sender' do
|
||||||
|
|
||||||
before do
|
|
||||||
subject.perform
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a block from sender to recipient' do
|
it 'creates a block from sender to recipient' do
|
||||||
expect(sender.blocking?(recipient)).to be true
|
subject.perform
|
||||||
end
|
|
||||||
|
expect(sender)
|
||||||
|
.to be_blocking(recipient)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the recipient is already blocked' do
|
context 'when the recipient is already blocked' do
|
||||||
before do
|
before { sender.block!(recipient, uri: 'old') }
|
||||||
sender.block!(recipient, uri: 'old')
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#perform' do
|
it 'creates a block from sender to recipient and sets uri to last received block activity' do
|
||||||
subject { described_class.new(json, sender) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
subject.perform
|
subject.perform
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a block from sender to recipient' do
|
expect(sender)
|
||||||
expect(sender.blocking?(recipient)).to be true
|
.to be_blocking(recipient)
|
||||||
end
|
expect(sender.block_relationships.find_by(target_account: recipient).uri)
|
||||||
|
.to eq 'foo'
|
||||||
it 'sets the uri to that of last received block activity' do
|
|
||||||
expect(sender.block_relationships.find_by(target_account: recipient).uri).to eq 'foo'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the recipient follows the sender' do
|
context 'when the recipient follows the sender' do
|
||||||
before do
|
before { recipient.follow!(sender) }
|
||||||
recipient.follow!(sender)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#perform' do
|
it 'creates a block from sender to recipient and ensures recipient not following sender' do
|
||||||
subject { described_class.new(json, sender) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
subject.perform
|
subject.perform
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a block from sender to recipient' do
|
expect(sender)
|
||||||
expect(sender.blocking?(recipient)).to be true
|
.to be_blocking(recipient)
|
||||||
end
|
expect(recipient)
|
||||||
|
.to_not be_following(sender)
|
||||||
it 'ensures recipient is not following sender' do
|
|
||||||
expect(recipient.following?(sender)).to be false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -90,19 +70,13 @@ RSpec.describe ActivityPub::Activity::Block do
|
||||||
ActivityPub::Activity::Undo.new(undo_json, sender).perform
|
ActivityPub::Activity::Undo.new(undo_json, sender).perform
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#perform' do
|
it 'does not create a block from sender to recipient and ensures recipient not following sender' do
|
||||||
subject { described_class.new(json, sender) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
subject.perform
|
subject.perform
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not create a block from sender to recipient' do
|
expect(sender)
|
||||||
expect(sender.blocking?(recipient)).to be false
|
.to_not be_blocking(recipient)
|
||||||
end
|
expect(recipient)
|
||||||
|
.to_not be_following(sender)
|
||||||
it 'ensures recipient is not following sender' do
|
|
||||||
expect(recipient.following?(sender)).to be false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,32 +14,24 @@ RSpec.describe Vacuum::AccessTokensVacuum do
|
||||||
let!(:expired_access_grant) { Fabricate(:access_grant, expires_in: 59.minutes.to_i, created_at: 1.hour.ago) }
|
let!(:expired_access_grant) { Fabricate(:access_grant, expires_in: 59.minutes.to_i, created_at: 1.hour.ago) }
|
||||||
let!(:active_access_grant) { Fabricate(:access_grant) }
|
let!(:active_access_grant) { Fabricate(:access_grant) }
|
||||||
|
|
||||||
before do
|
it 'deletes revoked/expired access tokens and revoked/expired grants, but preserves active tokens/grants' do
|
||||||
subject.perform
|
subject.perform
|
||||||
end
|
|
||||||
|
|
||||||
it 'deletes revoked access tokens' do
|
expect { revoked_access_token.reload }
|
||||||
expect { revoked_access_token.reload }.to raise_error ActiveRecord::RecordNotFound
|
.to raise_error ActiveRecord::RecordNotFound
|
||||||
end
|
expect { expired_access_token.reload }
|
||||||
|
.to raise_error ActiveRecord::RecordNotFound
|
||||||
|
|
||||||
it 'deletes expired access tokens' do
|
expect { revoked_access_grant.reload }
|
||||||
expect { expired_access_token.reload }.to raise_error ActiveRecord::RecordNotFound
|
.to raise_error ActiveRecord::RecordNotFound
|
||||||
end
|
expect { expired_access_grant.reload }
|
||||||
|
.to raise_error ActiveRecord::RecordNotFound
|
||||||
|
|
||||||
it 'deletes revoked access grants' do
|
expect { active_access_token.reload }
|
||||||
expect { revoked_access_grant.reload }.to raise_error ActiveRecord::RecordNotFound
|
.to_not raise_error
|
||||||
end
|
|
||||||
|
|
||||||
it 'deletes expired access grants' do
|
expect { active_access_grant.reload }
|
||||||
expect { expired_access_grant.reload }.to raise_error ActiveRecord::RecordNotFound
|
.to_not raise_error
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete active access tokens' do
|
|
||||||
expect { active_access_token.reload }.to_not raise_error
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete active access grants' do
|
|
||||||
expect { active_access_grant.reload }.to_not raise_error
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,16 +11,13 @@ RSpec.describe Vacuum::BackupsVacuum do
|
||||||
let!(:expired_backup) { Fabricate(:backup, created_at: (retention_period + 1.day).ago) }
|
let!(:expired_backup) { Fabricate(:backup, created_at: (retention_period + 1.day).ago) }
|
||||||
let!(:current_backup) { Fabricate(:backup) }
|
let!(:current_backup) { Fabricate(:backup) }
|
||||||
|
|
||||||
before do
|
it 'deletes backups past the retention period but preserves those within the period' do
|
||||||
subject.perform
|
subject.perform
|
||||||
end
|
|
||||||
|
|
||||||
it 'deletes backups past the retention period' do
|
expect { expired_backup.reload }
|
||||||
expect { expired_backup.reload }.to raise_error ActiveRecord::RecordNotFound
|
.to raise_error ActiveRecord::RecordNotFound
|
||||||
end
|
expect { current_backup.reload }
|
||||||
|
.to_not raise_error
|
||||||
it 'does not delete backups within the retention period' do
|
|
||||||
expect { current_backup.reload }.to_not raise_error
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,11 +14,11 @@ RSpec.describe Vacuum::FeedsVacuum do
|
||||||
redis.zadd(feed_key_for(active_user), 1, 1)
|
redis.zadd(feed_key_for(active_user), 1, 1)
|
||||||
redis.zadd(feed_key_for(inactive_user, 'reblogs'), 2, 2)
|
redis.zadd(feed_key_for(inactive_user, 'reblogs'), 2, 2)
|
||||||
redis.sadd(feed_key_for(inactive_user, 'reblogs:2'), 3)
|
redis.sadd(feed_key_for(inactive_user, 'reblogs:2'), 3)
|
||||||
|
|
||||||
subject.perform
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'clears feeds of inactive users and lists' do
|
it 'clears feeds of inactive users and lists' do
|
||||||
|
subject.perform
|
||||||
|
|
||||||
expect(redis.zcard(feed_key_for(inactive_user))).to eq 0
|
expect(redis.zcard(feed_key_for(inactive_user))).to eq 0
|
||||||
expect(redis.zcard(feed_key_for(active_user))).to eq 1
|
expect(redis.zcard(feed_key_for(active_user))).to eq 1
|
||||||
expect(redis.exists?(feed_key_for(inactive_user, 'reblogs'))).to be false
|
expect(redis.exists?(feed_key_for(inactive_user, 'reblogs'))).to be false
|
||||||
|
|
|
@ -17,9 +17,9 @@ RSpec.describe Vacuum::MediaAttachmentsVacuum do
|
||||||
let!(:old_unattached_media) { Fabricate(:media_attachment, account_id: nil, created_at: 10.days.ago) }
|
let!(:old_unattached_media) { Fabricate(:media_attachment, account_id: nil, created_at: 10.days.ago) }
|
||||||
let!(:new_unattached_media) { Fabricate(:media_attachment, account_id: nil, created_at: 1.hour.ago) }
|
let!(:new_unattached_media) { Fabricate(:media_attachment, account_id: nil, created_at: 1.hour.ago) }
|
||||||
|
|
||||||
before { subject.perform }
|
|
||||||
|
|
||||||
it 'handles attachments based on metadata details' do
|
it 'handles attachments based on metadata details' do
|
||||||
|
subject.perform
|
||||||
|
|
||||||
expect(old_remote_media.reload.file) # Remote and past retention period
|
expect(old_remote_media.reload.file) # Remote and past retention period
|
||||||
.to be_blank
|
.to be_blank
|
||||||
expect(old_local_media.reload.file) # Local and past retention
|
expect(old_local_media.reload.file) # Local and past retention
|
||||||
|
|
|
@ -15,24 +15,22 @@ RSpec.describe Vacuum::PreviewCardsVacuum do
|
||||||
before do
|
before do
|
||||||
old_preview_card.statuses << Fabricate(:status)
|
old_preview_card.statuses << Fabricate(:status)
|
||||||
new_preview_card.statuses << Fabricate(:status)
|
new_preview_card.statuses << Fabricate(:status)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'handles preview card cleanup' do
|
||||||
subject.perform
|
subject.perform
|
||||||
end
|
|
||||||
|
|
||||||
it 'deletes cache of preview cards last updated before the retention period' do
|
expect(old_preview_card.reload.image) # last updated before retention period
|
||||||
expect(old_preview_card.reload.image).to be_blank
|
.to be_blank
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete cache of preview cards last updated within the retention period' do
|
expect(new_preview_card.reload.image) # last updated within the retention period
|
||||||
expect(new_preview_card.reload.image).to_not be_blank
|
.to_not be_blank
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete attached preview cards' do
|
expect(new_preview_card.reload) # Keep attached preview cards
|
||||||
expect(new_preview_card.reload).to be_persisted
|
.to be_persisted
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete orphaned preview cards in the retention period' do
|
expect(orphaned_preview_card.reload) # keep orphaned cards in the retention period
|
||||||
expect(orphaned_preview_card.reload).to be_persisted
|
.to be_persisted
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,24 +15,20 @@ RSpec.describe Vacuum::StatusesVacuum do
|
||||||
let!(:local_status_old) { Fabricate(:status, created_at: (retention_period + 2.days).ago) }
|
let!(:local_status_old) { Fabricate(:status, created_at: (retention_period + 2.days).ago) }
|
||||||
let!(:local_status_recent) { Fabricate(:status, created_at: (retention_period - 2.days).ago) }
|
let!(:local_status_recent) { Fabricate(:status, created_at: (retention_period - 2.days).ago) }
|
||||||
|
|
||||||
before do
|
it 'deletes remote statuses past the retention period and keeps others' do
|
||||||
subject.perform
|
subject.perform
|
||||||
end
|
|
||||||
|
|
||||||
it 'deletes remote statuses past the retention period' do
|
expect { remote_status_old.reload }
|
||||||
expect { remote_status_old.reload }.to raise_error ActiveRecord::RecordNotFound
|
.to raise_error ActiveRecord::RecordNotFound
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete local statuses past the retention period' do
|
expect { local_status_old.reload }
|
||||||
expect { local_status_old.reload }.to_not raise_error
|
.to_not raise_error
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete remote statuses within the retention period' do
|
expect { remote_status_recent.reload }
|
||||||
expect { remote_status_recent.reload }.to_not raise_error
|
.to_not raise_error
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete local statuses within the retention period' do
|
expect { local_status_recent.reload }
|
||||||
expect { local_status_recent.reload }.to_not raise_error
|
.to_not raise_error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,8 @@ RSpec.describe AccountStatusesCleanupPolicy do
|
||||||
|
|
||||||
describe 'save hooks' do
|
describe 'save hooks' do
|
||||||
context 'when widening a policy' do
|
context 'when widening a policy' do
|
||||||
|
subject { account_statuses_cleanup_policy.last_inspected }
|
||||||
|
|
||||||
let!(:account_statuses_cleanup_policy) do
|
let!(:account_statuses_cleanup_policy) do
|
||||||
Fabricate(:account_statuses_cleanup_policy,
|
Fabricate(:account_statuses_cleanup_policy,
|
||||||
account: account,
|
account: account,
|
||||||
|
@ -33,64 +35,64 @@ RSpec.describe AccountStatusesCleanupPolicy do
|
||||||
account_statuses_cleanup_policy.record_last_inspected(42)
|
account_statuses_cleanup_policy.record_last_inspected(42)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'invalidates last_inspected when widened because of keep_direct' do
|
context 'when widened because of keep_direct' do
|
||||||
account_statuses_cleanup_policy.keep_direct = false
|
before { account_statuses_cleanup_policy.update(keep_direct: false) }
|
||||||
account_statuses_cleanup_policy.save
|
|
||||||
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'invalidates last_inspected when widened because of keep_pinned' do
|
context 'when widened because of keep_pinned' do
|
||||||
account_statuses_cleanup_policy.keep_pinned = false
|
before { account_statuses_cleanup_policy.update(keep_pinned: false) }
|
||||||
account_statuses_cleanup_policy.save
|
|
||||||
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'invalidates last_inspected when widened because of keep_polls' do
|
context 'when widened because of keep_polls' do
|
||||||
account_statuses_cleanup_policy.keep_polls = false
|
before { account_statuses_cleanup_policy.update(keep_polls: false) }
|
||||||
account_statuses_cleanup_policy.save
|
|
||||||
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'invalidates last_inspected when widened because of keep_media' do
|
context 'when widened because of keep_media' do
|
||||||
account_statuses_cleanup_policy.keep_media = false
|
before { account_statuses_cleanup_policy.update(keep_media: false) }
|
||||||
account_statuses_cleanup_policy.save
|
|
||||||
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'invalidates last_inspected when widened because of keep_self_fav' do
|
context 'when widened because of keep_self_fav' do
|
||||||
account_statuses_cleanup_policy.keep_self_fav = false
|
before { account_statuses_cleanup_policy.update(keep_self_fav: false) }
|
||||||
account_statuses_cleanup_policy.save
|
|
||||||
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'invalidates last_inspected when widened because of keep_self_bookmark' do
|
context 'when widened because of keep_self_bookmark' do
|
||||||
account_statuses_cleanup_policy.keep_self_bookmark = false
|
before { account_statuses_cleanup_policy.update(keep_self_bookmark: false) }
|
||||||
account_statuses_cleanup_policy.save
|
|
||||||
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'invalidates last_inspected when widened because of higher min_favs' do
|
context 'when widened because of higher min_favs' do
|
||||||
account_statuses_cleanup_policy.min_favs = 5
|
before { account_statuses_cleanup_policy.update(min_favs: 5) }
|
||||||
account_statuses_cleanup_policy.save
|
|
||||||
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'invalidates last_inspected when widened because of disabled min_favs' do
|
context 'when widened because of disabled min_favs' do
|
||||||
account_statuses_cleanup_policy.min_favs = nil
|
before { account_statuses_cleanup_policy.update(min_favs: nil) }
|
||||||
account_statuses_cleanup_policy.save
|
|
||||||
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'invalidates last_inspected when widened because of higher min_reblogs' do
|
context 'when widened because of higher min_reblogs' do
|
||||||
account_statuses_cleanup_policy.min_reblogs = 5
|
before { account_statuses_cleanup_policy.update(min_reblogs: 5) }
|
||||||
account_statuses_cleanup_policy.save
|
|
||||||
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'invalidates last_inspected when widened because of disable min_reblogs' do
|
context 'when widened because of disable min_reblogs' do
|
||||||
account_statuses_cleanup_policy.min_reblogs = nil
|
before { account_statuses_cleanup_policy.update(min_reblogs: nil) }
|
||||||
account_statuses_cleanup_policy.save
|
|
||||||
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,37 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Block do
|
RSpec.describe Block do
|
||||||
describe 'validations' do
|
describe 'Associations' do
|
||||||
it { is_expected.to belong_to(:account).required }
|
it { is_expected.to belong_to(:account).required }
|
||||||
it { is_expected.to belong_to(:target_account).required }
|
it { is_expected.to belong_to(:target_account).required }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#local?' do
|
||||||
|
it { is_expected.to_not be_local }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'Callbacks' do
|
||||||
|
describe 'Setting a URI' do
|
||||||
|
context 'when URI exists' do
|
||||||
|
subject { Fabricate.build :block, uri: 'https://uri/value' }
|
||||||
|
|
||||||
|
it 'does not change' do
|
||||||
|
expect { subject.save }
|
||||||
|
.to not_change(subject, :uri)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when URI is blank' do
|
||||||
|
subject { Fabricate.build :follow, uri: nil }
|
||||||
|
|
||||||
|
it 'populates the value' do
|
||||||
|
expect { subject.save }
|
||||||
|
.to change(subject, :uri).to(be_present)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'removes blocking cache after creation' do
|
it 'removes blocking cache after creation' do
|
||||||
account = Fabricate(:account)
|
account = Fabricate(:account)
|
||||||
target_account = Fabricate(:account)
|
target_account = Fabricate(:account)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Poll do
|
RSpec.describe Poll do
|
||||||
describe 'scopes' do
|
describe 'Scopes' do
|
||||||
let(:status) { Fabricate(:status) }
|
let(:status) { Fabricate(:status) }
|
||||||
let(:attached_poll) { Fabricate(:poll, status: status) }
|
let(:attached_poll) { Fabricate(:poll, status: status) }
|
||||||
let(:not_attached_poll) do
|
let(:not_attached_poll) do
|
||||||
|
@ -13,7 +13,7 @@ RSpec.describe Poll do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'attached' do
|
describe '.attached' do
|
||||||
it 'finds the correct records' do
|
it 'finds the correct records' do
|
||||||
results = described_class.attached
|
results = described_class.attached
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ RSpec.describe Poll do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'unattached' do
|
describe '.unattached' do
|
||||||
it 'finds the correct records' do
|
it 'finds the correct records' do
|
||||||
results = described_class.unattached
|
results = described_class.unattached
|
||||||
|
|
||||||
|
@ -30,11 +30,23 @@ RSpec.describe Poll do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'validations' do
|
describe '#reset_votes!' do
|
||||||
context 'when not valid' do
|
let(:poll) { Fabricate :poll, cached_tallies: [2, 3], votes_count: 5, voters_count: 5 }
|
||||||
|
let!(:vote) { Fabricate :poll_vote, poll: }
|
||||||
|
|
||||||
|
it 'resets vote data and deletes votes' do
|
||||||
|
expect { poll.reset_votes! }
|
||||||
|
.to change(poll, :cached_tallies).to([0, 0])
|
||||||
|
.and change(poll, :votes_count).to(0)
|
||||||
|
.and(change(poll, :voters_count).to(0))
|
||||||
|
expect { vote.reload }
|
||||||
|
.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'Validations' do
|
||||||
subject { Fabricate.build(:poll) }
|
subject { Fabricate.build(:poll) }
|
||||||
|
|
||||||
it { is_expected.to validate_presence_of(:expires_at) }
|
it { is_expected.to validate_presence_of(:expires_at) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
|
@ -55,12 +55,10 @@ RSpec.describe PublicFeed do
|
||||||
context 'without a viewer' do
|
context 'without a viewer' do
|
||||||
let(:viewer) { nil }
|
let(:viewer) { nil }
|
||||||
|
|
||||||
it 'includes remote instances statuses' do
|
it 'includes remote instances statuses and local statuses' do
|
||||||
expect(subject).to include(remote_status.id)
|
expect(subject)
|
||||||
end
|
.to include(remote_status.id)
|
||||||
|
.and include(local_status.id)
|
||||||
it 'includes local statuses' do
|
|
||||||
expect(subject).to include(local_status.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not include local-only statuses' do
|
it 'does not include local-only statuses' do
|
||||||
|
@ -71,12 +69,10 @@ RSpec.describe PublicFeed do
|
||||||
context 'with a viewer' do
|
context 'with a viewer' do
|
||||||
let(:viewer) { Fabricate(:account, username: 'viewer') }
|
let(:viewer) { Fabricate(:account, username: 'viewer') }
|
||||||
|
|
||||||
it 'includes remote instances statuses' do
|
it 'includes remote instances statuses and local statuses' do
|
||||||
expect(subject).to include(remote_status.id)
|
expect(subject)
|
||||||
end
|
.to include(remote_status.id)
|
||||||
|
.and include(local_status.id)
|
||||||
it 'includes local statuses' do
|
|
||||||
expect(subject).to include(local_status.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not include local-only statuses' do
|
it 'does not include local-only statuses' do
|
||||||
|
|
|
@ -387,23 +387,43 @@ RSpec.describe User do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'token_for_app' do
|
describe '#token_for_app' do
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
|
context 'when user owns app but does not have tokens' do
|
||||||
let(:app) { Fabricate(:application, owner: user) }
|
let(:app) { Fabricate(:application, owner: user) }
|
||||||
|
|
||||||
it 'returns a token' do
|
it 'creates and returns a persisted token' do
|
||||||
expect(user.token_for_app(app)).to be_a(Doorkeeper::AccessToken)
|
expect { user.token_for_app(app) }
|
||||||
|
.to change(Doorkeeper::AccessToken.where(resource_owner_id: user.id, application: app), :count).by(1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'persists a token' do
|
context 'when user owns app and already has tokens' do
|
||||||
t = user.token_for_app(app)
|
let(:app) { Fabricate(:application, owner: user) }
|
||||||
expect(user.token_for_app(app)).to eql(t)
|
let!(:token) { Fabricate :access_token, application: app, resource_owner_id: user.id }
|
||||||
|
|
||||||
|
it 'returns a persisted token' do
|
||||||
|
expect(user.token_for_app(app))
|
||||||
|
.to be_a(Doorkeeper::AccessToken)
|
||||||
|
.and eq(token)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'is nil if user does not own app' do
|
context 'when user does not own app' do
|
||||||
app.update!(owner: nil)
|
let(:app) { Fabricate(:application) }
|
||||||
|
|
||||||
expect(user.token_for_app(app)).to be_nil
|
it 'returns nil' do
|
||||||
|
expect(user.token_for_app(app))
|
||||||
|
.to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when app is nil' do
|
||||||
|
it 'returns nil' do
|
||||||
|
expect(user.token_for_app(nil))
|
||||||
|
.to be_nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe AccountModerationNotePolicy do
|
RSpec.describe AccountModerationNotePolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
@ -12,13 +11,13 @@ RSpec.describe AccountModerationNotePolicy do
|
||||||
permissions :create? do
|
permissions :create? do
|
||||||
context 'when staff' do
|
context 'when staff' do
|
||||||
it 'grants to create' do
|
it 'grants to create' do
|
||||||
expect(subject).to permit(admin, described_class)
|
expect(subject).to permit(admin, AccountModerationNote)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when not staff' do
|
context 'when not staff' do
|
||||||
it 'denies to create' do
|
it 'denies to create' do
|
||||||
expect(subject).to_not permit(john, described_class)
|
expect(subject).to_not permit(john, AccountModerationNote)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe AccountPolicy do
|
RSpec.describe AccountPolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
@ -24,7 +23,7 @@ RSpec.describe AccountPolicy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
permissions :show?, :unsilence?, :unsensitive?, :remove_avatar?, :remove_header? do
|
permissions :show?, :unsilence?, :unsensitive?, :remove_avatar?, :remove_header?, :sensitive?, :warn? do
|
||||||
context 'when staff' do
|
context 'when staff' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(subject).to permit(admin, alice)
|
expect(subject).to permit(admin, alice)
|
||||||
|
|
42
spec/policies/account_warning_policy_spec.rb
Normal file
42
spec/policies/account_warning_policy_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe AccountWarningPolicy do
|
||||||
|
subject { described_class }
|
||||||
|
|
||||||
|
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
permissions :show? do
|
||||||
|
context 'with an admin' do
|
||||||
|
it { is_expected.to permit(admin, AccountWarning.new) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a non-admin' do
|
||||||
|
context 'when account is not target' do
|
||||||
|
it { is_expected.to_not permit(account, AccountWarning.new) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when account is target' do
|
||||||
|
it { is_expected.to permit(account, AccountWarning.new(target_account_id: account.id)) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
permissions :appeal? do
|
||||||
|
context 'when account is not target' do
|
||||||
|
it { is_expected.to_not permit(account, AccountWarning.new) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when account is target' do
|
||||||
|
context 'when record is appealable' do
|
||||||
|
it { is_expected.to permit(account, AccountWarning.new(target_account_id: account.id, created_at: Appeal::MAX_STRIKE_AGE.ago + 1.hour)) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when record is not appealable' do
|
||||||
|
it { is_expected.to_not permit(account, AccountWarning.new(target_account_id: account.id, created_at: Appeal::MAX_STRIKE_AGE.ago - 1.hour)) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe AccountWarningPresetPolicy do
|
RSpec.describe AccountWarningPresetPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -11,13 +10,13 @@ RSpec.describe AccountWarningPresetPolicy do
|
||||||
permissions :index?, :create?, :update?, :destroy? do
|
permissions :index?, :create?, :update?, :destroy? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, AccountWarningPreset)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, AccountWarningPreset)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe Admin::StatusPolicy do
|
RSpec.describe Admin::StatusPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -13,13 +12,13 @@ RSpec.describe Admin::StatusPolicy do
|
||||||
permissions :index?, :update?, :review?, :destroy? do
|
permissions :index?, :update?, :review?, :destroy? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, Status)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, Status)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe AnnouncementPolicy do
|
RSpec.describe AnnouncementPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -11,13 +10,13 @@ RSpec.describe AnnouncementPolicy do
|
||||||
permissions :index?, :create?, :update?, :destroy? do
|
permissions :index?, :create?, :update?, :destroy? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, Announcement)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, Announcement)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe AppealPolicy do
|
RSpec.describe AppealPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -12,18 +11,18 @@ RSpec.describe AppealPolicy do
|
||||||
permissions :index? do
|
permissions :index? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, Appeal)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, Appeal)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
permissions :reject? do
|
permissions :reject?, :approve? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
context 'with a pending appeal' do
|
context 'with a pending appeal' do
|
||||||
before { allow(appeal).to receive(:pending?).and_return(true) }
|
before { allow(appeal).to receive(:pending?).and_return(true) }
|
||||||
|
|
20
spec/policies/audit_log_policy_spec.rb
Normal file
20
spec/policies/audit_log_policy_spec.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe AuditLogPolicy do
|
||||||
|
subject { described_class }
|
||||||
|
|
||||||
|
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
permissions :index? do
|
||||||
|
context 'with an admin' do
|
||||||
|
it { is_expected.to permit(admin, nil) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a non-admin' do
|
||||||
|
it { is_expected.to_not permit(account, nil) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe BackupPolicy do
|
RSpec.describe BackupPolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe CanonicalEmailBlockPolicy do
|
RSpec.describe CanonicalEmailBlockPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -11,13 +10,13 @@ RSpec.describe CanonicalEmailBlockPolicy do
|
||||||
permissions :index?, :show?, :test?, :create?, :destroy? do
|
permissions :index?, :show?, :test?, :create?, :destroy? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, CanonicalEmailBlock)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, CanonicalEmailBlock)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe CustomEmojiPolicy do
|
RSpec.describe CustomEmojiPolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
20
spec/policies/dashboard_policy_spec.rb
Normal file
20
spec/policies/dashboard_policy_spec.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe DashboardPolicy do
|
||||||
|
subject { described_class }
|
||||||
|
|
||||||
|
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
permissions :index? do
|
||||||
|
context 'with an admin' do
|
||||||
|
it { is_expected.to permit(admin, nil) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a non-admin' do
|
||||||
|
it { is_expected.to_not permit(account, nil) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe DeliveryPolicy do
|
RSpec.describe DeliveryPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -11,13 +10,13 @@ RSpec.describe DeliveryPolicy do
|
||||||
permissions :clear_delivery_errors?, :restart_delivery?, :stop_delivery? do
|
permissions :clear_delivery_errors?, :restart_delivery?, :stop_delivery? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
24
spec/policies/domain_allow_policy_spec.rb
Normal file
24
spec/policies/domain_allow_policy_spec.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe DomainAllowPolicy do
|
||||||
|
subject { described_class }
|
||||||
|
|
||||||
|
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
|
||||||
|
let(:john) { Fabricate(:account) }
|
||||||
|
|
||||||
|
permissions :index?, :show?, :create?, :destroy? do
|
||||||
|
context 'when admin' do
|
||||||
|
it 'permits' do
|
||||||
|
expect(subject).to permit(admin, DomainAllow)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not admin' do
|
||||||
|
it 'denies' do
|
||||||
|
expect(subject).to_not permit(john, DomainAllow)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe DomainBlockPolicy do
|
RSpec.describe DomainBlockPolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
@ -9,7 +8,7 @@ RSpec.describe DomainBlockPolicy do
|
||||||
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
|
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
|
||||||
let(:john) { Fabricate(:account) }
|
let(:john) { Fabricate(:account) }
|
||||||
|
|
||||||
permissions :index?, :show?, :create?, :destroy? do
|
permissions :index?, :show?, :create?, :destroy?, :update? do
|
||||||
context 'when admin' do
|
context 'when admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(subject).to permit(admin, DomainBlock)
|
expect(subject).to permit(admin, DomainBlock)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe EmailDomainBlockPolicy do
|
RSpec.describe EmailDomainBlockPolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe FollowRecommendationPolicy do
|
RSpec.describe FollowRecommendationPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -11,13 +10,13 @@ RSpec.describe FollowRecommendationPolicy do
|
||||||
permissions :show?, :suppress?, :unsuppress? do
|
permissions :show?, :suppress?, :unsuppress? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, FollowRecommendation)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, FollowRecommendation)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe InstancePolicy do
|
RSpec.describe InstancePolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe InvitePolicy do
|
RSpec.describe InvitePolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe IpBlockPolicy do
|
RSpec.describe IpBlockPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -11,13 +10,13 @@ RSpec.describe IpBlockPolicy do
|
||||||
permissions :index?, :show?, :create?, :update?, :destroy? do
|
permissions :index?, :show?, :create?, :update?, :destroy? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, IpBlock)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, IpBlock)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
36
spec/policies/poll_policy_spec.rb
Normal file
36
spec/policies/poll_policy_spec.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe PollPolicy do
|
||||||
|
subject { described_class }
|
||||||
|
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
let(:poll) { Fabricate :poll }
|
||||||
|
|
||||||
|
permissions :vote? do
|
||||||
|
context 'when account cannot view status' do
|
||||||
|
before { poll.status.update(visibility: :private) }
|
||||||
|
|
||||||
|
it { is_expected.to_not permit(account, poll) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when account can view status' do
|
||||||
|
context 'when accounts do not block each other' do
|
||||||
|
it { is_expected.to permit(account, poll) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when view blocks poll creator' do
|
||||||
|
before { Fabricate :block, account: account, target_account: poll.account }
|
||||||
|
|
||||||
|
it { is_expected.to_not permit(account, poll) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when poll creator blocks viewer' do
|
||||||
|
before { Fabricate :block, account: poll.account, target_account: account }
|
||||||
|
|
||||||
|
it { is_expected.to_not permit(account, poll) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe PreviewCardPolicy do
|
RSpec.describe PreviewCardPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -11,13 +10,13 @@ RSpec.describe PreviewCardPolicy do
|
||||||
permissions :index?, :review? do
|
permissions :index?, :review? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, PreviewCard)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, PreviewCard)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe PreviewCardProviderPolicy do
|
RSpec.describe PreviewCardProviderPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -11,13 +10,13 @@ RSpec.describe PreviewCardProviderPolicy do
|
||||||
permissions :index?, :review? do
|
permissions :index?, :review? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, PreviewCardProvider)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, PreviewCardProvider)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe RelayPolicy do
|
RSpec.describe RelayPolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe ReportNotePolicy do
|
RSpec.describe ReportNotePolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe ReportPolicy do
|
RSpec.describe ReportPolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe RulePolicy do
|
RSpec.describe RulePolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
@ -11,13 +10,13 @@ RSpec.describe RulePolicy do
|
||||||
permissions :index?, :create?, :update?, :destroy? do
|
permissions :index?, :create?, :update?, :destroy? do
|
||||||
context 'with an admin' do
|
context 'with an admin' do
|
||||||
it 'permits' do
|
it 'permits' do
|
||||||
expect(policy).to permit(admin, Tag)
|
expect(policy).to permit(admin, Rule)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-admin' do
|
context 'with a non-admin' do
|
||||||
it 'denies' do
|
it 'denies' do
|
||||||
expect(policy).to_not permit(john, Tag)
|
expect(policy).to_not permit(john, Rule)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe SettingsPolicy do
|
RSpec.describe SettingsPolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe SoftwareUpdatePolicy do
|
RSpec.describe SoftwareUpdatePolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe StatusPolicy, type: :model do
|
RSpec.describe StatusPolicy, type: :model do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe TagPolicy do
|
RSpec.describe TagPolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe UserPolicy do
|
RSpec.describe UserPolicy do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
@ -112,4 +111,42 @@ RSpec.describe UserPolicy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
permissions :approve?, :reject? do
|
||||||
|
context 'when admin' do
|
||||||
|
context 'when user is approved' do
|
||||||
|
it { is_expected.to_not permit(admin, User.new(approved: true)) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is not approved' do
|
||||||
|
it { is_expected.to permit(admin, User.new(approved: false)) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not admin' do
|
||||||
|
it { is_expected.to_not permit(john, User.new) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
permissions :change_role? do
|
||||||
|
context 'when not admin' do
|
||||||
|
it { is_expected.to_not permit(john, User.new) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when admin' do
|
||||||
|
let(:user) { User.new(role: role) }
|
||||||
|
|
||||||
|
context 'when role of admin overrides user role' do
|
||||||
|
let(:role) { UserRole.new(position: admin.user.role.position - 10, id: 123) }
|
||||||
|
|
||||||
|
it { is_expected.to permit(admin, user) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when role of admin does not override user role' do
|
||||||
|
let(:role) { UserRole.new(position: admin.user.role.position + 10, id: 123) }
|
||||||
|
|
||||||
|
it { is_expected.to_not permit(admin, user) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
56
spec/policies/user_role_policy_spec.rb
Normal file
56
spec/policies/user_role_policy_spec.rb
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe UserRolePolicy do
|
||||||
|
subject { described_class }
|
||||||
|
|
||||||
|
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
permissions :index?, :create? do
|
||||||
|
context 'when admin' do
|
||||||
|
it { is_expected.to permit(admin, UserRole.new) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not admin' do
|
||||||
|
it { is_expected.to_not permit(account, UserRole.new) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
permissions :update? do
|
||||||
|
context 'when admin' do
|
||||||
|
context 'when role of admin overrides relevant role' do
|
||||||
|
it { is_expected.to permit(admin, UserRole.new(position: admin.user.role.position - 10, id: 123)) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when role of admin does not override relevant role' do
|
||||||
|
it { is_expected.to_not permit(admin, UserRole.new(position: admin.user.role.position + 10, id: 123)) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not admin' do
|
||||||
|
it { is_expected.to_not permit(account, UserRole.new) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
permissions :destroy? do
|
||||||
|
context 'when admin' do
|
||||||
|
context 'when role of admin overrides relevant role' do
|
||||||
|
it { is_expected.to permit(admin, UserRole.new(position: admin.user.role.position - 10)) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when role of admin does not override relevant role' do
|
||||||
|
it { is_expected.to_not permit(admin, UserRole.new(position: admin.user.role.position + 10)) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when everyone role' do
|
||||||
|
it { is_expected.to_not permit(admin, UserRole.everyone) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not admin' do
|
||||||
|
it { is_expected.to_not permit(account, UserRole.new) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe WebhookPolicy do
|
RSpec.describe WebhookPolicy do
|
||||||
let(:policy) { described_class }
|
let(:policy) { described_class }
|
||||||
|
|
|
@ -43,6 +43,7 @@ require 'paperclip/matchers'
|
||||||
require 'capybara/rspec'
|
require 'capybara/rspec'
|
||||||
require 'chewy/rspec'
|
require 'chewy/rspec'
|
||||||
require 'email_spec/rspec'
|
require 'email_spec/rspec'
|
||||||
|
require 'pundit/rspec'
|
||||||
require 'test_prof/recipes/rspec/before_all'
|
require 'test_prof/recipes/rspec/before_all'
|
||||||
|
|
||||||
Rails.root.glob('spec/support/**/*.rb').each { |f| require f }
|
Rails.root.glob('spec/support/**/*.rb').each { |f| require f }
|
||||||
|
|
74
spec/requests/statuses/embed_spec.rb
Normal file
74
spec/requests/statuses/embed_spec.rb
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Status embed' do
|
||||||
|
describe 'GET /users/:account_username/statuses/:id/embed' do
|
||||||
|
subject { get "/users/#{account.username}/statuses/#{status.id}/embed" }
|
||||||
|
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
let(:status) { Fabricate(:status, account: account) }
|
||||||
|
|
||||||
|
context 'when account is suspended' do
|
||||||
|
let(:account) { Fabricate(:account, suspended: true) }
|
||||||
|
|
||||||
|
it 'returns http gone' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response)
|
||||||
|
.to have_http_status(410)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when status is a reblog' do
|
||||||
|
let(:original_account) { Fabricate(:account, domain: 'example.com') }
|
||||||
|
let(:original_status) { Fabricate(:status, account: original_account, url: 'https://example.com/123') }
|
||||||
|
let(:status) { Fabricate(:status, account: account, reblog: original_status) }
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response)
|
||||||
|
.to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when status is public' do
|
||||||
|
it 'renders status successfully', :aggregate_failures do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
expect(response.parsed_body.at('body.embed'))
|
||||||
|
.to be_present
|
||||||
|
expect(response.headers).to include(
|
||||||
|
'Vary' => 'Accept, Accept-Language, Cookie',
|
||||||
|
'Cache-Control' => include('public'),
|
||||||
|
'Link' => include('activity+json')
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when status is private' do
|
||||||
|
let(:status) { Fabricate(:status, account: account, visibility: :private) }
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response)
|
||||||
|
.to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when status is direct' do
|
||||||
|
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response)
|
||||||
|
.to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -14,7 +14,7 @@ RSpec.describe ActivityPub::NoteSerializer do
|
||||||
let!(:reply_by_account_third) { Fabricate(:status, account: account, thread: parent, visibility: :public) }
|
let!(:reply_by_account_third) { Fabricate(:status, account: account, thread: parent, visibility: :public) }
|
||||||
let!(:reply_by_account_visibility_direct) { Fabricate(:status, account: account, thread: parent, visibility: :direct) }
|
let!(:reply_by_account_visibility_direct) { Fabricate(:status, account: account, thread: parent, visibility: :direct) }
|
||||||
|
|
||||||
it 'has the expected shape' do
|
it 'has the expected shape and replies collection' do
|
||||||
expect(subject).to include({
|
expect(subject).to include({
|
||||||
'@context' => include('https://www.w3.org/ns/activitystreams'),
|
'@context' => include('https://www.w3.org/ns/activitystreams'),
|
||||||
'type' => 'Note',
|
'type' => 'Note',
|
||||||
|
@ -22,26 +22,23 @@ RSpec.describe ActivityPub::NoteSerializer do
|
||||||
'contentMap' => include({
|
'contentMap' => include({
|
||||||
'zh-TW' => a_kind_of(String),
|
'zh-TW' => a_kind_of(String),
|
||||||
}),
|
}),
|
||||||
|
'replies' => replies_collection_values,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has a replies collection' do
|
def replies_collection_values
|
||||||
expect(subject['replies']['type']).to eql('Collection')
|
include(
|
||||||
|
'type' => eql('Collection'),
|
||||||
|
'first' => include(
|
||||||
|
'type' => eql('CollectionPage'),
|
||||||
|
'items' => reply_items
|
||||||
|
)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has a replies collection with a first Page' do
|
def reply_items
|
||||||
expect(subject['replies']['first']['type']).to eql('CollectionPage')
|
include(reply_by_account_first.uri, reply_by_account_next.uri, reply_by_account_third.uri) # Public self replies
|
||||||
end
|
.and(not_include(reply_by_other_first.uri)) # Replies from others
|
||||||
|
.and(not_include(reply_by_account_visibility_direct.uri)) # Replies with direct visibility
|
||||||
it 'includes public self-replies in its replies collection' do
|
|
||||||
expect(subject['replies']['first']['items']).to include(reply_by_account_first.uri, reply_by_account_next.uri, reply_by_account_third.uri)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not include replies from others in its replies collection' do
|
|
||||||
expect(subject['replies']['first']['items']).to_not include(reply_by_other_first.uri)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not include replies with direct visibility in its replies collection' do
|
|
||||||
expect(subject['replies']['first']['items']).to_not include(reply_by_account_visibility_direct.uri)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,7 +18,7 @@ RSpec.describe TranslateStatusService do
|
||||||
describe '#call' do
|
describe '#call' do
|
||||||
before do
|
before do
|
||||||
translation_service = TranslationService.new
|
translation_service = TranslationService.new
|
||||||
allow(translation_service).to receive(:languages).and_return({ 'en' => ['es'] })
|
allow(translation_service).to receive(:languages).and_return({ 'en' => ['es', 'es-MX'] })
|
||||||
allow(translation_service).to receive(:translate) do |texts|
|
allow(translation_service).to receive(:translate) do |texts|
|
||||||
texts.map do |text|
|
texts.map do |text|
|
||||||
TranslationService::Translation.new(
|
TranslationService::Translation.new(
|
||||||
|
@ -37,6 +37,7 @@ RSpec.describe TranslateStatusService do
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
content: '<p>Hola</p>',
|
content: '<p>Hola</p>',
|
||||||
detected_source_language: 'en',
|
detected_source_language: 'en',
|
||||||
|
language: 'es',
|
||||||
provider: 'Dummy',
|
provider: 'Dummy',
|
||||||
status: status
|
status: status
|
||||||
)
|
)
|
||||||
|
@ -101,6 +102,16 @@ RSpec.describe TranslateStatusService do
|
||||||
expect(media_attachment.description).to eq 'Hola & :highfive:'
|
expect(media_attachment.description).to eq 'Hola & :highfive:'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'target language is regional' do
|
||||||
|
it 'uses regional variant' do
|
||||||
|
expect(service.call(status, 'es-MX').language).to eq 'es-MX'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses parent locale for unsupported regional variant' do
|
||||||
|
expect(service.call(status, 'es-XX').language).to eq 'es'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#source_texts' do
|
describe '#source_texts' do
|
||||||
|
|
63
spec/system/admin/invites_spec.rb
Normal file
63
spec/system/admin/invites_spec.rb
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Admin Invites' do
|
||||||
|
describe 'Invite interaction' do
|
||||||
|
let!(:invite) { Fabricate(:invite, expires_at: nil) }
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before { sign_in user }
|
||||||
|
|
||||||
|
it 'allows invite listing and creation' do
|
||||||
|
visit admin_invites_path
|
||||||
|
|
||||||
|
expect(page)
|
||||||
|
.to have_title(I18n.t('admin.invites.title'))
|
||||||
|
for_invite(invite) do
|
||||||
|
expect(find('input').value)
|
||||||
|
.to include(invite.code)
|
||||||
|
end
|
||||||
|
|
||||||
|
select I18n.t('invites.max_uses', count: 10), from: max_use_field
|
||||||
|
|
||||||
|
expect { generate_invite }
|
||||||
|
.to change(Invite, :count).by(1)
|
||||||
|
expect(user.invites.last)
|
||||||
|
.to have_attributes(max_uses: 10)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows invite expiration' do
|
||||||
|
visit admin_invites_path
|
||||||
|
|
||||||
|
for_invite(invite) do
|
||||||
|
expect { expire_invite }
|
||||||
|
.to change { invite.reload.expired? }.from(false).to(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows invite deactivation' do
|
||||||
|
visit admin_invites_path
|
||||||
|
|
||||||
|
expect { click_on I18n.t('admin.invites.deactivate_all') }
|
||||||
|
.to change { Invite.exists?(expires_at: nil) }.from(true).to(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_invite(invite, &block)
|
||||||
|
within("#invite_#{invite.id}", &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def expire_invite
|
||||||
|
click_on I18n.t('invites.delete')
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_invite
|
||||||
|
click_on I18n.t('invites.generate')
|
||||||
|
end
|
||||||
|
|
||||||
|
def max_use_field
|
||||||
|
I18n.t('simple_form.labels.defaults.max_uses')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
38
spec/system/admin/tags_spec.rb
Normal file
38
spec/system/admin/tags_spec.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Admin Tags' do
|
||||||
|
describe 'Tag interaction' do
|
||||||
|
let!(:tag) { Fabricate(:tag, name: 'test') }
|
||||||
|
|
||||||
|
before { sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
it 'allows tags listing and editing' do
|
||||||
|
visit admin_tags_path
|
||||||
|
|
||||||
|
expect(page)
|
||||||
|
.to have_title(I18n.t('admin.tags.title'))
|
||||||
|
|
||||||
|
click_on '#test'
|
||||||
|
|
||||||
|
fill_in display_name_field, with: 'NewTagName'
|
||||||
|
expect { click_on submit_button }
|
||||||
|
.to_not(change { tag.reload.display_name })
|
||||||
|
expect(page)
|
||||||
|
.to have_content(match_error_text)
|
||||||
|
|
||||||
|
fill_in display_name_field, with: 'TEST'
|
||||||
|
expect { click_on submit_button }
|
||||||
|
.to(change { tag.reload.display_name }.to('TEST'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def display_name_field
|
||||||
|
I18n.t('simple_form.labels.defaults.display_name')
|
||||||
|
end
|
||||||
|
|
||||||
|
def match_error_text
|
||||||
|
I18n.t('tags.does_not_match_previous_name')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,9 +5,50 @@ require 'rails_helper'
|
||||||
RSpec.describe Scheduler::IpCleanupScheduler do
|
RSpec.describe Scheduler::IpCleanupScheduler do
|
||||||
let(:worker) { described_class.new }
|
let(:worker) { described_class.new }
|
||||||
|
|
||||||
describe 'perform' do
|
describe '#perform' do
|
||||||
it 'runs without error' do
|
context 'with IP-related data past retention times' do
|
||||||
expect { worker.perform }.to_not raise_error
|
let!(:future_ip_block) { Fabricate :ip_block, expires_at: 1.week.from_now }
|
||||||
|
let!(:old_ip_block) { Fabricate :ip_block, expires_at: 1.week.ago }
|
||||||
|
let!(:session_past_retention) { Fabricate :session_activation, ip: '10.0.0.0', updated_at: 18.months.ago }
|
||||||
|
let!(:inactive_user) { Fabricate :user, current_sign_in_at: 18.months.ago, sign_up_ip: '10.0.0.0' }
|
||||||
|
let!(:old_login_activity) { Fabricate :login_activity, created_at: 18.months.ago }
|
||||||
|
let!(:old_token) { Fabricate :access_token, last_used_at: 18.months.ago, last_used_ip: '10.0.0.0' }
|
||||||
|
|
||||||
|
before { stub_const 'Scheduler::IpCleanupScheduler::SESSION_RETENTION_PERIOD', 10.years.to_i.seconds }
|
||||||
|
|
||||||
|
it 'deletes the expired block' do
|
||||||
|
expect { worker.perform }
|
||||||
|
.to_not raise_error
|
||||||
|
expect { old_ip_block.reload }
|
||||||
|
.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
expect { old_login_activity.reload }
|
||||||
|
.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
expect(session_past_retention.reload.ip)
|
||||||
|
.to be_nil
|
||||||
|
expect(inactive_user.reload.sign_up_ip)
|
||||||
|
.to be_nil
|
||||||
|
expect(old_token.reload.last_used_ip)
|
||||||
|
.to be_nil
|
||||||
|
expect(future_ip_block.reload)
|
||||||
|
.to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with old session data' do
|
||||||
|
let!(:new_activation) { Fabricate :session_activation, updated_at: 1.week.ago }
|
||||||
|
let!(:old_activation) { Fabricate :session_activation, updated_at: 1.month.ago }
|
||||||
|
|
||||||
|
before { stub_const 'Scheduler::IpCleanupScheduler::SESSION_RETENTION_PERIOD', 10.days.to_i.seconds }
|
||||||
|
|
||||||
|
it 'clears old sessions' do
|
||||||
|
expect { worker.perform }
|
||||||
|
.to_not raise_error
|
||||||
|
|
||||||
|
expect { old_activation.reload }
|
||||||
|
.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
expect(new_activation.reload)
|
||||||
|
.to be_present
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,9 +50,9 @@ function getSentinelConfiguration(env, commonOptions) {
|
||||||
return {
|
return {
|
||||||
db: redisDatabase,
|
db: redisDatabase,
|
||||||
name: env.REDIS_SENTINEL_MASTER,
|
name: env.REDIS_SENTINEL_MASTER,
|
||||||
username: env.REDIS_USERNAME,
|
username: env.REDIS_USER,
|
||||||
password: env.REDIS_PASSWORD,
|
password: env.REDIS_PASSWORD,
|
||||||
sentinelUsername: env.REDIS_SENTINEL_USERNAME ?? env.REDIS_USERNAME,
|
sentinelUsername: env.REDIS_SENTINEL_USERNAME ?? env.REDIS_USER,
|
||||||
sentinelPassword: env.REDIS_SENTINEL_PASSWORD ?? env.REDIS_PASSWORD,
|
sentinelPassword: env.REDIS_SENTINEL_PASSWORD ?? env.REDIS_PASSWORD,
|
||||||
sentinels,
|
sentinels,
|
||||||
...commonOptions,
|
...commonOptions,
|
||||||
|
@ -104,7 +104,7 @@ export function configFromEnv(env) {
|
||||||
host: env.REDIS_HOST ?? '127.0.0.1',
|
host: env.REDIS_HOST ?? '127.0.0.1',
|
||||||
port: redisPort,
|
port: redisPort,
|
||||||
db: redisDatabase,
|
db: redisDatabase,
|
||||||
username: env.REDIS_USERNAME,
|
username: env.REDIS_USER,
|
||||||
password: env.REDIS_PASSWORD,
|
password: env.REDIS_PASSWORD,
|
||||||
...commonOptions,
|
...commonOptions,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue