Test a method which uses external API - ruby-on-rails

I have a model called Video. This model has a after_save callback which runs a method #upload_video_to_depot. That method uses :file param (which is not saved in the db) and uploads a video file to a remote API using RestClient.
The question is - how to handle that in my specs without actually sending the file to the API ? I need to test my Video model and #upload_video_to_depot method but i can't imagine how it should be done (i'm quite fresh in the TDD thing).
Can it be fully handled in the specs or it also involves some changes in my model?

Use a gem like WebMock to stub external requests. You can set expectations on what requests were sent and with what params, as well as the responses the server should be giving you.
This will allow you to ensure that your REST client is sending the correct params to the correct place and processing responses correctly.

Related

How can I test the POST request an external webhook makes to my application?

Folks this is my scenario
There is this external webhook that I would like to test
Is there a way to use VCR or some other tool to record the POST request the webhook makes to my rails app so I can use it in Rspec to run my tests?
The webhook's body is long and very specific that's why I would like to record the request somehow
Is there a way to achieve this?
I tried Requestbin and Charles to record the request but did not manage to make them work so far
Assuming that when you say the problem input is "too long and too specific" what you mean is that it would be inconvenient to construct it inside a test: a simple solution is to put the post body into a separate file.
You can save an example POST body as a file under test/ or spec/ and then read it and post to your app in a request spec:
post(
my_webhook_path,
params: JSON.parse(File.read(path_to_json_file) # assuming the post body is JSON
)
# expect/assert some things about the effects/response of the request
In the docs you will see that you can also add headers, so it should be possible to construct the exact request.
I don't think the VCR is the best tool for that. It's great for recording outgoing requests, not the incoming ones.
I'm guessing you already have a controller and an action that should accept those requests, make sure your app is running and accessible for the service that is going to ping you (ngrok is great for that)
Then in your action do something like
def my_action_accepting_webhook_requests
puts request_parameters
puts query_parameters
end
You can just read from the console (or save to file) the params, decide if you want to hardcode it in the spec or save it as a file. And then in your request spec you can re-use it and test your webhook.

Rails app with no databse and continually updated models

I'm wondering what the best way to go about developing a rails application with the following features:
All of the data comes from a SOAP request to a 3rd party
A background task will make this soap request every ~10s
The background task will parse the response and then update an ActiveRecord model accordingly
The data isn't written to a database at all, if the app fails, when we start it back up the data will come from the soap request again
Users will make a request to the app which will simply show data in the model (i.e. from the soap request).
The idea is to avoid making the SOAP request for every single user as the data won't change that frequently. Not using a database avoids reading and writing of data that only ever comes from the request anyway.
I imagine that all of this can be completely quite simply with a few gems but I've had a bit of trouble sorting through what meets my requirements and what doesn't.
Thanks
I'm not sure what benefit you're getting from using ActiveRecord in this case.
Perhaps consider some other type of persistance for the SOAP calls?
If the results form the WebService are really not changing, I would recommend the Rails caching mechanism. Wherever in your Rails app, you can do:
Rails.cache.fetch "a_unique_cache_key" do
... do your SOAP request and return the result
end
This will do the work within the block just once and fetch its result from the rails cache store in the future.
The cache store be of various types (one of which is the memcache store). I usually go with the file store for medium traffic sites, but you may choose another:
http://guides.rubyonrails.org/caching_with_rails.html

How to simulate external APIs?

We call many different external APIs in our system and now I'm looking for a system I can use to simulate those APIs so we can test ours in the Staging and Development environments?
Our application is written in Ruby on Rails 3.0 but since all the API calls to and from it are over HTTP there is no language dependency.
VCR will record the actual input from the webservice and then replay that feedback from then on.
To simulate it completely, you can use fakeweb. You'll record output to a file and have it sent back to your application.
This something called test mocking/stubbing and is a common practice. Basically you override the response code of the API call to return data w/o actually doing the HTTP request. Just search it for more details.

Posting multipart form data with Ruby

I'm building a rails app that interacts with a 3rd party API
When a user uploads a file to rails, it should be forwarded on to the 3rd party site via an HTTP POST.
In some cases, the upload can be several hundred MBs.
At the moment, I've just been re-posting to the API using Net::HTTP and accessing the multipart form object like so
#tempfile = params[:video][:file_upload].tempfile
This is hella slow though and feels kinda dirty.
Is there a better way to do this?
Is it possible to have the user post directly to the 3rd party service or do you have to handle the API through your Rails stack? Ideally you would be able to do this and would not have to load the file into your stack and then re-post it to the API. If you can't post directly, I would recommend seeing if the API has a streaming service so that you can send parts of the file instead of the entire thing at once. Either way I think you'll start running into Timeout errors on your side and on the API side with large files, so you'll have to increase your own timeouts or create a different type of streaming file uploader.
Spin up a background job using DelayedJob. In the delayed job, you could try rails redirect_to.
https://github.com/tobi/delayed_job
http://apidock.com/rails/ActionController/Base/redirect_to

In Rails, can an internal request be generated that behaves identically to an HTTP request?

Within my Rails application, I'd like to generate requests that behave identically to "genuine" HTTP requests.
For a somewhat contrived example, suppose I were creating a system that could batch incoming HTTP requests for later processing. The interface for it would be something like:
Create a new batch resource via the usual CRUD methodology (POST, receive a location to the newly created resource).
Update the batch resource by sending it URLs, HTTP methods, and data to be added to the collection of requests it's supposed to later perform in bulk.
"Process" the batch resource, wherein it would iterate over its collection of requests (each of which might be represented by a URL, HTTP method, and a set of data), and somehow tell Rails to process those requests in the same way as it would were they coming in as normal, "non-batched" requests.
It seems to me that there are two important pieces of work that need to happen to make this functional:
First, the incoming requests need to be somehow saved for later. This could be simply a case of saving various aspects of the incoming request, such as the path, method, data, headers, etc. that are already exposed as part of the incoming request object within a controller. It would be nice if there was a more "automatic" way of handling this--perhaps something more like object marshaling or serialization--but the brute force approach of recording individual parameters should work as well.
Second, the saved requests need to be able to be re-injected into the rails application at a later time, and go through the same process that a normal HTTP request goes through: routing, controllers, views, etc. I'd like to be able to capture the response in a string, much as the HTTP client would have seen it, and I'd also like to do this using Rails' internal machinery rather than simply using an HTTP library to have the application literally make a new request to itself.
Thoughts?
a straight forward way of storing the arguments should be serializing the request object in your controller - this should contain all important data
to call the requests later on, i would consider using the Dispatcher.dispatch class method, that takes 3 arguments: the cgi request, the session options (CgiRequest::DEFAULT_SESSION_OPTIONS should be ok) and the stream which the output is written to
Rack Middleware
After doing a lot of investigation after I'd initially asked this question, I eventually experimented with and successfully implemented a solution using Rack Middleware.
A Basic Methodology
In the `call' method of the middleware:
Check to see if we're making a request as a nested resource of a
transaction object, or if it's an otherwise ordinary request. If it's
ordinary, proceed as normal through the middleware by making a call to
app.call(env), and return the status, headers, and response.
Unless this is a transaction commit, record the "interesting" parts of the
request's env hash, and save them to the database as an "operation" associated
with this transaction object.
If this is a transaction commit, retrieve all of the relevant operations
for this transaction. Either create a new request environment, or clone the
existing one and populate it with the values saved for the operation. Also
make a copy of the original request environment for later restoration, if
control is meant to pass through the application normally post-commit.
Feed the constructed environment into a call to app.call(env). Repeat for
each operation.
If the original request environment was preserved, restore it and make one
final call to app.call(env), returning from the invocation of `call' in the
middleware the status, headers, and response from this final call to
app.call(env).
A Sample Application
I've implemented an example implementation of the methodology I describe here, which I've made available on GitHub. It also contains an in-depth example describing how the implementation might look from an API perspective. Be warned: it's quite rough, totally undocumented (with the exception of the README), and quite possibly in violation of Rails good coding practices. It can be obtained here:
http://github.com/mcwehner/transact-example
A Plugin/Gem
I'm also beginning work on a plugin or gem that will provide this sort of interface to any Rails application. It's in its formative stages (in fact it's completely devoid of code at the moment), and work on it will likely proceed slowly. Explore it as it develops here:
http://github.com/mcwehner/transact
See also
Railscasts - Rack Middleware
Rails Guides - Rails on Rack

Resources