Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #4635 (assigned defect)

Opened 3 years ago

Last modified 1 year ago

unable to test file uploads with integration testing framework

Reported by: johnwilger@gmail.com Assigned to: turnip (accepted)
Priority: normal Milestone:
Component: ActionPack Version: edge
Severity: normal Keywords: integration test file upload
Cc: turnip@turnipspatch.com, someone23

Description

It's currently not possible to test file uploads in the integration testing framework, because the MockCGI object marshals all of the inputs into a StringIO, and does not support the multipart/form-data needed for file uploading.

Attachments

integration_test_file_upload.rb (1.3 kB) - added by maxlapshin on 02/18/07 15:15:51.
support for integration test upload
integration_test_file_upload_deep.rb (1.4 kB) - added by someone23 on 10/10/07 07:03:10.
integration test file upload support with deeply nested params hash

Change History

04/07/06 02:40:23 changed by johnwilger@gmail.com

Here's a quick example of what happens:

Here's the call in the integration test:

s.post_via_redirect( color_rounds_create_url( :view_id => view.id ),

:file => fixture_file_upload( '/images/1.pdf' ) )

and here's the result of a breakpoint in the action method:

irb(#<ColorRoundsController:0x2335bdc>):001:0> params => {"action"=>"create", "controller"=>"color_rounds", "file"=>"#<ActionController::TestUploadedFile:0x2361d18>", "view_id"=>"3"}

As you can see, the input for the file parameter had #to_s called on it before being passed into the controller.

04/08/06 11:45:24 changed by anonymous

I've verified this bug too. Both integration and functional tests have the same bug.

(follow-up: ↓ 7 ) 04/08/06 17:01:38 changed by johnwilger@gmail.com

"Both integration and functional tests have the same bug."

Hmm... works fine for me in the functional tests. I've only noticed the bug in the integration tests.

04/25/06 14:50:25 changed by andrew.preece@gmail.com

I'm also having troubles with this bug. Any chance of a fix or workaround?

05/24/06 20:59:07 changed by ocher@ocher.one.pl

There is some model-side, temporary fix for this problem. Instead of doing:

def photo=(picture_field)

@data = picture_field.read

end

write:

def photo=(picture_field)

@data = picture_field.respond_to?(:read) ? picture_field.read : picture_field

end

It works both with functional and integration tests.

06/17/06 21:18:06 changed by blake@near-time.com

Instead of checking for the read method, I'd put this in the test_helper.rb:

class StringIO
  def read
    self.to_s
  end
end

Instead of checking for the read method, just mock it out in the tests...

(in reply to: ↑ 3 ) 09/10/06 21:48:51 changed by anonymous

I had the problem in functional and integration tests. I'm using Rick Olson's restful_authentication plugin and I fixed this problem for my functional tests by commenting out the content_type function.

01/22/07 05:58:42 changed by smeade

see actionpack/lib/action_controller/test_process.rb/TestUploadedFile

does that help?

(in reply to: ↑ description ) 02/08/07 10:49:47 changed by turnip

  • cc set to turnip@turnipspatch.com.
  • owner changed from David to turnip.
  • status changed from new to assigned.

I'll probably take a look at this bug - I wrote the original TestUploadedFile class. I've had a look at the code. I guess there needs to be some way to specify the content_type of the request when calling the post method (which would obviously default to application/x-www-form-urlencoded). We could try to detect the content-type based on the presence of a TestUploadedFile in the parameters but I think that is the wrong way to do it - you have to specify content-type in a HTML form anyway.

For my reference at a later point, see RFC 1867: http://www.ietf.org/rfc/rfc1867.txt

02/18/07 15:15:26 changed by maxlapshin

  • keywords set to integration test file upload.

My patch does the following: it marshall the parameters to multipart form.

Usage is:

1. require this file in test/helper.rb

2.

  multipart_post "/photos/create", {:photo => {:post_id => id, :photo => fixture_file_upload("/files/test.gif", "image/gif")}}

02/18/07 15:15:51 changed by maxlapshin

  • attachment integration_test_file_upload.rb added.

support for integration test upload

04/25/07 13:55:56 changed by maxlapshin

  • version changed from 1.1.0 to edge.

I wonder, what will happen with this bug. multipart_post seems to be a rather clean way. Better, if we add some clean mechanism, that perform transparent switching between clean POST and multipart_post. But this bug should be closed and there is a solution.

04/25/07 14:15:46 changed by helzer

Hello,

I've tried using integration_test_file_upload.rb but am getting truncated files when uploading. Does it work with Rails 1.2.3?

I'm using acts_as_attachment for the model. It works just fine with browser uploads, but gets truncated files on the tests.

My test code for uploading is:

fdata = fixture_file_upload(fname, 'application/octet-stream')
multipart_post url_for(:controller=>:support_files, :action=>:create),
                :session=>session, :project_id=>project_id,
                :support_file=>{'uploaded_data'=>fdata}, :format=>'xml' 

Thanks, Amir

04/25/07 14:19:09 changed by maxlapshin

What is :support_file=>{'uploaded_data'=>fdata}, :format=>'xml' ? I've written multipart_post, assuming, that You use fixture_file_upload method right as in functional tests

04/25/07 17:46:57 changed by helzer

Thanks for the speedy reply.

Before I started using your patch, it just sent a useless string for the upload, without the content_type, or filename or anything else. Then, it sent the correct structure, but the body of the uploaded file is truncated.

The :request=>'xml' is just for setting the correct response_to at the end of my handling action. It doesn't do anything to the file upload.

fdata = fixture_file_upload(fname, 'application/octet-stream') is what gets assigned to the :upload_data field. Acts_as_attachment expects to see the upload file in that field. And, my action expects to find the entire thing inside the :support_file parameter.

Here is the handling action:

def create
 begin
  support_file = SupportFile.create! params[:support_file]
  @result = {"message" => "Support_File created", "id" => support_file.id}
 rescue ActiveRecord::RecordInvalid
  @result = {"message" => "Support_File failed"}
 end
 respond_to do |format|
  format.html
  format.xml
 end
end

And, last, this is the model for the SupportFile class:

class SupportFile < ActiveRecord::Base
 acts_as_attachment :storage => :file_system, :max_size => 4000.kilobytes,
                    :file_system_path => "private/#{table_name}"
 validates_as_attachment

What I'm seeing when I look at the file that gets saved on the disk (after being uploaded) is that it's just truncated. It's identical up to a certain point, and then just ends. The truncation is different for different files that I try to upload. Always several hundred bytes.

04/25/07 18:02:25 changed by helzer

It appears as if the problem is contents dependent.

All my previous tests were with .gz files (which I need for my application).

When I upload non compressed files, your plug-in works just fine. I'm getting problems when uploading .gz (gnu zip) files.

This is an example: styles.css.gz. The original file styles.css goes just fine.

I guess they contain something that causes the multi-part upload to think it's done.

(in reply to: ↑ description ) 04/25/07 19:24:04 changed by chuckbergeron

Throwing the attached integration_test_file_upload.rb file in lib/ and requiring it at the top of my Integration test worked beauitfully for me. I'm using my own polymorphic file upload handling, instead of relying on acts_as_attachment (which I'm sure works wonders, but I'd rather know how to do this stuff myself).

Just wanted to say thanks! And wondering if this will make it into Rails itself, as it's very helpful.

(follow-up: ↓ 18 ) 04/25/07 20:07:53 changed by maxlapshin

Ok, I'll test it on your files.

(in reply to: ↑ 17 ) 04/30/07 03:48:44 changed by helzer

Replying to maxlapshin:

Do you see the same error as I'm seeing, or is it something that I'm doing wrong?

05/08/07 12:40:59 changed by helzer

I've overlooked the file open mode in the 'integration_test_file_upload.rb' file. Once changed the file open mode to rb, it runs just fine. I think that line 26 should be:

File.open(value.path,'rb') do |f|

05/08/07 13:02:58 changed by maxlapshin

It's great, that You've located the problem. I don't have any Windows at all, thus I couldn't repeat this bug.

06/25/07 19:26:26 changed by matt

  • summary changed from unable to test file uploads with integration testing framework to [PATCH] unable to test file uploads with integration testing framework.

prefix the summary with [PATCH] so TRAC can manage this ticket properly.

06/25/07 21:32:43 changed by matt

  • summary changed from [PATCH] unable to test file uploads with integration testing framework to unable to test file uploads with integration testing framework.

not really a patch but a separate file.

(follow-up: ↓ 27 ) 07/03/07 21:13:24 changed by someone23

multipart_post doesn't appear to support deeply nested hashes in the params.

If I try:

multipart_post "/c/a/i", :key1 => {:key2 => 'val2', 
                                   :key3 => {:key4 => {:key5 => 'val5', :key6 => 'val6'},
                                             :key7 => {:key8 => '', :key9 => ''},
                                             :key10 => {:key11 => 'val11', :key12 => 'val12'}},
                                   :uploaded_file => fixture_file_upload("path", 'image/jpg')},
                         :key13 => 'val13'

The logs show that the params received were:

{"action"=>"a", "id"=>"i", "controller"=>"c", "key13"=>"val13", "key3"=>{"uploaded_file"=>#<File:/tmp/CGI650-0>, "key3"=>"key4key5val5key6val6key7key8key9key10key11val11key12val12", "key2"=>"val2"}}

When I would expect it to be a hash much like what I passed to multipart_post, like the normal post works. Am I using it wrong? Does multipart_post expect the params in a different format?

07/03/07 21:29:27 changed by someone23

  • cc changed from turnip@turnipspatch.com to turnip@turnipspatch.com, someone23.

(follow-up: ↓ 26 ) 10/06/07 01:52:50 changed by eggie5

Here is my test helper code to do a file post:

  def uploaded_file(path, content_type="application/octet-stream", filename=nil)
      filename ||= File.basename(path)
      t = Tempfile.new(filename)
      FileUtils.copy_file(path, t.path)
      (class << t; self; end;).class_eval do
        alias local_path path
        define_method(:original_filename) { filename }
        define_method(:content_type) { content_type }
      end
      return t
    end

It works fine in my functional tests, but fails in my integration tests:

NoMethodError (undefined method `original_filename' for "#<File:0x316c300>":String):
    /app/models/attachment.rb:21:in `file='
    /vendor/rails/activerecord/lib/active_record/base.rb:1846:in `send'
    /vendor/rails/activerecord/lib/active_record/base.rb:1846:in `attributes='
    /vendor/rails/activerecord/lib/active_record/base.rb:1845:in `each'
    /vendor/rails/activerecord/lib/active_record/base.rb:1845:in `attributes='
    /vendor/rails/activerecord/lib/active_record/base.rb:1672:in `initialize'
    /app/models/attachment.rb:11:in `initialize'

(in reply to: ↑ 25 ) 10/06/07 02:47:00 changed by eggie5

The attached file is working for me as a workaround. Save the file to you test dir and add this to the top of your integration test: require "#{File.dirname(FILE)}/../integration_test_file_upload"

Then you have access to the multipart_post method

10/10/07 07:03:10 changed by someone23

  • attachment integration_test_file_upload_deep.rb added.

integration test file upload support with deeply nested params hash

(in reply to: ↑ 23 ) 10/10/07 07:06:36 changed by someone23

Replying to someone23:

multipart_post doesn't appear to support deeply nested hashes in the params. If I try: {{{ multipart_post "/c/a/i", :key1 => {:key2 => 'val2', :key3 => {:key4 => {:key5 => 'val5', :key6 => 'val6'}, :key7 => {:key8 => , :key9 => }, :key10 => {:key11 => 'val11', :key12 => 'val12'}}, :uploaded_file => fixture_file_upload("path", 'image/jpg')}, :key13 => 'val13' }}} The logs show that the params received were: {{{ {"action"=>"a", "id"=>"i", "controller"=>"c", "key13"=>"val13", "key3"=>{"uploaded_file"=>#<File:/tmp/CGI650-0>, "key3"=>"key4key5val5key6val6key7key8key9key10key11val11key12val12", "key2"=>"val2"}} }}} When I would expect it to be a hash much like what I passed to multipart_post, like the normal post works. Am I using it wrong? Does multipart_post expect the params in a different format?

I finally got off my ass and solved my problem. It shouldn't matter how deeply you nest your params with integration_test_file_upload_deep.rb.

(in reply to: ↑ description ) 12/15/07 20:53:20 changed by riffraff

still broken on rails 2, afaict. Any chance to include the patch ?

05/01/08 11:30:50 changed by jkraemer

I'm using a tiny plugin to fix integration testing of file uploads:

http://projects.jkraemer.net/svn/plugins/multipart_integration_test/