discourse/spec/requests/admin/backups_controller_spec.rb
Alan Guo Xiang Tan 17e1bfe069
SECURITY: Preload data only when rendering application layout
This commit drops the `before_action :preload_json` callback in `ApplicationController` as it adds unnecessary complexity to `ApplicationController` as well as other controllers which has to skip this callback. The source of the complexity comes mainly from the following two conditionals in the `preload_json` method:

```
    # We don't preload JSON on xhr or JSON request
    return if request.xhr? || request.format.json?

    # if we are posting in makes no sense to preload
    return if request.method != "GET"
```

Basically, the conditionals solely exists for optimization purposes to ensure that we don't run the preloading code when the request is not a GET request and the response is not expected to be HTML. The key problem here is that the conditionals are trying to expect what the content type of the response will be and this has proven to be hard to get right. Instead, we can simplify this problem by running the preloading code in a more deterministic way which is to preload only when the `application` layout is being rendered and this is main change that this commit introduces.
2025-02-04 13:32:30 -03:00

1011 lines
31 KiB
Ruby

# frozen_string_literal: true
RSpec.describe Admin::BackupsController do
fab!(:admin)
fab!(:moderator)
fab!(:user)
let(:backup_filename) { "2014-02-10-065935.tar.gz" }
let(:backup_filename2) { "2014-02-11-065935.tar.gz" }
def create_backup_files(*filenames)
@paths =
filenames.map do |filename|
path = backup_path(filename)
File.open(path, "w") { |f| f.write("test backup") }
path
end
end
def backup_path(filename)
File.join(BackupRestore::LocalBackupStore.base_directory, filename)
end
def map_preloaded
JSON
.parse(Nokogiri.HTML5(response.body).at_css("[data-preloaded]")["data-preloaded"])
.map { |key, value| [key, JSON.parse(value)] }
.to_h
end
before { SiteSetting.backup_location = BackupLocationSiteSetting::LOCAL }
after do
Discourse.redis.flushdb
@paths&.each { |path| File.delete(path) if File.exist?(path) }
@paths = nil
end
describe "#index" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "raises an error when backups are disabled" do
SiteSetting.enable_backups = false
get "/admin/backups.json"
expect(response.status).to eq(403)
end
context "with html format" do
it "preloads important data" do
get "/admin/backups.html"
expect(response.status).to eq(200)
preloaded = map_preloaded
expect(preloaded["operations_status"].symbolize_keys).to eq(
BackupRestore.operations_status,
)
expect(preloaded["logs"].size).to eq(BackupRestore.logs.size)
end
end
context "with json format" do
it "returns a list of all the backups" do
begin
create_backup_files(backup_filename, backup_filename2)
get "/admin/backups.json"
expect(response.status).to eq(200)
filenames = response.parsed_body.map { |backup| backup["filename"] }
expect(filenames).to include(backup_filename)
expect(filenames).to include(backup_filename2)
end
end
end
end
shared_examples "backups inaccessible" do
it "denies access with a 404 response" do
get "/admin/backups.html"
expect(response.status).to eq(404)
get "/admin/backups.json"
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "backups inaccessible"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "backups inaccessible"
end
end
describe "#status" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "returns the current backups status" do
get "/admin/backups/status.json"
expect(response.body).to eq(BackupRestore.operations_status.to_json)
expect(response.status).to eq(200)
end
end
shared_examples "status inaccessible" do
it "denies access with a 404 response" do
get "/admin/backups/status.json"
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "status inaccessible"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "status inaccessible"
end
end
describe "#create" do
context "when logged in as an admin" do
before do
sign_in(admin)
BackupRestore.stubs(:backup!)
end
it "starts a backup" do
BackupRestore.expects(:backup!).with(
admin.id,
{ publish_to_message_bus: true, with_uploads: false, client_id: "foo" },
)
post "/admin/backups.json", params: { with_uploads: false, client_id: "foo" }
expect(response.status).to eq(200)
end
context "with rate limiting enabled" do
before { RateLimiter.enable }
after { RateLimiter.disable }
it "is rate limited" do
post "/admin/backups.json", params: { with_uploads: false, client_id: "foo" }
post "/admin/backups.json", params: { with_uploads: false, client_id: "foo" }
expect(response).to have_http_status :too_many_requests
end
end
end
shared_examples "backups creation not allowed" do
it "prevents backups creation with a 404 response" do
post "/admin/backups.json", params: { with_uploads: false, client_id: "foo" }
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "backups creation not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "backups creation not allowed"
end
end
describe "#show" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "uses send_file to transmit the backup" do
begin
token = EmailBackupToken.set(admin.id)
create_backup_files(backup_filename)
expect do
get "/admin/backups/#{backup_filename}.json", params: { token: token }
end.to change {
UserHistory.where(action: UserHistory.actions[:backup_download]).count
}.by(1)
expect(response.headers["Content-Length"]).to eq("11")
expect(response.headers["Content-Disposition"]).to match(/attachment; filename/)
end
end
it "returns 422 when token is bad" do
begin
get "/admin/backups/#{backup_filename}.json", params: { token: "bad_value" }
expect(response.status).to eq(422)
expect(response.headers["Content-Disposition"]).not_to match(/attachment; filename/)
expect(response.body).to include(I18n.t("download_backup_mailer.no_token"))
end
end
it "returns 404 when the backup does not exist" do
token = EmailBackupToken.set(admin.id)
get "/admin/backups/#{backup_filename}.json", params: { token: token }
expect(response.status).to eq(404)
end
end
shared_examples "backup inaccessible" do
it "denies access with a 404 response" do
begin
token = EmailBackupToken.set(admin.id)
create_backup_files(backup_filename)
expect do
get "/admin/backups/#{backup_filename}.json", params: { token: token }
end.not_to change {
UserHistory.where(action: UserHistory.actions[:backup_download]).count
}
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
expect(response.headers["Content-Disposition"]).not_to match(/attachment; filename/)
end
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "backup inaccessible"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "backup inaccessible"
end
end
describe "#destroy" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "removes the backup if found" do
begin
path = backup_path(backup_filename)
create_backup_files(backup_filename)
expect(File.exist?(path)).to eq(true)
expect do delete "/admin/backups/#{backup_filename}.json" end.to change {
UserHistory.where(action: UserHistory.actions[:backup_destroy]).count
}.by(1)
expect(response.status).to eq(200)
expect(File.exist?(path)).to eq(false)
end
end
it "doesn't remove the backup if not found" do
delete "/admin/backups/#{backup_filename}.json"
expect(response.status).to eq(404)
end
end
shared_examples "backup deletion not allowed" do
it "prevents deletion with a 404 response" do
begin
path = backup_path(backup_filename)
create_backup_files(backup_filename)
expect(File.exist?(path)).to eq(true)
expect do delete "/admin/backups/#{backup_filename}.json" end.not_to change {
UserHistory.where(action: UserHistory.actions[:backup_destroy]).count
}
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
expect(File.exist?(path)).to eq(true)
end
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "backup deletion not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "backup deletion not allowed"
end
end
describe "#logs" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "preloads important data" do
get "/admin/backups/logs.html"
expect(response.status).to eq(200)
preloaded = map_preloaded
expect(preloaded["operations_status"].symbolize_keys).to eq(BackupRestore.operations_status)
expect(preloaded["logs"].size).to eq(BackupRestore.logs.size)
end
end
shared_examples "backup logs inaccessible" do
it "denies access with a 404 response" do
get "/admin/backups/logs.html"
expect(response.status).to eq(404)
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "backup logs inaccessible"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "backup logs inaccessible"
end
end
describe "#restore" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "starts a restore" do
BackupRestore.expects(:restore!).with(
admin.id,
{ filename: backup_filename, publish_to_message_bus: true, client_id: "foo" },
)
post "/admin/backups/#{backup_filename}/restore.json", params: { client_id: "foo" }
expect(response.status).to eq(200)
end
end
shared_examples "backup restoration not allowed" do
it "prevents restoration with a 404 response" do
post "/admin/backups/#{backup_filename}/restore.json", params: { client_id: "foo" }
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "backup restoration not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "backup restoration not allowed"
end
end
describe "#readonly" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "enables readonly mode" do
expect(Discourse.readonly_mode?).to eq(false)
expect { put "/admin/backups/readonly.json", params: { enable: true } }.to change {
UserHistory.where(
action: UserHistory.actions[:change_readonly_mode],
new_value: "t",
).count
}.by(1)
expect(Discourse.readonly_mode?).to eq(true)
expect(response.status).to eq(200)
end
it "disables readonly mode" do
Discourse.enable_readonly_mode(Discourse::USER_READONLY_MODE_KEY)
expect(Discourse.readonly_mode?).to eq(true)
expect { put "/admin/backups/readonly.json", params: { enable: false } }.to change {
UserHistory.where(
action: UserHistory.actions[:change_readonly_mode],
new_value: "f",
).count
}.by(1)
expect(response.status).to eq(200)
expect(Discourse.readonly_mode?).to eq(false)
end
end
shared_examples "enabling readonly mode not allowed" do
it "prevents enabling readonly mode with a 404 response" do
expect(Discourse.readonly_mode?).to eq(false)
expect do put "/admin/backups/readonly.json", params: { enable: true } end.not_to change {
UserHistory.where(
action: UserHistory.actions[:change_readonly_mode],
new_value: "t",
).count
}
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
expect(Discourse.readonly_mode?).to eq(false)
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "enabling readonly mode not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "enabling readonly mode not allowed"
end
end
describe "#upload_backup_chunk" do
context "when logged in as an admin" do
before { sign_in(admin) }
describe "when filename contains invalid characters" do
it "should raise an error" do
["灰色.tar.gz", '; echo \'haha\'.tar.gz'].each do |invalid_filename|
described_class.any_instance.expects(:has_enough_space_on_disk?).returns(true)
post "/admin/backups/upload",
params: {
resumableFilename: invalid_filename,
resumableTotalSize: 1,
resumableIdentifier: "test",
}
expect(response.status).to eq(415)
expect(response.body).to eq(I18n.t("backup.invalid_filename"))
end
end
end
describe "when resumableIdentifier is invalid" do
it "should raise an error" do
filename = "test_site-0123456789.tar.gz"
@paths = [backup_path(File.join("tmp", "test", "#{filename}.part1"))]
post "/admin/backups/upload.json",
params: {
resumableFilename: filename,
resumableTotalSize: 1,
resumableIdentifier: "../test",
resumableChunkNumber: "1",
resumableChunkSize: "1",
resumableCurrentChunkSize: "1",
file: fixture_file_upload(Tempfile.new),
}
expect(response.status).to eq(400)
end
end
describe "when filename is valid" do
it "should upload the file successfully" do
freeze_time
described_class.any_instance.expects(:has_enough_space_on_disk?).returns(true)
filename = "test_Site-0123456789.tar.gz"
post "/admin/backups/upload.json",
params: {
resumableFilename: filename,
resumableTotalSize: 1,
resumableIdentifier: "test",
resumableChunkNumber: "1",
resumableChunkSize: "1",
resumableCurrentChunkSize: "1",
file: fixture_file_upload(Tempfile.new),
}
expect_job_enqueued(
job: :backup_chunks_merger,
args: {
filename: filename,
identifier: "test",
chunks: 1,
},
at: 5.seconds.from_now,
)
expect(response.status).to eq(200)
expect(response.body).to eq("")
end
end
describe "completing an upload by enqueuing backup_chunks_merger" do
let(:filename) { "test_Site-0123456789.tar.gz" }
it "works with a single chunk" do
freeze_time
described_class.any_instance.expects(:has_enough_space_on_disk?).returns(true)
# 2MB file, 2MB chunks = 1x 2MB chunk
post "/admin/backups/upload.json",
params: {
resumableFilename: filename,
resumableTotalSize: "2097152",
resumableIdentifier: "test",
resumableChunkNumber: "1",
resumableChunkSize: "2097152",
resumableCurrentChunkSize: "2097152",
file: fixture_file_upload(Tempfile.new),
}
expect_job_enqueued(
job: :backup_chunks_merger,
args: {
filename: filename,
identifier: "test",
chunks: 1,
},
at: 5.seconds.from_now,
)
end
it "works with multiple chunks when the final chunk is chunk_size + remainder" do
freeze_time
described_class.any_instance.expects(:has_enough_space_on_disk?).twice.returns(true)
# 5MB file, 2MB chunks = 1x 2MB chunk + 1x 3MB chunk with resumable.js
post "/admin/backups/upload.json",
params: {
resumableFilename: filename,
resumableTotalSize: "5242880",
resumableIdentifier: "test",
resumableChunkNumber: "1",
resumableChunkSize: "2097152",
resumableCurrentChunkSize: "2097152",
file: fixture_file_upload(Tempfile.new),
}
post "/admin/backups/upload.json",
params: {
resumableFilename: filename,
resumableTotalSize: "5242880",
resumableIdentifier: "test",
resumableChunkNumber: "2",
resumableChunkSize: "2097152",
resumableCurrentChunkSize: "3145728",
file: fixture_file_upload(Tempfile.new),
}
expect_job_enqueued(
job: :backup_chunks_merger,
args: {
filename: filename,
identifier: "test",
chunks: 2,
},
at: 5.seconds.from_now,
)
end
it "works with multiple chunks when the final chunk is just the remainder" do
freeze_time
described_class.any_instance.expects(:has_enough_space_on_disk?).times(3).returns(true)
# 5MB file, 2MB chunks = 2x 2MB chunk + 1x 1MB chunk with uppy.js
post "/admin/backups/upload.json",
params: {
resumableFilename: filename,
resumableTotalSize: "5242880",
resumableIdentifier: "test",
resumableChunkNumber: "1",
resumableChunkSize: "2097152",
resumableCurrentChunkSize: "2097152",
file: fixture_file_upload(Tempfile.new),
}
post "/admin/backups/upload.json",
params: {
resumableFilename: filename,
resumableTotalSize: "5242880",
resumableIdentifier: "test",
resumableChunkNumber: "2",
resumableChunkSize: "2097152",
resumableCurrentChunkSize: "2097152",
file: fixture_file_upload(Tempfile.new),
}
post "/admin/backups/upload.json",
params: {
resumableFilename: filename,
resumableTotalSize: "5242880",
resumableIdentifier: "test",
resumableChunkNumber: "3",
resumableChunkSize: "2097152",
resumableCurrentChunkSize: "1048576",
file: fixture_file_upload(Tempfile.new),
}
expect_job_enqueued(
job: :backup_chunks_merger,
args: {
filename: filename,
identifier: "test",
chunks: 3,
},
at: 5.seconds.from_now,
)
end
end
end
shared_examples "uploading backup chunk not allowed" do
it "prevents uploading of backup chunk with a 404 response" do
freeze_time
filename = "test_Site-0123456789.tar.gz"
post "/admin/backups/upload.json",
params: {
resumableFilename: filename,
resumableTotalSize: 1,
resumableIdentifier: "test",
resumableChunkNumber: "1",
resumableChunkSize: "1",
resumableCurrentChunkSize: "1",
file: fixture_file_upload(Tempfile.new),
}
expect_not_enqueued_with(
job: :backup_chunks_merger,
args: {
filename: filename,
identifier: "test",
chunks: 1,
},
at: 5.seconds.from_now,
)
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "uploading backup chunk not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "uploading backup chunk not allowed"
end
end
describe "#check_backup_chunk" do
context "when logged in as an admin" do
before { sign_in(admin) }
describe "when resumableIdentifier is invalid" do
it "should raise an error" do
get "/admin/backups/upload",
params: {
resumableidentifier: "../some_file",
resumablefilename: "test_site-0123456789.tar.gz",
resumablechunknumber: "1",
resumablecurrentchunksize: "1",
}
expect(response.status).to eq(400)
end
end
end
shared_examples "checking backup chunk not allowed" do
it "denies access with a 404 response" do
get "/admin/backups/upload",
params: {
resumableidentifier: "../some_file",
resumablefilename: "test_site-0123456789.tar.gz",
resumablechunknumber: "1",
resumablecurrentchunksize: "1",
}
expect(response.status).to eq(404)
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "checking backup chunk not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "checking backup chunk not allowed"
end
end
describe "#rollback" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "should rollback the restore" do
BackupRestore.expects(:rollback!)
post "/admin/backups/rollback.json"
expect(response.status).to eq(200)
end
it "should not allow rollback via a GET request" do
get "/admin/backups/rollback.json"
expect(response.status).to eq(404)
end
end
shared_examples "backup rollback not allowed" do
it "prevents rollbacks with a 404 response" do
post "/admin/backups/rollback.json"
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "backup rollback not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "backup rollback not allowed"
end
end
describe "#cancel" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "should cancel an backup" do
BackupRestore.expects(:cancel!)
delete "/admin/backups/cancel.json"
expect(response.status).to eq(200)
end
it "should not allow cancel via a GET request" do
get "/admin/backups/cancel.json"
expect(response.status).to eq(404)
end
end
shared_examples "backup cancellation not allowed" do
it "prevents cancellation with a 404 response" do
delete "/admin/backups/cancel.json"
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "backup cancellation not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "backup cancellation not allowed"
end
end
describe "#email" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "enqueues email job" do
# might as well test this here if we really want www.example.com
SiteSetting.force_hostname = "www.example.com"
create_backup_files(backup_filename)
expect { put "/admin/backups/#{backup_filename}.json" }.to change {
Jobs::DownloadBackupEmail.jobs.size
}.by(1)
job_args = Jobs::DownloadBackupEmail.jobs.last["args"].first
expect(job_args["user_id"]).to eq(admin.id)
expect(job_args["backup_file_path"]).to eq(
"http://www.example.com/admin/backups/#{backup_filename}",
)
expect(response.status).to eq(200)
end
it "returns 404 when the backup does not exist" do
put "/admin/backups/#{backup_filename}.json"
expect(response).to be_not_found
end
end
shared_examples "backup emails not allowed" do
it "prevents sending backup emails with a 404 response" do
SiteSetting.force_hostname = "www.example.com"
create_backup_files(backup_filename)
expect do put "/admin/backups/#{backup_filename}.json" end.not_to change {
Jobs::DownloadBackupEmail.jobs.size
}
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "backup emails not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "backup emails not allowed"
end
end
describe "S3 multipart uploads" do
let(:upload_type) { "backup" }
let(:test_bucket_prefix) { "test_#{ENV["TEST_ENV_NUMBER"].presence || "0"}" }
let(:backup_file_exists_response) { { status: 404 } }
let(:mock_multipart_upload_id) do
"ibZBv_75gd9r8lH_gqXatLdxMVpAlj6CFTR.OwyF3953YdwbcQnMA2BLGn8Lx12fQNICtMw5KyteFeHw.Sjng--"
end
before do
setup_s3
SiteSetting.enable_direct_s3_uploads = true
SiteSetting.s3_backup_bucket = "s3-backup-bucket"
SiteSetting.backup_location = BackupLocationSiteSetting::S3
stub_request(
:head,
"https://s3-backup-bucket.s3.dualstack.us-west-1.amazonaws.com/",
).to_return(status: 200, body: "", headers: {})
stub_request(
:head,
"https://s3-backup-bucket.s3.dualstack.us-west-1.amazonaws.com/default/test.tar.gz",
).to_return(backup_file_exists_response)
end
shared_examples "multipart uploads not allowed" do
it "prevents multipart uploads with a 404 response" do
post "/admin/backups/create-multipart.json",
params: {
file_name: "test.tar.gz",
upload_type: upload_type,
file_size: 4098,
}
expect(response.status).to eq(404)
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "multipart uploads not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "multipart uploads not allowed"
end
context "when the user is admin" do
before { sign_in(admin) }
def stub_create_multipart_backup_request
BackupRestore::S3BackupStore
.any_instance
.stubs(:temporary_upload_path)
.returns(
"temp/default/#{test_bucket_prefix}/28fccf8259bbe75b873a2bd2564b778c/2u98j832nx93272x947823.gz",
)
create_multipart_result = <<~XML
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<InitiateMultipartUploadResult>
<Bucket>s3-backup-bucket</Bucket>
<Key>temp/default/#{test_bucket_prefix}/28fccf8259bbe75b873a2bd2564b778c/2u98j832nx93272x947823.gz</Key>
<UploadId>#{mock_multipart_upload_id}</UploadId>
</InitiateMultipartUploadResult>
XML
stub_request(
:post,
"https://s3-backup-bucket.s3.dualstack.us-west-1.amazonaws.com/temp/default/#{test_bucket_prefix}/28fccf8259bbe75b873a2bd2564b778c/2u98j832nx93272x947823.gz?uploads",
).to_return(status: 200, body: create_multipart_result)
end
it "creates the multipart upload" do
stub_create_multipart_backup_request
post "/admin/backups/create-multipart.json",
params: {
file_name: "test.tar.gz",
upload_type: upload_type,
file_size: 4098,
}
expect(response.status).to eq(200)
result = response.parsed_body
external_upload_stub =
ExternalUploadStub.where(
unique_identifier: result["unique_identifier"],
original_filename: "test.tar.gz",
created_by: admin,
upload_type: upload_type,
key: result["key"],
multipart: true,
)
expect(external_upload_stub.exists?).to eq(true)
end
context "when backup of same filename already exists" do
let(:backup_file_exists_response) { { status: 200, body: "" } }
it "throws an error" do
post "/admin/backups/create-multipart.json",
params: {
file_name: "test.tar.gz",
upload_type: upload_type,
file_size: 4098,
}
expect(response.status).to eq(422)
expect(response.parsed_body["errors"]).to include(I18n.t("backup.file_exists"))
end
end
context "when filename is invalid" do
it "throws an error" do
post "/admin/backups/create-multipart.json",
params: {
file_name: "blah $$##.tar.gz",
upload_type: upload_type,
file_size: 4098,
}
expect(response.status).to eq(422)
expect(response.parsed_body["errors"]).to include(I18n.t("backup.invalid_filename"))
end
end
context "when extension is invalid" do
it "throws an error" do
post "/admin/backups/create-multipart.json",
params: {
file_name: "test.png",
upload_type: upload_type,
file_size: 4098,
}
expect(response.status).to eq(422)
expect(response.parsed_body["errors"]).to include(
I18n.t("backup.backup_file_should_be_tar_gz"),
)
end
end
end
end
end