diff options
author | Clement Ho <ClemMakesApps@gmail.com> | 2018-01-08 13:03:48 -0600 |
---|---|---|
committer | Clement Ho <ClemMakesApps@gmail.com> | 2018-01-08 13:03:48 -0600 |
commit | cecea7529fb37893af6bf514a53e239a3be1eea6 (patch) | |
tree | 2781b7d777b9cf6fb76ba238b2fdce0e01a08618 /spec/lib | |
parent | 691cf4409145d80c09e43a10fd252d88075dc3e6 (diff) | |
parent | 1d7b46062feb1d93dd3efaf6ba4d5d934068342c (diff) | |
download | gitlab-ce-cecea7529fb37893af6bf514a53e239a3be1eea6.tar.gz |
Merge branch 'master' into fix-no-template
Diffstat (limited to 'spec/lib')
80 files changed, 2649 insertions, 909 deletions
diff --git a/spec/lib/banzai/filter/mermaid_filter_spec.rb b/spec/lib/banzai/filter/mermaid_filter_spec.rb index 532d25e121d..f6474c8936d 100644 --- a/spec/lib/banzai/filter/mermaid_filter_spec.rb +++ b/spec/lib/banzai/filter/mermaid_filter_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' describe Banzai::Filter::MermaidFilter do include FilterSpecHelper - it 'adds `js-render-mermaid` class to the `pre` tag' do + it 'adds `js-render-mermaid` class to the `code` tag' do doc = filter("<pre class='code highlight js-syntax-highlight mermaid' lang='mermaid' v-pre='true'><code>graph TD;\n A-->B;\n</code></pre>") - result = doc.xpath('descendant-or-self::pre').first + result = doc.css('code').first expect(result[:class]).to include('js-render-mermaid') end diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb index 68643effb66..5a7858e77f3 100644 --- a/spec/lib/banzai/filter/redactor_filter_spec.rb +++ b/spec/lib/banzai/filter/redactor_filter_spec.rb @@ -46,7 +46,7 @@ describe Banzai::Filter::RedactorFilter do it 'allows permitted Project references' do user = create(:user) project = create(:project) - project.team << [user, :master] + project.add_master(user) link = reference_link(project: project.id, reference_type: 'test') doc = filter(link, current_user: user) @@ -94,7 +94,7 @@ describe Banzai::Filter::RedactorFilter do it 'removes references for project members with guest role' do member = create(:user) project = create(:project, :public) - project.team << [member, :guest] + project.add_guest(member) issue = create(:issue, :confidential, project: project) link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue') @@ -128,7 +128,7 @@ describe Banzai::Filter::RedactorFilter do it 'allows references for project members' do member = create(:user) project = create(:project, :public) - project.team << [member, :developer] + project.add_developer(member) issue = create(:issue, :confidential, project: project) link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue') diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb index 08beede62db..f38f0776303 100644 --- a/spec/lib/banzai/filter/relative_link_filter_spec.rb +++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb @@ -5,6 +5,7 @@ describe Banzai::Filter::RelativeLinkFilter do contexts.reverse_merge!({ commit: commit, project: project, + group: group, project_wiki: project_wiki, ref: ref, requested_path: requested_path @@ -25,7 +26,12 @@ describe Banzai::Filter::RelativeLinkFilter do %(<a href="#{path}">#{path}</a>) end + def nested(element) + %(<div>#{element}</div>) + end + let(:project) { create(:project, :repository) } + let(:group) { nil } let(:project_path) { project.full_path } let(:ref) { 'markdown' } let(:commit) { project.commit(ref) } @@ -70,6 +76,11 @@ describe Banzai::Filter::RelativeLinkFilter do expect { filter(act) }.not_to raise_error end + it 'does not raise an exception with a garbled path' do + act = link("open(/var/tmp/):%20/location%0Afrom:%20/test") + expect { filter(act) }.not_to raise_error + end + it 'ignores ref if commit is passed' do doc = filter(link('non/existent.file'), commit: project.commit('empty-branch') ) expect(doc.at_css('a')['href']) @@ -223,4 +234,79 @@ describe Banzai::Filter::RelativeLinkFilter do let(:commit) { nil } # force filter to use ref instead of commit include_examples :valid_repository end + + context 'with a /upload/ URL' do + # not needed + let(:commit) { nil } + let(:ref) { nil } + let(:requested_path) { nil } + + context 'to a project upload' do + it 'rebuilds relative URL for a link' do + doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.at_css('a')['href']) + .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + + doc = filter(nested(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))) + expect(doc.at_css('a')['href']) + .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + end + + it 'rebuilds relative URL for an image' do + doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.at_css('img')['src']) + .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + + doc = filter(nested(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))) + expect(doc.at_css('img')['src']) + .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + end + + it 'does not modify absolute URL' do + doc = filter(link('http://example.com')) + expect(doc.at_css('a')['href']).to eq 'http://example.com' + end + + it 'supports Unicode filenames' do + path = '/uploads/한글.png' + escaped = Addressable::URI.escape(path) + + # Stub these methods so the file doesn't actually need to be in the repo + allow_any_instance_of(described_class) + .to receive(:file_exists?).and_return(true) + allow_any_instance_of(described_class) + .to receive(:image?).with(path).and_return(true) + + doc = filter(image(escaped)) + expect(doc.at_css('img')['src']).to match "/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png" + end + end + + context 'to a group upload' do + let(:upload_link) { link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg') } + let(:group) { create(:group) } + let(:project) { nil } + let(:relative_path) { "/groups/#{group.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" } + + it 'rewrites the link correctly' do + doc = filter(upload_link) + + expect(doc.at_css('a')['href']).to eq(relative_path) + end + + it 'rewrites the link correctly for subgroup' do + group.update!(parent: create(:group)) + + doc = filter(upload_link) + + expect(doc.at_css('a')['href']).to eq(relative_path) + end + + it 'does not modify absolute URL' do + doc = filter(link('http://example.com')) + + expect(doc.at_css('a')['href']).to eq 'http://example.com' + end + end + end end diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb deleted file mode 100644 index 76bc0c36ab7..00000000000 --- a/spec/lib/banzai/filter/upload_link_filter_spec.rb +++ /dev/null @@ -1,133 +0,0 @@ -require 'spec_helper' - -describe Banzai::Filter::UploadLinkFilter do - def filter(doc, contexts = {}) - contexts.reverse_merge!({ - project: project - }) - - raw_filter(doc, contexts) - end - - def raw_filter(doc, contexts = {}) - described_class.call(doc, contexts) - end - - def image(path) - %(<img src="#{path}" />) - end - - def link(path) - %(<a href="#{path}">#{path}</a>) - end - - def nested_image(path) - %(<div><img src="#{path}" /></div>) - end - - def nested_link(path) - %(<div><a href="#{path}">#{path}</a></div>) - end - - let(:project) { create(:project) } - - shared_examples :preserve_unchanged do - it 'does not modify any relative URL in anchor' do - doc = filter(link('README.md')) - expect(doc.at_css('a')['href']).to eq 'README.md' - end - - it 'does not modify any relative URL in image' do - doc = filter(image('files/images/logo-black.png')) - expect(doc.at_css('img')['src']).to eq 'files/images/logo-black.png' - end - end - - it 'does not raise an exception on invalid URIs' do - act = link("://foo") - expect { filter(act) }.not_to raise_error - end - - context 'with a valid repository' do - it 'rebuilds relative URL for a link' do - doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) - expect(doc.at_css('a')['href']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" - - doc = filter(nested_link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) - expect(doc.at_css('a')['href']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" - end - - it 'rebuilds relative URL for an image' do - doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) - expect(doc.at_css('img')['src']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" - - doc = filter(nested_image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) - expect(doc.at_css('img')['src']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" - end - - it 'does not modify absolute URL' do - doc = filter(link('http://example.com')) - expect(doc.at_css('a')['href']).to eq 'http://example.com' - end - - it 'supports Unicode filenames' do - path = '/uploads/한글.png' - escaped = Addressable::URI.escape(path) - - # Stub these methods so the file doesn't actually need to be in the repo - allow_any_instance_of(described_class) - .to receive(:file_exists?).and_return(true) - allow_any_instance_of(described_class) - .to receive(:image?).with(path).and_return(true) - - doc = filter(image(escaped)) - expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png" - end - end - - context 'in group context' do - let(:upload_link) { link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg') } - let(:group) { create(:group) } - let(:filter_context) { { project: nil, group: group } } - let(:relative_path) { "groups/#{group.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" } - - it 'rewrites the link correctly' do - doc = raw_filter(upload_link, filter_context) - - expect(doc.at_css('a')['href']).to eq("#{Gitlab.config.gitlab.url}/#{relative_path}") - end - - it 'rewrites the link correctly for subgroup' do - subgroup = create(:group, parent: group) - relative_path = "groups/#{subgroup.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" - - doc = raw_filter(upload_link, { project: nil, group: subgroup }) - - expect(doc.at_css('a')['href']).to eq("#{Gitlab.config.gitlab.url}/#{relative_path}") - end - - it 'does not modify absolute URL' do - doc = filter(link('http://example.com'), filter_context) - - expect(doc.at_css('a')['href']).to eq 'http://example.com' - end - end - - context 'when project or group context does not exist' do - let(:upload_link) { link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg') } - - it 'does not raise error' do - expect { raw_filter(upload_link, project: nil) }.not_to raise_error - end - - it 'does not rewrite link' do - doc = raw_filter(upload_link, project: nil) - - expect(doc.to_html).to eq upload_link - end - end -end diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb index fc03741976e..c76adc262fc 100644 --- a/spec/lib/banzai/filter/user_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb @@ -34,11 +34,11 @@ describe Banzai::Filter::UserReferenceFilter do let(:reference) { User.reference_prefix + 'all' } before do - project.team << [project.creator, :developer] + project.add_developer(project.creator) end it 'supports a special @all mention' do - project.team << [user, :developer] + project.add_developer(user) doc = reference_filter("Hey #{reference}", author: user) expect(doc.css('a').length).to eq 1 @@ -47,7 +47,7 @@ describe Banzai::Filter::UserReferenceFilter do end it 'includes a data-author attribute when there is an author' do - project.team << [user, :developer] + project.add_developer(user) doc = reference_filter(reference, author: user) expect(doc.css('a').first.attr('data-author')).to eq(user.id.to_s) diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb index e49726aca6c..b079a3be029 100644 --- a/spec/lib/banzai/reference_parser/user_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb @@ -63,8 +63,8 @@ describe Banzai::ReferenceParser::UserParser do let(:contributor) { create(:user) } before do - project.team << [user, :developer] - project.team << [contributor, :developer] + project.add_developer(user) + project.add_developer(contributor) end it 'returns the members of a project' do @@ -162,7 +162,7 @@ describe Banzai::ReferenceParser::UserParser do context 'when the link has a data-author attribute' do it 'returns the nodes when the user is a member of the project' do other_project = create(:project) - other_project.team << [user, :developer] + other_project.add_developer(user) link['data-project'] = other_project.id.to_s link['data-author'] = user.id.to_s diff --git a/spec/lib/gitlab/action_rate_limiter_spec.rb b/spec/lib/gitlab/action_rate_limiter_spec.rb new file mode 100644 index 00000000000..542fc03e555 --- /dev/null +++ b/spec/lib/gitlab/action_rate_limiter_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe Gitlab::ActionRateLimiter do + let(:redis) { double('redis') } + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:key) { [user, project] } + let(:cache_key) { "action_rate_limiter:test_action:user:#{user.id}:project:#{project.id}" } + + subject { described_class.new(action: :test_action, expiry_time: 100) } + + before do + allow(Gitlab::Redis::Cache).to receive(:with).and_yield(redis) + end + + it 'increases the throttle count and sets the expire time' do + expect(redis).to receive(:incr).with(cache_key).and_return(1) + expect(redis).to receive(:expire).with(cache_key, 100) + + expect(subject.throttled?(key, 1)).to be false + end + + it 'returns true if the key is throttled' do + expect(redis).to receive(:incr).with(cache_key).and_return(2) + expect(redis).not_to receive(:expire) + + expect(subject.throttled?(key, 1)).to be true + end +end diff --git a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb index 79d2c071446..e1c4f9cfea7 100644 --- a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb +++ b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb @@ -2,19 +2,20 @@ require 'spec_helper' describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migration, schema: 20170929131201 do let(:migration) { described_class.new } + let(:projects) { table(:projects) } - let(:base1) { create(:project) } - let(:base1_fork1) { create(:project) } - let(:base1_fork2) { create(:project) } + let(:base1) { projects.create } + let(:base1_fork1) { projects.create } + let(:base1_fork2) { projects.create } - let(:base2) { create(:project) } - let(:base2_fork1) { create(:project) } - let(:base2_fork2) { create(:project) } + let(:base2) { projects.create } + let(:base2_fork1) { projects.create } + let(:base2_fork2) { projects.create } - let(:fork_of_fork) { create(:project) } - let(:fork_of_fork2) { create(:project) } - let(:second_level_fork) { create(:project) } - let(:third_level_fork) { create(:project) } + let(:fork_of_fork) { projects.create } + let(:fork_of_fork2) { projects.create } + let(:second_level_fork) { projects.create } + let(:third_level_fork) { projects.create } let(:fork_network1) { fork_networks.find_by(root_project_id: base1.id) } let(:fork_network2) { fork_networks.find_by(root_project_id: base2.id) } @@ -97,7 +98,7 @@ describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migrat end it 'does not miss members for forks of forks for which the root was deleted' do - forked_project_links.create(id: 9, forked_from_project_id: base1_fork1.id, forked_to_project_id: create(:project).id) + forked_project_links.create(id: 9, forked_from_project_id: base1_fork1.id, forked_to_project_id: projects.create.id) base1.destroy expect(migration.missing_members?(7, 10)).to be_falsy @@ -105,8 +106,8 @@ describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migrat context 'with more forks' do before do - forked_project_links.create(id: 9, forked_from_project_id: fork_of_fork.id, forked_to_project_id: create(:project).id) - forked_project_links.create(id: 10, forked_from_project_id: fork_of_fork.id, forked_to_project_id: create(:project).id) + forked_project_links.create(id: 9, forked_from_project_id: fork_of_fork.id, forked_to_project_id: projects.create.id) + forked_project_links.create(id: 10, forked_from_project_id: fork_of_fork.id, forked_to_project_id: projects.create.id) end it 'only processes a single batch of links at a time' do diff --git a/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb b/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb index 5c471cbdeda..9bae7e53b71 100644 --- a/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb +++ b/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb @@ -24,17 +24,12 @@ describe Gitlab::BackgroundMigration::DeleteConflictingRedirectRoutesRange, :mig redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo5') end - it 'deletes the conflicting redirect_routes in the range' do + # No-op. See https://gitlab.com/gitlab-com/infrastructure/issues/3460#note_53223252 + it 'NO-OP: does not delete any redirect_routes' do expect(redirect_routes.count).to eq(8) - expect do - described_class.new.perform(1, 3) - end.to change { redirect_routes.where("path like 'foo%'").count }.from(5).to(2) + described_class.new.perform(1, 5) - expect do - described_class.new.perform(4, 5) - end.to change { redirect_routes.where("path like 'foo%'").count }.from(2).to(0) - - expect(redirect_routes.count).to eq(3) + expect(redirect_routes.count).to eq(8) end end diff --git a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb index cb52d971047..5432d270555 100644 --- a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb +++ b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb @@ -225,9 +225,10 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migrati let(:user_class) { table(:users) } let(:author) { build(:user).becomes(user_class).tap(&:save!).becomes(User) } let(:namespace) { create(:namespace, owner: author) } - let(:project) { create(:project_empty_repo, namespace: namespace, creator: author) } + let(:projects) { table(:projects) } + let(:project) { projects.create(namespace_id: namespace.id, creator_id: author.id) } - # We can not rely on FactoryGirl as the state of Event may change in ways that + # We can not rely on FactoryBot as the state of Event may change in ways that # the background migration does not expect, hence we use the Event class of # the migration itself. def create_push_event(project, author, data = nil) @@ -280,6 +281,17 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migrati migration.process_event(event) end + + it 'handles an error gracefully' do + event1 = create_push_event(project, author, { commits: [] }) + + expect(migration).to receive(:replicate_event).and_call_original + expect(migration).to receive(:create_push_event_payload).and_raise(ActiveRecord::InvalidForeignKey, 'invalid foreign key') + + migration.process_event(event1) + + expect(described_class::EventForMigration.all.count).to eq(0) + end end describe '#replicate_event' do @@ -334,9 +346,8 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migrati it 'does not create push event payloads for removed events' do allow(event).to receive(:id).and_return(-1) - payload = migration.create_push_event_payload(event) + expect { migration.create_push_event_payload(event) }.to raise_error(ActiveRecord::InvalidForeignKey) - expect(payload).to be_nil expect(PushEventPayload.count).to eq(0) end diff --git a/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb b/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb index e52baf8dde7..8582af96199 100644 --- a/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb +++ b/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb @@ -2,10 +2,11 @@ require 'spec_helper' describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, schema: 20170929131201 do let(:migration) { described_class.new } - let(:base1) { create(:project) } + let(:projects) { table(:projects) } + let(:base1) { projects.create } - let(:base2) { create(:project) } - let(:base2_fork1) { create(:project) } + let(:base2) { projects.create } + let(:base2_fork1) { projects.create } let!(:forked_project_links) { table(:forked_project_links) } let!(:fork_networks) { table(:fork_networks) } @@ -18,10 +19,10 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch # A normal fork link forked_project_links.create(id: 1, forked_from_project_id: base1.id, - forked_to_project_id: create(:project).id) + forked_to_project_id: projects.create.id) forked_project_links.create(id: 2, forked_from_project_id: base1.id, - forked_to_project_id: create(:project).id) + forked_to_project_id: projects.create.id) forked_project_links.create(id: 3, forked_from_project_id: base2.id, forked_to_project_id: base2_fork1.id) @@ -29,10 +30,10 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch # create a fork of a fork forked_project_links.create(id: 4, forked_from_project_id: base2_fork1.id, - forked_to_project_id: create(:project).id) + forked_to_project_id: projects.create.id) forked_project_links.create(id: 5, - forked_from_project_id: create(:project).id, - forked_to_project_id: create(:project).id) + forked_from_project_id: projects.create.id, + forked_to_project_id: projects.create.id) # Stub out the calls to the other migrations allow(BackgroundMigrationWorker).to receive(:perform_in) @@ -63,7 +64,7 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch end it 'creates a fork network for the fork of which the source was deleted' do - fork = create(:project) + fork = projects.create forked_project_links.create(id: 6, forked_from_project_id: 99999, forked_to_project_id: fork.id) migration.perform(5, 8) diff --git a/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb b/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb new file mode 100644 index 00000000000..dfe3b31f1c0 --- /dev/null +++ b/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb @@ -0,0 +1,124 @@ +require 'rails_helper' + +describe Gitlab::BackgroundMigration::PopulateMergeRequestMetricsWithEventsData, :migration, schema: 20171128214150 do + describe '#perform' do + let(:mr_with_event) { create(:merge_request) } + let!(:merged_event) { create(:event, :merged, target: mr_with_event) } + let!(:closed_event) { create(:event, :closed, target: mr_with_event) } + + before do + # Make sure no metrics are created and kept through after_* callbacks. + mr_with_event.metrics.destroy! + end + + it 'inserts metrics and updates closed and merged events' do + subject.perform(mr_with_event.id, mr_with_event.id) + + mr_with_event.reload + + expect(mr_with_event.metrics).to have_attributes(latest_closed_by_id: closed_event.author_id, + merged_by_id: merged_event.author_id) + expect(mr_with_event.metrics.latest_closed_at.to_s).to eq(closed_event.updated_at.to_s) + end + end + + describe '#insert_metrics_for_range' do + let!(:mrs_without_metrics) { create_list(:merge_request, 3) } + let!(:mrs_with_metrics) { create_list(:merge_request, 2) } + + before do + # Make sure no metrics are created and kept through after_* callbacks. + mrs_without_metrics.each { |m| m.metrics.destroy! } + end + + it 'inserts merge_request_metrics for merge_requests without one' do + expect { subject.insert_metrics_for_range(MergeRequest.first.id, MergeRequest.last.id) } + .to change(MergeRequest::Metrics, :count).from(2).to(5) + + mrs_without_metrics.each do |mr_without_metrics| + expect(mr_without_metrics.reload.metrics).to be_present + end + end + + it 'does not inserts merge_request_metrics for MRs out of given range' do + expect { subject.insert_metrics_for_range(mrs_with_metrics.first.id, mrs_with_metrics.last.id) } + .not_to change(MergeRequest::Metrics, :count).from(2) + end + end + + describe '#update_metrics_with_events_data' do + context 'closed events data update' do + let(:users) { create_list(:user, 3) } + let(:mrs_with_event) { create_list(:merge_request, 3) } + + before do + create_list(:event, 2, :closed, author: users.first, target: mrs_with_event.first) + create_list(:event, 3, :closed, author: users.second, target: mrs_with_event.second) + create(:event, :closed, author: users.third, target: mrs_with_event.third) + end + + it 'migrates multiple MR metrics with closed event data' do + mr_without_event = create(:merge_request) + create(:event, :merged) + + subject.update_metrics_with_events_data(mrs_with_event.first.id, mrs_with_event.last.id) + + mrs_with_event.each do |mr_with_event| + latest_event = Event.where(action: 3, target: mr_with_event).last + + mr_with_event.metrics.reload + + expect(mr_with_event.metrics.latest_closed_by).to eq(latest_event.author) + expect(mr_with_event.metrics.latest_closed_at.to_s).to eq(latest_event.updated_at.to_s) + end + + expect(mr_without_event.metrics.reload).to have_attributes(latest_closed_by_id: nil, + latest_closed_at: nil) + end + + it 'does not updates metrics out of given range' do + out_of_range_mr = create(:merge_request) + create(:event, :closed, author: users.last, target: out_of_range_mr) + + expect { subject.perform(mrs_with_event.first.id, mrs_with_event.second.id) } + .not_to change { out_of_range_mr.metrics.reload.merged_by } + .from(nil) + end + end + + context 'merged events data update' do + let(:users) { create_list(:user, 3) } + let(:mrs_with_event) { create_list(:merge_request, 3) } + + before do + create_list(:event, 2, :merged, author: users.first, target: mrs_with_event.first) + create_list(:event, 3, :merged, author: users.second, target: mrs_with_event.second) + create(:event, :merged, author: users.third, target: mrs_with_event.third) + end + + it 'migrates multiple MR metrics with merged event data' do + mr_without_event = create(:merge_request) + create(:event, :merged) + + subject.update_metrics_with_events_data(mrs_with_event.first.id, mrs_with_event.last.id) + + mrs_with_event.each do |mr_with_event| + latest_event = Event.where(action: Event::MERGED, target: mr_with_event).last + + expect(mr_with_event.metrics.reload.merged_by).to eq(latest_event.author) + end + + expect(mr_without_event.metrics.reload).to have_attributes(merged_by_id: nil) + end + + it 'does not updates metrics out of given range' do + out_of_range_mr = create(:merge_request) + create(:event, :merged, author: users.last, target: out_of_range_mr) + + expect { subject.perform(mrs_with_event.first.id, mrs_with_event.second.id) } + .not_to change { out_of_range_mr.metrics.reload.merged_by } + .from(nil) + end + end + end +end diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb index b80df6956b0..be45c00dfe6 100644 --- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb +++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb @@ -182,13 +182,22 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do end context 'for a pre-Markdown Note attachment file path' do - class Note < ActiveRecord::Base - has_many :uploads, as: :model, dependent: :destroy + let(:model) { create(:note, :with_attachment) } + let!(:expected_upload_attrs) { Upload.where(model_type: 'Note', model_id: model.id).first.attributes.slice('path', 'uploader', 'size', 'checksum') } + let!(:untracked_file) { untracked_files_for_uploads.create!(path: expected_upload_attrs['path']) } + + before do + Upload.where(model_type: 'Note', model_id: model.id).delete_all end - let(:model) { create(:note, :with_attachment) } + # Can't use the shared example because Note doesn't have an `uploads` association + it 'creates an Upload record' do + expect do + subject.perform(1, untracked_files_for_uploads.last.id) + end.to change { Upload.where(model_type: 'Note', model_id: model.id).count }.from(0).to(1) - it_behaves_like 'non_markdown_file' + expect(Upload.where(model_type: 'Note', model_id: model.id).first.attributes).to include(expected_upload_attrs) + end end context 'for a user avatar file path' do diff --git a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb index cd3f1a45270..8bb9ebe0419 100644 --- a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb +++ b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb @@ -2,21 +2,10 @@ require 'spec_helper' describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq do include TrackUntrackedUploadsHelpers + include MigrationsHelpers let!(:untracked_files_for_uploads) { described_class::UntrackedFile } - matcher :be_scheduled_migration do |*expected| - match do |migration| - BackgroundMigrationWorker.jobs.any? do |job| - job['args'] == [migration, expected] - end - end - - failure_message do |migration| - "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!" - end - end - before do DatabaseCleaner.clean diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb index 8a83e446935..b5d86df09d2 100644 --- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb +++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb @@ -68,8 +68,14 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do expect(Project.find_by_full_path(project_path)).not_to be_nil end + it 'does not schedule an import' do + expect_any_instance_of(Project).not_to receive(:import_schedule) + + importer.create_project_if_needed + end + it 'creates the Git repo in disk' do - FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.git")) + create_bare_repository("#{project_path}.git") importer.create_project_if_needed @@ -124,13 +130,14 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do end it 'creates the Git repo in disk' do - FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.git")) + create_bare_repository("#{project_path}.git") importer.create_project_if_needed project = Project.find_by_full_path("#{admin.full_path}/#{project_path}") expect(File).to exist(File.join(project.repository_storage_path, project.disk_path + '.git')) + expect(File).to exist(File.join(project.repository_storage_path, project.disk_path + '.wiki.git')) end it 'moves an existing project to the correct path' do @@ -158,8 +165,11 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do it_behaves_like 'importing a repository' it 'creates the Wiki git repo in disk' do - FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.git")) - FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.wiki.git")) + create_bare_repository("#{project_path}.git") + create_bare_repository("#{project_path}.wiki.git") + + expect(Projects::CreateService).to receive(:new).with(admin, hash_including(skip_wiki: true, + import_type: 'bare_repository')).and_call_original importer.create_project_if_needed @@ -182,4 +192,9 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do end end end + + def create_bare_repository(project_path) + repo_path = File.join(base_dir, project_path) + Gitlab::Git::Repository.create(repo_path, bare: true) + end end diff --git a/spec/lib/gitlab/bare_repository_import/repository_spec.rb b/spec/lib/gitlab/bare_repository_import/repository_spec.rb index 61b73abcba4..9f42cf1dfca 100644 --- a/spec/lib/gitlab/bare_repository_import/repository_spec.rb +++ b/spec/lib/gitlab/bare_repository_import/repository_spec.rb @@ -1,58 +1,122 @@ require 'spec_helper' describe ::Gitlab::BareRepositoryImport::Repository do - let(:project_repo_path) { described_class.new('/full/path/', '/full/path/to/repo.git') } + context 'legacy storage' do + subject { described_class.new('/full/path/', '/full/path/to/repo.git') } - it 'stores the repo path' do - expect(project_repo_path.repo_path).to eq('/full/path/to/repo.git') - end + it 'stores the repo path' do + expect(subject.repo_path).to eq('/full/path/to/repo.git') + end - it 'stores the group path' do - expect(project_repo_path.group_path).to eq('to') - end + it 'stores the group path' do + expect(subject.group_path).to eq('to') + end - it 'stores the project name' do - expect(project_repo_path.project_name).to eq('repo') - end + it 'stores the project name' do + expect(subject.project_name).to eq('repo') + end - it 'stores the wiki path' do - expect(project_repo_path.wiki_path).to eq('/full/path/to/repo.wiki.git') - end + it 'stores the wiki path' do + expect(subject.wiki_path).to eq('/full/path/to/repo.wiki.git') + end + + describe '#processable?' do + it 'returns false if it is a wiki' do + subject = described_class.new('/full/path/', '/full/path/to/a/b/my.wiki.git') + + expect(subject).not_to be_processable + end + + it 'returns true if group path is missing' do + subject = described_class.new('/full/path/', '/full/path/repo.git') - describe '#wiki?' do - it 'returns true if it is a wiki' do - wiki_path = described_class.new('/full/path/', '/full/path/to/a/b/my.wiki.git') + expect(subject).to be_processable + end - expect(wiki_path.wiki?).to eq(true) + it 'returns true when group path and project name are present' do + expect(subject).to be_processable + end end - it 'returns false if it is not a wiki' do - expect(project_repo_path.wiki?).to eq(false) + describe '#project_full_path' do + it 'returns the project full path with trailing slash in the root path' do + expect(subject.project_full_path).to eq('to/repo') + end + + it 'returns the project full path with no trailing slash in the root path' do + subject = described_class.new('/full/path', '/full/path/to/repo.git') + + expect(subject.project_full_path).to eq('to/repo') + end end end - describe '#hashed?' do - it 'returns true if it is a hashed folder' do - path = described_class.new('/full/path/', '/full/path/@hashed/my.repo.git') + context 'hashed storage' do + let(:gitlab_shell) { Gitlab::Shell.new } + let(:repository_storage) { 'default' } + let(:root_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } + let(:hash) { '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' } + let(:hashed_path) { "@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" } + let(:repo_path) { File.join(root_path, "#{hashed_path}.git") } + let(:wiki_path) { File.join(root_path, "#{hashed_path}.wiki.git") } - expect(path.hashed?).to eq(true) + before do + gitlab_shell.add_repository(repository_storage, hashed_path) + repository = Rugged::Repository.new(repo_path) + repository.config['gitlab.fullpath'] = 'to/repo' end - it 'returns false if it is not a hashed folder' do - expect(project_repo_path.hashed?).to eq(false) + after do + gitlab_shell.remove_repository(root_path, hashed_path) end - end - describe '#project_full_path' do - it 'returns the project full path' do - expect(project_repo_path.repo_path).to eq('/full/path/to/repo.git') - expect(project_repo_path.project_full_path).to eq('to/repo') + subject { described_class.new(root_path, repo_path) } + + it 'stores the repo path' do + expect(subject.repo_path).to eq(repo_path) + end + + it 'stores the wiki path' do + expect(subject.wiki_path).to eq(wiki_path) + end + + it 'reads the group path from .git/config' do + expect(subject.group_path).to eq('to') + end + + it 'reads the project name from .git/config' do + expect(subject.project_name).to eq('repo') end - it 'with no trailing slash in the root path' do - repo_path = described_class.new('/full/path', '/full/path/to/repo.git') + describe '#processable?' do + it 'returns false if it is a wiki' do + subject = described_class.new(root_path, wiki_path) + + expect(subject).not_to be_processable + end + + it 'returns false when group and project name are missing' do + repository = Rugged::Repository.new(repo_path) + repository.config.delete('gitlab.fullpath') + + expect(subject).not_to be_processable + end + + it 'returns true when group path and project name are present' do + expect(subject).to be_processable + end + end + + describe '#project_full_path' do + it 'returns the project full path with trailing slash in the root path' do + expect(subject.project_full_path).to eq('to/repo') + end + + it 'returns the project full path with no trailing slash in the root path' do + subject = described_class.new(root_path[0...-1], repo_path) - expect(repo_path.project_full_path).to eq('to/repo') + expect(subject.project_full_path).to eq('to/repo') + end end end end diff --git a/spec/lib/gitlab/checks/project_moved_spec.rb b/spec/lib/gitlab/checks/project_moved_spec.rb index fa1575e2177..f90c2d6aded 100644 --- a/spec/lib/gitlab/checks/project_moved_spec.rb +++ b/spec/lib/gitlab/checks/project_moved_spec.rb @@ -35,6 +35,12 @@ describe Gitlab::Checks::ProjectMoved, :clean_gitlab_redis_shared_state do project_moved = described_class.new(project, user, 'foo/bar', 'http') expect(project_moved.add_redirect_message).to eq("OK") end + + it 'should handle anonymous clones' do + project_moved = described_class.new(project, nil, 'foo/bar', 'http') + + expect(project_moved.add_redirect_message).to eq(nil) + end end describe '#redirect_message' do diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb index 33540eab5d6..05e2d94cbd6 100644 --- a/spec/lib/gitlab/ci/ansi2html_spec.rb +++ b/spec/lib/gitlab/ci/ansi2html_spec.rb @@ -120,6 +120,10 @@ describe Gitlab::Ci::Ansi2html do expect(convert_html("\e[48;5;240mHello")).to eq('<span class="xterm-bg-240">Hello</span>') end + it "can print 256 xterm fg bold colors" do + expect(convert_html("\e[38;5;16;1mHello")).to eq('<span class="xterm-fg-16 term-bold">Hello</span>') + end + it "can print 256 xterm bg colors on normal magenta foreground" do expect(convert_html("\e[48;5;16;35mHello")).to eq('<span class="term-fg-magenta xterm-bg-16">Hello</span>') end diff --git a/spec/lib/gitlab/ci/status/build/common_spec.rb b/spec/lib/gitlab/ci/status/build/common_spec.rb index 03d1f46b517..2cce7a23ea7 100644 --- a/spec/lib/gitlab/ci/status/build/common_spec.rb +++ b/spec/lib/gitlab/ci/status/build/common_spec.rb @@ -18,7 +18,7 @@ describe Gitlab::Ci::Status::Build::Common do describe '#has_details?' do context 'when user has access to read build' do before do - project.team << [user, :developer] + project.add_developer(user) end it { is_expected.to have_details } diff --git a/spec/lib/gitlab/ci/status/external/common_spec.rb b/spec/lib/gitlab/ci/status/external/common_spec.rb index b38fbee2486..40871f86568 100644 --- a/spec/lib/gitlab/ci/status/external/common_spec.rb +++ b/spec/lib/gitlab/ci/status/external/common_spec.rb @@ -29,7 +29,7 @@ describe Gitlab::Ci::Status::External::Common do describe '#has_details?' do context 'when user has access to read commit status' do before do - project.team << [user, :developer] + project.add_developer(user) end it { is_expected.to have_details } diff --git a/spec/lib/gitlab/ci/status/external/factory_spec.rb b/spec/lib/gitlab/ci/status/external/factory_spec.rb index c96fd53e730..529d02a3e39 100644 --- a/spec/lib/gitlab/ci/status/external/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/external/factory_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::Ci::Status::External::Factory do let(:external_url) { 'http://gitlab.com/status' } before do - project.team << [user, :developer] + project.add_developer(user) end context 'when external status has a simple core status' do diff --git a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb index 4a5b45e7cae..57df8325635 100644 --- a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb @@ -18,7 +18,7 @@ describe Gitlab::Ci::Status::Pipeline::Common do describe '#has_details?' do context 'when user has access to read pipeline' do before do - project.team << [user, :developer] + project.add_developer(user) end it { is_expected.to have_details } diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb index dd754b849b2..defb3fdc0df 100644 --- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -7,7 +7,7 @@ describe Gitlab::Ci::Status::Pipeline::Factory do let(:factory) { described_class.new(pipeline, user) } before do - project.team << [user, :developer] + project.add_developer(user) end context 'when pipeline has a core status' do diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb index f5f03ac0395..6ec35f8da7e 100644 --- a/spec/lib/gitlab/ci/status/stage/common_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb @@ -27,7 +27,7 @@ describe Gitlab::Ci::Status::Stage::Common do context 'when user has permission to read pipeline' do before do - project.team << [user, :master] + project.add_master(user) end it 'has details' do diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb index 432b07e4902..dee4f4efd1b 100644 --- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb @@ -18,7 +18,7 @@ describe Gitlab::Ci::Status::Stage::Factory do end before do - project.team << [user, :developer] + project.add_developer(user) end context 'when stage has a core status' do diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index ef7d766a13d..8c79ef54c6c 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -13,8 +13,8 @@ describe Gitlab::ClosingIssueExtractor do subject { described_class.new(project, project.creator) } before do - project.team << [project.creator, :developer] - project2.team << [project.creator, :master] + project.add_developer(project.creator) + project2.add_master(project.creator) end describe "#closed_by_message" do @@ -297,7 +297,7 @@ describe Gitlab::ClosingIssueExtractor do context 'with an external issue tracker reference' do it 'extracts the referenced issue' do jira_project = create(:jira_project, name: 'JIRA_EXT1') - jira_project.team << [jira_project.creator, :master] + jira_project.add_master(jira_project.creator) jira_issue = ExternalIssue.new("#{jira_project.name}-1", project: jira_project) closing_issue_extractor = described_class.new(jira_project, jira_project.creator) message = "Resolve #{jira_issue.to_reference}" diff --git a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb index 0560c47f03f..3fe0493ed9b 100644 --- a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb @@ -23,6 +23,8 @@ describe Gitlab::CycleAnalytics::BaseEventFetcher do allow_any_instance_of(described_class).to receive(:serialize) do |event| event end + allow_any_instance_of(described_class) + .to receive(:allowed_ids).and_return(nil) stub_const('Gitlab::CycleAnalytics::BaseEventFetcher::MAX_EVENTS', max_events) diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 28ea7d4c303..38a47a159e1 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -122,17 +122,18 @@ describe 'cycle analytics events' do let(:stage) { :test } let(:merge_request) { MergeRequest.first } + let!(:pipeline) do create(:ci_pipeline, ref: merge_request.source_branch, sha: merge_request.diff_head_sha, - project: context.project, + project: project, head_pipeline_of: merge_request) end before do - create(:ci_build, pipeline: pipeline, status: :success, author: user) - create(:ci_build, pipeline: pipeline, status: :success, author: user) + create(:ci_build, :success, pipeline: pipeline, author: user) + create(:ci_build, :success, pipeline: pipeline, author: user) pipeline.run! pipeline.succeed! @@ -219,17 +220,18 @@ describe 'cycle analytics events' do describe '#staging_events' do let(:stage) { :staging } let(:merge_request) { MergeRequest.first } + let!(:pipeline) do create(:ci_pipeline, ref: merge_request.source_branch, sha: merge_request.diff_head_sha, - project: context.project, + project: project, head_pipeline_of: merge_request) end before do - create(:ci_build, pipeline: pipeline, status: :success, author: user) - create(:ci_build, pipeline: pipeline, status: :success, author: user) + create(:ci_build, :success, pipeline: pipeline, author: user) + create(:ci_build, :success, pipeline: pipeline, author: user) pipeline.run! pipeline.succeed! diff --git a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb index 2a0dd7be439..6de4bd3dc7c 100644 --- a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb @@ -38,7 +38,7 @@ describe Gitlab::CycleAnalytics::Permissions do context 'user is master' do before do - project.team << [user, :master] + project.add_master(user) end it 'has permissions to issue stage' do @@ -72,7 +72,7 @@ describe Gitlab::CycleAnalytics::Permissions do context 'user has no build permissions' do before do - project.team << [user, :guest] + project.add_guest(user) end it 'has permissions to issue stage' do @@ -90,7 +90,7 @@ describe Gitlab::CycleAnalytics::Permissions do context 'user has no merge request permissions' do before do - project.team << [user, :guest] + project.add_guest(user) end it 'has permissions to issue stage' do @@ -108,7 +108,7 @@ describe Gitlab::CycleAnalytics::Permissions do context 'user has no issue permissions' do before do - project.team << [user, :developer] + project.add_developer(user) project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) end diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index 664ba0f7234..43761c2fe0c 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -902,7 +902,7 @@ describe Gitlab::Database::MigrationHelpers do describe '#check_trigger_permissions!' do it 'does nothing when the user has the correct permissions' do expect { model.check_trigger_permissions!('users') } - .not_to raise_error(RuntimeError) + .not_to raise_error end it 'raises RuntimeError when the user does not have the correct permissions' do @@ -1006,12 +1006,12 @@ describe Gitlab::Database::MigrationHelpers do context 'with batch_size option' do it 'queues jobs correctly' do Sidekiq::Testing.fake! do - model.queue_background_migration_jobs_by_range_at_intervals(User, 'FooJob', 10.seconds, batch_size: 2) + model.queue_background_migration_jobs_by_range_at_intervals(User, 'FooJob', 10.minutes, batch_size: 2) expect(BackgroundMigrationWorker.jobs[0]['args']).to eq(['FooJob', [id1, id2]]) - expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.seconds.from_now.to_f) + expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.minutes.from_now.to_f) expect(BackgroundMigrationWorker.jobs[1]['args']).to eq(['FooJob', [id3, id3]]) - expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(20.seconds.from_now.to_f) + expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(20.minutes.from_now.to_f) end end end @@ -1019,10 +1019,10 @@ describe Gitlab::Database::MigrationHelpers do context 'without batch_size option' do it 'queues jobs correctly' do Sidekiq::Testing.fake! do - model.queue_background_migration_jobs_by_range_at_intervals(User, 'FooJob', 10.seconds) + model.queue_background_migration_jobs_by_range_at_intervals(User, 'FooJob', 10.minutes) expect(BackgroundMigrationWorker.jobs[0]['args']).to eq(['FooJob', [id1, id3]]) - expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.seconds.from_now.to_f) + expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.minutes.from_now.to_f) end end end @@ -1036,4 +1036,93 @@ describe Gitlab::Database::MigrationHelpers do end end end + + describe '#change_column_type_using_background_migration' do + let!(:issue) { create(:issue) } + + let(:issue_model) do + Class.new(ActiveRecord::Base) do + self.table_name = 'issues' + include EachBatch + end + end + + it 'changes the type of a column using a background migration' do + expect(model) + .to receive(:add_column) + .with('issues', 'closed_at_for_type_change', :datetime_with_timezone) + + expect(model) + .to receive(:install_rename_triggers) + .with('issues', :closed_at, 'closed_at_for_type_change') + + expect(BackgroundMigrationWorker) + .to receive(:perform_in) + .ordered + .with( + 10.minutes, + 'CopyColumn', + ['issues', :closed_at, 'closed_at_for_type_change', issue.id, issue.id] + ) + + expect(BackgroundMigrationWorker) + .to receive(:perform_in) + .ordered + .with( + 1.hour + 10.minutes, + 'CleanupConcurrentTypeChange', + ['issues', :closed_at, 'closed_at_for_type_change'] + ) + + expect(Gitlab::BackgroundMigration) + .to receive(:steal) + .ordered + .with('CopyColumn') + + expect(Gitlab::BackgroundMigration) + .to receive(:steal) + .ordered + .with('CleanupConcurrentTypeChange') + + model.change_column_type_using_background_migration( + issue_model.all, + :closed_at, + :datetime_with_timezone + ) + end + end + + describe '#perform_background_migration_inline?' do + it 'returns true in a test environment' do + allow(Rails.env) + .to receive(:test?) + .and_return(true) + + expect(model.perform_background_migration_inline?).to eq(true) + end + + it 'returns true in a development environment' do + allow(Rails.env) + .to receive(:test?) + .and_return(false) + + allow(Rails.env) + .to receive(:development?) + .and_return(true) + + expect(model.perform_background_migration_inline?).to eq(true) + end + + it 'returns false in a production environment' do + allow(Rails.env) + .to receive(:test?) + .and_return(false) + + allow(Rails.env) + .to receive(:development?) + .and_return(false) + + expect(model.perform_background_migration_inline?).to eq(false) + end + end end diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb index d81774c8b8f..a067c42b75b 100644 --- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb +++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb @@ -19,4 +19,18 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do diff_files end + + shared_examples 'initializes a DiffCollection' do + it 'returns a valid instance of a DiffCollection' do + expect(diff_files).to be_a(Gitlab::Git::DiffCollection) + end + end + + context 'with Gitaly disabled', :disable_gitaly do + it_behaves_like 'initializes a DiffCollection' + end + + context 'with Gitaly enabled' do + it_behaves_like 'initializes a DiffCollection' + end end diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index ff9acfd08b9..9204ea37963 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -431,4 +431,29 @@ describe Gitlab::Diff::File do end end end + + context 'when neither blob exists' do + let(:blank_diff_refs) { Gitlab::Diff::DiffRefs.new(base_sha: Gitlab::Git::BLANK_SHA, head_sha: Gitlab::Git::BLANK_SHA) } + let(:diff_file) { described_class.new(diff, diff_refs: blank_diff_refs, repository: project.repository) } + + describe '#blob' do + it 'returns a concrete nil so it can be used in boolean expressions' do + actual = diff_file.blob && true + + expect(actual).to be_nil + end + end + + describe '#binary?' do + it 'returns false' do + expect(diff_file).not_to be_binary + end + end + + describe '#size' do + it 'returns zero' do + expect(diff_file.size).to be_zero + end + end + end end diff --git a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb index 51ce3116880..dc1a93367a4 100644 --- a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb @@ -49,6 +49,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do expect(merge_request.author).to eq(user) expect(merge_request.source_branch).to eq('feature') expect(merge_request.title).to eq('Feature added') + expect(merge_request.description).to eq('Merge request description') expect(merge_request.target_branch).to eq(project.default_branch) end end @@ -79,6 +80,17 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do expect { receiver.execute }.to raise_error(Gitlab::Email::InvalidMergeRequestError) end end + + context "when the message body is blank" do + let(:email_raw) { fixture_file("emails/valid_new_merge_request_no_description.eml") } + + it "creates a new merge request with description set from the last commit" do + expect { receiver.execute }.to change { project.merge_requests.count }.by(1) + merge_request = project.merge_requests.last + + expect(merge_request.description).to eq('Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>') + end + end end end end diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb index d0fa16ce4d1..031efcf1291 100644 --- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb @@ -66,7 +66,7 @@ describe Gitlab::Email::Handler::CreateNoteHandler do context 'and current user can update noteable' do before do - project.team << [user, :developer] + project.add_developer(user) end it 'does not raise an error' do @@ -99,7 +99,7 @@ describe Gitlab::Email::Handler::CreateNoteHandler do context 'and current user can update noteable' do before do - project.team << [user, :developer] + project.add_developer(user) end it 'post a note and updates the noteable' do diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb index f6e5c55240f..4e9367323cb 100644 --- a/spec/lib/gitlab/encoding_helper_spec.rb +++ b/spec/lib/gitlab/encoding_helper_spec.rb @@ -120,6 +120,24 @@ describe Gitlab::EncodingHelper do it 'returns empty string on conversion errors' do expect { ext_class.encode_utf8('') }.not_to raise_error(ArgumentError) end + + context 'with strings that can be forcefully encoded into utf8' do + let(:test_string) do + "refs/heads/FixSymbolsTitleDropdown".encode("ASCII-8BIT") + end + let(:expected_string) do + "refs/heads/FixSymbolsTitleDropdown".encode("UTF-8") + end + + subject { ext_class.encode_utf8(test_string) } + + it "doesn't use CharlockHolmes if the encoding can be forced into utf_8" do + expect(CharlockHolmes::EncodingDetector).not_to receive(:detect) + + expect(subject).to eq(expected_string) + expect(subject.encoding.name).to eq('UTF-8') + end + end end describe '#clean' do @@ -145,4 +163,18 @@ describe Gitlab::EncodingHelper do end end end + + describe 'encode_binary' do + [ + [nil, ""], + ["", ""], + [" ", " "], + %w(a1 a1), + ["编码", "\xE7\xBC\x96\xE7\xA0\x81".b] + ].each do |input, result| + it "encodes #{input.inspect} to #{result.inspect}" do + expect(ext_class.encode_binary(input)).to eq(result) + end + end + end end diff --git a/spec/lib/gitlab/exclusive_lease_spec.rb b/spec/lib/gitlab/exclusive_lease_spec.rb index 7322a326b01..6193e177668 100644 --- a/spec/lib/gitlab/exclusive_lease_spec.rb +++ b/spec/lib/gitlab/exclusive_lease_spec.rb @@ -73,4 +73,19 @@ describe Gitlab::ExclusiveLease, :clean_gitlab_redis_shared_state do described_class.new(key, timeout: 3600).try_obtain end end + + describe '#ttl' do + it 'returns the TTL of the Redis key' do + lease = described_class.new('kittens', timeout: 100) + lease.try_obtain + + expect(lease.ttl <= 100).to eq(true) + end + + it 'returns nil when the lease does not exist' do + lease = described_class.new('kittens', timeout: 10) + + expect(lease.ttl).to be_nil + end + end end diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb index 7dc06c90078..4d2f08f95fc 100644 --- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb @@ -10,7 +10,7 @@ describe Gitlab::Gfm::ReferenceRewriter do let(:text) { 'some text' } before do - old_project.team << [user, :reporter] + old_project.add_reporter(user) end describe '#rewrite' do diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index c04a9688503..07eb5b82d5f 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -146,7 +146,7 @@ describe Gitlab::Git::Blob, seed_helper: true do context 'when sha references a tree' do it 'returns nil' do - tree = Gitlab::Git::Commit.find(repository, 'master').tree + tree = repository.rugged.rev_parse('master^{tree}') blob = Gitlab::Git::Blob.raw(repository, tree.oid) @@ -202,16 +202,6 @@ describe Gitlab::Git::Blob, seed_helper: true do context 'limiting' do subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) } - context 'default' do - let(:blob_size_limit) { nil } - - it 'limits to MAX_DATA_DISPLAY_SIZE' do - stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100) - - expect(subject.first.data.size).to eq(100) - end - end - context 'positive' do let(:blob_size_limit) { 10 } @@ -221,7 +211,10 @@ describe Gitlab::Git::Blob, seed_helper: true do context 'zero' do let(:blob_size_limit) { 0 } - it { expect(subject.first.data).to eq('') } + it 'only loads the metadata' do + expect(subject.first.size).not_to be(0) + expect(subject.first.data).to eq('') + end end context 'negative' do @@ -237,7 +230,7 @@ describe Gitlab::Git::Blob, seed_helper: true do end describe '.batch_lfs_pointers' do - let(:tree_object) { Gitlab::Git::Commit.find(repository, 'master').tree } + let(:tree_object) { repository.rugged.rev_parse('master^{tree}') } let(:non_lfs_blob) do Gitlab::Git::Blob.find( diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 5ed639543e0..6a07a3ca8b8 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -55,7 +55,6 @@ describe Gitlab::Git::Commit, seed_helper: true do it { expect(@commit.parents).to eq(@gitlab_parents) } it { expect(@commit.parent_id).to eq(@parents.first.oid) } it { expect(@commit.no_commit_message).to eq("--no commit message") } - it { expect(@commit.tree).to eq(@tree) } after do # Erase the new commit so other tests get the original repo @@ -428,6 +427,11 @@ describe Gitlab::Git::Commit, seed_helper: true do subject { super().deletions } it { is_expected.to eq(6) } end + + describe '#total' do + subject { super().total } + it { is_expected.to eq(17) } + end end describe '#stats with gitaly on' do diff --git a/spec/lib/gitlab/git/gitlab_projects_spec.rb b/spec/lib/gitlab/git/gitlab_projects_spec.rb new file mode 100644 index 00000000000..a798b188a0d --- /dev/null +++ b/spec/lib/gitlab/git/gitlab_projects_spec.rb @@ -0,0 +1,337 @@ +require 'spec_helper' + +describe Gitlab::Git::GitlabProjects do + after do + TestEnv.clean_test_path + end + + let(:project) { create(:project, :repository) } + + if $VERBOSE + let(:logger) { Logger.new(STDOUT) } + else + let(:logger) { double('logger').as_null_object } + end + + let(:tmp_repos_path) { TestEnv.repos_path } + let(:repo_name) { project.disk_path + '.git' } + let(:tmp_repo_path) { File.join(tmp_repos_path, repo_name) } + let(:gl_projects) { build_gitlab_projects(tmp_repos_path, repo_name) } + + describe '#initialize' do + it { expect(gl_projects.shard_path).to eq(tmp_repos_path) } + it { expect(gl_projects.repository_relative_path).to eq(repo_name) } + it { expect(gl_projects.repository_absolute_path).to eq(File.join(tmp_repos_path, repo_name)) } + it { expect(gl_projects.logger).to eq(logger) } + end + + describe '#mv_project' do + let(:new_repo_path) { File.join(tmp_repos_path, 'repo.git') } + + it 'moves a repo directory' do + expect(File.exist?(tmp_repo_path)).to be_truthy + + message = "Moving repository from <#{tmp_repo_path}> to <#{new_repo_path}>." + expect(logger).to receive(:info).with(message) + + expect(gl_projects.mv_project('repo.git')).to be_truthy + + expect(File.exist?(tmp_repo_path)).to be_falsy + expect(File.exist?(new_repo_path)).to be_truthy + end + + it "fails if the source path doesn't exist" do + expected_source_path = File.join(tmp_repos_path, 'bad-src.git') + expect(logger).to receive(:error).with("mv-project failed: source path <#{expected_source_path}> does not exist.") + + result = build_gitlab_projects(tmp_repos_path, 'bad-src.git').mv_project('repo.git') + expect(result).to be_falsy + end + + it 'fails if the destination path already exists' do + FileUtils.mkdir_p(File.join(tmp_repos_path, 'already-exists.git')) + + expected_distination_path = File.join(tmp_repos_path, 'already-exists.git') + message = "mv-project failed: destination path <#{expected_distination_path}> already exists." + expect(logger).to receive(:error).with(message) + + expect(gl_projects.mv_project('already-exists.git')).to be_falsy + end + end + + describe '#rm_project' do + it 'removes a repo directory' do + expect(File.exist?(tmp_repo_path)).to be_truthy + expect(logger).to receive(:info).with("Removing repository <#{tmp_repo_path}>.") + + expect(gl_projects.rm_project).to be_truthy + + expect(File.exist?(tmp_repo_path)).to be_falsy + end + end + + describe '#push_branches' do + let(:remote_name) { 'remote-name' } + let(:branch_name) { 'master' } + let(:cmd) { %W(git push -- #{remote_name} #{branch_name}) } + let(:force) { false } + + subject { gl_projects.push_branches(remote_name, 600, force, [branch_name]) } + + it 'executes the command' do + stub_spawn(cmd, 600, tmp_repo_path, success: true) + + is_expected.to be_truthy + end + + it 'fails' do + stub_spawn(cmd, 600, tmp_repo_path, success: false) + + is_expected.to be_falsy + end + + context 'with --force' do + let(:cmd) { %W(git push --force -- #{remote_name} #{branch_name}) } + let(:force) { true } + + it 'executes the command' do + stub_spawn(cmd, 600, tmp_repo_path, success: true) + + is_expected.to be_truthy + end + end + end + + describe '#fetch_remote' do + let(:remote_name) { 'remote-name' } + let(:branch_name) { 'master' } + let(:force) { false } + let(:tags) { true } + let(:args) { { force: force, tags: tags }.merge(extra_args) } + let(:extra_args) { {} } + let(:cmd) { %W(git fetch #{remote_name} --prune --quiet --tags) } + + subject { gl_projects.fetch_remote(remote_name, 600, args) } + + def stub_tempfile(name, filename, opts = {}) + chmod = opts.delete(:chmod) + file = StringIO.new + + allow(file).to receive(:close!) + allow(file).to receive(:path).and_return(name) + + expect(Tempfile).to receive(:new).with(filename).and_return(file) + expect(file).to receive(:chmod).with(chmod) if chmod + + file + end + + context 'with default args' do + it 'executes the command' do + stub_spawn(cmd, 600, tmp_repo_path, {}, success: true) + + is_expected.to be_truthy + end + + it 'fails' do + stub_spawn(cmd, 600, tmp_repo_path, {}, success: false) + + is_expected.to be_falsy + end + end + + context 'with --force' do + let(:force) { true } + let(:cmd) { %W(git fetch #{remote_name} --prune --quiet --force --tags) } + + it 'executes the command with forced option' do + stub_spawn(cmd, 600, tmp_repo_path, {}, success: true) + + is_expected.to be_truthy + end + end + + context 'with --no-tags' do + let(:tags) { false } + let(:cmd) { %W(git fetch #{remote_name} --prune --quiet --no-tags) } + + it 'executes the command' do + stub_spawn(cmd, 600, tmp_repo_path, {}, success: true) + + is_expected.to be_truthy + end + end + + describe 'with an SSH key' do + let(:extra_args) { { ssh_key: 'SSH KEY' } } + + it 'sets GIT_SSH to a custom script' do + script = stub_tempfile('scriptFile', 'gitlab-shell-ssh-wrapper', chmod: 0o755) + key = stub_tempfile('/tmp files/keyFile', 'gitlab-shell-key-file', chmod: 0o400) + + stub_spawn(cmd, 600, tmp_repo_path, { 'GIT_SSH' => 'scriptFile' }, success: true) + + is_expected.to be_truthy + + expect(script.string).to eq("#!/bin/sh\nexec ssh '-oIdentityFile=\"/tmp files/keyFile\"' '-oIdentitiesOnly=\"yes\"' \"$@\"") + expect(key.string).to eq('SSH KEY') + end + end + + describe 'with known_hosts data' do + let(:extra_args) { { known_hosts: 'KNOWN HOSTS' } } + + it 'sets GIT_SSH to a custom script' do + script = stub_tempfile('scriptFile', 'gitlab-shell-ssh-wrapper', chmod: 0o755) + key = stub_tempfile('/tmp files/knownHosts', 'gitlab-shell-known-hosts', chmod: 0o400) + + stub_spawn(cmd, 600, tmp_repo_path, { 'GIT_SSH' => 'scriptFile' }, success: true) + + is_expected.to be_truthy + + expect(script.string).to eq("#!/bin/sh\nexec ssh '-oStrictHostKeyChecking=\"yes\"' '-oUserKnownHostsFile=\"/tmp files/knownHosts\"' \"$@\"") + expect(key.string).to eq('KNOWN HOSTS') + end + end + end + + describe '#import_project' do + let(:project) { create(:project) } + let(:import_url) { TestEnv.factory_repo_path_bare } + let(:cmd) { %W(git clone --bare -- #{import_url} #{tmp_repo_path}) } + let(:timeout) { 600 } + + subject { gl_projects.import_project(import_url, timeout) } + + context 'success import' do + it 'imports a repo' do + expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy + + message = "Importing project from <#{import_url}> to <#{tmp_repo_path}>." + expect(logger).to receive(:info).with(message) + + is_expected.to be_truthy + + expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_truthy + end + end + + context 'already exists' do + it "doesn't import" do + FileUtils.mkdir_p(tmp_repo_path) + + is_expected.to be_falsy + end + end + + context 'timeout' do + it 'does not import a repo' do + stub_spawn_timeout(cmd, timeout, nil) + + message = "Importing project from <#{import_url}> to <#{tmp_repo_path}> failed." + expect(logger).to receive(:error).with(message) + + is_expected.to be_falsy + + expect(gl_projects.output).to eq("Timed out\n") + expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy + end + end + end + + describe '#fork_repository' do + let(:dest_repos_path) { tmp_repos_path } + let(:dest_repo_name) { File.join('@hashed', 'aa', 'bb', 'xyz.git') } + let(:dest_repo) { File.join(dest_repos_path, dest_repo_name) } + + subject { gl_projects.fork_repository(dest_repos_path, dest_repo_name) } + + before do + FileUtils.mkdir_p(dest_repos_path) + end + + after do + FileUtils.rm_rf(dest_repos_path) + end + + shared_examples 'forking a repository' do + it 'forks the repository' do + is_expected.to be_truthy + + expect(File.exist?(dest_repo)).to be_truthy + expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy + expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy + end + + it 'does not fork if a project of the same name already exists' do + # create a fake project at the intended destination + FileUtils.mkdir_p(dest_repo) + + is_expected.to be_falsy + end + end + + context 'when Gitaly fork_repository feature is enabled' do + it_behaves_like 'forking a repository' + end + + context 'when Gitaly fork_repository feature is disabled', :disable_gitaly do + it_behaves_like 'forking a repository' + + # We seem to be stuck to having only one working Gitaly storage in tests, changing + # that is not very straight-forward so I'm leaving this test here for now till + # https://gitlab.com/gitlab-org/gitlab-ce/issues/41393 is fixed. + context 'different storages' do + let(:dest_repos_path) { File.join(File.dirname(tmp_repos_path), 'alternative') } + + it 'forks the repo' do + is_expected.to be_truthy + + expect(File.exist?(dest_repo)).to be_truthy + expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy + expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy + end + end + + describe 'log messages' do + describe 'successful fork' do + it do + message = "Forking repository from <#{tmp_repo_path}> to <#{dest_repo}>." + expect(logger).to receive(:info).with(message) + + subject + end + end + + describe 'failed fork due existing destination' do + it do + FileUtils.mkdir_p(dest_repo) + message = "fork-repository failed: destination repository <#{dest_repo}> already exists." + expect(logger).to receive(:error).with(message) + + subject + end + end + end + end + end + + def build_gitlab_projects(*args) + described_class.new( + *args, + global_hooks_path: Gitlab.config.gitlab_shell.hooks_path, + logger: logger + ) + end + + def stub_spawn(*args, success: true) + exitstatus = success ? 0 : nil + expect(gl_projects).to receive(:popen_with_timeout).with(*args) + .and_return(["output", exitstatus]) + end + + def stub_spawn_timeout(*args) + expect(gl_projects).to receive(:popen_with_timeout).with(*args) + .and_raise(Timeout::Error) + end +end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index f19b65a5f71..f346a345f00 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -18,6 +18,52 @@ describe Gitlab::Git::Repository, seed_helper: true do end let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } + let(:storage_path) { TestEnv.repos_path } + + describe '.create_hooks' do + let(:repo_path) { File.join(storage_path, 'hook-test.git') } + let(:hooks_dir) { File.join(repo_path, 'hooks') } + let(:target_hooks_dir) { Gitlab.config.gitlab_shell.hooks_path } + let(:existing_target) { File.join(repo_path, 'foobar') } + + before do + FileUtils.rm_rf(repo_path) + FileUtils.mkdir_p(repo_path) + end + + context 'hooks is a directory' do + let(:existing_file) { File.join(hooks_dir, 'my-file') } + + before do + FileUtils.mkdir_p(hooks_dir) + FileUtils.touch(existing_file) + described_class.create_hooks(repo_path, target_hooks_dir) + end + + it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) } + it { expect(Dir[File.join(repo_path, "hooks.old.*/my-file")].count).to eq(1) } + end + + context 'hooks is a valid symlink' do + before do + FileUtils.mkdir_p existing_target + File.symlink(existing_target, hooks_dir) + described_class.create_hooks(repo_path, target_hooks_dir) + end + + it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) } + end + + context 'hooks is a broken symlink' do + before do + FileUtils.rm_f(existing_target) + File.symlink(existing_target, hooks_dir) + described_class.create_hooks(repo_path, target_hooks_dir) + end + + it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) } + end + end describe "Respond to" do subject { repository } @@ -588,44 +634,54 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe '#fetch_as_mirror_without_shell' do + describe '#fetch_repository_as_mirror' do let(:new_repository) do Gitlab::Git::Repository.new('default', 'my_project.git', '') end - subject { new_repository.fetch_as_mirror_without_shell(repository.path) } + subject { new_repository.fetch_repository_as_mirror(repository) } before do Gitlab::Shell.new.add_repository('default', 'my_project') end after do - Gitlab::Shell.new.remove_repository(TestEnv.repos_path, 'my_project') + Gitlab::Shell.new.remove_repository(storage_path, 'my_project') end - it 'fetches a url as a mirror remote' do - subject + shared_examples 'repository mirror fecthing' do + it 'fetches a repository as a mirror remote' do + subject - expect(refs(new_repository.path)).to eq(refs(repository.path)) - end + expect(refs(new_repository.path)).to eq(refs(repository.path)) + end - context 'with keep-around refs' do - let(:sha) { SeedRepo::Commit::ID } - let(:keep_around_ref) { "refs/keep-around/#{sha}" } - let(:tmp_ref) { "refs/tmp/#{SecureRandom.hex}" } + context 'with keep-around refs' do + let(:sha) { SeedRepo::Commit::ID } + let(:keep_around_ref) { "refs/keep-around/#{sha}" } + let(:tmp_ref) { "refs/tmp/#{SecureRandom.hex}" } - before do - repository.rugged.references.create(keep_around_ref, sha, force: true) - repository.rugged.references.create(tmp_ref, sha, force: true) - end + before do + repository.rugged.references.create(keep_around_ref, sha, force: true) + repository.rugged.references.create(tmp_ref, sha, force: true) + end - it 'includes the temporary and keep-around refs' do - subject + it 'includes the temporary and keep-around refs' do + subject - expect(refs(new_repository.path)).to include(keep_around_ref) - expect(refs(new_repository.path)).to include(tmp_ref) + expect(refs(new_repository.path)).to include(keep_around_ref) + expect(refs(new_repository.path)).to include(tmp_ref) + end end end + + context 'with gitaly enabled' do + it_behaves_like 'repository mirror fecthing' + end + + context 'with gitaly enabled', :skip_gitaly_mock do + it_behaves_like 'repository mirror fecthing' + end end describe '#remote_tags' do @@ -656,21 +712,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe '#remote_exists?' do - before(:all) do - @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - @repo.add_remote("new_remote", SeedHelper::GITLAB_GIT_TEST_REPO_URL) - end - - it 'returns true for an existing remote' do - expect(@repo.remote_exists?('new_remote')).to eq(true) - end - - it 'returns false for a non-existing remote' do - expect(@repo.remote_exists?('foo')).to eq(false) - end - end - describe "#log" do let(:commit_with_old_name) do Gitlab::Git::Commit.decorate(repository, @commit_with_old_name_id) @@ -985,7 +1026,7 @@ describe Gitlab::Git::Repository, seed_helper: true do shared_examples 'extended commit counting' do context 'with after timestamp' do it 'returns the number of commits after timestamp' do - options = { ref: 'master', limit: nil, after: Time.iso8601('2013-03-03T20:15:01+00:00') } + options = { ref: 'master', after: Time.iso8601('2013-03-03T20:15:01+00:00') } expect(repository.count_commits(options)).to eq(25) end @@ -993,19 +1034,65 @@ describe Gitlab::Git::Repository, seed_helper: true do context 'with before timestamp' do it 'returns the number of commits before timestamp' do - options = { ref: 'feature', limit: nil, before: Time.iso8601('2015-03-03T20:15:01+00:00') } + options = { ref: 'feature', before: Time.iso8601('2015-03-03T20:15:01+00:00') } expect(repository.count_commits(options)).to eq(9) end end + context 'with max_count' do + it 'returns the number of commits with path ' do + options = { ref: 'master', max_count: 5 } + + expect(repository.count_commits(options)).to eq(5) + end + end + context 'with path' do it 'returns the number of commits with path ' do - options = { ref: 'master', limit: nil, path: "encoding" } + options = { ref: 'master', path: 'encoding' } expect(repository.count_commits(options)).to eq(2) end end + + context 'with option :from and option :to' do + it 'returns the number of commits ahead for fix-mode..fix-blob-path' do + options = { from: 'fix-mode', to: 'fix-blob-path' } + + expect(repository.count_commits(options)).to eq(2) + end + + it 'returns the number of commits ahead for fix-blob-path..fix-mode' do + options = { from: 'fix-blob-path', to: 'fix-mode' } + + expect(repository.count_commits(options)).to eq(1) + end + + context 'with option :left_right' do + it 'returns the number of commits for fix-mode...fix-blob-path' do + options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true } + + expect(repository.count_commits(options)).to eq([1, 2]) + end + + context 'with max_count' do + it 'returns the number of commits with path ' do + options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true, max_count: 1 } + + expect(repository.count_commits(options)).to eq([1, 1]) + end + end + end + end + + context 'with max_count' do + it 'returns the number of commits up to the passed limit' do + options = { ref: 'master', max_count: 10, after: Time.iso8601('2013-03-03T20:15:01+00:00') } + + expect(repository.count_commits(options)).to eq(10) + end + end end context 'when Gitaly count_commits feature is enabled' do @@ -1625,7 +1712,7 @@ describe Gitlab::Git::Repository, seed_helper: true do let(:branch_name) { "to-be-deleted-soon" } before do - project.team << [user, :developer] + project.add_developer(user) repository.create_branch(branch_name) end @@ -1662,21 +1749,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe '#fetch_remote_without_shell' do - let(:git_path) { Gitlab.config.git.bin_path } - let(:remote_name) { 'my_remote' } - - subject { repository.fetch_remote_without_shell(remote_name) } - - it 'fetches the remote and returns true if the command was successful' do - expect(repository).to receive(:popen) - .with(%W(#{git_path} fetch #{remote_name}), repository.path, {}) - .and_return(['', 0]) - - expect(subject).to be(true) - end - end - describe '#merge' do let(:repository) do Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') @@ -1704,6 +1776,20 @@ describe Gitlab::Git::Repository, seed_helper: true do expect(result.repo_created).to eq(false) expect(result.branch_created).to eq(false) end + + it 'returns nil if there was a concurrent branch update' do + concurrent_update_id = '33f3729a45c02fc67d00adb1b8bca394b0e761d9' + result = repository.merge(user, source_sha, target_branch, 'Test merge') do + # This ref update should make the merge fail + repository.write_ref(Gitlab::Git::BRANCH_REF_PREFIX + target_branch, concurrent_update_id) + end + + # This 'nil' signals that the merge was not applied + expect(result).to be_nil + + # Our concurrent ref update should not have been undone + expect(repository.find_branch(target_branch).target).to eq(concurrent_update_id) + end end context 'with gitaly' do @@ -1813,6 +1899,166 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe 'remotes' do + let(:repository) do + Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') + end + let(:remote_name) { 'my-remote' } + + after do + ensure_seeds + end + + describe '#add_remote' do + let(:url) { 'http://my-repo.git' } + let(:mirror_refmap) { '+refs/*:refs/*' } + + it 'creates a new remote via Gitaly' do + expect_any_instance_of(Gitlab::GitalyClient::RemoteService) + .to receive(:add_remote).with(remote_name, url, mirror_refmap) + + repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap) + end + + context 'with Gitaly disabled', :skip_gitaly_mock do + it 'creates a new remote via Rugged' do + expect_any_instance_of(Rugged::RemoteCollection).to receive(:create) + .with(remote_name, url) + expect_any_instance_of(Rugged::Config).to receive(:[]=) + .with("remote.#{remote_name}.mirror", true) + expect_any_instance_of(Rugged::Config).to receive(:[]=) + .with("remote.#{remote_name}.prune", true) + expect_any_instance_of(Rugged::Config).to receive(:[]=) + .with("remote.#{remote_name}.fetch", mirror_refmap) + + repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap) + end + end + end + + describe '#remove_remote' do + it 'removes the remote via Gitaly' do + expect_any_instance_of(Gitlab::GitalyClient::RemoteService) + .to receive(:remove_remote).with(remote_name) + + repository.remove_remote(remote_name) + end + + context 'with Gitaly disabled', :skip_gitaly_mock do + it 'removes the remote via Rugged' do + expect_any_instance_of(Rugged::RemoteCollection).to receive(:delete) + .with(remote_name) + + repository.remove_remote(remote_name) + end + end + end + end + + describe '#gitlab_projects' do + subject { repository.gitlab_projects } + + it { expect(subject.shard_path).to eq(storage_path) } + it { expect(subject.repository_relative_path).to eq(repository.relative_path) } + end + + context 'gitlab_projects commands' do + let(:gitlab_projects) { repository.gitlab_projects } + let(:timeout) { Gitlab.config.gitlab_shell.git_timeout } + + describe '#push_remote_branches' do + subject do + repository.push_remote_branches('downstream-remote', ['master']) + end + + it 'executes the command' do + expect(gitlab_projects).to receive(:push_branches) + .with('downstream-remote', timeout, true, ['master']) + .and_return(true) + + is_expected.to be_truthy + end + + it 'raises an error if the command fails' do + allow(gitlab_projects).to receive(:output) { 'error' } + expect(gitlab_projects).to receive(:push_branches) + .with('downstream-remote', timeout, true, ['master']) + .and_return(false) + + expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error') + end + end + + describe '#delete_remote_branches' do + subject do + repository.delete_remote_branches('downstream-remote', ['master']) + end + + it 'executes the command' do + expect(gitlab_projects).to receive(:delete_remote_branches) + .with('downstream-remote', ['master']) + .and_return(true) + + is_expected.to be_truthy + end + + it 'raises an error if the command fails' do + allow(gitlab_projects).to receive(:output) { 'error' } + expect(gitlab_projects).to receive(:delete_remote_branches) + .with('downstream-remote', ['master']) + .and_return(false) + + expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error') + end + end + + describe '#delete_remote_branches' do + subject do + repository.delete_remote_branches('downstream-remote', ['master']) + end + + it 'executes the command' do + expect(gitlab_projects).to receive(:delete_remote_branches) + .with('downstream-remote', ['master']) + .and_return(true) + + is_expected.to be_truthy + end + + it 'raises an error if the command fails' do + allow(gitlab_projects).to receive(:output) { 'error' } + expect(gitlab_projects).to receive(:delete_remote_branches) + .with('downstream-remote', ['master']) + .and_return(false) + + expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error') + end + end + + describe '#delete_remote_branches' do + subject do + repository.delete_remote_branches('downstream-remote', ['master']) + end + + it 'executes the command' do + expect(gitlab_projects).to receive(:delete_remote_branches) + .with('downstream-remote', ['master']) + .and_return(true) + + is_expected.to be_truthy + end + + it 'raises an error if the command fails' do + allow(gitlab_projects).to receive(:output) { 'error' } + expect(gitlab_projects).to receive(:delete_remote_branches) + .with('downstream-remote', ['master']) + .and_return(false) + + expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error') + end + end + end + def create_remote_branch(repository, remote_name, branch_name, source_branch_name) source_branch = repository.branches.find { |branch| branch.name == source_branch_name } rugged = repository.rugged diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 2db560c2cec..4290fbb0087 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -275,7 +275,7 @@ describe Gitlab::GitAccess do describe '#check_command_disabled!' do before do - project.team << [user, :master] + project.add_master(user) end context 'over http' do @@ -404,7 +404,7 @@ describe Gitlab::GitAccess do describe 'reporter user' do before do - project.team << [user, :reporter] + project.add_reporter(user) end context 'pull code' do @@ -417,7 +417,7 @@ describe Gitlab::GitAccess do context 'when member of the project' do before do - project.team << [user, :reporter] + project.add_reporter(user) end context 'pull code' do @@ -497,7 +497,7 @@ describe Gitlab::GitAccess do if role == :admin user.update_attribute(:admin, true) else - project.team << [user, role] + project.add_role(user, role) end aggregate_failures do @@ -658,7 +658,7 @@ describe Gitlab::GitAccess do context 'when project is authorized' do before do - project.team << [user, :reporter] + project.add_reporter(user) end it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) } diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb index 1056074264a..186b2d9279d 100644 --- a/spec/lib/gitlab/git_access_wiki_spec.rb +++ b/spec/lib/gitlab/git_access_wiki_spec.rb @@ -18,7 +18,7 @@ describe Gitlab::GitAccessWiki do context 'when user can :create_wiki' do before do create(:protected_branch, name: 'master', project: project) - project.team << [user, :developer] + project.add_developer(user) end subject { access.check('git-receive-pack', changes) } @@ -41,7 +41,7 @@ describe Gitlab::GitAccessWiki do subject { access.check('git-upload-pack', '_any') } before do - project.team << [user, :developer] + project.add_developer(user) end context 'when wiki feature is enabled' do diff --git a/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb b/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb new file mode 100644 index 00000000000..b9641de7eda --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb @@ -0,0 +1,90 @@ +require 'spec_helper' + +describe Gitlab::GitalyClient::ConflictsService do + let(:project) { create(:project, :repository) } + let(:target_project) { create(:project, :repository) } + let(:source_repository) { project.repository.raw } + let(:target_repository) { target_project.repository.raw } + let(:target_gitaly_repository) { target_repository.gitaly_repository } + let(:our_commit_oid) { 'f00' } + let(:their_commit_oid) { 'f44' } + let(:client) do + described_class.new(target_repository, our_commit_oid, their_commit_oid) + end + + describe '#list_conflict_files' do + let(:request) do + Gitaly::ListConflictFilesRequest.new( + repository: target_gitaly_repository, our_commit_oid: our_commit_oid, + their_commit_oid: their_commit_oid + ) + end + let(:our_path) { 'our/path' } + let(:their_path) { 'their/path' } + let(:our_mode) { 0744 } + let(:header) do + double(repository: target_gitaly_repository, commit_oid: our_commit_oid, + our_path: our_path, our_mode: 0744, their_path: their_path) + end + let(:response) do + [ + double(files: [double(header: header), double(content: 'foo', header: nil)]), + double(files: [double(content: 'bar', header: nil)]) + ] + end + let(:file) { subject[0] } + + subject { client.list_conflict_files } + + it 'sends an RPC request' do + expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files) + .with(request, kind_of(Hash)).and_return([]) + + subject + end + + it 'forms a Gitlab::Git::ConflictFile collection from the response' do + allow_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files) + .with(request, kind_of(Hash)).and_return(response) + + expect(subject.size).to be(1) + expect(file.content).to eq('foobar') + expect(file.their_path).to eq(their_path) + expect(file.our_path).to eq(our_path) + expect(file.our_mode).to be(our_mode) + expect(file.repository).to eq(target_repository) + expect(file.commit_oid).to eq(our_commit_oid) + end + end + + describe '#resolve_conflicts' do + let(:user) { create(:user) } + let(:files) do + [{ old_path: 'some/path', new_path: 'some/path', content: '' }] + end + let(:source_branch) { 'master' } + let(:target_branch) { 'feature' } + let(:commit_message) { 'Solving conflicts' } + let(:resolution) do + Gitlab::Git::Conflict::Resolution.new(user, files, commit_message) + end + + subject do + client.resolve_conflicts(source_repository, resolution, source_branch, target_branch) + end + + it 'sends an RPC request' do + expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:resolve_conflicts) + .with(kind_of(Enumerator), kind_of(Hash)).and_return(double(resolution_error: "")) + + subject + end + + it 'raises a relevant exception if resolution_error is present' do + expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:resolve_conflicts) + .with(kind_of(Enumerator), kind_of(Hash)).and_return(double(resolution_error: "something happened")) + + expect { subject }.to raise_error(Gitlab::Git::Conflict::Resolver::ResolutionError) + end + end +end diff --git a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb new file mode 100644 index 00000000000..9d540446532 --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe Gitlab::GitalyClient::RemoteService do + let(:project) { create(:project) } + let(:storage_name) { project.repository_storage } + let(:relative_path) { project.disk_path + '.git' } + let(:remote_name) { 'my-remote' } + let(:client) { described_class.new(project.repository) } + + describe '#add_remote' do + let(:url) { 'http://my-repo.git' } + let(:mirror_refmap) { :all_refs } + + it 'sends an add_remote message' do + expect_any_instance_of(Gitaly::RemoteService::Stub) + .to receive(:add_remote) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(:add_remote_response)) + + client.add_remote(remote_name, url, mirror_refmap) + end + end + + describe '#remove_remote' do + it 'sends an remove_remote message and returns the result value' do + expect_any_instance_of(Gitaly::RemoteService::Stub) + .to receive(:remove_remote) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(result: true)) + + expect(client.remove_remote(remote_name)).to be(true) + end + end + + describe '#fetch_internal_remote' do + let(:remote_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + + it 'sends an fetch_internal_remote message and returns the result value' do + expect_any_instance_of(Gitaly::RemoteService::Stub) + .to receive(:fetch_internal_remote) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(result: true)) + + expect(client.fetch_internal_remote(remote_repository)).to be(true) + end + end +end diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index a871ed0df0e..309b7338ef0 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -38,20 +38,6 @@ describe Gitlab::GitalyClient, skip_gitaly_mock: true do end end - describe 'encode' do - [ - [nil, ""], - ["", ""], - [" ", " "], - %w(a1 a1), - ["编码", "\xE7\xBC\x96\xE7\xA0\x81".b] - ].each do |input, result| - it "encodes #{input.inspect} to #{result.inspect}" do - expect(described_class.encode(input)).to eq result - end - end - end - describe 'allow_n_plus_1_calls' do context 'when RequestStore is enabled', :request_store do it 'returns the result of the allow_n_plus_1_calls block' do diff --git a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb index 168e5d07504..46a57e08963 100644 --- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb @@ -70,7 +70,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do describe '#execute' do it 'imports the repository and wiki' do - expect(repository) + expect(project) .to receive(:empty_repo?) .and_return(true) @@ -93,7 +93,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do end it 'does not import the repository if it already exists' do - expect(repository) + expect(project) .to receive(:empty_repo?) .and_return(false) @@ -115,7 +115,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do end it 'does not import the wiki if it is disabled' do - expect(repository) + expect(project) .to receive(:empty_repo?) .and_return(true) @@ -137,7 +137,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do end it 'does not import the wiki if the repository could not be imported' do - expect(repository) + expect(project) .to receive(:empty_repo?) .and_return(true) diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb index 798ea0bac58..017facd0f5e 100644 --- a/spec/lib/gitlab/google_code_import/importer_spec.rb +++ b/spec/lib/gitlab/google_code_import/importer_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Importer do subject { described_class.new(project) } before do - project.team << [project.creator, :master] + project.add_master(project.creator) project.create_import_data(data: import_data) end diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index f0752649121..7580b62cfc0 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -6465,78 +6465,100 @@ } } ], - "statuses": [ - { - "id": 71, - "project_id": 5, - "status": "failed", - "finished_at": "2016-03-29T06:28:12.630Z", - "trace": null, - "created_at": "2016-03-22T15:20:35.772Z", - "updated_at": "2016-03-29T06:28:12.634Z", - "started_at": null, - "runner_id": null, - "coverage": null, - "commit_id": 36, - "commands": "$ build command", - "job_id": null, - "name": "test build 1", - "deploy": false, - "options": null, - "allow_failure": false, - "stage": "test", - "trigger_request_id": null, - "stage_idx": 1, - "tag": null, - "ref": "master", - "user_id": null, - "target_url": null, - "description": null, - "artifacts_file": { - "url": null - }, - "artifacts_metadata": { - "url": null - }, - "erased_by_id": null, - "erased_at": null, - "type": "Ci::Build", - "token": "abcd" - }, - { - "id": 72, - "project_id": 5, - "status": "success", - "finished_at": null, - "trace": "Porro ea qui ut dolores. Labore ab nemo explicabo aspernatur quis voluptates corporis. Et quasi delectus est sit aperiam perspiciatis asperiores. Repudiandae cum aut consectetur accusantium officia sunt.\n\nQuidem dolore iusto quaerat ut aut inventore et molestiae. Libero voluptates atque nemo qui. Nulla temporibus ipsa similique facere.\n\nAliquam ipsam perferendis qui fugit accusantium omnis id voluptatum. Dignissimos aliquid dicta eos voluptatem assumenda quia. Sed autem natus unde dolor et non nisi et. Consequuntur nihil consequatur rerum est.\n\nSimilique neque est iste ducimus qui fuga cupiditate. Libero autem est aut fuga. Consectetur natus quis non ducimus ut dolore. Magni voluptatibus eius et maxime aut.\n\nAd officiis tempore voluptate vitae corrupti explicabo labore est. Consequatur expedita et sunt nihil aut. Deleniti porro iusto molestiae et beatae.\n\nDeleniti modi nulla qui et labore sequi corrupti. Qui voluptatem assumenda eum cupiditate et. Nesciunt ipsam ut ea possimus eum. Consectetur quidem suscipit atque dolore itaque voluptatibus et cupiditate.", - "created_at": "2016-03-22T15:20:35.777Z", - "updated_at": "2016-03-22T15:20:35.777Z", - "started_at": null, - "runner_id": null, - "coverage": null, - "commit_id": 36, - "commands": "$ build command", - "job_id": null, - "name": "test build 2", - "deploy": false, - "options": null, - "allow_failure": false, - "stage": "test", - "trigger_request_id": null, - "stage_idx": 1, - "tag": null, - "ref": "master", - "user_id": null, - "target_url": null, - "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts_metadata.gz" - }, - "erased_by_id": null, - "erased_at": null + "stages": [ + { + "id": 11, + "project_id": 5, + "pipeline_id": 36, + "name": "test", + "status": 1, + "created_at": "2016-03-22T15:44:44.772Z", + "updated_at": "2016-03-29T06:44:44.634Z", + "statuses": [ + { + "id": 71, + "project_id": 5, + "status": "failed", + "finished_at": "2016-03-29T06:28:12.630Z", + "trace": null, + "created_at": "2016-03-22T15:20:35.772Z", + "updated_at": "2016-03-29T06:28:12.634Z", + "started_at": null, + "runner_id": null, + "coverage": null, + "commit_id": 36, + "commands": "$ build command", + "job_id": null, + "name": "test build 1", + "deploy": false, + "options": null, + "allow_failure": false, + "stage": "test", + "trigger_request_id": null, + "stage_idx": 1, + "stage_id": 11, + "tag": null, + "ref": "master", + "user_id": null, + "target_url": null, + "description": null, + "artifacts_file": { + "url": null + }, + "artifacts_metadata": { + "url": null + }, + "erased_by_id": null, + "erased_at": null, + "type": "Ci::Build", + "token": "abcd" + }, + { + "id": 72, + "project_id": 5, + "status": "success", + "finished_at": null, + "trace": "Porro ea qui ut dolores. Labore ab nemo explicabo aspernatur quis voluptates corporis. Et quasi delectus est sit aperiam perspiciatis asperiores. Repudiandae cum aut consectetur accusantium officia sunt.\n\nQuidem dolore iusto quaerat ut aut inventore et molestiae. Libero voluptates atque nemo qui. Nulla temporibus ipsa similique facere.\n\nAliquam ipsam perferendis qui fugit accusantium omnis id voluptatum. Dignissimos aliquid dicta eos voluptatem assumenda quia. Sed autem natus unde dolor et non nisi et. Consequuntur nihil consequatur rerum est.\n\nSimilique neque est iste ducimus qui fuga cupiditate. Libero autem est aut fuga. Consectetur natus quis non ducimus ut dolore. Magni voluptatibus eius et maxime aut.\n\nAd officiis tempore voluptate vitae corrupti explicabo labore est. Consequatur expedita et sunt nihil aut. Deleniti porro iusto molestiae et beatae.\n\nDeleniti modi nulla qui et labore sequi corrupti. Qui voluptatem assumenda eum cupiditate et. Nesciunt ipsam ut ea possimus eum. Consectetur quidem suscipit atque dolore itaque voluptatibus et cupiditate.", + "created_at": "2016-03-22T15:20:35.777Z", + "updated_at": "2016-03-22T15:20:35.777Z", + "started_at": null, + "runner_id": null, + "coverage": null, + "commit_id": 36, + "commands": "$ deploy command", + "job_id": null, + "name": "test build 2", + "deploy": false, + "options": null, + "allow_failure": false, + "stage": "deploy", + "trigger_request_id": null, + "stage_idx": 1, + "stage_id": 12, + "tag": null, + "ref": "master", + "user_id": null, + "target_url": null, + "description": null, + "artifacts_file": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts.zip" + }, + "artifacts_metadata": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts_metadata.gz" + }, + "erased_by_id": null, + "erased_at": null + } + ] + }, + { + "id": 12, + "project_id": 5, + "pipeline_id": 36, + "name": "deploy", + "status": 2, + "created_at": "2016-03-22T15:45:45.772Z", + "updated_at": "2016-03-29T06:45:45.634Z" } ] }, @@ -6556,76 +6578,87 @@ "started_at": null, "finished_at": null, "duration": null, - "statuses": [ - { - "id": 74, - "project_id": 5, - "status": "success", - "finished_at": null, - "trace": "Ad ut quod repudiandae iste dolor doloribus. Adipisci consequuntur deserunt omnis quasi eveniet et sed fugit. Aut nemo omnis molestiae impedit ex consequatur ducimus. Voluptatum exercitationem quia aut est et hic dolorem.\n\nQuasi repellendus et eaque magni eum facilis. Dolorem aperiam nam nihil pariatur praesentium ad aliquam. Commodi enim et eos tenetur. Odio voluptatibus laboriosam mollitia rerum exercitationem magnam consequuntur. Tenetur ea vel eum corporis.\n\nVoluptatibus optio in aliquid est voluptates. Ad a ut ab placeat vero blanditiis. Earum aspernatur quia beatae expedita voluptatem dignissimos provident. Quis minima id nemo ut aut est veritatis provident.\n\nRerum voluptatem quidem eius maiores magnam veniam. Voluptatem aperiam aut voluptate et nulla deserunt voluptas. Quaerat aut accusantium laborum est dolorem architecto reiciendis. Aliquam asperiores doloribus omnis maxime enim nesciunt. Eum aut rerum repellendus debitis et ut eius.\n\nQuaerat assumenda ea sit consequatur autem in. Cum eligendi voluptatem quo sed. Ut fuga iusto cupiditate autem sint.\n\nOfficia totam officiis architecto corporis molestiae amet ut. Tempora sed dolorum rerum omnis voluptatem accusantium sit eum. Quia debitis ipsum quidem aliquam inventore sunt consequatur qui.", - "created_at": "2016-03-22T15:20:35.846Z", - "updated_at": "2016-03-22T15:20:35.846Z", - "started_at": null, - "runner_id": null, - "coverage": null, - "commit_id": 37, - "commands": "$ build command", - "job_id": null, - "name": "test build 2", - "deploy": false, - "options": null, - "allow_failure": false, - "stage": "test", - "trigger_request_id": null, - "stage_idx": 1, - "tag": null, - "ref": "master", - "user_id": null, - "target_url": null, - "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts_metadata.gz" - }, - "erased_by_id": null, - "erased_at": null - }, - { - "id": 73, - "project_id": 5, - "status": "canceled", - "finished_at": null, - "trace": null, - "created_at": "2016-03-22T15:20:35.842Z", - "updated_at": "2016-03-22T15:20:35.842Z", - "started_at": null, - "runner_id": null, - "coverage": null, - "commit_id": 37, - "commands": "$ build command", - "job_id": null, - "name": "test build 1", - "deploy": false, - "options": null, - "allow_failure": false, - "stage": "test", - "trigger_request_id": null, - "stage_idx": 1, - "tag": null, - "ref": "master", - "user_id": null, - "target_url": null, - "description": null, - "artifacts_file": { - "url": null - }, - "artifacts_metadata": { - "url": null - }, - "erased_by_id": null, - "erased_at": null + "stages": [ + { + "id": 21, + "project_id": 5, + "pipeline_id": 37, + "name": "test", + "status": 1, + "created_at": "2016-03-22T15:44:44.772Z", + "updated_at": "2016-03-29T06:44:44.634Z", + "statuses": [ + { + "id": 74, + "project_id": 5, + "status": "success", + "finished_at": null, + "trace": "Ad ut quod repudiandae iste dolor doloribus. Adipisci consequuntur deserunt omnis quasi eveniet et sed fugit. Aut nemo omnis molestiae impedit ex consequatur ducimus. Voluptatum exercitationem quia aut est et hic dolorem.\n\nQuasi repellendus et eaque magni eum facilis. Dolorem aperiam nam nihil pariatur praesentium ad aliquam. Commodi enim et eos tenetur. Odio voluptatibus laboriosam mollitia rerum exercitationem magnam consequuntur. Tenetur ea vel eum corporis.\n\nVoluptatibus optio in aliquid est voluptates. Ad a ut ab placeat vero blanditiis. Earum aspernatur quia beatae expedita voluptatem dignissimos provident. Quis minima id nemo ut aut est veritatis provident.\n\nRerum voluptatem quidem eius maiores magnam veniam. Voluptatem aperiam aut voluptate et nulla deserunt voluptas. Quaerat aut accusantium laborum est dolorem architecto reiciendis. Aliquam asperiores doloribus omnis maxime enim nesciunt. Eum aut rerum repellendus debitis et ut eius.\n\nQuaerat assumenda ea sit consequatur autem in. Cum eligendi voluptatem quo sed. Ut fuga iusto cupiditate autem sint.\n\nOfficia totam officiis architecto corporis molestiae amet ut. Tempora sed dolorum rerum omnis voluptatem accusantium sit eum. Quia debitis ipsum quidem aliquam inventore sunt consequatur qui.", + "created_at": "2016-03-22T15:20:35.846Z", + "updated_at": "2016-03-22T15:20:35.846Z", + "started_at": null, + "runner_id": null, + "coverage": null, + "commit_id": 37, + "commands": "$ build command", + "job_id": null, + "name": "test build 2", + "deploy": false, + "options": null, + "allow_failure": false, + "stage": "test", + "trigger_request_id": null, + "stage_idx": 1, + "tag": null, + "ref": "master", + "user_id": null, + "target_url": null, + "description": null, + "artifacts_file": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts.zip" + }, + "artifacts_metadata": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts_metadata.gz" + }, + "erased_by_id": null, + "erased_at": null + }, + { + "id": 73, + "project_id": 5, + "status": "canceled", + "finished_at": null, + "trace": null, + "created_at": "2016-03-22T15:20:35.842Z", + "updated_at": "2016-03-22T15:20:35.842Z", + "started_at": null, + "runner_id": null, + "coverage": null, + "commit_id": 37, + "commands": "$ build command", + "job_id": null, + "name": "test build 1", + "deploy": false, + "options": null, + "allow_failure": false, + "stage": "test", + "trigger_request_id": null, + "stage_idx": 1, + "tag": null, + "ref": "master", + "user_id": null, + "target_url": null, + "description": null, + "artifacts_file": { + "url": null + }, + "artifacts_metadata": { + "url": null + }, + "erased_by_id": null, + "erased_at": null + } + ] } ] }, @@ -6645,76 +6678,87 @@ "started_at": null, "finished_at": null, "duration": null, - "statuses": [ - { - "id": 76, - "project_id": 5, - "status": "success", - "finished_at": null, - "trace": "Et rerum quia ea cumque ut modi non. Libero eaque ipsam architecto maiores expedita deleniti. Ratione quia qui est id.\n\nQuod sit officiis sed unde inventore veniam quisquam velit. Ea harum cum quibusdam quisquam minima quo possimus non. Temporibus itaque aliquam aut rerum veritatis at.\n\nMagnam ipsum eius recusandae qui quis sit maiores eum. Et animi iusto aut itaque. Doloribus harum deleniti nobis accusantium et libero.\n\nRerum fuga perferendis magni commodi officiis id repudiandae. Consequatur ratione consequatur suscipit facilis sunt iure est dicta. Qui unde quasi facilis et quae nesciunt. Magnam iste et nobis officiis tenetur. Aspernatur quo et temporibus non in.\n\nNisi rerum velit est ad enim sint molestiae consequuntur. Quaerat nisi nesciunt quasi officiis. Possimus non blanditiis laborum quos.\n\nRerum laudantium facere animi qui. Ipsa est iusto magnam nihil. Enim omnis occaecati non dignissimos ut recusandae eum quasi. Qui maxime dolor et nemo voluptates incidunt quia.", - "created_at": "2016-03-22T15:20:35.882Z", - "updated_at": "2016-03-22T15:20:35.882Z", - "started_at": null, - "runner_id": null, - "coverage": null, - "commit_id": 38, - "commands": "$ build command", - "job_id": null, - "name": "test build 2", - "deploy": false, - "options": null, - "allow_failure": false, - "stage": "test", - "trigger_request_id": null, - "stage_idx": 1, - "tag": null, - "ref": "master", - "user_id": null, - "target_url": null, - "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts_metadata.gz" - }, - "erased_by_id": null, - "erased_at": null - }, - { - "id": 75, - "project_id": 5, - "status": "failed", - "finished_at": null, - "trace": "Sed et iste recusandae dicta corporis. Sunt alias porro fugit sunt. Fugiat omnis nihil dignissimos aperiam explicabo doloremque sit aut. Harum fugit expedita quia rerum ut consequatur laboriosam aliquam.\n\nNatus libero ut ut tenetur earum. Tempora omnis autem omnis et libero dolores illum autem. Deleniti eos sunt mollitia ipsam. Cum dolor repellendus dolorum sequi officia. Ullam sunt in aut pariatur excepturi.\n\nDolor nihil debitis et est eos. Cumque eos eum saepe ducimus autem. Alias architecto consequatur aut pariatur possimus. Aut quos aut incidunt quam velit et. Quas voluptatum ad dolorum dignissimos.\n\nUt voluptates consectetur illo et. Est commodi accusantium vel quo. Eos qui fugiat soluta porro.\n\nRatione possimus alias vel maxime sint totam est repellat. Ipsum corporis eos sint voluptatem eos odit. Temporibus libero nulla harum eligendi labore similique ratione magnam. Suscipit sequi in omnis neque.\n\nLaudantium dolor amet omnis placeat mollitia aut molestiae. Aut rerum similique ipsum quod illo quas unde. Sunt aut veritatis eos omnis porro. Rem veritatis mollitia praesentium dolorem. Consequatur sequi ad cumque earum omnis quia necessitatibus.", - "created_at": "2016-03-22T15:20:35.864Z", - "updated_at": "2016-03-22T15:20:35.864Z", - "started_at": null, - "runner_id": null, - "coverage": null, - "commit_id": 38, - "commands": "$ build command", - "job_id": null, - "name": "test build 1", - "deploy": false, - "options": null, - "allow_failure": false, - "stage": "test", - "trigger_request_id": null, - "stage_idx": 1, - "tag": null, - "ref": "master", - "user_id": null, - "target_url": null, - "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts_metadata.gz" - }, - "erased_by_id": null, - "erased_at": null + "stages": [ + { + "id": 22, + "project_id": 5, + "pipeline_id": 38, + "name": "test", + "status": 1, + "created_at": "2016-03-22T15:44:44.772Z", + "updated_at": "2016-03-29T06:44:44.634Z", + "statuses": [ + { + "id": 76, + "project_id": 5, + "status": "success", + "finished_at": null, + "trace": "Et rerum quia ea cumque ut modi non. Libero eaque ipsam architecto maiores expedita deleniti. Ratione quia qui est id.\n\nQuod sit officiis sed unde inventore veniam quisquam velit. Ea harum cum quibusdam quisquam minima quo possimus non. Temporibus itaque aliquam aut rerum veritatis at.\n\nMagnam ipsum eius recusandae qui quis sit maiores eum. Et animi iusto aut itaque. Doloribus harum deleniti nobis accusantium et libero.\n\nRerum fuga perferendis magni commodi officiis id repudiandae. Consequatur ratione consequatur suscipit facilis sunt iure est dicta. Qui unde quasi facilis et quae nesciunt. Magnam iste et nobis officiis tenetur. Aspernatur quo et temporibus non in.\n\nNisi rerum velit est ad enim sint molestiae consequuntur. Quaerat nisi nesciunt quasi officiis. Possimus non blanditiis laborum quos.\n\nRerum laudantium facere animi qui. Ipsa est iusto magnam nihil. Enim omnis occaecati non dignissimos ut recusandae eum quasi. Qui maxime dolor et nemo voluptates incidunt quia.", + "created_at": "2016-03-22T15:20:35.882Z", + "updated_at": "2016-03-22T15:20:35.882Z", + "started_at": null, + "runner_id": null, + "coverage": null, + "commit_id": 38, + "commands": "$ build command", + "job_id": null, + "name": "test build 2", + "deploy": false, + "options": null, + "allow_failure": false, + "stage": "test", + "trigger_request_id": null, + "stage_idx": 1, + "tag": null, + "ref": "master", + "user_id": null, + "target_url": null, + "description": null, + "artifacts_file": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts.zip" + }, + "artifacts_metadata": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts_metadata.gz" + }, + "erased_by_id": null, + "erased_at": null + }, + { + "id": 75, + "project_id": 5, + "status": "failed", + "finished_at": null, + "trace": "Sed et iste recusandae dicta corporis. Sunt alias porro fugit sunt. Fugiat omnis nihil dignissimos aperiam explicabo doloremque sit aut. Harum fugit expedita quia rerum ut consequatur laboriosam aliquam.\n\nNatus libero ut ut tenetur earum. Tempora omnis autem omnis et libero dolores illum autem. Deleniti eos sunt mollitia ipsam. Cum dolor repellendus dolorum sequi officia. Ullam sunt in aut pariatur excepturi.\n\nDolor nihil debitis et est eos. Cumque eos eum saepe ducimus autem. Alias architecto consequatur aut pariatur possimus. Aut quos aut incidunt quam velit et. Quas voluptatum ad dolorum dignissimos.\n\nUt voluptates consectetur illo et. Est commodi accusantium vel quo. Eos qui fugiat soluta porro.\n\nRatione possimus alias vel maxime sint totam est repellat. Ipsum corporis eos sint voluptatem eos odit. Temporibus libero nulla harum eligendi labore similique ratione magnam. Suscipit sequi in omnis neque.\n\nLaudantium dolor amet omnis placeat mollitia aut molestiae. Aut rerum similique ipsum quod illo quas unde. Sunt aut veritatis eos omnis porro. Rem veritatis mollitia praesentium dolorem. Consequatur sequi ad cumque earum omnis quia necessitatibus.", + "created_at": "2016-03-22T15:20:35.864Z", + "updated_at": "2016-03-22T15:20:35.864Z", + "started_at": null, + "runner_id": null, + "coverage": null, + "commit_id": 38, + "commands": "$ build command", + "job_id": null, + "name": "test build 1", + "deploy": false, + "options": null, + "allow_failure": false, + "stage": "test", + "trigger_request_id": null, + "stage_idx": 1, + "tag": null, + "ref": "master", + "user_id": null, + "target_url": null, + "description": null, + "artifacts_file": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts.zip" + }, + "artifacts_metadata": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts_metadata.gz" + }, + "erased_by_id": null, + "erased_at": null + } + ] } ] }, @@ -6734,76 +6778,87 @@ "started_at": null, "finished_at": null, "duration": null, - "statuses": [ - { - "id": 78, - "project_id": 5, - "status": "success", - "finished_at": null, - "trace": "Dolorem deserunt quas quia error hic quo cum vel. Natus voluptatem cumque expedita numquam odit. Eos expedita nostrum corporis consequatur est recusandae.\n\nCulpa blanditiis rerum repudiandae alias voluptatem. Velit iusto est ullam consequatur doloribus porro. Corporis voluptas consectetur est veniam et quia quae.\n\nEt aut magni fuga nesciunt officiis molestias. Quaerat et nam necessitatibus qui rerum. Architecto quia officiis voluptatem laborum est recusandae. Quasi ducimus soluta odit necessitatibus labore numquam dignissimos. Quia facere sint temporibus inventore sunt nihil saepe dolorum.\n\nFacere dolores quis dolores a. Est minus nostrum nihil harum. Earum laborum et ipsum unde neque sit nemo. Corrupti est consequatur minima fugit. Illum voluptatem illo error ducimus officia qui debitis.\n\nDignissimos porro a autem harum aut. Aut id reprehenderit et exercitationem. Est et quisquam ipsa temporibus molestiae. Architecto natus dolore qui fugiat incidunt. Autem odit veniam excepturi et voluptatibus culpa ipsum eos.\n\nAmet quo quisquam dignissimos soluta modi dolores. Sint omnis eius optio corporis dolor. Eligendi animi porro quia placeat ut.", - "created_at": "2016-03-22T15:20:35.927Z", - "updated_at": "2016-03-22T15:20:35.927Z", - "started_at": null, - "runner_id": null, - "coverage": null, - "commit_id": 39, - "commands": "$ build command", - "job_id": null, - "name": "test build 2", - "deploy": false, - "options": null, - "allow_failure": false, - "stage": "test", - "trigger_request_id": null, - "stage_idx": 1, - "tag": null, - "ref": "master", - "user_id": null, - "target_url": null, - "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts_metadata.gz" - }, - "erased_by_id": null, - "erased_at": null - }, - { - "id": 77, - "project_id": 5, - "status": "failed", - "finished_at": null, - "trace": "Rerum ut et suscipit est perspiciatis. Inventore debitis cum eius vitae. Ex incidunt id velit aut quo nisi. Laboriosam repellat deserunt eius reiciendis architecto et. Est harum quos nesciunt nisi consectetur.\n\nAlias esse omnis sint officia est consequatur in nobis. Dignissimos dolorum vel eligendi nesciunt dolores sit. Veniam mollitia ducimus et exercitationem molestiae libero sed. Atque omnis debitis laudantium voluptatibus qui. Repellendus tempore est commodi pariatur.\n\nExpedita voluptate illum est alias non. Modi nesciunt ab assumenda laborum nulla consequatur molestias doloremque. Magnam quod officia vel explicabo accusamus ut voluptatem incidunt. Rerum ut aliquid ullam saepe. Est eligendi debitis beatae blanditiis reiciendis.\n\nQui fuga sit dolores libero maiores et suscipit. Consectetur asperiores omnis minima impedit eos fugiat. Similique omnis nisi sed vero inventore ipsum aliquam exercitationem.\n\nBlanditiis magni iure dolorum omnis ratione delectus molestiae. Atque officia dolor voluptatem culpa quod. Incidunt suscipit quidem possimus veritatis non vel. Iusto aliquid et id quia quasi.\n\nVel facere velit blanditiis incidunt cupiditate sed maiores consequuntur. Quasi quia dicta consequuntur et quia voluptatem iste id. Incidunt et rerum fuga esse sint.", - "created_at": "2016-03-22T15:20:35.905Z", - "updated_at": "2016-03-22T15:20:35.905Z", - "started_at": null, - "runner_id": null, - "coverage": null, - "commit_id": 39, - "commands": "$ build command", - "job_id": null, - "name": "test build 1", - "deploy": false, - "options": null, - "allow_failure": false, - "stage": "test", - "trigger_request_id": null, - "stage_idx": 1, - "tag": null, - "ref": "master", - "user_id": null, - "target_url": null, - "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts_metadata.gz" - }, - "erased_by_id": null, - "erased_at": null + "stages": [ + { + "id": 23, + "project_id": 5, + "pipeline_id": 39, + "name": "test", + "status": 1, + "created_at": "2016-03-22T15:44:44.772Z", + "updated_at": "2016-03-29T06:44:44.634Z", + "statuses": [ + { + "id": 78, + "project_id": 5, + "status": "success", + "finished_at": null, + "trace": "Dolorem deserunt quas quia error hic quo cum vel. Natus voluptatem cumque expedita numquam odit. Eos expedita nostrum corporis consequatur est recusandae.\n\nCulpa blanditiis rerum repudiandae alias voluptatem. Velit iusto est ullam consequatur doloribus porro. Corporis voluptas consectetur est veniam et quia quae.\n\nEt aut magni fuga nesciunt officiis molestias. Quaerat et nam necessitatibus qui rerum. Architecto quia officiis voluptatem laborum est recusandae. Quasi ducimus soluta odit necessitatibus labore numquam dignissimos. Quia facere sint temporibus inventore sunt nihil saepe dolorum.\n\nFacere dolores quis dolores a. Est minus nostrum nihil harum. Earum laborum et ipsum unde neque sit nemo. Corrupti est consequatur minima fugit. Illum voluptatem illo error ducimus officia qui debitis.\n\nDignissimos porro a autem harum aut. Aut id reprehenderit et exercitationem. Est et quisquam ipsa temporibus molestiae. Architecto natus dolore qui fugiat incidunt. Autem odit veniam excepturi et voluptatibus culpa ipsum eos.\n\nAmet quo quisquam dignissimos soluta modi dolores. Sint omnis eius optio corporis dolor. Eligendi animi porro quia placeat ut.", + "created_at": "2016-03-22T15:20:35.927Z", + "updated_at": "2016-03-22T15:20:35.927Z", + "started_at": null, + "runner_id": null, + "coverage": null, + "commit_id": 39, + "commands": "$ build command", + "job_id": null, + "name": "test build 2", + "deploy": false, + "options": null, + "allow_failure": false, + "stage": "test", + "trigger_request_id": null, + "stage_idx": 1, + "tag": null, + "ref": "master", + "user_id": null, + "target_url": null, + "description": null, + "artifacts_file": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts.zip" + }, + "artifacts_metadata": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts_metadata.gz" + }, + "erased_by_id": null, + "erased_at": null + }, + { + "id": 77, + "project_id": 5, + "status": "failed", + "finished_at": null, + "trace": "Rerum ut et suscipit est perspiciatis. Inventore debitis cum eius vitae. Ex incidunt id velit aut quo nisi. Laboriosam repellat deserunt eius reiciendis architecto et. Est harum quos nesciunt nisi consectetur.\n\nAlias esse omnis sint officia est consequatur in nobis. Dignissimos dolorum vel eligendi nesciunt dolores sit. Veniam mollitia ducimus et exercitationem molestiae libero sed. Atque omnis debitis laudantium voluptatibus qui. Repellendus tempore est commodi pariatur.\n\nExpedita voluptate illum est alias non. Modi nesciunt ab assumenda laborum nulla consequatur molestias doloremque. Magnam quod officia vel explicabo accusamus ut voluptatem incidunt. Rerum ut aliquid ullam saepe. Est eligendi debitis beatae blanditiis reiciendis.\n\nQui fuga sit dolores libero maiores et suscipit. Consectetur asperiores omnis minima impedit eos fugiat. Similique omnis nisi sed vero inventore ipsum aliquam exercitationem.\n\nBlanditiis magni iure dolorum omnis ratione delectus molestiae. Atque officia dolor voluptatem culpa quod. Incidunt suscipit quidem possimus veritatis non vel. Iusto aliquid et id quia quasi.\n\nVel facere velit blanditiis incidunt cupiditate sed maiores consequuntur. Quasi quia dicta consequuntur et quia voluptatem iste id. Incidunt et rerum fuga esse sint.", + "created_at": "2016-03-22T15:20:35.905Z", + "updated_at": "2016-03-22T15:20:35.905Z", + "started_at": null, + "runner_id": null, + "coverage": null, + "commit_id": 39, + "commands": "$ build command", + "job_id": null, + "name": "test build 1", + "deploy": false, + "options": null, + "allow_failure": false, + "stage": "test", + "trigger_request_id": null, + "stage_idx": 1, + "tag": null, + "ref": "master", + "user_id": null, + "target_url": null, + "description": null, + "artifacts_file": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts.zip" + }, + "artifacts_metadata": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts_metadata.gz" + }, + "erased_by_id": null, + "erased_at": null + } + ] } ] }, @@ -6823,76 +6878,87 @@ "started_at": null, "finished_at": null, "duration": null, - "statuses": [ - { - "id": 79, - "project_id": 5, - "status": "failed", - "finished_at": "2016-03-29T06:28:12.695Z", - "trace": "Sed culpa est et facere saepe vel id ab. Quas temporibus aut similique dolorem consequatur corporis aut praesentium. Cum officia molestiae sit earum excepturi.\n\nSint possimus aut ratione quia. Quis nesciunt ratione itaque illo. Tenetur est dolor assumenda possimus voluptatem quia minima. Accusamus reprehenderit ut et itaque non reiciendis incidunt.\n\nRerum suscipit quibusdam dolore nam omnis. Consequatur ipsa nihil ut enim blanditiis delectus. Nulla quis hic occaecati mollitia qui placeat. Quo rerum sed perferendis a accusantium consequatur commodi ut. Sit quae et cumque vel eius tempora nostrum.\n\nUllam dolorem et itaque sint est. Ea molestias quia provident dolorem vitae error et et. Ea expedita officiis iste non. Qui vitae odit saepe illum. Dolores enim ratione deserunt tempore expedita amet non neque.\n\nEligendi asperiores voluptatibus omnis repudiandae expedita distinctio qui aliquid. Autem aut doloremque distinctio ab. Nostrum sapiente repudiandae aspernatur ea et quae voluptas. Officiis perspiciatis nisi laudantium asperiores error eligendi ab. Eius quia amet magni omnis exercitationem voluptatum et.\n\nVoluptatem ullam labore quas dicta est ex voluptas. Pariatur ea modi voluptas consequatur dolores perspiciatis similique. Numquam in distinctio perspiciatis ut qui earum. Quidem omnis mollitia facere aut beatae. Ea est iure et voluptatem.", - "created_at": "2016-03-22T15:20:35.950Z", - "updated_at": "2016-03-29T06:28:12.696Z", - "started_at": null, - "runner_id": null, - "coverage": null, - "commit_id": 40, - "commands": "$ build command", - "job_id": null, - "name": "test build 1", - "deploy": false, - "options": null, - "allow_failure": false, - "stage": "test", - "trigger_request_id": null, - "stage_idx": 1, - "tag": null, - "ref": "master", - "user_id": null, - "target_url": null, - "description": null, - "artifacts_file": { - "url": null - }, - "artifacts_metadata": { - "url": null - }, - "erased_by_id": null, - "erased_at": null - }, - { - "id": 80, - "project_id": 5, - "status": "success", - "finished_at": null, - "trace": "Impedit et optio nemo ipsa. Non ad non quis ut sequi laudantium omnis velit. Corporis a enim illo eos. Quia totam tempore inventore ad est.\n\nNihil recusandae cupiditate eaque voluptatem molestias sint. Consequatur id voluptatem cupiditate harum. Consequuntur iusto quaerat reiciendis aut autem libero est. Quisquam dolores veritatis rerum et sint maxime ullam libero. Id quas porro ut perspiciatis rem amet vitae.\n\nNemo inventore minus blanditiis magnam. Modi consequuntur nostrum aut voluptatem ex. Sunt rerum rem optio mollitia qui aliquam officiis officia. Aliquid eos et id aut minus beatae reiciendis.\n\nDolores non in temporibus dicta. Fugiat voluptatem est aspernatur expedita voluptatum nam qui. Quia et eligendi sit quae sint tempore exercitationem eos. Est sapiente corrupti quidem at. Qui magni odio repudiandae saepe tenetur optio dolore.\n\nEos placeat soluta at dolorem adipisci provident. Quo commodi id reprehenderit possimus quo tenetur. Ipsum et quae eligendi laborum. Et qui nesciunt at quasi quidem voluptatem cum rerum. Excepturi non facilis aut sunt vero sed.\n\nQui explicabo ratione ut eligendi recusandae. Quis quasi quas molestiae consequatur voluptatem et voluptatem. Ex repellat saepe occaecati aperiam ea eveniet dignissimos facilis.", - "created_at": "2016-03-22T15:20:35.966Z", - "updated_at": "2016-03-22T15:20:35.966Z", - "started_at": null, - "runner_id": null, - "coverage": null, - "commit_id": 40, - "commands": "$ build command", - "job_id": null, - "name": "test build 2", - "deploy": false, - "options": null, - "allow_failure": false, - "stage": "test", - "trigger_request_id": null, - "stage_idx": 1, - "tag": null, - "ref": "master", - "user_id": null, - "target_url": null, - "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts_metadata.gz" - }, - "erased_by_id": null, - "erased_at": null + "stages": [ + { + "id": 24, + "project_id": 5, + "pipeline_id": 40, + "name": "test", + "status": 1, + "created_at": "2016-03-22T15:44:44.772Z", + "updated_at": "2016-03-29T06:44:44.634Z", + "statuses": [ + { + "id": 79, + "project_id": 5, + "status": "failed", + "finished_at": "2016-03-29T06:28:12.695Z", + "trace": "Sed culpa est et facere saepe vel id ab. Quas temporibus aut similique dolorem consequatur corporis aut praesentium. Cum officia molestiae sit earum excepturi.\n\nSint possimus aut ratione quia. Quis nesciunt ratione itaque illo. Tenetur est dolor assumenda possimus voluptatem quia minima. Accusamus reprehenderit ut et itaque non reiciendis incidunt.\n\nRerum suscipit quibusdam dolore nam omnis. Consequatur ipsa nihil ut enim blanditiis delectus. Nulla quis hic occaecati mollitia qui placeat. Quo rerum sed perferendis a accusantium consequatur commodi ut. Sit quae et cumque vel eius tempora nostrum.\n\nUllam dolorem et itaque sint est. Ea molestias quia provident dolorem vitae error et et. Ea expedita officiis iste non. Qui vitae odit saepe illum. Dolores enim ratione deserunt tempore expedita amet non neque.\n\nEligendi asperiores voluptatibus omnis repudiandae expedita distinctio qui aliquid. Autem aut doloremque distinctio ab. Nostrum sapiente repudiandae aspernatur ea et quae voluptas. Officiis perspiciatis nisi laudantium asperiores error eligendi ab. Eius quia amet magni omnis exercitationem voluptatum et.\n\nVoluptatem ullam labore quas dicta est ex voluptas. Pariatur ea modi voluptas consequatur dolores perspiciatis similique. Numquam in distinctio perspiciatis ut qui earum. Quidem omnis mollitia facere aut beatae. Ea est iure et voluptatem.", + "created_at": "2016-03-22T15:20:35.950Z", + "updated_at": "2016-03-29T06:28:12.696Z", + "started_at": null, + "runner_id": null, + "coverage": null, + "commit_id": 40, + "commands": "$ build command", + "job_id": null, + "name": "test build 1", + "deploy": false, + "options": null, + "allow_failure": false, + "stage": "test", + "trigger_request_id": null, + "stage_idx": 1, + "tag": null, + "ref": "master", + "user_id": null, + "target_url": null, + "description": null, + "artifacts_file": { + "url": null + }, + "artifacts_metadata": { + "url": null + }, + "erased_by_id": null, + "erased_at": null + }, + { + "id": 80, + "project_id": 5, + "status": "success", + "finished_at": null, + "trace": "Impedit et optio nemo ipsa. Non ad non quis ut sequi laudantium omnis velit. Corporis a enim illo eos. Quia totam tempore inventore ad est.\n\nNihil recusandae cupiditate eaque voluptatem molestias sint. Consequatur id voluptatem cupiditate harum. Consequuntur iusto quaerat reiciendis aut autem libero est. Quisquam dolores veritatis rerum et sint maxime ullam libero. Id quas porro ut perspiciatis rem amet vitae.\n\nNemo inventore minus blanditiis magnam. Modi consequuntur nostrum aut voluptatem ex. Sunt rerum rem optio mollitia qui aliquam officiis officia. Aliquid eos et id aut minus beatae reiciendis.\n\nDolores non in temporibus dicta. Fugiat voluptatem est aspernatur expedita voluptatum nam qui. Quia et eligendi sit quae sint tempore exercitationem eos. Est sapiente corrupti quidem at. Qui magni odio repudiandae saepe tenetur optio dolore.\n\nEos placeat soluta at dolorem adipisci provident. Quo commodi id reprehenderit possimus quo tenetur. Ipsum et quae eligendi laborum. Et qui nesciunt at quasi quidem voluptatem cum rerum. Excepturi non facilis aut sunt vero sed.\n\nQui explicabo ratione ut eligendi recusandae. Quis quasi quas molestiae consequatur voluptatem et voluptatem. Ex repellat saepe occaecati aperiam ea eveniet dignissimos facilis.", + "created_at": "2016-03-22T15:20:35.966Z", + "updated_at": "2016-03-22T15:20:35.966Z", + "started_at": null, + "runner_id": null, + "coverage": null, + "commit_id": 40, + "commands": "$ build command", + "job_id": null, + "name": "test build 2", + "deploy": false, + "options": null, + "allow_failure": false, + "stage": "test", + "trigger_request_id": null, + "stage_idx": 1, + "tag": null, + "ref": "master", + "user_id": null, + "target_url": null, + "description": null, + "artifacts_file": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts.zip" + }, + "artifacts_metadata": { + "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts_metadata.gz" + }, + "erased_by_id": null, + "erased_at": null + } + ] } ] } 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 0ab3afd0074..9dfd879a1bc 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -179,6 +179,32 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do end end end + + context 'when restoring hierarchy of pipeline, stages and jobs' do + it 'restores pipelines' do + expect(Ci::Pipeline.all.count).to be 5 + end + + it 'restores pipeline stages' do + expect(Ci::Stage.all.count).to be 6 + end + + it 'correctly restores association between stage and a pipeline' do + expect(Ci::Stage.all).to all(have_attributes(pipeline_id: a_value > 0)) + end + + it 'restores statuses' do + expect(CommitStatus.all.count).to be 10 + end + + it 'correctly restores association between a stage and a job' do + expect(CommitStatus.all).to all(have_attributes(stage_id: a_value > 0)) + end + + it 'correctly restores association between a pipeline and a job' do + expect(CommitStatus.all).to all(have_attributes(pipeline_id: a_value > 0)) + end + end end end 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 6243b6ac9f0..08e5bbbd400 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver do let!(:project) { setup_project } before do - project.team << [user, :master] + project.add_master(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') @@ -109,12 +109,20 @@ describe Gitlab::ImportExport::ProjectTreeSaver do expect(saved_project_json['merge_requests'].first['notes'].first['author']).not_to be_empty end + it 'has pipeline stages' do + expect(saved_project_json.dig('pipelines', 0, 'stages')).not_to be_empty + end + it 'has pipeline statuses' do - expect(saved_project_json['pipelines'].first['statuses']).not_to be_empty + expect(saved_project_json.dig('pipelines', 0, 'stages', 0, 'statuses')).not_to be_empty end it 'has pipeline builds' do - expect(saved_project_json['pipelines'].first['statuses'].count { |hash| hash['type'] == 'Ci::Build' }).to eq(1) + builds_count = saved_project_json + .dig('pipelines', 0, 'stages', 0, 'statuses') + .count { |hash| hash['type'] == 'Ci::Build' } + + expect(builds_count).to eq(1) end it 'has no when YML attributes but only the DB column' do diff --git a/spec/lib/gitlab/import_export/repo_saver_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb index e6ad516deef..44f972fe530 100644 --- a/spec/lib/gitlab/import_export/repo_saver_spec.rb +++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::ImportExport::RepoSaver do let(:bundler) { described_class.new(project: project, shared: shared) } before do - project.team << [user, :master] + project.add_master(user) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) end diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index ec8fa99e0da..ec577903eb5 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -459,6 +459,7 @@ Project: - delete_error - merge_requests_ff_only_enabled - merge_requests_rebase_enabled +- jobs_cache_index Author: - name ProjectFeature: diff --git a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb index 0e55993c8ef..1d1e7e7f89a 100644 --- a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb +++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb @@ -10,7 +10,7 @@ describe Gitlab::ImportExport::WikiRepoSaver do let!(:project_wiki) { ProjectWiki.new(project, user) } before do - project.team << [user, :master] + project.add_master(user) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) project_wiki.wiki project_wiki.create_page("index", "test content") diff --git a/spec/lib/gitlab/kubernetes/helm_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb index 15f99b0401f..69112fe90b1 100644 --- a/spec/lib/gitlab/kubernetes/helm_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb @@ -1,22 +1,23 @@ require 'spec_helper' -describe Gitlab::Kubernetes::Helm do +describe Gitlab::Kubernetes::Helm::Api do let(:client) { double('kubernetes client') } let(:helm) { described_class.new(client) } - let(:namespace) { Gitlab::Kubernetes::Namespace.new(described_class::NAMESPACE, client) } + let(:gitlab_namespace) { Gitlab::Kubernetes::Helm::NAMESPACE } + let(:namespace) { Gitlab::Kubernetes::Namespace.new(gitlab_namespace, client) } let(:install_helm) { true } let(:chart) { 'stable/a_chart' } let(:application_name) { 'app_name' } - let(:command) { Gitlab::Kubernetes::Helm::InstallCommand.new(application_name, install_helm, chart) } + let(:command) { Gitlab::Kubernetes::Helm::InstallCommand.new(application_name, install_helm: install_helm, chart: chart) } subject { helm } before do - allow(Gitlab::Kubernetes::Namespace).to receive(:new).with(described_class::NAMESPACE, client).and_return(namespace) + allow(Gitlab::Kubernetes::Namespace).to receive(:new).with(gitlab_namespace, client).and_return(namespace) end describe '#initialize' do it 'creates a namespace object' do - expect(Gitlab::Kubernetes::Namespace).to receive(:new).with(described_class::NAMESPACE, client) + expect(Gitlab::Kubernetes::Namespace).to receive(:new).with(gitlab_namespace, client) subject end @@ -41,7 +42,7 @@ describe Gitlab::Kubernetes::Helm do let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation it 'fetches POD phase from kubernetes cluster' do - expect(client).to receive(:get_pod).with(command.pod_name, described_class::NAMESPACE).once.and_return(pod) + expect(client).to receive(:get_pod).with(command.pod_name, gitlab_namespace).once.and_return(pod) expect(subject.installation_status(command.pod_name)).to eq(phase) end @@ -52,7 +53,7 @@ describe Gitlab::Kubernetes::Helm do let(:response) { RestClient::Response.new(log) } it 'fetches POD phase from kubernetes cluster' do - expect(client).to receive(:get_pod_log).with(command.pod_name, described_class::NAMESPACE).once.and_return(response) + expect(client).to receive(:get_pod_log).with(command.pod_name, gitlab_namespace).once.and_return(response) expect(subject.installation_log(command.pod_name)).to eq(log) end @@ -60,41 +61,9 @@ describe Gitlab::Kubernetes::Helm do describe '#delete_installation_pod!' do it 'deletes the POD from kubernetes cluster' do - expect(client).to receive(:delete_pod).with(command.pod_name, described_class::NAMESPACE).once + expect(client).to receive(:delete_pod).with(command.pod_name, gitlab_namespace).once subject.delete_installation_pod!(command.pod_name) end end - - describe '#helm_init_command' do - subject { helm.send(:helm_init_command, command) } - - context 'when command.install_helm is true' do - let(:install_helm) { true } - - it { is_expected.to eq('helm init >/dev/null') } - end - - context 'when command.install_helm is false' do - let(:install_helm) { false } - - it { is_expected.to eq('helm init --client-only >/dev/null') } - end - end - - describe '#helm_install_command' do - subject { helm.send(:helm_install_command, command) } - - context 'when command.chart is nil' do - let(:chart) { nil } - - it { is_expected.to be_nil } - end - - context 'when command.chart is set' do - let(:chart) { 'stable/a_chart' } - - it { is_expected.to eq("helm install #{chart} --name #{application_name} --namespace #{namespace.name} >/dev/null")} - end - end end diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb new file mode 100644 index 00000000000..4afe48e72ad --- /dev/null +++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb @@ -0,0 +1,111 @@ +require 'rails_helper' + +describe Gitlab::Kubernetes::Helm::InstallCommand do + let(:prometheus) { create(:clusters_applications_prometheus) } + + describe "#initialize" do + context "With all the params" do + subject { described_class.new(prometheus.name, install_helm: true, chart: prometheus.chart, chart_values_file: prometheus.chart_values_file) } + + it 'should assign all parameters' do + expect(subject.name).to eq(prometheus.name) + expect(subject.install_helm).to be_truthy + expect(subject.chart).to eq(prometheus.chart) + expect(subject.chart_values_file).to eq("#{Rails.root}/vendor/prometheus/values.yaml") + end + end + + context 'when install_helm is not set' do + subject { described_class.new(prometheus.name, chart: prometheus.chart, chart_values_file: true) } + + it 'should set install_helm as false' do + expect(subject.install_helm).to be_falsy + end + end + + context 'when chart is not set' do + subject { described_class.new(prometheus.name, install_helm: true) } + + it 'should set chart as nil' do + expect(subject.chart).to be_falsy + end + end + + context 'when chart_values_file is not set' do + subject { described_class.new(prometheus.name, install_helm: true, chart: prometheus.chart) } + + it 'should set chart_values_file as nil' do + expect(subject.chart_values_file).to be_falsy + end + end + end + + describe "#generate_script" do + let(:install_command) { described_class.new(prometheus.name, install_helm: install_helm) } + let(:client) { double('kubernetes client') } + let(:namespace) { Gitlab::Kubernetes::Namespace.new(Gitlab::Kubernetes::Helm::NAMESPACE, client) } + subject { install_command.send(:generate_script, namespace.name) } + + context 'when install helm is true' do + let(:install_helm) { true } + let(:command) do + <<~MSG + set -eo pipefail + apk add -U ca-certificates openssl >/dev/null + wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null + mv /tmp/linux-amd64/helm /usr/bin/ + + helm init >/dev/null + MSG + end + + it 'should return appropriate command' do + is_expected.to eq(command) + end + end + + context 'when install helm is false' do + let(:install_helm) { false } + let(:command) do + <<~MSG + set -eo pipefail + apk add -U ca-certificates openssl >/dev/null + wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null + mv /tmp/linux-amd64/helm /usr/bin/ + + helm init --client-only >/dev/null + MSG + end + + it 'should return appropriate command' do + is_expected.to eq(command) + end + end + + context 'when chart is present' do + let(:install_command) { described_class.new(prometheus.name, chart: prometheus.chart) } + let(:command) do + <<~MSG.chomp + set -eo pipefail + apk add -U ca-certificates openssl >/dev/null + wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null + mv /tmp/linux-amd64/helm /usr/bin/ + + helm init --client-only >/dev/null + helm install #{prometheus.chart} --name #{prometheus.name} --namespace #{namespace.name} >/dev/null + MSG + end + + it 'should return appropriate command' do + is_expected.to eq(command) + end + end + end + + describe "#pod_name" do + let(:install_command) { described_class.new(prometheus.name, install_helm: true, chart: prometheus.chart, chart_values_file: true) } + subject { install_command.send(:pod_name) } + + it { is_expected.to eq('install-prometheus') } + end +end diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb new file mode 100644 index 00000000000..906b10b96d4 --- /dev/null +++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb @@ -0,0 +1,86 @@ +require 'rails_helper' + +describe Gitlab::Kubernetes::Helm::Pod do + describe '#generate' do + let(:cluster) { create(:cluster) } + let(:app) { create(:clusters_applications_prometheus, cluster: cluster) } + let(:command) { app.install_command } + let(:client) { double('kubernetes client') } + let(:namespace) { Gitlab::Kubernetes::Namespace.new(Gitlab::Kubernetes::Helm::NAMESPACE, client) } + subject { described_class.new(command, namespace.name, client) } + + before do + allow(client).to receive(:create_config_map).and_return(nil) + end + + shared_examples 'helm pod' do + it 'should generate a Kubeclient::Resource' do + expect(subject.generate).to be_a_kind_of(Kubeclient::Resource) + end + + it 'should generate the appropriate metadata' do + metadata = subject.generate.metadata + expect(metadata.name).to eq("install-#{app.name}") + expect(metadata.namespace).to eq('gitlab-managed-apps') + expect(metadata.labels['gitlab.org/action']).to eq('install') + expect(metadata.labels['gitlab.org/application']).to eq(app.name) + end + + it 'should generate a container spec' do + spec = subject.generate.spec + expect(spec.containers.count).to eq(1) + end + + it 'should generate the appropriate specifications for the container' do + container = subject.generate.spec.containers.first + expect(container.name).to eq('helm') + expect(container.image).to eq('alpine:3.6') + expect(container.env.count).to eq(3) + expect(container.env.map(&:name)).to match_array([:HELM_VERSION, :TILLER_NAMESPACE, :COMMAND_SCRIPT]) + expect(container.command).to match_array(["/bin/sh"]) + expect(container.args).to match_array(["-c", "$(COMMAND_SCRIPT)"]) + end + + it 'should include a never restart policy' do + spec = subject.generate.spec + expect(spec.restartPolicy).to eq('Never') + end + end + + context 'with a configuration file' do + it_behaves_like 'helm pod' + + it 'should include volumes for the container' do + container = subject.generate.spec.containers.first + expect(container.volumeMounts.first['name']).to eq('config-volume') + expect(container.volumeMounts.first['mountPath']).to eq('/etc/config') + end + + it 'should include a volume inside the specification' do + spec = subject.generate.spec + expect(spec.volumes.first['name']).to eq('config-volume') + end + + it 'should mount configMap specification in the volume' do + spec = subject.generate.spec + expect(spec.volumes.first.configMap['name']).to eq('values-config') + end + end + + context 'without a configuration file' do + let(:app) { create(:clusters_applications_ingress, cluster: cluster) } + + it_behaves_like 'helm pod' + + it 'should not include volumeMounts inside the container' do + container = subject.generate.spec.containers.first + expect(container.volumeMounts).to be_nil + end + + it 'should not a volume inside the specification' do + spec = subject.generate.spec + expect(spec.volumes).to be_nil + end + end + end +end diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb index d9ddb4326be..6132abd9b35 100644 --- a/spec/lib/gitlab/ldap/adapter_spec.rb +++ b/spec/lib/gitlab/ldap/adapter_spec.rb @@ -16,7 +16,7 @@ describe Gitlab::LDAP::Adapter do expect(adapter).to receive(:ldap_search) do |arg| expect(arg[:filter].to_s).to eq('(uid=johndoe)') expect(arg[:base]).to eq('dc=example,dc=com') - expect(arg[:attributes]).to match(%w{dn uid cn mail email userPrincipalName}) + expect(arg[:attributes]).to match(ldap_attributes) end.and_return({}) adapter.users('uid', 'johndoe') @@ -26,7 +26,7 @@ describe Gitlab::LDAP::Adapter do expect(adapter).to receive(:ldap_search).with( base: 'uid=johndoe,ou=users,dc=example,dc=com', scope: Net::LDAP::SearchScope_BaseObject, - attributes: %w{dn uid cn mail email userPrincipalName}, + attributes: ldap_attributes, filter: nil ).and_return({}) @@ -63,7 +63,7 @@ describe Gitlab::LDAP::Adapter do it 'uses the right uid attribute when non-default' do stub_ldap_config(uid: 'sAMAccountName') expect(adapter).to receive(:ldap_search).with( - hash_including(attributes: %w{dn sAMAccountName cn mail email userPrincipalName}) + hash_including(attributes: ldap_attributes) ).and_return({}) adapter.users('sAMAccountName', 'johndoe') @@ -137,4 +137,8 @@ describe Gitlab::LDAP::Adapter do end end end + + def ldap_attributes + Gitlab::LDAP::Person.ldap_attributes(Gitlab::LDAP::Config.new('ldapmain')) + end end diff --git a/spec/lib/gitlab/ldap/person_spec.rb b/spec/lib/gitlab/ldap/person_spec.rb index d204050ef66..ff29d9aa5be 100644 --- a/spec/lib/gitlab/ldap/person_spec.rb +++ b/spec/lib/gitlab/ldap/person_spec.rb @@ -8,13 +8,16 @@ describe Gitlab::LDAP::Person do before do stub_ldap_config( options: { + 'uid' => 'uid', 'attributes' => { - 'name' => 'cn', - 'email' => %w(mail email userPrincipalName) + 'name' => 'cn', + 'email' => %w(mail email userPrincipalName), + 'username' => username_attribute } } ) end + let(:username_attribute) { %w(uid sAMAccountName userid) } describe '.normalize_dn' do subject { described_class.normalize_dn(given) } @@ -44,6 +47,34 @@ describe Gitlab::LDAP::Person do end end + describe '.ldap_attributes' do + it 'returns a compact and unique array' do + stub_ldap_config( + options: { + 'uid' => nil, + 'attributes' => { + 'name' => 'cn', + 'email' => 'mail', + 'username' => %w(uid mail memberof) + } + } + ) + config = Gitlab::LDAP::Config.new('ldapmain') + ldap_attributes = described_class.ldap_attributes(config) + + expect(ldap_attributes).to match_array(%w(dn uid cn mail memberof)) + end + end + + describe '.validate_entry' do + it 'raises InvalidEntryError' do + entry['foo'] = 'bar' + + expect { described_class.new(entry, 'ldapmain') } + .to raise_error(Gitlab::LDAP::Person::InvalidEntryError) + end + end + describe '#name' do it 'uses the configured name attribute and handles values as an array' do name = 'John Doe' @@ -72,6 +103,44 @@ describe Gitlab::LDAP::Person do end end + describe '#username' do + context 'with default uid username attribute' do + let(:username_attribute) { 'uid' } + + it 'returns the proper username value' do + attr_value = 'johndoe' + entry[username_attribute] = attr_value + person = described_class.new(entry, 'ldapmain') + + expect(person.username).to eq(attr_value) + end + end + + context 'with a different username attribute' do + let(:username_attribute) { 'sAMAccountName' } + + it 'returns the proper username value' do + attr_value = 'johndoe' + entry[username_attribute] = attr_value + person = described_class.new(entry, 'ldapmain') + + expect(person.username).to eq(attr_value) + end + end + + context 'with a non-standard username attribute' do + let(:username_attribute) { 'mail' } + + it 'returns the proper username value' do + attr_value = 'john.doe@example.com' + entry[username_attribute] = attr_value + person = described_class.new(entry, 'ldapmain') + + expect(person.username).to eq(attr_value) + end + end + end + def assert_generic_test(test_description, got, expected) test_failure_message = "Failed test description: '#{test_description}'\n\n expected: #{expected}\n got: #{got}" expect(got).to eq(expected), test_failure_message diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index 260df6e4dae..048caa38fcf 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -38,7 +38,6 @@ describe Gitlab::LDAP::User do it "does not mark existing ldap user as changed" do create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=john smith,ou=people,dc=example,dc=com', provider: 'ldapmain') - ldap_user.gl_user.user_synced_attributes_metadata(provider: 'ldapmain', email: true) expect(ldap_user.changed?).to be_falsey end end @@ -144,11 +143,15 @@ describe Gitlab::LDAP::User do expect(ldap_user.gl_user.email).to eq(info[:email]) end - it "has user_synced_attributes_metadata email set to true" do + it "has email set as synced" do expect(ldap_user.gl_user.user_synced_attributes_metadata.email_synced).to be_truthy end - it "has synced_attribute_provider set to ldapmain" do + it "has email set as read-only" do + expect(ldap_user.gl_user.read_only_attribute?(:email)).to be_truthy + end + + it "has synced attributes provider set to ldapmain" do expect(ldap_user.gl_user.user_synced_attributes_metadata.provider).to eql 'ldapmain' end end @@ -162,9 +165,13 @@ describe Gitlab::LDAP::User do expect(ldap_user.gl_user.temp_oauth_email?).to be_truthy end - it "has synced attribute email set to false" do + it "has email set as not synced" do expect(ldap_user.gl_user.user_synced_attributes_metadata.email_synced).to be_falsey end + + it "does not have email set as read-only" do + expect(ldap_user.gl_user.read_only_attribute?(:email)).to be_falsey + end end end diff --git a/spec/lib/gitlab/metrics/method_call_spec.rb b/spec/lib/gitlab/metrics/method_call_spec.rb index 5341addf911..41a9d1d9c90 100644 --- a/spec/lib/gitlab/metrics/method_call_spec.rb +++ b/spec/lib/gitlab/metrics/method_call_spec.rb @@ -20,9 +20,39 @@ describe Gitlab::Metrics::MethodCall do context 'prometheus instrumentation is enabled' do before do + allow(Feature.get(:prometheus_metrics_method_instrumentation)).to receive(:enabled?).and_call_original + described_class.measurement_enabled_cache_expires_at.value = Time.now.to_i - 1 Feature.get(:prometheus_metrics_method_instrumentation).enable end + around do |example| + Timecop.freeze do + example.run + end + end + + it 'caches subsequent invocations of feature check' do + 10.times do + method_call.measure { 'foo' } + end + + expect(Feature.get(:prometheus_metrics_method_instrumentation)).to have_received(:enabled?).once + end + + it 'expires feature check cache after 1 minute' do + method_call.measure { 'foo' } + + Timecop.travel(1.minute.from_now) do + method_call.measure { 'foo' } + end + + Timecop.travel(1.minute.from_now + 1.second) do + method_call.measure { 'foo' } + end + + expect(Feature.get(:prometheus_metrics_method_instrumentation)).to have_received(:enabled?).twice + end + it 'observes the performance of the supplied block' do expect(described_class.call_duration_histogram) .to receive(:observe) @@ -34,6 +64,8 @@ describe Gitlab::Metrics::MethodCall do context 'prometheus instrumentation is disabled' do before do + described_class.measurement_enabled_cache_expires_at.value = Time.now.to_i - 1 + Feature.get(:prometheus_metrics_method_instrumentation).disable end @@ -64,14 +96,17 @@ describe Gitlab::Metrics::MethodCall do describe '#to_metric' do it 'returns a Metric instance' do + expect(method_call).to receive(:real_time).and_return(4.0001) + expect(method_call).to receive(:cpu_time).and_return(3.0001) + method_call.measure { 'foo' } metric = method_call.to_metric expect(metric).to be_an_instance_of(Gitlab::Metrics::Metric) expect(metric.series).to eq('rails_method_calls') - expect(metric.values[:duration]).to be_a_kind_of(Numeric) - expect(metric.values[:cpu_duration]).to be_a_kind_of(Numeric) + expect(metric.values[:duration]).to eq(4000) + expect(metric.values[:cpu_duration]).to eq(3000) expect(metric.values[:call_count]).to be_an(Integer) expect(metric.tags).to eq({ method: 'Foo#bar' }) @@ -84,13 +119,13 @@ describe Gitlab::Metrics::MethodCall do end it 'returns false when the total call time is not above the threshold' do - expect(method_call).to receive(:real_time).and_return(9) + expect(method_call).to receive(:real_time).and_return(0.009) expect(method_call.above_threshold?).to eq(false) end it 'returns true when the total call time is above the threshold' do - expect(method_call).to receive(:real_time).and_return(9000) + expect(method_call).to receive(:real_time).and_return(9) expect(method_call.above_threshold?).to eq(true) end diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb index 4d94d8705fb..14afcdf5daa 100644 --- a/spec/lib/gitlab/metrics/system_spec.rb +++ b/spec/lib/gitlab/metrics/system_spec.rb @@ -29,19 +29,19 @@ describe Gitlab::Metrics::System do describe '.cpu_time' do it 'returns a Fixnum' do - expect(described_class.cpu_time).to be_an(Integer) + expect(described_class.cpu_time).to be_an(Float) end end describe '.real_time' do it 'returns a Fixnum' do - expect(described_class.real_time).to be_an(Integer) + expect(described_class.real_time).to be_an(Float) end end describe '.monotonic_time' do - it 'returns a Fixnum' do - expect(described_class.monotonic_time).to be_an(Integer) + it 'returns a Float' do + expect(described_class.monotonic_time).to be_an(Float) end end end diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index 2f19fb7312d..45fff4c5787 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -202,11 +202,13 @@ describe Gitlab::OAuth::User do end context "and no account for the LDAP user" do - it "creates a user with dual LDAP and omniauth identities" do + before do allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user) oauth_user.save + end + it "creates a user with dual LDAP and omniauth identities" do expect(gl_user).to be_valid expect(gl_user.username).to eql uid expect(gl_user.email).to eql 'johndoe@example.com' @@ -219,6 +221,18 @@ describe Gitlab::OAuth::User do ] ) end + + it "has email set as synced" do + expect(gl_user.user_synced_attributes_metadata.email_synced).to be_truthy + end + + it "has email set as read-only" do + expect(gl_user.read_only_attribute?(:email)).to be_truthy + end + + it "has synced attributes provider set to ldapmain" do + expect(gl_user.user_synced_attributes_metadata.provider).to eql 'ldapmain' + end end context "and LDAP user has an account already" do @@ -261,6 +275,26 @@ describe Gitlab::OAuth::User do end end + context 'and a corresponding LDAP person with a non-default username' do + before do + allow(ldap_user).to receive(:uid) { uid } + allow(ldap_user).to receive(:username) { 'johndoe@example.com' } + allow(ldap_user).to receive(:email) { %w(johndoe@example.com john2@example.com) } + allow(ldap_user).to receive(:dn) { dn } + end + + context 'and no account for the LDAP user' do + it 'creates a user favoring the LDAP username and strips email domain' do + allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user) + + oauth_user.save + + expect(gl_user).to be_valid + expect(gl_user.username).to eql 'johndoe' + end + end + end + context "and no corresponding LDAP person" do before do allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil) @@ -440,11 +474,15 @@ describe Gitlab::OAuth::User do expect(gl_user.email).to eq(info_hash[:email]) end - it "has external_attributes set to true" do - expect(gl_user.user_synced_attributes_metadata).not_to be_nil + it "has email set as synced" do + expect(gl_user.user_synced_attributes_metadata.email_synced).to be_truthy end - it "has attributes_provider set to my-provider" do + it "has email set as read-only" do + expect(gl_user.read_only_attribute?(:email)).to be_truthy + end + + it "has synced attributes provider set to my-provider" do expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider' end end @@ -458,10 +496,13 @@ describe Gitlab::OAuth::User do expect(gl_user.email).not_to eq(info_hash[:email]) end - it "has user_synced_attributes_metadata set to nil" do - expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider' + it "has email set as not synced" do expect(gl_user.user_synced_attributes_metadata.email_synced).to be_falsey end + + it "does not have email set as read-only" do + expect(gl_user.read_only_attribute?(:email)).to be_falsey + end end end @@ -508,11 +549,15 @@ describe Gitlab::OAuth::User do expect(gl_user.email).to eq(info_hash[:email]) end - it "has email_synced_attribute set to true" do + it "has email set as synced" do expect(gl_user.user_synced_attributes_metadata.email_synced).to be(true) end - it "has my-provider as attributes_provider" do + it "has email set as read-only" do + expect(gl_user.read_only_attribute?(:email)).to be_truthy + end + + it "has synced attributes provider set to my-provider" do expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider' end end @@ -524,7 +569,14 @@ describe Gitlab::OAuth::User do it "does not update the user email" do expect(gl_user.email).not_to eq(info_hash[:email]) - expect(gl_user.user_synced_attributes_metadata.email_synced).to be(false) + end + + it "has email set as not synced" do + expect(gl_user.user_synced_attributes_metadata.email_synced).to be_falsey + end + + it "does not have email set as read-only" do + expect(gl_user.read_only_attribute?(:email)).to be_falsey end end end diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb index 953cfbb8b88..f3cd6961e94 100644 --- a/spec/lib/gitlab/project_authorizations_spec.rb +++ b/spec/lib/gitlab/project_authorizations_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::ProjectAuthorizations do end before do - other_project.team << [user, :reporter] + other_project.add_reporter(user) group.add_developer(user) end diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index a424f0f5cfe..17937726f2c 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -179,7 +179,7 @@ describe Gitlab::ProjectSearchResults do end it 'does not list project confidential issues for project members with guest role' do - project.team << [member, :guest] + project.add_guest(member) results = described_class.new(member, project, query) issues = results.objects('issues') @@ -211,7 +211,7 @@ describe Gitlab::ProjectSearchResults do end it 'lists project confidential issues for project members' do - project.team << [member, :developer] + project.add_developer(member) results = described_class.new(member, project, query) issues = results.objects('issues') @@ -290,12 +290,12 @@ describe Gitlab::ProjectSearchResults do let!(:private_project) { create(:project, :private, :repository, creator: creator, namespace: creator.namespace) } let(:team_master) do user = create(:user, username: 'private-project-master') - private_project.team << [user, :master] + private_project.add_master(user) user end let(:team_reporter) do user = create(:user, username: 'private-project-reporter') - private_project.team << [user, :reporter] + private_project.add_reporter(user) user end diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index 8ec3f55e6de..4139d1c650c 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::ReferenceExtractor do let(:project) { create(:project) } before do - project.team << [project.creator, :developer] + project.add_developer(project.creator) end subject { described_class.new(project, project.creator) } @@ -14,8 +14,8 @@ describe Gitlab::ReferenceExtractor do @u_bar = create(:user, username: 'bar') @u_offteam = create(:user, username: 'offteam') - project.team << [@u_foo, :reporter] - project.team << [@u_bar, :guest] + project.add_guest(@u_foo) + project.add_guest(@u_bar) subject.analyze('@foo, @baduser, @bar, and @offteam') expect(subject.users).to match_array([@u_foo, @u_bar, @u_offteam]) @@ -26,8 +26,8 @@ describe Gitlab::ReferenceExtractor do @u_bar = create(:user, username: 'bar') @u_offteam = create(:user, username: 'offteam') - project.team << [@u_foo, :reporter] - project.team << [@u_bar, :guest] + project.add_reporter(@u_foo) + project.add_reporter(@u_bar) subject.analyze(%Q{ Inline code: `@foo` @@ -228,7 +228,7 @@ describe Gitlab::ReferenceExtractor do let(:issue) { create(:issue, project: other_project) } before do - other_project.team << [project.creator, :developer] + other_project.add_developer(project.creator) end it 'handles project issue references' do @@ -246,7 +246,7 @@ describe Gitlab::ReferenceExtractor do let(:text) { "Ref. #{issue.to_reference} and #{label.to_reference}" } before do - project.team << [project.creator, :developer] + project.add_developer(project.creator) subject.analyze(text) end diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb index e44a7c23452..b5a9ac570e6 100644 --- a/spec/lib/gitlab/search_results_spec.rb +++ b/spec/lib/gitlab/search_results_spec.rb @@ -16,7 +16,7 @@ describe Gitlab::SearchResults do context 'as a user with access' do before do - project.team << [user, :developer] + project.add_developer(user) end describe '#projects_count' do @@ -51,6 +51,38 @@ describe Gitlab::SearchResults do expect(results.objects('merge_requests')).to include merge_request_2 end + + describe '#merge_requests' do + it 'includes project filter by default' do + expect(results).to receive(:project_ids_relation).and_call_original + + results.objects('merge_requests') + end + + it 'it skips project filter if default project context is used' do + allow(results).to receive(:default_project_filter).and_return(true) + + expect(results).not_to receive(:project_ids_relation) + + results.objects('merge_requests') + end + end + + describe '#issues' do + it 'includes project filter by default' do + expect(results).to receive(:project_ids_relation).and_call_original + + results.objects('issues') + end + + it 'it skips project filter if default project context is used' do + allow(results).to receive(:default_project_filter).and_return(true) + + expect(results).not_to receive(:project_ids_relation) + + results.objects('issues') + end + end end it 'does not list issues on private projects' do @@ -93,8 +125,8 @@ describe Gitlab::SearchResults do end it 'does not list confidential issues for project members with guest role' do - project_1.team << [member, :guest] - project_2.team << [member, :guest] + project_1.add_guest(member) + project_2.add_guest(member) results = described_class.new(member, limit_projects, query) issues = results.objects('issues') @@ -135,8 +167,8 @@ describe Gitlab::SearchResults do end it 'lists confidential issues for project members' do - project_1.team << [member, :developer] - project_2.team << [member, :developer] + project_1.add_developer(member) + project_2.add_developer(member) results = described_class.new(member, limit_projects, query) issues = results.objects('issues') diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index eec6858a5de..81d9e6a8f82 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -2,12 +2,19 @@ require 'spec_helper' require 'stringio' describe Gitlab::Shell do - let(:project) { double('Project', id: 7, path: 'diaspora') } + set(:project) { create(:project, :repository) } + let(:gitlab_shell) { described_class.new } let(:popen_vars) { { 'GIT_TERMINAL_PROMPT' => ENV['GIT_TERMINAL_PROMPT'] } } + let(:gitlab_projects) { double('gitlab_projects') } + let(:timeout) { Gitlab.config.gitlab_shell.git_timeout } before do allow(Project).to receive(:find).and_return(project) + + allow(gitlab_shell).to receive(:gitlab_projects) + .with(project.repository_storage_path, project.disk_path + '.git') + .and_return(gitlab_projects) end it { is_expected.to respond_to :add_key } @@ -44,38 +51,6 @@ describe Gitlab::Shell do end end - describe 'projects commands' do - let(:gitlab_shell_path) { File.expand_path('tmp/tests/gitlab-shell') } - let(:projects_path) { File.join(gitlab_shell_path, 'bin/gitlab-projects') } - let(:gitlab_shell_hooks_path) { File.join(gitlab_shell_path, 'hooks') } - - before do - allow(Gitlab.config.gitlab_shell).to receive(:path).and_return(gitlab_shell_path) - allow(Gitlab.config.gitlab_shell).to receive(:hooks_path).and_return(gitlab_shell_hooks_path) - allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800) - end - - describe '#mv_repository' do - it 'executes the command' do - expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with( - [projects_path, 'mv-project', 'storage/path', 'project/path.git', 'new/path.git'] - ) - gitlab_shell.mv_repository('storage/path', 'project/path', 'new/path') - end - end - - describe '#add_key' do - it 'removes trailing garbage' do - allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path) - expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with( - [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar'] - ) - - gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage') - end - end - end - describe Gitlab::Shell::KeyAdder do describe '#add_key' do it 'removes trailing garbage' do @@ -121,6 +96,17 @@ describe Gitlab::Shell do allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800) end + describe '#add_key' do + it 'removes trailing garbage' do + allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path) + expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with( + [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar'] + ) + + gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage') + end + end + describe '#add_repository' do shared_examples '#add_repository' do let(:repository_storage) { 'default' } @@ -162,83 +148,76 @@ describe Gitlab::Shell do end describe '#remove_repository' do + subject { gitlab_shell.remove_repository(project.repository_storage_path, project.disk_path) } + it 'returns true when the command succeeds' do - expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'rm-project', 'current/storage', 'project/path.git'], - nil, popen_vars).and_return([nil, 0]) + expect(gitlab_projects).to receive(:rm_project) { true } - expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be true + is_expected.to be_truthy end it 'returns false when the command fails' do - expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'rm-project', 'current/storage', 'project/path.git'], - nil, popen_vars).and_return(["error", 1]) + expect(gitlab_projects).to receive(:rm_project) { false } - expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be false + is_expected.to be_falsy end end describe '#mv_repository' do it 'returns true when the command succeeds' do - expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'mv-project', 'current/storage', 'project/path.git', 'project/newpath.git'], - nil, popen_vars).and_return([nil, 0]) + expect(gitlab_projects).to receive(:mv_project).with('project/newpath.git') { true } - expect(gitlab_shell.mv_repository('current/storage', 'project/path', 'project/newpath')).to be true + expect(gitlab_shell.mv_repository(project.repository_storage_path, project.disk_path, 'project/newpath')).to be_truthy end it 'returns false when the command fails' do - expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'mv-project', 'current/storage', 'project/path.git', 'project/newpath.git'], - nil, popen_vars).and_return(["error", 1]) + expect(gitlab_projects).to receive(:mv_project).with('project/newpath.git') { false } - expect(gitlab_shell.mv_repository('current/storage', 'project/path', 'project/newpath')).to be false + expect(gitlab_shell.mv_repository(project.repository_storage_path, project.disk_path, 'project/newpath')).to be_falsy end end describe '#fork_repository' do + subject do + gitlab_shell.fork_repository( + project.repository_storage_path, + project.disk_path, + 'new/storage', + 'fork/path' + ) + end + it 'returns true when the command succeeds' do - expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'fork-repository', 'current/storage', 'project/path.git', 'new/storage', 'fork/path.git'], - nil, popen_vars).and_return([nil, 0]) + expect(gitlab_projects).to receive(:fork_repository).with('new/storage', 'fork/path.git') { true } - expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'fork/path')).to be true + is_expected.to be_truthy end it 'return false when the command fails' do - expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'fork-repository', 'current/storage', 'project/path.git', 'new/storage', 'fork/path.git'], - nil, popen_vars).and_return(["error", 1]) + expect(gitlab_projects).to receive(:fork_repository).with('new/storage', 'fork/path.git') { false } - expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'fork/path')).to be false + is_expected.to be_falsy end end shared_examples 'fetch_remote' do |gitaly_on| - let(:project2) { create(:project, :repository) } - let(:repository) { project2.repository } + let(:repository) { project.repository } def fetch_remote(ssh_auth = nil) - gitlab_shell.fetch_remote(repository.raw_repository, 'new/storage', ssh_auth: ssh_auth) + gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', ssh_auth: ssh_auth) end - def expect_popen(fail = false, vars = {}) - popen_args = [ - projects_path, - 'fetch-remote', - TestEnv.repos_path, - repository.relative_path, - 'new/storage', - Gitlab.config.gitlab_shell.git_timeout.to_s - ] - - return_value = fail ? ["error", 1] : [nil, 0] + def expect_gitlab_projects(fail = false, options = {}) + expect(gitlab_projects).to receive(:fetch_remote).with( + 'remote-name', + timeout, + options + ).and_return(!fail) - expect(Gitlab::Popen).to receive(:popen).with(popen_args, nil, popen_vars.merge(vars)).and_return(return_value) + allow(gitlab_projects).to receive(:output).and_return('error') if fail end - def expect_gitaly_call(fail, vars = {}) + def expect_gitaly_call(fail, options = {}) receive_fetch_remote = if fail receive(:fetch_remote).and_raise(GRPC::NotFound) @@ -250,12 +229,12 @@ describe Gitlab::Shell do end if gitaly_on - def expect_call(fail, vars = {}) - expect_gitaly_call(fail, vars) + def expect_call(fail, options = {}) + expect_gitaly_call(fail, options) end else - def expect_call(fail, vars = {}) - expect_popen(fail, vars) + def expect_call(fail, options = {}) + expect_gitlab_projects(fail, options) end end @@ -271,20 +250,27 @@ describe Gitlab::Shell do end it 'returns true when the command succeeds' do - expect_call(false) + expect_call(false, force: false, tags: true) expect(fetch_remote).to be_truthy end it 'raises an exception when the command fails' do - expect_call(true) + expect_call(true, force: false, tags: true) expect { fetch_remote }.to raise_error(Gitlab::Shell::Error) end + it 'allows forced and no_tags to be changed' do + expect_call(false, force: true, tags: false) + + result = gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', forced: true, no_tags: true) + expect(result).to be_truthy + end + context 'SSH auth' do it 'passes the SSH key if specified' do - expect_call(false, 'GITLAB_SHELL_SSH_KEY' => 'foo') + expect_call(false, force: false, tags: true, ssh_key: 'foo') ssh_auth = build_ssh_auth(ssh_key_auth?: true, ssh_private_key: 'foo') @@ -292,7 +278,7 @@ describe Gitlab::Shell do end it 'does not pass an empty SSH key' do - expect_call(false) + expect_call(false, force: false, tags: true) ssh_auth = build_ssh_auth(ssh_key_auth: true, ssh_private_key: '') @@ -300,7 +286,7 @@ describe Gitlab::Shell do end it 'does not pass the key unless SSH key auth is to be used' do - expect_call(false) + expect_call(false, force: false, tags: true) ssh_auth = build_ssh_auth(ssh_key_auth: false, ssh_private_key: 'foo') @@ -308,7 +294,7 @@ describe Gitlab::Shell do end it 'passes the known_hosts data if specified' do - expect_call(false, 'GITLAB_SHELL_KNOWN_HOSTS' => 'foo') + expect_call(false, force: false, tags: true, known_hosts: 'foo') ssh_auth = build_ssh_auth(ssh_known_hosts: 'foo') @@ -316,7 +302,7 @@ describe Gitlab::Shell do end it 'does not pass empty known_hosts data' do - expect_call(false) + expect_call(false, force: false, tags: true) ssh_auth = build_ssh_auth(ssh_known_hosts: '') @@ -324,7 +310,7 @@ describe Gitlab::Shell do end it 'does not pass known_hosts data unless SSH is to be used' do - expect_call(false, popen_vars) + expect_call(false, force: false, tags: true) ssh_auth = build_ssh_auth(ssh_import?: false, ssh_known_hosts: 'foo') @@ -342,20 +328,23 @@ describe Gitlab::Shell do end describe '#import_repository' do + let(:import_url) { 'https://gitlab.com/gitlab-org/gitlab-ce.git' } + it 'returns true when the command succeeds' do - expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"], - nil, popen_vars).and_return([nil, 0]) + expect(gitlab_projects).to receive(:import_project).with(import_url, timeout) { true } + + result = gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, import_url) - expect(gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git')).to be true + expect(result).to be_truthy end it 'raises an exception when the command fails' do - expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"], - nil, popen_vars).and_return(["error", 1]) + allow(gitlab_projects).to receive(:output) { 'error' } + expect(gitlab_projects).to receive(:import_project) { false } - expect { gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git') }.to raise_error(Gitlab::Shell::Error, "error") + expect do + gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, import_url) + end.to raise_error(Gitlab::Shell::Error, "error") end end end diff --git a/spec/lib/gitlab/sidekiq_config_spec.rb b/spec/lib/gitlab/sidekiq_config_spec.rb index 09f95be2213..0c66d764851 100644 --- a/spec/lib/gitlab/sidekiq_config_spec.rb +++ b/spec/lib/gitlab/sidekiq_config_spec.rb @@ -16,9 +16,30 @@ describe Gitlab::SidekiqConfig do expect(queues).to include('post_receive') expect(queues).to include('merge') - expect(queues).to include('cronjob') + expect(queues).to include('cronjob:stuck_import_jobs') expect(queues).to include('mailers') expect(queues).to include('default') end end + + describe '.expand_queues' do + it 'expands queue namespaces to concrete queue names' do + queues = described_class.expand_queues(%w[cronjob]) + + expect(queues).to include('cronjob:stuck_import_jobs') + expect(queues).to include('cronjob:stuck_merge_jobs') + end + + it 'lets concrete queue names pass through' do + queues = described_class.expand_queues(%w[post_receive]) + + expect(queues).to include('post_receive') + end + + it 'lets unknown queues pass through' do + queues = described_class.expand_queues(%w[unknown]) + + expect(queues).to include('unknown') + end + end end diff --git a/spec/lib/gitlab/sidekiq_versioning/manager_spec.rb b/spec/lib/gitlab/sidekiq_versioning/manager_spec.rb new file mode 100644 index 00000000000..7debf70a16f --- /dev/null +++ b/spec/lib/gitlab/sidekiq_versioning/manager_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe Gitlab::SidekiqVersioning::Manager do + before do + Sidekiq::Manager.prepend described_class + end + + describe '#initialize' do + it 'listens on all expanded queues' do + manager = Sidekiq::Manager.new(queues: %w[post_receive repository_fork cronjob unknown]) + + queues = manager.options[:queues] + + expect(queues).to include('post_receive') + expect(queues).to include('repository_fork') + expect(queues).to include('cronjob') + expect(queues).to include('cronjob:stuck_import_jobs') + expect(queues).to include('cronjob:stuck_merge_jobs') + expect(queues).to include('unknown') + end + end +end diff --git a/spec/lib/gitlab/sidekiq_versioning_spec.rb b/spec/lib/gitlab/sidekiq_versioning_spec.rb new file mode 100644 index 00000000000..fa6d42e730d --- /dev/null +++ b/spec/lib/gitlab/sidekiq_versioning_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe Gitlab::SidekiqVersioning, :sidekiq, :redis do + let(:foo_worker) do + Class.new do + def self.name + 'FooWorker' + end + + include ApplicationWorker + end + end + + let(:bar_worker) do + Class.new do + def self.name + 'BarWorker' + end + + include ApplicationWorker + end + end + + before do + allow(Gitlab::SidekiqConfig).to receive(:workers).and_return([foo_worker, bar_worker]) + allow(Gitlab::SidekiqConfig).to receive(:worker_queues).and_return([foo_worker.queue, bar_worker.queue]) + end + + describe '.install!' do + it 'prepends SidekiqVersioning::Manager into Sidekiq::Manager' do + described_class.install! + + expect(Sidekiq::Manager).to include(Gitlab::SidekiqVersioning::Manager) + end + + it 'registers all versionless and versioned queues with Redis' do + described_class.install! + + queues = Sidekiq::Queue.all.map(&:name) + expect(queues).to include('foo') + expect(queues).to include('bar') + end + end +end diff --git a/spec/lib/gitlab/slash_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb index 75ae58d0582..3b077c58c50 100644 --- a/spec/lib/gitlab/slash_commands/issue_new_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb @@ -7,7 +7,7 @@ describe Gitlab::SlashCommands::IssueNew do let(:regex_match) { described_class.match("issue create bird is the word") } before do - project.team << [user, :master] + project.add_master(user) end subject do diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb index 51f59216413..e41e5254dde 100644 --- a/spec/lib/gitlab/slash_commands/issue_search_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::SlashCommands::IssueSearch do context 'the user has access' do before do - project.team << [user, :master] + project.add_master(user) end it 'returns all results' do diff --git a/spec/lib/gitlab/slash_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb index 08c380ca8f1..e5834d5a2ee 100644 --- a/spec/lib/gitlab/slash_commands/issue_show_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::SlashCommands::IssueShow do let(:regex_match) { described_class.match("issue show #{issue.iid}") } before do - project.team << [user, :master] + project.add_master(user) end subject do diff --git a/spec/lib/gitlab/tcp_checker_spec.rb b/spec/lib/gitlab/tcp_checker_spec.rb new file mode 100644 index 00000000000..4acf0334496 --- /dev/null +++ b/spec/lib/gitlab/tcp_checker_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Gitlab::TcpChecker do + before do + @server = TCPServer.new('localhost', 0) + _, @port, _, @ip = @server.addr + end + + after do + @server.close + end + + subject(:checker) { described_class.new(@ip, @port) } + + describe '#check' do + subject { checker.check } + + it 'can connect to an open port' do + is_expected.to be_truthy + + expect(checker.error).to be_nil + end + + it 'fails to connect to a closed port' do + @server.close + + is_expected.to be_falsy + + expect(checker.error).to be_a(Errno::ECONNREFUSED) + end + end +end diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb index cd97416bcc9..7280acb6c82 100644 --- a/spec/lib/gitlab/user_access_spec.rb +++ b/spec/lib/gitlab/user_access_spec.rb @@ -8,19 +8,19 @@ describe Gitlab::UserAccess do describe '#can_push_to_branch?' do describe 'push to none protected branch' do it 'returns true if user is a master' do - project.team << [user, :master] + project.add_master(user) expect(access.can_push_to_branch?('random_branch')).to be_truthy end it 'returns true if user is a developer' do - project.team << [user, :developer] + project.add_developer(user) expect(access.can_push_to_branch?('random_branch')).to be_truthy end it 'returns false if user is a reporter' do - project.team << [user, :reporter] + project.add_reporter(user) expect(access.can_push_to_branch?('random_branch')).to be_falsey end @@ -31,34 +31,34 @@ describe Gitlab::UserAccess do let(:project_access) { described_class.new(user, project: empty_project) } it 'returns true if user is master' do - empty_project.team << [user, :master] + empty_project.add_master(user) expect(project_access.can_push_to_branch?('master')).to be_truthy end it 'returns false if user is developer and project is fully protected' do - empty_project.team << [user, :developer] + empty_project.add_developer(user) stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL) expect(project_access.can_push_to_branch?('master')).to be_falsey end it 'returns false if user is developer and it is not allowed to push new commits but can merge into branch' do - empty_project.team << [user, :developer] + empty_project.add_developer(user) stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE) expect(project_access.can_push_to_branch?('master')).to be_falsey end it 'returns true if user is developer and project is unprotected' do - empty_project.team << [user, :developer] + empty_project.add_developer(user) stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE) expect(project_access.can_push_to_branch?('master')).to be_truthy end it 'returns true if user is developer and project grants developers permission' do - empty_project.team << [user, :developer] + empty_project.add_developer(user) stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) expect(project_access.can_push_to_branch?('master')).to be_truthy @@ -70,25 +70,25 @@ describe Gitlab::UserAccess do let(:not_existing_branch) { create :protected_branch, :developers_can_merge, project: project } it 'returns true if user is a master' do - project.team << [user, :master] + project.add_master(user) expect(access.can_push_to_branch?(branch.name)).to be_truthy end it 'returns false if user is a developer' do - project.team << [user, :developer] + project.add_developer(user) expect(access.can_push_to_branch?(branch.name)).to be_falsey end it 'returns false if user is a reporter' do - project.team << [user, :reporter] + project.add_reporter(user) expect(access.can_push_to_branch?(branch.name)).to be_falsey end it 'returns false if branch does not exist' do - project.team << [user, :developer] + project.add_developer(user) expect(access.can_push_to_branch?(not_existing_branch.name)).to be_falsey end @@ -100,19 +100,19 @@ describe Gitlab::UserAccess do end it 'returns true if user is a master' do - project.team << [user, :master] + project.add_master(user) expect(access.can_push_to_branch?(@branch.name)).to be_truthy end it 'returns true if user is a developer' do - project.team << [user, :developer] + project.add_developer(user) expect(access.can_push_to_branch?(@branch.name)).to be_truthy end it 'returns false if user is a reporter' do - project.team << [user, :reporter] + project.add_reporter(user) expect(access.can_push_to_branch?(@branch.name)).to be_falsey end @@ -124,19 +124,19 @@ describe Gitlab::UserAccess do end it 'returns true if user is a master' do - project.team << [user, :master] + project.add_master(user) expect(access.can_merge_to_branch?(@branch.name)).to be_truthy end it 'returns true if user is a developer' do - project.team << [user, :developer] + project.add_developer(user) expect(access.can_merge_to_branch?(@branch.name)).to be_truthy end it 'returns false if user is a reporter' do - project.team << [user, :reporter] + project.add_reporter(user) expect(access.can_merge_to_branch?(@branch.name)).to be_falsey end diff --git a/spec/lib/gitlab/utils/strong_memoize_spec.rb b/spec/lib/gitlab/utils/strong_memoize_spec.rb index 4a104ab6d97..473f8100771 100644 --- a/spec/lib/gitlab/utils/strong_memoize_spec.rb +++ b/spec/lib/gitlab/utils/strong_memoize_spec.rb @@ -49,4 +49,16 @@ describe Gitlab::Utils::StrongMemoize do end end end + + describe '#clear_memoization' do + let(:value) { 'mepmep' } + + it 'removes the instance variable' do + object.method_name + + object.clear_memoization(:method_name) + + expect(object.instance_variable_defined?(:@method_name)).to be(false) + end + end end diff --git a/spec/lib/gitlab/view/presenter/factory_spec.rb b/spec/lib/gitlab/view/presenter/factory_spec.rb index 70d2e22b48f..6120bafb2e3 100644 --- a/spec/lib/gitlab/view/presenter/factory_spec.rb +++ b/spec/lib/gitlab/view/presenter/factory_spec.rb @@ -27,5 +27,13 @@ describe Gitlab::View::Presenter::Factory do expect(presenter).to be_a(Ci::BuildPresenter) end + + it 'uses the presenter_class if given on #initialize' do + MyCustomPresenter = Class.new(described_class) + + presenter = described_class.new(build, presenter_class: MyCustomPresenter).fabricate! + + expect(presenter).to be_a(MyCustomPresenter) + end end end diff --git a/spec/lib/gitlab/visibility_level_spec.rb b/spec/lib/gitlab/visibility_level_spec.rb index 48a67773de9..d85dac630b4 100644 --- a/spec/lib/gitlab/visibility_level_spec.rb +++ b/spec/lib/gitlab/visibility_level_spec.rb @@ -49,4 +49,31 @@ describe Gitlab::VisibilityLevel do .to eq([Gitlab::VisibilityLevel::PUBLIC]) end end + + describe '.allowed_levels' do + it 'only includes the levels that arent restricted' do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL]) + + expect(described_class.allowed_levels) + .to contain_exactly(described_class::PRIVATE, described_class::PUBLIC) + end + end + + describe '.closest_allowed_level' do + it 'picks INTERNAL instead of PUBLIC if public is restricted' do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + + expect(described_class.closest_allowed_level(described_class::PUBLIC)) + .to eq(described_class::INTERNAL) + end + + it 'picks PRIVATE if nothing is available' do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC, + Gitlab::VisibilityLevel::INTERNAL, + Gitlab::VisibilityLevel::PRIVATE]) + + expect(described_class.closest_allowed_level(described_class::PUBLIC)) + .to eq(described_class::PRIVATE) + end + end end diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb index ecb4034ec8b..f65e41dfea3 100644 --- a/spec/lib/google_api/cloud_platform/client_spec.rb +++ b/spec/lib/google_api/cloud_platform/client_spec.rb @@ -50,6 +50,30 @@ describe GoogleApi::CloudPlatform::Client do end end + describe '#projects_list' do + subject { client.projects_list } + let(:projects) { double } + + before do + allow_any_instance_of(Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService) + .to receive(:fetch_all).and_return(projects) + end + + it { is_expected.to eq(projects) } + end + + describe '#projects_get_billing_info' do + subject { client.projects_get_billing_info('project') } + let(:billing_info) { double } + + before do + allow_any_instance_of(Google::Apis::CloudbillingV1::CloudbillingService) + .to receive(:get_project_billing_info).and_return(billing_info) + end + + it { is_expected.to eq(billing_info) } + end + describe '#projects_zones_clusters_get' do subject { client.projects_zones_clusters_get(spy, spy, spy) } let(:gke_cluster) { double } |