2019-04-29 20:27:42 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-09-13 01:24:08 -04:00
|
|
|
RSpec.describe UploadRecovery do
|
2019-05-06 23:12:20 -04:00
|
|
|
fab!(:user) { Fabricate(:user) }
|
2018-09-13 01:24:08 -04:00
|
|
|
|
|
|
|
let(:upload) do
|
|
|
|
UploadCreator.new(
|
2018-09-18 22:44:36 -04:00
|
|
|
file_from_fixtures("smallest.png"),
|
2018-09-13 01:24:08 -04:00
|
|
|
"logo.png"
|
|
|
|
).create_for(user.id)
|
|
|
|
end
|
|
|
|
|
2018-09-18 22:44:36 -04:00
|
|
|
let(:upload2) do
|
|
|
|
UploadCreator.new(
|
|
|
|
file_from_fixtures("small.pdf", "pdf"),
|
|
|
|
"some.pdf"
|
|
|
|
).create_for(user.id)
|
|
|
|
end
|
|
|
|
|
2018-09-13 01:24:08 -04:00
|
|
|
let(:post) do
|
|
|
|
Fabricate(:post,
|
2020-10-01 08:54:45 -04:00
|
|
|
raw: "![logo.png](#{upload.short_url})",
|
2018-09-13 01:24:08 -04:00
|
|
|
user: user
|
2018-09-13 01:41:38 -04:00
|
|
|
).tap(&:link_post_uploads)
|
2018-09-13 01:24:08 -04:00
|
|
|
end
|
|
|
|
|
2018-09-13 01:59:17 -04:00
|
|
|
let(:upload_recovery) { UploadRecovery.new }
|
|
|
|
|
2018-09-13 01:24:08 -04:00
|
|
|
before do
|
2018-09-18 22:44:36 -04:00
|
|
|
SiteSetting.authorized_extensions = 'png|pdf'
|
2019-03-14 10:47:38 -04:00
|
|
|
Jobs.run_immediately!
|
2018-09-13 01:24:08 -04:00
|
|
|
end
|
|
|
|
|
2018-09-30 22:51:25 -04:00
|
|
|
after do
|
|
|
|
[upload, upload2].each do |u|
|
2018-10-01 02:20:50 -04:00
|
|
|
next if u
|
2018-09-30 22:51:25 -04:00
|
|
|
public_path = "#{Discourse.store.public_dir}#{u.url}"
|
|
|
|
|
|
|
|
[
|
|
|
|
public_path,
|
|
|
|
public_path.sub("uploads", "uploads/tombstone")
|
2022-01-05 12:45:08 -05:00
|
|
|
].each { |path| File.delete(path) if File.exist?(path) }
|
2018-09-13 01:59:17 -04:00
|
|
|
end
|
2018-09-30 22:51:25 -04:00
|
|
|
end
|
2018-09-13 01:59:17 -04:00
|
|
|
|
2018-09-30 22:51:25 -04:00
|
|
|
describe '#recover' do
|
2018-09-13 01:59:17 -04:00
|
|
|
describe 'when given an invalid sha1' do
|
2020-08-27 09:57:10 -04:00
|
|
|
it 'does nothing' do
|
2018-09-13 01:59:17 -04:00
|
|
|
upload_recovery.expects(:recover_from_local).never
|
|
|
|
|
|
|
|
post.update!(
|
|
|
|
raw: "![logo.png](upload://#{'a' * 28}.png)"
|
|
|
|
)
|
|
|
|
|
|
|
|
upload_recovery.recover
|
2018-09-20 02:21:57 -04:00
|
|
|
|
|
|
|
post.update!(
|
|
|
|
raw: "<a href=#{"/uploads/test/original/3X/a/6%0A/#{upload.sha1}.png"}>test</a>"
|
|
|
|
)
|
|
|
|
|
|
|
|
upload_recovery.recover
|
2018-09-13 01:24:08 -04:00
|
|
|
end
|
|
|
|
end
|
2018-09-13 01:59:17 -04:00
|
|
|
|
2020-08-27 09:57:10 -04:00
|
|
|
it 'accepts a custom ActiveRecord relation' do
|
2018-09-13 04:32:35 -04:00
|
|
|
post.update!(updated_at: 2.days.ago)
|
|
|
|
upload.destroy!
|
|
|
|
|
|
|
|
upload_recovery.expects(:recover_from_local).never
|
|
|
|
upload_recovery.recover(Post.where("updated_at >= ?", 1.day.ago))
|
|
|
|
end
|
|
|
|
|
2018-09-18 23:52:57 -04:00
|
|
|
describe 'for a missing attachment' do
|
|
|
|
let(:post) do
|
|
|
|
Fabricate(:post,
|
|
|
|
raw: <<~SQL,
|
|
|
|
<a class="attachment" href="#{upload2.url}">some.pdf</a>
|
|
|
|
<a>blank</a>
|
|
|
|
SQL
|
|
|
|
user: user
|
|
|
|
).tap(&:link_post_uploads)
|
|
|
|
end
|
|
|
|
|
2020-08-27 09:57:10 -04:00
|
|
|
it 'recovers the attachment' do
|
2018-09-18 23:52:57 -04:00
|
|
|
expect do
|
|
|
|
upload2.destroy!
|
|
|
|
end.to change { post.reload.uploads.count }.from(1).to(0)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload_recovery.recover
|
|
|
|
end.to change { post.reload.uploads.count }.from(0).to(1)
|
2018-09-19 04:03:52 -04:00
|
|
|
|
|
|
|
expect(File.read(Discourse.store.path_for(post.uploads.first)))
|
|
|
|
.to eq(File.read(file_from_fixtures("small.pdf", "pdf")))
|
2018-09-18 23:52:57 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-27 09:57:10 -04:00
|
|
|
it 'recovers uploads and attachments' do
|
2018-09-13 01:59:17 -04:00
|
|
|
stub_request(:get, "http://test.localhost#{upload.url}")
|
|
|
|
.to_return(status: 200)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload.destroy!
|
2018-09-18 23:52:57 -04:00
|
|
|
end.to change { post.reload.uploads.count }.from(1).to(0)
|
2018-09-13 01:59:17 -04:00
|
|
|
|
|
|
|
expect do
|
|
|
|
upload_recovery.recover
|
2018-09-18 23:52:57 -04:00
|
|
|
end.to change { post.reload.uploads.count }.from(0).to(1)
|
2018-09-19 04:03:52 -04:00
|
|
|
|
|
|
|
expect(File.read(Discourse.store.path_for(post.uploads.first)))
|
|
|
|
.to eq(File.read(file_from_fixtures("smallest.png")))
|
2018-09-13 01:59:17 -04:00
|
|
|
end
|
2019-04-01 23:41:00 -04:00
|
|
|
|
2022-07-27 12:14:14 -04:00
|
|
|
describe 'S3 store' do
|
2020-10-01 08:54:45 -04:00
|
|
|
before do
|
|
|
|
setup_s3
|
|
|
|
stub_s3_store
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'recovers the upload' do
|
|
|
|
expect do
|
|
|
|
upload.destroy!
|
|
|
|
end.to change { post.reload.uploads.count }.from(1).to(0)
|
|
|
|
|
|
|
|
original_key = Discourse.store.get_path_for_upload(upload)
|
|
|
|
tombstone_key = original_key.sub("original", "tombstone/original")
|
|
|
|
|
|
|
|
tombstone_copy = stub
|
|
|
|
tombstone_copy.expects(:key).returns(tombstone_key)
|
|
|
|
|
|
|
|
Discourse.store.s3_helper.expects(:list).with("original").returns([])
|
|
|
|
Discourse.store.s3_helper.expects(:list).with("#{FileStore::S3Store::TOMBSTONE_PREFIX}original").returns([tombstone_copy])
|
|
|
|
Discourse.store.s3_helper.expects(:copy).with(tombstone_key, original_key, options: { acl: "public-read" })
|
|
|
|
|
|
|
|
FileHelper.expects(:download).returns(file_from_fixtures("smallest.png"))
|
|
|
|
stub_request(:get, upload.url).to_return(body: file_from_fixtures("smallest.png"))
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload_recovery.recover
|
|
|
|
end.to change { post.reload.uploads.count }.from(0).to(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'when the upload exists but its file is missing' do
|
|
|
|
it 'recovers the file' do
|
|
|
|
upload.verification_status = Upload.verification_statuses[:invalid_etag]
|
|
|
|
upload.save!
|
|
|
|
|
|
|
|
original_key = Discourse.store.get_path_for_upload(upload)
|
|
|
|
tombstone_key = original_key.sub("original", "tombstone/original")
|
|
|
|
|
|
|
|
tombstone_copy = stub
|
|
|
|
tombstone_copy.expects(:key).returns(tombstone_key)
|
|
|
|
|
|
|
|
Discourse.store.s3_helper.expects(:list).with("original").returns([])
|
|
|
|
Discourse.store.s3_helper.expects(:list).with("#{FileStore::S3Store::TOMBSTONE_PREFIX}original").returns([tombstone_copy])
|
|
|
|
Discourse.store.s3_helper.expects(:copy).with(tombstone_key, original_key, options: { acl: "public-read" })
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload_recovery.recover
|
|
|
|
end.to_not change { [post.reload.uploads.count, Upload.count] }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not create a duplicate upload when secure uploads are enabled' do
|
2022-09-28 19:24:33 -04:00
|
|
|
SiteSetting.secure_uploads = true
|
2020-10-01 08:54:45 -04:00
|
|
|
upload.verification_status = Upload.verification_statuses[:invalid_etag]
|
|
|
|
upload.save!
|
|
|
|
|
|
|
|
original_key = Discourse.store.get_path_for_upload(upload)
|
|
|
|
tombstone_key = original_key.sub("original", "tombstone/original")
|
|
|
|
|
|
|
|
tombstone_copy = stub
|
|
|
|
tombstone_copy.expects(:key).returns(tombstone_key)
|
|
|
|
|
|
|
|
Discourse.store.s3_helper.expects(:list).with("original").returns([])
|
|
|
|
Discourse.store.s3_helper.expects(:list).with("#{FileStore::S3Store::TOMBSTONE_PREFIX}original").returns([tombstone_copy])
|
|
|
|
Discourse.store.s3_helper.expects(:copy).with(tombstone_key, original_key, options: { acl: "public-read" })
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload_recovery.recover
|
|
|
|
end.to_not change { [post.reload.uploads.count, Upload.count] }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-04-01 23:41:00 -04:00
|
|
|
describe 'image tag' do
|
|
|
|
let(:post) do
|
|
|
|
Fabricate(:post,
|
|
|
|
raw: <<~SQL,
|
|
|
|
<img src='#{upload.url}'>
|
|
|
|
SQL
|
|
|
|
user: user
|
|
|
|
).tap(&:link_post_uploads)
|
|
|
|
end
|
|
|
|
|
2020-08-27 09:57:10 -04:00
|
|
|
it 'recovers the upload' do
|
2019-04-01 23:41:00 -04:00
|
|
|
stub_request(:get, "http://test.localhost#{upload.url}")
|
|
|
|
.to_return(status: 200)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload.destroy!
|
|
|
|
end.to change { post.reload.uploads.count }.from(1).to(0)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload_recovery.recover
|
|
|
|
end.to change { post.reload.uploads.count }.from(0).to(1)
|
|
|
|
|
|
|
|
expect(File.read(Discourse.store.path_for(post.uploads.first)))
|
|
|
|
.to eq(File.read(file_from_fixtures("smallest.png")))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-04-10 04:22:35 -04:00
|
|
|
describe 'image markdown' do
|
|
|
|
let(:post) do
|
|
|
|
Fabricate(:post,
|
|
|
|
raw: <<~SQL,
|
|
|
|
![image](#{upload.url})
|
|
|
|
SQL
|
|
|
|
user: user
|
|
|
|
).tap(&:link_post_uploads)
|
|
|
|
end
|
|
|
|
|
2020-08-27 09:57:10 -04:00
|
|
|
it 'recovers the upload' do
|
2019-04-10 04:22:35 -04:00
|
|
|
stub_request(:get, "http://test.localhost#{upload.url}")
|
|
|
|
.to_return(status: 200)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload.destroy!
|
|
|
|
end.to change { post.reload.uploads.count }.from(1).to(0)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload_recovery.recover
|
|
|
|
end.to change { post.reload.uploads.count }.from(0).to(1)
|
|
|
|
|
|
|
|
expect(File.read(Discourse.store.path_for(post.uploads.first)))
|
|
|
|
.to eq(File.read(file_from_fixtures("smallest.png")))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-04-01 23:41:00 -04:00
|
|
|
describe 'bbcode' do
|
|
|
|
let(:post) do
|
|
|
|
Fabricate(:post,
|
|
|
|
raw: <<~SQL,
|
|
|
|
[img]#{upload.url}[/img]
|
|
|
|
SQL
|
|
|
|
user: user
|
|
|
|
).tap(&:link_post_uploads)
|
|
|
|
end
|
|
|
|
|
2020-08-27 09:57:10 -04:00
|
|
|
it 'recovers the upload' do
|
2019-04-01 23:41:00 -04:00
|
|
|
stub_request(:get, "http://test.localhost#{upload.url}")
|
|
|
|
.to_return(status: 200)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload.destroy!
|
|
|
|
end.to change { post.reload.uploads.count }.from(1).to(0)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
upload_recovery.recover
|
|
|
|
end.to change { post.reload.uploads.count }.from(0).to(1)
|
|
|
|
|
|
|
|
expect(File.read(Discourse.store.path_for(post.uploads.first)))
|
|
|
|
.to eq(File.read(file_from_fixtures("smallest.png")))
|
|
|
|
end
|
|
|
|
end
|
2018-09-13 01:24:08 -04:00
|
|
|
end
|
|
|
|
end
|