diff --git a/lib/diaspora/federation/dispatcher.rb b/lib/diaspora/federation/dispatcher.rb index 349280d28..9e2116154 100644 --- a/lib/diaspora/federation/dispatcher.rb +++ b/lib/diaspora/federation/dispatcher.rb @@ -38,7 +38,7 @@ module Diaspora local_people, remote_people = object.subscribers.partition(&:local?) deliver_to_local(local_people) unless local_people.empty? - deliver_to_remote(remote_people) unless remote_people.empty? + deliver_to_remote(remote_people) end def deliver_to_local(people) diff --git a/lib/diaspora/federation/dispatcher/private.rb b/lib/diaspora/federation/dispatcher/private.rb index b0f6c3725..c959dd3b6 100644 --- a/lib/diaspora/federation/dispatcher/private.rb +++ b/lib/diaspora/federation/dispatcher/private.rb @@ -2,13 +2,15 @@ module Diaspora module Federation class Dispatcher class Private < Dispatcher + private + def deliver_to_remote(people) + return if people.empty? + entity = Entities.build(object) Workers::SendPrivate.perform_async(sender.id, entity.to_s, targets(people, salmon_slap(entity))) end - private - def targets(people, salmon_slap) people.map {|person| [person.receive_url, salmon_slap.generate_xml(person.public_key)] }.to_h end diff --git a/lib/diaspora/federation/dispatcher/public.rb b/lib/diaspora/federation/dispatcher/public.rb index 399ae8488..c063ed118 100644 --- a/lib/diaspora/federation/dispatcher/public.rb +++ b/lib/diaspora/federation/dispatcher/public.rb @@ -2,6 +2,8 @@ module Diaspora module Federation class Dispatcher class Public < Dispatcher + private + def deliver_to_services deliver_to_hub if object.instance_of?(StatusMessage) super @@ -10,12 +12,12 @@ module Diaspora def deliver_to_remote(people) targets = target_urls(people) + additional_target_urls + return if targets.empty? + entity = Entities.build(object) Workers::SendPublic.perform_async(sender.id, entity.to_s, targets, salmon_xml(entity)) end - private - def target_urls(people) Pod.where(id: people.map(&:pod_id).uniq).map {|pod| pod.url_to("/receive/public") } end diff --git a/spec/lib/diaspora/federation/dispatcher/private_spec.rb b/spec/lib/diaspora/federation/dispatcher/private_spec.rb new file mode 100644 index 000000000..db9ed9682 --- /dev/null +++ b/spec/lib/diaspora/federation/dispatcher/private_spec.rb @@ -0,0 +1,43 @@ +require "spec_helper" + +describe Diaspora::Federation::Dispatcher::Private do + let(:post) { FactoryGirl.create(:status_message, author: alice.person, text: "hello", public: false) } + let(:comment) { FactoryGirl.create(:comment, author: alice.person, post: post) } + + before do + alice.share_with(remote_raphael, alice.aspects.first) + alice.add_to_streams(post, [alice.aspects.first]) + end + + describe "#dispatch" do + context "deliver to remote user" do + it "queues a private send job" do + xml = "" + + expect(Workers::SendPrivate).to receive(:perform_async) do |user_id, _entity_string, targets| + expect(user_id).to eq(alice.id) + expect(targets.size).to eq(1) + expect(targets).to have_key(remote_raphael.receive_url) + expect(targets[remote_raphael.receive_url]).to eq(xml) + end + + salmon = double + expect(DiasporaFederation::Salmon::EncryptedSlap).to receive(:prepare).and_return(salmon) + expect(salmon).to receive(:generate_xml).and_return(xml) + + Diaspora::Federation::Dispatcher.build(alice, post).dispatch + end + + it "does not queue a private send job when no remote recipients specified" do + bobs_post = FactoryGirl.create(:status_message, author: alice.person, text: "hello", public: false) + bob.add_to_streams(bobs_post, [bob.aspects.first]) + + expect(Workers::SendPrivate).not_to receive(:perform_async) + + Diaspora::Federation::Dispatcher.build(bob, bobs_post).dispatch + end + end + end + + it_behaves_like "a dispatcher" +end diff --git a/spec/lib/diaspora/federation/dispatcher/public_spec.rb b/spec/lib/diaspora/federation/dispatcher/public_spec.rb new file mode 100644 index 000000000..c8abf6d67 --- /dev/null +++ b/spec/lib/diaspora/federation/dispatcher/public_spec.rb @@ -0,0 +1,79 @@ +require "spec_helper" + +describe Diaspora::Federation::Dispatcher::Public do + let(:post) { FactoryGirl.create(:status_message, author: alice.person, text: "hello", public: true) } + let(:comment) { FactoryGirl.create(:comment, author: alice.person, post: post) } + + describe "#dispatch" do + context "pubsubhubbub" do + it "delivers public posts to pubsubhubbub" do + expect(Workers::PublishToHub).to receive(:perform_async).with(alice.atom_url) + Diaspora::Federation::Dispatcher.build(alice, post).dispatch + end + + it "does not call pubsubhubbub for comments" do + expect(Workers::PublishToHub).not_to receive(:perform_async) + Diaspora::Federation::Dispatcher.build(alice, comment).dispatch + end + end + + context "relay functionality" do + before do + AppConfig.relay.outbound.url = "https://relay.iliketoast.net/receive/public" + end + + it "delivers public post to relay when relay is enabled" do + AppConfig.relay.outbound.send = true + + expect(Workers::SendPublic).to receive(:perform_async) do |_user_id, _entity_string, urls, _xml| + expect(urls).to include("https://relay.iliketoast.net/receive/public") + end + + Diaspora::Federation::Dispatcher.build(alice, post).dispatch + end + + it "does not deliver post to relay when relay is disabled" do + AppConfig.relay.outbound.send = false + + expect(Workers::SendPublic).not_to receive(:perform_async) + + Diaspora::Federation::Dispatcher.build(alice, post).dispatch + end + + it "does not deliver comments to relay" do + AppConfig.relay.outbound.send = true + + expect(Workers::SendPublic).not_to receive(:perform_async) + + Diaspora::Federation::Dispatcher.build(alice, comment).dispatch + end + end + + context "deliver to remote user" do + it "queues a public send job" do + alice.share_with(remote_raphael, alice.aspects.first) + + salmon_xml = "" + + expect(Workers::SendPublic).to receive(:perform_async) do |user_id, _entity_string, urls, xml| + expect(user_id).to eq(alice.id) + expect(urls.size).to eq(1) + expect(urls[0]).to eq(remote_raphael.pod.url_to("/receive/public")) + expect(xml).to eq(salmon_xml) + end + + expect(DiasporaFederation::Salmon::Slap).to receive(:generate_xml).and_return(salmon_xml) + + Diaspora::Federation::Dispatcher.build(alice, post).dispatch + end + + it "does not queue a private send job when no remote recipients specified" do + expect(Workers::SendPublic).not_to receive(:perform_async) + + Diaspora::Federation::Dispatcher.build(alice, post).dispatch + end + end + end + + it_behaves_like "a dispatcher" +end diff --git a/spec/lib/diaspora/federation/dispatcher_spec.rb b/spec/lib/diaspora/federation/dispatcher_spec.rb new file mode 100644 index 000000000..fa27eea7c --- /dev/null +++ b/spec/lib/diaspora/federation/dispatcher_spec.rb @@ -0,0 +1,43 @@ +require "spec_helper" + +describe Diaspora::Federation::Dispatcher do + let(:post) { FactoryGirl.create(:status_message, author: alice.person, text: "hello", public: true) } + let(:opts) { {service_types: "Services::Twitter"} } + + describe ".build" do + it "creates a public dispatcher for a public post" do + expect(Diaspora::Federation::Dispatcher::Public).to receive(:new).with(alice, post, opts).and_call_original + + dispatcher = described_class.build(alice, post, opts) + + expect(dispatcher).to be_instance_of(Diaspora::Federation::Dispatcher::Public) + end + + it "creates a private dispatcher for a private post" do + private = FactoryGirl.create(:status_message, author: alice.person, text: "hello", public: false) + + expect(Diaspora::Federation::Dispatcher::Private).to receive(:new).with(alice, private, opts).and_call_original + + dispatcher = described_class.build(alice, private, opts) + + expect(dispatcher).to be_instance_of(Diaspora::Federation::Dispatcher::Private) + end + + it "creates a private dispatcher for object with no public flag" do + object = double + + expect(Diaspora::Federation::Dispatcher::Private).to receive(:new).with(alice, object, {}).and_call_original + + dispatcher = described_class.build(alice, object) + + expect(dispatcher).to be_instance_of(Diaspora::Federation::Dispatcher::Private) + end + end + + describe ".defer_dispatch" do + it "queues a job for dispatch" do + expect(Workers::DeferredDispatch).to receive(:perform_async).with(alice.id, "StatusMessage", post.id, opts) + described_class.defer_dispatch(alice, post, opts) + end + end +end diff --git a/spec/shared_behaviors/dispatcher.rb b/spec/shared_behaviors/dispatcher.rb new file mode 100644 index 000000000..26c693fff --- /dev/null +++ b/spec/shared_behaviors/dispatcher.rb @@ -0,0 +1,68 @@ +shared_examples "a dispatcher" do + describe "#dispatch" do + context "deliver to user services" do + let(:twitter) { Services::Twitter.new(access_token: "twitter") } + let(:facebook) { Services::Facebook.new(access_token: "facebook") } + + before do + alice.services << twitter << facebook + end + + it "delivers a StatusMessage to specified services" do + opts = {service_types: "Services::Twitter", url: "https://example.org/p/123"} + expect(Workers::PostToService).to receive(:perform_async).with(twitter.id, post.id, "https://example.org/p/123") + Diaspora::Federation::Dispatcher.build(alice, post, opts).dispatch + end + + it "delivers a Retraction of a Post to all user services" do + skip # TODO + + retraction = Retraction.for(post, alice) + Diaspora::Federation::Dispatcher.build(alice, retraction).dispatch + end + + it "does not deliver a Comment to services" do + expect(Workers::PostToService).not_to receive(:perform_async) + Diaspora::Federation::Dispatcher.build(alice, comment).dispatch + end + + it "does not deliver a Retraction of a Comment to services" do + expect(Workers::DeletePostFromService).not_to receive(:perform_async) + + retraction = Retraction.for(comment, alice) + Diaspora::Federation::Dispatcher.build(alice, retraction).dispatch + end + end + + context "deliver to local user" do + it "queues receive local job for all local receivers" do + expect(Workers::ReceiveLocal).to receive(:perform_async).with("StatusMessage", post.id, [bob.id]) + Diaspora::Federation::Dispatcher.build(alice, post).dispatch + end + + it "gets the object for the receiving user" do + expect(Workers::ReceiveLocal).to receive(:perform_async).with("RSpec::Mocks::Double", 42, [bob.id]) + + object = double + object_to_receive = double + expect(object).to receive(:subscribers).and_return([bob.person]) + expect(object).to receive(:object_to_receive).and_return(object_to_receive) + expect(object).to receive(:public?).and_return(post.public?) + expect(object_to_receive).to receive(:id).and_return(42) + + Diaspora::Federation::Dispatcher.build(alice, object).dispatch + end + + it "does not queue a job if the object to receive is nil" do + expect(Workers::ReceiveLocal).not_to receive(:perform_async) + + object = double + expect(object).to receive(:subscribers).and_return([bob.person]) + expect(object).to receive(:object_to_receive).and_return(nil) + expect(object).to receive(:public?).and_return(post.public?) + + Diaspora::Federation::Dispatcher.build(alice, object).dispatch + end + end + end +end