about summary refs log tree commit diff
path: root/spec/lib
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/activitypub/activity/create_spec.rb129
-rw-r--r--spec/lib/activitypub/tag_manager_spec.rb2
-rw-r--r--spec/lib/emoji_spec.rb15
-rw-r--r--spec/lib/formatter_spec.rb126
-rw-r--r--spec/lib/language_detector_spec.rb34
-rw-r--r--spec/lib/ostatus/atom_serializer_spec.rb104
-rw-r--r--spec/lib/ostatus/tag_manager_spec.rb70
-rw-r--r--spec/lib/tag_manager_spec.rb75
8 files changed, 406 insertions, 149 deletions
diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb
index fcb044ebc..cdd499150 100644
--- a/spec/lib/activitypub/activity/create_spec.rb
+++ b/spec/lib/activitypub/activity/create_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe ActivityPub::Activity::Create do
 
   before do
     stub_request(:get, 'http://example.com/attachment.png').to_return(request_fixture('avatar.txt'))
+    stub_request(:get, 'http://example.com/emoji.png').to_return(body: attachment_fixture('emojo.png'))
   end
 
   describe '#perform' do
@@ -170,6 +171,26 @@ RSpec.describe ActivityPub::Activity::Create do
       end
     end
 
+    context 'with mentions missing href' do
+      let(:object_json) do
+        {
+          id: 'bar',
+          type: 'Note',
+          content: 'Lorem ipsum',
+          tag: [
+            {
+              type: 'Mention',
+            },
+          ],
+        }
+      end
+
+      it 'creates status' do
+        status = sender.statuses.first
+        expect(status).to_not be_nil
+      end
+    end
+
     context 'with media attachments' do
       let(:object_json) do
         {
@@ -194,6 +215,27 @@ RSpec.describe ActivityPub::Activity::Create do
       end
     end
 
+    context 'with media attachments missing url' do
+      let(:object_json) do
+        {
+          id: 'bar',
+          type: 'Note',
+          content: 'Lorem ipsum',
+          attachment: [
+            {
+              type: 'Document',
+              mime_type: 'image/png',
+            },
+          ],
+        }
+      end
+
+      it 'creates status' do
+        status = sender.statuses.first
+        expect(status).to_not be_nil
+      end
+    end
+
     context 'with hashtags' do
       let(:object_json) do
         {
@@ -217,5 +259,92 @@ RSpec.describe ActivityPub::Activity::Create do
         expect(status.tags.map(&:name)).to include('test')
       end
     end
+
+    context 'with hashtags missing name' do
+      let(:object_json) do
+        {
+          id: 'bar',
+          type: 'Note',
+          content: 'Lorem ipsum',
+          tag: [
+            {
+              type: 'Hashtag',
+              href: 'http://example.com/blah',
+            },
+          ],
+        }
+      end
+
+      it 'creates status' do
+        status = sender.statuses.first
+        expect(status).to_not be_nil
+      end
+    end
+
+    context 'with emojis' do
+      let(:object_json) do
+        {
+          id: 'bar',
+          type: 'Note',
+          content: 'Lorem ipsum :tinking:',
+          tag: [
+            {
+              type: 'Emoji',
+              href: 'http://example.com/emoji.png',
+              name: 'tinking',
+            },
+          ],
+        }
+      end
+
+      it 'creates status' do
+        status = sender.statuses.first
+
+        expect(status).to_not be_nil
+        expect(status.emojis.map(&:shortcode)).to include('tinking')
+      end
+    end
+
+    context 'with emojis missing name' do
+      let(:object_json) do
+        {
+          id: 'bar',
+          type: 'Note',
+          content: 'Lorem ipsum :tinking:',
+          tag: [
+            {
+              type: 'Emoji',
+              href: 'http://example.com/emoji.png',
+            },
+          ],
+        }
+      end
+
+      it 'creates status' do
+        status = sender.statuses.first
+        expect(status).to_not be_nil
+      end
+    end
+
+    context 'with emojis missing href' do
+      let(:object_json) do
+        {
+          id: 'bar',
+          type: 'Note',
+          content: 'Lorem ipsum :tinking:',
+          tag: [
+            {
+              type: 'Emoji',
+              name: 'tinking',
+            },
+          ],
+        }
+      end
+
+      it 'creates status' do
+        status = sender.statuses.first
+        expect(status).to_not be_nil
+      end
+    end
   end
 end
diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb
index dea8abc65..0d1665216 100644
--- a/spec/lib/activitypub/tag_manager_spec.rb
+++ b/spec/lib/activitypub/tag_manager_spec.rb
@@ -108,7 +108,7 @@ RSpec.describe ActivityPub::TagManager do
 
     it 'returns the local status for OStatus tag: URI' do
       status = Fabricate(:status)
-      expect(subject.uri_to_resource(::TagManager.instance.uri_for(status), Status)).to eq status
+      expect(subject.uri_to_resource(OStatus::TagManager.instance.uri_for(status), Status)).to eq status
     end
 
     it 'returns the local status for OStatus StreamEntry URL' do
diff --git a/spec/lib/emoji_spec.rb b/spec/lib/emoji_spec.rb
deleted file mode 100644
index 04931ccfb..000000000
--- a/spec/lib/emoji_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe Emoji do
-  describe '#unicode' do
-    it 'returns a unicode for a shortcode' do
-      expect(Emoji.instance.unicode(':joy:')).to eq '😂'
-    end
-  end
-
-  describe '#names' do
-    it 'returns an array' do
-      expect(Emoji.instance.names).to be_an Array
-    end
-  end
-end
diff --git a/spec/lib/formatter_spec.rb b/spec/lib/formatter_spec.rb
index ab04ccbab..71b6b78d2 100644
--- a/spec/lib/formatter_spec.rb
+++ b/spec/lib/formatter_spec.rb
@@ -89,6 +89,54 @@ RSpec.describe Formatter do
       end
     end
 
+    context 'matches a URL with Japanese path string' do
+      let(:text) { 'https://ja.wikipedia.org/wiki/日本' }
+
+      it 'has valid URL' do
+        is_expected.to include 'href="https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC"'
+      end
+    end
+
+    context 'matches a URL with Korean path string' do
+      let(:text) { 'https://ko.wikipedia.org/wiki/대한민국' }
+
+      it 'has valid URL' do
+        is_expected.to include 'href="https://ko.wikipedia.org/wiki/%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD"'
+      end
+    end
+
+    context 'matches a URL with Simplified Chinese path string' do
+      let(:text) { 'https://baike.baidu.com/item/中华人民共和国' }
+
+      it 'has valid URL' do
+        is_expected.to include 'href="https://baike.baidu.com/item/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"'
+      end
+    end
+
+    context 'matches a URL with Traditional Chinese path string' do
+      let(:text) { 'https://zh.wikipedia.org/wiki/臺灣' }
+
+      it 'has valid URL' do
+        is_expected.to include 'href="https://zh.wikipedia.org/wiki/%E8%87%BA%E7%81%A3"'
+      end
+    end
+
+    context 'contains unsafe URL (XSS attack, visible part)' do
+      let(:text) { %q{http://example.com/b<del>b</del>} }
+
+      it 'has escaped HTML' do
+        is_expected.to include '&lt;del&gt;b&lt;/del&gt;'
+      end
+    end
+
+    context 'contains unsafe URL (XSS attack, invisible part)' do
+      let(:text) { %q{http://example.com/blahblahblahblah/a<script>alert("Hello")</script>} }
+
+      it 'has escaped HTML' do
+        is_expected.to include '&lt;script&gt;alert(&quot;Hello&quot;)&lt;/script&gt;'
+      end
+    end
+
     context 'contains HTML (script tag)' do
       let(:text) { '<script>alert("Hello")</script>' }
 
@@ -175,6 +223,45 @@ RSpec.describe Formatter do
 
         include_examples 'encode and link URLs'
       end
+
+      context 'with custom_emojify option' do
+        let!(:emoji) { Fabricate(:custom_emoji) }
+        let(:status) { Fabricate(:status, account: local_account, text: text) }
+
+        subject { Formatter.instance.format(status, custom_emojify: true) }
+
+        context 'with emoji at the start' do
+          let(:text) { ':coolcat: Beep boop' }
+
+          it 'converts shortcode to image tag' do
+            is_expected.to match(/<p><img draggable="false" class="emojione" alt=":coolcat:"/)
+          end
+        end
+
+        context 'with emoji in the middle' do
+          let(:text) { 'Beep :coolcat: boop' }
+
+          it 'converts shortcode to image tag' do
+            is_expected.to match(/Beep <img draggable="false" class="emojione" alt=":coolcat:"/)
+          end
+        end
+
+        context 'with concatenated emoji' do
+          let(:text) { ':coolcat::coolcat:' }
+
+          it 'does not touch the shortcodes' do
+            is_expected.to match(/:coolcat::coolcat:/)
+          end
+        end
+
+        context 'with emoji at the end' do
+          let(:text) { 'Beep boop :coolcat:' }
+
+          it 'converts shortcode to image tag' do
+            is_expected.to match(/boop <img draggable="false" class="emojione" alt=":coolcat:"/)
+          end
+        end
+      end
     end
 
     context 'with remote status' do
@@ -183,6 +270,45 @@ RSpec.describe Formatter do
       it 'reformats' do
         is_expected.to eq 'Beep boop'
       end
+
+      context 'with custom_emojify option' do
+        let!(:emoji) { Fabricate(:custom_emoji, domain: remote_account.domain) }
+        let(:status) { Fabricate(:status, account: remote_account, text: text) }
+
+        subject { Formatter.instance.format(status, custom_emojify: true) }
+
+        context 'with emoji at the start' do
+          let(:text) { '<p>:coolcat: Beep boop<br />' }
+
+          it 'converts shortcode to image tag' do
+            is_expected.to match(/<p><img draggable="false" class="emojione" alt=":coolcat:"/)
+          end
+        end
+
+        context 'with emoji in the middle' do
+          let(:text) { '<p>Beep :coolcat: boop</p>' }
+
+          it 'converts shortcode to image tag' do
+            is_expected.to match(/Beep <img draggable="false" class="emojione" alt=":coolcat:"/)
+          end
+        end
+
+        context 'with concatenated emoji' do
+          let(:text) { '<p>:coolcat::coolcat:</p>' }
+
+          it 'does not touch the shortcodes' do
+            is_expected.to match(/<p>:coolcat::coolcat:<\/p>/)
+          end
+        end
+
+        context 'with emoji at the end' do
+          let(:text) { '<p>Beep boop<br />:coolcat:</p>' }
+
+          it 'converts shortcode to image tag' do
+            is_expected.to match(/<br><img draggable="false" class="emojione" alt=":coolcat:"/)
+          end
+        end
+      end
     end
   end
 
diff --git a/spec/lib/language_detector_spec.rb b/spec/lib/language_detector_spec.rb
index ec39cb6a0..d17026511 100644
--- a/spec/lib/language_detector_spec.rb
+++ b/spec/lib/language_detector_spec.rb
@@ -3,10 +3,10 @@
 require 'rails_helper'
 
 describe LanguageDetector do
-  describe 'prepared_text' do
+  describe 'prepare_text' do
     it 'returns unmodified string without special cases' do
       string = 'just a regular string'
-      result = described_class.new(string).prepared_text
+      result = described_class.instance.send(:prepare_text, string)
 
       expect(result).to eq string
     end
@@ -14,33 +14,35 @@ describe LanguageDetector do
     it 'collapses spacing in strings' do
       string = 'The formatting   in    this is very        odd'
 
-      result = described_class.new(string).prepared_text
+      result = described_class.instance.send(:prepare_text, string)
       expect(result).to eq 'The formatting in this is very odd'
     end
 
     it 'strips usernames from strings before detection' do
       string = '@username Yeah, very surreal...! also @friend'
 
-      result = described_class.new(string).prepared_text
+      result = described_class.instance.send(:prepare_text, string)
       expect(result).to eq 'Yeah, very surreal...! also'
     end
 
     it 'strips URLs from strings before detection' do
       string = 'Our website is https://example.com and also http://localhost.dev'
 
-      result = described_class.new(string).prepared_text
+      result = described_class.instance.send(:prepare_text, string)
       expect(result).to eq 'Our website is and also'
     end
 
     it 'strips #hashtags from strings before detection' do
       string = 'Hey look at all the #animals and #fish'
 
-      result = described_class.new(string).prepared_text
+      result = described_class.instance.send(:prepare_text, string)
       expect(result).to eq 'Hey look at all the and'
     end
   end
 
-  describe 'to_iso_s' do
+  describe 'detect' do
+    let(:account_without_user_locale) { Fabricate(:user, locale: nil).account }
+
     it 'detects english language for basic strings' do
       strings = [
         "Hello and welcome to mastodon how are you today?",
@@ -48,7 +50,7 @@ describe LanguageDetector do
         "a lot of people just want to feel righteous all the time and that's all that matters",
       ]
       strings.each do |string|
-        result = described_class.new(string).to_iso_s
+        result = described_class.instance.detect(string, account_without_user_locale)
 
         expect(result).to eq(:en), string
       end
@@ -56,14 +58,14 @@ describe LanguageDetector do
 
     it 'detects spanish language' do
       string = 'Obtener un Hola y bienvenidos a Mastodon'
-      result = described_class.new(string).to_iso_s
+      result = described_class.instance.detect(string, account_without_user_locale)
 
       expect(result).to eq :es
     end
 
     describe 'when language can\'t be detected' do
       it 'uses nil when sent an empty document' do
-        result = described_class.new('').to_iso_s
+        result = described_class.instance.detect('', account_without_user_locale)
         expect(result).to eq nil
       end
 
@@ -73,7 +75,7 @@ describe LanguageDetector do
           cld_result = CLD3::NNetLanguageIdentifier.new(0, 2048).find_language(string)
           expect(cld_result).not_to eq :en
 
-          result = described_class.new(string).to_iso_s
+          result = described_class.instance.detect(string, account_without_user_locale)
 
           expect(result).to eq nil
         end
@@ -82,14 +84,13 @@ describe LanguageDetector do
       describe 'with an account' do
         it 'uses the account locale when present' do
           account = double(user_locale: 'fr')
-          result  = described_class.new('', account).to_iso_s
+          result  = described_class.instance.detect('', account)
 
           expect(result).to eq :fr
         end
 
         it 'uses nil when account is present but has no locale' do
-          account = double(user_locale: nil)
-          result  = described_class.new('', account).to_iso_s
+          result  = described_class.instance.detect('', account_without_user_locale)
 
           expect(result).to eq nil
         end
@@ -97,8 +98,7 @@ describe LanguageDetector do
 
       describe 'with an `en` default locale' do
         it 'uses nil for undetectable string' do
-          string = ''
-          result = described_class.new(string).to_iso_s
+          result = described_class.instance.detect('', account_without_user_locale)
 
           expect(result).to eq nil
         end
@@ -114,7 +114,7 @@ describe LanguageDetector do
 
         it 'uses nil for undetectable string' do
           string = ''
-          result = described_class.new(string).to_iso_s
+          result = described_class.instance.detect(string, account_without_user_locale)
 
           expect(result).to eq nil
         end
diff --git a/spec/lib/ostatus/atom_serializer_spec.rb b/spec/lib/ostatus/atom_serializer_spec.rb
index 0451eceeb..00e6f09dc 100644
--- a/spec/lib/ostatus/atom_serializer_spec.rb
+++ b/spec/lib/ostatus/atom_serializer_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe OStatus::AtomSerializer do
       follow_request_salmon = serialize(follow_request)
 
       object_type = follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq TagManager::TYPES[:activity]
+      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
     end
 
     it 'appends activity:verb element with request_friend type' do
@@ -26,7 +26,7 @@ RSpec.describe OStatus::AtomSerializer do
       follow_request_salmon = serialize(follow_request)
 
       verb = follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq TagManager::VERBS[:request_friend]
+      expect(verb.text).to eq OStatus::TagManager::VERBS[:request_friend]
     end
 
     it 'appends activity:object with target account' do
@@ -44,13 +44,13 @@ RSpec.describe OStatus::AtomSerializer do
     it 'adds namespaces' do
       element = serialize
 
-      expect(element['xmlns']).to eq TagManager::XMLNS
-      expect(element['xmlns:thr']).to eq TagManager::THR_XMLNS
-      expect(element['xmlns:activity']).to eq TagManager::AS_XMLNS
-      expect(element['xmlns:poco']).to eq TagManager::POCO_XMLNS
-      expect(element['xmlns:media']).to eq TagManager::MEDIA_XMLNS
-      expect(element['xmlns:ostatus']).to eq TagManager::OS_XMLNS
-      expect(element['xmlns:mastodon']).to eq TagManager::MTDN_XMLNS
+      expect(element['xmlns']).to eq OStatus::TagManager::XMLNS
+      expect(element['xmlns:thr']).to eq OStatus::TagManager::THR_XMLNS
+      expect(element['xmlns:activity']).to eq OStatus::TagManager::AS_XMLNS
+      expect(element['xmlns:poco']).to eq OStatus::TagManager::POCO_XMLNS
+      expect(element['xmlns:media']).to eq OStatus::TagManager::MEDIA_XMLNS
+      expect(element['xmlns:ostatus']).to eq OStatus::TagManager::OS_XMLNS
+      expect(element['xmlns:mastodon']).to eq OStatus::TagManager::MTDN_XMLNS
     end
   end
 
@@ -97,11 +97,23 @@ RSpec.describe OStatus::AtomSerializer do
 
       mentioned = element.nodes.find do |node|
         node.name == 'link' &&
-        node[:rel] == 'mentioned' &&
-        node['ostatus:object-type'] == TagManager::TYPES[:person]
+          node[:rel] == 'mentioned' &&
+          node['ostatus:object-type'] == OStatus::TagManager::TYPES[:person]
       end
+
       expect(mentioned[:href]).to eq 'https://cb6e6126.ngrok.io/users/username'
     end
+
+    it 'appends link elements for emojis' do
+      Fabricate(:custom_emoji)
+
+      status  = Fabricate(:status, text: ':coolcat:')
+      element = serialize(status)
+      emoji   = element.nodes.find { |node| node.name == 'link' && node[:rel] == 'emoji' }
+
+      expect(emoji[:name]).to eq 'coolcat'
+      expect(emoji[:href]).to_not be_blank
+    end
   end
 
   describe 'render' do
@@ -176,7 +188,7 @@ RSpec.describe OStatus::AtomSerializer do
       author = OStatus::AtomSerializer.new.author(account)
 
       object_type = author.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq TagManager::TYPES[:person]
+      expect(object_type.text).to eq OStatus::TagManager::TYPES[:person]
     end
 
     it 'appends email element with username and domain for local account' do
@@ -346,9 +358,9 @@ RSpec.describe OStatus::AtomSerializer do
         mentioned_person = entry.nodes.find do |node|
           node.name == 'link' &&
           node[:rel] == 'mentioned' &&
-          node['ostatus:object-type'] == TagManager::TYPES[:collection]
+          node['ostatus:object-type'] == OStatus::TagManager::TYPES[:collection]
         end
-        expect(mentioned_person[:href]).to eq TagManager::COLLECTIONS[:public]
+        expect(mentioned_person[:href]).to eq OStatus::TagManager::COLLECTIONS[:public]
       end
 
       it 'does not append link element for the public collection if status is not publicly visible' do
@@ -359,8 +371,8 @@ RSpec.describe OStatus::AtomSerializer do
         entry.nodes.each do |node|
           if node.name == 'link' &&
              node[:rel] == 'mentioned' &&
-             node['ostatus:object-type'] == TagManager::TYPES[:collection]
-            expect(mentioned_collection[:href]).not_to eq TagManager::COLLECTIONS[:public]
+             node['ostatus:object-type'] == OStatus::TagManager::TYPES[:collection]
+            expect(mentioned_collection[:href]).not_to eq OStatus::TagManager::COLLECTIONS[:public]
           end
         end
       end
@@ -494,7 +506,7 @@ RSpec.describe OStatus::AtomSerializer do
       status = Fabricate(:status)
       entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
       object_type = entry.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq TagManager::TYPES[:note]
+      expect(object_type.text).to eq OStatus::TagManager::TYPES[:note]
     end
 
     it 'appends activity:verb element with object type' do
@@ -503,7 +515,7 @@ RSpec.describe OStatus::AtomSerializer do
       entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
 
       object_type = entry.nodes.find { |node| node.name == 'activity:verb' }
-      expect(object_type.text).to eq TagManager::VERBS[:post]
+      expect(object_type.text).to eq OStatus::TagManager::VERBS[:post]
     end
 
     it 'appends activity:object element with target if present' do
@@ -727,8 +739,8 @@ RSpec.describe OStatus::AtomSerializer do
       time_after = Time.now
 
       expect(block_salmon.id.text).to(
-        eq(TagManager.instance.unique_tag(time_before.utc, block.id, 'Block'))
-          .or(eq(TagManager.instance.unique_tag(time_after.utc, block.id, 'Block')))
+        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, block.id, 'Block'))
+          .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, block.id, 'Block')))
       )
     end
 
@@ -757,7 +769,7 @@ RSpec.describe OStatus::AtomSerializer do
       block_salmon = OStatus::AtomSerializer.new.block_salmon(block)
 
       object_type = block_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq TagManager::TYPES[:activity]
+      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
     end
 
     it 'appends activity:verb element with block' do
@@ -766,7 +778,7 @@ RSpec.describe OStatus::AtomSerializer do
       block_salmon = OStatus::AtomSerializer.new.block_salmon(block)
 
       verb = block_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq TagManager::VERBS[:block]
+      expect(verb.text).to eq OStatus::TagManager::VERBS[:block]
     end
 
     it 'appends activity:object element with target account' do
@@ -814,8 +826,8 @@ RSpec.describe OStatus::AtomSerializer do
       time_after = Time.now
 
       expect(unblock_salmon.id.text).to(
-        eq(TagManager.instance.unique_tag(time_before.utc, block.id, 'Block'))
-          .or(eq(TagManager.instance.unique_tag(time_after.utc, block.id, 'Block')))
+        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, block.id, 'Block'))
+          .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, block.id, 'Block')))
       )
     end
 
@@ -844,7 +856,7 @@ RSpec.describe OStatus::AtomSerializer do
       unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block)
 
       object_type = unblock_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq TagManager::TYPES[:activity]
+      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
     end
 
     it 'appends activity:verb element with block' do
@@ -853,7 +865,7 @@ RSpec.describe OStatus::AtomSerializer do
       unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block)
 
       verb = unblock_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq TagManager::VERBS[:unblock]
+      expect(verb.text).to eq OStatus::TagManager::VERBS[:unblock]
     end
 
     it 'appends activity:object element with target account' do
@@ -922,7 +934,7 @@ RSpec.describe OStatus::AtomSerializer do
       favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
 
       verb = favourite_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq TagManager::VERBS[:favorite]
+      expect(verb.text).to eq OStatus::TagManager::VERBS[:favorite]
     end
 
     it 'appends activity:object element with status' do
@@ -993,8 +1005,8 @@ RSpec.describe OStatus::AtomSerializer do
       time_after = Time.now
 
       expect(unfavourite_salmon.id.text).to(
-        eq(TagManager.instance.unique_tag(time_before.utc, favourite.id, 'Favourite'))
-          .or(eq(TagManager.instance.unique_tag(time_after.utc, favourite.id, 'Favourite')))
+        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, favourite.id, 'Favourite'))
+          .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, favourite.id, 'Favourite')))
       )
     end
 
@@ -1022,7 +1034,7 @@ RSpec.describe OStatus::AtomSerializer do
       unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
 
       verb = unfavourite_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq TagManager::VERBS[:unfavorite]
+      expect(verb.text).to eq OStatus::TagManager::VERBS[:unfavorite]
     end
 
     it 'appends activity:object element with status' do
@@ -1105,7 +1117,7 @@ RSpec.describe OStatus::AtomSerializer do
       follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow)
 
       object_type = follow_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq TagManager::TYPES[:activity]
+      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
     end
 
     it 'appends activity:verb element with follow' do
@@ -1114,7 +1126,7 @@ RSpec.describe OStatus::AtomSerializer do
       follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow)
 
       verb = follow_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq TagManager::VERBS[:follow]
+      expect(verb.text).to eq OStatus::TagManager::VERBS[:follow]
     end
 
     it 'appends activity:object element with target account' do
@@ -1178,8 +1190,8 @@ RSpec.describe OStatus::AtomSerializer do
       time_after = Time.now
 
       expect(unfollow_salmon.id.text).to(
-        eq(TagManager.instance.unique_tag(time_before.utc, follow.id, 'Follow'))
-          .or(eq(TagManager.instance.unique_tag(time_after.utc, follow.id, 'Follow')))
+        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow.id, 'Follow'))
+          .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, follow.id, 'Follow')))
       )
     end
 
@@ -1222,7 +1234,7 @@ RSpec.describe OStatus::AtomSerializer do
       unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
 
       object_type = unfollow_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq TagManager::TYPES[:activity]
+      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
     end
 
     it 'appends activity:verb element with follow' do
@@ -1232,7 +1244,7 @@ RSpec.describe OStatus::AtomSerializer do
       unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
 
       verb = unfollow_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq TagManager::VERBS[:unfollow]
+      expect(verb.text).to eq OStatus::TagManager::VERBS[:unfollow]
     end
 
     it 'appends activity:object element with target account' do
@@ -1326,8 +1338,8 @@ RSpec.describe OStatus::AtomSerializer do
       time_after = Time.now
 
       expect(authorize_follow_request_salmon.id.text).to(
-        eq(TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest'))
-          .or(eq(TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest')))
+        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest'))
+          .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest')))
       )
     end
 
@@ -1347,7 +1359,7 @@ RSpec.describe OStatus::AtomSerializer do
       authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request)
 
       object_type = authorize_follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq TagManager::TYPES[:activity]
+      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
     end
 
     it 'appends activity:verb element with authorize' do
@@ -1356,7 +1368,7 @@ RSpec.describe OStatus::AtomSerializer do
       authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request)
 
       verb = authorize_follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq TagManager::VERBS[:authorize]
+      expect(verb.text).to eq OStatus::TagManager::VERBS[:authorize]
     end
 
     it 'returns element whose rendered view creates follow from follow request when processed' do
@@ -1395,8 +1407,8 @@ RSpec.describe OStatus::AtomSerializer do
       time_after = Time.now
 
       expect(reject_follow_request_salmon.id.text).to(
-        eq(TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest'))
-          .or(TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest'))
+        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest'))
+          .or(OStatus::TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest'))
       )
     end
 
@@ -1412,14 +1424,14 @@ RSpec.describe OStatus::AtomSerializer do
       follow_request = Fabricate(:follow_request)
       reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request)
       object_type = reject_follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq TagManager::TYPES[:activity]
+      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
     end
 
     it 'appends activity:verb element with authorize' do
       follow_request = Fabricate(:follow_request)
       reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request)
       verb = reject_follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq TagManager::VERBS[:reject]
+      expect(verb.text).to eq OStatus::TagManager::VERBS[:reject]
     end
 
     it 'returns element whose rendered view deletes follow request when processed' do
@@ -1491,7 +1503,7 @@ RSpec.describe OStatus::AtomSerializer do
       entry = OStatus::AtomSerializer.new.object(status)
 
       object_type = entry.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq TagManager::TYPES[:note]
+      expect(object_type.text).to eq OStatus::TagManager::TYPES[:note]
     end
 
     it 'appends activity:verb element with verb' do
@@ -1500,7 +1512,7 @@ RSpec.describe OStatus::AtomSerializer do
       entry = OStatus::AtomSerializer.new.object(status)
 
       object_type = entry.nodes.find { |node| node.name == 'activity:verb' }
-      expect(object_type.text).to eq TagManager::VERBS[:post]
+      expect(object_type.text).to eq OStatus::TagManager::VERBS[:post]
     end
 
     it 'appends link element for an alternative' do
diff --git a/spec/lib/ostatus/tag_manager_spec.rb b/spec/lib/ostatus/tag_manager_spec.rb
new file mode 100644
index 000000000..31195bae2
--- /dev/null
+++ b/spec/lib/ostatus/tag_manager_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe OStatus::TagManager do
+  describe '#unique_tag' do
+    it 'returns a unique tag' do
+      expect(OStatus::TagManager.instance.unique_tag(Time.utc(2000), 12, 'Status')).to eq 'tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status'
+    end
+  end
+
+  describe '#unique_tag_to_local_id' do
+    it 'returns the ID part' do
+      expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status', 'Status')).to eql '12'
+    end
+
+    it 'returns nil if it is not local id' do
+      expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:remote,2000-01-01:objectId=12:objectType=Status', 'Status')).to eq nil
+    end
+
+    it 'returns nil if it is not expected type' do
+      expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Block', 'Status')).to eq nil
+    end
+
+    it 'returns nil if it does not have object ID' do
+      expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectType=Status', 'Status')).to eq nil
+    end
+  end
+
+  describe '#local_id?' do
+    it 'returns true for a local ID' do
+      expect(OStatus::TagManager.instance.local_id?('tag:cb6e6126.ngrok.io;objectId=12:objectType=Status')).to be true
+    end
+
+    it 'returns false for a foreign ID' do
+      expect(OStatus::TagManager.instance.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false
+    end
+  end
+
+  describe '#uri_for' do
+    subject { OStatus::TagManager.instance.uri_for(target) }
+
+    context 'comment object' do
+      let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: true) }
+
+      it 'returns the unique tag for status' do
+        expect(target.object_type).to eq :comment
+        is_expected.to eq target.uri
+      end
+    end
+
+    context 'note object' do
+      let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: false, thread: nil) }
+
+      it 'returns the unique tag for status' do
+        expect(target.object_type).to eq :note
+        is_expected.to eq target.uri
+      end
+    end
+
+    context 'person object' do
+      let(:target) { Fabricate(:account, username: 'alice') }
+
+      it 'returns the URL for account' do
+        expect(target.object_type).to eq :person
+        is_expected.to eq 'https://cb6e6126.ngrok.io/users/alice'
+      end
+    end
+  end
+end
diff --git a/spec/lib/tag_manager_spec.rb b/spec/lib/tag_manager_spec.rb
index 1cd6e0a6f..5427a2929 100644
--- a/spec/lib/tag_manager_spec.rb
+++ b/spec/lib/tag_manager_spec.rb
@@ -63,23 +63,23 @@ RSpec.describe TagManager do
 
   describe '#local_url?' do
     around do |example|
-      original_local_domain = Rails.configuration.x.local_domain
+      original_web_domain = Rails.configuration.x.web_domain
       example.run
-      Rails.configuration.x.local_domain = original_local_domain
+      Rails.configuration.x.web_domain = original_web_domain
     end
 
     it 'returns true if the normalized string with port is local URL' do
-      Rails.configuration.x.local_domain = 'domain:42'
+      Rails.configuration.x.web_domain = 'domain:42'
       expect(TagManager.instance.local_url?('https://DoMaIn:42/')).to eq true
     end
 
     it 'returns true if the normalized string without port is local URL' do
-      Rails.configuration.x.local_domain = 'domain'
+      Rails.configuration.x.web_domain = 'domain'
       expect(TagManager.instance.local_url?('https://DoMaIn/')).to eq true
     end
 
     it 'returns false for string with irrelevant characters' do
-      Rails.configuration.x.local_domain = 'domain'
+      Rails.configuration.x.web_domain = 'domain'
       expect(TagManager.instance.local_url?('https://domainn/')).to eq false
     end
   end
@@ -120,71 +120,6 @@ RSpec.describe TagManager do
     end
   end
 
-  describe '#unique_tag' do
-    it 'returns a unique tag' do
-      expect(TagManager.instance.unique_tag(Time.utc(2000), 12, 'Status')).to eq 'tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status'
-    end
-  end
-
-  describe '#unique_tag_to_local_id' do
-    it 'returns the ID part' do
-      expect(TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status', 'Status')).to eql '12'
-    end
-
-    it 'returns nil if it is not local id' do
-      expect(TagManager.instance.unique_tag_to_local_id('tag:remote,2000-01-01:objectId=12:objectType=Status', 'Status')).to eq nil
-    end
-
-    it 'returns nil if it is not expected type' do
-      expect(TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Block', 'Status')).to eq nil
-    end
-
-    it 'returns nil if it does not have object ID' do
-      expect(TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectType=Status', 'Status')).to eq nil
-    end
-  end
-
-  describe '#local_id?' do
-    it 'returns true for a local ID' do
-      expect(TagManager.instance.local_id?('tag:cb6e6126.ngrok.io;objectId=12:objectType=Status')).to be true
-    end
-
-    it 'returns false for a foreign ID' do
-      expect(TagManager.instance.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false
-    end
-  end
-
-  describe '#uri_for' do
-    subject { TagManager.instance.uri_for(target) }
-
-    context 'comment object' do
-      let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: true) }
-
-      it 'returns the unique tag for status' do
-        expect(target.object_type).to eq :comment
-        is_expected.to eq target.uri
-      end
-    end
-
-    context 'note object' do
-      let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: false, thread: nil) }
-
-      it 'returns the unique tag for status' do
-        expect(target.object_type).to eq :note
-        is_expected.to eq target.uri
-      end
-    end
-
-    context 'person object' do
-      let(:target) { Fabricate(:account, username: 'alice') }
-
-      it 'returns the URL for account' do
-        expect(target.object_type).to eq :person
-        is_expected.to eq 'https://cb6e6126.ngrok.io/users/alice'
-      end
-    end
-  end
-
   describe '#url_for' do
     let(:alice) { Fabricate(:account, username: 'alice') }