Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions spec/factories/active_storage_blobs.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# frozen_string_literal: true

FactoryBot.define do
factory :active_storage_blob, class: 'ActiveStorage::Blob' do
factory :active_storage_blob, class: "ActiveStorage::Blob" do
sequence(:filename) { |n| "file_#{n}" }
byte_size { 1234 }
checksum { 'some_checksum' }
created_at { Time.now }
checksum { Digest::MD5.base64digest("factory_blob_data") }
service_name { "db" }
created_at { Time.current }
end
end
35 changes: 24 additions & 11 deletions spec/integration/attachments_spec.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# frozen_string_literal: true

RSpec.describe 'Attachments' do
context 'with a new target entity' do
let(:filename) { 'file1.txt' }
RSpec.describe "Attachments" do
context "with a new target entity" do
let(:filename) { "file1.txt" }
let(:uploaded_file) do
ActionDispatch::Http::UploadedFile.new(
tempfile: file_fixture(filename),
filename: filename,
content_type: 'text/plain'
content_type: "text/plain"
)
end
let(:test_post) { Post.new(title: 'A test post', some_file: uploaded_file) }
let(:test_post) { Post.new(title: "A test post", some_file: uploaded_file) }

around do |example|
# NOTE: touch_attachment_records trigger an extra includes on ActiveStorage::Attachment
Expand All @@ -22,20 +22,20 @@
end
end

it 'creates the entity with the attached file', :aggregate_failures do
it "creates the entity with the attached file", :aggregate_failures do
expect { test_post.save! }.to change(ActiveStorageDB::File, :count).by(1)

expect(test_post.some_file).to be_attached
expect(test_post.some_file.download).to eq file_fixture(filename).read
end
end

context 'with an existing target entity' do
let(:filename) { 'file3.png' }
context "with an existing target entity" do
let(:filename) { "file3.png" }

let!(:test_post) { Post.create!(title: 'A test post') }
let!(:test_post) { Post.create!(title: "A test post") }

it 'attaches the file to the target entity', :aggregate_failures do
it "attaches the file to the target entity", :aggregate_failures do
expect {
test_post.some_file.attach(io: file_fixture(filename).open, filename: filename)
}.to change(ActiveStorageDB::File, :count).from(0).to(1)
Expand All @@ -49,11 +49,24 @@
expect(test_post.some_file.download).to eq file_fixture(filename).binread

blob_path = Rails.application.routes.url_helpers.rails_blob_path(test_post.some_file, only_path: true)
expect(blob_path).to match %r[/rails/active_storage/.+#{filename}]
expect(blob_path).to match %r{/rails/active_storage/.+#{filename}}

expect {
test_post.some_file.purge
}.to change(ActiveStorageDB::File, :count).by(-1)
end

it "replaces an existing attachment", :aggregate_failures, :skip_bullet do
test_post.some_file.attach(io: file_fixture(filename).open, filename: filename)
expect(test_post.some_file).to be_attached

replacement = "file1.txt"
test_post.some_file.attach(io: file_fixture(replacement).open, filename: replacement)
test_post.reload

expect(test_post.some_file).to be_attached
expect(test_post.some_file.filename.to_s).to eq replacement
expect(test_post.some_file.download).to eq file_fixture(replacement).read
end
end
end
65 changes: 57 additions & 8 deletions spec/models/active_storage_db/file_spec.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,65 @@
# frozen_string_literal: true

RSpec.describe ActiveStorageDB::File do
let(:file) { create(:active_storage_db_file, ref: 'just_some_key') }
describe "validations" do
context "when ref is nil" do
it "raises record invalid exception" do
expect { create(:active_storage_db_file, ref: nil) }.to raise_exception(
ActiveRecord::RecordInvalid,
/Ref can't be blank/
)
end
end

context "when ref is blank" do
it "raises record invalid exception" do
expect { create(:active_storage_db_file, ref: "") }.to raise_exception(
ActiveRecord::RecordInvalid,
/Ref can't be blank/
)
end
end

context "when creating a file with an already existing key" do
before { create(:active_storage_db_file, ref: "just_some_key") }

it "raises record invalid exception" do
expect { create(:active_storage_db_file, ref: "just_some_key") }.to raise_exception(
ActiveRecord::RecordInvalid,
/Ref has already been taken/
)
end
end

context "when ref uniqueness is case-insensitive" do
before { create(:active_storage_db_file, ref: "Some_Key") }

context 'when creating a file with an already existing key' do
before { file }
it "rejects a key differing only in case" do
expect { create(:active_storage_db_file, ref: "some_key") }.to raise_exception(
ActiveRecord::RecordInvalid,
/Ref has already been taken/
)
end
end
end

describe "CRUD lifecycle" do
let(:file) { create(:active_storage_db_file, ref: "lifecycle_key") }

it "persists and retrieves binary data", :aggregate_failures do
reloaded = described_class.find(file.id)
expect(reloaded.data).to eq file.data
expect(reloaded.ref).to eq "lifecycle_key"
end

it "updates data" do
file.update!(data: "new content")
expect(file.reload.data).to eq "new content"
end

it 'raises record invalid exception' do
expect { create(:active_storage_db_file, ref: 'just_some_key') }.to raise_exception(
ActiveRecord::RecordInvalid,
/Ref has already been taken/
)
it "destroys the record" do
file
expect { file.destroy! }.to change(described_class, :count).by(-1)
end
end
end
32 changes: 13 additions & 19 deletions spec/requests/file_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,6 @@
# frozen_string_literal: true

RSpec.describe "File controller" do
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain", identify: true, record: nil)
ActiveStorage::Blob.create_and_upload!(
io: StringIO.new(data),
filename: filename,
content_type: content_type,
identify: identify,
record: record
)
end

def create_blob_before_direct_upload(byte_size:, checksum:, filename: "hello.txt", content_type: "text/plain")
ActiveStorage::Blob.create_before_direct_upload!(
filename: filename,
byte_size: byte_size,
checksum: checksum,
content_type: content_type
)
end

def unprocessable
Gem::Version.new(Rails.version) >= Gem::Version.new("7.1") ? :unprocessable_content : :unprocessable_entity
end
Expand Down Expand Up @@ -71,6 +52,19 @@ def unprocessable
expect(response.body).to eq "Hello world!"
end

context "with a blob that has no content type" do
let(:blob) { create_blob(filename: "data.bin", content_type: "application/octet-stream") }

it "serves the file with the stored content type", :aggregate_failures do
blob_url = blob.respond_to?(:url) ? blob.url : blob.service_url
get blob_url

expect(response).to have_http_status(:ok)
expect(response.content_type).to eq("application/octet-stream")
expect(response.body).to eq "Hello world!"
end
end

context "with a deleted blob" do
let!(:blob) { create_blob }

Expand Down
Loading
Loading