0

I'm trying to create an rspec to route to a nested resource but I keep getting a No route matches error.

1) V1::DevicesController scan submitted to #create
     Failure/Error: post "/v1/devices/#{device.serial_number}/scans.json", params: { data: scan }

     ActionController::UrlGenerationError:
       No route matches {:action=>"/v1/devices/7929e287466c493ba03947c189cadc81/scans.json", :controller=>"v1/devices", :data=>#<Rack::Test::UploadedFile:0x00556f7c423808 @content_type="application/gzip", @original_filename="sample_test.gz", @tempfile=#<Tempfile:/tmp/sample_test.gz20170719-65-sdfczl>>}

My spec is pretty simple right now:

require 'rails_helper'

RSpec.describe V1::DevicesController, type: :controller do
  it "scan submitted to #create" do
    company = Company.create(name: "Test Company", domain: "testcompany.com")
    device = Device.create(name: "Test Device", company_id: company.id)
    scan = fixture_file_upload( "#{Rails.root}/spec/assets/sample_test.gz", 'application/gzip')
    post "/v1/devices/#{device.serial_number}/scans.json", params: { data: scan }
    expect(response).to be_successful
  end    
end

My controller is relatively basic as well:

class V1::ScansController < ApplicationController
  skip_before_action :authenticate_user_from_token!, only: [:create]
  skip_before_action :authenticate_user!, only: [:create]
  skip_before_filter :verify_authenticity_token, :if => Proc.new { |c| c.request.format == 'application/json' }, only: [:create]

  def create
    @scan = Scan.new(scan_params)

    respond_to do |format|
      if @scan.save
        # GenerateReport.perform_async(@scan.id)
        format.json { render json: @scan, status: :created }
      else
        format.json { render json: @scan.errors, status: :unprocessable_entity }
      end
    end

  end


  private

  def set_device
    @device = Device.find_by!(serial_number: params[:device_serial_number])
  end

  def scan_params
    params.require(:scan).permit(:device_serial_number, :data)
  end

end

and my routes file has:

api_version(:module => "V1", :path => {:value => "v1"}) do
  resources :devices, only: [:show, :update], param: :serial_number do
    resources :scans, only: [:create]
  end
end

Which yields the following route:

v1_device_scans POST   /v1/devices/:device_serial_number/scans(.:format) v1/scans#create

So, clearly my route is there... what am I missing?

Mayur Shah
  • 3,344
  • 1
  • 22
  • 41
Godzilla74
  • 2,358
  • 1
  • 31
  • 67
  • the problem comes fron `post "/v1/devices/#{device.serial_number}/scans.json",` you need give it a mapped route symbol. – Mathew P. Jones Jul 19 '17 at 00:53
  • what do you mean? something like `post :create, params: { device_serial_number: device.serial_number }` – Godzilla74 Jul 19 '17 at 00:54
  • @MathewP.Jones it does seem building the url the way I did the `device.serial_number` does actually get put into the string though. – Godzilla74 Jul 19 '17 at 00:56
  • yes, usually we put that `action method name` as a symbol there, just try. – Mathew P. Jones Jul 19 '17 at 00:58
  • pretty much the same thing: `No route matches {:action=>"create", :controller=>"v1/devices", :data=>#>, :device_serial_number=>"da512d2b15de4bd887161d6cb83e79b2"}` – Godzilla74 Jul 19 '17 at 00:59
  • Try dropping the `.json` from your request in `post "/v1/devices/#{device.serial_number}/scans.json", params: { data: scan }`; then set the headers to request for `json`. [This question](https://stackoverflow.com/questions/9576756/using-rspec-how-do-i-test-the-json-format-of-my-controller-in-rails-3-0-11) may be useful. – Gerry Jul 19 '17 at 01:11
  • @Gerry dropped `.json` still the same error message. What do you mean about the headers? – Godzilla74 Jul 19 '17 at 01:14

1 Answers1

1

Using a mixture of Mathew and Gerry's ideas I was able to get past the no route issue with:

post :create, params: { device_serial_number: device.serial_number, scan: { data: scan } }

Since my controller has this method, I ended up needing to nest the additional attributes in the params hash:

  def scan_params
    params.require(:scan).permit(:device_serial_number, :data)
  end
Godzilla74
  • 2,358
  • 1
  • 31
  • 67
  • 1
    Great! I think we mixed two kinds of params, we need this `device_serial_number` to route to right action, then we need `data` to give it to action. – Mathew P. Jones Jul 19 '17 at 01:40