From 3cc47beb6e1f646baca64fdf56168e2f2e2bc726 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 9 Sep 2016 20:04:34 +0200 Subject: [PATCH] Refactored generation of unique tags, URIs and object URLs into own classes, as well as formatting of content --- app/helpers/application_helper.rb | 50 -------- app/helpers/atom_builder_helper.rb | 40 ++----- app/lib/feed_manager.rb | 8 +- app/lib/formatter.rb | 47 ++++++++ app/lib/tag_manager.rb | 41 +++++++ app/models/feed.rb | 2 +- app/services/fan_out_on_write_service.rb | 10 +- app/services/precompute_feed_service.rb | 4 +- app/services/process_feed_service.rb | 8 +- app/services/process_interaction_service.rb | 4 +- app/services/remove_status_service.rb | 2 +- app/views/accounts/_grid_card.html.haml | 2 +- app/views/accounts/show.atom.ruby | 2 +- app/views/api/accounts/show.rabl | 2 +- app/views/api/statuses/show.rabl | 6 +- app/views/notification_mailer/follow.text.erb | 2 +- .../notification_mailer/mention.text.erb | 2 +- app/views/stream_entries/_follow.html.haml | 2 +- app/views/stream_entries/_status.html.haml | 8 +- app/views/xrd/webfinger.xml.ruby | 4 +- .../oauth/applications_controller_spec.rb | 9 +- spec/helpers/application_helper_spec.rb | 54 --------- spec/helpers/atom_builder_helper_spec.rb | 12 +- spec/lib/feed_manager_spec.rb | 23 ++++ spec/lib/formatter_spec.rb | 39 +++++++ spec/lib/tag_manager_spec.rb | 107 ++++++++++++++++++ spec/models/account_spec.rb | 4 + spec/models/media_attachment_spec.rb | 2 +- 28 files changed, 316 insertions(+), 180 deletions(-) create mode 100644 app/lib/formatter.rb create mode 100644 app/lib/tag_manager.rb create mode 100644 spec/lib/feed_manager_spec.rb create mode 100644 spec/lib/formatter_spec.rb create mode 100644 spec/lib/tag_manager_spec.rb diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e43875544..5ed8499aa 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,54 +1,4 @@ module ApplicationHelper - def unique_tag(date, id, type) - "tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}" - end - - def unique_tag_to_local_id(tag, expected_type) - matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag) - return matches[1] unless matches.nil? - end - - def local_id?(id) - id.start_with?("tag:#{Rails.configuration.x.local_domain}") - end - - def content_for_status(actual_status) - if actual_status.local? - linkify(actual_status) - else - sanitize(actual_status.content, tags: %w(a br p), attributes: %w(href rel)) - end - end - - def account_from_mentions(search_string, mentions) - mentions.each { |x| return x.account if x.account.acct.eql?(search_string) } - nil - - # If that was unsuccessful, try fetching user from db separately - # But this shouldn't ever happen if the mentions were created correctly! - # username, domain = search_string.split('@') - - # if domain == Rails.configuration.x.local_domain - # account = Account.find_local(username) - # else - # account = Account.find_remote(username, domain) - # end - - # account - end - - def linkify(status) - auto_link(HTMLEntities.new.encode(status.text), link: :urls, html: { rel: 'nofollow noopener' }).gsub(Account::MENTION_RE) do |m| - account = account_from_mentions(Account::MENTION_RE.match(m)[1], status.mentions) - - unless account.nil? - "#{m.split('@').first}@#{account.acct}" - else - m - end - end.html_safe - end - def active_nav_class(path) current_page?(path) ? 'active' : '' end diff --git a/app/helpers/atom_builder_helper.rb b/app/helpers/atom_builder_helper.rb index c8046182f..39ea20e31 100644 --- a/app/helpers/atom_builder_helper.rb +++ b/app/helpers/atom_builder_helper.rb @@ -16,7 +16,7 @@ module AtomBuilderHelper end def unique_id(xml, date, id, type) - xml.id_ unique_tag(date, id, type) + xml.id_ TagManager.instance.unique_tag(date, id, type) end def simple_id(xml, id) @@ -97,32 +97,8 @@ module AtomBuilderHelper xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' }) end - def uri_for_target(target) - if target.local? - if target.object_type == :person - account_url(target) - else - unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type) - end - else - target.uri - end - end - - def url_for_target(target) - if target.local? - if target.object_type == :person - account_url(target) - else - account_stream_entry_url(target.account, target.stream_entry) - end - else - target.url - end - end - def link_mention(xml, account) - xml.link(rel: 'mentioned', href: uri_for_target(account)) + xml.link(rel: 'mentioned', href: TagManager.instance.uri_for(account)) end def link_enclosure(xml, media) @@ -145,7 +121,7 @@ module AtomBuilderHelper def conditionally_formatted(activity) if activity.is_a?(Status) - content_for_status(activity.reblog? ? activity.reblog : activity) + Formatter.instance.format(activity.reblog? ? activity.reblog : activity) elsif activity.nil? nil else @@ -155,11 +131,11 @@ module AtomBuilderHelper def include_author(xml, account) object_type xml, :person - uri xml, uri_for_target(account) + uri xml, TagManager.instance.uri_for(account) name xml, account.username email xml, account.local? ? "#{account.acct}@#{Rails.configuration.x.local_domain}" : account.acct summary xml, account.note - link_alternate xml, url_for_target(account) + link_alternate xml, TagManager.instance.url_for(account) link_avatar xml, account portable_contact xml, account end @@ -176,7 +152,7 @@ module AtomBuilderHelper # Comments need thread element if stream_entry.threaded? - in_reply_to xml, uri_for_target(stream_entry.thread), url_for_target(stream_entry.thread) + in_reply_to xml, TagManager.instance.uri_for(stream_entry.thread), TagManager.instance.url_for(stream_entry.thread) end if stream_entry.targeted? @@ -185,9 +161,9 @@ module AtomBuilderHelper include_author xml, stream_entry.target else object_type xml, stream_entry.target.object_type - simple_id xml, uri_for_target(stream_entry.target) + simple_id xml, TagManager.instance.uri_for(stream_entry.target) title xml, stream_entry.target.title - link_alternate xml, url_for_target(stream_entry.target) + link_alternate xml, TagManager.instance.url_for(stream_entry.target) end # Statuses have content and author diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index a19d06a85..a0c480b94 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -1,11 +1,15 @@ +require 'singleton' + class FeedManager + include Singleton + MAX_ITEMS = 800 - def self.key(type, id) + def key(type, id) "feed:#{type}:#{id}" end - def self.filter_status?(status, follower) + def filter_status?(status, follower) replied_to_user = status.reply? ? status.thread.account : nil (status.reply? && !(follower.id = replied_to_user.id || follower.following?(replied_to_user))) end diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb new file mode 100644 index 000000000..52e229520 --- /dev/null +++ b/app/lib/formatter.rb @@ -0,0 +1,47 @@ +require 'singleton' + +class Formatter + include Singleton + + include ActionView::Helpers::TextHelper + include ActionView::Helpers::SanitizeHelper + + def format(status) + return reformat(status) unless status.local? + + html = status.text + html = encode(html) + html = link_urls(html) + html = link_mentions(html, status.mentions) + + html.html_safe + end + + def reformat(status) + sanitize(status.content, tags: %w(a br p), attributes: %w(href rel)) + end + + private + + def encode(html) + HTMLEntities.new.encode(html) + end + + def link_urls(html) + auto_link(html, link: :urls, html: { rel: 'nofollow noopener' }) + end + + def link_mentions(html, mentions) + html.gsub(Account::MENTION_RE) do |match| + acct = Account::MENTION_RE.match(match)[1] + mention = mentions.find { |mention| mention.account.acct.eql?(acct) } + + return match if mention.nil? + mention_html(match, mention.account) + end + end + + def mention_html(match, account) + "#{match.split('@').first}@#{account.acct}" + end +end diff --git a/app/lib/tag_manager.rb b/app/lib/tag_manager.rb new file mode 100644 index 000000000..4d29ca1f8 --- /dev/null +++ b/app/lib/tag_manager.rb @@ -0,0 +1,41 @@ +require 'singleton' + +class TagManager + include Singleton + include RoutingHelper + + def unique_tag(date, id, type) + "tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}" + end + + def unique_tag_to_local_id(tag, expected_type) + matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag) + return matches[1] unless matches.nil? + end + + def local_id?(id) + id.start_with?("tag:#{Rails.configuration.x.local_domain}") + end + + def uri_for(target) + return target.uri if target.respond_to?(:local?) && !target.local? + + case target.object_type + when :person + account_url(target) + else + unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type) + end + end + + def url_for(target) + return target.url if target.respond_to?(:local?) && !target.local? + + case target.object_type + when :person + account_url(target) + else + account_stream_entry_url(target.account, target.stream_entry) + end + end +end diff --git a/app/models/feed.rb b/app/models/feed.rb index e7574956e..bc3e960d4 100644 --- a/app/models/feed.rb +++ b/app/models/feed.rb @@ -21,7 +21,7 @@ class Feed private def key - FeedManager.key(@type, @account.id) + FeedManager.instance.key(@type, @account.id) end def redis diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index d51681e53..973451e33 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -15,7 +15,7 @@ class FanOutOnWriteService < BaseService def deliver_to_followers(status) status.account.followers.each do |follower| - next if !follower.local? || FeedManager.filter_status?(status, follower) + next if !follower.local? || FeedManager.instance.filter_status?(status, follower) push(:home, follower, status) end end @@ -29,16 +29,16 @@ class FanOutOnWriteService < BaseService end def push(type, receiver, status) - redis.zadd(FeedManager.key(type, receiver.id), status.id, status.id) + redis.zadd(FeedManager.instance.key(type, receiver.id), status.id, status.id) trim(type, receiver) ActionCable.server.broadcast("timeline:#{receiver.id}", type: 'update', timeline: type, message: inline_render(receiver, status)) end def trim(type, receiver) - return unless redis.zcard(FeedManager.key(type, receiver.id)) > FeedManager::MAX_ITEMS + return unless redis.zcard(FeedManager.instance.key(type, receiver.id)) > FeedManager::MAX_ITEMS - last = redis.zrevrange(FeedManager.key(type, receiver.id), FeedManager::MAX_ITEMS - 1, FeedManager::MAX_ITEMS - 1) - redis.zremrangebyscore(FeedManager.key(type, receiver.id), '-inf', "(#{last.last}") + last = redis.zrevrange(FeedManager.instance.key(type, receiver.id), FeedManager::MAX_ITEMS - 1, FeedManager::MAX_ITEMS - 1) + redis.zremrangebyscore(FeedManager.instance.key(type, receiver.id), '-inf', "(#{last.last}") end def redis diff --git a/app/services/precompute_feed_service.rb b/app/services/precompute_feed_service.rb index c8050bbd0..df2330d09 100644 --- a/app/services/precompute_feed_service.rb +++ b/app/services/precompute_feed_service.rb @@ -7,8 +7,8 @@ class PrecomputeFeedService < BaseService instant_return = [] Status.send("as_#{type}_timeline", account).order('created_at desc').limit(FeedManager::MAX_ITEMS).each do |status| - next if type == :home && FeedManager.filter_status?(status, account) - redis.zadd(FeedManager.key(type, account.id), status.id, status.id) + next if type == :home && FeedManager.instance.filter_status?(status, account) + redis.zadd(FeedManager.instance.key(type, account.id), status.id, status.id) instant_return << status unless instant_return.size > limit end diff --git a/app/services/process_feed_service.rb b/app/services/process_feed_service.rb index 97033a004..ba7558e1f 100644 --- a/app/services/process_feed_service.rb +++ b/app/services/process_feed_service.rb @@ -39,10 +39,10 @@ class ProcessFeedService < BaseService # Also record all media attachments for the status and for the reblogged status if present unless status.new_record? record_remote_mentions(status, entry.xpath('./xmlns:link[@rel="mentioned"]')) - + process_attachments(entry, status) process_attachments(entry.xpath('./activity:object'), status.reblog) if status.reblog? - + DistributionWorker.perform_async(status.id) end end @@ -112,8 +112,8 @@ class ProcessFeedService < BaseService def find_original_status(_xml, id) return nil if id.nil? - if local_id?(id) - Status.find(unique_tag_to_local_id(id, 'Status')) + if TagManager.instance.local_id?(id) + Status.find(TagManager.instance.unique_tag_to_local_id(id, 'Status')) else Status.find_by(uri: id) end diff --git a/app/services/process_interaction_service.rb b/app/services/process_interaction_service.rb index 536911e2f..d9fcf9032 100644 --- a/app/services/process_interaction_service.rb +++ b/app/services/process_interaction_service.rb @@ -45,7 +45,7 @@ class ProcessInteractionService < BaseService end def mentions_account?(xml, account) - xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == url_for_target(account) } + xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == TagManager.instance.url_for(account) } false end @@ -85,7 +85,7 @@ class ProcessInteractionService < BaseService end def status(xml) - Status.find(unique_tag_to_local_id(activity_id(xml), 'Status')) + Status.find(TagManager.instance.unique_tag_to_local_id(activity_id(xml), 'Status')) end def activity_id(xml) diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index db043df08..e48a43be0 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -44,7 +44,7 @@ class RemoveStatusService < BaseService end def unpush(type, receiver, status) - redis.zremrangebyscore(FeedManager.key(type, receiver.id), status.id, status.id) + redis.zremrangebyscore(FeedManager.instance.key(type, receiver.id), status.id, status.id) ActionCable.server.broadcast("timeline:#{receiver.id}", type: 'delete', id: status.id) end diff --git a/app/views/accounts/_grid_card.html.haml b/app/views/accounts/_grid_card.html.haml index d107f5274..f65b78470 100644 --- a/app/views/accounts/_grid_card.html.haml +++ b/app/views/accounts/_grid_card.html.haml @@ -2,7 +2,7 @@ .account-grid-card__header .avatar= image_tag account.avatar.url(:medium) .name - = link_to url_for_target(account) do + = link_to TagManager.instance.url_for(account) do %span.display_name= display_name(account) %span.username= "@#{account.acct}" %p.note= truncate(strip_tags(account.note), length: 150) diff --git a/app/views/accounts/show.atom.ruby b/app/views/accounts/show.atom.ruby index 8a7bc1b92..d7b2201d4 100644 --- a/app/views/accounts/show.atom.ruby +++ b/app/views/accounts/show.atom.ruby @@ -10,7 +10,7 @@ Nokogiri::XML::Builder.new do |xml| include_author xml, @account end - link_alternate xml, url_for_target(@account) + link_alternate xml, TagManager.instance.url_for(@account) link_self xml, account_url(@account, format: 'atom') link_hub xml, Rails.configuration.x.hub_url link_salmon xml, api_salmon_url(@account.id) diff --git a/app/views/api/accounts/show.rabl b/app/views/api/accounts/show.rabl index 4f7cee680..d779393d1 100644 --- a/app/views/api/accounts/show.rabl +++ b/app/views/api/accounts/show.rabl @@ -2,7 +2,7 @@ object @account attributes :id, :username, :acct, :display_name, :note -node(:url) { |account| url_for_target(account) } +node(:url) { |account| TagManager.instance.url_for(account) } node(:avatar) { |account| full_asset_url(account.avatar.url(:large, false)) } node(:followers_count) { |account| account.followers.count } node(:following_count) { |account| account.following.count } diff --git a/app/views/api/statuses/show.rabl b/app/views/api/statuses/show.rabl index c7d028e14..047436b61 100644 --- a/app/views/api/statuses/show.rabl +++ b/app/views/api/statuses/show.rabl @@ -1,9 +1,9 @@ object @status attributes :id, :created_at, :in_reply_to_id -node(:uri) { |status| uri_for_target(status) } -node(:content) { |status| content_for_status(status) } -node(:url) { |status| url_for_target(status) } +node(:uri) { |status| TagManager.instance.uri_for(status) } +node(:content) { |status| Formatter.instance.format(status) } +node(:url) { |status| TagManager.instance.url_for(status) } node(:reblogs_count) { |status| status.reblogs_count } node(:favourites_count) { |status| status.favourites_count } node(:favourited) { |status| current_account.favourited?(status) } diff --git a/app/views/notification_mailer/follow.text.erb b/app/views/notification_mailer/follow.text.erb index 6f70c0d90..a13b6d7a9 100644 --- a/app/views/notification_mailer/follow.text.erb +++ b/app/views/notification_mailer/follow.text.erb @@ -2,4 +2,4 @@ <%= @account.acct %> is now following you! -<%= url_for_target(@account) %> +<%= TagManager.instance.url_for(@account) %> diff --git a/app/views/notification_mailer/mention.text.erb b/app/views/notification_mailer/mention.text.erb index b093f4ec0..f3582749d 100644 --- a/app/views/notification_mailer/mention.text.erb +++ b/app/views/notification_mailer/mention.text.erb @@ -4,4 +4,4 @@ You were mentioned by <%= @status.account.acct %> in: <%= strip_tags(@status.content) %> -<%= url_for_target(@status) %> +<%= TagManager.instance.url_for(@status) %> diff --git a/app/views/stream_entries/_follow.html.haml b/app/views/stream_entries/_follow.html.haml index f19e8783d..f6ec8c4f5 100644 --- a/app/views/stream_entries/_follow.html.haml +++ b/app/views/stream_entries/_follow.html.haml @@ -2,4 +2,4 @@ .content %strong= link_to follow.account.acct, account_path(follow.account) is now following - %strong= link_to follow.target_account.acct, url_for_target(follow.target_account) + %strong= link_to follow.target_account.acct, TagManager.instance.url_for(follow.target_account) diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml index ece31d0e2..a0afccd39 100644 --- a/app/views/stream_entries/_status.html.haml +++ b/app/views/stream_entries/_status.html.haml @@ -12,7 +12,7 @@ .pre-header %i.fa.fa-retweet Shared by - = link_to display_name(status.account), url_for_target(status.account), class: 'name' + = link_to display_name(status.account), TagManager.instance.url_for(status.account), class: 'name' .entry__container .avatar @@ -21,10 +21,10 @@ .entry__container__container .header .header__left - = link_to url_for_target(proper_status(status).account), class: 'name' do + = link_to TagManager.instance.url_for(proper_status(status).account), class: 'name' do %strong= display_name(proper_status(status).account) = "@#{proper_status(status).account.acct}" - = link_to url_for_target(proper_status(status)), class: 'time' do + = link_to TagManager.instance.url_for(proper_status(status)), class: 'time' do %span{ title: proper_status(status).created_at } = relative_time(proper_status(status).created_at) @@ -36,7 +36,7 @@ %i.fa.fa-star %span.counter-number= proper_status(status).favourites_count - .content= content_for_status(proper_status(status)) + .content= Formatter.instance.format(proper_status(status)) %ul.media-attachments - status.media_attachments.each do |media| diff --git a/app/views/xrd/webfinger.xml.ruby b/app/views/xrd/webfinger.xml.ruby index 7c920b678..d633e3477 100644 --- a/app/views/xrd/webfinger.xml.ruby +++ b/app/views/xrd/webfinger.xml.ruby @@ -1,8 +1,8 @@ Nokogiri::XML::Builder.new do |xml| xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do xml.Subject @canonical_account_uri - xml.Alias url_for_target(@account) - xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: url_for_target(@account)) + xml.Alias TagManager.instance.url_for(@account) + xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: TagManager.instance.url_for(@account)) xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: account_url(@account, format: 'atom')) xml.Link(rel: 'salmon', href: api_salmon_url(@account.id)) xml.Link(rel: 'magic-public-key', href: @magic_key) diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb index 350d5d521..ecd8b16c6 100644 --- a/spec/controllers/oauth/applications_controller_spec.rb +++ b/spec/controllers/oauth/applications_controller_spec.rb @@ -1,8 +1,15 @@ require 'rails_helper' RSpec.describe Oauth::ApplicationsController, type: :controller do + before do + sign_in Fabricate(:user), scope: :user + end + describe 'GET #index' do - it 'returns http success' + it 'returns http success' do + get :index + expect(response).to have_http_status(:success) + end end describe 'POST #create' do diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 9f68a504a..c2063c995 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -1,59 +1,5 @@ require 'rails_helper' RSpec.describe ApplicationHelper, type: :helper do - let(:local_domain) { Rails.configuration.x.local_domain } - describe '#unique_tag' do - it 'returns a string' do - expect(helper.unique_tag(Time.now, 12, 'Status')).to be_a String - end - end - - describe '#unique_tag_to_local_id' do - it 'returns the ID part' do - expect(helper.unique_tag_to_local_id("tag:#{local_domain};objectId=12:objectType=Status", 'Status')).to eql '12' - end - end - - describe '#local_id?' do - it 'returns true for a local ID' do - expect(helper.local_id?("tag:#{local_domain};objectId=12:objectType=Status")).to be true - end - - it 'returns false for a foreign ID' do - expect(helper.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false - end - end - - describe '#linkify' do - let(:alice) { Fabricate(:account, username: 'alice') } - let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', url: 'http://example.com/bob') } - - it 'turns mention of remote user into link' do - status = Fabricate(:status, text: 'Hello @bob@example.com', account: bob) - status.mentions.create(account: bob) - expect(helper.linkify(status)).to match('@bob@example.com') - end - - it 'turns mention of local user into link' do - status = Fabricate(:status, text: 'Hello @alice', account: bob) - status.mentions.create(account: alice) - expect(helper.linkify(status)).to match('@alice') - end - - it 'leaves mention of unresolvable user alone' do - status = Fabricate(:status, text: 'Hello @foo', account: bob) - expect(helper.linkify(status)).to match('Hello @foo') - end - end - - describe '#account_from_mentions' do - let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com') } - let(:status) { Fabricate(:status, text: 'Hello @bob@example.com', account: bob) } - let(:mentions) { [Mention.create(status: status, account: bob)] } - - it 'returns account' do - expect(helper.account_from_mentions('bob@example.com', mentions)).to eq bob - end - end end diff --git a/spec/helpers/atom_builder_helper_spec.rb b/spec/helpers/atom_builder_helper_spec.rb index 7366b5b79..7e11a8a77 100644 --- a/spec/helpers/atom_builder_helper_spec.rb +++ b/spec/helpers/atom_builder_helper_spec.rb @@ -20,7 +20,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do describe '#unique_id' do it 'creates an id' do time = Time.now - expect(used_in_builder { |xml| helper.unique_id(xml, time, 1, 'Status') }).to match "#{helper.unique_tag(time, 1, 'Status')}" + expect(used_in_builder { |xml| helper.unique_id(xml, time, 1, 'Status') }).to match "#{TagManager.instance.unique_tag(time, 1, 'Status')}" end end @@ -146,18 +146,10 @@ RSpec.describe AtomBuilderHelper, type: :helper do let(:account) { Fabricate(:account, username: 'alice') } it 'creates a link' do - expect(used_in_builder { |xml| helper.link_mention(xml, account) }).to match '' + expect(used_in_builder { |xml| helper.link_mention(xml, account) }).to match '' end end - describe '#disambiguate_uri' do - pending - end - - describe '#disambiguate_url' do - pending - end - describe '#include_author' do pending end diff --git a/spec/lib/feed_manager_spec.rb b/spec/lib/feed_manager_spec.rb new file mode 100644 index 000000000..4501e27b7 --- /dev/null +++ b/spec/lib/feed_manager_spec.rb @@ -0,0 +1,23 @@ +require 'rails_helper' + +RSpec.describe FeedManager do + describe '#key' do + subject { FeedManager.instance.key(:home, 1) } + + it 'returns a string' do + expect(subject).to be_a String + end + end + + describe '#filter_status?' do + let(:followee) { Fabricate(:account, username: 'alice') } + let(:status) { Fabricate(:status, text: 'Hello world', account: followee) } + let(:follower) { Fabricate(:account, username: 'bob') } + + subject { FeedManager.instance.filter_status?(status, follower) } + + it 'returns a boolean value' do + expect(subject).to be false + end + end +end diff --git a/spec/lib/formatter_spec.rb b/spec/lib/formatter_spec.rb new file mode 100644 index 000000000..36c4af748 --- /dev/null +++ b/spec/lib/formatter_spec.rb @@ -0,0 +1,39 @@ +require 'rails_helper' + +RSpec.describe Formatter do + let(:account) { Fabricate(:account, username: 'alice') } + let(:local_status) { Fabricate(:status, text: 'Hello world http://google.com', account: account) } + let(:remote_status) { Fabricate(:status, text: ' Beep boop', uri: 'beepboop', account: account) } + + describe '#format' do + subject { Formatter.instance.format(local_status) } + + it 'returns a string' do + expect(subject).to be_a String + end + + it 'contains plain text' do + expect(subject).to match('Hello world') + end + + it 'contains a link' do + expect(subject).to match('http://google.com') + end + end + + describe '#reformat' do + subject { Formatter.instance.format(remote_status) } + + it 'returns a string' do + expect(subject).to be_a String + end + + it 'contains plain text' do + expect(subject).to match('Beep boop') + end + + it 'does not contain scripts' do + expect(subject).to_not match('') + end + end +end diff --git a/spec/lib/tag_manager_spec.rb b/spec/lib/tag_manager_spec.rb new file mode 100644 index 000000000..b60584253 --- /dev/null +++ b/spec/lib/tag_manager_spec.rb @@ -0,0 +1,107 @@ +require 'rails_helper' + +RSpec.describe TagManager do + let(:local_domain) { Rails.configuration.x.local_domain } + + describe '#unique_tag' do + it 'returns a string' do + expect(TagManager.instance.unique_tag(Time.now, 12, 'Status')).to be_a String + end + end + + describe '#unique_tag_to_local_id' do + it 'returns the ID part' do + expect(TagManager.instance.unique_tag_to_local_id("tag:#{local_domain};objectId=12:objectType=Status", 'Status')).to eql '12' + end + end + + describe '#local_id?' do + it 'returns true for a local ID' do + expect(TagManager.instance.local_id?("tag:#{local_domain};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 + let(:alice) { Fabricate(:account, username: 'alice') } + let(:bob) { Fabricate(:account, username: 'bob') } + let(:status) { Fabricate(:status, text: 'Hello world', account: alice) } + + subject { TagManager.instance.uri_for(target) } + + context 'Account' do + let(:target) { alice } + + it 'returns a string' do + expect(subject).to be_a String + end + end + + context 'Status' do + let(:target) { status } + + it 'returns a string' 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 + let(:alice) { Fabricate(:account, username: 'alice') } + let(:bob) { Fabricate(:account, username: 'bob') } + let(:status) { Fabricate(:status, text: 'Hello world', account: alice) } + + subject { TagManager.instance.url_for(target) } + + context 'Account' do + let(:target) { alice } + + it 'returns a URL' do + expect(subject).to be_a String + end + end + + context 'Status' do + let(:target) { status } + + it 'returns a URL' 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/models/account_spec.rb b/spec/models/account_spec.rb index 9d19fd3b4..93731d1e4 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -112,6 +112,10 @@ RSpec.describe Account, type: :model do pending end + describe '.find_remote' do + pending + end + describe 'MENTION_RE' do subject { Account::MENTION_RE } diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb index 0a1627684..5995aa4f4 100644 --- a/spec/models/media_attachment_spec.rb +++ b/spec/models/media_attachment_spec.rb @@ -1,5 +1,5 @@ require 'rails_helper' RSpec.describe MediaAttachment, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + end