Merge branch 'master' into glitch-soc/merge-upstream
Conflicts: - `app/controllers/activitypub/collections_controller.rb`: Conflict caused because we have additional code to make sure pinned local-only toots don't get rendered on the ActivityPub endpoints. Ported upstream changes.
This commit is contained in:
		
						commit
						e5f934ddf0
					
				
					 18 changed files with 128 additions and 138 deletions
				
			
		|  | @ -12,7 +12,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController | |||
| 
 | ||||
|   def show | ||||
|     expires_in 3.minutes, public: public_fetch_mode? | ||||
|     render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true | ||||
|     render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
|  | @ -20,17 +20,9 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController | |||
|   def set_items | ||||
|     case params[:id] | ||||
|     when 'featured' | ||||
|       @items = begin | ||||
|         # Because in public fetch mode we cache the response, there would be no | ||||
|         # benefit from performing the check below, since a blocked account or domain | ||||
|         # would likely be served the cache from the reverse proxy anyway | ||||
| 
 | ||||
|         if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain))) | ||||
|           [] | ||||
|         else | ||||
|           cache_collection(@account.pinned_statuses.not_local_only, Status) | ||||
|         end | ||||
|       end | ||||
|       @items = for_signed_account { cache_collection(@account.pinned_statuses.not_local_only, Status) } | ||||
|     when 'tags' | ||||
|       @items = for_signed_account { @account.featured_tags } | ||||
|     when 'devices' | ||||
|       @items = @account.devices | ||||
|     else | ||||
|  | @ -40,7 +32,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController | |||
| 
 | ||||
|   def set_size | ||||
|     case params[:id] | ||||
|     when 'featured', 'devices' | ||||
|     when 'featured', 'devices', 'tags' | ||||
|       @size = @items.size | ||||
|     else | ||||
|       not_found | ||||
|  | @ -51,7 +43,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController | |||
|     case params[:id] | ||||
|     when 'featured' | ||||
|       @type = :ordered | ||||
|     when 'devices' | ||||
|     when 'devices', 'tags' | ||||
|       @type = :unordered | ||||
|     else | ||||
|       not_found | ||||
|  | @ -66,4 +58,16 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController | |||
|       items: @items | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   def for_signed_account | ||||
|     # Because in public fetch mode we cache the response, there would be no | ||||
|     # benefit from performing the check below, since a blocked account or domain | ||||
|     # would likely be served the cache from the reverse proxy anyway | ||||
| 
 | ||||
|     if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain))) | ||||
|       [] | ||||
|     else | ||||
|       yield | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -20,9 +20,9 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController | |||
|   def outbox_presenter | ||||
|     if page_requested? | ||||
|       ActivityPub::CollectionPresenter.new( | ||||
|         id: account_outbox_url(@account, page_params), | ||||
|         id: outbox_url(page_params), | ||||
|         type: :ordered, | ||||
|         part_of: account_outbox_url(@account), | ||||
|         part_of: outbox_url, | ||||
|         prev: prev_page, | ||||
|         next: next_page, | ||||
|         items: @statuses | ||||
|  | @ -32,12 +32,20 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController | |||
|         id: account_outbox_url(@account), | ||||
|         type: :ordered, | ||||
|         size: @account.statuses_count, | ||||
|         first: account_outbox_url(@account, page: true), | ||||
|         last: account_outbox_url(@account, page: true, min_id: 0) | ||||
|         first: outbox_url(page: true), | ||||
|         last: outbox_url(page: true, min_id: 0) | ||||
|       ) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def outbox_url(**kwargs) | ||||
|     if params[:account_username].present? | ||||
|       account_outbox_url(@account, **kwargs) | ||||
|     else | ||||
|       instance_actor_outbox_url(**kwargs) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def next_page | ||||
|     account_outbox_url(@account, page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT | ||||
|   end | ||||
|  | @ -65,4 +73,8 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController | |||
|   def page_params | ||||
|     { page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact | ||||
|   end | ||||
| 
 | ||||
|   def set_account | ||||
|     @account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative | ||||
|   end | ||||
| end | ||||
|  |  | |||
							
								
								
									
										22
									
								
								app/controllers/api/v1/accounts/featured_tags_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/controllers/api/v1/accounts/featured_tags_controller.rb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class Api::V1::Accounts::FeaturedTagsController < Api::BaseController | ||||
|   before_action :set_account | ||||
|   before_action :set_featured_tags | ||||
| 
 | ||||
|   respond_to :json | ||||
| 
 | ||||
|   def index | ||||
|     render json: @featured_tags, each_serializer: REST::AccountFeaturedTagSerializer | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def set_account | ||||
|     @account = Account.find(params[:account_id]) | ||||
|   end | ||||
| 
 | ||||
|   def set_featured_tags | ||||
|     @featured_tags = @account.featured_tags | ||||
|   end | ||||
| end | ||||
|  | @ -17,6 +17,6 @@ class InstanceActorsController < ApplicationController | |||
|   end | ||||
| 
 | ||||
|   def restrict_fields_to | ||||
|     %i(id type preferred_username inbox public_key endpoints url manually_approves_followers) | ||||
|     %i(id type preferred_username inbox outbox public_key endpoints url manually_approves_followers) | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import api, { getLinks } from '../api'; | ||||
| import openDB from '../storage/db'; | ||||
| import { importAccount, importFetchedAccount, importFetchedAccounts } from './importer'; | ||||
| import { importFetchedAccount, importFetchedAccounts } from './importer'; | ||||
| 
 | ||||
| export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'; | ||||
| export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS'; | ||||
|  | @ -74,45 +73,13 @@ export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST'; | |||
| export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS'; | ||||
| export const FOLLOW_REQUEST_REJECT_FAIL    = 'FOLLOW_REQUEST_REJECT_FAIL'; | ||||
| 
 | ||||
| function getFromDB(dispatch, getState, index, id) { | ||||
|   return new Promise((resolve, reject) => { | ||||
|     const request = index.get(id); | ||||
| 
 | ||||
|     request.onerror = reject; | ||||
| 
 | ||||
|     request.onsuccess = () => { | ||||
|       if (!request.result) { | ||||
|         reject(); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       dispatch(importAccount(request.result)); | ||||
|       resolve(request.result.moved && getFromDB(dispatch, getState, index, request.result.moved)); | ||||
|     }; | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| export function fetchAccount(id) { | ||||
|   return (dispatch, getState) => { | ||||
|     dispatch(fetchRelationships([id])); | ||||
| 
 | ||||
|     if (getState().getIn(['accounts', id], null) !== null) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     dispatch(fetchAccountRequest(id)); | ||||
| 
 | ||||
|     openDB().then(db => getFromDB( | ||||
|       dispatch, | ||||
|       getState, | ||||
|       db.transaction('accounts', 'read').objectStore('accounts').index('id'), | ||||
|       id, | ||||
|     ).then(() => db.close(), error => { | ||||
|       db.close(); | ||||
|       throw error; | ||||
|     })).catch(() => api(getState).get(`/api/v1/accounts/${id}`).then(response => { | ||||
|     api(getState).get(`/api/v1/accounts/${id}`).then(response => { | ||||
|       dispatch(importFetchedAccount(response.data)); | ||||
|     })).then(() => { | ||||
|       dispatch(fetchAccountSuccess()); | ||||
|     }).catch(error => { | ||||
|       dispatch(fetchAccountFail(id, error)); | ||||
|  |  | |||
|  | @ -1,9 +1,7 @@ | |||
| import api from '../api'; | ||||
| import openDB from '../storage/db'; | ||||
| import { evictStatus } from '../storage/modifier'; | ||||
| 
 | ||||
| import { deleteFromTimelines } from './timelines'; | ||||
| import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus, importFetchedAccount } from './importer'; | ||||
| import { importFetchedStatus, importFetchedStatuses, importFetchedAccount } from './importer'; | ||||
| import { ensureComposeIsVisible } from './compose'; | ||||
| 
 | ||||
| export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST'; | ||||
|  | @ -40,48 +38,6 @@ export function fetchStatusRequest(id, skipLoading) { | |||
|   }; | ||||
| }; | ||||
| 
 | ||||
| function getFromDB(dispatch, getState, accountIndex, index, id) { | ||||
|   return new Promise((resolve, reject) => { | ||||
|     const request = index.get(id); | ||||
| 
 | ||||
|     request.onerror = reject; | ||||
| 
 | ||||
|     request.onsuccess = () => { | ||||
|       const promises = []; | ||||
| 
 | ||||
|       if (!request.result) { | ||||
|         reject(); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       dispatch(importStatus(request.result)); | ||||
| 
 | ||||
|       if (getState().getIn(['accounts', request.result.account], null) === null) { | ||||
|         promises.push(new Promise((accountResolve, accountReject) => { | ||||
|           const accountRequest = accountIndex.get(request.result.account); | ||||
| 
 | ||||
|           accountRequest.onerror = accountReject; | ||||
|           accountRequest.onsuccess = () => { | ||||
|             if (!request.result) { | ||||
|               accountReject(); | ||||
|               return; | ||||
|             } | ||||
| 
 | ||||
|             dispatch(importAccount(accountRequest.result)); | ||||
|             accountResolve(); | ||||
|           }; | ||||
|         })); | ||||
|       } | ||||
| 
 | ||||
|       if (request.result.reblog && getState().getIn(['statuses', request.result.reblog], null) === null) { | ||||
|         promises.push(getFromDB(dispatch, getState, accountIndex, index, request.result.reblog)); | ||||
|       } | ||||
| 
 | ||||
|       resolve(Promise.all(promises)); | ||||
|     }; | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| export function fetchStatus(id) { | ||||
|   return (dispatch, getState) => { | ||||
|     const skipLoading = getState().getIn(['statuses', id], null) !== null; | ||||
|  | @ -94,23 +50,10 @@ export function fetchStatus(id) { | |||
| 
 | ||||
|     dispatch(fetchStatusRequest(id, skipLoading)); | ||||
| 
 | ||||
|     openDB().then(db => { | ||||
|       const transaction = db.transaction(['accounts', 'statuses'], 'read'); | ||||
|       const accountIndex = transaction.objectStore('accounts').index('id'); | ||||
|       const index = transaction.objectStore('statuses').index('id'); | ||||
| 
 | ||||
|       return getFromDB(dispatch, getState, accountIndex, index, id).then(() => { | ||||
|         db.close(); | ||||
|       }, error => { | ||||
|         db.close(); | ||||
|         throw error; | ||||
|       }); | ||||
|     }).then(() => { | ||||
|       dispatch(fetchStatusSuccess(skipLoading)); | ||||
|     }, () => api(getState).get(`/api/v1/statuses/${id}`).then(response => { | ||||
|     api(getState).get(`/api/v1/statuses/${id}`).then(response => { | ||||
|       dispatch(importFetchedStatus(response.data)); | ||||
|       dispatch(fetchStatusSuccess(skipLoading)); | ||||
|     })).catch(error => { | ||||
|     }).catch(error => { | ||||
|       dispatch(fetchStatusFail(id, error, skipLoading)); | ||||
|     }); | ||||
|   }; | ||||
|  | @ -152,7 +95,6 @@ export function deleteStatus(id, routerHistory, withRedraft = false) { | |||
|     dispatch(deleteStatusRequest(id)); | ||||
| 
 | ||||
|     api(getState).delete(`/api/v1/statuses/${id}`).then(response => { | ||||
|       evictStatus(id); | ||||
|       dispatch(deleteStatusSuccess(id)); | ||||
|       dispatch(deleteFromTimelines(id)); | ||||
|       dispatch(importFetchedAccount(response.data.account)); | ||||
|  |  | |||
|  | @ -256,14 +256,6 @@ html { | |||
|   background: $ui-base-color; | ||||
| } | ||||
| 
 | ||||
| .status.status-direct { | ||||
|   background: lighten($ui-base-color, 4%); | ||||
| } | ||||
| 
 | ||||
| .focusable:focus .status.status-direct { | ||||
|   background: lighten($ui-base-color, 8%); | ||||
| } | ||||
| 
 | ||||
| .detailed-status, | ||||
| .detailed-status__action-bar { | ||||
|   background: $white; | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base | |||
|     moved_to: { 'movedTo' => { '@id' => 'as:movedTo', '@type' => '@id' } }, | ||||
|     also_known_as: { 'alsoKnownAs' => { '@id' => 'as:alsoKnownAs', '@type' => '@id' } }, | ||||
|     emoji: { 'toot' => 'http://joinmastodon.org/ns#', 'Emoji' => 'toot:Emoji' }, | ||||
|     featured: { 'toot' => 'http://joinmastodon.org/ns#', 'featured' => { '@id' => 'toot:featured', '@type' => '@id' } }, | ||||
|     featured: { 'toot' => 'http://joinmastodon.org/ns#', 'featured' => { '@id' => 'toot:featured', '@type' => '@id' }, 'featuredTags' => { '@id' => 'toot:featuredTags', '@type' => '@id' } }, | ||||
|     property_value: { 'schema' => 'http://schema.org#', 'PropertyValue' => 'schema:PropertyValue', 'value' => 'schema:value' }, | ||||
|     atom_uri: { 'ostatus' => 'http://ostatus.org#', 'atomUri' => 'ostatus:atomUri' }, | ||||
|     conversation: { 'ostatus' => 'http://ostatus.org#', 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri', 'conversation' => 'ostatus:conversation' }, | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer | |||
|                      :discoverable, :olm | ||||
| 
 | ||||
|   attributes :id, :type, :following, :followers, | ||||
|              :inbox, :outbox, :featured, | ||||
|              :inbox, :outbox, :featured, :featured_tags, | ||||
|              :preferred_username, :name, :summary, | ||||
|              :url, :manually_approves_followers, | ||||
|              :discoverable | ||||
|  | @ -74,13 +74,17 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer | |||
|   end | ||||
| 
 | ||||
|   def outbox | ||||
|     account_outbox_url(object) | ||||
|     object.instance_actor? ? instance_actor_outbox_url : account_outbox_url(object) | ||||
|   end | ||||
| 
 | ||||
|   def featured | ||||
|     account_collection_url(object, :featured) | ||||
|   end | ||||
| 
 | ||||
|   def featured_tags | ||||
|     account_collection_url(object, :tags) | ||||
|   end | ||||
| 
 | ||||
|   def endpoints | ||||
|     object | ||||
|   end | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ class ActivityPub::CollectionSerializer < ActivityPub::Serializer | |||
|       ActivityPub::NoteSerializer | ||||
|     when 'Device' | ||||
|       ActivityPub::DeviceSerializer | ||||
|     when 'FeaturedTag' | ||||
|       ActivityPub::HashtagSerializer | ||||
|     when 'ActivityPub::CollectionPresenter' | ||||
|       ActivityPub::CollectionSerializer | ||||
|     when 'String' | ||||
|  |  | |||
							
								
								
									
										23
									
								
								app/serializers/activitypub/hashtag_serializer.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/serializers/activitypub/hashtag_serializer.rb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class ActivityPub::HashtagSerializer < ActivityPub::Serializer | ||||
|   include RoutingHelper | ||||
| 
 | ||||
|   attributes :type, :href, :name | ||||
| 
 | ||||
|   def type | ||||
|     'Hashtag' | ||||
|   end | ||||
| 
 | ||||
|   def name | ||||
|     "##{object.name}" | ||||
|   end | ||||
| 
 | ||||
|   def href | ||||
|     if object.class.name == 'FeaturedTag' | ||||
|       short_account_tag_url(object.account, object.tag) | ||||
|     else | ||||
|       tag_url(object) | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										19
									
								
								app/serializers/rest/account_featured_tag_serializer.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/serializers/rest/account_featured_tag_serializer.rb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class REST::AccountFeaturedTagSerializer < ActiveModel::Serializer | ||||
|   include RoutingHelper | ||||
| 
 | ||||
|   attributes :id, :name, :url | ||||
| 
 | ||||
|   def id | ||||
|     object.tag.id.to_s | ||||
|   end | ||||
| 
 | ||||
|   def name | ||||
|     "##{object.name}" | ||||
|   end | ||||
| 
 | ||||
|   def url | ||||
|     short_account_tag_url(object.account, object.tag) | ||||
|   end | ||||
| end | ||||
|  | @ -37,6 +37,7 @@ Rails.application.routes.draw do | |||
| 
 | ||||
|   resource :instance_actor, path: 'actor', only: [:show] do | ||||
|     resource :inbox, only: [:create], module: :activitypub | ||||
|     resource :outbox, only: [:show], module: :activitypub | ||||
|   end | ||||
| 
 | ||||
|   devise_scope :user do | ||||
|  | @ -438,6 +439,7 @@ Rails.application.routes.draw do | |||
|         resources :following, only: :index, controller: 'accounts/following_accounts' | ||||
|         resources :lists, only: :index, controller: 'accounts/lists' | ||||
|         resources :identity_proofs, only: :index, controller: 'accounts/identity_proofs' | ||||
|         resources :featured_tags, only: :index, controller: 'accounts/featured_tags' | ||||
| 
 | ||||
|         member do | ||||
|           post :follow | ||||
|  |  | |||
|  | @ -67,6 +67,7 @@ module Mastodon | |||
|       when :s3 | ||||
|         paperclip_instance = MediaAttachment.new.file | ||||
|         s3_interface       = paperclip_instance.s3_interface | ||||
|         s3_permissions     = Paperclip::Attachment.default_options[:s3_permissions] | ||||
|         bucket             = s3_interface.bucket(Paperclip::Attachment.default_options[:s3_credentials][:bucket]) | ||||
|         last_key           = options[:start_after] | ||||
| 
 | ||||
|  | @ -87,7 +88,7 @@ module Mastodon | |||
|           record_map = preload_records_from_mixed_objects(objects) | ||||
| 
 | ||||
|           objects.each do |object| | ||||
|             object.acl.put(acl: 'public-read') if options[:fix_permissions] && !options[:dry_run] | ||||
|             object.acl.put(acl: s3_permissions) if options[:fix_permissions] && !options[:dry_run] | ||||
| 
 | ||||
|             path_segments = object.key.split('/') | ||||
|             path_segments.delete('cache') | ||||
|  |  | |||
|  | @ -448,7 +448,7 @@ RSpec.describe FeedManager do | |||
|       FeedManager.instance.push_to_home(receiver, another_status) | ||||
| 
 | ||||
|       # We should have a tracking set and an entry in reblogs. | ||||
|       expect(Redis.current.exists(reblog_set_key)).to be true | ||||
|       expect(Redis.current.exists?(reblog_set_key)).to be true | ||||
|       expect(Redis.current.zrange(reblogs_key, 0, -1)).to eq [reblogged.id.to_s] | ||||
| 
 | ||||
|       # Push everything off the end of the feed. | ||||
|  | @ -461,7 +461,7 @@ RSpec.describe FeedManager do | |||
|       FeedManager.instance.trim('home', receiver.id) | ||||
| 
 | ||||
|       # We should not have any reblog tracking data. | ||||
|       expect(Redis.current.exists(reblog_set_key)).to be false | ||||
|       expect(Redis.current.exists?(reblog_set_key)).to be false | ||||
|       expect(Redis.current.zrange(reblogs_key, 0, -1)).to be_empty | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -150,9 +150,9 @@ RSpec.describe SpamCheck do | |||
|     let(:redis_key) { spam_check.send(:redis_key) } | ||||
| 
 | ||||
|     it 'remembers' do | ||||
|       expect(Redis.current.exists(redis_key)).to be true | ||||
|       expect(Redis.current.exists?(redis_key)).to be true | ||||
|       spam_check.remember! | ||||
|       expect(Redis.current.exists(redis_key)).to be true | ||||
|       expect(Redis.current.exists?(redis_key)).to be true | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|  | @ -166,9 +166,9 @@ RSpec.describe SpamCheck do | |||
|     end | ||||
| 
 | ||||
|     it 'resets' do | ||||
|       expect(Redis.current.exists(redis_key)).to be true | ||||
|       expect(Redis.current.exists?(redis_key)).to be true | ||||
|       spam_check.reset! | ||||
|       expect(Redis.current.exists(redis_key)).to be false | ||||
|       expect(Redis.current.exists?(redis_key)).to be false | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -55,9 +55,9 @@ RSpec.describe UnallowDomainService, type: :service do | |||
|       end | ||||
| 
 | ||||
|       it 'removes the remote accounts\'s statuses and media attachments' do | ||||
|         expect { bad_status1.reload }.to_not raise_exception ActiveRecord::RecordNotFound | ||||
|         expect { bad_status2.reload }.to_not raise_exception ActiveRecord::RecordNotFound | ||||
|         expect { bad_attachment.reload }.to_not raise_exception ActiveRecord::RecordNotFound | ||||
|         expect { bad_status1.reload }.to_not raise_error | ||||
|         expect { bad_status2.reload }.to_not raise_error | ||||
|         expect { bad_attachment.reload }.to_not raise_error | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -16,8 +16,8 @@ describe Scheduler::FeedCleanupScheduler do | |||
| 
 | ||||
|     expect(Redis.current.zcard(feed_key_for(inactive_user))).to eq 0 | ||||
|     expect(Redis.current.zcard(feed_key_for(active_user))).to eq 1 | ||||
|     expect(Redis.current.exists(feed_key_for(inactive_user, 'reblogs'))).to be false | ||||
|     expect(Redis.current.exists(feed_key_for(inactive_user, 'reblogs:2'))).to be false | ||||
|     expect(Redis.current.exists?(feed_key_for(inactive_user, 'reblogs'))).to be false | ||||
|     expect(Redis.current.exists?(feed_key_for(inactive_user, 'reblogs:2'))).to be false | ||||
|   end | ||||
| 
 | ||||
|   def feed_key_for(user, subtype = nil) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue