Rails testing fileupload receives error "File not found" - ruby-on-rails

I am using RSpec (Rails 3.2) to test my controllers. I have a controller which also contains a fileupload (using CarrierWave), but I keep getting the error:
Failure/Error: "image" => fixture_file_upload(Rails.root.join('spec',
'fixtures', 'files', 'image.png'), 'image/png')
RuntimeError:
.../spec/fixtures/files/image.png file does not exist
In my controller I have defined the image upload like this:
def valid_attributes
{ "title" => "My own title",
"description" => "Something cool",
"image" => fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'image.png'), 'image/png')
}
end
I have of course checked that file exists, but might there be something else that I have overlooked?

Seems you have to use Rack::Test::UploadedFile.new rather than fixture_file_upload in Rails 3.2:
def valid_attributes
{ "title" => "My own title",
"description" => "Something cool",
"image" => Rack::Test::UploadedFile.new(Rails.root.join('spec', 'fixtures', 'files', 'image.png'), 'image/png')
}
end
See this SO question/answers: fixture_file_upload has {file} does not exist error

Related

Rails 5, Swagger UI not generating documentation json files for controllers

I was following this Swagger tutorial to get familiar with the swagger environment and API documenting. I looked all the related issues on the sample app's repo but none of them worked for me.
When I run rake swagger:docs it's supposed to generate users.json file under public/api/v1/ hovewer it doesn't. It only generates api-docs.json file.
Also in the terminal gives the message 1.0: 0 processed / 4 skipped.
I tried adding Swagger::Docs::Config.base_api_controller = ActionController::API which did not solved the issue either.
Sharing the basic files. If you need any further informations I will gladly share them with you. Hope you can help, really stuck here. Thank you.
swagger_docs.rb
# config/initializers/swagger-docs.rb
Swagger::Docs::Config.base_api_controller = ActionController::API
Swagger::Docs::Config.register_apis({
"1.0" => {
:api_file_path => "public/",
:base_path => "http://localhost:3000",
:clean_directory => true,
:base_api_controller => ActionController::API,
:attributes => {
:info => {
"title" => "Swagger Doc",
"description" => "Sample app shows how to setup swagger for your Ruby APIs",
"contact" => "recepinancc#gmail.com",
"license" => "Apache 2.0",
"licenseUrl" => "http://www.apache.org/licenses/LICENSE-2.0.html"
}
}
}
})
users_controller.rb
class Api::V1::UsersController < ApplicationController
swagger_controller :users, "User Management"
# /api/v1/users create documentation
swagger_api :create do
summary "To create user"
notes "Implementation notes, such as required params, example queries for apis are written here."
param :form, "user[name]", :string, :required, "Name of user"
param :form, "user[age]", :integer, :optional, "Age of user"
param_list :form, "user[status]", :string, :required, "Status of user, can be active or inactive"
response :success
response :unprocessable_entity
response :500, "Internal Error"
end
# POST /api/v1/users
def create
...
end
...
end
And here's the only generated api-docs.json
{
"apiVersion": "1.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:3000",
"apis": [
],
"authorizations": null,
"info": {
"title": "Swagger Doc",
"description": "Sample app shows how to setup swagger for your Ruby APIs",
"contact": "recepinancc#gmail.com",
"license": "Apache 2.0",
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
}
I have find the solution to my problem comparing a running application to mine. And the only difference was the routes.rb file. Apparently I and Rails are skipped the step generating the nested route for our user model in routes.rb. After I added below to routes.rb problem solved.
namespace :api do
namespace :v1 do
resources :users
end
end
Hope it helps.
Try this in your swagger_docs.rb
include Swagger::Docs::ImpotentMethods
# Swagger::Docs::Config.register_apis({})
class Swagger::Docs::Config
def self.base_api_controller
ActionController::API
end
end
Or putting this at top of routes.rb
Swagger::Docs::Config.base_api_controller = ActionController::API
include Swagger::Docs::ImpotentMethods

Defining fabricator w/ file attachment & testing file upload w/ rspec

I'm trying to figure out the how to test file attachments w/ the Fabrication and Rspec gems. The file upload works fine when testing the site manually, there's just no Rspec coverage. The problem seems to be that I don't know how to include an attachment in a PUT request.
How do I add a file attachment, preferably using a fabricator, to this test
Fabricator:
Fabricator(:application) do
email Faker::Internet.email
name Faker::Name.name
resume_url { File.open(
File.join(
Rails.root,
"spec",
"support",
"files",
"hey_look_a_pdf_pdf_lolz.pdf"
)
)
}
end
Controller:
class ApplicationsController < ApplicationController
def update
#application = Application.find_or_initialize_by(id: params[:id])
if #application.update(application_params)
flash[:success] = "Application has been saved :)"
redirect_to application_path(#application)
else
render :edit
end
end
private
def application_params
params[:application].permit(:email, :job_id, :name, :resume_url)
end
end
Controller test
require "spec_helper"
# this is a sample application attributes being passed into controller
# it should have a file attachment, but haven't figured out how to do that
#
# {
# "id"=>"fa446fdf-b82d-48c0-8979-cbbb2de4fb47",
# "email"=>"old#example.com",
# "name"=>"Dr. Rebeca Dach",
# "resume_url"=>nil,
# "job_id"=>"cf66dbcf-d110-42cc-889b-0b5ceeeed239",
# "created_at"=>nil,
# "updated_at"=>nil
# }
describe ApplicationsController do
context "PUT /applications/:id" do
it "creates an application" do
expect { put(
:update,
application: application.attributes,
id: application.id
)}.to change{Application.count}.from(0).to(1)
end
end
end
Update #1
The following Fabricator seems to work okay. I'm still not able to get file attachments to work in controller tests.
Fabricator(:application) do
email Faker::Internet.email
name Faker::Name.name
resume_url { ActionDispatch::Http::UploadedFile.new(
tempfile: File.new(Rails.root.join(
"./spec/support/files/hey_look_a_pdf_pdf_lolz.pdf"
)),
filename: File.basename(File.new(Rails.root.join(
"./spec/support/files/hey_look_a_pdf_pdf_lolz.pdf"
)))
)}
end
OK, I got it figured out. There were two pieces.
1) I had to fix the Fabricator, which I mentioned in "Update #1" to my question. Here's a simpler format for the Fabricator using Rack::Test::Upload.new
Fabricator(:application) do
email Faker::Internet.email
name Faker::Name.name
resume_url {
Rack::Test::UploadedFile.new(
"./spec/support/files/hey_look_a_pdf_pdf_lolz.pdf",
"application/pdf"
)
}
end
2) I was using Fabricator.build(:application).attributes, which wasn't compatible with a file attachment. Instead, I started using Fabricator.attributes_for(:application), and everything started working great. Here's the passing.
describe ApplicationsController do
context "PUT /applications/:id" do
let(:job) { Fabricate(:job) }
let(:application) do
Fabricate.attributes_for(
:application,
email: "old#example.com",
id: SecureRandom.uuid,
job_id: job.id
)
end
it "creates an application" do
expect { put(
:update,
application: application,
id: application["id"]
)}.to change{Application.count}.from(0).to(1)
end
end
end

FactoryGirl calling `original_filename` for an object in rspec

I'm working the a Documents class, trying to test it. I've defined the following factory:
require 'factory_girl'
FactoryGirl.define do
factory :document do
user_id '6315'
name 'Test doc'
description 'W9'
filename 'test_doc.pdf'
filetype 'file'
filesize 500
end
factory :invalid_doc, parent: :document do
filesize 5242900
end
end
with the following helper method to access the right attributes in the test:
def build_attributes(*args)
attrs = FactoryGirl.build(*args).attributes
attrs.delete_if do |k, v|
["id", "created_at", "updated_at"].member?(k)
end
paramify_values(attrs)
end
Before each test I run:
before(:each) do
login_as_admin
#doc = #user.documents.create(FactoryGirl.attributes_for(:document))
end
where #user is set in the login_as_admin macro. Within my test, I'm running this:
describe 'POST #create' do
it "should create a new document" do
expect{
post :create, document: build_attributes(:document, user_id: #doc.user_id)
}.to change(Document,:count).by(1)
end
it "should find the right user" do
post :create, document: build_attributes(:document, user_id: #doc.user_id)
assigns(:user).should eq(#user)
end
# some other tests...
end
The former test was suggested on this article, the latter is just what I think should be happening. The controller action is assigning the instance with the following:
#user = User.find(document[:user_id])
so, pretty standard. However, both of these tests throw the same error,
Failure/Error: post :create, document: build_attributes(:document, user_id: #doc.user_id)
NoMethodError:
undefined method `original_filename' for nil:NilClass
but I never call that method explicitly, so is it something FactoryGirl is calling? The model is described as follows:
attr_accessible :description, :filename, :filesize, :filetype, :name, :user_id
where :filename is just a string. What could be going wrong here? I'm not using paperclip to upload the files, just a file_field in the view. I grab the path and save the file to the production server in the controller, but never call this method.
Edit:
I suppose an actual controller description might help haha
def create
uploaded_file = params[:document][:file]
document = params[:document]
document.delete(:file)
#user = User.find(document[:user_id])
filepath = Rails.root.join('documents', #user.company_id.to_s, #user.id.to_s, uploaded_file.original_filename)
%x[ mkdir #{Rails.root.join('documents', #user.company_id.to_s)} ]
%x[ mkdir #{Rails.root.join('documents', #user.company_id.to_s, #user.id.to_s)} ]
File.open(filepath, 'wb') do |file|
file.write(uploaded_file.read)
end
document[:filesize]= File.size(filepath)
document[:filetype]= File.ftype(filepath)
document[:filename] = uploaded_file.original_filename
d =Document.new(document)
d.save
redirect_to :action => 'show', :id => user.id
end
Please keep in mind I'm sure there are many things wrong with this method. I'm trying to refactor it and test as I go. For the moment, all I'm trying to do is get past this first hiccough, the original_filename method is being called somewhere, and I don't define it myself. Can anyone see why/where?
original_filename is a method on an uploaded file, see the rack documentation.
filepath = Rails.root.join('documents', #user.company_id.to_s, #user.id.to_s, uploaded_file.original_filename)
and
document[:filename] = uploaded_file.original_filename
In the controller are getting the original filename, since when a file gets uploaded it gets an ugly temp filename for storage you want to use the original filename to make it readable and accurate.
Consider using the fixture_file_upload helper in rspec. Here is an example spec:
expect {
post :create, document: attributes_for(:document, user_id: #doc.user_id, file: fixture_file_upload('spec/assets/documents/test_doc.pdf', 'appliation/pdf'))
}.to change(Document, :count).by(1)
And place a test pdf in spec/assets/documents/test_doc.pdf
You can use
Rack::Multipart::UploadedFile.new(path)
for your test.

Paperclip::Attachment passed as string when using FactoryGirl

I am trying to create a functional test for paperclip 3.1.2 on rails 3.2.6 using FactoryGirl 3.0 and when i pass the file attachment my controller receives a string which is the file location and not the Paperclip::Attachment object.
My factory is:
include ActionDispatch::TestProcess
FactoryGirl.define do
factory :artist do
id 1
name 'MyString'
photo {fixture_file_upload("#{Rails.root}/test/fixtures/files/rails.png",'image/png')}
end
end
And my test is:
test "should create artist" do
assert_difference('Artist.count') do
post :create, :artist => { name: #artist.name, photo: #artist.photo }, :html => { :multipart => true }
end
assert_redirected_to artist_path(assigns(:artist))
end
In my controller this :
params[:artist][:photo].class.name
equals "String", but this passes when i add it to the test
assert #artist.photo.class.name == "Paperclip::Attachment"
My current workaround is to create the fixture_file_upload in the test :
test "should create artist" do
assert_difference('Artist.count') do
photo = fixture_file_upload("/files/rails.png",'image/png')
post :create, :artist => { name: #artist.name, photo: photo }, :html => { :multipart => true }
end
assert_redirected_to artist_path(assigns(:artist))
end
which works but creates a "Rack::Test::UploadedFile" and so I'm pretty confused why my factory returns a "Paperclip::Attachment" when the same method is called to create them both.
Thanks in advance for any light that you can shed on this as obviously I would like to use the factory rather than defining the fixture_file_upload outside of it.
Have you tried to replace
photo {fixture_file_upload("#{Rails.root}/test/fixtures/files/rails.png",'image/png')}
with
photo File.new("#{Rails.root}/test/fixtures/files/rails.png")
inside your factory.

test a file upload using rspec - rails

I want to test a file upload in rails, but am not sure how to do this.
Here is the controller code:
def uploadLicense
#Create the license object
#license = License.create(params[:license])
#Get Session ID
sessid = session[:session_id]
puts "\n\nSession_id:\n#{sessid}\n"
#Generate a random string
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
newpass = ""
1.upto(5) { |i| newpass << chars[rand(chars.size-1)] }
#Get the original file name
upload=params[:upload]
name = upload['datafile'].original_filename
#license.format = File.extname(name)
#calculate license ID and location
#license.location = './public/licenses/' + sessid + newpass + name
#Save the license file
#Fileupload.save(params[:upload], #license.location)
File.open(#license.location, "wb") { |f| f.write(upload['datafile'].read) }
#Set license ID
#license.license_id = sessid + newpass
#Save the license
#license.save
redirect_to :action => 'show', :id => #license.id
end
I have tried this spec, but it doesnt work:
it "can upload a license and download a license" do
file = File.new(Rails.root + 'app/controllers/lic.xml')
license = HashWithIndifferentAccess.new
license[:datafile] = file
info = {:id => 4}
post :uploadLicense, {:license => info, :upload => license}
end
How can I simulate the file upload, using rspec?
You can use fixture_file_upload method to test file uploading:
Put your test file in "{Rails.root}/spec/fixtures/files" directory
before :each do
#file = fixture_file_upload('files/test_lic.xml', 'text/xml')
end
it "can upload a license" do
post :uploadLicense, :upload => #file
response.should be_success
end
In case you were expecting the file in the form of params['upload']['datafile']
it "can upload a license" do
file = Hash.new
file['datafile'] = #file
post :uploadLicense, :upload => file
response.should be_success
end
I am not sure if you can test file uploads using RSpec alone. Have you tried Capybara?
It's easy to test file uploads using capybara's attach_file method from a request spec.
For example (this code is a demo only):
it "can upload a license" do
visit upload_license_path
attach_file "uploadLicense", /path/to/file/to/upload
click_button "Upload License"
end
it "can download an uploaded license" do
visit license_path
click_link "Download Uploaded License"
page.should have_content("Uploaded License")
end
if you include Rack::Test*, simply include the test methods
describe "my test set" do
include Rack::Test::Methods
then you can use the UploadedFile method:
post "/upload/", "file" => Rack::Test::UploadedFile.new("path/to/file.ext", "mime/type")
*NOTE: My example is based on Sinatra, which extends Rack, but should work with Rails, which also uses Rack, TTBOMK
I haven't done this using RSpec, but I do have a Test::Unit test that does something similar for uploading a photo. I set up the uploaded file as an instance of ActionDispatch::Http::UploadedFile, as follows:
test "should create photo" do
setup_file_upload
assert_difference('Photo.count') do
post :create, :photo => #photo.attributes
end
assert_redirected_to photo_path(assigns(:photo))
end
def setup_file_upload
test_photo = ActionDispatch::Http::UploadedFile.new({
:filename => 'test_photo_1.jpg',
:type => 'image/jpeg',
:tempfile => File.new("#{Rails.root}/test/fixtures/files/test_photo_1.jpg")
})
#photo = Photo.new(
:title => 'Uploaded photo',
:description => 'Uploaded photo description',
:filename => test_photo,
:public => true)
end
Something similar might work for you also.
This is how I did it with Rails 6, RSpec and Rack::Test::UploadedFile
describe 'POST /create' do
it 'responds with success' do
post :create, params: {
license: {
picture: Rack::Test::UploadedFile.new("#{Rails.root}/spec/fixtures/test-pic.png"),
name: 'test'
}
}
expect(response).to be_successful
end
end
DO NOT include ActionDispatch::TestProcess or any other code unless you're sure about what you're including.
I had to add both of these includes to get it working:
describe "my test set" do
include Rack::Test::Methods
include ActionDispatch::TestProcess

Resources