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