about summary refs log tree commit diff
path: root/spec/services
diff options
context:
space:
mode:
authorStarfall <us@starfall.systems>2022-11-10 08:50:11 -0600
committerStarfall <us@starfall.systems>2022-11-10 08:50:11 -0600
commit67d1a0476d77e2ed0ca15dd2981c54c2b90b0742 (patch)
tree152f8c13a341d76738e8e2c09b24711936e6af68 /spec/services
parentb581e6b6d4a5ba9ed4ae17427b7f2d5d158be4e5 (diff)
parentee7e49d1b1323618e16026bc8db8ab7f9459cc2d (diff)
Merge remote-tracking branch 'glitch/main'
- Remove Helm charts
- Lots of conflicts with our removal of recommended settings and custom
  icons
Diffstat (limited to 'spec/services')
-rw-r--r--spec/services/account_search_service_spec.rb1
-rw-r--r--spec/services/activitypub/fetch_featured_collection_service_spec.rb2
-rw-r--r--spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb95
-rw-r--r--spec/services/activitypub/fetch_remote_account_service_spec.rb52
-rw-r--r--spec/services/activitypub/fetch_remote_actor_service_spec.rb180
-rw-r--r--spec/services/activitypub/fetch_remote_key_service_spec.rb83
-rw-r--r--spec/services/activitypub/process_collection_service_spec.rb6
-rw-r--r--spec/services/fetch_resource_service_spec.rb2
-rw-r--r--spec/services/follow_service_spec.rb13
-rw-r--r--spec/services/process_mentions_service_spec.rb100
-rw-r--r--spec/services/report_service_spec.rb37
-rw-r--r--spec/services/resolve_account_service_spec.rb4
12 files changed, 528 insertions, 47 deletions
diff --git a/spec/services/account_search_service_spec.rb b/spec/services/account_search_service_spec.rb
index 5b7182586..81cbc175e 100644
--- a/spec/services/account_search_service_spec.rb
+++ b/spec/services/account_search_service_spec.rb
@@ -45,7 +45,6 @@ describe AccountSearchService, type: :service do
 
         results = subject.call('e@example.com', nil, limit: 2)
 
-        expect(results.size).to eq 2
         expect(results).to eq([exact, remote]).or eq([exact, remote_too])
       end
     end
diff --git a/spec/services/activitypub/fetch_featured_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_collection_service_spec.rb
index f552b9dc0..e6336dc1b 100644
--- a/spec/services/activitypub/fetch_featured_collection_service_spec.rb
+++ b/spec/services/activitypub/fetch_featured_collection_service_spec.rb
@@ -65,7 +65,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
       stub_request(:get, 'https://example.com/account/pinned/3').to_return(status: 404)
       stub_request(:get, 'https://example.com/account/pinned/4').to_return(status: 200, body: Oj.dump(status_json_4))
 
-      subject.call(actor)
+      subject.call(actor, note: true, hashtag: false)
     end
 
     it 'sets expected posts as pinned posts' do
diff --git a/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb
new file mode 100644
index 000000000..6ca22c9fc
--- /dev/null
+++ b/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb
@@ -0,0 +1,95 @@
+require 'rails_helper'
+
+RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service do
+  let(:collection_url) { 'https://example.com/account/tags' }
+  let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/account') }
+
+  let(:items) do
+    [
+      { type: 'Hashtag', href: 'https://example.com/account/tagged/foo', name: 'Foo' },
+      { type: 'Hashtag', href: 'https://example.com/account/tagged/bar', name: 'bar' },
+      { type: 'Hashtag', href: 'https://example.com/account/tagged/baz', name: 'baZ' },
+    ]
+  end
+
+  let(:payload) do
+    {
+      '@context': 'https://www.w3.org/ns/activitystreams',
+      type: 'Collection',
+      id: collection_url,
+      items: items,
+    }.with_indifferent_access
+  end
+
+  subject { described_class.new }
+
+  shared_examples 'sets featured tags' do
+    before do
+      subject.call(actor, collection_url)
+    end
+
+    it 'sets expected tags as pinned tags' do
+      expect(actor.featured_tags.map(&:display_name)).to match_array ['Foo', 'bar', 'baZ']
+    end
+  end
+
+  describe '#call' do
+    context 'when the endpoint is a Collection' do
+      before do
+        stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
+      end
+
+      it_behaves_like 'sets featured tags'
+    end
+
+    context 'when the account already has featured tags' do
+      before do
+        stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
+
+        actor.featured_tags.create!(name: 'FoO')
+        actor.featured_tags.create!(name: 'baz')
+        actor.featured_tags.create!(name: 'oh').update(name: nil)
+      end
+
+      it_behaves_like 'sets featured tags'
+    end
+
+    context 'when the endpoint is an OrderedCollection' do
+      let(:payload) do
+        {
+          '@context': 'https://www.w3.org/ns/activitystreams',
+          type: 'OrderedCollection',
+          id: collection_url,
+          orderedItems: items,
+        }.with_indifferent_access
+      end
+
+      before do
+        stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
+      end
+
+      it_behaves_like 'sets featured tags'
+    end
+
+    context 'when the endpoint is a paginated Collection' do
+      let(:payload) do
+        {
+          '@context': 'https://www.w3.org/ns/activitystreams',
+          type: 'Collection',
+          id: collection_url,
+          first: {
+            type: 'CollectionPage',
+            partOf: collection_url,
+            items: items,
+          }
+        }.with_indifferent_access
+      end
+
+      before do
+        stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
+      end
+
+      it_behaves_like 'sets featured tags'
+    end
+  end
+end
diff --git a/spec/services/activitypub/fetch_remote_account_service_spec.rb b/spec/services/activitypub/fetch_remote_account_service_spec.rb
index aa13f0a9b..ec6f1f41d 100644
--- a/spec/services/activitypub/fetch_remote_account_service_spec.rb
+++ b/spec/services/activitypub/fetch_remote_account_service_spec.rb
@@ -119,6 +119,58 @@ RSpec.describe ActivityPub::FetchRemoteAccountService, type: :service do
       include_examples 'sets profile data'
     end
 
+    context 'when WebFinger returns a different URI' do
+      let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'does not create account' do
+        expect(account).to be_nil
+      end
+    end
+
+    context 'when WebFinger returns a different URI after a redirection' do
+      let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+        stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'looks up "redirected" webfinger' do
+        account
+        expect(a_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af')).to have_been_made.once
+      end
+
+      it 'does not create account' do
+        expect(account).to be_nil
+      end
+    end
+
     context 'with wrong id' do
       it 'does not create account' do
         expect(subject.call('https://fake.address/@foo', prefetched_body: Oj.dump(actor))).to be_nil
diff --git a/spec/services/activitypub/fetch_remote_actor_service_spec.rb b/spec/services/activitypub/fetch_remote_actor_service_spec.rb
new file mode 100644
index 000000000..20117c66d
--- /dev/null
+++ b/spec/services/activitypub/fetch_remote_actor_service_spec.rb
@@ -0,0 +1,180 @@
+require 'rails_helper'
+
+RSpec.describe ActivityPub::FetchRemoteActorService, type: :service do
+  subject { ActivityPub::FetchRemoteActorService.new }
+
+  let!(:actor) do
+    {
+      '@context': 'https://www.w3.org/ns/activitystreams',
+      id: 'https://example.com/alice',
+      type: 'Person',
+      preferredUsername: 'alice',
+      name: 'Alice',
+      summary: 'Foo bar',
+      inbox: 'http://example.com/alice/inbox',
+    }
+  end
+
+  describe '#call' do
+    let(:account) { subject.call('https://example.com/alice', id: true) }
+
+    shared_examples 'sets profile data' do
+      it 'returns an account' do
+        expect(account).to be_an Account
+      end
+
+      it 'sets display name' do
+        expect(account.display_name).to eq 'Alice'
+      end
+
+      it 'sets note' do
+        expect(account.note).to eq 'Foo bar'
+      end
+
+      it 'sets URL' do
+        expect(account.url).to eq 'https://example.com/alice'
+      end
+    end
+
+    context 'when the account does not have a inbox' do
+      let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
+
+      before do
+        actor[:inbox] = nil
+
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'returns nil' do
+        expect(account).to be_nil
+      end
+    end
+
+    context 'when URI and WebFinger share the same host' do
+      let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'sets username and domain from webfinger' do
+        expect(account.username).to eq 'alice'
+        expect(account.domain).to eq 'example.com'
+      end
+
+      include_examples 'sets profile data'
+    end
+
+    context 'when WebFinger presents different domain than URI' do
+      let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+        stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'looks up "redirected" webfinger' do
+        account
+        expect(a_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af')).to have_been_made.once
+      end
+
+      it 'sets username and domain from final webfinger' do
+        expect(account.username).to eq 'alice'
+        expect(account.domain).to eq 'iscool.af'
+      end
+
+      include_examples 'sets profile data'
+    end
+
+    context 'when WebFinger returns a different URI' do
+      let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'does not create account' do
+        expect(account).to be_nil
+      end
+    end
+
+    context 'when WebFinger returns a different URI after a redirection' do
+      let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+        stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'looks up "redirected" webfinger' do
+        account
+        expect(a_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af')).to have_been_made.once
+      end
+
+      it 'does not create account' do
+        expect(account).to be_nil
+      end
+    end
+
+    context 'with wrong id' do
+      it 'does not create account' do
+        expect(subject.call('https://fake.address/@foo', prefetched_body: Oj.dump(actor))).to be_nil
+      end
+    end
+  end
+end
diff --git a/spec/services/activitypub/fetch_remote_key_service_spec.rb b/spec/services/activitypub/fetch_remote_key_service_spec.rb
new file mode 100644
index 000000000..3186c4270
--- /dev/null
+++ b/spec/services/activitypub/fetch_remote_key_service_spec.rb
@@ -0,0 +1,83 @@
+require 'rails_helper'
+
+RSpec.describe ActivityPub::FetchRemoteKeyService, type: :service do
+  subject { ActivityPub::FetchRemoteKeyService.new }
+
+  let(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
+
+  let(:public_key_pem) do
+    "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu3L4vnpNLzVH31MeWI39\n4F0wKeJFsLDAsNXGeOu0QF2x+h1zLWZw/agqD2R3JPU9/kaDJGPIV2Sn5zLyUA9S\n6swCCMOtn7BBR9g9sucgXJmUFB0tACH2QSgHywMAybGfmSb3LsEMNKsGJ9VsvYoh\n8lDET6X4Pyw+ZJU0/OLo/41q9w+OrGtlsTm/PuPIeXnxa6BLqnDaxC+4IcjG/FiP\nahNCTINl/1F/TgSSDZ4Taf4U9XFEIFw8wmgploELozzIzKq+t8nhQYkgAkt64euW\npva3qL5KD1mTIZQEP+LZvh3s2WHrLi3fhbdRuwQ2c0KkJA2oSTFPDpqqbPGZ3Qvu\nHQIDAQAB\n-----END PUBLIC KEY-----\n"
+  end
+
+  let(:public_key_id) { 'https://example.com/alice#main-key' }
+
+  let(:key_json) do
+    {
+      id: public_key_id,
+      owner: 'https://example.com/alice',
+      publicKeyPem: public_key_pem,
+    }
+  end
+
+  let(:actor_public_key) { key_json }
+
+  let(:actor) do
+    {
+      '@context': [
+        'https://www.w3.org/ns/activitystreams',
+        'https://w3id.org/security/v1',
+      ],
+      id: 'https://example.com/alice',
+      type: 'Person',
+      preferredUsername: 'alice',
+      name: 'Alice',
+      summary: 'Foo bar',
+      inbox: 'http://example.com/alice/inbox',
+      publicKey: actor_public_key,
+    }
+  end
+
+  before do
+    stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+    stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+  end
+
+  describe '#call' do
+    let(:account) { subject.call(public_key_id, id: false) }
+
+    context 'when the key is a sub-object from the actor' do
+      before do
+        stub_request(:get, public_key_id).to_return(body: Oj.dump(actor))
+      end
+
+      it 'returns the expected account' do
+        expect(account.uri).to eq 'https://example.com/alice'
+      end
+    end
+
+    context 'when the key is a separate document' do
+      let(:public_key_id) { 'https://example.com/alice-public-key.json' }
+
+      before do
+        stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })))
+      end
+
+      it 'returns the expected account' do
+        expect(account.uri).to eq 'https://example.com/alice'
+      end
+    end
+
+    context 'when the key and owner do not match' do
+      let(:public_key_id) { 'https://example.com/fake-public-key.json' }
+      let(:actor_public_key) { 'https://example.com/alice-public-key.json' }
+
+      before do
+        stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })))
+      end
+
+      it 'returns the nil' do
+        expect(account).to be_nil
+      end
+    end
+  end
+end
diff --git a/spec/services/activitypub/process_collection_service_spec.rb b/spec/services/activitypub/process_collection_service_spec.rb
index 3eccaab5b..093a188a2 100644
--- a/spec/services/activitypub/process_collection_service_spec.rb
+++ b/spec/services/activitypub/process_collection_service_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
       let(:forwarder) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/other_account') }
 
       it 'does not process payload if no signature exists' do
-        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_account!).and_return(nil)
+        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil)
         expect(ActivityPub::Activity).not_to receive(:factory)
 
         subject.call(json, forwarder)
@@ -77,7 +77,7 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
       it 'processes payload with actor if valid signature exists' do
         payload['signature'] = { 'type' => 'RsaSignature2017' }
 
-        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_account!).and_return(actor)
+        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(actor)
         expect(ActivityPub::Activity).to receive(:factory).with(instance_of(Hash), actor, instance_of(Hash))
 
         subject.call(json, forwarder)
@@ -86,7 +86,7 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
       it 'does not process payload if invalid signature exists' do
         payload['signature'] = { 'type' => 'RsaSignature2017' }
 
-        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_account!).and_return(nil)
+        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil)
         expect(ActivityPub::Activity).not_to receive(:factory)
 
         subject.call(json, forwarder)
diff --git a/spec/services/fetch_resource_service_spec.rb b/spec/services/fetch_resource_service_spec.rb
index ded05ffbc..c0c96ab69 100644
--- a/spec/services/fetch_resource_service_spec.rb
+++ b/spec/services/fetch_resource_service_spec.rb
@@ -66,7 +66,7 @@ RSpec.describe FetchResourceService, type: :service do
 
       it 'signs request' do
         subject
-        expect(a_request(:get, url).with(headers: { 'Signature' => /keyId="#{Regexp.escape(ActivityPub::TagManager.instance.uri_for(Account.representative) + '#main-key')}"/ })).to have_been_made
+        expect(a_request(:get, url).with(headers: { 'Signature' => /keyId="#{Regexp.escape(ActivityPub::TagManager.instance.key_uri_for(Account.representative))}"/ })).to have_been_made
       end
 
       context 'when content type is application/atom+xml' do
diff --git a/spec/services/follow_service_spec.rb b/spec/services/follow_service_spec.rb
index 02bc87c58..88346ec54 100644
--- a/spec/services/follow_service_spec.rb
+++ b/spec/services/follow_service_spec.rb
@@ -121,6 +121,19 @@ RSpec.describe FollowService, type: :service do
         expect(sender.muting_reblogs?(bob)).to be false
       end
     end
+
+    describe 'already followed account, changing languages' do
+      let(:bob) { Fabricate(:account, username: 'bob') }
+
+      before do
+        sender.follow!(bob)
+        subject.call(sender, bob, languages: %w(en es))
+      end
+
+      it 'changes languages' do
+        expect(Follow.find_by(account: sender, target_account: bob)&.languages).to match_array %w(en es)
+      end
+    end
   end
 
   context 'remote ActivityPub account' do
diff --git a/spec/services/process_mentions_service_spec.rb b/spec/services/process_mentions_service_spec.rb
index 89b265e9a..5b9d17a4c 100644
--- a/spec/services/process_mentions_service_spec.rb
+++ b/spec/services/process_mentions_service_spec.rb
@@ -1,43 +1,85 @@
 require 'rails_helper'
 
 RSpec.describe ProcessMentionsService, type: :service do
-  let(:account)    { Fabricate(:account, username: 'alice') }
-  let(:visibility) { :public }
-  let(:status)     { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}", visibility: visibility) }
+  let(:account) { Fabricate(:account, username: 'alice') }
 
   subject { ProcessMentionsService.new }
 
-  context 'ActivityPub' do
-    context do
-      let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
+  context 'when mentions contain blocked accounts' do
+    let(:non_blocked_account)          { Fabricate(:account) }
+    let(:individually_blocked_account) { Fabricate(:account) }
+    let(:domain_blocked_account)       { Fabricate(:account, domain: 'evil.com') }
+    let(:status) { Fabricate(:status, account: account, text: "Hello @#{non_blocked_account.acct} @#{individually_blocked_account.acct} @#{domain_blocked_account.acct}", visibility: :public) }
 
-      before do
-        subject.call(status)
-      end
+    before do
+      account.block!(individually_blocked_account)
+      account.domain_blocks.create!(domain: domain_blocked_account.domain)
 
-      it 'creates a mention' do
-        expect(remote_user.mentions.where(status: status).count).to eq 1
-      end
+      subject.call(status)
+    end
+
+    it 'creates a mention to the non-blocked account' do
+      expect(non_blocked_account.mentions.where(status: status).count).to eq 1
     end
 
-    context 'with an IDN domain' do
-      let!(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') }
-      let!(:status) { Fabricate(:status, account: account, text: "Hello @sneak@hæresiar.ch") }
+    it 'does not create a mention to the individually blocked account' do
+      expect(individually_blocked_account.mentions.where(status: status).count).to eq 0
+    end
 
-      before do
-        subject.call(status)
+    it 'does not create a mention to the domain-blocked account' do
+      expect(domain_blocked_account.mentions.where(status: status).count).to eq 0
+    end
+  end
+
+  context 'resolving a mention to a remote account' do
+    let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}", visibility: :public) }
+
+    context 'ActivityPub' do
+      context do
+        let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
+
+        before do
+          subject.call(status)
+        end
+
+        it 'creates a mention' do
+          expect(remote_user.mentions.where(status: status).count).to eq 1
+        end
       end
 
-      it 'creates a mention' do
-        expect(remote_user.mentions.where(status: status).count).to eq 1
+      context 'with an IDN domain' do
+        let!(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') }
+        let!(:status) { Fabricate(:status, account: account, text: "Hello @sneak@hæresiar.ch") }
+
+        before do
+          subject.call(status)
+        end
+
+        it 'creates a mention' do
+          expect(remote_user.mentions.where(status: status).count).to eq 1
+        end
+      end
+
+      context 'with an IDN TLD' do
+        let!(:remote_user) { Fabricate(:account, username: 'foo', protocol: :activitypub, domain: 'xn--y9a3aq.xn--y9a3aq', inbox_url: 'http://example.com/inbox') }
+        let!(:status) { Fabricate(:status, account: account, text: "Hello @foo@հայ.հայ") }
+
+        before do
+          subject.call(status)
+        end
+
+        it 'creates a mention' do
+          expect(remote_user.mentions.where(status: status).count).to eq 1
+        end
       end
     end
 
-    context 'with an IDN TLD' do
-      let!(:remote_user) { Fabricate(:account, username: 'foo', protocol: :activitypub, domain: 'xn--y9a3aq.xn--y9a3aq', inbox_url: 'http://example.com/inbox') }
-      let!(:status) { Fabricate(:status, account: account, text: "Hello @foo@հայ.հայ") }
+    context 'Temporarily-unreachable ActivityPub user' do
+      let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) }
 
       before do
+        stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
+        stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com").to_return(status: 500)
         subject.call(status)
       end
 
@@ -46,18 +88,4 @@ RSpec.describe ProcessMentionsService, type: :service do
       end
     end
   end
-
-  context 'Temporarily-unreachable ActivityPub user' do
-    let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) }
-
-    before do
-      stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
-      stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com").to_return(status: 500)
-      subject.call(status)
-    end
-
-    it 'creates a mention' do
-      expect(remote_user.mentions.where(status: status).count).to eq 1
-    end
-  end
 end
diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb
index ea68b3344..02bc42ac1 100644
--- a/spec/services/report_service_spec.rb
+++ b/spec/services/report_service_spec.rb
@@ -42,13 +42,44 @@ RSpec.describe ReportService, type: :service do
       end
 
       it 'creates a report' do
-        is_expected.to change { target_account.targeted_reports.count }.from(0).to(1)
+        expect { subject.call }.to change { target_account.targeted_reports.count }.from(0).to(1)
+      end
+
+      it 'attaches the DM to the report' do
+        subject.call
+        expect(target_account.targeted_reports.pluck(:status_ids)).to eq [[status.id]]
       end
     end
 
     context 'when it is not addressed to the reporter' do
       it 'errors out' do
-        is_expected.to raise_error
+        expect { subject.call }.to raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+
+    context 'when the reporter is remote' do
+      let(:source_account) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/1') }
+
+      context 'when it is addressed to the reporter' do
+        before do
+          status.mentions.create(account: source_account)
+        end
+
+        it 'creates a report' do
+          expect { subject.call }.to change { target_account.targeted_reports.count }.from(0).to(1)
+        end
+
+        it 'attaches the DM to the report' do
+          subject.call
+          expect(target_account.targeted_reports.pluck(:status_ids)).to eq [[status.id]]
+        end
+      end
+
+      context 'when it is not addressed to the reporter' do
+        it 'does not add the DM to the report' do
+          subject.call
+          expect(target_account.targeted_reports.pluck(:status_ids)).to eq [[]]
+        end
       end
     end
   end
@@ -67,7 +98,7 @@ RSpec.describe ReportService, type: :service do
     end
 
     it 'does not send an e-mail' do
-      is_expected.to_not change(ActionMailer::Base.deliveries, :count).from(0)
+      expect { subject.call }.to_not change(ActionMailer::Base.deliveries, :count).from(0)
     end
   end
 end
diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb
index 8c302e1d8..654606bea 100644
--- a/spec/services/resolve_account_service_spec.rb
+++ b/spec/services/resolve_account_service_spec.rb
@@ -137,8 +137,8 @@ RSpec.describe ResolveAccountService, type: :service do
       stub_request(:get, 'https://evil.example.com/.well-known/webfinger?resource=acct:foo@evil.example.com').to_return(body: Oj.dump(webfinger2), headers: { 'Content-Type': 'application/jrd+json' })
     end
 
-    it 'returns new remote account' do
-      expect { subject.call('Foo@redirected.example.com') }.to raise_error Webfinger::RedirectError
+    it 'does not return a new remote account' do
+      expect(subject.call('Foo@redirected.example.com')).to be_nil
     end
   end