diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 11:18:50 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 11:18:50 +0000 |
commit | 8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch) | |
tree | a77e7fe7a93de11213032ed4ab1f33a3db51b738 /spec/lib/gitlab/import_export | |
parent | 00b35af3db1abfe813a778f643dad221aad51fca (diff) | |
download | gitlab-ce-8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781.tar.gz |
Add latest changes from gitlab-org/gitlab@13-1-stable-ee
Diffstat (limited to 'spec/lib/gitlab/import_export')
14 files changed, 394 insertions, 108 deletions
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index c78b4501310..ef9321dc1fc 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -179,10 +179,12 @@ merge_request_context_commits: ci_pipelines: - project - user +- ci_ref - stages - statuses - latest_statuses_ordered_by_stage - builds +- bridges - processables - trigger_requests - variables @@ -195,7 +197,7 @@ ci_pipelines: - cancelable_statuses - manual_actions - scheduled_actions -- artifacts +- downloadable_artifacts - pipeline_schedule - merge_requests_as_head_pipeline - merge_request @@ -220,6 +222,11 @@ ci_pipelines: - pipeline_config - security_scans - daily_build_group_report_results +- latest_builds +- daily_report_results +ci_refs: +- project +- ci_pipelines pipeline_variables: - pipeline stages: @@ -236,6 +243,7 @@ statuses: - stage - user - auto_canceled_by +- needs variables: - project triggers: @@ -417,6 +425,7 @@ project: - deploy_tokens - settings - ci_cd_settings +- project_settings - import_export_upload - repository_languages - pool_repository @@ -479,6 +488,7 @@ project: - upstream_project_subscriptions - downstream_project_subscriptions - service_desk_setting +- security_setting - import_failures - container_expiration_policy - resource_groups @@ -494,6 +504,7 @@ project: - repository_storage_moves - freeze_periods - webex_teams_service +- build_report_results award_emoji: - awardable - user @@ -579,6 +590,7 @@ boards: - board_assignee - assignee - labels +- user_preferences lists: - user - milestone @@ -596,6 +608,7 @@ design: &design - versions - notes - user_mentions +- events designs: *design actions: - design diff --git a/spec/lib/gitlab/import_export/attributes_permitter_spec.rb b/spec/lib/gitlab/import_export/attributes_permitter_spec.rb new file mode 100644 index 00000000000..d6217811b9c --- /dev/null +++ b/spec/lib/gitlab/import_export/attributes_permitter_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::ImportExport::AttributesPermitter do + let(:yml_config) do + <<-EOF + tree: + project: + - labels: + - :priorities + - milestones: + - events: + - :push_event_payload + + included_attributes: + labels: + - :title + - :description + + methods: + labels: + - :type + EOF + end + + let(:file) { Tempfile.new(%w(import_export .yml)) } + let(:config_hash) { Gitlab::ImportExport::Config.new(config: file.path).to_h } + + before do + file.write(yml_config) + file.rewind + end + + after do + file.close + file.unlink + end + + subject { described_class.new(config: config_hash) } + + describe '#permitted_attributes' do + it 'builds permitted attributes hash' do + expect(subject.permitted_attributes).to match( + a_hash_including( + project: [:labels, :milestones], + labels: [:priorities, :title, :description, :type], + events: [:push_event_payload], + milestones: [:events], + priorities: [], + push_event_payload: [] + ) + ) + end + end + + describe '#permit' do + let(:unfiltered_hash) do + { + title: 'Title', + description: 'Description', + undesired_attribute: 'Undesired Attribute', + another_attribute: 'Another Attribute' + } + end + + it 'only allows permitted attributes' do + expect(subject.permit(:labels, unfiltered_hash)).to eq(title: 'Title', description: 'Description') + end + end + + describe '#permitted_attributes_for' do + it 'returns an array of permitted attributes for a relation' do + expect(subject.permitted_attributes_for(:labels)).to contain_exactly(:title, :description, :type, :priorities) + end + end +end diff --git a/spec/lib/gitlab/import_export/import_test_coverage_spec.rb b/spec/lib/gitlab/import_export/import_test_coverage_spec.rb index 038b95809b4..c5a7327332e 100644 --- a/spec/lib/gitlab/import_export/import_test_coverage_spec.rb +++ b/spec/lib/gitlab/import_export/import_test_coverage_spec.rb @@ -10,61 +10,66 @@ require 'spec_helper' describe 'Test coverage of the Project Import' do include ConfigurationHelper - # `MUTED_RELATIONS` is a technical debt. + # `muted_relations` is a technical debt. # This list expected to be empty or used as a workround # in case this spec blocks an important urgent MR. # It is also expected that adding a relation in the list should lead to # opening a follow-up issue to fix this. - MUTED_RELATIONS = %w[ - project.milestones.events.push_event_payload - project.issues.events - project.issues.events.push_event_payload - project.issues.notes.events - project.issues.notes.events.push_event_payload - project.issues.milestone.events.push_event_payload - project.issues.issue_milestones - project.issues.issue_milestones.milestone - project.issues.resource_label_events.label.priorities - project.issues.designs.notes - project.issues.designs.notes.author - project.issues.designs.notes.events - project.issues.designs.notes.events.push_event_payload - project.merge_requests.metrics - project.merge_requests.notes.events.push_event_payload - project.merge_requests.events.push_event_payload - project.merge_requests.timelogs - project.merge_requests.label_links - project.merge_requests.label_links.label - project.merge_requests.label_links.label.priorities - project.merge_requests.milestone - project.merge_requests.milestone.events - project.merge_requests.milestone.events.push_event_payload - project.merge_requests.merge_request_milestones - project.merge_requests.merge_request_milestones.milestone - project.merge_requests.resource_label_events.label - project.merge_requests.resource_label_events.label.priorities - project.ci_pipelines.notes.events - project.ci_pipelines.notes.events.push_event_payload - project.protected_branches.unprotect_access_levels - project.prometheus_metrics - project.metrics_setting - project.boards.lists.label.priorities - project.service_desk_setting - ].freeze + let(:muted_relations) do + %w[ + project.milestones.events.push_event_payload + project.issues.events + project.issues.events.push_event_payload + project.issues.notes.events + project.issues.notes.events.push_event_payload + project.issues.milestone.events.push_event_payload + project.issues.issue_milestones + project.issues.issue_milestones.milestone + project.issues.resource_label_events.label.priorities + project.issues.designs.notes + project.issues.designs.notes.author + project.issues.designs.notes.events + project.issues.designs.notes.events.push_event_payload + project.merge_requests.metrics + project.merge_requests.notes.events.push_event_payload + project.merge_requests.events.push_event_payload + project.merge_requests.timelogs + project.merge_requests.label_links + project.merge_requests.label_links.label + project.merge_requests.label_links.label.priorities + project.merge_requests.milestone + project.merge_requests.milestone.events + project.merge_requests.milestone.events.push_event_payload + project.merge_requests.merge_request_milestones + project.merge_requests.merge_request_milestones.milestone + project.merge_requests.resource_label_events.label + project.merge_requests.resource_label_events.label.priorities + project.ci_pipelines.notes.events + project.ci_pipelines.notes.events.push_event_payload + project.protected_branches.unprotect_access_levels + project.prometheus_metrics + project.metrics_setting + project.boards.lists.label.priorities + project.service_desk_setting + project.security_setting + ].freeze + end # A list of JSON fixture files we use to test Import. # Most of the relations are present in `complex/project.json` # which is our main fixture. - PROJECT_JSON_FIXTURES = [ - 'spec/fixtures/lib/gitlab/import_export/complex/project.json', - 'spec/fixtures/lib/gitlab/import_export/group/project.json', - 'spec/fixtures/lib/gitlab/import_export/light/project.json', - 'spec/fixtures/lib/gitlab/import_export/milestone-iid/project.json', - 'spec/fixtures/lib/gitlab/import_export/designs/project.json' - ].freeze + let(:project_json_fixtures) do + [ + 'spec/fixtures/lib/gitlab/import_export/complex/project.json', + 'spec/fixtures/lib/gitlab/import_export/group/project.json', + 'spec/fixtures/lib/gitlab/import_export/light/project.json', + 'spec/fixtures/lib/gitlab/import_export/milestone-iid/project.json', + 'spec/fixtures/lib/gitlab/import_export/designs/project.json' + ].freeze + end it 'ensures that all imported/exported relations are present in test JSONs' do - not_tested_relations = (relations_from_config - tested_relations) - MUTED_RELATIONS + not_tested_relations = (relations_from_config - tested_relations) - muted_relations expect(not_tested_relations).to be_empty, failure_message(not_tested_relations) end @@ -76,7 +81,7 @@ describe 'Test coverage of the Project Import' do end def tested_relations - PROJECT_JSON_FIXTURES.flat_map(&method(:relations_from_json)).to_set + project_json_fixtures.flat_map(&method(:relations_from_json)).to_set end def relations_from_json(json_file) @@ -106,7 +111,7 @@ describe 'Test coverage of the Project Import' do These relations seem to be added recenty and they expected to be covered in our Import specs: #{not_tested_relations}. - To do that, expand one of the files listed in `PROJECT_JSON_FIXTURES` + To do that, expand one of the files listed in `project_json_fixtures` (or expand the list if you consider adding a new fixture file). After that, add a new spec into @@ -114,7 +119,7 @@ describe 'Test coverage of the Project Import' do to check that the relation is being imported correctly. In case the spec breaks the master or there is a sense of urgency, - you could include the relations into the `MUTED_RELATIONS` list. + you could include the relations into the `muted_relations` list. Muting relations is considered to be a temporary solution, so please open a follow-up issue and try to fix that when it is possible. diff --git a/spec/lib/gitlab/import_export/importer_spec.rb b/spec/lib/gitlab/import_export/importer_spec.rb index 60179146416..494f7e3a00d 100644 --- a/spec/lib/gitlab/import_export/importer_spec.rb +++ b/spec/lib/gitlab/import_export/importer_spec.rb @@ -18,6 +18,7 @@ describe Gitlab::ImportExport::Importer do FileUtils.mkdir_p(shared.export_path) ImportExportUpload.create(project: project, import_file: import_file) + allow(FileUtils).to receive(:rm_rf).and_call_original end after do @@ -78,6 +79,13 @@ describe Gitlab::ImportExport::Importer do expect(project.import_export_upload.import_file&.file).to be_nil end + it 'removes tmp files' do + importer.execute + + expect(FileUtils).to have_received(:rm_rf).with(shared.base_path) + expect(Dir.exist?(shared.base_path)).to eq(false) + end + it 'sets the correct visibility_level when visibility level is a string' do project.create_or_update_import_data( data: { override_params: { visibility_level: Gitlab::VisibilityLevel::PRIVATE.to_s } } @@ -89,6 +97,49 @@ describe Gitlab::ImportExport::Importer do end end + context 'when import fails' do + let(:error_message) { 'foo' } + + shared_examples 'removes any non migrated snippet' do + specify do + create_list(:project_snippet, 2, project: project) + snippet_with_repo = create(:project_snippet, :repository, project: project) + + expect { importer.execute }.to change(Snippet, :count).by(-2).and(raise_error(Projects::ImportService::Error)) + + expect(snippet_with_repo.reload).to be_present + end + end + + context 'when there is a graceful error' do + before do + allow_next_instance_of(Gitlab::ImportExport::AvatarRestorer) do |instance| + allow(instance).to receive(:avatar_export_file).and_raise(StandardError, error_message) + end + end + + it 'raises and exception' do + expect { importer.execute }.to raise_error(Projects::ImportService::Error, error_message) + end + + it_behaves_like 'removes any non migrated snippet' + end + + context 'when an unexpected exception is raised' do + before do + allow_next_instance_of(Gitlab::ImportExport::AvatarRestorer) do |instance| + allow(instance).to receive(:restore).and_raise(StandardError, error_message) + end + end + + it 'captures it and raises the Projects::ImportService::Error exception' do + expect { importer.execute }.to raise_error(Projects::ImportService::Error, error_message) + end + + it_behaves_like 'removes any non migrated snippet' + end + end + context 'when project successfully restored' do context "with a project in a user's namespace" do let!(:existing_project) { create(:project, namespace: user.namespace) } diff --git a/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb b/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb index 076f454895f..30f8280fda3 100644 --- a/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb +++ b/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb @@ -95,4 +95,26 @@ describe Gitlab::ImportExport::JSON::StreamingSerializer do end end end + + describe '.batch_size' do + context 'when export_reduce_relation_batch_size feature flag is enabled' do + before do + stub_feature_flags(export_reduce_relation_batch_size: true) + end + + it 'returns 20' do + expect(described_class.batch_size(exportable)).to eq(described_class::SMALLER_BATCH_SIZE) + end + end + + context 'when export_reduce_relation_batch_size feature flag is disabled' do + before do + stub_feature_flags(export_reduce_relation_batch_size: false) + end + + it 'returns default batch size' do + expect(described_class.batch_size(exportable)).to eq(described_class::BATCH_SIZE) + end + end + end end diff --git a/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb b/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb index 958865f52a0..6562aa5b8a6 100644 --- a/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb @@ -8,17 +8,35 @@ describe Gitlab::ImportExport::LegacyRelationTreeSaver do let(:tree) { {} } describe '#serialize' do - let(:serializer) { instance_double(Gitlab::ImportExport::FastHashSerializer) } + shared_examples 'FastHashSerializer with batch size' do |batch_size| + let(:serializer) { instance_double(Gitlab::ImportExport::FastHashSerializer) } - it 'uses FastHashSerializer' do - expect(Gitlab::ImportExport::FastHashSerializer) - .to receive(:new) - .with(exportable, tree) - .and_return(serializer) + it 'uses FastHashSerializer' do + expect(Gitlab::ImportExport::FastHashSerializer) + .to receive(:new) + .with(exportable, tree, batch_size: batch_size) + .and_return(serializer) - expect(serializer).to receive(:execute) + expect(serializer).to receive(:execute) - relation_tree_saver.serialize(exportable, tree) + relation_tree_saver.serialize(exportable, tree) + end + end + + context 'when export_reduce_relation_batch_size feature flag is enabled' do + before do + stub_feature_flags(export_reduce_relation_batch_size: true) + end + + include_examples 'FastHashSerializer with batch size', Gitlab::ImportExport::JSON::StreamingSerializer::SMALLER_BATCH_SIZE + end + + context 'when export_reduce_relation_batch_size feature flag is disabled' do + before do + stub_feature_flags(export_reduce_relation_batch_size: false) + end + + include_examples 'FastHashSerializer with batch size', Gitlab::ImportExport::JSON::StreamingSerializer::BATCH_SIZE end end end diff --git a/spec/lib/gitlab/import_export/project/relation_factory_spec.rb b/spec/lib/gitlab/import_export/project/relation_factory_spec.rb index 175da623c1b..3339129cb8f 100644 --- a/spec/lib/gitlab/import_export/project/relation_factory_spec.rb +++ b/spec/lib/gitlab/import_export/project/relation_factory_spec.rb @@ -18,6 +18,22 @@ describe Gitlab::ImportExport::Project::RelationFactory do excluded_keys: excluded_keys) end + before do + # Mocks an ActiveRecordish object with the dodgy columns + stub_const('FooModel', Class.new) + FooModel.class_eval do + include ActiveModel::Model + + def initialize(params = {}) + params.each { |key, value| send("#{key}=", value) } + end + + def values + instance_variables.map { |ivar| instance_variable_get(ivar) } + end + end + end + context 'hook object' do let(:relation_sym) { :hooks } let(:id) { 999 } @@ -83,19 +99,6 @@ describe Gitlab::ImportExport::Project::RelationFactory do end end - # Mocks an ActiveRecordish object with the dodgy columns - class FooModel - include ActiveModel::Model - - def initialize(params = {}) - params.each { |key, value| send("#{key}=", value) } - end - - def values - instance_variables.map { |ivar| instance_variable_get(ivar) } - end - end - context 'merge_request object' do let(:relation_sym) { :merge_requests } @@ -208,11 +211,12 @@ describe Gitlab::ImportExport::Project::RelationFactory do } end - class HazardousFooModel < FooModel - attr_accessor :service_id, :moved_to_id, :namespace_id, :ci_id, :random_project_id, :random_id, :milestone_id, :project_id - end - before do + stub_const('HazardousFooModel', Class.new(FooModel)) + HazardousFooModel.class_eval do + attr_accessor :service_id, :moved_to_id, :namespace_id, :ci_id, :random_project_id, :random_id, :milestone_id, :project_id + end + allow(HazardousFooModel).to receive(:reflect_on_association).and_return(nil) end @@ -246,11 +250,12 @@ describe Gitlab::ImportExport::Project::RelationFactory do Gitlab::ImportExport::Project::RelationFactory::PROJECT_REFERENCES.map { |ref| { ref => 99 } }.inject(:merge) end - class ProjectFooModel < FooModel - attr_accessor(*Gitlab::ImportExport::Project::RelationFactory::PROJECT_REFERENCES) - end - before do + stub_const('ProjectFooModel', Class.new(FooModel)) + ProjectFooModel.class_eval do + attr_accessor(*Gitlab::ImportExport::Project::RelationFactory::PROJECT_REFERENCES) + end + allow(ProjectFooModel).to receive(:reflect_on_association).and_return(nil) end diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb index 58589a7bbbe..867dc37c5c5 100644 --- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb @@ -26,7 +26,7 @@ describe Gitlab::ImportExport::Project::TreeRestorer do @project = create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') @shared = @project.import_export_shared - allow(Feature).to receive(:enabled?) { true } + stub_all_feature_flags stub_feature_flags(project_import_ndjson: ndjson_enabled) setup_import_export_config('complex') diff --git a/spec/lib/gitlab/import_export/project/tree_saver_spec.rb b/spec/lib/gitlab/import_export/project/tree_saver_spec.rb index b9bfe253f10..533d1097928 100644 --- a/spec/lib/gitlab/import_export/project/tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_saver_spec.rb @@ -29,7 +29,7 @@ describe Gitlab::ImportExport::Project::TreeSaver do before_all do RSpec::Mocks.with_temporary_scope do - allow(Feature).to receive(:enabled?) { true } + stub_all_feature_flags stub_feature_flags(project_export_as_ndjson: ndjson_enabled) project.add_maintainer(user) diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb index a61d966bdfa..d5839589633 100644 --- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb @@ -13,11 +13,8 @@ describe Gitlab::ImportExport::RepoRestorer do let(:shared) { project.import_export_shared } let(:bundler) { Gitlab::ImportExport::RepoSaver.new(project: project_with_repo, shared: shared) } let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename) } - let(:restorer) do - described_class.new(path_to_bundle: bundle_path, - shared: shared, - project: project) - end + + subject { described_class.new(path_to_bundle: bundle_path, shared: shared, project: project) } before do allow_next_instance_of(Gitlab::ImportExport) do |instance| @@ -36,7 +33,25 @@ describe Gitlab::ImportExport::RepoRestorer do end it 'restores the repo successfully' do - expect(restorer.restore).to be_truthy + expect(subject.restore).to be_truthy + end + + context 'when the repository creation fails' do + before do + allow_next_instance_of(Repositories::DestroyService) do |instance| + expect(instance).to receive(:execute).and_call_original + end + end + + it 'logs the error' do + allow(project.repository) + .to receive(:create_from_bundle) + .and_raise('9:CreateRepositoryFromBundle: target directory is non-empty') + + expect(shared).to receive(:error).and_call_original + + expect(subject.restore).to be_falsey + end end end end diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index c29a85ce624..0d112bfdb2a 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -144,6 +144,7 @@ Releases::Link: - url - name - filepath +- link_type - created_at - updated_at ProjectMember: @@ -471,6 +472,7 @@ Service: - properties - template - instance +- alert_events - push_events - issues_events - commit_events @@ -701,6 +703,8 @@ Badge: - type ProjectCiCdSetting: - group_runners_enabled +ProjectSetting: +- allow_merge_on_skipped_pipeline ProtectedEnvironment: - id - project_id @@ -749,6 +753,7 @@ ProjectMetricsSetting: - external_dashboard_url - created_at - updated_at +- dashboard_timezone Board: - id - project_id @@ -861,3 +866,11 @@ SystemNoteMetadata: - action - created_at - updated_at +ProjectSecuritySetting: + - project_id + - auto_fix_container_scanning + - auto_fix_dast + - auto_fix_dependency_scanning + - auto_fix_sast + - created_at + - updated_at diff --git a/spec/lib/gitlab/import_export/saver_spec.rb b/spec/lib/gitlab/import_export/saver_spec.rb index a59cf7a1260..18e9d7da32d 100644 --- a/spec/lib/gitlab/import_export/saver_spec.rb +++ b/spec/lib/gitlab/import_export/saver_spec.rb @@ -5,18 +5,21 @@ require 'fileutils' describe Gitlab::ImportExport::Saver do let!(:project) { create(:project, :public, name: 'project') } - let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } + let(:base_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } + let(:export_path) { "#{base_path}/project_tree_saver_spec/export" } let(:shared) { project.import_export_shared } subject { described_class.new(exportable: project, shared: shared) } before do + allow(shared).to receive(:base_path).and_return(base_path) allow_next_instance_of(Gitlab::ImportExport) do |instance| allow(instance).to receive(:storage_path).and_return(export_path) end FileUtils.mkdir_p(shared.export_path) FileUtils.touch("#{shared.export_path}/tmp.bundle") + allow(FileUtils).to receive(:rm_rf).and_call_original end after do @@ -31,4 +34,11 @@ describe Gitlab::ImportExport::Saver do expect(ImportExportUpload.find_by(project: project).export_file.url) .to match(%r[\/uploads\/-\/system\/import_export_upload\/export_file.*]) end + + it 'removes tmp files' do + subject.save + + expect(FileUtils).to have_received(:rm_rf).with(base_path) + expect(Dir.exist?(base_path)).to eq(false) + end end diff --git a/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb b/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb index 3ce950d6a64..779b65e33d8 100644 --- a/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb @@ -25,16 +25,24 @@ describe Gitlab::ImportExport::SnippetRepoRestorer do expect(snippet.repository_exists?).to be_falsey aggregate_failures do - expect(restorer.restore).to be_truthy - - expect(snippet.repository_exists?).to be_truthy - expect(snippet.snippet_repository).not_to be_nil + expect do + expect(restorer.restore).to be_truthy + end.to change { SnippetRepository.count }.by(1) blob = snippet.repository.blob_at('HEAD', snippet.file_name) expect(blob).not_to be_nil expect(blob.data).to eq(snippet.content) end end + + context 'when the repository creation fails' do + it 'returns false' do + allow_any_instance_of(Gitlab::BackgroundMigration::BackfillSnippetRepositories).to receive(:perform_by_ids).and_return(nil) + + expect(restorer.restore).to be false + expect(shared.errors.first).to match(/Error creating repository for snippet/) + end + end end context 'when the snippet does not have a bundle file path' do diff --git a/spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb b/spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb index 242f6f6b58c..fdae259c2f1 100644 --- a/spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb @@ -8,43 +8,92 @@ describe Gitlab::ImportExport::SnippetsRepoRestorer do describe 'bundle a snippet Git repo' do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, namespace: user.namespace) } - let_it_be(:snippet_with_repo) { create(:project_snippet, :repository, project: project, author: user) } - let_it_be(:snippet_without_repo) { create(:project_snippet, project: project, author: user) } + let!(:snippet1) { create(:project_snippet, project: project, author: user) } + let!(:snippet2) { create(:project_snippet, project: project, author: user) } let(:shared) { project.import_export_shared } let(:exporter) { Gitlab::ImportExport::SnippetsRepoSaver.new(current_user: user, project: project, shared: shared) } let(:bundle_dir) { ::Gitlab::ImportExport.snippets_repo_bundle_path(shared.export_path) } + let(:service) { instance_double(Gitlab::ImportExport::SnippetRepoRestorer) } let(:restorer) do described_class.new(user: user, shared: shared, project: project) end - let(:service) { instance_double(Gitlab::ImportExport::SnippetRepoRestorer) } - - before do - exporter.save - end after do FileUtils.rm_rf(shared.export_path) end - it 'calls SnippetRepoRestorer per each snippet with the bundle path' do - allow(service).to receive(:restore).and_return(true) + shared_examples 'imports snippet repositories' do + before do + snippet1.snippet_repository&.delete + snippet1.repository.remove + + snippet2.snippet_repository&.delete + snippet2.repository.remove + end + + specify do + expect(snippet1.repository_exists?).to be false + expect(snippet2.repository_exists?).to be false + + expect(Gitlab::ImportExport::SnippetRepoRestorer).to receive(:new).with(hash_including(snippet: snippet1, path_to_bundle: bundle_path(snippet1))).and_call_original + expect(Gitlab::ImportExport::SnippetRepoRestorer).to receive(:new).with(hash_including(snippet: snippet2, path_to_bundle: bundle_path(snippet2))).and_call_original + expect(restorer.restore).to be_truthy + + snippet1.repository.expire_exists_cache + snippet2.repository.expire_exists_cache + + expect(snippet1.blobs).not_to be_empty + expect(snippet2.blobs).not_to be_empty + end + end + + context 'when export has no snippet repository bundle' do + before do + expect(Dir.exist?(bundle_dir)).to be false + end + + it_behaves_like 'imports snippet repositories' + end + + context 'when export has snippet repository bundles and snippets without them' do + let!(:snippet1) { create(:project_snippet, :repository, project: project, author: user) } + let!(:snippet2) { create(:project_snippet, project: project, author: user) } - expect(Gitlab::ImportExport::SnippetRepoRestorer).to receive(:new).with(hash_including(snippet: snippet_with_repo, path_to_bundle: bundle_path(snippet_with_repo))).and_return(service) - expect(Gitlab::ImportExport::SnippetRepoRestorer).to receive(:new).with(hash_including(snippet: snippet_without_repo, path_to_bundle: bundle_path(snippet_without_repo))).and_return(service) + before do + exporter.save - expect(restorer.restore).to be_truthy + expect(File.exist?(bundle_path(snippet1))).to be true + expect(File.exist?(bundle_path(snippet2))).to be false + end + + it_behaves_like 'imports snippet repositories' end - context 'when one snippet cannot be saved' do - it 'returns false and do not process other snippets' do - allow(Gitlab::ImportExport::SnippetRepoRestorer).to receive(:new).with(hash_including(snippet: snippet_with_repo)).and_return(service) + context 'when export has only snippet bundles' do + let!(:snippet1) { create(:project_snippet, :repository, project: project, author: user) } + let!(:snippet2) { create(:project_snippet, :repository, project: project, author: user) } + + before do + exporter.save + + expect(File.exist?(bundle_path(snippet1))).to be true + expect(File.exist?(bundle_path(snippet2))).to be true + end + + it_behaves_like 'imports snippet repositories' + end + + context 'when any of the snippet repositories cannot be created' do + it 'continues processing other snippets and returns false' do + allow(Gitlab::ImportExport::SnippetRepoRestorer).to receive(:new).with(hash_including(snippet: snippet1)).and_return(service) allow(service).to receive(:restore).and_return(false) - expect(Gitlab::ImportExport::SnippetRepoRestorer).not_to receive(:new).with(hash_including(snippet: snippet_without_repo)) - expect(restorer.restore).to be_falsey + expect(Gitlab::ImportExport::SnippetRepoRestorer).to receive(:new).with(hash_including(snippet: snippet2)).and_call_original + + expect(restorer.restore).to be false end end |