Ilya Sterin
Ilya Sterin

Reputation: 123

Testing ActiveStorage attachments (FileNotFound)

I'm getting an error testing the ActiveStorage attachment. The code is something like this:

class AssemblyTest < ActiveSupport::TestCase

  test 'Updating svg attachment should upload the updated file' do
    @assembly = Assembly.create(name: assemblies(:head_gasket).name,
                                image: 
    fixture_file_upload('files/track-bar.svg', 'image/svg+xml'))
    assert @assembly.image.attached?
    assert_not_empty @assembly.image.download
  end
end

I'm getting the following error Minitest::UnexpectedError: ActiveStorage::FileNotFoundError: ActiveStorage::FileNotFoundError when @assembly.image.download is called. The attached? assertion is passing, but I can't figure out why the download of the file is failing. Also, nothing shows up in the tmp/storage directory, where the ActiveStorage is configured to store files.

Upvotes: 8

Views: 3723

Answers (4)

I had a same on production and in my case it happened because user uploaded broken png file

so I added validation to prevent corrupted files from being uploaded using the demension option, because broken images will never have a dimension value.

class YourModel < ApplicationRecord
  validates :image, content_type: %w[image/png image/jpg image/jpeg],
            dimension: { width: { min: 50, max: 5000 },
                         height: { min: 50, max: 5000 }}

end

also I used these 3 gems to make everything work properly

#Gemfile

gem "active_storage_validations"
gem "ruby-vips"
gem "image_processing"

Upvotes: 0

jprosevear
jprosevear

Reputation: 401

You can create the blob directly (which is how the direct upload process works) and then attach it so the blob is guaranteed to already be uploaded.

blob = ActiveStorage::Blob.create_and_upload!(
  io: File.open(Rails.root.join("test/fixtures/files/test.csv")),
  filename: "test.csv",
  content_type: "text/csv",
  identify: false
)
@model.file.attach(blob)

Upvotes: 1

mordaroso
mordaroso

Reputation: 1550

While digging in the ActiveStorage code I found this snipped which relies on actual database commits to execute the document upload (or save to disc):

after_commit(on: %i[ create update ]) { attachment_changes.delete(name.to_s).try(:upload) }

In case you use database transactions in the test environment this will then not store the document.

To solve this you can trigger the commit callback manually:

run_callbacks(:commit)

So in your case this might work:

class AssemblyTest < ActiveSupport::TestCase

  test 'Updating svg attachment should upload the updated file' do
    @assembly = Assembly.create(name: assemblies(:head_gasket).name,
                                image: 
    fixture_file_upload('files/track-bar.svg', 'image/svg+xml'))
    @assembly.run_callbacks(:commit) # Run commit callback to store on disk
    assert @assembly.image.attached?
    assert_not_empty @assembly.image.download
  end
end

Upvotes: 7

mechnicov
mechnicov

Reputation: 15248

Try this

@assembly = Assembly.create(name: assemblies(:head_gasket).name)
@assembly.image.attach(io: File.open('/path/to/file'), filename: 'file.name', content_type: 'mime/type')

Upvotes: 0

Related Questions