splitwise/super_diff

Unexpected error : unable to convert unpermitted parameters to hash

ngouy opened this issue · 9 comments

ngouy commented

When I run a specific spec without supper diff, spec is passing
When I run it with it, it is failing
The spec is using a custom matcher, that is itself a wrapp of a regular matcher:

RSpec::Matchers.define :be_within_last do |expected|
      match do |actual|
        actual = Time.zone.parse(actual) if actual.is_a?(String)
        actual = Time.zone.at(actual) if actual.is_a?(Integer)

        expect(actual).to be_within(expected).of(Time.current)
      end
    end

I have absolutely no clue on how it's working behind the scene. But here is what i can observe with debug breakpoints

It is failing in
object_inspection/inspection_tree_builders/default_object.rb:46
In this context object is an instance a spec: RSpec::ExampleGroups::SurveysSubmissions::SurveysSubmissions::Post::<etc...> "does something"

41:                 insert_separated_list(object.instance_variables.sort) do |name|
42:                   as_prefix_when_rendering_to_lines do
43:                     add_text "#{name}="
44:                   end
45:
46:                   add_inspection_of object.instance_variable_get(name) # here
47:                 end
48:               end

One of the object instance_variable is @controller (which carries an instance of a controller)
So it runs add_inspection_of(@controller)

Which latter on, recursively, goes to the exact same code where object is the actual @controller
In this context, object is now instance of one of a controller

because one of the object.instance_variable is @_params = ActionController::Parameters <instance>
And it runs add_inspection_of(@_params), which triggers:

@_params.to_hash (that then triggers)
@_params.to_h

actionpack-7.0.4/lib/action_controller/metal/strong_parameters.rb:309 ActionController::Parameters#to_h:

305: def to_h
306:   if permitted?
307:     convert_parameters_to_hashes(@parameters, :to_h)
308:   else
309:     binding.pry # breakpoint
310:     raise UnfilteredParameters
311:   end
312: end
ngouy commented

If don't use my custom matcher (which is just a wrapper) and use directly the "original" rspec helper, it works

ngouy commented

found why

customer matcher has an @matcher_execution_context instance variable, that is "inspected" through the same piece of code with add_inspection_of...
This context contains the instance of the spec, that contains @controller, that contains @params that is inspected with to_h

not sure how to solve that

ngouy commented

and as I understand it, to_h is called from "Ruby" itself because it needs to decompose the *args of

image

My args are not even printed when the faulty params are called, to that's my guess of what happens

ngouy commented

also lets say I override to_h in my action controller params to not raise params and return actual hash, super diff continue and deep_inspect the whole actual @matcher_execution_context which is not pretty to look at

Takes literally minutes

ngouy commented

Also for the record: my matcher was not constructed properly and the matching was failing, hence super_diff triggering

(when the custom matcher runs successfully, the issue is not here)

@ngouy You say you're using a custom matcher and that a controller instance ends up being inspected when the matcher fails. Out of curiosity what is the test look like itself?

ngouy commented

💯 that's exactly that

ngouy commented

Test is a swagger test and it goes something like

post "/whatever" do
  response "200", "it does something" do
    run_test! do
      expect(response["created_at"]).to be_within_last(3.seconds) # my custom matcher call
    end
  end
end

@ngouy @mcmire we're having the same issue when testing using rails controller functional test like

  it "..." do
    get :index
    expect(response).to be_client_error
  end
  0) BLRegistry::API::V2::TemplatesController does
     Failure/Error: expect(response).to be_client_error

     ActionController::UnfilteredParameters:
       unable to convert unpermitted parameters to hash

We can probably leverage custom diffing object (https://github.com/mcmire/super_diff#diffing-custom-objects) but not sure how complex it is?

For now the poor man solution is to disable super diff for this controller specs

# rails_helper.rb
require "super_diff/rspec-rails" unless defined?(DISABLE_SUPER_DIFF)
# controller_spec.rb
DISABLE_SUPER_DIFF = true
require "rails_helper"

Is there any options to disable super diff for a given test?