From 311b0269b4eb9839fa63f80c8d7a58f32b8138a0 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 18 Nov 2021 13:16:36 +0000 Subject: Add latest changes from gitlab-org/gitlab@14-5-stable-ee --- spec/lib/gitlab/import_export/all_models.yml | 3 + .../import_export/attributes_permitter_spec.rb | 83 +++++++--- .../import_export/fast_hash_serializer_spec.rb | 4 +- .../group/relation_tree_restorer_spec.rb | 88 ++++++++++ .../import_export/project/object_builder_spec.rb | 132 +++++++++++++++ .../project/relation_tree_restorer_spec.rb | 150 +++++++++++++++++ .../project/sample/relation_tree_restorer_spec.rb | 48 +++--- .../import_export/project/tree_restorer_spec.rb | 3 +- .../import_export/project/tree_saver_spec.rb | 50 +++++- .../import_export/relation_tree_restorer_spec.rb | 184 --------------------- .../gitlab/import_export/safe_model_attributes.yml | 2 + 11 files changed, 510 insertions(+), 237 deletions(-) create mode 100644 spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb create mode 100644 spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb delete mode 100644 spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb (limited to 'spec/lib/gitlab/import_export') diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 10f0e687077..b474f5825fd 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -60,6 +60,7 @@ issues: - incident_management_issuable_escalation_status - pending_escalations - customer_relations_contacts +- issue_customer_relations_contacts work_item_type: - issues events: @@ -132,6 +133,7 @@ project_members: - user - source - project +- member_task merge_requests: - status_check_responses - subscriptions @@ -382,6 +384,7 @@ project: - emails_on_push_integration - pipelines_email_integration - mattermost_slash_commands_integration +- shimo_integration - slack_slash_commands_integration - irker_integration - packagist_integration diff --git a/spec/lib/gitlab/import_export/attributes_permitter_spec.rb b/spec/lib/gitlab/import_export/attributes_permitter_spec.rb index 2b974f8985d..8ae387d95e3 100644 --- a/spec/lib/gitlab/import_export/attributes_permitter_spec.rb +++ b/spec/lib/gitlab/import_export/attributes_permitter_spec.rb @@ -80,25 +80,66 @@ RSpec.describe Gitlab::ImportExport::AttributesPermitter do let(:attributes_permitter) { described_class.new } - where(:relation_name, :permitted_attributes_defined) do - :user | false - :author | false - :ci_cd_settings | true - :metrics_setting | true - :project_badges | true - :pipeline_schedules | true - :error_tracking_setting | true - :auto_devops | true - :boards | true - :custom_attributes | true - :labels | true - :protected_branches | true - :protected_tags | true - :create_access_levels | true - :merge_access_levels | true - :push_access_levels | true - :releases | true - :links | true + where(:relation_name, :permitted_attributes_defined ) do + :user | true + :author | false + :ci_cd_settings | true + :metrics_setting | true + :project_badges | true + :pipeline_schedules | true + :error_tracking_setting | true + :auto_devops | true + :boards | true + :custom_attributes | true + :label | true + :labels | true + :protected_branches | true + :protected_tags | true + :create_access_levels | true + :merge_access_levels | true + :push_access_levels | true + :releases | true + :links | true + :priorities | true + :milestone | true + :milestones | true + :snippets | true + :project_members | true + :merge_request | true + :merge_requests | true + :award_emoji | true + :commit_author | true + :committer | true + :events | true + :label_links | true + :merge_request_diff | true + :merge_request_diff_commits | true + :merge_request_diff_files | true + :metrics | true + :notes | true + :push_event_payload | true + :resource_label_events | true + :suggestions | true + :system_note_metadata | true + :timelogs | true + :container_expiration_policy | true + :project_feature | true + :prometheus_metrics | true + :service_desk_setting | true + :external_pull_request | true + :external_pull_requests | true + :statuses | true + :ci_pipelines | true + :stages | true + :actions | true + :design | true + :designs | true + :design_versions | true + :issue_assignees | true + :sentry_issue | true + :zoom_meetings | true + :issues | true + :group_members | true end with_them do @@ -109,9 +150,11 @@ RSpec.describe Gitlab::ImportExport::AttributesPermitter do describe 'included_attributes for Project' do subject { described_class.new } + additional_attributes = { user: %w[id] } + Gitlab::ImportExport::Config.new.to_h[:included_attributes].each do |relation_sym, permitted_attributes| context "for #{relation_sym}" do - it_behaves_like 'a permitted attribute', relation_sym, permitted_attributes + it_behaves_like 'a permitted attribute', relation_sym, permitted_attributes, additional_attributes[relation_sym] end end end diff --git a/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb b/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb index fc08a13a8bd..d5f31f235f5 100644 --- a/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb +++ b/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb @@ -207,9 +207,9 @@ RSpec.describe Gitlab::ImportExport::FastHashSerializer do context 'relation ordering' do it 'orders exported pipelines by primary key' do - expected_order = project.ci_pipelines.reorder(:id).ids + expected_order = project.ci_pipelines.reorder(:id).pluck(:sha) - expect(subject['ci_pipelines'].pluck('id')).to eq(expected_order) + expect(subject['ci_pipelines'].pluck('sha')).to eq(expected_order) end end diff --git a/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb new file mode 100644 index 00000000000..473dbf5ecc5 --- /dev/null +++ b/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# This spec is a lightweight version of: +# * project/tree_restorer_spec.rb +# +# In depth testing is being done in the above specs. +# This spec tests that restore project works +# but does not have 100% relation coverage. + +require 'spec_helper' + +RSpec.describe Gitlab::ImportExport::Group::RelationTreeRestorer do + let_it_be(:group) { create(:group) } + let_it_be(:importable) { create(:group, parent: group) } + + include_context 'relation tree restorer shared context' do + let(:importable_name) { nil } + end + + let(:path) { 'spec/fixtures/lib/gitlab/import_export/group_exports/no_children/group.json' } + let(:relation_reader) do + Gitlab::ImportExport::Json::LegacyReader::File.new( + path, + relation_names: reader.group_relation_names) + end + + let(:reader) do + Gitlab::ImportExport::Reader.new( + shared: shared, + config: Gitlab::ImportExport::Config.new(config: Gitlab::ImportExport.legacy_group_config_file).to_h + ) + end + + let(:relation_tree_restorer) do + described_class.new( + user: user, + shared: shared, + relation_reader: relation_reader, + object_builder: Gitlab::ImportExport::Group::ObjectBuilder, + members_mapper: members_mapper, + relation_factory: Gitlab::ImportExport::Group::RelationFactory, + reader: reader, + importable: importable, + importable_path: nil, + importable_attributes: attributes + ) + end + + subject { relation_tree_restorer.restore } + + shared_examples 'logging of relations creation' do + context 'when log_import_export_relation_creation feature flag is enabled' do + before do + stub_feature_flags(log_import_export_relation_creation: group) + end + + it 'logs top-level relation creation' do + expect(shared.logger) + .to receive(:info) + .with(hash_including(message: '[Project/Group Import] Created new object relation')) + .at_least(:once) + + subject + end + end + + context 'when log_import_export_relation_creation feature flag is disabled' do + before do + stub_feature_flags(log_import_export_relation_creation: false) + end + + it 'does not log top-level relation creation' do + expect(shared.logger) + .to receive(:info) + .with(hash_including(message: '[Project/Group Import] Created new object relation')) + .never + + subject + end + end + end + + it 'restores group tree' do + expect(subject).to eq(true) + end + + include_examples 'logging of relations creation' +end diff --git a/spec/lib/gitlab/import_export/project/object_builder_spec.rb b/spec/lib/gitlab/import_export/project/object_builder_spec.rb index 4c9f9f7c690..189b798c2e8 100644 --- a/spec/lib/gitlab/import_export/project/object_builder_spec.rb +++ b/spec/lib/gitlab/import_export/project/object_builder_spec.rb @@ -123,6 +123,24 @@ RSpec.describe Gitlab::ImportExport::Project::ObjectBuilder do expect(milestone.persisted?).to be true end + + context 'with clashing iid' do + it 'creates milestone and claims iid for the new milestone' do + clashing_iid = 1 + create(:milestone, iid: clashing_iid, project: project) + + milestone = described_class.build(Milestone, + 'iid' => clashing_iid, + 'title' => 'milestone', + 'project' => project, + 'group' => nil, + 'group_id' => nil) + + expect(milestone.persisted?).to be true + expect(Milestone.count).to eq(2) + expect(milestone.iid).to eq(clashing_iid) + end + end end context 'merge_request' do @@ -176,4 +194,118 @@ RSpec.describe Gitlab::ImportExport::Project::ObjectBuilder do expect(found.email).to eq('alice@example.com') end end + + context 'merge request diff commits' do + context 'when the "committer" object is present' do + it 'uses this object as the committer' do + user = MergeRequest::DiffCommitUser + .find_or_create('Alice', 'alice@example.com') + + commit = described_class.build( + MergeRequestDiffCommit, + { + 'committer' => user, + 'committer_name' => 'Bla', + 'committer_email' => 'bla@example.com', + 'author_name' => 'Bla', + 'author_email' => 'bla@example.com' + } + ) + + expect(commit.committer).to eq(user) + end + end + + context 'when the "committer" object is missing' do + it 'creates one from the committer name and Email' do + commit = described_class.build( + MergeRequestDiffCommit, + { + 'committer_name' => 'Alice', + 'committer_email' => 'alice@example.com', + 'author_name' => 'Alice', + 'author_email' => 'alice@example.com' + } + ) + + expect(commit.committer.name).to eq('Alice') + expect(commit.committer.email).to eq('alice@example.com') + end + end + + context 'when the "commit_author" object is present' do + it 'uses this object as the author' do + user = MergeRequest::DiffCommitUser + .find_or_create('Alice', 'alice@example.com') + + commit = described_class.build( + MergeRequestDiffCommit, + { + 'committer_name' => 'Alice', + 'committer_email' => 'alice@example.com', + 'commit_author' => user, + 'author_name' => 'Bla', + 'author_email' => 'bla@example.com' + } + ) + + expect(commit.commit_author).to eq(user) + end + end + + context 'when the "commit_author" object is missing' do + it 'creates one from the author name and Email' do + commit = described_class.build( + MergeRequestDiffCommit, + { + 'committer_name' => 'Alice', + 'committer_email' => 'alice@example.com', + 'author_name' => 'Alice', + 'author_email' => 'alice@example.com' + } + ) + + expect(commit.commit_author.name).to eq('Alice') + expect(commit.commit_author.email).to eq('alice@example.com') + end + end + end + + describe '#find_or_create_diff_commit_user' do + context 'when the user already exists' do + it 'returns the existing user' do + user = MergeRequest::DiffCommitUser + .find_or_create('Alice', 'alice@example.com') + + found = described_class + .new(MergeRequestDiffCommit, {}) + .send(:find_or_create_diff_commit_user, user.name, user.email) + + expect(found).to eq(user) + end + end + + context 'when the user does not exist' do + it 'creates the user' do + found = described_class + .new(MergeRequestDiffCommit, {}) + .send(:find_or_create_diff_commit_user, 'Alice', 'alice@example.com') + + expect(found.name).to eq('Alice') + expect(found.email).to eq('alice@example.com') + end + end + + it 'caches the results' do + builder = described_class.new(MergeRequestDiffCommit, {}) + + builder.send(:find_or_create_diff_commit_user, 'Alice', 'alice@example.com') + + record = ActiveRecord::QueryRecorder.new do + builder.send(:find_or_create_diff_commit_user, 'Alice', 'alice@example.com') + end + + expect(record.count).to eq(1) + end + end end diff --git a/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb new file mode 100644 index 00000000000..5ebace263ba --- /dev/null +++ b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +# This spec is a lightweight version of: +# * project/tree_restorer_spec.rb +# +# In depth testing is being done in the above specs. +# This spec tests that restore project works +# but does not have 100% relation coverage. + +require 'spec_helper' + +RSpec.describe Gitlab::ImportExport::Project::RelationTreeRestorer do + let_it_be(:importable, reload: true) do + create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') + end + + include_context 'relation tree restorer shared context' do + let(:importable_name) { 'project' } + end + + let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) } + let(:relation_tree_restorer) do + described_class.new( + user: user, + shared: shared, + relation_reader: relation_reader, + object_builder: Gitlab::ImportExport::Project::ObjectBuilder, + members_mapper: members_mapper, + relation_factory: Gitlab::ImportExport::Project::RelationFactory, + reader: reader, + importable: importable, + importable_path: 'project', + importable_attributes: attributes + ) + end + + subject { relation_tree_restorer.restore } + + shared_examples 'import project successfully' do + describe 'imported project' do + it 'has the project attributes and relations', :aggregate_failures do + expect(subject).to eq(true) + + project = Project.find_by_path('project') + + expect(project.description).to eq('Nisi et repellendus ut enim quo accusamus vel magnam.') + expect(project.labels.count).to eq(3) + expect(project.boards.count).to eq(1) + expect(project.project_feature).not_to be_nil + expect(project.custom_attributes.count).to eq(2) + expect(project.project_badges.count).to eq(2) + expect(project.snippets.count).to eq(1) + end + end + end + + shared_examples 'logging of relations creation' do + context 'when log_import_export_relation_creation feature flag is enabled' do + before do + stub_feature_flags(log_import_export_relation_creation: group) + end + + it 'logs top-level relation creation' do + expect(shared.logger) + .to receive(:info) + .with(hash_including(message: '[Project/Group Import] Created new object relation')) + .at_least(:once) + + subject + end + end + + context 'when log_import_export_relation_creation feature flag is disabled' do + before do + stub_feature_flags(log_import_export_relation_creation: false) + end + + it 'does not log top-level relation creation' do + expect(shared.logger) + .to receive(:info) + .with(hash_including(message: '[Project/Group Import] Created new object relation')) + .never + + subject + end + end + end + + context 'with legacy reader' do + let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/project.json' } + let(:relation_reader) do + Gitlab::ImportExport::Json::LegacyReader::File.new( + path, + relation_names: reader.project_relation_names, + allowed_path: 'project' + ) + end + + let(:attributes) { relation_reader.consume_attributes('project') } + + it_behaves_like 'import project successfully' + + context 'with logging of relations creation' do + let_it_be(:group) { create(:group) } + let_it_be(:importable) do + create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project', group: group) + end + + include_examples 'logging of relations creation' + end + end + + context 'with ndjson reader' do + let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/tree' } + let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } + + it_behaves_like 'import project successfully' + + context 'when inside a group' do + let_it_be(:group) do + create(:group, :disabled_and_unoverridable) + end + + before do + importable.update!(shared_runners_enabled: false, group: group) + end + + it_behaves_like 'import project successfully' + end + end + + context 'with invalid relations' do + let(:path) { 'spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree' } + let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } + + it 'logs the invalid relation and its errors' do + expect(shared.logger) + .to receive(:warn) + .with( + error_messages: "Title can't be blank. Title is invalid", + message: '[Project/Group Import] Invalid object relation built', + relation_class: 'ProjectLabel', + relation_index: 0, + relation_key: 'labels' + ).once + + relation_tree_restorer.restore + end + end +end diff --git a/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb index f6a028383f2..3dab84af744 100644 --- a/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb @@ -10,19 +10,26 @@ require 'spec_helper' RSpec.describe Gitlab::ImportExport::Project::Sample::RelationTreeRestorer do - include_context 'relation tree restorer shared context' + let_it_be(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') } + include_context 'relation tree restorer shared context' do + let(:importable_name) { 'project' } + end + + let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) } + let(:path) { 'spec/fixtures/lib/gitlab/import_export/sample_data/tree' } + let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } let(:sample_data_relation_tree_restorer) do described_class.new( user: user, shared: shared, relation_reader: relation_reader, - object_builder: object_builder, + object_builder: Gitlab::ImportExport::Project::ObjectBuilder, members_mapper: members_mapper, - relation_factory: relation_factory, + relation_factory: Gitlab::ImportExport::Project::Sample::RelationFactory, reader: reader, importable: importable, - importable_path: importable_path, + importable_path: 'project', importable_attributes: attributes ) end @@ -69,32 +76,21 @@ RSpec.describe Gitlab::ImportExport::Project::Sample::RelationTreeRestorer do end end - context 'when restoring a project' do - let(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') } - let(:importable_name) { 'project' } - let(:importable_path) { 'project' } - let(:object_builder) { Gitlab::ImportExport::Project::ObjectBuilder } - let(:relation_factory) { Gitlab::ImportExport::Project::Sample::RelationFactory } - let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) } - let(:path) { 'spec/fixtures/lib/gitlab/import_export/sample_data/tree' } - let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } - - it 'initializes relation_factory with date_calculator as parameter' do - expect(Gitlab::ImportExport::Project::Sample::RelationFactory).to receive(:create).with(hash_including(:date_calculator)).at_least(:once).times + it 'initializes relation_factory with date_calculator as parameter' do + expect(Gitlab::ImportExport::Project::Sample::RelationFactory).to receive(:create).with(hash_including(:date_calculator)).at_least(:once).times - subject - end + subject + end - context 'when relation tree restorer is initialized' do - it 'initializes date calculator with due dates' do - expect(Gitlab::ImportExport::Project::Sample::DateCalculator).to receive(:new).with(Array) + context 'when relation tree restorer is initialized' do + it 'initializes date calculator with due dates' do + expect(Gitlab::ImportExport::Project::Sample::DateCalculator).to receive(:new).with(Array) - sample_data_relation_tree_restorer - end + sample_data_relation_tree_restorer end + end - context 'using ndjson reader' do - it_behaves_like 'import project successfully' - end + context 'using ndjson reader' do + it_behaves_like 'import project successfully' end 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 f512f49764d..cd3d29f1a51 100644 --- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb @@ -23,7 +23,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do ] RSpec::Mocks.with_temporary_scope do - @project = create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') + @project = create(:project, :repository, :builds_enabled, :issues_disabled, name: 'project', path: 'project') @shared = @project.import_export_shared stub_all_feature_flags @@ -36,7 +36,6 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do allow_any_instance_of(Gitlab::Git::Repository).to receive(:branch_exists?).and_return(false) expect(@shared).not_to receive(:error) - expect_any_instance_of(Gitlab::Git::Repository).to receive(:create_branch).with('feature', 'DCBA') allow_any_instance_of(Gitlab::Git::Repository).to receive(:create_branch) project_tree_restorer = described_class.new(user: @user, shared: @shared, project: @project) 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 374d688576e..f68ec21039d 100644 --- a/spec/lib/gitlab/import_export/project/tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_saver_spec.rb @@ -5,6 +5,9 @@ require 'spec_helper' RSpec.describe Gitlab::ImportExport::Project::TreeSaver do let_it_be(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let_it_be(:exportable_path) { 'project' } + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { setup_project } shared_examples 'saves project tree successfully' do |ndjson_enabled| include ImportExport::CommonUtil @@ -12,9 +15,6 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do subject { get_json(full_path, exportable_path, relation_name, ndjson_enabled) } describe 'saves project tree attributes' do - let_it_be(:user) { create(:user) } - let_it_be(:group) { create(:group) } - let_it_be(:project) { setup_project } let_it_be(:shared) { project.import_export_shared } let(:relation_name) { :projects } @@ -402,6 +402,50 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do it_behaves_like "saves project tree successfully", true end + context 'when streaming has to retry', :aggregate_failures do + let(:shared) { double('shared', export_path: exportable_path) } + let(:logger) { Gitlab::Import::Logger.build } + let(:serializer) { double('serializer') } + let(:error_class) { Net::OpenTimeout } + let(:info_params) do + { + 'error.class': error_class, + project_name: project.name, + project_id: project.id + } + end + + before do + allow(Gitlab::ImportExport::Json::StreamingSerializer).to receive(:new).and_return(serializer) + end + + subject(:project_tree_saver) do + described_class.new(project: project, current_user: user, shared: shared, logger: logger) + end + + it 'retries and succeeds' do + call_count = 0 + allow(serializer).to receive(:execute) do + call_count += 1 + call_count > 1 ? true : raise(error_class, 'execution expired') + end + + expect(logger).to receive(:info).with(hash_including(info_params)).once + + expect(project_tree_saver.save).to be(true) + end + + it 'retries and does not succeed' do + retry_count = 3 + allow(serializer).to receive(:execute).and_raise(error_class, 'execution expired') + + expect(logger).to receive(:info).with(hash_including(info_params)).exactly(retry_count).times + expect(shared).to receive(:error).with(instance_of(error_class)) + + expect(project_tree_saver.save).to be(false) + end + end + def setup_project release = create(:release) diff --git a/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb deleted file mode 100644 index 5e4075c2b59..00000000000 --- a/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb +++ /dev/null @@ -1,184 +0,0 @@ -# frozen_string_literal: true - -# This spec is a lightweight version of: -# * project/tree_restorer_spec.rb -# -# In depth testing is being done in the above specs. -# This spec tests that restore project works -# but does not have 100% relation coverage. - -require 'spec_helper' - -RSpec.describe Gitlab::ImportExport::RelationTreeRestorer do - include_context 'relation tree restorer shared context' - - let(:relation_tree_restorer) do - described_class.new( - user: user, - shared: shared, - relation_reader: relation_reader, - object_builder: object_builder, - members_mapper: members_mapper, - relation_factory: relation_factory, - reader: reader, - importable: importable, - importable_path: importable_path, - importable_attributes: attributes - ) - end - - subject { relation_tree_restorer.restore } - - shared_examples 'import project successfully' do - describe 'imported project' do - it 'has the project attributes and relations', :aggregate_failures do - expect(subject).to eq(true) - - project = Project.find_by_path('project') - - expect(project.description).to eq('Nisi et repellendus ut enim quo accusamus vel magnam.') - expect(project.labels.count).to eq(3) - expect(project.boards.count).to eq(1) - expect(project.project_feature).not_to be_nil - expect(project.custom_attributes.count).to eq(2) - expect(project.project_badges.count).to eq(2) - expect(project.snippets.count).to eq(1) - end - end - end - - shared_examples 'logging of relations creation' do - context 'when log_import_export_relation_creation feature flag is enabled' do - before do - stub_feature_flags(log_import_export_relation_creation: group) - end - - it 'logs top-level relation creation' do - expect(relation_tree_restorer.shared.logger) - .to receive(:info) - .with(hash_including(message: '[Project/Group Import] Created new object relation')) - .at_least(:once) - - subject - end - end - - context 'when log_import_export_relation_creation feature flag is disabled' do - before do - stub_feature_flags(log_import_export_relation_creation: false) - end - - it 'does not log top-level relation creation' do - expect(relation_tree_restorer.shared.logger) - .to receive(:info) - .with(hash_including(message: '[Project/Group Import] Created new object relation')) - .never - - subject - end - end - end - - context 'when restoring a project' do - let_it_be(:importable, reload: true) do - create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') - end - - let(:importable_name) { 'project' } - let(:importable_path) { 'project' } - let(:object_builder) { Gitlab::ImportExport::Project::ObjectBuilder } - let(:relation_factory) { Gitlab::ImportExport::Project::RelationFactory } - let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) } - - context 'using legacy reader' do - let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/project.json' } - let(:relation_reader) do - Gitlab::ImportExport::Json::LegacyReader::File.new( - path, - relation_names: reader.project_relation_names, - allowed_path: 'project' - ) - end - - let(:attributes) { relation_reader.consume_attributes('project') } - - it_behaves_like 'import project successfully' - - context 'logging of relations creation' do - let_it_be(:group) { create(:group) } - let_it_be(:importable) do - create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project', group: group) - end - - include_examples 'logging of relations creation' - end - end - - context 'using ndjson reader' do - let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/tree' } - let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } - - it_behaves_like 'import project successfully' - - context 'when inside a group' do - let_it_be(:group) do - create(:group, :disabled_and_unoverridable) - end - - before do - importable.update!(shared_runners_enabled: false, group: group) - end - - it_behaves_like 'import project successfully' - end - end - - context 'with invalid relations' do - let(:path) { 'spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree' } - let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } - - it 'logs the invalid relation and its errors' do - expect(relation_tree_restorer.shared.logger) - .to receive(:warn) - .with( - error_messages: "Title can't be blank. Title is invalid", - message: '[Project/Group Import] Invalid object relation built', - relation_class: 'ProjectLabel', - relation_index: 0, - relation_key: 'labels' - ).once - - relation_tree_restorer.restore - end - end - end - - context 'when restoring a group' do - let_it_be(:group) { create(:group) } - let_it_be(:importable) { create(:group, parent: group) } - - let(:path) { 'spec/fixtures/lib/gitlab/import_export/group_exports/no_children/group.json' } - let(:importable_name) { nil } - let(:importable_path) { nil } - let(:object_builder) { Gitlab::ImportExport::Group::ObjectBuilder } - let(:relation_factory) { Gitlab::ImportExport::Group::RelationFactory } - let(:relation_reader) do - Gitlab::ImportExport::Json::LegacyReader::File.new( - path, - relation_names: reader.group_relation_names) - end - - let(:reader) do - Gitlab::ImportExport::Reader.new( - shared: shared, - config: Gitlab::ImportExport::Config.new(config: Gitlab::ImportExport.legacy_group_config_file).to_h - ) - end - - it 'restores group tree' do - expect(subject).to eq(true) - end - - include_examples 'logging of relations creation' - 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 4b125cab49b..9daa3b32fd1 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -561,6 +561,7 @@ Project: - require_password_to_approve - autoclose_referenced_issues - suggestion_commit_message +- merge_commit_template ProjectTracingSetting: - external_url Author: @@ -692,6 +693,7 @@ ProjectCiCdSetting: ProjectSetting: - allow_merge_on_skipped_pipeline - has_confluence +- has_shimo - has_vulnerabilities ProtectedEnvironment: - id -- cgit v1.2.3