about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJenkins <jenkins@jenkins.ninjawedding.org>2018-02-19 15:17:14 +0000
committerJenkins <jenkins@jenkins.ninjawedding.org>2018-02-19 15:17:14 +0000
commit1445ba17033cdfdd21d0999f65267754af6344af (patch)
tree4919ecd746c8dec4be33af47d83f11cec098cf69
parent3d033a468748338b6036cb24bb00ea4e88656ae6 (diff)
parent66105929e07fc7ddbdb8b66696b9ce1ed5d25957 (diff)
Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master
-rw-r--r--.travis.yml2
-rw-r--r--app/controllers/api/v1/accounts/credentials_controller.rb2
-rw-r--r--app/javascript/mastodon/actions/search.js2
-rw-r--r--app/javascript/mastodon/components/media_gallery.js6
-rw-r--r--app/javascript/styles/mastodon/basics.scss1
-rw-r--r--app/javascript/styles/mastodon/components.scss20
-rw-r--r--app/javascript/styles/mastodon/stream_entries.scss20
-rw-r--r--app/lib/activitypub/activity.rb2
-rw-r--r--app/lib/ostatus/activity/creation.rb2
-rw-r--r--app/models/account_domain_block.rb8
-rw-r--r--app/models/block.rb4
-rw-r--r--app/models/concerns/account_avatar.rb12
-rw-r--r--app/models/concerns/account_header.rb12
-rw-r--r--app/models/concerns/relationship_cacheable.rb16
-rw-r--r--app/models/follow.rb1
-rw-r--r--app/models/follow_request.rb1
-rw-r--r--app/models/mute.rb4
-rw-r--r--app/models/status.rb6
-rw-r--r--app/presenters/account_relationships_presenter.rb68
-rw-r--r--config/environments/test.rb4
-rw-r--r--spec/mailers/notification_mailer_spec.rb2
-rw-r--r--spec/models/setting_spec.rb13
-rw-r--r--spec/presenters/instance_presenter_spec.rb12
-rw-r--r--spec/rails_helper.rb2
-rw-r--r--spec/services/fetch_remote_status_service_spec.rb31
25 files changed, 193 insertions, 60 deletions
diff --git a/.travis.yml b/.travis.yml
index 2aa647e9a..8a0931c26 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,7 +19,6 @@ env:
     - LOCAL_HTTPS=true
     - RAILS_ENV=test
     - PARALLEL_TEST_PROCESSORS=2
-    - "PATH=$HOME:$PATH"
 
 addons:
   postgresql: 9.4
@@ -49,7 +48,6 @@ install:
 
 before_script:
   - ./bin/rails parallel:create parallel:load_schema parallel:prepare assets:precompile
-  - ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
 
 script:
   - travis_retry bundle exec parallel_test spec/ --group-by filesize --type rspec
diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb
index da534d960..68af22529 100644
--- a/app/controllers/api/v1/accounts/credentials_controller.rb
+++ b/app/controllers/api/v1/accounts/credentials_controller.rb
@@ -20,6 +20,6 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
   private
 
   def account_params
-    params.permit(:display_name, :note, :avatar, :header)
+    params.permit(:display_name, :note, :avatar, :header, :locked)
   end
 end
diff --git a/app/javascript/mastodon/actions/search.js b/app/javascript/mastodon/actions/search.js
index 78c6109f7..73cb106ec 100644
--- a/app/javascript/mastodon/actions/search.js
+++ b/app/javascript/mastodon/actions/search.js
@@ -1,4 +1,5 @@
 import api from '../api';
+import { fetchRelationships } from './accounts';
 
 export const SEARCH_CHANGE = 'SEARCH_CHANGE';
 export const SEARCH_CLEAR  = 'SEARCH_CLEAR';
@@ -38,6 +39,7 @@ export function submitSearch() {
       },
     }).then(response => {
       dispatch(fetchSearchSuccess(response.data));
+      dispatch(fetchRelationships(response.data.accounts.map(item => item.id)));
     }).catch(error => {
       dispatch(fetchSearchFail(error));
     });
diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js
index 00943e205..a3ffc45ea 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -227,12 +227,8 @@ export default class MediaGallery extends React.PureComponent {
     const style = {};
 
     if (this.isStandaloneEligible()) {
-      if (!visible && width) {
-        // only need to forcibly set the height in "sensitive" mode
+      if (width) {
         style.height = width / this.props.media.getIn([0, 'meta', 'small', 'aspect']);
-      } else {
-        // layout automatically, using image's natural aspect ratio
-        style.height = '';
       }
     } else {
       // crop the image
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index 0e3b9ad6b..bec0d4d91 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -122,5 +122,6 @@ button {
     height: 100%;
     align-items: center;
     justify-content: center;
+    outline: 0 !important;
   }
 }
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index d57481f97..66e4adc2b 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -686,12 +686,13 @@
   background: transparent;
   border: 0;
   color: lighten($ui-base-color, 8%);
-  font-weight: 500;
+  font-weight: 700;
   font-size: 11px;
   padding: 0 6px;
   text-transform: uppercase;
-  line-height: inherit;
+  line-height: 20px;
   cursor: pointer;
+  vertical-align: middle;
 }
 
 .status__prepend-icon-wrapper {
@@ -899,6 +900,11 @@
       height: 24px;
       margin: -1px 0 0;
     }
+
+    .status__content__spoiler-link {
+      line-height: 24px;
+      margin: -1px 0 0;
+    }
   }
 
   .video-player {
@@ -2667,12 +2673,16 @@ a.status-card {
   background: $base-overlay-background;
   color: $ui-primary-color;
   border: 0;
+  padding: 0;
   width: 100%;
   height: 100%;
+  border-radius: 4px;
+  appearance: none;
 
   &:hover,
   &:active,
   &:focus {
+    padding: 0;
     color: lighten($ui-primary-color, 8%);
   }
 }
@@ -2685,7 +2695,7 @@ a.status-card {
 .media-spoiler__trigger {
   display: block;
   font-size: 11px;
-  font-weight: 500;
+  font-weight: 700;
 }
 
 .spoiler-button {
@@ -4091,6 +4101,7 @@ a.status-card {
   box-sizing: border-box;
   margin-top: 8px;
   overflow: hidden;
+  border-radius: 4px;
   position: relative;
   width: 100%;
 }
@@ -4101,6 +4112,8 @@ a.status-card {
   display: block;
   float: left;
   position: relative;
+  border-radius: 4px;
+  overflow: hidden;
 
   &.standalone {
     .media-gallery__item-gifv-thumbnail {
@@ -4113,6 +4126,7 @@ a.status-card {
   cursor: zoom-in;
   display: block;
   text-decoration: none;
+  color: $ui-secondary-color;
   height: 100%;
   line-height: 0;
 
diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss
index e11ca29d9..442b143a0 100644
--- a/app/javascript/styles/mastodon/stream_entries.scss
+++ b/app/javascript/styles/mastodon/stream_entries.scss
@@ -146,10 +146,10 @@
 
       a.status__content__spoiler-link {
         color: $primary-text-color;
-        background: $ui-primary-color;
+        background: $ui-base-color;
 
         &:hover {
-          background: lighten($ui-primary-color, 8%);
+          background: lighten($ui-base-color, 8%);
         }
       }
     }
@@ -214,10 +214,10 @@
 
       a.status__content__spoiler-link {
         color: $primary-text-color;
-        background: $ui-primary-color;
+        background: $ui-base-color;
 
         &:hover {
-          background: lighten($ui-primary-color, 8%);
+          background: lighten($ui-base-color, 8%);
         }
       }
     }
@@ -260,16 +260,8 @@
   }
 
   .media-spoiler {
-    background: $ui-primary-color;
-    color: $white;
-    transition: all 40ms linear;
-
-    &:hover,
-    &:active,
-    &:focus {
-      background: darken($ui-primary-color, 2%);
-      color: unset;
-    }
+    background: $ui-base-color;
+    color: $ui-primary-color;
   }
 
   .pre-header {
diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb
index 0f9e4f263..4617905c6 100644
--- a/app/lib/activitypub/activity.rb
+++ b/app/lib/activitypub/activity.rb
@@ -74,7 +74,7 @@ class ActivityPub::Activity
 
     # Only continue if the status is supposed to have
     # arrived in real-time
-    return unless @options[:override_timestamps]
+    return unless @options[:override_timestamps] || status.within_realtime_window?
 
     distribute_to_followers(status)
   end
diff --git a/app/lib/ostatus/activity/creation.rb b/app/lib/ostatus/activity/creation.rb
index b38407cd3..7cf2d90dc 100644
--- a/app/lib/ostatus/activity/creation.rb
+++ b/app/lib/ostatus/activity/creation.rb
@@ -61,7 +61,7 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
     Rails.logger.debug "Queuing remote status #{status.id} (#{id}) for distribution"
 
     LinkCrawlWorker.perform_async(status.id) unless status.spoiler_text?
-    DistributionWorker.perform_async(status.id) if @options[:override_timestamps]
+    DistributionWorker.perform_async(status.id) if @options[:override_timestamps] || status.within_realtime_window?
 
     status
   end
diff --git a/app/models/account_domain_block.rb b/app/models/account_domain_block.rb
index abcc923b3..bc00b4f32 100644
--- a/app/models/account_domain_block.rb
+++ b/app/models/account_domain_block.rb
@@ -16,12 +16,16 @@ class AccountDomainBlock < ApplicationRecord
   belongs_to :account
   validates :domain, presence: true, uniqueness: { scope: :account_id }
 
-  after_create  :remove_blocking_cache
-  after_destroy :remove_blocking_cache
+  after_commit :remove_blocking_cache
+  after_commit :remove_relationship_cache
 
   private
 
   def remove_blocking_cache
     Rails.cache.delete("exclude_domains_for:#{account_id}")
   end
+
+  def remove_relationship_cache
+    Rails.cache.delete_matched("relationship:#{account_id}:*")
+  end
 end
diff --git a/app/models/block.rb b/app/models/block.rb
index 441e6bca3..d6ecabd3b 100644
--- a/app/models/block.rb
+++ b/app/models/block.rb
@@ -12,14 +12,14 @@
 
 class Block < ApplicationRecord
   include Paginable
+  include RelationshipCacheable
 
   belongs_to :account
   belongs_to :target_account, class_name: 'Account'
 
   validates :account_id, uniqueness: { scope: :target_account_id }
 
-  after_create  :remove_blocking_cache
-  after_destroy :remove_blocking_cache
+  after_commit :remove_blocking_cache
 
   private
 
diff --git a/app/models/concerns/account_avatar.rb b/app/models/concerns/account_avatar.rb
index 8a5c9a22c..53d0d876f 100644
--- a/app/models/concerns/account_avatar.rb
+++ b/app/models/concerns/account_avatar.rb
@@ -7,9 +7,15 @@ module AccountAvatar
 
   class_methods do
     def avatar_styles(file)
-      styles = { original: '120x120#' }
-      styles[:static] = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif'
+      styles   = {}
+      geometry = Paperclip::Geometry.from_file(file)
+
+      styles[:original] = '120x120#' if geometry.width != geometry.height || geometry.width > 120 || geometry.height > 120
+      styles[:static]   = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif'
+
       styles
+    rescue Paperclip::Errors::NotIdentifiedByImageMagickError
+      {}
     end
 
     private :avatar_styles
@@ -17,7 +23,7 @@ module AccountAvatar
 
   included do
     # Avatar upload
-    has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-quality 80 -strip' }
+    has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-strip' }
     validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES
     validates_attachment_size :avatar, less_than: 2.megabytes
   end
diff --git a/app/models/concerns/account_header.rb b/app/models/concerns/account_header.rb
index aff2aa3f9..991473d8c 100644
--- a/app/models/concerns/account_header.rb
+++ b/app/models/concerns/account_header.rb
@@ -7,9 +7,15 @@ module AccountHeader
 
   class_methods do
     def header_styles(file)
-      styles = { original: '700x335#' }
-      styles[:static] = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif'
+      styles   = {}
+      geometry = Paperclip::Geometry.from_file(file)
+
+      styles[:original] = '700x335#' unless geometry.width == 700 && geometry.height == 335
+      styles[:static]   = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif'
+
       styles
+    rescue Paperclip::Errors::NotIdentifiedByImageMagickError
+      {}
     end
 
     private :header_styles
@@ -17,7 +23,7 @@ module AccountHeader
 
   included do
     # Header upload
-    has_attached_file :header, styles: ->(f) { header_styles(f) }, convert_options: { all: '-quality 80 -strip' }
+    has_attached_file :header, styles: ->(f) { header_styles(f) }, convert_options: { all: '-strip' }
     validates_attachment_content_type :header, content_type: IMAGE_MIME_TYPES
     validates_attachment_size :header, less_than: 2.megabytes
   end
diff --git a/app/models/concerns/relationship_cacheable.rb b/app/models/concerns/relationship_cacheable.rb
new file mode 100644
index 000000000..0d9359f7e
--- /dev/null
+++ b/app/models/concerns/relationship_cacheable.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module RelationshipCacheable
+  extend ActiveSupport::Concern
+
+  included do
+    after_commit :remove_relationship_cache
+  end
+
+  private
+
+  def remove_relationship_cache
+    Rails.cache.delete("relationship:#{account_id}:#{target_account_id}")
+    Rails.cache.delete("relationship:#{target_account_id}:#{account_id}")
+  end
+end
diff --git a/app/models/follow.rb b/app/models/follow.rb
index f953b8e3e..8e6fe537a 100644
--- a/app/models/follow.rb
+++ b/app/models/follow.rb
@@ -13,6 +13,7 @@
 
 class Follow < ApplicationRecord
   include Paginable
+  include RelationshipCacheable
 
   belongs_to :account, counter_cache: :following_count
 
diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb
index bd6c4a0b9..cde26ceed 100644
--- a/app/models/follow_request.rb
+++ b/app/models/follow_request.rb
@@ -13,6 +13,7 @@
 
 class FollowRequest < ApplicationRecord
   include Paginable
+  include RelationshipCacheable
 
   belongs_to :account
   belongs_to :target_account, class_name: 'Account'
diff --git a/app/models/mute.rb b/app/models/mute.rb
index da4787179..ebb3818c7 100644
--- a/app/models/mute.rb
+++ b/app/models/mute.rb
@@ -13,14 +13,14 @@
 
 class Mute < ApplicationRecord
   include Paginable
+  include RelationshipCacheable
 
   belongs_to :account
   belongs_to :target_account, class_name: 'Account'
 
   validates :account_id, uniqueness: { scope: :target_account_id }
 
-  after_create  :remove_blocking_cache
-  after_destroy :remove_blocking_cache
+  after_commit :remove_blocking_cache
 
   private
 
diff --git a/app/models/status.rb b/app/models/status.rb
index 6fb9b15dd..86bf3deba 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -83,6 +83,8 @@ class Status < ApplicationRecord
 
   delegate :domain, to: :account, prefix: true
 
+  REAL_TIME_WINDOW = 6.hours
+
   def searchable_by(preloaded = nil)
     ids = [account_id]
 
@@ -111,6 +113,10 @@ class Status < ApplicationRecord
     !reblog_of_id.nil?
   end
 
+  def within_realtime_window?
+    created_at >= REAL_TIME_WINDOW.ago
+  end
+
   def verb
     if destroyed?
       :delete
diff --git a/app/presenters/account_relationships_presenter.rb b/app/presenters/account_relationships_presenter.rb
index bf1ba3716..d27fb7b01 100644
--- a/app/presenters/account_relationships_presenter.rb
+++ b/app/presenters/account_relationships_presenter.rb
@@ -5,11 +5,67 @@ class AccountRelationshipsPresenter
               :muting, :requested, :domain_blocking
 
   def initialize(account_ids, current_account_id, **options)
-    @following       = Account.following_map(account_ids, current_account_id).merge(options[:following_map] || {})
-    @followed_by     = Account.followed_by_map(account_ids, current_account_id).merge(options[:followed_by_map] || {})
-    @blocking        = Account.blocking_map(account_ids, current_account_id).merge(options[:blocking_map] || {})
-    @muting          = Account.muting_map(account_ids, current_account_id).merge(options[:muting_map] || {})
-    @requested       = Account.requested_map(account_ids, current_account_id).merge(options[:requested_map] || {})
-    @domain_blocking = Account.domain_blocking_map(account_ids, current_account_id).merge(options[:domain_blocking_map] || {})
+    @account_ids        = account_ids.map { |a| a.is_a?(Account) ? a.id : a }
+    @current_account_id = current_account_id
+
+    @following       = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id))
+    @followed_by     = cached[:followed_by].merge(Account.followed_by_map(@uncached_account_ids, @current_account_id))
+    @blocking        = cached[:blocking].merge(Account.blocking_map(@uncached_account_ids, @current_account_id))
+    @muting          = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id))
+    @requested       = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id))
+    @domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id))
+
+    cache_uncached!
+
+    @following.merge!(options[:following_map] || {})
+    @followed_by.merge!(options[:followed_by_map] || {})
+    @blocking.merge!(options[:blocking_map] || {})
+    @muting.merge!(options[:muting_map] || {})
+    @requested.merge!(options[:requested_map] || {})
+    @domain_blocking.merge!(options[:domain_blocking_map] || {})
+  end
+
+  private
+
+  def cached
+    return @cached if defined?(@cached)
+
+    @cached = {
+      following: {},
+      followed_by: {},
+      blocking: {},
+      muting: {},
+      requested: {},
+      domain_blocking: {},
+    }
+
+    @uncached_account_ids = []
+
+    @account_ids.each do |account_id|
+      maps_for_account = Rails.cache.read("relationship:#{@current_account_id}:#{account_id}")
+
+      if maps_for_account.is_a?(Hash)
+        @cached.merge!(maps_for_account)
+      else
+        @uncached_account_ids << account_id
+      end
+    end
+
+    @cached
+  end
+
+  def cache_uncached!
+    @uncached_account_ids.each do |account_id|
+      maps_for_account = {
+        following:       { account_id => following[account_id] },
+        followed_by:     { account_id => followed_by[account_id] },
+        blocking:        { account_id => blocking[account_id] },
+        muting:          { account_id => muting[account_id] },
+        requested:       { account_id => requested[account_id] },
+        domain_blocking: { account_id => domain_blocking[account_id] },
+      }
+
+      Rails.cache.write("relationship:#{@current_account_id}:#{account_id}", maps_for_account, expires_in: 1.day)
+    end
   end
 end
diff --git a/config/environments/test.rb b/config/environments/test.rb
index e68cb156d..20fe5f813 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -23,6 +23,10 @@ Rails.application.configure do
   config.consider_all_requests_local       = true
   config.action_controller.perform_caching = false
 
+  # The default store, file_store is shared by processses parallely executed
+  # and should not be used.
+  config.cache_store = :memory_store
+
   # Raise exceptions instead of rendering exception templates.
   config.action_dispatch.show_exceptions = false
 
diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb
index 811435606..1be01e8ba 100644
--- a/spec/mailers/notification_mailer_spec.rb
+++ b/spec/mailers/notification_mailer_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe NotificationMailer, type: :mailer do
 
   shared_examples 'localized subject' do |*args, **kwrest|
     it 'renders subject localized for the locale of the receiver' do
-      locale = I18n.available_locales.sample
+      locale = %i(de en).sample
       receiver.update!(locale: locale)
       expect(mail.subject).to eq I18n.t(*args, kwrest.merge(locale: locale))
     end
diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb
index bbba5f98d..1cc528674 100644
--- a/spec/models/setting_spec.rb
+++ b/spec/models/setting_spec.rb
@@ -42,11 +42,6 @@ RSpec.describe Setting, type: :model do
         described_class[key]
       end
 
-      it 'calls Rails.cache.fetch' do
-        expect(Rails).to receive_message_chain(:cache, :fetch).with(cache_key)
-        described_class[key]
-      end
-
       context 'Rails.cache does not exists' do
         before do
           allow(RailsSettings::Settings).to receive(:object).with(key).and_return(object)
@@ -103,6 +98,14 @@ RSpec.describe Setting, type: :model do
           Rails.cache.write(cache_key, cache_value)
         end
 
+        it 'does not query the database' do
+          expect do |callback|
+            ActiveSupport::Notifications.subscribed callback, 'sql.active_record' do
+              described_class[key]
+            end
+          end.not_to yield_control
+        end
+
         it 'returns the cached value' do
           expect(described_class[key]).to eq cache_value
         end
diff --git a/spec/presenters/instance_presenter_spec.rb b/spec/presenters/instance_presenter_spec.rb
index 7c47aed61..006403925 100644
--- a/spec/presenters/instance_presenter_spec.rb
+++ b/spec/presenters/instance_presenter_spec.rb
@@ -90,9 +90,7 @@ describe InstancePresenter do
 
   describe "user_count" do
     it "returns the number of site users" do
-      cache = double
-      allow(Rails).to receive(:cache).and_return(cache)
-      allow(cache).to receive(:fetch).with("user_count").and_return(123)
+      Rails.cache.write 'user_count', 123
 
       expect(instance_presenter.user_count).to eq(123)
     end
@@ -100,9 +98,7 @@ describe InstancePresenter do
 
   describe "status_count" do
     it "returns the number of local statuses" do
-      cache = double
-      allow(Rails).to receive(:cache).and_return(cache)
-      allow(cache).to receive(:fetch).with("local_status_count").and_return(234)
+      Rails.cache.write 'local_status_count', 234
 
       expect(instance_presenter.status_count).to eq(234)
     end
@@ -110,9 +106,7 @@ describe InstancePresenter do
 
   describe "domain_count" do
     it "returns the number of known domains" do
-      cache = double
-      allow(Rails).to receive(:cache).and_return(cache)
-      allow(cache).to receive(:fetch).with("distinct_domain_count").and_return(345)
+      Rails.cache.write 'distinct_domain_count', 345
 
       expect(instance_presenter.domain_count).to eq(345)
     end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 4f7399505..dc1f32e08 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -51,6 +51,8 @@ RSpec.configure do |config|
   end
 
   config.after :each do
+    Rails.cache.clear
+
     keys = Redis.current.keys
     Redis.current.del(keys) if keys.any?
   end
diff --git a/spec/services/fetch_remote_status_service_spec.rb b/spec/services/fetch_remote_status_service_spec.rb
index cbdecbf25..fa5782b94 100644
--- a/spec/services/fetch_remote_status_service_spec.rb
+++ b/spec/services/fetch_remote_status_service_spec.rb
@@ -1,4 +1,35 @@
 require 'rails_helper'
 
 RSpec.describe FetchRemoteStatusService do
+  let(:account) { Fabricate(:account) }
+  let(:prefetched_body) { nil }
+  let(:valid_domain) { Rails.configuration.x.local_domain }
+
+  let(:note) do
+    {
+      '@context': 'https://www.w3.org/ns/activitystreams',
+      id: "https://#{valid_domain}/@foo/1234",
+      type: 'Note',
+      content: 'Lorem ipsum',
+      attributedTo: ActivityPub::TagManager.instance.uri_for(account),
+    }
+  end
+
+  context 'protocol is :activitypub' do
+    subject { described_class.new.call(note[:id], prefetched_body, protocol) }
+    let(:prefetched_body) { Oj.dump(note) }
+    let(:protocol) { :activitypub }
+
+    before do
+      account.update(uri: ActivityPub::TagManager.instance.uri_for(account))
+      subject
+    end
+
+    it 'creates status' do
+      status = account.statuses.first
+
+      expect(status).to_not be_nil
+      expect(status.text).to eq 'Lorem ipsum'
+    end
+  end
 end