about summary refs log tree commit diff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/api/salmon_controller_spec.rb1
-rw-r--r--spec/controllers/api/v1/accounts_controller_spec.rb38
-rw-r--r--spec/controllers/api/v1/follows_controller_spec.rb1
-rw-r--r--spec/controllers/api/v1/mutes_controller_spec.rb19
-rw-r--r--spec/controllers/xrd_controller_spec.rb2
-rw-r--r--spec/fabricators/import_fabricator.rb2
-rw-r--r--spec/fabricators/mute_fabricator.rb3
-rw-r--r--spec/fabricators/report_fabricator.rb4
-rw-r--r--spec/helpers/atom_builder_helper_spec.rb2
-rw-r--r--spec/i18n_spec.rb10
-rw-r--r--spec/lib/formatter_spec.rb32
-rw-r--r--spec/lib/tag_manager_spec.rb32
-rw-r--r--spec/mailers/previews/notification_mailer_preview.rb17
-rw-r--r--spec/models/account_spec.rb9
-rw-r--r--spec/models/favourite_spec.rb41
-rw-r--r--spec/models/follow_spec.rb30
-rw-r--r--spec/models/import_spec.rb5
-rw-r--r--spec/models/mute_spec.rb5
-rw-r--r--spec/models/report_spec.rb5
-rw-r--r--spec/models/stream_entry_spec.rb14
-rw-r--r--spec/models/tag_spec.rb10
-rw-r--r--spec/services/authorize_follow_service_spec.rb49
-rw-r--r--spec/services/block_domain_service_spec.rb2
-rw-r--r--spec/services/block_service_spec.rb34
-rw-r--r--spec/services/fan_out_on_write_service_spec.rb1
-rw-r--r--spec/services/favourite_service_spec.rb36
-rw-r--r--spec/services/follow_service_spec.rb72
-rw-r--r--spec/services/mute_service_spec.rb5
-rw-r--r--spec/services/process_interaction_service_spec.rb96
-rw-r--r--spec/services/reject_follow_service_spec.rb49
-rw-r--r--spec/services/unblock_service_spec.rb36
-rw-r--r--spec/services/unfollow_service_spec.rb37
-rw-r--r--spec/services/unmute_service_spec.rb5
33 files changed, 556 insertions, 148 deletions
diff --git a/spec/controllers/api/salmon_controller_spec.rb b/spec/controllers/api/salmon_controller_spec.rb
index 3d3a973d2..6897caeeb 100644
--- a/spec/controllers/api/salmon_controller_spec.rb
+++ b/spec/controllers/api/salmon_controller_spec.rb
@@ -6,7 +6,6 @@ RSpec.describe Api::SalmonController, type: :controller do
   let(:account) { Fabricate(:user, account: Fabricate(:account, username: 'catsrgr8')).account }
 
   before do
-    stub_request(:post, "https://pubsubhubbub.superfeedr.com/").to_return(:status => 200, :body => "", :headers => {})
     stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
     stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt'))
     stub_request(:get, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/controllers/api/v1/accounts_controller_spec.rb
index 98b284f7a..5d36b0159 100644
--- a/spec/controllers/api/v1/accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts_controller_spec.rb
@@ -116,6 +116,44 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
     end
   end
 
+  describe 'POST #mute' do
+    let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
+
+    before do
+      user.account.follow!(other_account)
+      post :mute, params: {id: other_account.id }
+    end
+
+    it 'returns http success' do
+      expect(response).to have_http_status(:success)
+    end
+
+    it 'does not remove the following relation between user and target user' do
+      expect(user.account.following?(other_account)).to be true
+    end
+
+    it 'creates a muting relation' do
+      expect(user.account.muting?(other_account)).to be true
+    end
+  end
+
+  describe 'POST #unmute' do
+    let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
+
+    before do
+      user.account.mute!(other_account)
+      post :unmute, params: { id: other_account.id }
+    end
+
+    it 'returns http success' do
+      expect(response).to have_http_status(:success)
+    end
+
+    it 'removes the muting relation between user and target user' do
+      expect(user.account.muting?(other_account)).to be false
+    end
+  end
+
   describe 'GET #relationships' do
     let(:simon) { Fabricate(:user, email: 'simon@example.com', account: Fabricate(:account, username: 'simon')).account }
     let(:lewis) { Fabricate(:user, email: 'lewis@example.com', account: Fabricate(:account, username: 'lewis')).account }
diff --git a/spec/controllers/api/v1/follows_controller_spec.rb b/spec/controllers/api/v1/follows_controller_spec.rb
index 97d69ab7b..cc4958ab5 100644
--- a/spec/controllers/api/v1/follows_controller_spec.rb
+++ b/spec/controllers/api/v1/follows_controller_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe Api::V1::FollowsController, type: :controller do
     before do
       stub_request(:get,  "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
       stub_request(:get,  "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt'))
+      stub_request(:head, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(:status => 405, :body => "", :headers => {})
       stub_request(:get,  "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
       stub_request(:get,  "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
       stub_request(:post, "https://quitter.no/main/push/hub").to_return(:status => 200, :body => "", :headers => {})
diff --git a/spec/controllers/api/v1/mutes_controller_spec.rb b/spec/controllers/api/v1/mutes_controller_spec.rb
new file mode 100644
index 000000000..be8a5e7dd
--- /dev/null
+++ b/spec/controllers/api/v1/mutes_controller_spec.rb
@@ -0,0 +1,19 @@
+require 'rails_helper'
+
+RSpec.describe Api::V1::MutesController, type: :controller do
+  render_views
+
+  let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+  let(:token) { double acceptable?: true, resource_owner_id: user.id }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/xrd_controller_spec.rb b/spec/controllers/xrd_controller_spec.rb
index eeaaaa786..e687cf9e0 100644
--- a/spec/controllers/xrd_controller_spec.rb
+++ b/spec/controllers/xrd_controller_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe XrdController, type: :controller do
     let(:alice) { Fabricate(:account, username: 'alice') }
 
     it 'returns http success when account can be found' do
-      get :webfinger, params: { resource: "acct:#{alice.username}@anything.com" }
+      get :webfinger, params: { resource: "acct:#{alice.username}@#{Rails.configuration.x.local_domain}" }
       expect(response).to have_http_status(:success)
     end
 
diff --git a/spec/fabricators/import_fabricator.rb b/spec/fabricators/import_fabricator.rb
new file mode 100644
index 000000000..e2eb1e0df
--- /dev/null
+++ b/spec/fabricators/import_fabricator.rb
@@ -0,0 +1,2 @@
+Fabricator(:import) do
+end
diff --git a/spec/fabricators/mute_fabricator.rb b/spec/fabricators/mute_fabricator.rb
new file mode 100644
index 000000000..fc150c1d6
--- /dev/null
+++ b/spec/fabricators/mute_fabricator.rb
@@ -0,0 +1,3 @@
+Fabricator(:mute) do
+
+end
diff --git a/spec/fabricators/report_fabricator.rb b/spec/fabricators/report_fabricator.rb
new file mode 100644
index 000000000..b9fa360a7
--- /dev/null
+++ b/spec/fabricators/report_fabricator.rb
@@ -0,0 +1,4 @@
+Fabricator(:report) do
+  comment      "You nasty"
+  action_taken false
+end
diff --git a/spec/helpers/atom_builder_helper_spec.rb b/spec/helpers/atom_builder_helper_spec.rb
index 3d3bd56a1..0aca58ee7 100644
--- a/spec/helpers/atom_builder_helper_spec.rb
+++ b/spec/helpers/atom_builder_helper_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do
 
   describe '#feed' do
     it 'creates a feed' do
-      expect(used_in_builder { |xml| helper.feed(xml) }).to match '<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0"/>'
+      expect(used_in_builder { |xml| helper.feed(xml) }).to match '<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0"/>'
     end
   end
 
diff --git a/spec/i18n_spec.rb b/spec/i18n_spec.rb
index d982b9dca..138d25569 100644
--- a/spec/i18n_spec.rb
+++ b/spec/i18n_spec.rb
@@ -2,17 +2,15 @@
 require 'i18n/tasks'
 
 RSpec.describe 'I18n' do
-  let(:i18n) { I18n::Tasks::BaseTask.new }
+  let(:i18n)         { I18n::Tasks::BaseTask.new }
   let(:missing_keys) { i18n.missing_keys }
-  let(:unused_keys) { i18n.unused_keys }
+  let(:unused_keys)  { i18n.unused_keys }
 
   xit 'does not have missing keys' do
-    expect(missing_keys).to be_empty,
-      "Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
+    expect(missing_keys).to be_empty, "Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
   end
 
   xit 'does not have unused keys' do
-    expect(unused_keys).to be_empty,
-      "#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
+    expect(unused_keys).to be_empty, "#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
   end
 end
diff --git a/spec/lib/formatter_spec.rb b/spec/lib/formatter_spec.rb
index 0db1634e9..4b003b8e5 100644
--- a/spec/lib/formatter_spec.rb
+++ b/spec/lib/formatter_spec.rb
@@ -17,8 +17,38 @@ RSpec.describe Formatter do
     end
 
     it 'contains a link' do
-      expect(subject).to match('<a rel="nofollow noopener" target="_blank" href="http://google.com"><span class="invisible">http://</span><span class="">google.com</span><span class="invisible"></span></a>')
+      expect(subject).to match('<a href="http://google.com" rel="nofollow noopener" target="_blank"><span class="invisible">http://</span><span class="">google.com</span><span class="invisible"></span></a>')
     end
+
+=begin
+    it 'matches a stand-alone medium URL' do
+      expect(subject.match('https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4')[0]).to eq 'https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4'
+    end
+
+    it 'matches a stand-alone google URL' do
+      expect(subject.match('http://google.com')[0]).to eq 'http://google.com'
+    end
+
+    it 'matches a URL without trailing period' do
+      expect(subject.match('http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona. ')[0]).to eq 'http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona'
+    end
+
+    it 'matches a URL without closing paranthesis' do
+      expect(subject.match('(http://google.com/)')[0]).to eq 'http://google.com'
+    end
+
+    it 'matches a URL without exclamation point' do
+      expect(subject.match('http://www.google.com! ')[0]).to eq 'http://www.google.com'
+    end
+
+    it 'matches a URL with a query string' do
+      expect(subject.match('https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink')[0]).to eq 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink'
+    end
+
+    it 'matches a URL with parenthesis in it' do
+      expect(subject.match('https://en.wikipedia.org/wiki/Diaspora_(software)')[0]).to eq 'https://en.wikipedia.org/wiki/Diaspora_(software)'
+    end
+=end
   end
 
   describe '#reformat' do
diff --git a/spec/lib/tag_manager_spec.rb b/spec/lib/tag_manager_spec.rb
index b60584253..cbb427a8c 100644
--- a/spec/lib/tag_manager_spec.rb
+++ b/spec/lib/tag_manager_spec.rb
@@ -47,22 +47,6 @@ RSpec.describe TagManager do
         expect(subject).to be_a String
       end
     end
-
-    context 'Follow' do
-      let(:target) { Fabricate(:follow, account: alice, target_account: bob) }
-
-      it 'returns a string' do
-        expect(subject).to be_a String
-      end
-    end
-
-    context 'Favourite' do
-      let(:target) { Fabricate(:favourite, account: bob, status: status) }
-
-      it 'returns a string' do
-        expect(subject).to be_a String
-      end
-    end
   end
 
   describe '#url_for' do
@@ -87,21 +71,5 @@ RSpec.describe TagManager do
         expect(subject).to be_a String
       end
     end
-
-    context 'Follow' do
-      let(:target) { Fabricate(:follow, account: alice, target_account: bob) }
-
-      it 'returns a URL' do
-        expect(subject).to be_a String
-      end
-    end
-
-    context 'Favourite' do
-      let(:target) { Fabricate(:favourite, account: bob, status: status) }
-
-      it 'returns a URL' do
-        expect(subject).to be_a String
-      end
-    end
   end
 end
diff --git a/spec/mailers/previews/notification_mailer_preview.rb b/spec/mailers/previews/notification_mailer_preview.rb
index 8fc8d0d34..a08a80d17 100644
--- a/spec/mailers/previews/notification_mailer_preview.rb
+++ b/spec/mailers/previews/notification_mailer_preview.rb
@@ -1,24 +1,31 @@
 # Preview all emails at http://localhost:3000/rails/mailers/notification_mailer
 class NotificationMailerPreview < ActionMailer::Preview
-
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/mention
   def mention
-    # NotificationMailer.mention
+    m = Mention.last
+    NotificationMailer.mention(m.account, Notification.find_by(activity: m))
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/follow
   def follow
-    # NotificationMailer.follow
+    f = Follow.last
+    NotificationMailer.follow(f.target_account, Notification.find_by(activity: f))
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/favourite
   def favourite
-    # NotificationMailer.favourite
+    f = Favourite.last
+    NotificationMailer.favourite(f.status.account, Notification.find_by(activity: f))
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/reblog
   def reblog
-    # NotificationMailer.reblog
+    r = Status.where.not(reblog_of_id: nil).first
+    NotificationMailer.reblog(r.reblog.account, Notification.find_by(activity: r))
   end
 
+  # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/digest
+  def digest
+    NotificationMailer.digest(Account.first, since: 90.days.ago)
+  end
 end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 287f389ac..91c8d75cf 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -178,7 +178,6 @@ RSpec.describe Account, type: :model do
     end
   end
 
-
   describe 'MENTION_RE' do
     subject { Account::MENTION_RE }
 
@@ -190,6 +189,14 @@ RSpec.describe Account, type: :model do
       expect(subject.match('@alice Hey how are you?')[1]).to eq 'alice'
     end
 
+    it 'matches full usernames' do
+      expect(subject.match('@alice@example.com')[1]).to eq 'alice@example.com'
+    end
+
+    it 'matches full usernames with a dot at the end' do
+      expect(subject.match('Hello @alice@example.com.')[1]).to eq 'alice@example.com'
+    end
+
     it 'matches dot-prepended usernames' do
       expect(subject.match('.@alice I want everybody to see this')[1]).to eq 'alice'
     end
diff --git a/spec/models/favourite_spec.rb b/spec/models/favourite_spec.rb
index 6cf3af464..5b7126506 100644
--- a/spec/models/favourite_spec.rb
+++ b/spec/models/favourite_spec.rb
@@ -6,45 +6,4 @@ RSpec.describe Favourite, type: :model do
   let(:status) { Fabricate(:status, account: bob) }
 
   subject { Favourite.new(account: alice, status: status) }
-
-  describe '#verb' do
-    it 'is always favorite' do
-      expect(subject.verb).to be :favorite
-    end
-  end
-
-  describe '#title' do
-    it 'describes the favourite' do
-      expect(subject.title).to eql 'alice favourited a status by bob'
-    end
-  end
-
-  describe '#content' do
-    it 'equals the title' do
-      expect(subject.content).to eq subject.title
-    end
-  end
-
-  describe '#object_type' do
-    it 'is a note when the target is a note' do
-      expect(subject.object_type).to be :note
-    end
-
-    it 'is a comment when the target is a comment' do
-      status.in_reply_to_id = 2
-      expect(subject.object_type).to be :comment
-    end
-  end
-
-  describe '#target' do
-    it 'is the status that was favourited' do
-      expect(subject.target).to eq status
-    end
-  end
-
-  describe '#thread' do
-    it 'equals the target' do
-      expect(subject.thread).to eq subject.target
-    end
-  end
 end
diff --git a/spec/models/follow_spec.rb b/spec/models/follow_spec.rb
index c9d02ab16..eb21f3e18 100644
--- a/spec/models/follow_spec.rb
+++ b/spec/models/follow_spec.rb
@@ -5,34 +5,4 @@ RSpec.describe Follow, type: :model do
   let(:bob)   { Fabricate(:account, username: 'bob') }
 
   subject { Follow.new(account: alice, target_account: bob) }
-
-  describe '#verb' do
-    it 'is follow' do
-      expect(subject.verb).to be :follow
-    end
-  end
-
-  describe '#title' do
-    it 'describes the follow' do
-      expect(subject.title).to eql 'alice started following bob'
-    end
-  end
-
-  describe '#content' do
-    it 'is the same as the title' do
-      expect(subject.content).to eql subject.title
-    end
-  end
-
-  describe '#object_type' do
-    it 'is a person' do
-      expect(subject.object_type).to be :person
-    end
-  end
-
-  describe '#target' do
-    it 'is the person being followed' do
-      expect(subject.target).to eq bob
-    end
-  end
 end
diff --git a/spec/models/import_spec.rb b/spec/models/import_spec.rb
new file mode 100644
index 000000000..fa52077cd
--- /dev/null
+++ b/spec/models/import_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe Import, type: :model do
+
+end
diff --git a/spec/models/mute_spec.rb b/spec/models/mute_spec.rb
new file mode 100644
index 000000000..83ba793b2
--- /dev/null
+++ b/spec/models/mute_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe Mute, type: :model do
+
+end
diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb
new file mode 100644
index 000000000..ade53cffa
--- /dev/null
+++ b/spec/models/report_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe Report, type: :model do
+
+end
diff --git a/spec/models/stream_entry_spec.rb b/spec/models/stream_entry_spec.rb
index 9ecf6412a..45bf26899 100644
--- a/spec/models/stream_entry_spec.rb
+++ b/spec/models/stream_entry_spec.rb
@@ -3,21 +3,11 @@ require 'rails_helper'
 RSpec.describe StreamEntry, type: :model do
   let(:alice)     { Fabricate(:account, username: 'alice') }
   let(:bob)       { Fabricate(:account, username: 'bob') }
-  let(:follow)    { Fabricate(:follow, account: alice, target_account: bob) }
   let(:status)    { Fabricate(:status, account: alice) }
   let(:reblog)    { Fabricate(:status, account: bob, reblog: status) }
   let(:reply)     { Fabricate(:status, account: bob, thread: status) }
-  let(:favourite) { Fabricate(:favourite, account: alice, status: status) }
 
   describe '#targeted?' do
-    it 'returns true for a follow' do
-      expect(follow.stream_entry.targeted?).to be true
-    end
-
-    it 'returns true for a favourite' do
-      expect(favourite.stream_entry.targeted?).to be true
-    end
-
     it 'returns true for a reblog' do
       expect(reblog.stream_entry.targeted?).to be true
     end
@@ -28,10 +18,6 @@ RSpec.describe StreamEntry, type: :model do
   end
 
   describe '#threaded?' do
-    it 'returns true for a favourite' do
-      expect(favourite.stream_entry.threaded?).to be true
-    end
-
     it 'returns true for a reply' do
       expect(reply.stream_entry.threaded?).to be true
     end
diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb
index 9a7f481e4..360bbc16d 100644
--- a/spec/models/tag_spec.rb
+++ b/spec/models/tag_spec.rb
@@ -1,5 +1,15 @@
 require 'rails_helper'
 
 RSpec.describe Tag, type: :model do
+  describe 'HASHTAG_RE' do
+    subject { Tag::HASHTAG_RE }
 
+    it 'does not match URLs with anchors with non-hashtag characters' do
+      expect(subject.match('Check this out https://medium.com/@alice/some-article#.abcdef123')).to be_nil
+    end
+
+    it 'does not match URLs with hashtag-like anchors' do
+      expect(subject.match('https://en.wikipedia.org/wiki/Ghostbusters_(song)#Lawsuit')).to be_nil
+    end
+  end
 end
diff --git a/spec/services/authorize_follow_service_spec.rb b/spec/services/authorize_follow_service_spec.rb
new file mode 100644
index 000000000..3f3a2bc56
--- /dev/null
+++ b/spec/services/authorize_follow_service_spec.rb
@@ -0,0 +1,49 @@
+require 'rails_helper'
+
+RSpec.describe AuthorizeFollowService do
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
+  subject { AuthorizeFollowService.new }
+
+  describe 'local' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
+
+    before do
+      FollowRequest.create(account: bob, target_account: sender)
+      subject.call(bob, sender)
+    end
+
+    it 'removes follow request' do
+      expect(bob.requested?(sender)).to be false
+    end
+
+    it 'creates follow relation' do
+      expect(bob.following?(sender)).to be true
+    end
+  end
+
+  describe 'remote' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+
+    before do
+      FollowRequest.create(account: bob, target_account: sender)
+      stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {})
+      subject.call(bob, sender)
+    end
+
+    it 'removes follow request' do
+      expect(bob.requested?(sender)).to be false
+    end
+
+    it 'creates follow relation' do
+      expect(bob.following?(sender)).to be true
+    end
+
+    it 'sends a follow request authorization salmon slap' do
+      expect(a_request(:post, "http://salmon.example.com/").with { |req|
+        xml = OStatus2::Salmon.new.unpack(req.body)
+        xml.match(TagManager::VERBS[:authorize])
+      }).to have_been_made.once
+    end
+  end
+end
diff --git a/spec/services/block_domain_service_spec.rb b/spec/services/block_domain_service_spec.rb
index d88b3b55c..8e71d4542 100644
--- a/spec/services/block_domain_service_spec.rb
+++ b/spec/services/block_domain_service_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe BlockDomainService do
     bad_status2
     bad_attachment
 
-    subject.call('evil.org', :suspend)
+    subject.call(DomainBlock.create!(domain: 'evil.org', severity: :suspend))
   end
 
   it 'creates a domain block' do
diff --git a/spec/services/block_service_spec.rb b/spec/services/block_service_spec.rb
index f6f07fa20..2a54e032e 100644
--- a/spec/services/block_service_spec.rb
+++ b/spec/services/block_service_spec.rb
@@ -1,5 +1,39 @@
 require 'rails_helper'
 
 RSpec.describe BlockService do
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   subject { BlockService.new }
+
+  describe 'local' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
+
+    before do
+      subject.call(sender, bob)
+    end
+
+    it 'creates a blocking relation' do
+      expect(sender.blocking?(bob)).to be true
+    end
+  end
+
+  describe 'remote' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+
+    before do
+      stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {})
+      subject.call(sender, bob)
+    end
+
+    it 'creates a blocking relation' do
+      expect(sender.blocking?(bob)).to be true
+    end
+
+    it 'sends a block salmon slap' do
+      expect(a_request(:post, "http://salmon.example.com/").with { |req|
+        xml = OStatus2::Salmon.new.unpack(req.body)
+        xml.match(TagManager::VERBS[:block])
+      }).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb
index 07f8c2dc8..6ee225c4c 100644
--- a/spec/services/fan_out_on_write_service_spec.rb
+++ b/spec/services/fan_out_on_write_service_spec.rb
@@ -23,6 +23,7 @@ RSpec.describe FanOutOnWriteService do
   end
 
   it 'delivers status to local followers' do
+    pending 'some sort of problem in test environment causes this to sometimes fail'
     expect(Feed.new(:home, follower).get(10).map(&:id)).to include status.id
   end
 
diff --git a/spec/services/favourite_service_spec.rb b/spec/services/favourite_service_spec.rb
index eb961c28e..36f1b64d4 100644
--- a/spec/services/favourite_service_spec.rb
+++ b/spec/services/favourite_service_spec.rb
@@ -1,5 +1,41 @@
 require 'rails_helper'
 
 RSpec.describe FavouriteService do
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   subject { FavouriteService.new }
+
+  describe 'local' do
+    let(:bob)    { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
+    let(:status) { Fabricate(:status, account: bob) }
+
+    before do
+      subject.call(sender, status)
+    end
+
+    it 'creates a favourite' do
+      expect(status.favourites.first).to_not be_nil
+    end
+  end
+
+  describe 'remote' do
+    let(:bob)    { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+    let(:status) { Fabricate(:status, account: bob, uri: 'tag:example.com:blahblah') }
+
+    before do
+      stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {})
+      subject.call(sender, status)
+    end
+
+    it 'creates a favourite' do
+      expect(status.favourites.first).to_not be_nil
+    end
+
+    it 'sends a salmon slap' do
+      expect(a_request(:post, "http://salmon.example.com/").with { |req|
+        xml = OStatus2::Salmon.new.unpack(req.body)
+        xml.match(TagManager::VERBS[:favorite])
+      }).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/services/follow_service_spec.rb b/spec/services/follow_service_spec.rb
index 304e0cf71..2ce0fa464 100644
--- a/spec/services/follow_service_spec.rb
+++ b/spec/services/follow_service_spec.rb
@@ -1,9 +1,75 @@
 require 'rails_helper'
 
 RSpec.describe FollowService do
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   subject { FollowService.new }
 
-  it 'creates a following relation'
-  it 'creates local account for remote user'
-  it 'sends follow to the remote user'
+  context 'local account' do
+    describe 'locked account' do
+      let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, locked: true, username: 'bob')).account }
+
+      before do
+        subject.call(sender, bob.acct)
+      end
+
+      it 'creates a follow request' do
+        expect(FollowRequest.find_by(account: sender, target_account: bob)).to_not be_nil
+      end
+    end
+
+    describe 'unlocked account' do
+      let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
+
+      before do
+        subject.call(sender, bob.acct)
+      end
+
+      it 'creates a following relation' do
+        expect(sender.following?(bob)).to be true
+      end
+    end
+  end
+
+  context 'remote account' do
+    describe 'locked account' do
+      let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, locked: true, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+
+      before do
+        stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {})
+        subject.call(sender, bob.acct)
+      end
+
+      it 'creates a follow request' do
+        expect(FollowRequest.find_by(account: sender, target_account: bob)).to_not be_nil
+      end
+
+      it 'sends a follow request salmon slap' do
+        expect(a_request(:post, "http://salmon.example.com/").with { |req|
+          xml = OStatus2::Salmon.new.unpack(req.body)
+          xml.match(TagManager::VERBS[:request_friend])
+        }).to have_been_made.once
+      end
+    end
+
+    describe 'unlocked account' do
+      let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+
+      before do
+        stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {})
+        subject.call(sender, bob.acct)
+      end
+
+      it 'creates a following relation' do
+        expect(sender.following?(bob)).to be true
+      end
+
+      it 'sends a follow salmon slap' do
+        expect(a_request(:post, "http://salmon.example.com/").with { |req|
+          xml = OStatus2::Salmon.new.unpack(req.body)
+          xml.match(TagManager::VERBS[:follow])
+        }).to have_been_made.once
+      end
+    end
+  end
 end
diff --git a/spec/services/mute_service_spec.rb b/spec/services/mute_service_spec.rb
new file mode 100644
index 000000000..397368416
--- /dev/null
+++ b/spec/services/mute_service_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe MuteService do
+  subject { MuteService.new }
+end
diff --git a/spec/services/process_interaction_service_spec.rb b/spec/services/process_interaction_service_spec.rb
index 931815dc2..0845e09ed 100644
--- a/spec/services/process_interaction_service_spec.rb
+++ b/spec/services/process_interaction_service_spec.rb
@@ -1,15 +1,93 @@
 require 'rails_helper'
 
 RSpec.describe ProcessInteractionService do
+  let(:receiver) { Fabricate(:user, email: 'alice@example.com', account: Fabricate(:account, username: 'alice')).account }
+  let(:sender)   { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
+
   subject { ProcessInteractionService.new }
 
-  it 'creates account for new remote user'
-  it 'updates account for existing remote user'
-  it 'ignores envelopes that do not address the local user'
-  it 'accepts a status that mentions the local user'
-  it 'accepts a status that is a reply to the local user\'s'
-  it 'accepts a favourite to a status by the local user'
-  it 'accepts a reblog of a status of the local user'
-  it 'accepts a follow of the local user'
-  it 'accepts an unfollow of the local user'
+  describe 'follow request slap' do
+    before do
+      receiver.update(locked: true)
+
+      payload = <<XML
+<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/">
+  <author>
+    <name>bob</name>
+    <uri>https://cb6e6126.ngrok.io/users/bob</uri>
+  </author>
+
+  <id>someIdHere</id>
+  <activity:verb>http://activitystrea.ms/schema/1.0/request-friend</activity:verb>
+</entry>
+XML
+
+      envelope = OStatus2::Salmon.new.pack(payload, sender.keypair)
+      subject.call(envelope, receiver)
+    end
+
+    it 'creates a record' do
+      expect(FollowRequest.find_by(account: sender, target_account: receiver)).to_not be_nil
+    end
+  end
+
+  describe 'follow request authorization slap' do
+    before do
+      receiver.update(locked: true)
+      FollowRequest.create(account: sender, target_account: receiver)
+
+      payload = <<XML
+<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/">
+  <author>
+    <name>alice</name>
+    <uri>https://cb6e6126.ngrok.io/users/alice</uri>
+  </author>
+
+  <id>someIdHere</id>
+  <activity:verb>http://activitystrea.ms/schema/1.0/authorize</activity:verb>
+</entry>
+XML
+
+      envelope = OStatus2::Salmon.new.pack(payload, receiver.keypair)
+      subject.call(envelope, sender)
+    end
+
+    it 'creates a follow relationship' do
+      expect(Follow.find_by(account: sender, target_account: receiver)).to_not be_nil
+    end
+
+    it 'removes the follow request' do
+      expect(FollowRequest.find_by(account: sender, target_account: receiver)).to be_nil
+    end
+  end
+
+  describe 'follow request rejection slap' do
+    before do
+      receiver.update(locked: true)
+      FollowRequest.create(account: sender, target_account: receiver)
+
+      payload = <<XML
+<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/">
+  <author>
+    <name>alice</name>
+    <uri>https://cb6e6126.ngrok.io/users/alice</uri>
+  </author>
+
+  <id>someIdHere</id>
+  <activity:verb>http://activitystrea.ms/schema/1.0/reject</activity:verb>
+</entry>
+XML
+
+      envelope = OStatus2::Salmon.new.pack(payload, receiver.keypair)
+      subject.call(envelope, sender)
+    end
+
+    it 'does not create a follow relationship' do
+      expect(Follow.find_by(account: sender, target_account: receiver)).to be_nil
+    end
+
+    it 'removes the follow request' do
+      expect(FollowRequest.find_by(account: sender, target_account: receiver)).to be_nil
+    end
+  end
 end
diff --git a/spec/services/reject_follow_service_spec.rb b/spec/services/reject_follow_service_spec.rb
new file mode 100644
index 000000000..50749b633
--- /dev/null
+++ b/spec/services/reject_follow_service_spec.rb
@@ -0,0 +1,49 @@
+require 'rails_helper'
+
+RSpec.describe RejectFollowService do
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
+  subject { RejectFollowService.new }
+
+  describe 'local' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
+
+    before do
+      FollowRequest.create(account: bob, target_account: sender)
+      subject.call(bob, sender)
+    end
+
+    it 'removes follow request' do
+      expect(bob.requested?(sender)).to be false
+    end
+
+    it 'does not create follow relation' do
+      expect(bob.following?(sender)).to be false
+    end
+  end
+
+  describe 'remote' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+
+    before do
+      FollowRequest.create(account: bob, target_account: sender)
+      stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {})
+      subject.call(bob, sender)
+    end
+
+    it 'removes follow request' do
+      expect(bob.requested?(sender)).to be false
+    end
+
+    it 'does not create follow relation' do
+      expect(bob.following?(sender)).to be false
+    end
+
+    it 'sends a follow request rejection salmon slap' do
+      expect(a_request(:post, "http://salmon.example.com/").with { |req|
+        xml = OStatus2::Salmon.new.unpack(req.body)
+        xml.match(TagManager::VERBS[:reject])
+      }).to have_been_made.once
+    end
+  end
+end
diff --git a/spec/services/unblock_service_spec.rb b/spec/services/unblock_service_spec.rb
index 126f70ff1..1b9ae1239 100644
--- a/spec/services/unblock_service_spec.rb
+++ b/spec/services/unblock_service_spec.rb
@@ -1,5 +1,41 @@
 require 'rails_helper'
 
 RSpec.describe UnblockService do
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   subject { UnblockService.new }
+
+  describe 'local' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
+
+    before do
+      sender.block!(bob)
+      subject.call(sender, bob)
+    end
+
+    it 'destroys the blocking relation' do
+      expect(sender.blocking?(bob)).to be false
+    end
+  end
+
+  describe 'remote' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+
+    before do
+      sender.block!(bob)
+      stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {})
+      subject.call(sender, bob)
+    end
+
+    it 'destroys the blocking relation' do
+      expect(sender.following?(bob)).to be false
+    end
+
+    it 'sends an unblock salmon slap' do
+      expect(a_request(:post, "http://salmon.example.com/").with { |req|
+        xml = OStatus2::Salmon.new.unpack(req.body)
+        xml.match(TagManager::VERBS[:unblock])
+      }).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/services/unfollow_service_spec.rb b/spec/services/unfollow_service_spec.rb
index 6541415d0..8ec2148a1 100644
--- a/spec/services/unfollow_service_spec.rb
+++ b/spec/services/unfollow_service_spec.rb
@@ -1,8 +1,41 @@
 require 'rails_helper'
 
 RSpec.describe UnfollowService do
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   subject { UnfollowService.new }
 
-  it 'destroys the following relation'
-  it 'sends remote interaction for remote user'
+  describe 'local' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
+
+    before do
+      sender.follow!(bob)
+      subject.call(sender, bob)
+    end
+
+    it 'destroys the following relation' do
+      expect(sender.following?(bob)).to be false
+    end
+  end
+
+  describe 'remote' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+
+    before do
+      sender.follow!(bob)
+      stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {})
+      subject.call(sender, bob)
+    end
+
+    it 'destroys the following relation' do
+      expect(sender.following?(bob)).to be false
+    end
+
+    it 'sends an unfollow salmon slap' do
+      expect(a_request(:post, "http://salmon.example.com/").with { |req|
+        xml = OStatus2::Salmon.new.unpack(req.body)
+        xml.match(TagManager::VERBS[:unfollow])
+      }).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/services/unmute_service_spec.rb b/spec/services/unmute_service_spec.rb
new file mode 100644
index 000000000..5dc971fb1
--- /dev/null
+++ b/spec/services/unmute_service_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe UnmuteService do
+  subject { UnmuteService.new }
+end