Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class TagFollowingService
  def initialize(user=nil)
    @user = user
  end

  def create(name)
    name_normalized = ActsAsTaggableOn::Tag.normalize(name)
    raise ArgumentError, "Name field null or empty" if name_normalized.blank?

    tag = ActsAsTaggableOn::Tag.find_or_create_by(name: name_normalized)
    raise DuplicateTag if @user.tag_followings.exists?(tag_id: tag.id)

    tag_following = @user.tag_followings.new(tag_id: tag.id)
    raise "Can't process tag entity" unless tag_following.save

    tag
  end

  def find(name)
    name_normalized = ActsAsTaggableOn::Tag.normalize(name)
    ActsAsTaggableOn::Tag.find_or_create_by(name: name_normalized)
  end

  def destroy(id)
    tag_following = @user.tag_followings.find_by!(tag_id: id)
    tag_following.destroy
  end

  def destroy_by_name(name)
    name_normalized = ActsAsTaggableOn::Tag.normalize(name)
    followed_tag = @user.followed_tags.find_by!(name: name_normalized)
    destroy(followed_tag.id)
  end

  def index
    @user.followed_tags
  end

  class DuplicateTag < RuntimeError; end
end

```

Response:
# frozen_string_literal: true

describe TagFollowingService do
  before do
    add_tag("tag1", alice)
    add_tag("tag2", alice)
  end

  describe "#create" do
    it "Creates new tag with valid name" do
      name = SecureRandom.uuid
      expect(alice.followed_tags.find_by(name: name)).to be_nil
      tag_data = tag_following_service(alice).create(name)
      expect(alice.followed_tags.find_by(name: name).name).to eq(name)
      expect(tag_data["name"]).to eq(name)
      expect(tag_data["id"]).to be_truthy
      expect(tag_data["taggings_count"]).to eq(0)
    end

    it "Throws error with empty tag" do
      expect { tag_following_service(alice).create(nil) }.to raise_error(ArgumentError)
      expect { tag_following_service(alice).create("") }.to raise_error(ArgumentError)
      expect { tag_following_service(alice).create("#") }.to raise_error(ArgumentError)
      expect { tag_following_service(alice).create(" ") }.to raise_error(ArgumentError)
    end

    it "throws an error when trying to follow an already followed tag" do
      name = SecureRandom.uuid
      tag_following_service.create(name)
      expect {
        tag_following_service.create(name)
      }.to raise_error TagFollowingService::DuplicateTag
    end
  end

  describe "#destroy" do
    it "Deletes tag with valid name" do
      name = SecureRandom.uuid
      add_tag(name, alice)
      expect(alice.followed_tags.find_by(name: name).name).to eq(name)
      expect(tag_following_service(alice).destroy_by_name(name)).to be_truthy
      expect(alice.followed_tags.find_by(name: name)).to be_nil
    end

    it "Deletes tag with id" do
      name = SecureRandom.uuid
      new_tag = add_tag(name, alice)
      expect(alice.followed_tags.find_by(name: name).name).to eq(name)
      expect(tag_following_service(alice).destroy(new_tag.tag_id)).to be_truthy
      expect(alice.followed_tags.find_by(name: name)).to be_nil
    end

    it "Does nothing with tag that isn't already followed" do
      original_length = alice.followed_tags.length
      expect {
        tag_following_service(alice).destroy_by_name(SecureRandom.uuid)
      }.to raise_error ActiveRecord::RecordNotFound
      expect {
        tag_following_service(alice).destroy(-1)
      }.to raise_error ActiveRecord::RecordNotFound
      expect(alice.followed_tags.length).to eq(original_length)
    end

    it "Does nothing with empty tag name" do
      original_length = alice.followed_tags.length
      expect {
        tag_following_service(alice).destroy_by_name("")
      }.to raise_error ActiveRecord::RecordNotFound
      expect(alice.followed_tags.length).to eq(original_length)
    end
  end

  describe "#index" do
    it "Returns user's list of tags" do
      tags = tag_following_service(alice).index
      expect(tags.length).to eq(alice.followed_tags.length)
    end
  end

  private

  def tag_following_service(user=alice)
    TagFollowingService.new(user)
  end

  def add_tag(name, user)
    tag = ActsAsTaggableOn::Tag.find_or_create_by(name: name)
    tag_following = user.tag_followings.new(tag_id: tag.id)
    tag_following.save
    tag_following
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class AspectsMembershipService
  def initialize(user=nil)
    @user = user
  end

  def create(aspect_id, person_id)
    person = Person.find(person_id)
    aspect = @user.aspects.where(id: aspect_id).first
    raise ActiveRecord::RecordNotFound unless person.present? && aspect.present?

    contact = @user.share_with(person, aspect)
    raise I18n.t("aspects.add_to_aspect.failure") if contact.blank?

    AspectMembership.where(contact_id: contact.id, aspect_id: aspect.id).first
  end

  def destroy_by_ids(aspect_id, contact_id)
    aspect = @user.aspects.where(id: aspect_id).first
    contact = @user.contacts.where(person_id: contact_id).first
    destroy(aspect, contact)
  end

  def destroy_by_membership_id(membership_id)
    aspect = @user.aspects.joins(:aspect_memberships).where(aspect_memberships: {id: membership_id}).first
    contact = @user.contacts.joins(:aspect_memberships).where(aspect_memberships: {id: membership_id}).first
    destroy(aspect, contact)
  end

  def contacts_in_aspect(aspect_id)
    order = [Arel.sql("contact_id IS NOT NULL DESC"), "profiles.first_name ASC", "profiles.last_name ASC",
             "profiles.diaspora_handle ASC"]
    @user.aspects.find(aspect_id) # to provide better error code if aspect isn't correct
    contacts = @user.contacts.arel_table
    aspect_memberships = AspectMembership.arel_table
    @user.contacts.joins(
      contacts.join(aspect_memberships).on(
        aspect_memberships[:aspect_id].eq(aspect_id).and(
          aspect_memberships[:contact_id].eq(contacts[:id])
        )
      ).join_sources
    ).includes(person: :profile).order(order)
  end

  def all_contacts
    order = ["profiles.first_name ASC", "profiles.last_name ASC",
             "profiles.diaspora_handle ASC"]
    @user.contacts.includes(person: :profile).order(order)
  end

  private

  def destroy(aspect, contact)
    raise ActiveRecord::RecordNotFound unless aspect.present? && contact.present?

    raise Diaspora::NotMine unless @user.mine?(aspect) && @user.mine?(contact)

    membership = contact.aspect_memberships.where(aspect_id: aspect.id).first
    raise ActiveRecord::RecordNotFound if membership.blank?

    success = membership.destroy

    {success: success, membership: membership}
  end
end

```

Response:
# frozen_string_literal: true

describe AspectsMembershipService do
  before do
    @alice_aspect1  = alice.aspects.first
    @alice_aspect2  = alice.aspects.create(name: "another aspect")
    @bob_aspect1 = bob.aspects.first
  end

  describe "#create" do
    context "with valid IDs" do
      it "succeeds" do
        membership = aspects_membership_service.create(@alice_aspect2.id, bob.person.id)
        expect(membership[:aspect_id]).to eq(@alice_aspect2.id)
        expect(@alice_aspect2.contacts.find_by(person_id: bob.person.id)).not_to be_nil
      end

      it "fails if already in aspect" do
        aspects_membership_service.create(@alice_aspect2.id, bob.person.id)
        expect {
          aspects_membership_service.create(@alice_aspect2.id, bob.person.id)
        }.to raise_error ActiveRecord::RecordNotUnique
      end
    end

    context "with invalid IDs" do
      it "fails with invalid User ID" do
        expect {
          aspects_membership_service.create(@alice_aspect2.id, -1)
        }.to raise_error ActiveRecord::RecordNotFound
      end

      it "fails with invalid Aspect ID" do
        expect {
          aspects_membership_service.create(-1, bob.person.id)
        }.to raise_error ActiveRecord::RecordNotFound
      end

      it "fails with aspect ID that isn't user's" do
        expect {
          aspects_membership_service.create(@bob_aspect1.id, eve.person.id)
        }.to raise_error ActiveRecord::RecordNotFound
      end
    end
  end

  describe "#destroy" do
    before do
      @membership = aspects_membership_service.create(@alice_aspect2.id, bob.person.id)
    end

    context "with aspect/user valid IDs" do
      it "succeeds if in aspect" do
        aspects_membership_service.destroy_by_ids(@alice_aspect2.id, bob.person.id)
        expect(@alice_aspect2.contacts.find_by(person_id: bob.person.id)).to be_nil
      end

      it "fails if not in aspect" do
        expect {
          aspects_membership_service.destroy_by_ids(@alice_aspect2.id, eve.person.id)
        }.to raise_error ActiveRecord::RecordNotFound
      end
    end

    context "with a membership ID" do
      it "succeeds if their membership" do
        aspects_membership_service.destroy_by_membership_id(@membership.id)
        expect(@alice_aspect2.contacts.find_by(person_id: bob.person.id)).to be_nil
      end

      it "fails if not their membership" do
        expect {
          aspects_membership_service(eve).destroy_by_membership_id(@membership.id)
        }.to raise_error ActiveRecord::RecordNotFound
      end

      it "fails if invalid membership ID" do
        expect {
          aspects_membership_service(eve).destroy_by_membership_id(-1)
        }.to raise_error ActiveRecord::RecordNotFound
      end
    end

    context "with invalid IDs" do
      it "fails with invalid User ID" do
        expect {
          aspects_membership_service.destroy_by_ids(@alice_aspect2.id, -1)
        }.to raise_error ActiveRecord::RecordNotFound
      end

      it "fails with invalid Aspect ID" do
        expect {
          aspects_membership_service.destroy_by_ids(-1, eve.person.id)
        }.to raise_error ActiveRecord::RecordNotFound
      end

      it "fails with aspect ID that isn't user's" do
        expect {
          aspects_membership_service(eve).destroy_by_ids(@alice_aspect2.id, bob.person.id)
        }.to raise_error ActiveRecord::RecordNotFound
      end
    end
  end

  describe "#list" do
    before do
      aspects_membership_service.create(@alice_aspect2.id, bob.person.id)
      aspects_membership_service.create(@alice_aspect2.id, eve.person.id)
      @alice_aspect3 = alice.aspects.create(name: "empty aspect")
    end

    context "with valid aspect ID" do
      it "returns users in full aspect" do
        contacts = aspects_membership_service.contacts_in_aspect(@alice_aspect2.id)
        expect(contacts.length).to eq(2)
        expect(contacts.map {|c| c.person.guid }.sort).to eq([bob.person.guid, eve.person.guid].sort)
      end

      it "returns empty array in empty aspect" do
        contacts = aspects_membership_service.contacts_in_aspect(@alice_aspect3.id)
        expect(contacts.length).to eq(0)
      end
    end

    context "with invalid aspect ID" do
      it "fails" do
        expect {
          aspects_membership_service.contacts_in_aspect(-1)
        }.to raise_error ActiveRecord::RecordNotFound
      end
    end
  end

  describe "#all_contacts" do
    before do
      aspects_membership_service.create(@alice_aspect2.id, bob.person.id)
      aspects_membership_service.create(@alice_aspect2.id, eve.person.id)
      @alice_aspect3 = alice.aspects.create(name: "empty aspect")
    end

    it "returns all user's contacts" do
      contacts = aspects_membership_service.all_contacts
      expect(contacts.length).to eq(2)
    end
  end

  def aspects_membership_service(user=alice)
    AspectsMembershipService.new(user)
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class LikeService
  def initialize(user=nil)
    @user = user
  end

  def create_for_post(post_id)
    post = post_service.find!(post_id)
    user.like!(post)
  end

  def create_for_comment(comment_id)
    comment = comment_service.find!(comment_id)
    post_service.find!(comment.commentable_id) # checks implicit for visible posts
    user.like_comment!(comment)
  end

  def destroy(like_id)
    like = Like.find(like_id)
    if user.owns?(like)
      user.retract(like)
      true
    else
      false
    end
  end

  def find_for_post(post_id)
    likes = post_service.find!(post_id).likes
    user ? likes.order(Arel.sql("author_id = #{user.person.id} DESC")) : likes
  end

  def find_for_comment(comment_id)
    comment = comment_service.find!(comment_id)
    post_service.find!(comment.post.id) # checks implicit for visible posts
    likes = comment.likes
    user ? likes.order(Arel.sql("author_id = #{user.person.id} DESC")) : likes
  end

  def unlike_post(post_id)
    likes = post_service.find!(post_id).likes
    likes = likes.order(Arel.sql("author_id = #{user.person.id} DESC"))
    if !likes.empty? && user.owns?(likes[0])
      user.retract(likes[0])
      true
    else
      false
    end
  end

  def unlike_comment(comment_id)
    likes = comment_service.find!(comment_id).likes
    likes = likes.order(Arel.sql("author_id = #{user.person.id} DESC"))
    if !likes.empty? && user.owns?(likes[0])
      user.retract(likes[0])
      true
    else
      false
    end
  end

  private

  attr_reader :user

  def post_service
    @post_service ||= PostService.new(user)
  end

  def comment_service
    @comment_service ||= CommentService.new(user)
  end
end

```

Response:
# frozen_string_literal: true

describe LikeService do
  let(:post) { alice.post(:status_message, text: "hello", to: alice.aspects.first) }
  let(:alice_comment) { CommentService.new(alice).create(post.id, "This is a wonderful post") }
  let(:bobs_comment) { CommentService.new(bob).create(post.id, "My post was better than yours") }

  describe "#create_for_post" do
    it "creates a like on my own post" do
      expect {
        LikeService.new(alice).create_for_post(post.id)
      }.not_to raise_error
    end

    it "creates a like on a post of a contact" do
      expect {
        LikeService.new(bob).create_for_post(post.id)
      }.not_to raise_error
    end

    it "attaches the like to the post" do
      like = LikeService.new(alice).create_for_post(post.id)
      expect(post.likes.first.id).to eq(like.id)
    end

    it "fails if the post does not exist" do
      expect {
        LikeService.new(bob).create_for_post("unknown id")
      }.to raise_error ActiveRecord::RecordNotFound
    end

    it "fails if the user can't see the post" do
      expect {
        LikeService.new(eve).create_for_post(post.id)
      }.to raise_error ActiveRecord::RecordNotFound
    end

    it "fails if the user already liked the post" do
      LikeService.new(alice).create_for_post(post.id)
      expect {
        LikeService.new(alice).create_for_post(post.id)
      }.to raise_error ActiveRecord::RecordInvalid
    end
  end

  describe "#create_for_comment" do
    it "creates a like on a posts comment" do
      expect {
        LikeService.new(alice).create_for_comment(alice_comment.id)
      }.not_to raise_error
    end

    it "creates a like on someone else comment" do
      expect {
        LikeService.new(alice).create_for_comment(bobs_comment.id)
      }.not_to raise_error
    end

    it "attaches the like to the comment" do
      like = LikeService.new(alice).create_for_comment(bobs_comment.id)
      expect(bobs_comment.likes.first.id).to eq(like.id)
    end

    it "fails if comment does not exist" do
      expect {
        LikeService.new(alice).create_for_comment("unknown_id")
      }.to raise_error ActiveRecord::RecordNotFound
    end

    it "fails if user cant see post and its comments" do
      expect {
        LikeService.new(eve).create_for_comment(bobs_comment.id)
      }.to raise_error ActiveRecord::RecordNotFound
    end

    it "fails if user already liked the comment" do
      LikeService.new(alice).create_for_comment(bobs_comment.id)
      expect {
        LikeService.new(alice).create_for_comment(bobs_comment.id)
      }.to raise_error ActiveRecord::RecordInvalid
    end
  end

  describe "#destroy" do
    context "for post like" do
      let(:like) { LikeService.new(bob).create_for_post(post.id) }

      it "lets the user destroy their own like" do
        result = LikeService.new(bob).destroy(like.id)
        expect(result).to be_truthy
      end

      it "doesn't let the parent author destroy others likes" do
        result = LikeService.new(alice).destroy(like.id)
        expect(result).to be_falsey
      end

      it "doesn't let someone destroy others likes" do
        result = LikeService.new(eve).destroy(like.id)
        expect(result).to be_falsey
      end

      it "fails if the like doesn't exist" do
        expect {
          LikeService.new(bob).destroy("unknown id")
        }.to raise_error ActiveRecord::RecordNotFound
      end
    end

    context "for comment like" do
      let(:like) { LikeService.new(bob).create_for_comment(alice_comment.id) }

      it "let the user destroy its own comment like" do
        result = LikeService.new(bob).destroy(like.id)
        expect(result).to be_truthy
      end

      it "doesn't let the parent author destroy other comment likes" do
        result = LikeService.new(alice).destroy(like.id)
        expect(result).to be_falsey
      end

      it "fails if the like doesn't exist" do
        expect {
          LikeService.new(alice).destroy("unknown id")
        }.to raise_error ActiveRecord::RecordNotFound
      end
    end
  end

  describe "#find_for_post" do
    context "with user" do
      it "returns likes for a public post" do
        post = alice.post(:status_message, text: "hello", public: true)
        like = LikeService.new(alice).create_for_post(post.id)
        expect(LikeService.new(eve).find_for_post(post.id)).to include(like)
      end

      it "returns likes for a visible private post" do
        like = LikeService.new(alice).create_for_post(post.id)
        expect(LikeService.new(bob).find_for_post(post.id)).to include(like)
      end

      it "doesn't return likes for a private post the user can not see" do
        LikeService.new(alice).create_for_post(post.id)
        expect {
          LikeService.new(eve).find_for_post(post.id)
        }.to raise_error ActiveRecord::RecordNotFound
      end

      it "returns the user's like first" do
        post = alice.post(:status_message, text: "hello", public: true)
        [alice, bob, eve].map {|user| LikeService.new(user).create_for_post(post.id) }

        [alice, bob, eve].each do |user|
          expect(
            LikeService.new(user).find_for_post(post.id).first.author.id
          ).to be user.person.id
        end
      end
    end

    context "without user" do
      it "returns likes for a public post" do
        post = alice.post(:status_message, text: "hello", public: true)
        like = LikeService.new(alice).create_for_post(post.id)
        expect(LikeService.new.find_for_post(post.id)).to include(like)
      end

      it "doesn't return likes a for private post" do
        LikeService.new(alice).create_for_post(post.id)
        expect {
          LikeService.new.find_for_post(post.id)
        }.to raise_error Diaspora::NonPublic
      end
    end

    it "returns all likes of a post" do
      post = alice.post(:status_message, text: "hello", public: true)
      likes = [alice, bob, eve].map {|user| LikeService.new(user).create_for_post(post.id) }

      expect(LikeService.new.find_for_post(post.id)).to match_array(likes)
    end
  end

  describe "#find_for_comment" do
    context "with user" do
      it "returns likes for a public post comment" do
        post = alice.post(:status_message, text: "hello", public: true)
        comment = CommentService.new(bob).create(post.id, "Hello comment")
        like = LikeService.new(alice).create_for_comment(comment.id)
        expect(LikeService.new(eve).find_for_comment(comment.id)).to include(like)
      end

      it "returns likes for visible private post comments" do
        comment = CommentService.new(bob).create(post.id, "Hello comment")
        like = LikeService.new(alice).create_for_comment(comment.id)
        expect(LikeService.new(bob).find_for_comment(comment.id)).to include(like)
      end

      it "doesn't return likes for a posts comment the user can not see" do
        expect {
          LikeService.new(eve).find_for_comment(alice_comment.id)
        }.to raise_error ActiveRecord::RecordNotFound
      end

      it "returns the user's like first" do
        post = alice.post(:status_message, text: "hello", public: true)
        comment = CommentService.new(alice).create(post.id, "I like my own post")

        [alice, bob, eve].map {|user| LikeService.new(user).create_for_comment(comment.id) }
        [alice, bob, eve].each do |user|
          expect(
            LikeService.new(user).find_for_comment(comment.id).first.author.id
          ).to be user.person.id
        end
      end
    end

    context "without user" do
      it "returns likes for a comment on a public post" do
        post = alice.post(:status_message, text: "hello", public: true)
        comment = CommentService.new(bob).create(post.id, "I like my own post")
        like = LikeService.new(alice).create_for_comment(comment.id)
        expect(
          LikeService.new.find_for_comment(comment.id)
        ).to include(like)
      end

      it "doesn't return likes for a private post comment" do
        LikeService.new(alice).create_for_comment(alice_comment.id)
        expect {
          LikeService.new.find_for_comment(alice_comment.id)
        }.to raise_error Diaspora::NonPublic
      end
    end
  end

  describe "#unlike_post" do
    before do
      LikeService.new(alice).create_for_post(post.id)
    end

    it "removes the like to the post" do
      LikeService.new(alice).unlike_post(post.id)
      expect(post.likes.length).to eq(0)
    end
  end

  describe "#unlike_comment" do
    it "removes the like for a comment" do
      comment = CommentService.new(alice).create(post.id, "I like my own post")
      LikeService.new(alice).create_for_comment(comment.id)
      expect(comment.likes.length).to eq(1)

      LikeService.new(alice).unlike_comment(comment.id)
      comment = CommentService.new(alice).find!(comment.id)
      expect(comment.likes.length).to eq(0)
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class StatusMessageCreationService
  include Rails.application.routes.url_helpers

  def initialize(user)
    @user = user
  end

  def create(params)
    validate_content(params)

    build_status_message(params).tap do |status_message|
      load_aspects(params[:aspect_ids]) unless status_message.public?
      add_attachments(status_message, params)
      status_message.save
      process(status_message, params[:services])
    end
  end

  private

  attr_reader :user, :aspects

  def validate_content(params)
    raise MissingContent unless params[:status_message][:text].present? || params[:photos].present?
  end

  def build_status_message(params)
    public = params[:public] || false
    user.build_post(:status_message, params[:status_message].merge(public: public))
  end

  def add_attachments(status_message, params)
    add_location(status_message, params[:location_address], params[:location_coords])
    add_poll(status_message, params)
    add_photos(status_message, params[:photos])
  end

  def add_location(status_message, address, coordinates)
    status_message.build_location(address: address, coordinates: coordinates) if address.present?
  end

  def add_poll(status_message, params)
    if params[:poll_question].present?
      status_message.build_poll(question: params[:poll_question])
      [*params[:poll_answers]].each do |poll_answer|
        answer = status_message.poll.poll_answers.build(answer: poll_answer)
        answer.poll = status_message.poll
      end
    end
  end

  def add_photos(status_message, photos)
    if photos.present?
      status_message.photos << Photo.where(id: photos, author_id: status_message.author_id)
      status_message.photos.each do |photo|
        photo.public = status_message.public
        photo.pending = false
      end
    end
  end

  def load_aspects(aspect_ids)
    @aspects = user.aspects_from_ids(aspect_ids)
    raise BadAspectsIDs if aspects.empty?
  end

  def process(status_message, services)
    add_to_streams(status_message) unless status_message.public?
    dispatch(status_message, services)
  end

  def add_to_streams(status_message)
    user.add_to_streams(status_message, aspects)
    status_message.photos.each {|photo| user.add_to_streams(photo, aspects) }
  end

  def dispatch(status_message, services)
    receiving_services = services ? Service.titles(services) : []
    status_message.filter_mentions # this is only required until changes from #6818 are deployed on every pod
    user.dispatch_post(status_message,
                       url:           short_post_url(status_message.guid, host: AppConfig.environment.url),
                       service_types: receiving_services)
  end

  class BadAspectsIDs < RuntimeError
  end

  class MissingContent < RuntimeError
  end
end

```

Response:
# frozen_string_literal: true

describe StatusMessageCreationService do
  describe "#create" do
    let(:aspect) { alice.aspects.first }
    let(:text) { "I'm writing tests" }
    let(:params) {
      {
        status_message: {text: text},
        aspect_ids:     [aspect.id.to_s]
      }
    }

    it "returns the created StatusMessage" do
      status_message = StatusMessageCreationService.new(alice).create(params)
      expect(status_message).to_not be_nil
      expect(status_message.text).to eq(text)
    end

    context "with aspect_ids" do
      it "creates aspect_visibilities for the StatusMessages" do
        alice.aspects.create(name: "another aspect")

        status_message = StatusMessageCreationService.new(alice).create(params)
        expect(status_message.aspect_visibilities.map(&:aspect)).to eq([aspect])
      end

      it "does not create aspect_visibilities if the post is public" do
        status_message = StatusMessageCreationService.new(alice).create(params.merge(public: true))
        expect(status_message.aspect_visibilities).to be_empty
      end

      it "raises exception if aspects_ids don't contain any applicable aspect identifiers" do
        bad_ids = [Aspect.ids.max.next, bob.aspects.first.id].map(&:to_s)
        expect {
          StatusMessageCreationService.new(alice).create(params.merge(aspect_ids: bad_ids))
        }.to remain(StatusMessage, :count).and raise_error(StatusMessageCreationService::BadAspectsIDs)
      end
    end

    context "with public" do
      it "it creates a private StatusMessage by default" do
        status_message = StatusMessageCreationService.new(alice).create(params)
        expect(status_message.public).to be_falsey
      end

      it "it creates a private StatusMessage" do
        status_message = StatusMessageCreationService.new(alice).create(params.merge(public: false))
        expect(status_message.public).to be_falsey
      end

      it "it creates a public StatusMessage" do
        status_message = StatusMessageCreationService.new(alice).create(params.merge(public: true))
        expect(status_message.public).to be_truthy
      end
    end

    context "with location" do
      it "it creates a location" do
        location_params = {location_address: "somewhere", location_coords: "1,2"}
        status_message = StatusMessageCreationService.new(alice).create(params.merge(location_params))
        location = status_message.location
        expect(location.address).to eq("somewhere")
        expect(location.lat).to eq("1")
        expect(location.lng).to eq("2")
      end

      it "does not add a location without location params" do
        status_message = StatusMessageCreationService.new(alice).create(params)
        expect(status_message.location).to be_nil
      end
    end

    context "with poll" do
      it "it creates a poll" do
        poll_params = {poll_question: "something?", poll_answers: %w(yes no maybe)}
        status_message = StatusMessageCreationService.new(alice).create(params.merge(poll_params))
        poll = status_message.poll
        expect(poll.question).to eq("something?")
        expect(poll.poll_answers.size).to eq(3)
        poll_answers = poll.poll_answers.map(&:answer)
        expect(poll_answers).to include("yes")
        expect(poll_answers).to include("no")
        expect(poll_answers).to include("maybe")
      end

      it "does not add a poll without poll params" do
        status_message = StatusMessageCreationService.new(alice).create(params)
        expect(status_message.poll).to be_nil
      end
    end

    context "with photos" do
      let(:photo1) {
        alice.build_post(:photo, pending: true, user_file: File.open(photo_fixture_name), to: aspect.id).tap(&:save!)
      }
      let(:photo2) {
        alice.build_post(:photo, pending: true, user_file: File.open(photo_fixture_name), to: aspect.id).tap(&:save!)
      }
      let(:photo_ids) { [photo1.id.to_s, photo2.id.to_s] }

      it "it attaches all photos" do
        status_message = StatusMessageCreationService.new(alice).create(params.merge(photos: photo_ids))
        photos = status_message.photos
        expect(photos.size).to eq(2)
        expect(photos.map(&:id).map(&:to_s)).to match_array(photo_ids)
      end

      it "does not attach photos without photos param" do
        status_message = StatusMessageCreationService.new(alice).create(params)
        expect(status_message.photos).to be_empty
      end

      context "with aspect_ids" do
        it "it marks the photos as non-public if the post is non-public" do
          status_message = StatusMessageCreationService.new(alice).create(params.merge(photos: photo_ids))
          status_message.photos.each do |photo|
            expect(photo.public).to be_falsey
          end
        end

        it "creates aspect_visibilities for the Photo" do
          alice.aspects.create(name: "another aspect")

          status_message = StatusMessageCreationService.new(alice).create(params.merge(photos: photo_ids))
          status_message.photos.each do |photo|
            expect(photo.aspect_visibilities.map(&:aspect)).to eq([aspect])
          end
        end

        it "does not create aspect_visibilities if the post is public" do
          status_message = StatusMessageCreationService.new(alice).create(params.merge(photos: photo_ids, public: true))
          status_message.photos.each do |photo|
            expect(photo.aspect_visibilities).to be_empty
          end
        end

        it "sets pending to false on any attached photos" do
          status_message = StatusMessageCreationService.new(alice).create(params.merge(photos: photo_ids))
          status_message.photos.each do |photo|
            expect(photo.reload.pending).to be_falsey
          end
        end
      end

      context "with public" do
        it "it marks the photos as public if the post is public" do
          status_message = StatusMessageCreationService.new(alice).create(params.merge(photos: photo_ids, public: true))
          status_message.photos.each do |photo|
            expect(photo.public).to be_truthy
          end
        end

        it "sets pending to false on any attached photos" do
          status_message = StatusMessageCreationService.new(alice).create(params.merge(photos: photo_ids, public: true))
          status_message.photos.each do |photo|
            expect(photo.reload.pending).to be_falsey
          end
        end
      end
    end

    context "dispatch" do
      it "dispatches the StatusMessage" do
        expect(alice).to receive(:dispatch_post).with(instance_of(StatusMessage), hash_including(service_types: []))
        StatusMessageCreationService.new(alice).create(params)
      end

      it "dispatches the StatusMessage to services" do
        expect(alice).to receive(:dispatch_post)
          .with(instance_of(StatusMessage),
                hash_including(service_types: array_including(%w[Services::Tumblr Services::Twitter])))
        StatusMessageCreationService.new(alice).create(params.merge(services: %w[twitter tumblr]))
      end

      context "with mention" do
        let(:text) { text_mentioning(eve) }

        # this is only required until changes from #6818 are deployed on every pod
        it "filters out mentions from text attribute" do
          status_message = StatusMessageCreationService.new(alice).create(params)
          expect(status_message.text).not_to include(eve.diaspora_handle)
        end
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class PostService
  def initialize(user=nil)
    @user = user
  end

  def find(id)
    if user
      user.find_visible_shareable_by_id(Post, id)
    else
      Post.find_by_id_and_public(id, true)
    end
  end

  def find!(id_or_guid)
    if user
      find_non_public_by_guid_or_id_with_user!(id_or_guid)
    else
      find_public!(id_or_guid)
    end
  end

  def present_json
    PostPresenter.new(post, user)
  end

  def present_interactions_json
    PostInteractionPresenter.new(post, user)
  end

  def mark_user_notifications(post_id)
    return unless user
    mark_comment_reshare_like_notifications_read(post_id)
    mark_mention_notifications_read(post_id)
  end

  def destroy(post_id, private_allowed=true)
    post = if private_allowed
             find_non_public_by_guid_or_id_with_user!(post_id)
           else
             find_public!(post_id)
           end
    raise Diaspora::NotMine unless post.author == user.person

    user.retract(post)
  end

  def mentionable_in_comment(post_id, query)
    post = find!(post_id)
    Person
      .allowed_to_be_mentioned_in_a_comment_to(post)
      .where.not(id: user.person_id)
      .find_by_substring(query)
      .sort_for_mention_suggestion(post, user)
      .for_json
      .limit(15)
  end

  private

  attr_reader :user

  def find_public!(id_or_guid)
    Post.where(post_key(id_or_guid) => id_or_guid).first.tap do |post|
      raise ActiveRecord::RecordNotFound, "could not find a post with id #{id_or_guid}" unless post
      raise Diaspora::NonPublic unless post.public?
    end
  end

  def find_non_public_by_guid_or_id_with_user!(id_or_guid)
    user.find_visible_shareable_by_id(Post, id_or_guid, key: post_key(id_or_guid)).tap do |post|
      raise ActiveRecord::RecordNotFound, "could not find a post with id #{id_or_guid} for user #{user.id}" unless post
    end
  end

  # We can assume a guid is at least 16 characters long as we have guids set to hex(8) since we started using them.
  def post_key(id_or_guid)
    id_or_guid.to_s.length < 16 ? :id : :guid
  end

  def mark_comment_reshare_like_notifications_read(post_id)
    Notification.where(recipient_id: user.id, target_type: "Post", target_id: post_id, unread: true)
      .update_all(unread: false)
  end

  def mark_mention_notifications_read(post_id)
    mention_ids = Mention.where(
      mentions_container_id:   post_id,
      mentions_container_type: "Post",
      person_id:               user.person_id
    ).ids
    mention_ids.concat(mentions_in_comments_for_post(post_id).pluck(:id))

    Notification.where(recipient_id: user.id, target_type: "Mention", target_id: mention_ids, unread: true)
                .update_all(unread: false) if mention_ids.any?
  end

  def mentions_in_comments_for_post(post_id)
    Mention
      .joins("INNER JOIN comments ON mentions_container_id = comments.id AND mentions_container_type = 'Comment'")
      .where(comments: {commentable_id: post_id, commentable_type: "Post"})
  end
end

```

Response:
# frozen_string_literal: true

describe PostService do
  let(:post) { alice.post(:status_message, text: "ohai", to: alice.aspects.first) }
  let(:public) { alice.post(:status_message, text: "hey", public: true) }

  describe "#find" do
    context "with user" do
      it "returns the post, if it is the users post" do
        expect(PostService.new(alice).find(post.id)).to eq(post)
      end

      it "returns the post, if the user can see the it" do
        expect(PostService.new(bob).find(post.id)).to eq(post)
      end

      it "returns the post, if it is public" do
        expect(PostService.new(eve).find(public.id)).to eq(public)
      end

      it "does not return the post, if the post cannot be found" do
        expect(PostService.new(alice).find("unknown")).to be_nil
      end

      it "does not return the post, if user cannot see the post" do
        expect(PostService.new(eve).find(post.id)).to be_nil
      end
    end

    context "without user" do
      it "returns the post, if it is public" do
        expect(PostService.new.find(public.id)).to eq(public)
      end

      it "does not return the post, if the post is private" do
        expect(PostService.new.find(post.id)).to be_nil
      end

      it "does not return the post, if the post cannot be found" do
        expect(PostService.new.find("unknown")).to be_nil
      end
    end
  end

  describe "#find!" do
    context "with user" do
      it "returns the post, if it is the users post" do
        expect(PostService.new(alice).find!(post.id)).to eq(post)
      end

      it "works with guid" do
        expect(PostService.new(alice).find!(post.guid)).to eq(post)
      end

      it "returns the post, if the user can see the it" do
        expect(PostService.new(bob).find!(post.id)).to eq(post)
      end

      it "returns the post, if it is public" do
        expect(PostService.new(eve).find!(public.id)).to eq(public)
      end

      it "RecordNotFound if the post cannot be found" do
        expect {
          PostService.new(alice).find!("unknown")
        }.to raise_error ActiveRecord::RecordNotFound, "could not find a post with id unknown for user #{alice.id}"
      end

      it "RecordNotFound if user cannot see the post" do
        expect {
          PostService.new(eve).find!(post.id)
        }.to raise_error ActiveRecord::RecordNotFound, "could not find a post with id #{post.id} for user #{eve.id}"
      end
    end

    context "without user" do
      it "returns the post, if it is public" do
        expect(PostService.new.find!(public.id)).to eq(public)
      end

      it "works with guid" do
        expect(PostService.new.find!(public.guid)).to eq(public)
      end

      it "NonPublic if the post is private" do
        expect {
          PostService.new.find!(post.id)
        }.to raise_error Diaspora::NonPublic
      end

      it "RecordNotFound if the post cannot be found" do
        expect {
          PostService.new.find!("unknown")
        }.to raise_error ActiveRecord::RecordNotFound, "could not find a post with id unknown"
      end
    end

    context "id/guid switch" do
      let(:public) { alice.post(:status_message, text: "ohai", public: true) }

      it "assumes ids less than 16 chars are ids and not guids" do
        post = Post.where(id: public.id)
        expect(Post).to receive(:where).with(hash_including(id: "123456789012345")).and_return(post).at_least(:once)
        PostService.new(alice).find!("123456789012345")
      end

      it "assumes ids more than (or equal to) 16 chars are actually guids" do
        post = Post.where(guid: public.guid)
        expect(Post).to receive(:where).with(hash_including(guid: "1234567890123456")).and_return(post).at_least(:once)
        PostService.new(alice).find!("1234567890123456")
      end
    end
  end

  describe "#mark_user_notifications" do
    let(:status_text) { text_mentioning(alice) }

    it "marks a corresponding notifications as read" do
      FactoryBot.create(:notification, recipient: alice, target: post, unread: true)
      FactoryBot.create(:notification, recipient: alice, target: post, unread: true)

      expect {
        PostService.new(alice).mark_user_notifications(post.id)
      }.to change(Notification.where(unread: true), :count).by(-2)
    end

    it "marks a corresponding mention notification as read" do
      mention_post = bob.post(:status_message, text: status_text, public: true)

      expect {
        PostService.new(alice).mark_user_notifications(mention_post.id)
      }.to change(Notification.where(unread: true), :count).by(-1)
    end

    it "marks a corresponding mention in comment notification as read" do
      notification = FactoryBot.create(:notification_mentioned_in_comment)
      status_message = notification.target.mentions_container.parent
      user = notification.recipient

      expect {
        PostService.new(user).mark_user_notifications(status_message.id)
      }.to change(Notification.where(unread: true), :count).by(-1)
    end

    it "does not change the update_at date/time for post notifications" do
      notification = Timecop.travel(1.minute.ago) do
        FactoryBot.create(:notification, recipient: alice, target: post, unread: true)
      end

      expect {
        PostService.new(alice).mark_user_notifications(post.id)
      }.not_to change { Notification.where(id: notification.id).pluck(:updated_at) }
    end

    it "does not change the update_at date/time for mention notifications" do
      mention_post = Timecop.travel(1.minute.ago) do
        bob.post(:status_message, text: status_text, public: true)
      end
      mention = mention_post.mentions.where(person_id: alice.person.id).first

      expect {
        PostService.new(alice).mark_user_notifications(post.id)
      }.not_to change { Notification.where(target_type: "Mention", target_id: mention.id).pluck(:updated_at) }
    end

    it "does nothing without a user" do
      expect_any_instance_of(PostService).not_to receive(:mark_comment_reshare_like_notifications_read).with(post.id)
      expect_any_instance_of(PostService).not_to receive(:mark_mention_notifications_read).with(post.id)
      PostService.new.mark_user_notifications(post.id)
    end
  end

  describe "#destroy" do
    it "let a user delete his message" do
      PostService.new(alice).destroy(post.id)
      expect(StatusMessage.find_by_id(post.id)).to be_nil
    end

    it "sends a retraction on delete" do
      expect(alice).to receive(:retract).with(post)
      PostService.new(alice).destroy(post.id)
    end

    it "won't delete private post if explicitly unallowed" do
      expect {
        PostService.new(alice).destroy(post.id, false)
      }.to raise_error Diaspora::NonPublic
      expect(StatusMessage.find_by(id: post.id)).not_to be_nil
    end

    it "will not let you destroy posts visible to you but that you do not own" do
      expect {
        PostService.new(bob).destroy(post.id)
      }.to raise_error Diaspora::NotMine
      expect(StatusMessage.find_by_id(post.id)).not_to be_nil
    end

    it "will not let you destroy posts that are not visible to you" do
      expect {
        PostService.new(eve).destroy(post.id)
      }.to raise_error(ActiveRecord::RecordNotFound)
      expect(StatusMessage.find_by_id(post.id)).not_to be_nil
    end
  end

  describe "#mentionable_in_comment" do
    describe "semi-integration test" do
      let(:post_author_attributes) { {first_name: "Ro#{r_str}"} }
      let(:post_author)  { FactoryBot.create(:person, post_author_attributes) }
      let(:current_user) { FactoryBot.create(:user_with_aspect) }
      let(:post_service) { PostService.new(current_user) }

      shared_context "with commenters and likers" do
        # randomize ids of the created people so that the test doesn't pass just because of
        # the id sequence matched against the expected ordering
        let(:ids) { (1..4).map {|i| Person.maximum(:id) + i }.shuffle }

        before do
          # in case when post_author has't been instantiated before this context, specify id
          # in order to avoid id conflict with the people generated here
          post_author_attributes.merge!(id: ids.max + 1)
        end

        let!(:commenter1) {
          FactoryBot.create(:person, id: ids.shift, first_name: "Ro1#{r_str}").tap {|person|
            FactoryBot.create(:comment, author: person, post: post)
          }
        }

        let!(:commenter2) {
          FactoryBot.create(:person, id: ids.shift, first_name: "Ro2#{r_str}").tap {|person|
            FactoryBot.create(:comment, author: person, post: post)
          }
        }

        let!(:liker1) {
          FactoryBot.create(:person, id: ids.shift, first_name: "Ro1#{r_str}").tap {|person|
            FactoryBot.create(:like, author: person, target: post)
          }
        }

        let!(:liker2) {
          FactoryBot.create(:person, id: ids.shift, first_name: "Ro2#{r_str}").tap {|person|
            FactoryBot.create(:like, author: person, target: post)
          }
        }
      end

      shared_context "with a current user's friend" do
        let!(:current_users_friend) {
          FactoryBot.create(:person).tap {|friend|
            current_user.contacts.create!(
              person:    friend,
              aspects:   [current_user.aspects.first],
              sharing:   true,
              receiving: true
            )
          }
        }
      end

      context "with private post" do
        let(:post) { FactoryBot.create(:status_message, text: "ohai", author: post_author) }

        context "when the post doesn't have a visibility for the current user" do
          it "doesn't find a post and raises an exception" do
            expect {
              post_service.mentionable_in_comment(post.id, "Ro")
            }.to raise_error(ActiveRecord::RecordNotFound)
          end
        end

        context "when the post has a visibility for the current user" do
          before do
            ShareVisibility.batch_import([current_user.id], post)
          end

          context "with commenters and likers" do
            include_context "with commenters and likers"

            it "returns mention suggestions in the correct order" do
              expected_suggestions = [
                post_author, commenter1, commenter2, liker1, liker2
              ]
              expect(post_service.mentionable_in_comment(post.id, "Ro")).to eq(expected_suggestions)
            end
          end

          context "with a current user's friend" do
            include_context "with a current user's friend"

            it "doesn't include a contact" do
              expect(post_service.mentionable_in_comment(post.id, current_users_friend.first_name)).to be_empty
            end
          end

          it "doesn't include a non contact" do
            expect(post_service.mentionable_in_comment(post.id, eve.person.first_name)).to be_empty
          end
        end
      end

      context "with public post" do
        let(:post) { FactoryBot.create(:status_message, text: "ohai", public: true, author: post_author) }

        context "with commenters and likers and with a current user's friend" do
          include_context "with commenters and likers"
          include_context "with a current user's friend"

          it "returns mention suggestions in the correct order" do
            result = post_service.mentionable_in_comment(post.id, "Ro").to_a
            expect(result.size).to be > 7
            # participants: post author, comments, likers
            expect(result[0..4]).to eq([post_author, commenter1, commenter2, liker1, liker2])
            # contacts
            expect(result[5]).to eq(current_users_friend)
            # non-contacts
            result[6..-1].each {|person|
              expect(person.contacts.where(user_id: current_user.id)).to be_empty
              expect(person.profile.first_name).to include("Ro")
            }
          end

          it "doesn't include people with non-matching names" do
            commenter = FactoryBot.create(:person, first_name: "RRR#{r_str}")
            FactoryBot.create(:comment, author: commenter)
            liker = FactoryBot.create(:person, first_name: "RRR#{r_str}")
            FactoryBot.create(:like, author: liker)
            friend = FactoryBot.create(:person, first_name: "RRR#{r_str}")
            current_user.contacts.create!(
              person:    friend,
              aspects:   [current_user.aspects.first],
              sharing:   true,
              receiving: true
            )

            result = post_service.mentionable_in_comment(post.id, "Ro")
            expect(result).not_to include(commenter)
            expect(result).not_to include(liker)
            expect(result).not_to include(friend)
          end
        end

        shared_examples "current user can't mention themself" do
          before do
            current_user.profile.update(first_name: "Ro#{r_str}")
          end

          it "doesn't include current user" do
            expect(post_service.mentionable_in_comment(post.id, "Ro")).not_to include(current_user.person)
          end
        end

        context "when current user is a post author" do
          let(:post_author) { current_user.person }

          include_examples "current user can't mention themself"
        end

        context "current user is a participant" do
          before do
            current_user.like!(post)
            current_user.comment!(post, "hello")
          end

          include_examples "current user can't mention themself"
        end

        context "current user is a stranger matching a search pattern" do
          include_examples "current user can't mention themself"
        end

        it "doesn't fail when the post author doesn't match the requested pattern" do
          expect(post_service.mentionable_in_comment(post.id, "#{r_str}#{r_str}#{r_str}")).to be_empty
        end

        it "renders a commenter with multiple comments only once" do
          person = FactoryBot.create(:person, first_name: "Ro2#{r_str}")
          2.times { FactoryBot.create(:comment, author: person, post: post) }
          expect(post_service.mentionable_in_comment(post.id, person.first_name).length).to eq(1)
        end
      end
    end

    describe "unit test" do
      let(:post_service) { PostService.new(alice) }

      before do
        expect(post_service).to receive(:find!).and_return(post)
      end

      it "calls Person.allowed_to_be_mentioned_in_a_comment_to" do
        expect(Person).to receive(:allowed_to_be_mentioned_in_a_comment_to).with(post).and_call_original
        post_service.mentionable_in_comment(post.id, "whatever")
      end

      it "calls Person.find_by_substring" do
        expect(Person).to receive(:find_by_substring).with("whatever").and_call_original
        post_service.mentionable_in_comment(post.id, "whatever")
      end

      it "calls Person.sort_for_mention_suggestion" do
        expect(Person).to receive(:sort_for_mention_suggestion).with(post, alice).and_call_original
        post_service.mentionable_in_comment(post.id, "whatever")
      end

      it "calls Person.limit" do
        16.times {
          FactoryBot.create(:comment, author: FactoryBot.create(:person, first_name: "Ro#{r_str}"), post: post)
        }
        expect(post_service.mentionable_in_comment(post.id, "Ro").length).to eq(15)
      end

      it "contains a constraint on a current user" do
        expect(Person).to receive(:allowed_to_be_mentioned_in_a_comment_to) { Person.all }
        expect(Person).to receive(:find_by_substring) { Person.all }
        expect(Person).to receive(:sort_for_mention_suggestion) { Person.all }
        expect(post_service.mentionable_in_comment(post.id, alice.person.first_name))
          .not_to include(alice.person)
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class NotificationService
  NOTIFICATION_TYPES = {
    Comment       => [Notifications::MentionedInComment, Notifications::CommentOnPost, Notifications::AlsoCommented],
    Like          => [Notifications::Liked, Notifications::LikedComment],
    StatusMessage => [Notifications::MentionedInPost],
    Conversation  => [Notifications::PrivateMessage],
    Message       => [Notifications::PrivateMessage],
    Reshare       => [Notifications::Reshared],
    Contact       => [Notifications::StartedSharing]
  }.freeze

  NOTIFICATIONS_JSON_TYPES = {
    "also_commented"       => "Notifications::AlsoCommented",
    "comment_on_post"      => "Notifications::CommentOnPost",
    "liked"                => "Notifications::Liked",
    "liked_comment"        => "Notifications::LikedComment",
    "mentioned"            => "Notifications::MentionedInPost",
    "mentioned_in_comment" => "Notifications::MentionedInComment",
    "reshared"             => "Notifications::Reshared",
    "started_sharing"      => "Notifications::StartedSharing",
    "contacts_birthday"    => "Notifications::ContactsBirthday"
  }.freeze

  NOTIFICATIONS_REVERSE_JSON_TYPES = NOTIFICATIONS_JSON_TYPES.invert.freeze

  def initialize(user=nil)
    @user = user
  end

  def index(unread_only=nil, only_after=nil)
    query_string = "recipient_id = ? "
    query_string += "AND unread = true " if unread_only
    where_clause = [query_string, @user.id]
    if only_after
      query_string += " AND created_at >= ?"
      where_clause = [query_string, @user.id, only_after]
    end
    Notification.where(where_clause).includes(:target, actors: :profile)
  end

  def get_by_guid(guid)
    Notification.where(recipient_id: @user.id, guid: guid).first
  end

  def update_status_by_guid(guid, is_read_status)
    notification = get_by_guid(guid)
    raise ActiveRecord::RecordNotFound unless notification

    notification.set_read_state(is_read_status)
    true
  end

  def notify(object, recipient_user_ids)
    notification_types(object).each {|type| type.notify(object, recipient_user_ids) }
  end

  private

  def notification_types(object)
    NOTIFICATION_TYPES.fetch(object.class, [])
  end
end

```

Response:
# frozen_string_literal: true

describe NotificationService do
  describe "notification interrelation" do
    context "with mention in comment" do
      let(:status_message) {
        FactoryBot.create(:status_message, public: true, author: alice.person).tap {|status_message|
          eve.comment!(status_message, "whatever")
        }
      }

      let(:comment) {
        FactoryBot.create(
          :comment,
          author: bob.person,
          text:   text_mentioning(alice, eve),
          post:   status_message
        )
      }

      it "sends only mention notification" do
        [alice, eve].each do |user|
          expect(Workers::Mail::MentionedInComment).to receive(:perform_async).with(
            user.id,
            bob.person.id,
            *comment.mentions.where(person: user.person).ids
          )
        end

        expect {
          NotificationService.new.notify(comment, [])
        }.to change { Notification.where(recipient_id: alice).count }.by(1)
          .and change { Notification.where(recipient_id: eve).count }.by(1)

        [alice, eve].each do |user|
          expect(
            Notifications::MentionedInComment.where(target: comment.mentions, recipient_id: user.id)
          ).to exist

          expect(
            Notifications::CommentOnPost.where(target: comment.parent, recipient_id: user.id)
          ).not_to exist

          expect(
            Notifications::AlsoCommented.where(target: comment.parent, recipient_id: user.id)
          ).not_to exist
        end
      end

      context "with \"mentioned in comment\" email turned off" do
        before do
          alice.user_preferences.create(email_type: "mentioned_in_comment")
          eve.user_preferences.create(email_type: "mentioned_in_comment")
        end

        it "calls appropriate mail worker instead" do
          expect(Workers::Mail::MentionedInComment).not_to receive(:perform_async)

          expect(Workers::Mail::CommentOnPost).to receive(:perform_async).with(
            alice.id,
            bob.person.id,
            *comment.mentions.where(person: alice.person).ids
          )

          expect(Workers::Mail::AlsoCommented).to receive(:perform_async).with(
            eve.id,
            bob.person.id,
            *comment.mentions.where(person: eve.person).ids
          )

          NotificationService.new.notify(comment, [])
        end
      end
    end
  end

  describe "query methods" do
    before do
      @post = alice.post(
        :status_message,
        text:   "This is a status message",
        public: true,
        to:     "all"
      )
      @notification = FactoryBot.create(:notification, recipient: alice, target: @post)
      @service = NotificationService.new(alice)
    end

    describe "#index" do
      it "gets all" do
        notifications = @service.index
        expect(notifications.length).to eq(1)
      end

      it "gets unread only" do
        notifications = @service.index(true)
        expect(notifications.length).to eq(1)
        @notification.set_read_state(true)
        notifications = @service.index(true)
        expect(notifications.length).to eq(0)
      end

      it "gets only after" do
        notifications = @service.index(nil, (Time.current - 1.day))
        expect(notifications.length).to eq(1)
        @notification.set_read_state(true)
        notifications = @service.index(nil, (Time.current + 1.day))
        expect(notifications.length).to eq(0)
      end

      it "combined filtering" do
        notifications = @service.index(true, (Time.current - 1.day))
        expect(notifications.length).to eq(1)
      end
    end

    describe "#show" do
      it "succeeds with valid GUID" do
        notification = @service.get_by_guid(@notification.guid)
        expect(notification).not_to be_nil
      end
    end

    describe "#update" do
      it "succeeds with valid GUID" do
        expect(@service.update_status_by_guid(@notification.guid, true)).to be_truthy
        expect(@notification.reload.unread).to eq(false)
        expect(@service.update_status_by_guid(@notification.guid, false)).to be_truthy
        expect(@notification.reload.unread).to eq(true)
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

# Encapsulates logic of processing diaspora:// links
class DiasporaLinkService
  attr_reader :type, :author, :guid

  def initialize(link)
    @link = link.dup
    parse
  end

  def find_or_fetch_entity
    if type && guid
      entity_finder.find || fetch_entity
    elsif author
      find_or_fetch_person
    end
  end

  private

  attr_accessor :link

  def fetch_entity
    DiasporaFederation::Federation::Fetcher.fetch_public(author, type, guid)
    entity_finder.find
  rescue DiasporaFederation::Federation::Fetcher::NotFetchable
    nil
  end

  def entity_finder
    @entity_finder ||= Diaspora::EntityFinder.new(type, guid)
  end

  def find_or_fetch_person
    Person.find_or_fetch_by_identifier(author)
  rescue DiasporaFederation::Discovery::DiscoveryError
    nil
  end

  def normalize
    link.gsub!(%r{^web\+diaspora://}, "diaspora://") ||
      link.gsub!(%r{^//}, "diaspora://") ||
      %r{^diaspora://}.match(link) ||
      self.link = "diaspora://#{link}"
  end

  def parse
    normalize
    match = DiasporaFederation::Federation::DiasporaUrlParser::DIASPORA_URL_REGEX.match(link)
    if match
      @author, @type, @guid = match.captures
    else
      @author = %r{^diaspora://(#{Validation::Rule::DiasporaId::DIASPORA_ID_REGEX})$}u.match(link)&.captures&.first
    end
  end
end

```

Response:
# frozen_string_literal: true

describe DiasporaLinkService do
  let(:service) { described_class.new(link) }

  describe "#find_or_fetch_entity" do
    context "when entity is known" do
      let(:post) { FactoryBot.create(:status_message) }
      let(:link) { "diaspora://#{post.author.diaspora_handle}/post/#{post.guid}" }

      it "returns the entity" do
        expect(service.find_or_fetch_entity).to eq(post)
      end
    end

    context "when entity is unknown" do
      let(:remote_person) { FactoryBot.create(:person) }
      let(:guid) { "1234567890abcdef" }
      let(:link) { "diaspora://#{remote_person.diaspora_handle}/post/#{guid}" }

      it "fetches entity" do
        expect(DiasporaFederation::Federation::Fetcher)
          .to receive(:fetch_public)
            .with(remote_person.diaspora_handle, "post", guid) {
              FactoryBot.create(:status_message, author: remote_person, guid: guid)
            }

        entity = service.find_or_fetch_entity
        expect(entity).to be_a(StatusMessage)
        expect(entity.guid).to eq(guid)
        expect(entity.author).to eq(remote_person)
      end

      it "returns nil when entity is non fetchable" do
        expect(DiasporaFederation::Federation::Fetcher)
          .to receive(:fetch_public)
          .with(remote_person.diaspora_handle, "post", guid)
          .and_raise(DiasporaFederation::Federation::Fetcher::NotFetchable)

        expect(service.find_or_fetch_entity).to be_nil
      end
    end

    context "with invalid links" do
      it "returns nil when the link is invalid" do
        service = described_class.new("web+diaspora://something_invalid")
        expect(service.find_or_fetch_entity).to be_nil
      end

      it "returns nil when the author is valid, but rest of the link is invalid" do
        service = described_class.new("web+diaspora://#{alice.diaspora_handle}/foo/bar")
        expect(service.find_or_fetch_entity).to be_nil
      end
    end

    context "with only a diaspora ID" do
      let(:person) { FactoryBot.create(:person) }
      let(:link) { "diaspora://#{person.diaspora_handle}" }

      it "returns the person" do
        expect(service.find_or_fetch_entity).to eq(person)
      end

      it "returns nil when person is non fetchable" do
        expect(Person).to receive(:find_or_fetch_by_identifier)
          .with(person.diaspora_handle).and_raise(DiasporaFederation::Discovery::DiscoveryError)

        expect(service.find_or_fetch_entity).to be_nil
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class PollParticipationService
  def initialize(user)
    @user = user
  end

  def vote(post_id, answer_id)
    answer = PollAnswer.find(answer_id)
    @user.participate_in_poll!(target(post_id), answer) if target(post_id)
  end

  private

  def target(post_id)
    @target ||= @user.find_visible_shareable_by_id(Post, post_id) || raise(ActiveRecord::RecordNotFound.new)
  end
end

```

Response:
# frozen_string_literal: true

describe PollParticipationService do
  let(:poll_post) { FactoryBot.create(:status_message_with_poll, public: true) }
  let(:poll_answer) { poll_post.poll.poll_answers.first }

  describe "voting on poll" do
    it "succeeds" do
      expect(poll_service.vote(poll_post.id, poll_answer.id)).not_to be_nil
    end

    it "fails to vote twice" do
      expect(poll_service.vote(poll_post.id, poll_answer.id)).not_to be_nil
      expect { poll_service.vote(poll_post.id, poll_answer.id) }.to raise_error(ActiveRecord::RecordInvalid)
    end

    it "fails with bad answer id" do
      expect { poll_service.vote(poll_post.id, -2) }.to raise_error(ActiveRecord::RecordNotFound)
    end

    it "fails with bad post id" do
      expect { poll_service.vote(-1, poll_answer.id) }.to raise_error(ActiveRecord::RecordNotFound)
    end
  end

  def poll_service(user=alice)
    PollParticipationService.new(user)
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class ReshareService
  def initialize(user=nil)
    @user = user
  end

  def create(post_id)
    post = post_service.find!(post_id)
    post = post.absolute_root if post.is_a? Reshare
    user.reshare!(post)
  end

  def find_for_post(post_id)
    reshares = post_service.find!(post_id).reshares
    user ? reshares.order(Arel.sql("author_id = #{user.person.id} DESC")) : reshares
  end

  private

  attr_reader :user

  def post_service
    @post_service ||= PostService.new(user)
  end
end

```

Response:
# frozen_string_literal: true

describe ReshareService do
  let(:post) { alice.post(:status_message, text: "hello", public: true) }

  describe "#create" do
    it "doesn't create a reshare of my own post" do
      expect {
        ReshareService.new(alice).create(post.id)
      }.to raise_error RuntimeError
    end

    it "creates a reshare of a post of a contact" do
      expect {
        ReshareService.new(bob).create(post.id)
      }.not_to raise_error
    end

    it "attaches the reshare to the post" do
      reshare = ReshareService.new(bob).create(post.id)
      expect(post.reshares.first.id).to eq(reshare.id)
    end

    it "reshares the original post when called with a reshare" do
      reshare = ReshareService.new(bob).create(post.id)
      reshare2 = ReshareService.new(eve).create(reshare.id)
      expect(post.reshares.map(&:id)).to include(reshare2.id)
    end

    it "fails if the post does not exist" do
      expect {
        ReshareService.new(bob).create("unknown id")
      }.to raise_error ActiveRecord::RecordNotFound
    end

    it "fails if the post is not public" do
      post = alice.post(:status_message, text: "hello", to: alice.aspects.first)

      expect {
        ReshareService.new(bob).create(post.id)
      }.to raise_error ActiveRecord::RecordInvalid
    end

    it "fails if the user already reshared the post" do
      ReshareService.new(bob).create(post.id)
      expect {
        ReshareService.new(bob).create(post.id)
      }.to raise_error ActiveRecord::RecordInvalid
    end

    it "fails if the user already reshared the original post" do
      reshare = ReshareService.new(bob).create(post.id)
      expect {
        ReshareService.new(bob).create(reshare.id)
      }.to raise_error ActiveRecord::RecordInvalid
    end
  end

  describe "#find_for_post" do
    context "with user" do
      it "returns reshares for a public post" do
        reshare = ReshareService.new(bob).create(post.id)
        expect(ReshareService.new(eve).find_for_post(post.id)).to include(reshare)
      end

      it "returns reshares for a visible private post" do
        post = alice.post(:status_message, text: "hello", to: alice.aspects.first)
        expect(ReshareService.new(bob).find_for_post(post.id)).to be_empty
      end

      it "doesn't return reshares for a private post the user can not see" do
        post = alice.post(:status_message, text: "hello", to: alice.aspects.first)
        expect {
          ReshareService.new(eve).find_for_post(post.id)
        }.to raise_error ActiveRecord::RecordNotFound
      end

      it "returns the user's reshare first" do
        [bob, eve].map {|user| ReshareService.new(user).create(post.id) }

        [bob, eve].each do |user|
          expect(
            ReshareService.new(user).find_for_post(post.id).first.author.id
          ).to be user.person.id
        end
      end
    end

    context "without user" do
      it "returns reshares for a public post" do
        reshare = ReshareService.new(bob).create(post.id)
        expect(ReshareService.new.find_for_post(post.id)).to include(reshare)
      end

      it "doesn't return reshares a for private post" do
        post = alice.post(:status_message, text: "hello", to: alice.aspects.first)
        expect {
          ReshareService.new.find_for_post(post.id)
        }.to raise_error Diaspora::NonPublic
      end
    end

    it "returns all reshares of a post" do
      reshares = [bob, eve].map {|user| ReshareService.new(user).create(post.id) }

      expect(ReshareService.new.find_for_post(post.id)).to match_array(reshares)
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class PhotoService
  def initialize(user=nil, deny_raw_files=true)
    @user = user
    @deny_raw_files = deny_raw_files
  end

  def visible_photo(photo_guid)
    Photo.owned_or_visible_by_user(@user).where(guid: photo_guid).first
  end

  def create_from_params_and_file(base_params, uploaded_file)
    photo_params = build_params(base_params)
    raise RuntimeError if @deny_raw_files && !confirm_uploaded_file_settings(uploaded_file)

    photo_params[:user_file] = uploaded_file
    photo = @user.build_post(:photo, photo_params)
    raise RuntimeError unless photo.save

    send_messages(photo, photo_params)
    update_profile_photo(photo) if photo_params[:set_profile_photo]

    photo
  end

  private

  def build_params(base_params)
    photo_params = base_params.permit(:pending, :set_profile_photo, aspect_ids: [])
    if base_params.permit(:aspect_ids)[:aspect_ids] == "all"
      photo_params[:aspect_ids] = @user.aspects.map(&:id)
    elsif photo_params[:aspect_ids].is_a?(Hash)
      photo_params[:aspect_ids] = params[:photo][:aspect_ids].values
    end
    photo_params
  end

  def confirm_uploaded_file_settings(uploaded_file)
    unless uploaded_file.is_a?(ActionDispatch::Http::UploadedFile) || uploaded_file.is_a?(Rack::Test::UploadedFile)
      return false
    end
    return false if uploaded_file.original_filename.empty?

    return false if uploaded_file.content_type.empty?

    true
  end

  def send_messages(photo, photo_params)
    send_to_streams(photo, photo_params) unless photo.pending && photo.public?

    @user.dispatch_post(photo, to: photo_params[:aspect_ids]) unless photo.pending
  end

  def update_profile_photo(photo)
    @user.update_profile(photo: photo)
  end

  def send_to_streams(photo, photo_params)
    aspects = @user.aspects_from_ids(photo_params[:aspect_ids])
    @user.add_to_streams(photo, aspects)
  end
end

```

Response:
# frozen_string_literal: true

describe PhotoService do
  before do
    alice_eve_spec = alice.aspects.create(name: "eve aspect")
    alice.share_with(eve.person, alice_eve_spec)
    alice_bob_spec = alice.aspects.create(name: "bob aspect")
    alice.share_with(bob.person, alice_bob_spec)
    @alice_eve_photo = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name),
                                 to: alice_eve_spec.id)
    @alice_bob_photo = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name),
                                 to: alice_bob_spec.id)
    @alice_public_photo = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name), public: true)
    @bob_photo1 = bob.post(:photo, pending: true, user_file: File.open(photo_fixture_name), public: true)
  end

  describe "visible_photo" do
    it "returns a user's photo" do
      photo = photo_service.visible_photo(@bob_photo1.guid)
      expect(photo.guid).to eq(@bob_photo1.guid)
    end

    it "returns another user's public photo" do
      photo = photo_service.visible_photo(@alice_public_photo.guid)
      expect(photo.guid).to eq(@alice_public_photo.guid)
    end

    it "returns another user's shared photo" do
      photo = photo_service.visible_photo(@alice_bob_photo.guid)
      expect(photo.guid).to eq(@alice_bob_photo.guid)
    end

    it "returns nil for other user's private photo" do
      photo = photo_service.visible_photo(@alice_eve_photo.guid)
      expect(photo).to be_nil
    end
  end

  describe "create" do
    before do
      @image_file = Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "button.png").to_s, "image/png")
    end

    context "succeeds" do
      it "accepts a photo from a regular form uploaded file no parameters" do
        params = ActionController::Parameters.new
        photo = photo_service.create_from_params_and_file(params, @image_file)
        expect(photo).not_to be_nil
        expect(photo.pending?).to be_falsey
        expect(photo.public?).to be_falsey
      end

      it "honors pending" do
        params = ActionController::Parameters.new(pending: true)
        photo = photo_service.create_from_params_and_file(params, @image_file)
        expect(photo).not_to be_nil
        expect(photo.pending?).to be_truthy
        expect(photo.public?).to be_falsey
      end

      it "sets a user profile when requested" do
        original_profile_pic = bob.person.profile.image_url
        params = ActionController::Parameters.new(set_profile_photo: true)
        photo = photo_service.create_from_params_and_file(params, @image_file)
        expect(photo).not_to be_nil
        expect(bob.reload.person.profile.image_url).not_to eq(original_profile_pic)
      end

      it "has correct aspects settings for limited shared" do
        params = ActionController::Parameters.new(pending: false, aspect_ids: [bob.aspects.first.id])
        photo = photo_service.create_from_params_and_file(params, @image_file)
        expect(photo).not_to be_nil
        expect(photo.pending?).to be_falsey
        expect(photo.public?).to be_falsey
      end

      it "allow raw file if explicitly allowing" do
        params = ActionController::Parameters.new
        photo = photo_service(bob, false).create_from_params_and_file(params, uploaded_photo)
        expect(photo).not_to be_nil
      end
    end

    context "fails" do
      before do
        @params = ActionController::Parameters.new
      end

      it "fails if given a raw file" do
        expect {
          photo_service.create_from_params_and_file(@params, uploaded_photo)
        }.to raise_error RuntimeError
      end

      it "file type isn't an image" do
        text_file = Rack::Test::UploadedFile.new(Rails.root.join("README.md").to_s, "text/plain")
        expect {
          photo_service.create_from_params_and_file(@params, text_file)
        }.to raise_error CarrierWave::IntegrityError

        text_file = Rack::Test::UploadedFile.new(Rails.root.join("README.md").to_s, "image/png")
        expect {
          photo_service.create_from_params_and_file(@params, text_file)
        }.to raise_error CarrierWave::IntegrityError
      end
    end
  end

  def photo_service(user=bob, deny_raw_files=true)
    PhotoService.new(user, deny_raw_files)
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class ConversationService
  def initialize(user=nil)
    @user = user
  end

  def all_for_user(filter={})
    conversation_filter = {}
    unless filter[:only_after].nil?
      conversation_filter = \
        "conversations.created_at >= ?", filter[:only_after]
    end

    visibility_filter = if filter[:unread]
                          {
                            person_id: @user.person_id,
                            unread:    1
                          }
                        else
                          {person_id: @user.person_id}
                        end

    Conversation.where(conversation_filter)
                .joins(:conversation_visibilities)
                .where(conversation_visibilities: visibility_filter)
                .all
  end

  def build(subject, text, recipients)
    person_ids = @user.contacts
                      .mutual
                      .where(person_id: recipients)
                      .pluck(:person_id)

    opts = {
      subject:         subject,
      message:         {text: text},
      participant_ids: person_ids
    }

    @user.build_conversation(opts)
  end

  def find!(conversation_guid)
    conversation = Conversation.find_by!(guid: conversation_guid)
    @user.conversations
         .joins(:conversation_visibilities)
         .where(conversation_visibilities: {
                  person_id:       @user.person_id,
                  conversation_id: conversation.id
                }).first!
  end

  def destroy!(conversation_guid)
    conversation = find!(conversation_guid)
    conversation.destroy!
  end

  def get_visibility(conversation_guid)
    conversation = find!(conversation_guid)
    ConversationVisibility.where(
      person_id:       @user.person.id,
      conversation_id: conversation.id
    ).first!
  end
end

```

Response:
# frozen_string_literal: true

describe ConversationService do
  before do
    opts = {
      subject:         "conversation subject",
      message:         {text: "conversation text"},
      participant_ids: [bob.person.id]
    }
    @conversation = alice.build_conversation(opts)
    @conversation.created_at = 2.hours.ago
    @conversation.save!
  end

  describe "#all_for_user" do
    before do
      opts = {
        subject:         "conversation subject 2",
        message:         {text: "conversation text 2"},
        participant_ids: [bob.person.id]
      }
      @conversation = alice.build_conversation(opts)
      @conversation.created_at = 1.hour.ago
      @conversation.save!
      @date = @conversation.created_at
      opts = {
        subject:         "conversation subject 3",
        message:         {text: "conversation text 3"},
        participant_ids: []
      }
      @conversation = bob.build_conversation(opts)
      @conversation.save!
    end

    it "returns all conversations" do
      expect(alice_conversation_service.all_for_user.length).to eq(2)
      expect(bob_conversation_service.all_for_user.length).to eq(3)
    end

    it "returns all unread conversations" do
      @conversation.conversation_visibilities[0].unread = true
      @conversation.conversation_visibilities[0].save!
      conversations = bob_conversation_service.all_for_user(unread: true)
      expect(conversations.length).to eq(1)
    end

    it "returns conversation after a given date" do
      conversations = bob_conversation_service.all_for_user(only_after: @date)
      expect(conversations.length).to eq(2)
    end
  end

  describe "#find!" do
    it "returns the conversation, if it is the user's conversation" do
      expect(bob_conversation_service.find!(@conversation.guid)).to eq(
        @conversation
      )
    end

    it "returns the conversation, if the user is recipient" do
      expect(bob_conversation_service.find!(@conversation.guid)).to eq(
        @conversation
      )
    end

    it "raises RecordNotFound if the conversation cannot be found" do
      expect {
        alice_conversation_service.find!("unknown")
      }.to raise_error ActiveRecord::RecordNotFound
    end

    it "raises RecordNotFound if the user is not recipient" do
      expect {
        eve_conversation_service.find!(@conversation.guid)
      }.to raise_error ActiveRecord::RecordNotFound
    end
  end

  describe "#build" do
    it "creates the conversation for given user and recipients" do
      new_conversation = alice_conversation_service.build(
        "subject test",
        "message test",
        [bob.person.id]
      )
      expect(new_conversation.subject).to eq("subject test")
      expect(new_conversation.author_id).to eq(alice.person.id)
      expect(new_conversation.messages[0].text).to eq("message test")
      expect(new_conversation.messages[0].author_id).to eq(alice.person.id)
      expect(new_conversation.participants.length).to eq(2)
    end

    it "doesn't add recipients if they are not user contacts" do
      new_conversation = alice_conversation_service.build(
        "subject test",
        "message test",
        [bob.person.id, eve.person.id]
      )
      expect(new_conversation.participants.length).to eq(2)
      expect(new_conversation.messages[0].text).to eq("message test")
      expect(new_conversation.messages[0].author_id).to eq(alice.person.id)
    end
  end

  describe "#get_visibility" do
    it "returns visibility for current user" do
      visibility = alice_conversation_service.get_visibility(
        @conversation.guid
      )
      expect(visibility).to_not be_nil
    end

    it "raises RecordNotFound if the user has no visibility" do
      expect {
        eve_conversation_service.get_visibility(@conversation.id)
      }.to raise_error ActiveRecord::RecordNotFound
    end
  end

  describe "#destroy!" do
    it "deletes the conversation, when it is the user conversation" do
      alice_conversation_service.destroy!(@conversation.guid)
      expect {
        alice_conversation_service.find!(@conversation.guid)
      }.to raise_error ActiveRecord::RecordNotFound
    end

    it "raises RecordNotFound if the conversation cannot be found" do
      expect {
        alice_conversation_service.destroy!("unknown")
      }.to raise_error ActiveRecord::RecordNotFound
    end

    it "raises RecordNotFound if the user is not part of the conversation" do
      expect {
        eve_conversation_service.destroy!(@conversation.guid)
      }.to raise_error ActiveRecord::RecordNotFound
    end
  end

  def alice_conversation_service
    ConversationService.new(alice)
  end

  def bob_conversation_service
    ConversationService.new(bob)
  end

  def eve_conversation_service
    ConversationService.new(eve)
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class CommentService
  def initialize(user=nil)
    @user = user
  end

  def create(post_id, text)
    post = post_service.find!(post_id)
    user.comment!(post, text)
  end

  def find_for_post(post_id)
    post_service.find!(post_id).comments.for_a_stream
  end

  def find!(id_or_guid)
    Comment.find_by!(comment_key(id_or_guid) => id_or_guid)
  end

  def destroy(comment_id)
    comment = Comment.find(comment_id)
    if user.owns?(comment) || user.owns?(comment.parent)
      user.retract(comment)
      true
    else
      false
    end
  end

  def destroy!(comment_guid)
    comment = find!(comment_guid)
    if user.owns?(comment)
      user.retract(comment)
    elsif user.owns?(comment.parent)
      user.retract(comment)
    elsif comment
      raise ActiveRecord::RecordInvalid
    else
      raise ActiveRecord::RecordNotFound
    end
  end

  private

  attr_reader :user

  # We can assume a guid is at least 16 characters long as we have guids set to hex(8) since we started using them.
  def comment_key(id_or_guid)
    id_or_guid.to_s.length < 16 ? :id : :guid
  end

  def post_service
    @post_service ||= PostService.new(user)
  end
end

```

Response:
# frozen_string_literal: true

describe CommentService do
  let(:post) { alice.post(:status_message, text: "hello", to: alice.aspects.first) }

  describe "#create" do
    it "creates a comment on my own post" do
      comment = CommentService.new(alice).create(post.id, "hi")
      expect(comment.text).to eq("hi")
    end

    it "creates a comment on post of a contact" do
      comment = CommentService.new(bob).create(post.id, "hi")
      expect(comment.text).to eq("hi")
    end

    it "attaches the comment to the post" do
      comment = CommentService.new(alice).create(post.id, "hi")
      expect(post.comments.first.text).to eq("hi")
      expect(post.comments.first.id).to eq(comment.id)
    end

    it "fail if the post does not exist" do
      expect {
        CommentService.new(alice).create("unknown id", "hi")
      }.to raise_error ActiveRecord::RecordNotFound
    end

    it "fail if the user can not see the post" do
      expect {
        CommentService.new(eve).create(post.id, "hi")
      }.to raise_error ActiveRecord::RecordNotFound
    end
  end

  describe "#find!" do
    let(:comment) { CommentService.new(bob).create(post.id, "hi") }

    it "returns comment" do
      result = CommentService.new(bob).find!(comment.guid)
      expect(result.id).to eq(comment.id)
    end

    it "raises exception the comment does not exist" do
      expect {
        CommentService.new(bob).find!("unknown id")
      }.to raise_error ActiveRecord::RecordNotFound
    end
  end

  describe "#destroy" do
    let(:comment) { CommentService.new(bob).create(post.id, "hi") }

    it "lets the user destroy his own comment" do
      result = CommentService.new(bob).destroy(comment.id)
      expect(result).to be_truthy
    end

    it "lets the parent author destroy others comment" do
      result = CommentService.new(alice).destroy(comment.id)
      expect(result).to be_truthy
    end

    it "does not let someone destroy others comment" do
      result = CommentService.new(eve).destroy(comment.id)
      expect(result).to be_falsey
    end

    it "fails if the comment does not exist" do
      expect {
        CommentService.new(bob).destroy("unknown id")
      }.to raise_error ActiveRecord::RecordNotFound
    end
  end

  describe "#destroy!" do
    let(:comment) { CommentService.new(bob).create(post.id, "hi") }

    it "lets the user destroy his own comment" do
      result = CommentService.new(bob).destroy!(comment.guid)
      expect(result).to be_truthy
    end

    it "lets the parent author destroy others comment" do
      result = CommentService.new(alice).destroy!(comment.guid)
      expect(result).to be_truthy
    end

    it "does not let someone destroy others comment" do
      expect {
        CommentService.new(eve).destroy!(comment.guid)
      }.to raise_error ActiveRecord::RecordInvalid
    end

    it "raises exception the comment does not exist" do
      expect {
        CommentService.new(bob).destroy!("unknown id")
      }.to raise_error ActiveRecord::RecordNotFound
    end
  end

  describe "#find_for_post" do
    context "with user" do
      it "returns comments for a public post" do
        post = alice.post(:status_message, text: "hello", public: true)
        comment = CommentService.new(alice).create(post.id, "hi")
        expect(CommentService.new(eve).find_for_post(post.id)).to include(comment)
      end

      it "returns comments for a visible private post" do
        comment = CommentService.new(alice).create(post.id, "hi")
        expect(CommentService.new(bob).find_for_post(post.id)).to include(comment)
      end

      it "does not return comments for private post the user can not see" do
        expect {
          CommentService.new(eve).find_for_post(post.id)
        }.to raise_error ActiveRecord::RecordNotFound
      end
    end

    context "without user" do
      it "returns comments for a public post" do
        post = alice.post(:status_message, text: "hello", public: true)
        comment = CommentService.new(alice).create(post.id, "hi")
        expect(CommentService.new.find_for_post(post.id)).to include(comment)
      end

      it "does not return comments for private post" do
        expect {
          CommentService.new.find_for_post(post.id)
        }.to raise_error Diaspora::NonPublic
      end
    end

    it "returns all comments of a post" do
      post = alice.post(:status_message, text: "hello", public: true)
      comments = [alice, bob, eve].map {|user| CommentService.new(user).create(post.id, "hi") }

      expect(CommentService.new.find_for_post(post.id)).to match_array(comments)
    end
  end
end