# frozen_string_literal: true
require "test_helper"
require "database/setup"
class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
include ActiveJob::TestHelper
setup { @user = User.create!(name: "DHH") }
teardown { ActiveStorage::Blob.all.each(&:purge) }
test "attach existing blob" do
@user.avatar.attach create_blob(filename: "funky.jpg")
assert_equal "funky.jpg", @user.avatar.filename.to_s
end
test "attach existing blob from a signed ID" do
@user.avatar.attach create_blob(filename: "funky.jpg").signed_id
assert_equal "funky.jpg", @user.avatar.filename.to_s
end
test "attach new blob from a Hash" do
@user.avatar.attach io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg"
assert_equal "town.jpg", @user.avatar.filename.to_s
end
test "attach new blob from an UploadedFile" do
file = file_fixture "racecar.jpg"
@user.avatar.attach Rack::Test::UploadedFile.new file.to_s
assert_equal "racecar.jpg", @user.avatar.filename.to_s
end
test "replace attached blob" do
@user.avatar.attach create_blob(filename: "funky.jpg")
perform_enqueued_jobs do
assert_no_difference -> { ActiveStorage::Blob.count } do
@user.avatar.attach create_blob(filename: "town.jpg")
end
end
assert_equal "town.jpg", @user.avatar.filename.to_s
end
test "replace attached blob unsuccessfully" do
@user.avatar.attach create_blob(filename: "funky.jpg")
perform_enqueued_jobs do
assert_raises do
@user.avatar.attach nil
end
end
assert_equal "funky.jpg", @user.reload.avatar.filename.to_s
assert ActiveStorage::Blob.service.exist?(@user.avatar.key)
end
test "replace attached blob with itself" do
@user.avatar.attach create_blob(filename: "funky.jpg")
assert_no_changes -> { @user.reload.avatar.blob } do
assert_no_changes -> { @user.reload.avatar.attachment } do
assert_no_enqueued_jobs do
@user.avatar.attach @user.avatar.blob
end
end
end
end
test "replaced attached blob with itself by signed ID" do
@user.avatar.attach create_blob(filename: "funky.jpg")
assert_no_changes -> { @user.reload.avatar.blob } do
assert_no_changes -> { @user.reload.avatar.attachment } do
assert_no_enqueued_jobs do
@user.avatar.attach @user.avatar.blob.signed_id
end
end
end
end
test "replace independent attached blob" do
@user.cover_photo.attach create_blob(filename: "funky.jpg")
perform_enqueued_jobs do
assert_difference -> { ActiveStorage::Blob.count }, +1 do
assert_no_difference -> { ActiveStorage::Attachment.count } do
@user.cover_photo.attach create_blob(filename: "town.jpg")
end
end
end
assert_equal "town.jpg", @user.cover_photo.filename.to_s
end
test "attach blob to new record" do
user = User.new(name: "Jason")
assert_no_changes -> { user.new_record? } do
assert_no_difference -> { ActiveStorage::Attachment.count } do
user.avatar.attach create_blob(filename: "funky.jpg")
end
end
assert_predicate user.avatar, :attached?
assert_equal "funky.jpg", user.avatar.filename.to_s
assert_difference -> { ActiveStorage::Attachment.count }, +1 do
user.save!
end
assert_predicate user.reload.avatar, :attached?
assert_equal "funky.jpg", user.avatar.filename.to_s
end
test "build new record with attached blob" do
assert_no_difference -> { ActiveStorage::Attachment.count } do
@user = User.new(name: "Jason", avatar: { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" })
end
assert_predicate @user, :new_record?
assert_predicate @user.avatar, :attached?
assert_equal "town.jpg", @user.avatar.filename.to_s
@user.save!
assert_predicate @user.reload.avatar, :attached?
assert_equal "town.jpg", @user.avatar.filename.to_s
end
test "access underlying associations of new blob" do
@user.avatar.attach create_blob(filename: "funky.jpg")
assert_equal @user, @user.avatar_attachment.record
assert_equal @user.avatar_attachment.blob, @user.avatar_blob
assert_equal "funky.jpg", @user.avatar_attachment.blob.filename.to_s
end
test "identify newly-attached, directly-uploaded blob" do
blob = directly_upload_file_blob(content_type: "application/octet-stream")
@user.avatar.attach(blob)
assert_equal "image/jpeg", @user.avatar.reload.content_type
assert_predicate @user.avatar, :identified?
end
test "identify and analyze newly-attached, directly-uploaded blob" do
blob = directly_upload_file_blob(content_type: "application/octet-stream")
perform_enqueued_jobs do
@user.avatar.attach blob
end
assert_equal true, @user.avatar.reload.metadata[:identified]
assert_equal 4104, @user.avatar.metadata[:width]
assert_equal 2736, @user.avatar.metadata[:height]
end
test "identify newly-attached blob only once" do
blob = create_file_blob
assert_predicate blob, :identified?
# The blob's backing file is a PNG image. Fudge its content type so we can tell if it's identified when we attach it.
blob.update! content_type: "application/octet-stream"
@user.avatar.attach blob
assert_equal "application/octet-stream", blob.content_type
end
test "analyze newly-attached blob" do
perform_enqueued_jobs do
@user.avatar.attach create_file_blob
end
assert_equal 4104, @user.avatar.reload.metadata[:width]
assert_equal 2736, @user.avatar.metadata[:height]
end
test "analyze attached blob only once" do
blob = create_file_blob
perform_enqueued_jobs do
@user.avatar.attach blob
end
assert_predicate blob.reload, :analyzed?
@user.avatar.detach
assert_no_enqueued_jobs do
@user.reload.avatar.attach blob
end
end
test "preserve existing metadata when analyzing a newly-attached blob" do
blob = create_file_blob(metadata: { foo: "bar" })
perform_enqueued_jobs do
@user.avatar.attach blob
end
assert_equal "bar", blob.reload.metadata[:foo]
end
test "detach blob" do
@user.avatar.attach create_blob(filename: "funky.jpg")
avatar_blob_id = @user.avatar.blob.id
avatar_key = @user.avatar.key
@user.avatar.detach
assert_not_predicate @user.avatar, :attached?
assert ActiveStorage::Blob.exists?(avatar_blob_id)
assert ActiveStorage::Blob.service.exist?(avatar_key)
end
test "purge attached blob" do
@user.avatar.attach create_blob(filename: "funky.jpg")
avatar_key = @user.avatar.key
@user.avatar.purge
assert_not_predicate @user.avatar, :attached?
assert_not ActiveStorage::Blob.service.exist?(avatar_key)
end
test "purge attached blob later when the record is destroyed" do
@user.avatar.attach create_blob(filename: "funky.jpg")
avatar_key = @user.avatar.key
perform_enqueued_jobs do
@user.reload.destroy
assert_nil ActiveStorage::Blob.find_by(key: avatar_key)
assert_not ActiveStorage::Blob.service.exist?(avatar_key)
end
end
test "delete attachment for independent blob when record is destroyed" do
@user.cover_photo.attach create_blob(filename: "funky.jpg")
@user.destroy
assert_not ActiveStorage::Attachment.exists?(record: @user, name: "cover_photo")
end
test "find with attached blob" do
records = %w[alice bob].map do |name|
User.create!(name: name).tap do |user|
user.avatar.attach create_blob(filename: "#{name}.jpg")
end
end
users = User.where(id: records.map(&:id)).with_attached_avatar.all
assert_equal "alice.jpg", users.first.avatar.filename.to_s
assert_equal "bob.jpg", users.second.avatar.filename.to_s
end
test "attach existing blobs" do
@user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
assert_equal "funky.jpg", @user.highlights.first.filename.to_s
assert_equal "wonky.jpg", @user.highlights.second.filename.to_s
end
test "attach new blobs" do
@user.highlights.attach(
{ io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
{ io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" })
assert_equal "town.jpg", @user.highlights.first.filename.to_s
assert_equal "country.jpg", @user.highlights.second.filename.to_s
end
test "attach blobs to new record" do
user = User.new(name: "Jason")
assert_no_changes -> { user.new_record? } do
assert_no_difference -> { ActiveStorage::Attachment.count } do
user.highlights.attach(
{ io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
{ io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" })
end
end
assert_predicate user.highlights, :attached?
assert_equal "town.jpg", user.highlights.first.filename.to_s
assert_equal "country.jpg", user.highlights.second.filename.to_s
assert_difference -> { ActiveStorage::Attachment.count }, +2 do
user.save!
end
assert_predicate user.reload.highlights, :attached?
assert_equal "town.jpg", user.highlights.first.filename.to_s
assert_equal "country.jpg", user.highlights.second.filename.to_s
end
test "build new record with attached blobs" do
assert_no_difference -> { ActiveStorage::Attachment.count } do
@user = User.new(name: "Jason", highlights: [
{ io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
{ io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" }])
end
assert_predicate @user, :new_record?
assert_predicate @user.highlights, :attached?
assert_equal "town.jpg", @user.highlights.first.filename.to_s
assert_equal "country.jpg", @user.highlights.second.filename.to_s
@user.save!
assert_predicate @user.reload.highlights, :attached?
assert_equal "town.jpg", @user.highlights.first.filename.to_s
assert_equal "country.jpg", @user.highlights.second.filename.to_s
end
test "find attached blobs" do
@user.highlights.attach(
{ io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
{ io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" })
highlights = User.where(id: @user.id).with_attached_highlights.first.highlights
assert_equal "town.jpg", highlights.first.filename.to_s
assert_equal "country.jpg", highlights.second.filename.to_s
end
test "access underlying associations of new blobs" do
@user.highlights.attach(
{ io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
{ io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" })
assert_equal @user, @user.highlights_attachments.first.record
assert_equal @user.highlights_attachments.collect(&:blob).sort, @user.highlights_blobs.sort
assert_equal "town.jpg", @user.highlights_attachments.first.blob.filename.to_s
end
test "analyze newly-attached blobs" do
perform_enqueued_jobs do
@user.highlights.attach(
create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg"),
create_file_blob(filename: "video.mp4", content_type: "video/mp4"))
end
assert_equal 4104, @user.highlights.first.metadata[:width]
assert_equal 2736, @user.highlights.first.metadata[:height]
assert_equal 640, @user.highlights.second.metadata[:width]
assert_equal 480, @user.highlights.second.metadata[:height]
end
test "analyze attached blobs only once" do
blobs = [
create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg"),
create_file_blob(filename: "video.mp4", content_type: "video/mp4")
]
perform_enqueued_jobs do
@user.highlights.attach(blobs)
end
assert blobs.each(&:reload).all?(&:analyzed?)
@user.highlights.attachments.destroy_all
assert_no_enqueued_jobs do
@user.highlights.attach(blobs)
end
end
test "preserve existing metadata when analyzing newly-attached blobs" do
blobs = [
create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", metadata: { foo: "bar" }),
create_file_blob(filename: "video.mp4", content_type: "video/mp4", metadata: { foo: "bar" })
]
perform_enqueued_jobs do
@user.highlights.attach(blobs)
end
blobs.each do |blob|
assert_equal "bar", blob.reload.metadata[:foo]
end
end
test "detach blobs" do
@user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
highlight_blob_ids = @user.highlights.collect { |highlight| highlight.blob.id }
highlight_keys = @user.highlights.collect(&:key)
@user.highlights.detach
assert_not_predicate @user.highlights, :attached?
assert ActiveStorage::Blob.exists?(highlight_blob_ids.first)
assert ActiveStorage::Blob.exists?(highlight_blob_ids.second)
assert ActiveStorage::Blob.service.exist?(highlight_keys.first)
assert ActiveStorage::Blob.service.exist?(highlight_keys.second)
end
test "purge attached blobs" do
@user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
highlight_keys = @user.highlights.collect(&:key)
@user.highlights.purge
assert_not_predicate @user.highlights, :attached?
assert_not ActiveStorage::Blob.service.exist?(highlight_keys.first)
assert_not ActiveStorage::Blob.service.exist?(highlight_keys.second)
end
test "purge attached blobs later when the record is destroyed" do
@user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
highlight_keys = @user.highlights.collect(&:key)
perform_enqueued_jobs do
@user.reload.destroy
assert_nil ActiveStorage::Blob.find_by(key: highlight_keys.first)
assert_not ActiveStorage::Blob.service.exist?(highlight_keys.first)
assert_nil ActiveStorage::Blob.find_by(key: highlight_keys.second)
assert_not ActiveStorage::Blob.service.exist?(highlight_keys.second)
end
end
test "delete attachments for independent blobs when the record is destroyed" do
@user.vlogs.attach create_blob(filename: "funky.mp4"), create_blob(filename: "wonky.mp4")
@user.destroy
assert_not ActiveStorage::Attachment.exists?(record: @user, name: "vlogs")
end
test "selectively purge one attached blob of many" do
first_blob = create_blob(filename: "funky.jpg")
second_blob = create_blob(filename: "wonky.jpg")
attachments = @user.highlights.attach(first_blob, second_blob)
assert_difference -> { ActiveStorage::Blob.count }, -1 do
@user.highlights.where(id: attachments.first.id).purge
end
assert_not ActiveStorage::Blob.exists?(key: first_blob.key)
assert ActiveStorage::Blob.exists?(key: second_blob.key)
end
test "selectively purge one attached blob of many later" do
first_blob = create_blob(filename: "funky.jpg")
second_blob = create_blob(filename: "wonky.jpg")
attachments = @user.highlights.attach(first_blob, second_blob)
perform_enqueued_jobs do
assert_difference -> { ActiveStorage::Blob.count }, -1 do
@user.highlights.where(id: attachments.first.id).purge_later
end
end
assert_not ActiveStorage::Blob.exists?(key: first_blob.key)
assert ActiveStorage::Blob.exists?(key: second_blob.key)
end
end