Rails Minitest for API connection - ruby-on-rails

A rails action calls an API to get some data and is handled via this method:
response_osm = Geocoder::Lookup.get([...]
results = JSON.parse(response_osm)
A number of further methods can be triggered based on this data.
geometry = results['results'][0]['geometry']
zone_calc = point_in_polygon(shop.id, geometry['lng'], geometry['lat'])
The test, using a GET request will complain with WebMock::NetConnectNotAllowedError: Real HTTP connections are disabled and provide a suggested stub stub_request(:get, [...] with( headers: { [...] }).
to_return(status: 200, body: "", headers: {})
Having actual API responses in hand, one could assert changes based on the response_osm hash.
{"documentation"=>"https://example.com/api", "licenses"=>[{"name"=>"see attribution guide", "url"=>" [...]
Inserting the quote-escaped hash as the attribute for body: "" in the stub
does not trigger any Minitest error, but also does not process subsequent steps as evidenced by a number of data captures via puts commands.
I recognize that I may be tripping myself up between stubs and mock at this point
How can this test actual work with real response data?

Related

Problem when trying to stub API requests on Rails

I'm trying to stub an API request using Webmock. Instead of getting the "real" data from my Rails controller, I want to return dummy data, for test purposes only.
I have a React frontend with a button that fetches my api endpoint:
const handleClick = async () => {
const response = await fetch("api_endpoint");
const data = await response.json();
console.log("data: ", JSON.stringify(data));
};
This is my test file:
require 'rails_helper'
require 'spec_helper'
RSpec.describe 'visiting the embedded app', type: :system do
it 'visits the embedded app' do
stub_request(:get, 'api_endpoint').to_return(
body: { data: 'dummy' }.to_json
)
visit 'my react page with the button'
click_button "Call API"
sleep 10
random_assert
end
end
Instead of getting data: dummy, I get the "real" data from by rails controller.
What is wrong with this implementation? Tell me if you need more information!
You have fundamentially missunderstood how HTTP stubbing in tests works. WebMock is used to stub HTTP requests sent from your server to other servers.
I'm assuming this is WebMock but the same applies to pretty much any other server side HTTP stubbing tool.
It does not stub requests sent from the client - simply becuase this code isn't even running in the client. That is done with javascript libraries like Mirage JS running in the client or by sending the requests to endpoints on the server that return dummy data instead of the actual implementation.

How do i test requests with digest authentication on RSpec

Hope you're having a great day.
I'm new to security in Rails and I've come through a problem of authenticating a Digest request on tests in Rails. From the course I'm learning this from (which doesn't use tests), the Digest authentication provided by Rails is not really that much used, so there wouldn't be a necessity for it. But i still got curious on how to test it with RSpec, which led me to a lot of hours trying to do something that probably isn't supposed to be done.
Anyways, here's my dilemma, it seems to me that a Digest authentication needs two requests, where it can send some variables through the header (like "nonce" and "opaque") and another that receives some calculated stuff with the "failed" request header (like "cnonce" and "response").
Bellow is a code of a helper to make the second header before mocking the request again, which would then make a OK status. Unfortunately, there're some variables lacking (in the case, "cnonce" and "response"), and i have no idea where to find them. I tried finding methods in Digest related classes on Ruby and Rails, but none seem to make these calculations, although i feel like i'm not seeing something here.
class DigestHeaderWriter
def initialize(header, user, password)
#header = header
#user = user
#password = password
end
def call
str_pieces = #header["WWW-Authenticate"].split(/\"/)
{
"ACCEPT" => "application/vnd.api+json",
"Authorization" =>
"Digest username=\"#{#user}\","\
"realm=\"#{str_pieces[1]}\","\
"nonce=\"#{str_pieces[5]}\","\
"uri=\"/kinds/1\","\
"nc=00000001"\
"algorithm=MD5"\
"qop=auth"\
"opaque=#{str_pieces[7]}"
}
end
end
OBS: forgive me the unused password variable, i thought i could calculate something with it.
Here is an example string array from the first response header, aka str_pieces:
[
"Digest realm=",
"Application",
", qop=",
"auth",
", algorithm=MD5, nonce=",
"MTY2MDY4NTQ5MTpiMmE3N2FhYWJlMTYwYmI3ZTM3YzZkY2VlMjcxZmEyOA==",
", opaque=",
"5d5ba5ba4a787523d37a8ad54c64a8a9"
]
Bellow is the test on the spec archive:
describe "GET /show" do
it "renders a successful response" do
kind = Kind.create! valid_attributes
kind2 = Kind.create! valid_attributes2
get "http://www.example.com/kinds/1", headers: header
get "http://www.example.com/kinds/1", headers: DigestHeaderWriter.new(response.header, "Name", "secret").call
expect(response).to be_successful
expect(response.body).to include valid_attributes[:description]
expect(response.body).not_to include valid_attributes2[:description]
end
end
Basically it just stops at expect(response).to be_successful, and the header string just says that it's not an authorized request (401).
Maybe there's a whole other way to do it, either way i'm curious for a solution.

proper way to consume local endpoint from another controller in Rails app

I'm working on a soon to be legacy Rails App and I need to consume a local endpoint.
What I need to do is something like this
controller A
response = restClient.call("some other local endpoint belong to controller B")
do some formatting on the response
return formatted response to client
What I'm doing now is using Faraday lib for rest call:
conn = Faraday.new(
url: 'http://127.0.0.1:3000',
params: orig_params,
headers: { 'Content-Type' => 'application/json' }
)
response = conn.post('/api/v2/points/adjust')
format_response(response)
Is there a better way of doing so? what I was aiming for is like in a controller test
POST/GET like methods that 'knows' how to reach the local endpoint...
conceptually something like:
response = post('/api/v2/points/adjust')
format_response(response)
And yes, its very bad design but that is the constraint(as I mentioned it's a soon to be legacy app) I need to live with and the only questions what would be the best way to do it.

Lua scripting language: modify a response body in API gateway

I would like to modify the response body returned by the backend.
As background I'll detail my specific problem (but I don't require a solution to the specific problem, just the method for manipulating a response body). I want to insert/add a key value pair to the response body based on the status code of the response and I want to transform snake_case keys into camelCase keys.
For example, given a response with
status code: 401
body: {'detail_message': 'user is not logged in'}
I want to transform it to a response with
status code: 401
body: {'success': False, 'detailMessage': 'user is not logged in'}
The rule for success would be True for anything below 400 and False for anything above or equal.
Lua scripting can be used in my API gateway which is Krakend
https://www.krakend.io/docs/endpoints/lua/
The documentation only includes examples for printing the response body and modifying headers but not for modifying the response body.
I have no experience with Lua and only need it for one task. I haven't been able to find online example of response body manipulation which I could play with.
What methods do I need in order to add a key value pair to a response body and to manipulate the keys in the response body?

Swapping Rails 4 ParamsParser removes params body

I'm trying to follow this solution to add a params parser to my rails app, but all that happens is that I now get the headers but no parameters from the body of the JSON request at all. In other words, calling params from within the controller returns this:
{"controller"=>"residences", "action"=>"create",
"user_email"=>"wjdhamilton#wibble.com",
"user_token"=>"ayAJ8kDUKjCiy1r1Mxzp"}
but I expect this as well:
{"data"=>{"type"=>"residences",
"attributes"=>{"name-number"=>"The Byre",
"street"=>"Next Door",
"town"=>"Just Dulnain Bridge",
"postcode"=>"PH1 3SY",
"country-code"=>""},
"relationships"=>{"residence-histories"=>{"data"=>nil},
"occupants"=>{"data"=>nil}}}}
Here is my initializer, which as you can see is almost identical to the one in the other post:
Rails.application.config.middleware.swap(
::ActionDispatch::ParamsParser, ::ActionDispatch::ParamsParser,
::Mime::Type.lookup("application/vnd.api+json") => Proc.new { |raw_post|
# Borrowed from action_dispatch/middleware/params_parser.rb except for
# data.deep_transform_keys!(&:underscore) :
data = ::ActiveSupport::JSON.decode(raw_post)
data = {:_json => data} unless data.is_a?(::Hash)
data = ::ActionDispatch::Request::Utils.deep_munge(data)
# Transform dash-case param keys to snake_case:
data = data.deep_transform_keys(&:underscore)
data.with_indifferent_access
}
)
Can anyone tell me where I'm going wrong? I'm running Rails 4.2.7.1
Update 1: I decided to try and use the Rails 5 solution instead, the upgrade was overdue anyway, and now things have changed slightly. Given the following request:
"user_email=mogwai%40balnaan.com
&user_token=_1o3Kpzo4gTdPC2bivy
&format=json
&data[type]=messages&data[attributes][sent-on]=2014-01-15
&data[attributes][details]=Beautiful+Shetland+Pony
&data[attributes][message-type]=card
&data[relationships][occasion][data][type]=occasions
&data[relationships][occasion][data][id]=5743
&data[relationships][person][data][type]=people
&data[relationships][person][data][id]=66475"
the ParamsParser middleware only receives the following hash:
"{user":{"email":"mogwai#balnaan.com","password":"0h!Mr5M0g5"}}
Whereas I would expect it to receive the following:
{"user_email"=>"mogwai#balnaan.com", "user_token"=>"_1o3Kpzo4gTdPC2b-ivy", "format"=>"5743", "data"=>{"type"=>"messages", "attributes"=>{"sent-on"=>"2014-01-15", "details"=>"Beautiful Shetland Pony", "message-type"=>"card"}, "relationships"=>{"occasion"=>{"data"=> "type"=>"occasions", "id"=>"5743"}}, "person"=>{"data"=>{"type"=>"people", "id"=>"66475"}}}}, "controller"=>"messages", "action"=>"create"}
The problem was caused by the tests that I had written. I had not added the Content-Type to the requests in the tests, and had not explicitly converted the payload to JSON like so (in Rails 5):
post thing_path, params: my_data.to_json, headers: { "Content-Type" => "application/vnd.api+json }
The effects of this were twofold: Firstly, since params parsers are mapped to specific media types then withholding the media type meant that rails assumed its default media type (in this case application/json) so the parser was not used to process the body of the request. What confused me was that it still passed the headers to the parser. Once I fixed that problem, I was then faced with the body in the format of the request above. That is where the explicit conversion to JSON is required. I could have avoided all of this if I had just written accurate tests!

Resources