diff --git a/app/models/profile.rb b/app/models/profile.rb
index 02c7ce617b6e99d5065fc8dd1dfb36961c13d3b4..8735d55f4245a6092c0ff8df3ce8c526bf64e47b 100644
--- a/app/models/profile.rb
+++ b/app/models/profile.rb
@@ -129,12 +129,7 @@ class Profile < ActiveRecord::Base
   end
 
   def tag_string
-    if @tag_string
-      @tag_string
-    else
-      tags = self.tags.pluck(:name)
-      tags.inject(""){|string, tag| string << "##{tag} " }
-    end
+    @tag_string ||= tags.pluck(:name).map {|tag| "##{tag}" }.join(" ")
   end
 
   # Constructs a full name by joining #first_name and #last_name
diff --git a/lib/diaspora/federation/receive.rb b/lib/diaspora/federation/receive.rb
index 97aa916de618e8e01cbe67fb807b58772bfc8040..692026a5cdfd14b0e06ddf8e7a3608e622963334 100644
--- a/lib/diaspora/federation/receive.rb
+++ b/lib/diaspora/federation/receive.rb
@@ -4,10 +4,7 @@ module Diaspora
       extend Diaspora::Logging
 
       def self.account_deletion(entity)
-        AccountDeletion.new(
-          person:          author_of(entity),
-          diaspora_handle: entity.author
-        ).tap(&:save!)
+        AccountDeletion.create!(person: author_of(entity), diaspora_handle: entity.author)
       end
 
       def self.comment(entity)
@@ -38,16 +35,14 @@ module Diaspora
       def self.conversation(entity)
         author = author_of(entity)
         ignore_existing_guid(Conversation, entity.guid, author) do
-          Conversation.new(
+          Conversation.create!(
             author:              author,
             guid:                entity.guid,
             subject:             entity.subject,
             created_at:          entity.created_at,
-            participant_handles: entity.participants
-          ).tap do |conversation|
-            conversation.messages = entity.messages.map {|message| build_message(message) }
-            conversation.save!
-          end
+            participant_handles: entity.participants,
+            messages:            entity.messages.map {|message| build_message(message) }
+          )
         end
       end
 
@@ -70,15 +65,12 @@ module Diaspora
       end
 
       def self.participation(entity)
-        parent = entity.parent_type.constantize.find_by(guid: entity.parent_guid)
-
-        return unless parent.author.local?
+        author = author_of(entity)
+        ignore_existing_guid(Participation, entity.guid, author) do
+          parent = entity.parent_type.constantize.find_by(guid: entity.parent_guid)
 
-        Participation.new(
-          author: author_of(entity),
-          guid:   entity.guid,
-          target: entity.parent_type.constantize.find_by(guid: entity.parent_guid)
-        ).tap(&:save!)
+          Participation.create!(author: author, guid: entity.guid, target: parent) if parent.author.local?
+        end
       end
 
       def self.photo(entity)
@@ -107,14 +99,11 @@ module Diaspora
         author = author_of(entity)
         ignore_existing_guid(PollParticipation, entity.guid, author) do
           PollParticipation.new(
-            author: author,
-            guid:   entity.guid,
-            poll:   Poll.find_by(guid: entity.parent_guid)
-          ).tap do |poll_participation|
-            poll_participation.poll_answer_guid = entity.poll_answer_guid
-
-            save_relayable(poll_participation, entity)
-          end
+            author:           author,
+            guid:             entity.guid,
+            poll:             Poll.find_by(guid: entity.parent_guid),
+            poll_answer_guid: entity.poll_answer_guid
+          ).tap {|poll_participation| save_relayable(poll_participation, entity) }
         end
       end
 
@@ -138,14 +127,17 @@ module Diaspora
       end
 
       def self.reshare(entity)
-        Reshare.new(
-          author:                author_of(entity),
-          guid:                  entity.guid,
-          created_at:            entity.created_at,
-          provider_display_name: entity.provider_display_name,
-          public:                entity.public,
-          root_guid:             entity.root_guid
-        ).tap(&:save!)
+        author = author_of(entity)
+        ignore_existing_guid(Reshare, entity.guid, author) do
+          Reshare.create!(
+            author:                author,
+            guid:                  entity.guid,
+            created_at:            entity.created_at,
+            provider_display_name: entity.provider_display_name,
+            public:                entity.public,
+            root_guid:             entity.root_guid
+          )
+        end
       end
 
       def self.retraction(entity, recipient_id)
@@ -208,7 +200,7 @@ module Diaspora
       private_class_method :build_poll
 
       def self.save_photo(entity)
-        Photo.new(
+        Photo.create!(
           author:              author_of(entity),
           guid:                entity.guid,
           text:                entity.text,
@@ -219,7 +211,7 @@ module Diaspora
           status_message_guid: entity.status_message_guid,
           height:              entity.height,
           width:               entity.width
-        ).tap(&:save!)
+        )
       end
       private_class_method :save_photo
 
diff --git a/spec/integration/receiving_spec.rb b/spec/integration/receiving_spec.rb
index 9e3079eed303a09336f9d933810182d263df70c5..451a29e77163ae6124394d6a1641e8d7df3dcbd2 100644
--- a/spec/integration/receiving_spec.rb
+++ b/spec/integration/receiving_spec.rb
@@ -5,12 +5,6 @@
 require 'spec_helper'
 
 describe 'a user receives a post', :type => :request do
-
-  def receive_with_zord(user, person, xml)
-    zord = Postzord::Receiver::Private.new(user, :person => person)
-    zord.parse_and_receive(xml)
-  end
-
   before do
     @alices_aspect = alice.aspects.where(:name => "generic").first
     @bobs_aspect = bob.aspects.where(:name => "generic").first
@@ -56,152 +50,4 @@ describe 'a user receives a post', :type => :request do
       expect(ShareVisibility.find_by(user_id: alice.id, shareable_id: @status_message.id)).not_to be_nil
     end
   end
-
-  describe 'comments' do
-
-    context 'remote' do
-      before do
-        skip # TODO
-        inlined_jobs do |queue|
-          connect_users(alice, @alices_aspect, eve, @eves_aspect)
-          @post = alice.post(:status_message, :text => "hello", :to => @alices_aspect.id)
-
-          xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.status_message(@post)).to_xml
-
-          receive_with_zord(bob, alice.person, xml)
-          receive_with_zord(eve, alice.person, xml)
-
-          comment = eve.comment!(@post, 'tada')
-          queue.drain_all
-          # After Eve creates her comment, it gets sent to Alice, who signs it with her private key
-          # before relaying it out to the contacts on the top-level post
-          comment.parent_author_signature = comment.sign_with_key(alice.encryption_key)
-          @xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.comment(comment)).to_xml
-          comment.delete
-
-          comment_with_whitespace = alice.comment!(@post, '   I cannot lift my thumb from the spacebar  ')
-          queue.drain_all
-          comment_entity = Diaspora::Federation::Entities.comment(comment_with_whitespace)
-          @xml_with_whitespace = Diaspora::Federation.xml(comment_entity).to_xml
-          @guid_with_whitespace = comment_with_whitespace.guid
-          comment_with_whitespace.delete
-        end
-      end
-
-      it 'should receive a relayed comment with leading whitespace' do
-        expect(eve.reload.visible_shareables(Post).size).to eq(1)
-        post_in_db = StatusMessage.find(@post.id)
-        expect(post_in_db.comments).to eq([])
-        receive_with_zord(eve, alice.person, @xml_with_whitespace)
-
-        expect(post_in_db.comments(true).first.guid).to eq(@guid_with_whitespace)
-      end
-
-      it 'should correctly marshal a stranger for the downstream user' do
-        remote_person = eve.person.dup
-        eve.person.delete
-        eve.delete
-        Person.where(:id => remote_person.id).delete_all
-        Profile.where(:person_id => remote_person.id).delete_all
-        remote_person.attributes.delete(:id) # leaving a nil id causes it to try to save with id set to NULL in postgres
-
-        remote_person.save(:validate => false)
-        remote_person.profile = FactoryGirl.create(:profile, :person => remote_person)
-        expect(Person).to receive(:find_or_fetch_by_identifier).twice.with(eve.person.diaspora_handle)
-                            .and_return(remote_person)
-
-        expect(bob.reload.visible_shareables(Post).size).to eq(1)
-        post_in_db = StatusMessage.find(@post.id)
-        expect(post_in_db.comments).to eq([])
-
-        receive_with_zord(bob, alice.person, @xml)
-
-        expect(post_in_db.comments(true).first.author).to eq(remote_person)
-      end
-    end
-
-    context 'local' do
-      before do
-        skip # TODO
-        @post = alice.post :status_message, :text => "hello", :to => @alices_aspect.id
-
-        xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.status_message(@post)).to_xml
-
-        alice.share_with(eve.person, alice.aspects.first)
-
-        receive_with_zord(bob, alice.person, xml)
-        receive_with_zord(eve, alice.person, xml)
-      end
-
-      it 'does not raise a `Mysql2::Error: Duplicate entry...` exception on save' do
-        inlined_jobs do
-          @comment = bob.comment!(@post, 'tada')
-          @xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.comment(@comment)).to_xml
-
-          expect {
-            receive_with_zord(alice, bob.person, @xml)
-          }.to_not raise_exception
-        end
-      end
-    end
-  end
-
-
-  describe 'receiving mulitple versions of the same post from a remote pod' do
-    before do
-      @local_luke, @local_leia, @remote_raphael = set_up_friends
-
-      @post = FactoryGirl.build(
-        :status_message,
-        text:       "hey",
-        guid:       UUID.generate(:compact),
-        author:     @remote_raphael,
-        created_at: 5.days.ago,
-        updated_at: 5.days.ago
-      )
-    end
-
-    it "allows two people saving the same post" do
-      skip # TODO
-      xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.status_message(@post)).to_xml
-      receive_with_zord(@local_luke, @remote_raphael, xml)
-      receive_with_zord(@local_leia, @remote_raphael, xml)
-      expect(Post.find_by_guid(@post.guid).updated_at).to be < Time.now.utc + 1
-      expect(Post.find_by_guid(@post.guid).created_at.day).to eq(@post.created_at.day)
-    end
-
-    it 'does not update the post if a new one is sent with a new created_at' do
-      skip # TODO
-      old_time = @post.created_at
-      xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.status_message(@post)).to_xml
-      receive_with_zord(@local_luke, @remote_raphael, xml)
-
-      @post = FactoryGirl.build(
-        :status_message,
-        text:       "hey",
-        guid:       @post.guid,
-        author:     @remote_raphael,
-        created_at: 2.days.ago
-      )
-      xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.status_message(@post)).to_xml
-      receive_with_zord(@local_luke, @remote_raphael, xml)
-
-      expect(Post.find_by_guid(@post.guid).created_at.day).to eq(old_time.day)
-    end
-  end
-
-
-  describe 'salmon' do
-    let(:post){alice.post :status_message, :text => "hello", :to => @alices_aspect.id}
-    let(:salmon){alice.salmon( post )}
-
-    it 'processes a salmon for a post' do
-      salmon_xml = salmon.xml_for(bob.person)
-
-      zord = Postzord::Receiver::Private.new(bob, :salmon_xml => salmon_xml)
-      zord.perform!
-
-      expect(bob.visible_shareables(Post).include?(post)).to be true
-    end
-  end
 end
diff --git a/spec/lib/diaspora/federated/request_spec.rb b/spec/lib/diaspora/federated/request_spec.rb
index ed588d0bf3360f04ae129125887a2d20f2e33456..c92666baab4e60c8f7afce0ad1c1a83ff95583fa 100644
--- a/spec/lib/diaspora/federated/request_spec.rb
+++ b/spec/lib/diaspora/federated/request_spec.rb
@@ -65,38 +65,6 @@ describe Request do
     end
   end
 
-  describe '#receive' do
-    it 'creates a contact' do
-      skip # TODO
-      request = described_class.diaspora_initialize(:from => alice.person, :to => eve.person, :into => @aspect)
-      expect{
-        request.receive(eve, alice.person)
-      }.to change{
-        eve.contacts(true).size
-      }.by(1)
-    end
-
-    it 'sets mutual if a contact already exists' do
-      skip # TODO
-      alice.share_with(eve.person, alice.aspects.first)
-
-      expect {
-        described_class.diaspora_initialize(:from => eve.person, :to => alice.person,
-                                    :into => eve.aspects.first).receive(alice, eve.person)
-      }.to change {
-        alice.contacts.find_by_person_id(eve.person.id).mutual?
-      }.from(false).to(true)
-
-    end
-
-    it 'sets sharing' do
-      skip # TODO
-      described_class.diaspora_initialize(:from => eve.person, :to => alice.person,
-                                  :into => eve.aspects.first).receive(alice, eve.person)
-      expect(alice.contact_for(eve.person)).to be_sharing
-    end
-  end
-
   context 'xml' do
     before do
       @request = described_class.diaspora_initialize(:from => alice.person, :to => eve.person, :into => @aspect)
diff --git a/spec/lib/diaspora/federation/receive_spec.rb b/spec/lib/diaspora/federation/receive_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c4a8a7e9ce7683444398631d55eddd01427bf5ca
--- /dev/null
+++ b/spec/lib/diaspora/federation/receive_spec.rb
@@ -0,0 +1,549 @@
+require "spec_helper"
+
+describe Diaspora::Federation::Receive do
+  let(:sender) { FactoryGirl.create(:person) }
+  let(:post) { alice.post(:status_message, text: "hello", public: true) }
+
+  describe ".account_deletion" do
+    let(:account_deletion_entity) { FactoryGirl.build(:account_deletion_entity, author: sender.diaspora_handle) }
+
+    it "saves the account deletion" do
+      Diaspora::Federation::Receive.account_deletion(account_deletion_entity)
+
+      account_deletion = AccountDeletion.find_by!(diaspora_handle: sender.diaspora_handle)
+
+      expect(account_deletion.person).to eq(sender)
+    end
+  end
+
+  describe ".comment" do
+    let(:comment_entity) { FactoryGirl.build(:comment_entity, author: sender.diaspora_handle, parent_guid: post.guid) }
+
+    it "saves the comment" do
+      received = Diaspora::Federation::Receive.comment(comment_entity)
+
+      comment = Comment.find_by!(guid: comment_entity.guid)
+
+      expect(received).to eq(comment)
+      expect(comment.author).to eq(sender)
+      expect(comment.text).to eq(comment_entity.text)
+      expect(comment.created_at.iso8601).to eq(comment_entity.created_at.iso8601)
+    end
+
+    it "attaches the comment to the post" do
+      Diaspora::Federation::Receive.comment(comment_entity)
+
+      comment = Comment.find_by!(guid: comment_entity.guid)
+
+      expect(post.comments).to include(comment)
+      expect(comment.post).to eq(post)
+    end
+
+    it_behaves_like "it ignores existing object received twice", Comment, :comment do
+      let(:entity) { comment_entity }
+    end
+  end
+
+  describe ".contact" do
+    let(:contact_entity) {
+      FactoryGirl.build(:contact_entity, author: sender.diaspora_handle, recipient: alice.diaspora_handle)
+    }
+
+    it "creates the contact if it doesn't exist" do
+      received = Diaspora::Federation::Receive.contact(contact_entity)
+
+      contact = alice.contacts.find_by!(person_id: sender.id)
+
+      expect(received).to eq(contact)
+      expect(contact.sharing).to be_truthy
+    end
+
+    it "updates the contact if it exists" do
+      alice.contacts.find_or_initialize_by(person_id: sender.id, receiving: true, sharing: false).save!
+
+      received = Diaspora::Federation::Receive.contact(contact_entity)
+
+      contact = alice.contacts.find_by!(person_id: sender.id)
+
+      expect(received).to eq(contact)
+      expect(contact.sharing).to be_truthy
+    end
+
+    it "does nothing, if already sharing" do
+      alice.contacts.find_or_initialize_by(person_id: sender.id, receiving: true, sharing: true).save!
+
+      expect_any_instance_of(Contact).not_to receive(:save!)
+
+      expect(Diaspora::Federation::Receive.contact(contact_entity)).to be_nil
+    end
+  end
+
+  describe ".conversation" do
+    let(:conv_guid) { FactoryGirl.generate(:guid) }
+    let(:message_entity) {
+      FactoryGirl.build(
+        :message_entity,
+        author:            alice.diaspora_handle,
+        parent_guid:       conv_guid,
+        conversation_guid: conv_guid
+      )
+    }
+    let(:conversation_entity) {
+      FactoryGirl.build(
+        :conversation_entity,
+        guid:         conv_guid,
+        author:       alice.diaspora_handle,
+        messages:     [message_entity],
+        participants: "#{alice.diaspora_handle};#{bob.diaspora_handle}"
+      )
+    }
+
+    it "saves the conversation" do
+      received = Diaspora::Federation::Receive.conversation(conversation_entity)
+
+      conv = Conversation.find_by!(guid: conversation_entity.guid)
+
+      expect(received).to eq(conv)
+      expect(conv.author).to eq(alice.person)
+      expect(conv.subject).to eq(conversation_entity.subject)
+    end
+
+    it "saves the message" do
+      Diaspora::Federation::Receive.conversation(conversation_entity)
+
+      conv = Conversation.find_by!(guid: conversation_entity.guid)
+
+      expect(conv.messages.count).to eq(1)
+      expect(conv.messages.first.author).to eq(alice.person)
+      expect(conv.messages.first.text).to eq(message_entity.text)
+      expect(conv.messages.first.created_at.iso8601).to eq(message_entity.created_at.iso8601)
+    end
+
+    it "creates appropriate visibilities" do
+      Diaspora::Federation::Receive.conversation(conversation_entity)
+
+      conv = Conversation.find_by!(guid: conversation_entity.guid)
+
+      expect(conv.participants.count).to eq(2)
+      expect(conv.participants).to include(alice.person, bob.person)
+    end
+
+    it_behaves_like "it ignores existing object received twice", Conversation, :conversation do
+      let(:entity) { conversation_entity }
+    end
+  end
+
+  describe ".like" do
+    let(:like_entity) { FactoryGirl.build(:like_entity, author: sender.diaspora_handle, parent_guid: post.guid) }
+
+    it "saves the like" do
+      received = Diaspora::Federation::Receive.like(like_entity)
+
+      like = Like.find_by!(guid: like_entity.guid)
+
+      expect(received).to eq(like)
+      expect(like.author).to eq(sender)
+      expect(like.positive).to be_truthy
+    end
+
+    it "attaches the like to the post" do
+      Diaspora::Federation::Receive.like(like_entity)
+
+      like = Like.find_by!(guid: like_entity.guid)
+
+      expect(post.likes).to include(like)
+      expect(like.target).to eq(post)
+    end
+
+    it_behaves_like "it ignores existing object received twice", Like, :like do
+      let(:entity) { like_entity }
+    end
+  end
+
+  describe ".message" do
+    let(:conversation) {
+      FactoryGirl.build(:conversation, author: alice.person).tap do |conv|
+        conv.participants << sender
+        conv.save!
+      end
+    }
+    let(:message_entity) {
+      FactoryGirl.build(
+        :message_entity,
+        author:            sender.diaspora_handle,
+        parent_guid:       conversation.guid,
+        conversation_guid: conversation.guid
+      )
+    }
+
+    it "saves the message" do
+      received = Diaspora::Federation::Receive.message(message_entity)
+
+      msg = Message.find_by!(guid: message_entity.guid)
+
+      expect(received).to eq(msg)
+      expect(msg.author).to eq(sender)
+      expect(msg.text).to eq(message_entity.text)
+      expect(msg.created_at.iso8601).to eq(message_entity.created_at.iso8601)
+    end
+
+    it "attaches the message to the conversation" do
+      msg = Diaspora::Federation::Receive.message(message_entity)
+
+      conv = Conversation.find_by!(guid: conversation.guid)
+
+      expect(conv.messages).to include(msg)
+      expect(msg.conversation).to eq(conv)
+    end
+
+    it_behaves_like "it ignores existing object received twice", Message, :message do
+      let(:entity) { message_entity }
+    end
+  end
+
+  describe ".participation" do
+    let(:participation_entity) {
+      FactoryGirl.build(:participation_entity, author: sender.diaspora_handle, parent_guid: post.guid)
+    }
+
+    it "saves the participation" do
+      received = Diaspora::Federation::Receive.participation(participation_entity)
+
+      participation = Participation.find_by!(guid: participation_entity.guid)
+
+      expect(received).to eq(participation)
+      expect(participation.author).to eq(sender)
+    end
+
+    it "attaches the participation to the post" do
+      Diaspora::Federation::Receive.participation(participation_entity)
+
+      participation = Participation.find_by!(guid: participation_entity.guid)
+
+      expect(post.participations).to include(participation)
+      expect(participation.target).to eq(post)
+    end
+
+    it "does not save the participation if the target is not local" do
+      remote_post = FactoryGirl.create(:status_message, author: sender, public: true)
+      remote_participation = FactoryGirl.build(
+        :participation_entity,
+        author:      sender.diaspora_handle,
+        parent_guid: remote_post.guid
+      )
+
+      expect(Diaspora::Federation::Receive.participation(remote_participation)).to be_nil
+
+      expect(Participation.exists?(guid: remote_participation.guid)).to be_falsey
+    end
+
+    it_behaves_like "it ignores existing object received twice", Participation, :participation do
+      let(:entity) { participation_entity }
+    end
+  end
+
+  describe ".photo" do
+    let(:photo_entity) { FactoryGirl.build(:photo_entity, author: sender.diaspora_handle) }
+
+    it "saves the photo if it does not already exist" do
+      received = Diaspora::Federation::Receive.photo(photo_entity)
+
+      photo = Photo.find_by!(guid: photo_entity.guid)
+
+      expect(received).to eq(photo)
+      expect(photo.author).to eq(sender)
+      expect(photo.remote_photo_name).to eq(photo_entity.remote_photo_name)
+      expect(photo.created_at.iso8601).to eq(photo_entity.created_at.iso8601)
+    end
+
+    it "updates the photo if it is already persisted" do
+      Diaspora::Federation::Receive.photo(photo_entity)
+
+      photo = Photo.find_by!(guid: photo_entity.guid)
+      photo.remote_photo_name = "foobar.jpg"
+      photo.save
+
+      received = Diaspora::Federation::Receive.photo(photo_entity)
+      photo.reload
+
+      expect(received).to eq(photo)
+      expect(photo.author).to eq(sender)
+      expect(photo.remote_photo_name).to eq(photo_entity.remote_photo_name)
+    end
+
+    it "does not update the photo if the author mismatches" do
+      Diaspora::Federation::Receive.photo(photo_entity)
+
+      photo = Photo.find_by!(guid: photo_entity.guid)
+      photo.remote_photo_name = "foobar.jpg"
+      photo.author = bob.person
+      photo.save
+
+      expect {
+        Diaspora::Federation::Receive.photo(photo_entity)
+      }.to raise_error Diaspora::Federation::InvalidAuthor
+
+      photo.reload
+
+      expect(photo.author).to eq(bob.person)
+      expect(photo.remote_photo_name).to eq("foobar.jpg")
+    end
+  end
+
+  describe ".poll_participation" do
+    let(:post_with_poll) { FactoryGirl.create(:status_message_with_poll) }
+    let(:poll_participation_entity) {
+      FactoryGirl.build(
+        :poll_participation_entity,
+        author:           sender.diaspora_handle,
+        parent_guid:      post_with_poll.poll.guid,
+        poll_answer_guid: post_with_poll.poll.poll_answers.first.guid
+      )
+    }
+
+    it "saves the poll participation" do
+      received = Diaspora::Federation::Receive.poll_participation(poll_participation_entity)
+
+      poll_participation = PollParticipation.find_by!(guid: poll_participation_entity.guid)
+
+      expect(received).to eq(poll_participation)
+      expect(poll_participation.author).to eq(sender)
+      expect(poll_participation.poll_answer).to eq(post_with_poll.poll.poll_answers.first)
+    end
+
+    it "attaches the poll participation to the poll" do
+      Diaspora::Federation::Receive.poll_participation(poll_participation_entity)
+
+      poll_participation = PollParticipation.find_by!(guid: poll_participation_entity.guid)
+
+      expect(post_with_poll.poll.poll_participations).to include(poll_participation)
+      expect(poll_participation.poll).to eq(post_with_poll.poll)
+    end
+
+    it_behaves_like "it ignores existing object received twice", PollParticipation, :poll_participation do
+      let(:entity) { poll_participation_entity }
+    end
+  end
+
+  describe ".profile" do
+    let(:profile_entity) { FactoryGirl.build(:profile_entity, author: sender.diaspora_handle) }
+
+    it "updates the profile of the person" do
+      received = Diaspora::Federation::Receive.profile(profile_entity)
+
+      profile = Profile.find(sender.profile.id)
+
+      expect(received).to eq(profile)
+      expect(profile.first_name).to eq(profile_entity.first_name)
+      expect(profile.last_name).to eq(profile_entity.last_name)
+      expect(profile.gender).to eq(profile_entity.gender)
+      expect(profile.bio).to eq(profile_entity.bio)
+      expect(profile.location).to eq(profile_entity.location)
+      expect(profile.searchable).to eq(profile_entity.searchable)
+      expect(profile.nsfw).to eq(profile_entity.nsfw)
+      expect(profile.tag_string).to eq(profile_entity.tag_string)
+    end
+  end
+
+  describe ".reshare" do
+    let(:reshare_entity) { FactoryGirl.build(:reshare_entity, author: sender.diaspora_handle, root_guid: post.guid) }
+
+    it "saves the reshare" do
+      received = Diaspora::Federation::Receive.reshare(reshare_entity)
+
+      reshare = Reshare.find_by!(guid: reshare_entity.guid)
+
+      expect(received).to eq(reshare)
+      expect(reshare.author).to eq(sender)
+    end
+
+    it "attaches the reshare to the post" do
+      Diaspora::Federation::Receive.reshare(reshare_entity)
+
+      reshare = Reshare.find_by!(guid: reshare_entity.guid)
+
+      expect(post.reshares).to include(reshare)
+      expect(reshare.root).to eq(post)
+      expect(reshare.created_at.iso8601).to eq(reshare_entity.created_at.iso8601)
+    end
+
+    it_behaves_like "it ignores existing object received twice", Reshare, :reshare do
+      let(:entity) { reshare_entity }
+    end
+  end
+
+  describe ".retraction" do
+    it "destroys the post" do
+      remote_post = FactoryGirl.create(:status_message, author: sender, public: true)
+
+      retraction = FactoryGirl.build(
+        :retraction_entity,
+        author:      sender.diaspora_handle,
+        target_guid: remote_post.guid,
+        target_type: "Post"
+      )
+
+      expect_any_instance_of(StatusMessage).to receive(:destroy!).and_call_original
+
+      Diaspora::Federation::Receive.retraction(retraction, nil)
+
+      expect(StatusMessage.exists?(guid: remote_post.guid)).to be_falsey
+    end
+
+    it "raises when the post does not exist" do
+      retraction = FactoryGirl.build(
+        :retraction_entity,
+        author:      sender.diaspora_handle,
+        target_guid: FactoryGirl.generate(:guid),
+        target_type: "Post"
+      )
+
+      expect {
+        Diaspora::Federation::Receive.retraction(retraction, nil)
+      }.to raise_error ActiveRecord::RecordNotFound
+    end
+
+    it "disconnects on Person-Retraction" do
+      alice.contacts.find_or_initialize_by(person_id: sender.id, receiving: true, sharing: true).save!
+
+      retraction = FactoryGirl.build(
+        :retraction_entity,
+        author:      sender.diaspora_handle,
+        target_guid: sender.guid,
+        target_type: "Person"
+      )
+
+      Diaspora::Federation::Receive.retraction(retraction, alice.id)
+
+      contact = alice.contacts.find_by!(person_id: sender.id)
+
+      expect(contact).not_to be_nil
+      expect(contact.sharing).to be_falsey
+    end
+  end
+
+  describe ".status_message" do
+    context "basic status message" do
+      let(:status_message_entity) { FactoryGirl.build(:status_message_entity, author: sender.diaspora_handle) }
+
+      it "saves the status message" do
+        received = Diaspora::Federation::Receive.status_message(status_message_entity)
+
+        status_message = StatusMessage.find_by!(guid: status_message_entity.guid)
+
+        expect(received).to eq(status_message)
+        expect(status_message.author).to eq(sender)
+        expect(status_message.raw_message).to eq(status_message_entity.raw_message)
+        expect(status_message.public).to eq(status_message_entity.public)
+        expect(status_message.created_at.iso8601).to eq(status_message_entity.created_at.iso8601)
+        expect(status_message.provider_display_name).to eq(status_message_entity.provider_display_name)
+
+        expect(status_message.location).to be_nil
+        expect(status_message.poll).to be_nil
+        expect(status_message.photos).to be_empty
+      end
+
+      it "returns the status message if it already exists" do
+        first = Diaspora::Federation::Receive.status_message(status_message_entity)
+        second = Diaspora::Federation::Receive.status_message(status_message_entity)
+
+        expect(second).not_to be_nil
+        expect(first).to eq(second)
+      end
+
+      it "does not change anything if the status message already exists" do
+        Diaspora::Federation::Receive.status_message(status_message_entity)
+
+        expect_any_instance_of(StatusMessage).not_to receive(:create_or_update)
+
+        Diaspora::Federation::Receive.status_message(status_message_entity)
+      end
+    end
+
+    context "with poll" do
+      let(:poll_entity) { FactoryGirl.build(:poll_entity) }
+      let(:status_message_entity) {
+        FactoryGirl.build(:status_message_entity, author: sender.diaspora_handle, poll: poll_entity)
+      }
+
+      it "saves the status message" do
+        received = Diaspora::Federation::Receive.status_message(status_message_entity)
+
+        status_message = StatusMessage.find_by!(guid: status_message_entity.guid)
+
+        expect(received).to eq(status_message)
+        expect(status_message.author).to eq(sender)
+
+        expect(status_message.poll.question).to eq(poll_entity.question)
+        expect(status_message.poll.guid).to eq(poll_entity.guid)
+        expect(status_message.poll.poll_answers.count).to eq(poll_entity.poll_answers.count)
+        expect(status_message.poll.poll_answers.map(&:answer)).to eq(poll_entity.poll_answers.map(&:answer))
+      end
+    end
+
+    context "with location" do
+      let(:location_entity) { FactoryGirl.build(:location_entity) }
+      let(:status_message_entity) {
+        FactoryGirl.build(:status_message_entity, author: sender.diaspora_handle, location: location_entity)
+      }
+
+      it "saves the status message" do
+        received = Diaspora::Federation::Receive.status_message(status_message_entity)
+
+        status_message = StatusMessage.find_by!(guid: status_message_entity.guid)
+
+        expect(received).to eq(status_message)
+        expect(status_message.author).to eq(sender)
+
+        expect(status_message.location.address).to eq(location_entity.address)
+        expect(status_message.location.lat).to eq(location_entity.lat)
+        expect(status_message.location.lng).to eq(location_entity.lng)
+      end
+    end
+
+    context "with photos" do
+      let(:status_message_guid) { FactoryGirl.generate(:guid) }
+      let(:photo1) {
+        FactoryGirl.build(:photo_entity, author: sender.diaspora_handle, status_message_guid: status_message_guid)
+      }
+      let(:photo2) {
+        FactoryGirl.build(:photo_entity, author: sender.diaspora_handle, status_message_guid: status_message_guid)
+      }
+      let(:status_message_entity) {
+        FactoryGirl.build(
+          :status_message_entity,
+          author: sender.diaspora_handle,
+          guid:   status_message_guid,
+          photos: [photo1, photo2]
+        )
+      }
+
+      it "saves the status message and photos" do
+        received = Diaspora::Federation::Receive.status_message(status_message_entity)
+
+        status_message = StatusMessage.find_by!(guid: status_message_entity.guid)
+
+        expect(received).to eq(status_message)
+        expect(status_message.author).to eq(sender)
+
+        expect(status_message.photos.map(&:guid)).to include(photo1.guid, photo2.guid)
+      end
+
+      it "does not overwrite the photos if they already exist" do
+        received_photo = Diaspora::Federation::Receive.photo(photo1)
+        received_photo.text = "foobar"
+        received_photo.save!
+
+        received = Diaspora::Federation::Receive.status_message(status_message_entity)
+
+        status_message = StatusMessage.find_by!(guid: status_message_entity.guid)
+
+        expect(received).to eq(status_message)
+        expect(status_message.author).to eq(sender)
+
+        expect(status_message.photos.map(&:guid)).to include(photo1.guid, photo2.guid)
+        expect(status_message.photos.map(&:text)).to include(received_photo.text, photo2.text)
+      end
+    end
+  end
+end
diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb
index 5c4641037fe1cf152de4caac08ef6b62a693f6b0..d181bba163164f4abe1b6acd8772ff095e327aff 100644
--- a/spec/models/conversation_spec.rb
+++ b/spec/models/conversation_spec.rb
@@ -99,32 +99,6 @@ describe Conversation, :type => :model do
         expect(conversation.subscribers(user1)).to eq(user1.contacts.map(&:person))
       end
     end
-
-    describe "#receive" do
-      before do
-        Message.destroy_all
-        Conversation.destroy_all
-      end
-
-      it "creates a message" do
-        skip # TODO
-        expect {
-          Diaspora::Parser.from_xml(xml).receive(user1, user2.person)
-        }.to change(Message, :count).by(1)
-      end
-      it "creates a conversation" do
-        skip # TODO
-        expect {
-          Diaspora::Parser.from_xml(xml).receive(user1, user2.person)
-        }.to change(Conversation, :count).by(1)
-      end
-      it "creates appropriate visibilities" do
-        skip # TODO
-        expect {
-          Diaspora::Parser.from_xml(xml).receive(user1, user2.person)
-        }.to change(ConversationVisibility, :count).by(participant_ids.size)
-      end
-    end
   end
 
   describe "#invalid parameters" do
diff --git a/spec/models/photo_spec.rb b/spec/models/photo_spec.rb
index b96eccc51a6aab71a559d0189d8194f89c6d92ab..bc6d2a041ca0757361725601d9d22b97ec14439d 100644
--- a/spec/models/photo_spec.rb
+++ b/spec/models/photo_spec.rb
@@ -274,23 +274,6 @@ describe Photo, :type => :model do
     end
   end
 
-  describe "#receive_public" do
-    it "updates the photo if it is already persisted" do
-      skip # TODO
-      allow(@photo).to receive(:persisted_shareable).and_return(@photo2)
-      expect(@photo2).to receive(:update_attributes)
-      @photo.receive_public
-    end
-
-    it "does not update the photo if the author mismatches" do
-      skip # TODO
-      @photo.author = bob.person
-      allow(@photo).to receive(:persisted_shareable).and_return(@photo2)
-      expect(@photo).not_to receive(:update_existing_sharable)
-      @photo.receive_public
-    end
-  end
-
   describe "#visible" do
     context "with a current user" do
       it "calls photos_from" do
diff --git a/spec/shared_behaviors/receiving.rb b/spec/shared_behaviors/receiving.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2637ff49eac39a5515e6381d6cdba7503a7136af
--- /dev/null
+++ b/spec/shared_behaviors/receiving.rb
@@ -0,0 +1,16 @@
+require "spec_helper"
+
+shared_examples_for "it ignores existing object received twice" do |klass, method|
+  it "return nil if the #{klass} already exists" do
+    expect(Diaspora::Federation::Receive.public_send(method, entity)).not_to be_nil
+    expect(Diaspora::Federation::Receive.public_send(method, entity)).to be_nil
+  end
+
+  it "does not change anything if the #{klass} already exists" do
+    Diaspora::Federation::Receive.public_send(method, entity)
+
+    expect_any_instance_of(klass).not_to receive(:create_or_update)
+
+    Diaspora::Federation::Receive.public_send(method, entity)
+  end
+end
diff --git a/spec/shared_behaviors/relayable.rb b/spec/shared_behaviors/relayable.rb
index d8e856354dcb17a4f65a327c9719071fb2b7891e..048c2e4aa5407593b78278686885afbd95e4d645 100644
--- a/spec/shared_behaviors/relayable.rb
+++ b/spec/shared_behaviors/relayable.rb
@@ -79,13 +79,6 @@ shared_examples_for "it is relayable" do
 
   context 'propagation' do
     describe '#receive' do
-      it 'does not overwrite a object that is already in the db' do
-        skip # TODO
-        expect {
-          @dup_object_by_parent_author.receive(@local_leia, @local_luke.person)
-        }.to_not change { @dup_object_by_parent_author.class.count }
-      end
-
       it 'dispatches when the person receiving is the parent author' do
         skip # TODO
         p = Postzord::Dispatcher.build(@local_luke, @object_by_recipient)