diff options
Diffstat (limited to 'spec/lib/gitlab/import_export/project/tree_saver_spec.rb')
-rw-r--r-- | spec/lib/gitlab/import_export/project/tree_saver_spec.rb | 469 |
1 files changed, 274 insertions, 195 deletions
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 c910ee5430b..d859af5df02 100644 --- a/spec/lib/gitlab/import_export/project/tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_saver_spec.rb @@ -3,314 +3,397 @@ require 'spec_helper' describe Gitlab::ImportExport::Project::TreeSaver do - describe 'saves the project tree into a json object' do - let(:shared) { project.import_export_shared } - let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared) } - let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } - let(:user) { create(:user) } - let!(:project) { setup_project } - - before do - project.add_maintainer(user) - allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) - allow_any_instance_of(MergeRequest).to receive(:source_branch_sha).and_return('ABCD') - allow_any_instance_of(MergeRequest).to receive(:target_branch_sha).and_return('DCBA') - end + let_it_be(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } + let_it_be(:exportable_path) { 'project' } - after do - FileUtils.rm_rf(export_path) - end + shared_examples 'saves project tree successfully' do |ndjson_enabled| + include ImportExport::CommonUtil - it 'saves project successfully' do - expect(project_tree_saver.save).to be true - end + subject { get_json(full_path, exportable_path, relation_name, ndjson_enabled) } - context 'JSON' do - let(:saved_project_json) do - project_tree_saver.save - project_json(project_tree_saver.full_path) - end + 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_it_be(:project_tree_saver ) { described_class.new(project: project, current_user: user, shared: shared) } - # It is not duplicated in - # `spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb` - context 'with description override' do - let(:params) { { description: 'Foo Bar' } } - let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared, params: params) } + let(:relation_name) { :projects } - it 'overrides the project description' do - expect(saved_project_json).to include({ 'description' => params[:description] }) + let_it_be(:full_path) do + if ndjson_enabled + File.join(shared.export_path, 'tree') + else + File.join(shared.export_path, Gitlab::ImportExport.project_filename) end end - it 'saves the correct json' do - expect(saved_project_json).to include({ 'description' => 'description', 'visibility_level' => 20 }) + before_all do + Feature.enable(:project_export_as_ndjson) if ndjson_enabled + project.add_maintainer(user) + project_tree_saver.save end - it 'has approvals_before_merge set' do - expect(saved_project_json['approvals_before_merge']).to eq(1) + after :all do + FileUtils.rm_rf(export_path) end - it 'has milestones' do - expect(saved_project_json['milestones']).not_to be_empty - end + context 'with project root' do + it { is_expected.to include({ 'description' => 'description', 'visibility_level' => 20 }) } - it 'has merge requests' do - expect(saved_project_json['merge_requests']).not_to be_empty - end + it { is_expected.not_to include("runners_token" => 'token') } - it 'has merge request\'s milestones' do - expect(saved_project_json['merge_requests'].first['milestone']).not_to be_empty + it 'has approvals_before_merge set' do + expect(subject['approvals_before_merge']).to eq(1) + end end - it 'has merge request\'s source branch SHA' do - expect(saved_project_json['merge_requests'].first['source_branch_sha']).to eq('ABCD') - end + context 'with milestones' do + let(:relation_name) { :milestones } - it 'has merge request\'s target branch SHA' do - expect(saved_project_json['merge_requests'].first['target_branch_sha']).to eq('DCBA') + it { is_expected.not_to be_empty } end - it 'has events' do - expect(saved_project_json['merge_requests'].first['milestone']['events']).not_to be_empty - end + context 'with merge_requests' do + let(:relation_name) { :merge_requests } - it 'has snippets' do - expect(saved_project_json['snippets']).not_to be_empty - end + it { is_expected.not_to be_empty } - it 'has snippet notes' do - expect(saved_project_json['snippets'].first['notes']).not_to be_empty - end + it 'has merge request\'s milestones' do + expect(subject.first['milestone']).not_to be_empty + end + it 'has merge request\'s source branch SHA' do + expect(subject.first['source_branch_sha']).to eq('b83d6e391c22777fca1ed3012fce84f633d7fed0') + end - it 'has releases' do - expect(saved_project_json['releases']).not_to be_empty - end + it 'has merge request\'s target branch SHA' do + expect(subject.first['target_branch_sha']).to eq('0b4bc9a49b562e85de7cc9e834518ea6828729b9') + end - it 'has no author on releases' do - expect(saved_project_json['releases'].first['author']).to be_nil - end + it 'has events' do + expect(subject.first['milestone']['events']).not_to be_empty + end - it 'has the author ID on releases' do - expect(saved_project_json['releases'].first['author_id']).not_to be_nil - end + it 'has merge requests diffs' do + expect(subject.first['merge_request_diff']).not_to be_empty + end - it 'has issues' do - expect(saved_project_json['issues']).not_to be_empty - end + it 'has merge request diff files' do + expect(subject.first['merge_request_diff']['merge_request_diff_files']).not_to be_empty + end - it 'has issue comments' do - notes = saved_project_json['issues'].first['notes'] + it 'has merge request diff commits' do + expect(subject.first['merge_request_diff']['merge_request_diff_commits']).not_to be_empty + end - expect(notes).not_to be_empty - expect(notes.first['type']).to eq('DiscussionNote') - end + it 'has merge requests comments' do + expect(subject.first['notes']).not_to be_empty + end - it 'has issue assignees' do - expect(saved_project_json['issues'].first['issue_assignees']).not_to be_empty - end + it 'has author on merge requests comments' do + expect(subject.first['notes'].first['author']).not_to be_empty + end - it 'has author on issue comments' do - expect(saved_project_json['issues'].first['notes'].first['author']).not_to be_empty + it 'has merge request resource label events' do + expect(subject.first['resource_label_events']).not_to be_empty + end end - it 'has system note metadata on issue comments' do - metadata = saved_project_json['issues'].first['notes'].first['system_note_metadata'] + context 'with snippets' do + let(:relation_name) { :snippets } - expect(metadata['action']).to eq('description') - end + it { is_expected.not_to be_empty } - it 'has project members' do - expect(saved_project_json['project_members']).not_to be_empty + it 'has snippet notes' do + expect(subject.first['notes']).not_to be_empty + end end - it 'has merge requests diffs' do - expect(saved_project_json['merge_requests'].first['merge_request_diff']).not_to be_empty - end + context 'with releases' do + let(:relation_name) { :releases } - it 'has merge request diff files' do - expect(saved_project_json['merge_requests'].first['merge_request_diff']['merge_request_diff_files']).not_to be_empty - end + it { is_expected.not_to be_empty } - it 'has merge request diff commits' do - expect(saved_project_json['merge_requests'].first['merge_request_diff']['merge_request_diff_commits']).not_to be_empty - end + it 'has no author on releases' do + expect(subject.first['author']).to be_nil + end - it 'has merge requests comments' do - expect(saved_project_json['merge_requests'].first['notes']).not_to be_empty + it 'has the author ID on releases' do + expect(subject.first['author_id']).not_to be_nil + end end - it 'has author on merge requests comments' do - expect(saved_project_json['merge_requests'].first['notes'].first['author']).not_to be_empty - end + context 'with issues' do + let(:relation_name) { :issues } - it 'has system note metadata on merge requests comments' do - metadata = saved_project_json['merge_requests'].first['notes'].first['system_note_metadata'] + it { is_expected.not_to be_empty } - expect(metadata['commit_count']).to eq(1) - expect(metadata['action']).to eq('commit') - end + it 'has issue comments' do + notes = subject.first['notes'] - it 'has pipeline stages' do - expect(saved_project_json.dig('ci_pipelines', 0, 'stages')).not_to be_empty - end + expect(notes).not_to be_empty + expect(notes.first['type']).to eq('DiscussionNote') + end - it 'has pipeline statuses' do - expect(saved_project_json.dig('ci_pipelines', 0, 'stages', 0, 'statuses')).not_to be_empty - end + it 'has issue assignees' do + expect(subject.first['issue_assignees']).not_to be_empty + end - it 'has pipeline builds' do - builds_count = saved_project_json - .dig('ci_pipelines', 0, 'stages', 0, 'statuses') - .count { |hash| hash['type'] == 'Ci::Build' } + it 'has author on issue comments' do + expect(subject.first['notes'].first['author']).not_to be_empty + end - expect(builds_count).to eq(1) - end + it 'has labels associated to records' do + expect(subject.first['label_links'].first['label']).not_to be_empty + end - it 'has no when YML attributes but only the DB column' do - expect_any_instance_of(Gitlab::Ci::YamlProcessor).not_to receive(:build_attributes) + it 'has project and group labels' do + label_types = subject.first['label_links'].map { |link| link['label']['type'] } - saved_project_json - end + expect(label_types).to match_array(%w(ProjectLabel GroupLabel)) + end - it 'has pipeline commits' do - expect(saved_project_json['ci_pipelines']).not_to be_empty - end + it 'has priorities associated to labels' do + priorities = subject.first['label_links'].flat_map { |link| link['label']['priorities'] } - it 'has ci pipeline notes' do - expect(saved_project_json['ci_pipelines'].first['notes']).not_to be_empty - end + expect(priorities).not_to be_empty + end - it 'has labels with no associations' do - expect(saved_project_json['labels']).not_to be_empty + it 'has issue resource label events' do + expect(subject.first['resource_label_events']).not_to be_empty + end end - it 'has labels associated to records' do - expect(saved_project_json['issues'].first['label_links'].first['label']).not_to be_empty - end + context 'with ci_pipelines' do + let(:relation_name) { :ci_pipelines } + + it { is_expected.not_to be_empty } + + it 'has pipeline stages' do + expect(subject.dig(0, 'stages')).not_to be_empty + end + + it 'has pipeline statuses' do + expect(subject.dig(0, 'stages', 0, 'statuses')).not_to be_empty + end - it 'has project and group labels' do - label_types = saved_project_json['issues'].first['label_links'].map { |link| link['label']['type'] } + it 'has pipeline builds' do + builds_count = subject.dig(0, 'stages', 0, 'statuses') + .count { |hash| hash['type'] == 'Ci::Build' } - expect(label_types).to match_array(%w(ProjectLabel GroupLabel)) + expect(builds_count).to eq(1) + end + + it 'has ci pipeline notes' do + expect(subject.first['notes']).not_to be_empty + end end - it 'has priorities associated to labels' do - priorities = saved_project_json['issues'].first['label_links'].flat_map { |link| link['label']['priorities'] } + context 'with labels' do + let(:relation_name) { :labels } - expect(priorities).not_to be_empty + it { is_expected.not_to be_empty } end - it 'has issue resource label events' do - expect(saved_project_json['issues'].first['resource_label_events']).not_to be_empty + context 'with services' do + let(:relation_name) { :services } + + it 'saves the correct service type' do + expect(subject.first['type']).to eq('CustomIssueTrackerService') + end + + it 'saves the properties for a service' do + expect(subject.first['properties']).to eq('one' => 'value') + end end - it 'has merge request resource label events' do - expect(saved_project_json['merge_requests'].first['resource_label_events']).not_to be_empty + context 'with project_feature' do + let(:relation_name) { :project_feature } + + it { is_expected.not_to be_empty } + + it 'has project feature' do + expect(subject["issues_access_level"]).to eq(ProjectFeature::DISABLED) + expect(subject["wiki_access_level"]).to eq(ProjectFeature::ENABLED) + expect(subject["builds_access_level"]).to eq(ProjectFeature::PRIVATE) + end end - it 'saves the correct service type' do - expect(saved_project_json['services'].first['type']).to eq('CustomIssueTrackerService') + context 'with custom_attributes' do + let(:relation_name) { :custom_attributes } + + it 'has custom attributes' do + expect(subject.count).to eq(2) + end end - it 'saves the properties for a service' do - expect(saved_project_json['services'].first['properties']).to eq('one' => 'value') + context 'with badges' do + let(:relation_name) { :custom_attributes } + + it 'has badges' do + expect(subject.count).to eq(2) + end end - it 'has project feature' do - project_feature = saved_project_json['project_feature'] - expect(project_feature).not_to be_empty - expect(project_feature["issues_access_level"]).to eq(ProjectFeature::DISABLED) - expect(project_feature["wiki_access_level"]).to eq(ProjectFeature::ENABLED) - expect(project_feature["builds_access_level"]).to eq(ProjectFeature::PRIVATE) + context 'with project_members' do + let(:relation_name) { :project_members } + + it { is_expected.not_to be_empty } end - it 'has custom attributes' do - expect(saved_project_json['custom_attributes'].count).to eq(2) + context 'with boards' do + let(:relation_name) { :boards } + + it { is_expected.not_to be_empty } end + end - it 'has badges' do - expect(saved_project_json['project_badges'].count).to eq(2) + describe '#saves project tree' do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let(:project) { setup_project } + let(:full_path) do + if ndjson_enabled + File.join(shared.export_path, 'tree') + else + File.join(shared.export_path, Gitlab::ImportExport.project_filename) + end end + let(:shared) { project.import_export_shared } + let(:params) { {} } - it 'does not complain about non UTF-8 characters in MR diff files' do - ActiveRecord::Base.connection.execute("UPDATE merge_request_diff_files SET diff = '---\n- :diff: !binary |-\n LS0tIC9kZXYvbnVsbAorKysgYi9pbWFnZXMvbnVjb3IucGRmCkBAIC0wLDAg\n KzEsMTY3OSBAQAorJVBERi0xLjUNJeLjz9MNCisxIDAgb2JqDTw8L01ldGFk\n YXR'") + let(:project_tree_saver ) { described_class.new(project: project, current_user: user, shared: shared, params: params) } - expect(project_tree_saver.save).to be true + before do + stub_feature_flags(project_export_as_ndjson: ndjson_enabled) + project.add_maintainer(user) + + FileUtils.rm_rf(export_path) end - context 'group members' do + after do + FileUtils.rm_rf(export_path) + end + + context 'overrides group members' do let(:user2) { create(:user, email: 'group@member.com') } + let(:relation_name) { :project_members } + let(:member_emails) do - saved_project_json['project_members'].map do |pm| + emails = subject.map do |pm| pm['user']['email'] end + emails end before do - Group.first.add_developer(user2) + group.add_developer(user2) end - it 'does not export group members if it has no permission' do - Group.first.add_developer(user) + context 'when has no permission' do + before do + group.add_developer(user) + project_tree_saver.save + end - expect(member_emails).not_to include('group@member.com') + it 'does not export group members' do + expect(member_emails).not_to include('group@member.com') + end end - it 'does not export group members as maintainer' do - Group.first.add_maintainer(user) + context 'when has permission as maintainer' do + before do + group.add_maintainer(user) - expect(member_emails).not_to include('group@member.com') + project_tree_saver.save + end + + it 'does not export group members' do + expect(member_emails).not_to include('group@member.com') + end end - it 'exports group members as group owner' do - Group.first.add_owner(user) + context 'when has permission as group owner' do + before do + group.add_owner(user) - expect(member_emails).to include('group@member.com') + project_tree_saver.save + end + + it 'exports group members as group owner' do + expect(member_emails).to include('group@member.com') + end end context 'as admin' do let(:user) { create(:admin) } + before do + project_tree_saver.save + end + it 'exports group members as admin' do expect(member_emails).to include('group@member.com') end it 'exports group members as project members' do - member_types = saved_project_json['project_members'].map { |pm| pm['source_type'] } + member_types = subject.map { |pm| pm['source_type'] } expect(member_types).to all(eq('Project')) end end end - context 'project attributes' do - it 'does not contain the runners token' do - expect(saved_project_json).not_to include("runners_token" => 'token') + context 'with description override' do + let(:params) { { description: 'Foo Bar' } } + let(:relation_name) { :projects } + + before do + project_tree_saver.save end + + it { is_expected.to include({ 'description' => params[:description] }) } + end + + it 'saves project successfully' do + expect(project_tree_saver.save).to be true + end + + it 'does not complain about non UTF-8 characters in MR diff files' do + ActiveRecord::Base.connection.execute("UPDATE merge_request_diff_files SET diff = '---\n- :diff: !binary |-\n LS0tIC9kZXYvbnVsbAorKysgYi9pbWFnZXMvbnVjb3IucGRmCkBAIC0wLDAg\n KzEsMTY3OSBAQAorJVBERi0xLjUNJeLjz9MNCisxIDAgb2JqDTw8L01ldGFk\n YXR'") + + expect(project_tree_saver.save).to be true end - it 'has a board and a list' do - expect(saved_project_json['boards'].first['lists']).not_to be_empty + it 'has no when YML attributes but only the DB column' do + expect_any_instance_of(Gitlab::Ci::YamlProcessor).not_to receive(:build_attributes) + + project_tree_saver.save end end end + context 'with JSON' do + it_behaves_like "saves project tree successfully", false + end + + context 'with NDJSON' do + it_behaves_like "saves project tree successfully", true + end + def setup_project release = create(:release) - group = create(:group) project = create(:project, - :public, - :repository, - :issues_disabled, - :wiki_enabled, - :builds_private, - description: 'description', - releases: [release], - group: group, - approvals_before_merge: 1 - ) - allow(project).to receive(:commit).and_return(Commit.new(RepoHelpers.sample_commit, project)) + :public, + :repository, + :issues_disabled, + :wiki_enabled, + :builds_private, + description: 'description', + releases: [release], + group: group, + approvals_before_merge: 1) issue = create(:issue, assignees: [user], project: project) snippet = create(:project_snippet, project: project) @@ -331,9 +414,9 @@ describe Gitlab::ImportExport::Project::TreeSaver do mr_note = create(:note, noteable: merge_request, project: project) create(:note, noteable: snippet, project: project) create(:note_on_commit, - author: user, - project: project, - commit_id: ci_build.pipeline.sha) + author: user, + project: project, + commit_id: ci_build.pipeline.sha) create(:system_note_metadata, action: 'description', note: discussion_note) create(:system_note_metadata, commit_count: 1, action: 'commit', note: mr_note) @@ -355,8 +438,4 @@ describe Gitlab::ImportExport::Project::TreeSaver do project end - - def project_json(filename) - ::JSON.parse(IO.read(filename)) - end end |