WebMock stub data from Aws CognitoIdentityProvider - ruby-on-rails

I have the following module in a Rails controller concern:
module AwsAuth
extend ActiveSupport::Concern
require 'aws-sdk'
def get_cognito_user(token)
cognitoidentityprovider = Aws::CognitoIdentityProvider::Client.new(region: ENV['AWS_REGION'])
begin
cognito_user = cognitoidentityprovider.get_user({ access_token: token })
puts cognito_user
return {"email" => cognito_user.username}
rescue StandardError => msg
puts "ERROR!"
puts msg
return {"error" => msg}
end
end
end
At the moment, puts cognito_user returns:
#<Aws::CognitoIdentityProvider::Types::GetUserResponse:0x7fe51b0013a8
mfa_options = nil,
user_attributes = nil,
username = nil
>
How can I stud the response in Rspec so that username and user_attributes are not nil ?

after playing around with yzalavin's suggestion... the following works for me...
allow_any_instance_of( AwsAuth )
.to receive(:get_cognito_user)
.and_return( JSON[ {email: "testy#example.com"}.to_json ] )

Related

Rails RSpec net-ssh mock return of second ssh request

I'm working on a web application that frequently access simulation data on a remote server. I want to create test for errors handling that might happen during these request.
The problem I currently have is I cannot seems to mock a request with my ssh_with_stderr method. The ssh method works fine.
This the code I'm trying to test:
# app/jobs/zip_files_sync_job.rb
class ZipFilesSyncJob < ApplicationJob
queue_as :default
discard_on ActiveJob::DeserializationError
def perform(simulation)
simulation.zip_files.each do |f|
if f.path.nil? && f.created_at < 6.hours.ago
f.state = 'error'
f.save!
next
end
next if f.path.nil?
_, errors = simulation.server.ssh_with_stderr("ls #{f.path.shellescape}")
if errors.blank?
f.size = f.simulation.server.ssh("stat -c %s #{f.path.shellescape}")
f.state = 'ready' if f.size.to_i.positive?
elsif f.state == 'ready' && errors.present?
f.state = 'error'
elsif f.state == 'zipping' && errors.present? && f.created_at < 6.hours.ago
f.state = 'error'
end
f.save!
end
end
end
And this is what I want to test:
# spec/jobs/zip_files_sync_job_spec.rb
require 'rails_helper'
RSpec.describe ZipFilesSyncJob, type: :job do
let(:private_group) { Group::PRIVATE }
let(:user) { FactoryBot.create :user }
let(:server) { FactoryBot.create :server, user: user, external_storage: false }
let(:simulation) { FactoryBot.create :simulation, user: user, group: private_group, server: server }
let(:zip_file) { FactoryBot.create :zip_file, simulation: simulation, path: 'test/zip_file', state: 'pending', size: '100' }
let(:zip_file_no_path) { FactoryBot.create :zip_file, simulation: simulation, path: nil, created_at: 10.hours.ago, state: 'pending' }
let(:ssh_connection) { double('net-ssh') }
before do
zip_file_no_path
allow(Net::SSH).to receive(:start).and_yield(ssh_connection)
end
def perform_zip_file_sync(zip_file)
perform_enqueued_jobs do
ZipFilesSyncJob.perform_now(simulation)
end
zip_file.reload
yield
allow(Net::SSH).to receive(:start).and_call_original
end
describe '#perform' do
include ActiveJob::TestHelper
#################################
##### This test works fine #####
#################################
context 'with no errors' do
before do
zip_file
end
it 'it will change the state to ready' do
allow(Net::SSH).to receive(:start).and_return('144371201')
perform_zip_file_sync(zip_file) do
expect(zip_file.state).to eq 'ready'
end
end
end
#############################################################################
##### This test fails because it does not return on the ssh_with_stderr #####
#############################################################################
context 'with errors' do
it 'will change the state to error' do
allow(Net::SSH).to receive(:start).and_return("[' ', 'Error with connection']")
perform_enqueued_jobs do
ZipFilesSyncJob.perform_now(simulation)
end
zip_file.reload
expect(zip_file.state).to eq 'error'
end
end
end
end
This the the code for the server connection. It uses the net-ssh gem
# app/models/server.rb
Class Server < ApplicationRecord
def ssh(command, storage = true, &block)
Net::SSH.start(hostname, username, port: port, keys: ["key"], non_interactive: true, timeout: 1) do |ssh|
ssh.exec! "cd #{folder.shellescape}; #{command}", &block
end
end
def ssh_with_stderr(command)
#output = ""
#errors = ""
begin
Net::SSH.start(hostname, username, port: port, keys: ["key"], non_interactive: true, timeout: 1) do |ssh|
ssh.exec! "cd #{folder.shellescape}; #{command}" do |_ch, stream, data|
if stream == :stderr
#errors += data
else
#output += data
end
end
end
rescue Net::SSH::Exception, Errno::ECONNREFUSED, Errno::EINVAL, Errno::EADDRNOTAVAIL => e
#output = nil
#errors = e.message
end
[#output, #errors]
end
With this mock
allow(Net::SSH).to receive(:start).and_return("[' ', 'Error with connection']")
the ssh_with_stderr looks like
def ssh_with_stderr(command)
#output = ""
#errors = ""
begin
[' ', 'Error with connection']
rescue Net::SSH::Exception, Errno::ECONNREFUSED, Errno::EINVAL, Errno::EADDRNOTAVAIL => e
#output = nil
#errors = e.message
end
[#output, #errors]
end
So it always returns ["",""] , and checking errors.blank? always positive.
Try to mock Net::SSH with and_raise instead of and_return, something like
allow(Net::SSH).to receive(:start).and_raise(Errno::ECONNREFUSED, "Error with connection")

Uninitialized constant ActiveMerchant

In rails i'm getting the error uninitialized constant CartController::EXPRESS_CHECKOUT while i'm calling the method express_checkout of the controller CartController, i think that the problem is in config/environments/development.rb file, because i defined
config.after_initialize do
ActiveMerchant::Billing::Base.mode = :test
paypal_options = {
login: "<mail>",
password: "<pass>",
signature: "<sig>"
}
::EXPRESS_GATEWAY =
ActiveMerchant::Billing::PaypalExpressGateway.new(paypal_options)
end
But even if i tried to change something in this file, i can't fix this error.
This is the code of cart_controller.rb
def express_checkout
response = EXPRESS_GATEWAY.setup_purchase(params[:amount], ip: request.remote_ip, return_url: "http://localhost:8080/checkout_details", cancel_return_url: "http://localhost:8080", currency: "EUR", allow_guest_checkout: true, items: [{name: "Order", description: "Order description", quantity: "1", amount: params[:amount]}]
)
redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
end
def checkout_details
#token = params[:token]
#payer_id = params[:PayerID]
#details = EXPRESS_GATEWAY.details_for(#token)
end
def purchase
#t = params[:token]
#p = params[:PayerID]
#result = EXPRESS_GATEWAY.purchase(params[:amount], {:token => #t, :payer_id => #p, :currency => "EUR"})
end
I checked the version of the gem activemerchant and is version 1.66.0, so the version of this gem is not a problem.
Anyone knows something about this?
As Tobias suggested me, i corrected the mistake in CartController.rb, modifying EXPRESS_GATEWAY in ::EXPRESS_GATEWAY. After this change, i restarted the rails server and everything works perfectly!

Testing a service object job with rspec

I have the following job:
class Test::MooJob < ApplicationJob
queue_as :onboarding
def perform
avariable = Test::AragornService.build("a").call
if avariable.status == true
puts "job succeeded"
end
end
end
and the service looks like this:
module Test
class AragornService
def self.build(x)
self.new(x)
end
def initialize(x)
#x = x
end
def call
10.times do
Rails.logger.info #x
end
return ServiceResult.new :status => true, :message => "Service Complete", :data => #x
rescue => e
Bugsnag.notify(e, :context => 'service')
return ServiceResult.new :status => false, :message => "Error occurred - #{e.message}"
end
end
end
I am trying to test it with the following spec:
# bundle exec rspec spec/jobs/test/moo_job_spec.rb
require "rails_helper"
describe Test::MooJob do
subject(:job) { described_class.perform_later }
subject(:job_now) { described_class.perform_now }
let(:key) { "a" }
it 'queues the job' do
ActiveJob::Base.queue_adapter = :test
expect { job }.to have_enqueued_job(described_class)
.on_queue("onboarding")
end
it 'calls the aragorn service once' do
allow(Test::AragornService.new(key)).to receive(:call).and_return(ServiceResult.new(:status => true))
expect_any_instance_of(Test::AragornService).to receive(:call).exactly(1).times
job_now
end
end
Why is it that avariable value keeps returning nil
I get the following error "undefined method `status' for nil:NilClass"
however, when I return a simple boolean,
allow(Test::AragornService.new(key)).to receive(:call).and_return(true)
It sets avariable value to true
here's the ServiceResult class:
class ServiceResult
attr_reader :status, :message, :data, :errors
def initialize(status:, message: nil, data: nil, errors: [])
#status = status
#message = message
#data = data
#errors = errors
end
def success?
status == true
end
def failure?
!success?
end
def has_data?
data.present?
end
def has_errors?
errors.present? && errors.length > 0
end
def to_s
"#{success? ? 'Success!' : 'Failure!'} - #{message} - #{data}"
end
end
Its because you are just setting expections on a unrelated instance of Test::AragornService in your spec:
allow(Test::AragornService.new(key)).to
receive(:call).and_return(ServiceResult.new(:status => true))
This does nothing to effect the instance created by Test::AragornService.build
class Test::MooJob < ApplicationJob
queue_as :onboarding
def perform
avariable = Test::AragornService.build("a").call
if avariable.status == true
puts "job succeeded"
end
end
end
You can solve it by stubbing Test::AragornService.build to return a double:
double = instance_double("Test::AragornService")
allow(double).to receive(:call).and_return(ServiceResult.new(status: true))
# bundle exec rspec spec/jobs/test/moo_job_spec.rb
require "rails_helper"
describe Test::MooJob do
let(:perform_later) { described_class.perform_later }
let(:perform_now ) { described_class.perform_now }
let(:service) { instance_double("Test::AragornService") }
before do
# This injects our double instead when the job calls Test::AragornService.build
allow(Test::AragornService).to receive(:build).and_return(service)
end
it 'queues the job' do
# this should be done in `rails_helper.rb` or `config/environments/test.rb` not in the spec!
ActiveJob::Base.queue_adapter = :test
expect { perform_later }.to have_enqueued_job(described_class)
.on_queue("onboarding")
end
it 'calls the aragorn service once' do
expect(service).to receive(:call).and_return(ServiceResult.new(status: true))
perform_now
end
end

Rspec bypass inner function and return mock data

In rails I am writing a test for a controller method search_backups with Rspec:
def elastic_mongo_lookup(search_term)
devices_ids_from_elastic = ConfigTextSearch.search search_term
puts devices_ids_from_elastic
device_ids = devices_ids_from_elastic.map { |device| device._source.device_id }
csv_string = CSV.generate do |csv|
Device.where(:_id.in => device_ids).each do |device|
csv << [device.logical_name, device.primary_ip]
end
end
return csv_string
end
def search_backups
authorize! :read, :custom_report
csv_string = elastic_mongo_lookup params[:search_term]
if csv_string.blank?
flash[:notice] = "No results were found"
redirect_to reports_path
else
render text: "DeviceID, primary_ip\n" + csv_string
end
end#search_backups
describe "try controller method" do
let(:reports_controller) { ReportsController.new }
before do
allow(CSV).to receive(:generate).and_return("1234", "blah")
allow(ConfigTextSearch).to receive(:search).and_return(['"hits": [ {"_source":{"device_id":"54afe167b3000006"}]'])
allow(:devices_ids_from_elastic).to receive(:map).and_return('54afe167b3000006')
stub_request(:get, "http://localhost:9200/mongo_index/config_files/_search?q=").
with(:headers => {'Expect'=>'', 'User-Agent'=>'Faraday v0.9.1'}).
to_return(:status => 200, :body => '', :headers => {})
end
it "allows people to search backups" do
reports = double(ReportsController)
post 'search_backups'
end
end
The issue is that ConfigTextSearch.search search_term returns a elasticsearch ORM object.. which means I can't stub it because the .map() method on devices_ids_from_elastic.map is unique with it's nested _source method.
How could I bypass elastic_mongo_lookup entirely and just return a mocked csv_string to search_backups?
In an RSpec controller test, controller is defined as the controller under test. You can therefore achieve what you're asking about with the following:
allow(controller).to receive(:elastic_mongo_lookup).and_return('whatever string you choose')

Resque require 'csv' not working

I am using resque to process a file in the background. It's a CSV file however I get the following error: uninitialized constant ImportFileHelper::CSV
I have tried to require 'csv' and also include CSV neither will work.
require 'csv'
module ImportFileHelper
HOST = ""
USER_NAME = ""
PASSWORD = ""
def self.process_file(file_data, file_name)
init
#file_name = file_name
begin
csv = CSV.parse(file_data, :headers => true)
csv.each do |row|
#first_name = row["FirstName"]
#last_name = row["LastName"]
#email = row["Email"]
#password = "ch#ngeM3!"
#user_group_name = row["GroupName"].split(",")
#store_name = row["StoreName"]
#external_id = row["ExternalID"]
add_user unless #first_name.nil? || #last_name.nil? || #email.nil? || #password.nil? || #first_name.empty? || #last_name.empty? || #email.empty?
end
rescue NoMethodError => no_method_error
log_error_to_db no_method_error
rescue IOError => error
log_error_to_db error
#errors << error.to_s
rescue Exception => ex
log_error_to_db ex
end
prep_soap_responses_for_output
end
def self.init
HTTPI.log = false
#body = { username: USER_NAME, password: PASSWORD }
#errors = []
#existing_users = []
configure_savon
get_all_groups
get_all_stores
end
def self.prep_soap_responses_for_output
[#existing_users, #errors]
end
def self.log_error_to_db(error)
error.backtrace ||= "Not Available"
if error.message.length > 250
error_message = "There was an error"
else
error_message = error.message
end
ErrorLog.create(message: error_message, trace: error.backtrace, file_name: #file_name)
end
def self.get_store_id
#store_id = #stores[#store_name.to_sym]
end
def self.get_all_stores
#stores = { }
client = Savon::Client.new(HOST + "Storews.asmx?wsdl")
body_data = { mall_id: 1, is_return_offline_store: :false }
#body.merge! body_data
begin
response = client.request :get_store_list, body: #body
if response
hash_response = response.to_hash
stores = hash_response[:get_store_list_response][:get_store_list_result][:store]
stores.each do |s|
store = { s[:name].to_sym => s[:store_id] }
#stores.merge! store
end
end
rescue Savon::Error => ex
log_error_to_db error
#errors << error.to_s
end
end
def self.create_adbuilder_user_object
AdbuilderUser.new(#first_name, #last_name, #email, #user_id, #store_id, #store_name, #user_group_name, #group_id, #external_id)
end
def self.configure_savon
Savon.configure do |configure|
configure.log = false
end
end
def self.add_user
body_data = { first_name: #first_name, last_name: #last_name, user_password: #password, email: #email, external_id: #external_id }
#body.merge! body_data
begin
client = Savon::Client.new(HOST + "UserWS.asmx?wsdl")
response = client.request :add_user, body: #body
if response
#user_id = response.body[:add_user_response][:add_user_result]
if #user_group_name
get_group_id
end
if #store_name
#store_id = get_store_id
unless #store_id.to_s =~ /^0$/
adbuilder_user = create_adbuilder_user_object
UserMailer.create_password(adbuilder_user).deliver if adbuilder_user
end
end
end
rescue Savon::Error => error
log_error_to_db error
if error.message == "(soap:Client) 3: A user with the same email login already exists. Please choose a different login."
#existing_users << #email
else
#errors << error.to_s
end
rescue Exception => error
log_error_to_db error
#errors << error.message.to_s
end
end
def self.get_group_id
begin
#user_group_name.each do |group_name|
user_group_id = #groups_info[group_name.downcase.to_sym]
add_user_to_group user_group_id if user_group_id
end
rescue Exception => error
log_error_to_db error
#errors << error.message.to_s
end
end
def self.get_all_groups
#groups_info = {}
begin
client = Savon::Client.new(HOST + "Usergroupws.asmx?wsdl")
response = client.request :get_user_group_list, body: #body
if response
group = response.to_hash
groups = group[:get_user_group_list_response][:get_user_group_list_result][:user_group]
groups.each do |g|
new_hash = { g[:name].gsub(/\s/, "_").downcase.to_sym => g[:user_group_id] }
#groups_info.merge! new_hash
end
end
rescue Savon::Error => error
log_error_to_db
#errors << error.to_s
end
end
def self.add_user_to_group(group_id)
body_data = { user_id: #user_id, user_group_id: group_id }
#body.merge! body_data
begin
client = Savon::Client.new(HOST + "Usergroupws.asmx?wsdl")
response = client.request :add_user_to_group, body: #body
rescue Savon::Error => error
log_error_to_db error
#errors << error.to_s
end
end
end
So as a work around for this I am doing the csv parsing in the resque job file. This is now allowing it to run. Not sure if this is the best way to do it though.
class ProcessFile
#queue = :rts_file_parser
def self.perform(file_data, file_name)
csv = CSV.parse(file_data, :headers => true)
csv.each do |row|
row_data = { first_name: row["FirstName"], last_name: row["LastName"], email: row["Email"], password: "ch#ngeM3!", user_group_name: row["GroupName"].split(","), store_name: row["StoreName"], external_id: row["ExternalID"] }
ImportFileHelper.process_file file_name, row_data
end
end
end
Mind if I claim the answer (via my comment)?
It looks like it might be a scope resolution issue.
Try ::CSV instead of CSV.
Try adding the gem to the gemfile.

Resources