debian-mirror-gitlab/spec/lib/gitlab/import_export/command_line_util_spec.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

370 lines
12 KiB
Ruby
Raw Normal View History

2018-12-21 20:21:59 +05:30
# frozen_string_literal: true
require 'spec_helper'
2023-05-27 22:25:52 +05:30
RSpec.describe Gitlab::ImportExport::CommandLineUtil, feature_category: :importers do
2018-12-21 20:21:59 +05:30
include ExportFileHelper
let(:shared) { Gitlab::ImportExport::Shared.new(nil) }
2023-09-09 18:01:29 +05:30
# Separate where files are written during this test by their kind, to avoid them interfering with each other:
# - `source_dir` Dir to compress files from.
# - `target_dir` Dir to decompress archived files into.
# - `archive_dir` Dir to write any archive files to.
let(:source_dir) { Dir.mktmpdir }
let(:target_dir) { Dir.mktmpdir }
2023-05-27 22:25:52 +05:30
let(:archive_dir) { Dir.mktmpdir }
2018-12-21 20:21:59 +05:30
2023-09-09 18:01:29 +05:30
subject(:mock_class) do
2018-12-21 20:21:59 +05:30
Class.new do
include Gitlab::ImportExport::CommandLineUtil
def initialize
@shared = Gitlab::ImportExport::Shared.new(nil)
end
2022-05-07 20:08:51 +05:30
# Make the included methods public for testing
public :download_or_copy_upload, :download
2018-12-21 20:21:59 +05:30
end.new
end
before do
2023-09-09 18:01:29 +05:30
FileUtils.mkdir_p(source_dir)
2018-12-21 20:21:59 +05:30
end
after do
2023-09-09 18:01:29 +05:30
FileUtils.rm_rf(source_dir)
FileUtils.rm_rf(target_dir)
2023-05-27 22:25:52 +05:30
FileUtils.rm_rf(archive_dir)
2018-12-21 20:21:59 +05:30
end
2023-05-27 22:25:52 +05:30
shared_examples 'deletes symlinks' do |compression, decompression|
it 'deletes the symlinks', :aggregate_failures do
2023-09-09 18:01:29 +05:30
Dir.mkdir("#{source_dir}/.git")
Dir.mkdir("#{source_dir}/folder")
FileUtils.touch("#{source_dir}/file.txt")
FileUtils.touch("#{source_dir}/folder/file.txt")
FileUtils.touch("#{source_dir}/.gitignore")
FileUtils.touch("#{source_dir}/.git/config")
File.symlink('file.txt', "#{source_dir}/.symlink")
File.symlink('file.txt', "#{source_dir}/.git/.symlink")
File.symlink('file.txt', "#{source_dir}/folder/.symlink")
archive_file = File.join(archive_dir, 'symlink_archive.tar.gz')
subject.public_send(compression, archive: archive_file, dir: source_dir)
subject.public_send(decompression, archive: archive_file, dir: target_dir)
expect(File).to exist("#{target_dir}/file.txt")
expect(File).to exist("#{target_dir}/folder/file.txt")
expect(File).to exist("#{target_dir}/.gitignore")
expect(File).to exist("#{target_dir}/.git/config")
expect(File).not_to exist("#{target_dir}/.symlink")
expect(File).not_to exist("#{target_dir}/.git/.symlink")
expect(File).not_to exist("#{target_dir}/folder/.symlink")
end
end
shared_examples 'handles shared hard links' do |compression, decompression|
let(:archive_file) { File.join(archive_dir, 'hard_link_archive.tar.gz') }
subject(:decompress) { mock_class.public_send(decompression, archive: archive_file, dir: target_dir) }
before do
Dir.mkdir("#{source_dir}/dir")
FileUtils.touch("#{source_dir}/file.txt")
FileUtils.touch("#{source_dir}/dir/.file.txt")
FileUtils.link("#{source_dir}/file.txt", "#{source_dir}/.hard_linked_file.txt")
mock_class.public_send(compression, archive: archive_file, dir: source_dir)
end
it 'raises an exception and deletes the extraction dir', :aggregate_failures do
expect(FileUtils).to receive(:remove_dir).with(target_dir).and_call_original
expect(Dir).to exist(target_dir)
expect { decompress }.to raise_error(described_class::HardLinkError)
expect(Dir).not_to exist(target_dir)
2023-05-27 22:25:52 +05:30
end
2018-12-21 20:21:59 +05:30
end
2021-06-08 01:23:25 +05:30
2022-05-07 20:08:51 +05:30
describe '#download_or_copy_upload' do
let(:upload) { instance_double(Upload, local?: local) }
let(:uploader) { instance_double(ImportExportUploader, path: :path, url: :url, upload: upload) }
let(:upload_path) { '/some/path' }
context 'when the upload is local' do
let(:local) { true }
it 'copies the file' do
expect(subject).to receive(:copy_files).with(:path, upload_path)
subject.download_or_copy_upload(uploader, upload_path)
end
end
context 'when the upload is remote' do
let(:local) { false }
it 'downloads the file' do
expect(subject).to receive(:download).with(:url, upload_path, size_limit: nil)
subject.download_or_copy_upload(uploader, upload_path)
end
end
end
describe '#download' do
let(:content) { File.open('spec/fixtures/rails_sample.tif') }
context 'a non-localhost uri' do
before do
stub_request(:get, url)
.to_return(
status: status,
body: content
)
end
let(:url) { 'https://gitlab.com/file' }
context 'with ok status code' do
let(:status) { HTTP::Status::OK }
it 'gets the contents' do
Tempfile.create('test') do |file|
subject.download(url, file.path)
expect(file.read).to eq(File.open('spec/fixtures/rails_sample.tif').read)
end
end
it 'streams the contents via Gitlab::HTTP' do
expect(Gitlab::HTTP).to receive(:get).with(url, hash_including(stream_body: true))
Tempfile.create('test') do |file|
subject.download(url, file.path)
end
end
it 'does not get the content over the size_limit' do
Tempfile.create('test') do |file|
subject.download(url, file.path, size_limit: 300.kilobytes)
expect(file.read).to eq('')
end
end
it 'gets the content within the size_limit' do
Tempfile.create('test') do |file|
subject.download(url, file.path, size_limit: 400.kilobytes)
expect(file.read).to eq(File.open('spec/fixtures/rails_sample.tif').read)
end
end
end
2022-06-21 17:19:12 +05:30
%w[MOVED_PERMANENTLY FOUND SEE_OTHER TEMPORARY_REDIRECT].each do |code|
2022-05-07 20:08:51 +05:30
context "with a redirect status code #{code}" do
let(:status) { HTTP::Status.const_get(code, false) }
it 'logs the redirect' do
expect(Gitlab::Import::Logger).to receive(:warn)
Tempfile.create('test') do |file|
subject.download(url, file.path)
end
end
end
end
%w[ACCEPTED UNAUTHORIZED BAD_REQUEST].each do |code|
context "with an invalid status code #{code}" do
let(:status) { HTTP::Status.const_get(code, false) }
it 'throws an error' do
Tempfile.create('test') do |file|
expect { subject.download(url, file.path) }.to raise_error(Gitlab::ImportExport::Error)
end
end
end
end
end
context 'a localhost uri' do
include StubRequests
let(:status) { HTTP::Status::OK }
let(:url) { "#{host}/foo/bar" }
let(:host) { 'http://localhost:8081' }
before do
# Note: the hostname gets changed to an ip address due to dns_rebind_protection
stub_dns(url, ip_address: '127.0.0.1')
stub_request(:get, 'http://127.0.0.1:8081/foo/bar')
.to_return(
status: status,
body: content
)
end
it 'throws a blocked url error' do
Tempfile.create('test') do |file|
expect { subject.download(url, file.path) }.to raise_error((Gitlab::HTTP::BlockedUrlError))
end
end
context 'for object_storage uri' do
let(:enabled_object_storage_setting) do
{
'enabled' => true,
'object_store' =>
{
'enabled' => true,
'connection' => {
'endpoint' => host
}
}
}
end
before do
allow(Settings).to receive(:external_diffs).and_return(enabled_object_storage_setting)
end
it 'gets the content' do
Tempfile.create('test') do |file|
subject.download(url, file.path)
expect(file.read).to eq(File.open('spec/fixtures/rails_sample.tif').read)
end
end
end
end
end
2021-06-08 01:23:25 +05:30
describe '#gzip' do
2023-09-09 18:01:29 +05:30
let(:path) { source_dir }
2021-06-08 01:23:25 +05:30
it 'compresses specified file' do
tempfile = Tempfile.new('test', path)
filename = File.basename(tempfile.path)
subject.gzip(dir: path, filename: filename)
2021-09-04 01:27:46 +05:30
expect(File.exist?("#{tempfile.path}.gz")).to eq(true)
2021-06-08 01:23:25 +05:30
end
context 'when exception occurs' do
it 'raises an exception' do
expect { subject.gzip(dir: path, filename: 'test') }.to raise_error(Gitlab::ImportExport::Error)
end
end
end
2021-09-04 01:27:46 +05:30
describe '#gunzip' do
2023-09-09 18:01:29 +05:30
let(:path) { source_dir }
2021-09-04 01:27:46 +05:30
it 'decompresses specified file' do
filename = 'labels.ndjson.gz'
gz_filepath = "spec/fixtures/bulk_imports/gz/#{filename}"
2023-09-09 18:01:29 +05:30
FileUtils.copy_file(gz_filepath, File.join(path, filename))
2021-09-04 01:27:46 +05:30
2023-09-09 18:01:29 +05:30
subject.gunzip(dir: path, filename: filename)
2021-09-04 01:27:46 +05:30
2023-09-09 18:01:29 +05:30
expect(File.exist?(File.join(path, 'labels.ndjson'))).to eq(true)
2021-09-04 01:27:46 +05:30
end
context 'when exception occurs' do
it 'raises an exception' do
expect { subject.gunzip(dir: path, filename: 'test') }.to raise_error(Gitlab::ImportExport::Error)
end
end
end
2021-11-18 22:05:49 +05:30
describe '#tar_cf' do
it 'archives a folder without compression' do
archive_file = File.join(archive_dir, 'archive.tar')
2023-09-09 18:01:29 +05:30
result = subject.tar_cf(archive: archive_file, dir: source_dir)
2021-11-18 22:05:49 +05:30
expect(result).to eq(true)
expect(File.exist?(archive_file)).to eq(true)
end
context 'when something goes wrong' do
it 'raises an error' do
expect(Gitlab::Popen).to receive(:popen).and_return(['Error', 1])
klass = Class.new do
include Gitlab::ImportExport::CommandLineUtil
end.new
2022-04-04 11:22:00 +05:30
expect { klass.tar_cf(archive: 'test', dir: 'test') }.to raise_error(Gitlab::ImportExport::Error, 'command exited with error code 1: Error')
2021-11-18 22:05:49 +05:30
end
end
end
2022-01-26 12:08:38 +05:30
2023-05-27 22:25:52 +05:30
describe '#untar_zxf' do
2023-09-09 18:01:29 +05:30
let(:tar_archive_fixture) { 'spec/fixtures/symlink_export.tar.gz' }
2023-05-27 22:25:52 +05:30
it_behaves_like 'deletes symlinks', :tar_czf, :untar_zxf
2023-09-09 18:01:29 +05:30
it_behaves_like 'handles shared hard links', :tar_czf, :untar_zxf
2022-01-26 12:08:38 +05:30
2023-05-27 22:25:52 +05:30
it 'has the right mask for project.json' do
2023-09-09 18:01:29 +05:30
subject.untar_zxf(archive: tar_archive_fixture, dir: target_dir)
2023-05-27 22:25:52 +05:30
2023-09-09 18:01:29 +05:30
expect(file_permissions("#{target_dir}/project.json")).to eq(0755) # originally 777
2023-05-27 22:25:52 +05:30
end
it 'has the right mask for uploads' do
2023-09-09 18:01:29 +05:30
subject.untar_zxf(archive: tar_archive_fixture, dir: target_dir)
2023-05-27 22:25:52 +05:30
2023-09-09 18:01:29 +05:30
expect(file_permissions("#{target_dir}/uploads")).to eq(0755) # originally 555
2022-01-26 12:08:38 +05:30
end
2023-05-27 22:25:52 +05:30
end
describe '#untar_xf' do
2023-09-09 18:01:29 +05:30
let(:tar_archive_fixture) { 'spec/fixtures/symlink_export.tar.gz' }
2023-05-27 22:25:52 +05:30
it_behaves_like 'deletes symlinks', :tar_cf, :untar_xf
2023-09-09 18:01:29 +05:30
it_behaves_like 'handles shared hard links', :tar_cf, :untar_xf
2022-01-26 12:08:38 +05:30
it 'extracts archive without decompression' do
filename = 'archive.tar.gz'
archive_file = File.join(archive_dir, 'archive.tar')
2023-09-09 18:01:29 +05:30
FileUtils.copy_file(tar_archive_fixture, File.join(archive_dir, filename))
2022-01-26 12:08:38 +05:30
subject.gunzip(dir: archive_dir, filename: filename)
result = subject.untar_xf(archive: archive_file, dir: archive_dir)
expect(result).to eq(true)
expect(File.exist?(archive_file)).to eq(true)
expect(File.exist?(File.join(archive_dir, 'project.json'))).to eq(true)
expect(Dir.exist?(File.join(archive_dir, 'uploads'))).to eq(true)
end
context 'when something goes wrong' do
2022-04-04 11:22:00 +05:30
before do
2022-01-26 12:08:38 +05:30
expect(Gitlab::Popen).to receive(:popen).and_return(['Error', 1])
2022-04-04 11:22:00 +05:30
end
2022-01-26 12:08:38 +05:30
2022-04-04 11:22:00 +05:30
it 'raises an error' do
2022-01-26 12:08:38 +05:30
klass = Class.new do
include Gitlab::ImportExport::CommandLineUtil
end.new
2022-04-04 11:22:00 +05:30
expect { klass.untar_xf(archive: 'test', dir: 'test') }.to raise_error(Gitlab::ImportExport::Error, 'command exited with error code 1: Error')
end
it 'returns false and includes error status' do
klass = Class.new do
include Gitlab::ImportExport::CommandLineUtil
attr_accessor :shared
def initialize
@shared = Gitlab::ImportExport::Shared.new(nil)
end
end.new
expect(klass.tar_czf(archive: 'test', dir: 'test')).to eq(false)
expect(klass.shared.errors).to eq(['command exited with error code 1: Error'])
2022-01-26 12:08:38 +05:30
end
end
end
2018-12-21 20:21:59 +05:30
end