Search code examples
arraysruby-on-railsrspecfactory-botrspec-rails

Rspec Arrays not working as they should be?


The test should update the item (which is already created) with the tag_id that was placed into the tag_id parameter. I know that I can do this because I have tried it on postman and it works every time. Rspec tells me that the tags is invalid even though I logged its id (which means that it exists).

Any help would be appreciated.

rspec code error message with logs

I tried to change the way of input; I've placed "tag.id" into the array expecting it to read it as an integer and I have tried every other way of inputting the number into the array.

I also know that the problem is with the array because once I leave it empty (aka: []) it works perfectly fine.

Note: I am 100% sure that the code works because it works on postman.

Code in items_controller.rb for updating an item:

def update

    item = Item.find(params[:id])
    item.item_tags.destroy_all
    error_exist = false

    params[:tag_id].each do |tag_id|
      if ItemTag.find_by(tag_id: tag_id) == nil
        error_exist = true
        break
      else
        item.tags << Tag.find(tag_id)
      end
    end
    @items = item

    if error_exist == false
      if current_user.id == item.user_id
        if item.update(item_params)
          if item.is_sold == true
            if item.bids.last != nil
              item.bids.last.update_attribute(:is_accepted, true)
            end
          else
            if item.bids.last != nil
              item.bids.last.update_attribute(:is_accepted, false)
            end
          end
          render 'items/update'
        else
          render json: {status: "Error", message: "Item not updated", data:item.errors}, status: :unprocessable_entity
        end
      else
        render json: {status: "Error", message: "Item is not yours"}, status: :unauthorized
      end
    else
      render json: {status: "Error", message: "Invalid tags"}, status: :unprocessable_entity
    end
  end

private

  def item_params
    params.permit(:name, :price, :is_auction, :is_sold)
  end

Note 2: Passing in nothing, a single tag_id, or multiple tags should work. The only time it would render invalid tags is if one of the tag_is does not exist.


Solution

  • The problem is here.

        params[:tag_id].each do |tag_id|
          if ItemTag.find_by(tag_id: tag_id) == nil
            error_exist = true
            break
          else
            item.tags << Tag.find(tag_id)
          end
        end
    

    Your test passes Tag.last.id as tag_id but your code is searching by ItemTag.find_by(tag_id: tag_id). The conclusion is that the tag is missing from ItemTag.


    Digging deeper, you're not asking "does this tag exist", but "does any item already use this tag" which seems odd.

    You're also deleting all the tags from the item in question, which will impact the tag not being in ItemTag...

        item = Item.find(params[:id])
        item.item_tags.destroy_all
    

    ...and then pushing the new tags on one by one. I think the intent is to make sure you fully replace the item's tags, but this is done with item.tags = tags.


    A better approach is to replace the tags in one go.

    # Check if the item exists.
    item = Item.find(params[:id])
    if !item
      # Not found is the appropriate response. See
      # https://restfulapi.net/http-status-codes/
      render json: {status: "Error", message: "Item #{params[:id]} was not found"}, status: :not_found
      return
    end
    
    # Get the tags, make sure they exist.
    begin
      tags = Tag.find(params[:tag_Id])
    rescue ActiveRecord::RecordNotFound => e
      # Not found is the appropriate response. See
      # https://restfulapi.net/http-status-codes/
      render json: {status: "Error", message: "Tag #{e.primary_key} was not found"}, status: :not_found
      return
    end
    
    # Delete and add tags as necessary.
    item.tags = tags