How to inspect CSV file columns inside RSpec test? - ruby

I really don't know how to inspect CSV file I created in my ROR App.
require "rails_helper"
require "shared_contexts/vcr/s3"
require "csv"
RSpec.describe ReportRuns::RunService do
describe "CSV columns" do
include_context "vcr s3 put csv"
let(:report_run) { create :report_run, report_template: report_template, created_by: user.id, mime_type: "csv" }
#let(:report_template) { create :report_template, template_structure: { module: "trial_members", filters: { trial_members: [trial_members.id] } } }
let(:report_template) { create :report_template, trial: trial }
let(:trial) { create :trial }
let(:user) { create :user }
let(:user_role) { create :user_role }
subject { described_class.new(report_run) }
before do
end
it do
get :index, format: :csv
p "response jee: #{response.body}"
p response.headers
p "report run: #{report_run.inspect}"
p "templejt: #{report_template.inspect}"
p "mime type: #{report_run[:mime_type]}"
#p "trila je: #{trial.inspect}"
p "users are: #{user.inspect}"
p "user roles su: #{user_role.inspect}"
is_expected.to be_truthy
expect(5).to match(5)
end
end
end

Use the CSV library to parse the body of the response. Then work with the CSV object.
csv = CSV.new(response.body)
You can also check the Content-type headers are correct, text/csv.

Related

How to properly mock objects in RSpec?

I have a simple class, which generates a download URL to a file stored on S3 and I need to write a unit test to test this class. So far I've had no luck.
class S3DownloadUrlGenerator
def initialize(filename)
#filename = filename
end
def presigned_url
signer = Aws::S3::Presigner.new(client: s3)
signer.presigned_url(
:get_object,
bucket: "my-bucket",
key: filename,
response_content_disposition: "attachment",
)
end
private
def s3
#s3 ||= Aws::S3::Client.new(
region: "my-region,
http_open_timeout: 5,
http_read_timeout: 25,
)
end
attr_reader :filename
end
I want to test if calling #presigned_url on an instance of S3DownloadUrlGenerator returns a URL.
This is my test:
describe S3DownloadUrlGenerator do
before do
allow(Aws::S3::Client).to receive(:new) { s3_client }
end
let(:s3_client) { spy("s3 client") }
let(:presigner) { spy("s3 presigner") }
it "generates download URL for a file" do
expect(Aws::S3::Presigner).to receive(:new).with(client: s3_client).and_return(presigner)
expect(presigner).to receive(:presigned_url).with(
:get_object,
bucket: "my-test-bucket",
key: "test_file.txt",
response_content_disposition: "attachment",
).and_return("https://www.example.com")
expect(described_class.new("Test_file.txt").presigned_url).to eq("https://www.example.com")
end
end
but I get an error:
Failure/Error: expect(described_class.new("Test_file.txt").presigned_url).to eq("https://www.example.com")
expected: "https://www.example.com"
got: #<Double "s3 presigner">
(compared using ==)
I am bit new to this and I would like to learn how to properly test such cases. Thank you very much for the help.
bucket and key parameters differ in actual calling and mocking.
Use below code it works:
describe S3DownloadUrlGenerator do
before do
allow(Aws::S3::Client).to receive(:new) { s3_client }
end
let(:s3_client) { spy("s3 client") }
let(:presigner) { spy("s3 presigner") }
it "generates download URL for a file" do
expect(Aws::S3::Presigner).to receive(:new).with(client: s3_client).and_return(presigner)
expect(presigner).to receive(:presigned_url).with(
:get_object,
bucket: "my-bucket",
key: "Test_file.txt",
response_content_disposition: "attachment",
).and_return("https://www.example.com")
expect(described_class.new("Test_file.txt").presigned_url).to eq("https://www.example.com")
end
end

Load different files from let

Is there a way to load different files passed as arguments in Ruby let definition? This is the code that I sue to load file in rspec:
let(:xml_file) { File.read(File.join('spec', 'fixtures', 'xml', 'example1.xml')) }
I need something like this:
let(:xml_file) { File.read(File.join('spec', 'fixtures', 'xml', #{file})) }
Can you propose some solution?
Just make file name a variable and define it per describe/context
let(:xml_file) { File.read(File.join('spec', 'fixtures', 'xml', file_name)) }
context '1' do
let(:file_name) { 'foobar_1.xml' }
it 'test sth' do
xml_file # should be spec/fixtures/xml/foobar_1.xml
end
end
context '2' do
let(:file_name) { 'foobar_2.xml' }
it 'test sth' do
xml_file # should be spec/fixtures/xml/foobar_2.xml
end
end
If you need to use an specific file on each test, you can define another let on each context or describe section:
let(:xml_file) { File.read(File.join('spec', 'fixtures', 'xml', file))}
describe "first set of tests" do
let(:file) { "file_1.txt" }
end
describe "second set of tests" do
let(:file) { "file_2.txt" }
end

Generate XML sub items using iteration

I want to use this Ruby code to generate XML file with 10 terminals:
module WriteXML
def write_data_xml
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.genesis {
xml.terminals {
/// create here some loop to iterate
xml.terminal {
xml.name "PPRO_Terminal"
xml.type "ppro"
xml.credentials {
xml.username 'user1'
xml.password 'passwd1'
xml.token '5e36e51de2dde626804a8772dc26238c4d722bbc'
}
}}
////////
}
end
puts builder.to_xml
file = File.new("credentials.xml", "w")
File.open('credentials.xml', 'w') do |file|
file << builder.to_xml
end
end
end
How I can use iteration in order to save code when I want to create many terminals?
Depends on where you keep the data that identify these terminals, is that in a table ? Then you could do something like this
def write_data_xml credential
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.genesis {
xml.terminals {
xml.terminal {
xml.name credential.name
xml.type credential.type
xml.credentials {
xml.username credential.username
xml.password credential.password
xml.token credential.token
}
}}
}
end
File.open("credentials.xml", "a+") { |file| file.write builder.to_xml}
end
end
Suppose you use activerecord you could then
Credentials.each do |credential|
write_data_xml credential
end
If no table, you could use an array of structs where you gather the needed data.
EDIT on request of the OP, here a version that doesn't follow the single responsibility principle
def write_data_xml
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.genesis {
xml.terminals {
Credentials.each do |credential|
xml.terminal {
xml.name credential.name
xml.type credential.type
xml.credentials {
xml.username credential.username
xml.password credential.password
xml.token credential.token
}
end
}}
}
end
File.write("credentials.xml", builder.to_xml)
end
end
EDIT2
here an example of how to use this with an array of structs since there is no database yet
Credentials = []
Credential = Struct.new(:name, :type, :username, :password, :token)
Credentials << Credential.new('PPRO_Terminal', 'ppro', 'user1', 'passwd1', '5e36e51de2dde626804a8772dc26238c4d722bbc')
Credentials << Credential.new( 'PPRO_Terminal2', 'ppro', 'user2', 'passwd2', '...')
p Credentials
[#<struct Credential name="PPRO_Terminal", type="ppro", username="user1", password="passwd1", token="5e36e51de2dde626804a8772dc26238c4d722bbc">, #<struct Credential name="PPRO_Terminal2", type="ppro", username="user2", password="passwd2", token="...">]
NB at least, create this outside of the method

RSpec spies work differently on two different classes

I have a class Uploader which takes a file and uploads it to S3. I'm trying to test that #s3 is actually receiving a file body when upload_file is called. When I test that File is getting messages sent, the test passes. However, trying to spy on Aws::S3::Client does not work.
class Uploader
def initialize(tmp_dir_name, bucket)
#base_tmp_dir = tmp_dir_name
#s3 = Aws::S3::Client.new(region: 'us-east-1')
#bucket = bucket
#uploaded_assets = []
end
def upload_file(key, file_path)
file = File.new(file_path)
#s3.put_object(bucket: #bucket, key: key.to_s, body: file.read)
end
end
RSpec.describe Uploader do
let(:bucket) { 'test_bucket' }
let(:base_temp_dir) { 'test_temp_dir' }
let(:uploader) { Uploader.new(base_temp_dir, bucket) }
describe "#upload_file" do
let(:file) { double('file') }
before { allow(File).to receive(:new) { file } }
before { allow(file).to receive(:read).and_return('text') }
before { allow(Aws::S3::Client).to receive(:put_object) }
it "uses one file" do
uploader.upload_file('test_key', 'file_path')
expect(File).to have_received(:new).with('file_path')
end
it "sends data to s3" do
uploader.upload_file('test_key', 'file_path')
expect(Aws::S3::Client).to have_received(:put_object)
end
end
end
I ended up mocking out s3 for this particular test.
it "sends data to s3" do
test_key = 'test_key'
bucket = 'test_bucket'
fake_s3 = instance_double(Aws::S3::Client)
allow(Aws::S3::Client).to receive(:new).and_return(fake_s3)
allow(fake_s3).to receive(:put_object)
uploader.upload_file(test_key, 'file_path', record=true)
expect(fake_s3).to have_received(:put_object).with(
{bucket: bucket, key: test_key, body: 'text'})
end

How to spec a file download

I am working on a library that needs to be able to download plugin files from a remote API using RestClient. The library first grabs a list of plugins, and then downloads each plugin as a raw file, saving each inside a plugins directory.
Here is what I have thus far but it is failing me:
require 'yaml'
module Monitaur
class Client
attr_accessor :logger, :client_key, :server_url, :config, :raw_config,
:plugin_manifest
def initialize
load_config
#plugin_manifest ||= []
end
def run
get_plugin_manifest
sync_plugins
end
def get_plugin_manifest
res = RestClient.get("#{server_url}/nodes/#{client_key}/plugins")
#plugin_manifest = JSON.parse(res)
end
def sync_plugins
#plugin_manifest.each do |plugin|
res = RestClient.get("#{server_url}/plugins/#{plugin['name']}")
File.open(File.join(Monitaur.plugin_dir, "#{plugin['name']}.rb"), "w+") do |file|
file.write res.body
end
end
end
def load_config
if File.exist?(Monitaur.config_file_path) && File.readable?(Monitaur.config_file_path)
#raw_config = YAML.load_file(Monitaur.config_file_path)
else
raise IOError, "Cannot open or read #{Monitaur.config_file_path}"
end
#server_url = raw_config['server_url']
#client_key = raw_config['client_key']
end
end
end
And the client_spec.rb
require 'spec_helper'
module Monitaur
describe Client do
let(:server_url) { "http://api.monitaurapp.com" }
let(:client_key) { "asdf1234" }
describe "#load_config" do
let(:client) { Monitaur::Client.new }
before do
File.open(Monitaur.config_file_path, "w") do |file|
file.puts "server_url: http://api.monitaurapp.com"
file.puts "client_key: asdf1234"
end
end
it "loads up the configuration file" do
client.load_config
client.server_url.should == "http://api.monitaurapp.com"
client.client_key.should == "asdf1234"
end
end
describe "#get_plugin_manifest" do
let(:client) { Monitaur::Client.new }
before do
stub_get_plugin_manifest
end
it "retrieves a plugins manifest from the server" do
client.get_plugin_manifest
client.plugin_manifest.should == plugin_manifest_response
end
end
describe "#sync_plugins" do
let(:client) { Monitaur::Client.new }
let(:foo_plugin) { mock('foo_plugin') }
let(:bar_plugin) { mock('bar_plugin') }
before do
FileUtils.mkdir("/tmp")
File.open("/tmp/foo_plugin.rb", "w+") do |file|
file.write %|
class FooPlugin < Monitaur::Plugin
name "foo_plugin"
desc "A test plugin to determine whether plugin sync works"
def run
{ :foo => 'foo' }
end
end
|
end
File.open("/tmp/bar_plugin.rb", "w+") do |file|
file.write %|
class BarPlugin < Monitaur::Plugin
name "bar_plugin"
desc "A test plugin to determine whether plugin sync works"
def run
{ :bar => 'bar' }
end
end
|
end
Monitaur.install
stub_get_plugin_manifest
stub_sync_plugins
client.get_plugin_manifest
end
it "downloads plugins to the cache directory" do
File.should_receive(:open).
with(File.join(Monitaur.plugin_dir, "foo_plugin.rb"), "w+")
and_yield(foo_plugin)
client.sync_plugins
File.exist?("/home/user/.monitaur/cache/plugins/foo_plugin.rb").should be_true
File.exist?("/home/user/.monitaur/cache/plugins/bar_plugin.rb").should be_true
end
end
end
end
def stub_get_plugin_manifest
stub_request(:get, "#{server_url}/nodes/#{client_key}/plugins").
to_return(
:status => 200,
:body => %Q{
[
{
"name": "foo_plugin",
"checksum": "qwer5678"
},
{
"name": "bar_plugin",
"checksum": "hjkl4321"
}
]
}
)
end
def plugin_manifest_response
[
{
"name" => "foo_plugin",
"checksum" => "qwer5678"
},
{
"name" => "bar_plugin",
"checksum" => "hjkl4321"
}
]
end
def stub_sync_plugins
stub_request(:get, "#{server_url}/plugins/foo_plugin").
to_return(:body => File.open('/tmp/foo_plugin.rb').read)
stub_request(:get, "#{server_url}/plugins/bar_plugin").
to_return(:body => File.open('/tmp/bar_plugin.rb').read)
end
How can I test the download process?
I use FakeWeb for this purpose, as there's really no need for your spec to fail if the other site is down or something. See "Replaying a recorded response" in the docs. What we do is curl the page, save it somewhere as a fixture and replay that in the specs.

Resources