Adding a test for ReblogService, fixing mentions for remote statuses
This commit is contained in:
		
							
								
								
									
										1
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Gemfile
									
									
									
									
									
								
							| @@ -40,6 +40,7 @@ end | ||||
|  | ||||
| group :test do | ||||
|   gem 'simplecov', require: false | ||||
|   gem 'webmock' | ||||
| end | ||||
|  | ||||
| group :development do | ||||
|   | ||||
| @@ -65,6 +65,8 @@ GEM | ||||
|       execjs | ||||
|     coffee-script-source (1.10.0) | ||||
|     concurrent-ruby (1.0.0) | ||||
|     crack (0.4.3) | ||||
|       safe_yaml (~> 1.0.0) | ||||
|     debug_inspector (0.0.2) | ||||
|     descendants_tracker (0.0.4) | ||||
|       thread_safe (~> 0.3, >= 0.3.1) | ||||
| @@ -116,6 +118,7 @@ GEM | ||||
|       haml (>= 4.0.6, < 5.0) | ||||
|       html2haml (>= 1.0.1) | ||||
|       railties (>= 4.0.1) | ||||
|     hashdiff (0.3.0) | ||||
|     hashie (3.4.3) | ||||
|     hashie-forbidden_attributes (0.1.1) | ||||
|       hashie (>= 3.0) | ||||
| @@ -255,6 +258,7 @@ GEM | ||||
|     ruby-progressbar (1.7.5) | ||||
|     ruby_parser (3.8.1) | ||||
|       sexp_processor (~> 4.1) | ||||
|     safe_yaml (1.0.4) | ||||
|     sass (3.4.21) | ||||
|     sass-rails (5.0.4) | ||||
|       railties (>= 4.0.0, < 5.0) | ||||
| @@ -306,6 +310,10 @@ GEM | ||||
|       binding_of_caller (>= 0.7.2) | ||||
|       railties (>= 4.0) | ||||
|       sprockets-rails (>= 2.0, < 4.0) | ||||
|     webmock (1.24.1) | ||||
|       addressable (>= 2.3.6) | ||||
|       crack (>= 0.3.2) | ||||
|       hashdiff | ||||
|  | ||||
| PLATFORMS | ||||
|   ruby | ||||
| @@ -349,6 +357,7 @@ DEPENDENCIES | ||||
|   therubyracer | ||||
|   uglifier (>= 1.3.0) | ||||
|   web-console (~> 2.0) | ||||
|   webmock | ||||
|  | ||||
| BUNDLED WITH | ||||
|    1.11.2 | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| module AtomHelper | ||||
|   def stream_updated_at | ||||
|     @account.stream_entries.last ? @account.stream_entries.last.created_at : @account.updated_at | ||||
|     @account.stream_entries.last ? (@account.updated_at > @account.stream_entries.last.created_at ? @account.updated_at : @account.stream_entries.last.created_at) : @account.updated_at | ||||
|   end | ||||
|  | ||||
|   def entry(xml, is_root, &block) | ||||
| @@ -126,7 +126,9 @@ module AtomHelper | ||||
|   end | ||||
|  | ||||
|   def link_avatar(xml, account) | ||||
|     xml.link(rel: 'avatar', type: account.avatar_content_type, href: asset_url(account.avatar.url(:large))) | ||||
|     xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '300', 'media:height' =>'300', 'href' => asset_url(account.avatar.url(:large))) | ||||
|     xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '96', 'media:height' =>'96', 'href' => asset_url(account.avatar.url(:medium))) | ||||
|     xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '48', 'media:height' =>'48', 'href' => asset_url(account.avatar.url(:small))) | ||||
|   end | ||||
|  | ||||
|   def logo(xml, url) | ||||
|   | ||||
| @@ -11,6 +11,7 @@ class Account < ActiveRecord::Base | ||||
|   has_many :stream_entries, inverse_of: :account | ||||
|   has_many :statuses, inverse_of: :account | ||||
|   has_many :favourites, inverse_of: :account | ||||
|   has_many :mentions, inverse_of: :account | ||||
|  | ||||
|   # Follow relations | ||||
|   has_many :active_relationships,  class_name: 'Follow', foreign_key: 'account_id',        dependent: :destroy | ||||
| @@ -77,7 +78,7 @@ class Account < ActiveRecord::Base | ||||
|  | ||||
|   before_create do | ||||
|     if local? | ||||
|       keypair = OpenSSL::PKey::RSA.new(Rails.env.test? ? 48 : 2048) | ||||
|       keypair = OpenSSL::PKey::RSA.new(Rails.env.test? ? 1024 : 2048) | ||||
|       self.private_key = keypair.to_pem | ||||
|       self.public_key  = keypair.public_key.to_pem | ||||
|     end | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| class Mention < ActiveRecord::Base | ||||
|   belongs_to :account, inverse_of: :mentions | ||||
|   belongs_to :status, inverse_of: :mentions | ||||
|   belongs_to :status | ||||
|  | ||||
|   validates :account, :status, presence: true | ||||
|   validates :account, uniqueness: { scope: :status } | ||||
|   | ||||
| @@ -5,10 +5,12 @@ class ProcessFeedService < BaseService | ||||
|   def call(body, account) | ||||
|     xml = Nokogiri::XML(body) | ||||
|  | ||||
|     # If we got a full feed, make sure the account's profile is up to date | ||||
|     unless xml.at_xpath('/xmlns:feed').nil? | ||||
|       update_remote_profile_service.(xml.at_xpath('/xmlns:feed/xmlns:author'), account) | ||||
|     end | ||||
|  | ||||
|     # Process entries | ||||
|     xml.xpath('//xmlns:entry').each do |entry| | ||||
|       next unless [:note, :comment, :activity].include? object_type(entry) | ||||
|  | ||||
| @@ -16,7 +18,7 @@ class ProcessFeedService < BaseService | ||||
|  | ||||
|       next unless status.nil? | ||||
|  | ||||
|       status = Status.new(uri: activity_id(entry), account: account, text: content(entry), created_at: published(entry), updated_at: updated(entry)) | ||||
|       status = Status.new(uri: activity_id(entry), url: activity_link(entry), account: account, text: content(entry), created_at: published(entry), updated_at: updated(entry)) | ||||
|  | ||||
|       if object_type(entry) == :comment | ||||
|         add_reply!(entry, status) | ||||
| @@ -26,7 +28,25 @@ class ProcessFeedService < BaseService | ||||
|         add_post!(entry, status) | ||||
|       end | ||||
|  | ||||
|       process_mentions_service.(status) unless status.new_record? | ||||
|       # If we added a status, go through accounts it mentions and create respective relations | ||||
|       unless status.new_record? | ||||
|         entry.xpath('./xmlns:link[@rel="mentioned"]').each do |mention_link| | ||||
|           # Here we have to do a reverse lookup of local accounts by their URL! | ||||
|           # It's not pretty at all! I really wish all these protocols sticked to | ||||
|           # using acct:username@domain only! It would make things so much easier | ||||
|           # and tidier | ||||
|  | ||||
|           href = Addressable::URI.parse(mention_link.attribute('href').value) | ||||
|  | ||||
|           if href.host == LOCAL_DOMAIN | ||||
|             mentioned_account = Account.find_by(username: href.path.gsub('/users/', ''), domain: nil) | ||||
|  | ||||
|             unless mentioned_account.nil? | ||||
|               mentioned_account.mentions.first_or_create(status: status) | ||||
|             end | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  | ||||
| @@ -103,12 +123,18 @@ class ProcessFeedService < BaseService | ||||
|     xml.at_xpath('./xmlns:id').content | ||||
|   end | ||||
|  | ||||
|   def activity_link(xml) | ||||
|     xml.at_xpath('./xmlns:link[@rel="alternate"]').attribute('href').value | ||||
|   rescue | ||||
|     '' | ||||
|   end | ||||
|  | ||||
|   def target_content(xml) | ||||
|     xml.at_xpath('.//activity:object/xmlns:content').content | ||||
|   end | ||||
|  | ||||
|   def target_url(xml) | ||||
|     xml.at_xpath('.//activity:object/xmlns:link[@rel=alternate]').attribute('href').value | ||||
|     xml.at_xpath('.//activity:object/xmlns:link[@rel="alternate"]').attribute('href').value | ||||
|   end | ||||
|  | ||||
|   def object_type(xml) | ||||
| @@ -127,10 +153,6 @@ class ProcessFeedService < BaseService | ||||
|     @follow_remote_account_service ||= FollowRemoteAccountService.new | ||||
|   end | ||||
|  | ||||
|   def process_mentions_service | ||||
|     @process_mentions_service ||= ProcessMentionsService.new | ||||
|   end | ||||
|  | ||||
|   def update_remote_profile_service | ||||
|     @update_remote_profile_service ||= UpdateRemoteProfileService.new | ||||
|   end | ||||
|   | ||||
| @@ -4,15 +4,17 @@ class ProcessMentionsService < BaseService | ||||
|   # remote users | ||||
|   # @param [Status] status | ||||
|   def call(status) | ||||
|     status.text.scan(Account::MENTION_RE).each do |match| | ||||
|       username, domain = match.first.split('@') | ||||
|       local_account = Account.find_by(username: username, domain: domain) | ||||
|     return unless status.local? | ||||
|  | ||||
|       if local_account.nil? | ||||
|         local_account = follow_remote_account_service.("acct:#{match.first}") | ||||
|     status.text.scan(Account::MENTION_RE).each do |match| | ||||
|       username, domain  = match.first.split('@') | ||||
|       mentioned_account = Account.find_by(username: username, domain: domain) | ||||
|  | ||||
|       if mentioned_account.nil? | ||||
|         mentioned_account = follow_remote_account_service.("acct:#{match.first}") | ||||
|       end | ||||
|  | ||||
|       local_account.mentions.first_or_create(status: status) | ||||
|       mentioned_account.mentions.first_or_create(status: status) | ||||
|     end | ||||
|  | ||||
|     status.mentions.each do |mentioned_account| | ||||
|   | ||||
| @@ -1,10 +1,2 @@ | ||||
| .counter.counter-reblogs | ||||
|   %i.fa.fa-retweet | ||||
|   %span.num= status.reblogs.count | ||||
|  | ||||
| .counter.counter-favourites | ||||
|   %i.fa.fa-star | ||||
|   %span.num= status.favourites.count | ||||
|  | ||||
| - if status.reply? | ||||
|   = link_to "In response to #{status.thread.account.acct}", status_url(status.thread), class: 'conversation-link' | ||||
|   | ||||
| @@ -5,8 +5,10 @@ abort("The Rails environment is running in production mode!") if Rails.env.produ | ||||
|  | ||||
| require 'spec_helper' | ||||
| require 'rspec/rails' | ||||
| require 'webmock/rspec' | ||||
|  | ||||
| ActiveRecord::Migration.maintain_test_schema! | ||||
| WebMock.disable_net_connect! | ||||
|  | ||||
| RSpec.configure do |config| | ||||
|   config.fixture_path = "#{::Rails.root}/spec/fixtures" | ||||
|   | ||||
| @@ -1,5 +1,30 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe ReblogService do | ||||
|   pending | ||||
|   let(:alice)  { Fabricate(:account, username: 'alice') } | ||||
|   let(:bob)    { Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com') } | ||||
|   let(:status) { Fabricate(:status, account: bob, uri: 'tag:example.com;something:something') } | ||||
|  | ||||
|   subject { ReblogService.new } | ||||
|  | ||||
|   before do | ||||
|     stub_const('HUB_URL', 'http://hub.example.com') | ||||
|  | ||||
|     stub_request(:post, 'http://hub.example.com') | ||||
|     stub_request(:post, 'http://salmon.example.com') | ||||
|  | ||||
|     subject.(alice, status) | ||||
|   end | ||||
|  | ||||
|   it 'creates a reblog' do | ||||
|     expect(status.reblogs.count).to eq 1 | ||||
|   end | ||||
|  | ||||
|   it 'pings PubSubHubbub hubs' do | ||||
|     expect(a_request(:post, 'http://hub.example.com')).to have_been_made | ||||
|   end | ||||
|  | ||||
|   it 'sends a Salmon slap for a remote reblog' do | ||||
|     expect(a_request(:post, 'http://salmon.example.com')).to have_been_made | ||||
|   end | ||||
| end | ||||
|   | ||||
		Reference in New Issue
	
	Block a user