diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-13 13:26:31 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-13 13:26:31 +0000 |
commit | b7dfe2ae4054aa40e15182fd3c6cb7dd39f131db (patch) | |
tree | 5ab080ca9cadeb6cd9578bf301e4e9e8810bed9e /spec | |
parent | 25cb337cf12438169f1b14bc5dace8a06a7356e3 (diff) | |
download | gitlab-ce-b7dfe2ae4054aa40e15182fd3c6cb7dd39f131db.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
43 files changed, 719 insertions, 46 deletions
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index bd3e66efd58..b455b55bd11 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -265,7 +265,8 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do let(:job) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) } before do - create(:deployment, :success, environment: environment, project: project) + create(:deployment, :success, :on_cluster, environment: environment, project: project) + project.add_maintainer(user) # Need to be a maintianer to view cluster.path end it 'exposes the deployment information' do @@ -276,8 +277,9 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do expect(json_response.dig('deployment_status', 'status')).to eq 'creating' expect(json_response.dig('deployment_status', 'environment')).not_to be_nil expect(json_response.dig('deployment_status', 'environment', 'last_deployment')).not_to be_nil - expect(json_response.dig('deployment_status', 'environment', 'last_deployment')) - .not_to include('commit') + expect(json_response.dig('deployment_status', 'environment', 'last_deployment')).not_to include('commit') + expect(json_response.dig('deployment_status', 'environment', 'last_deployment', 'cluster', 'name')).to eq('test-cluster') + expect(json_response.dig('deployment_status', 'environment', 'last_deployment', 'cluster', 'path')).to be_present end end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 52af470efac..5216683bd36 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -4,6 +4,8 @@ require 'spec_helper' require Rails.root.join('ee', 'spec', 'db', 'schema_support') if Gitlab.ee? describe 'Database schema' do + prepend_if_ee('EE::DB::SchemaSupport') + let(:connection) { ActiveRecord::Base.connection } let(:tables) { connection.tables } diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb index 89ff1c527df..50dc304a10e 100644 --- a/spec/factories/deployments.rb +++ b/spec/factories/deployments.rb @@ -17,6 +17,10 @@ FactoryBot.define do unless deployment.project.repository_exists? allow(deployment.project.repository).to receive(:create_ref) end + + if deployment.cluster && deployment.cluster.project_type? && deployment.cluster.project.nil? + deployment.cluster.projects << deployment.project + end end trait :review_app do diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index d1783de0330..cebca338f33 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -534,9 +534,32 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do end it 'shows deployment message' do - expect(page).to have_content 'This job is the most recent deployment' + expect(page).to have_content 'This job is the most recent deployment to production' expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}") end + + context 'when there is a cluster used for the deployment' do + let(:cluster) { create(:cluster, name: 'the-cluster') } + let(:deployment) { create(:deployment, :success, cluster: cluster, environment: environment, project: environment.project) } + let(:user_access_level) { :maintainer } + + it 'shows a link to the cluster' do + expect(page).to have_link 'the-cluster' + end + + it 'shows the name of the cluster' do + expect(page).to have_content 'Cluster the-cluster was used' + end + + context 'when the user is not able to view the cluster' do + let(:user_access_level) { :developer } + + it 'includes only the name of the cluster without a link' do + expect(page).to have_content 'Cluster the-cluster was used' + expect(page).not_to have_link 'the-cluster' + end + end + end end context 'job is complete and not successful' do diff --git a/spec/fixtures/api/schemas/cluster_basic.json b/spec/fixtures/api/schemas/cluster_basic.json new file mode 100644 index 00000000000..6f0e77997f0 --- /dev/null +++ b/spec/fixtures/api/schemas/cluster_basic.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { "type": "string" }, + "path": { + "oneOf": [ + { "type": "null" }, + { "type": "string" } + ] + } + }, + "additionalProperties": false +} diff --git a/spec/fixtures/api/schemas/deployment.json b/spec/fixtures/api/schemas/deployment.json index fe725b97c21..81c2d1ef5ab 100644 --- a/spec/fixtures/api/schemas/deployment.json +++ b/spec/fixtures/api/schemas/deployment.json @@ -47,6 +47,12 @@ { "$ref": "job/job.json" } ] }, + "cluster": { + "oneOf": [ + { "type": "null" }, + { "$ref": "cluster_basic.json" } + ] + }, "manual_actions": { "type": "array", "items": { "$ref": "job/job.json" } diff --git a/spec/fixtures/api/schemas/environment.json b/spec/fixtures/api/schemas/environment.json index 5b1e3c049fa..e38b05ec008 100644 --- a/spec/fixtures/api/schemas/environment.json +++ b/spec/fixtures/api/schemas/environment.json @@ -28,6 +28,8 @@ "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "can_stop": { "type": "boolean" }, + "cluster_type": { "type": "types/nullable_string.json" }, + "terminal_path": { "type": "types/nullable_string.json" }, "last_deployment": { "oneOf": [ { "type": "null" }, diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js index 98077498998..ee3b7d8aa90 100644 --- a/spec/frontend/clusters/stores/clusters_store_spec.js +++ b/spec/frontend/clusters/stores/clusters_store_spec.js @@ -152,6 +152,7 @@ describe('Clusters Store', () => { }, }, environments: [], + fetchingEnvironments: false, }); }); diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 039143eb8d7..d15b5a4ab58 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -142,7 +142,7 @@ describe IssuesHelper do expect(link_to_discussions_to_resolve(merge_request, nil)).to include(expected_path) end - it "containst the reference to the merge request" do + it "contains the reference to the merge request" do expect(link_to_discussions_to_resolve(merge_request, nil)).to include(merge_request.to_reference) end end @@ -185,4 +185,79 @@ describe IssuesHelper do expect(helper.show_new_issue_link?(project)).to be_truthy end end + + describe '#issue_closed_link' do + let(:new_issue) { create(:issue, project: project) } + let(:guest) { create(:user) } + + before do + allow(helper).to receive(:can?) do |*args| + Ability.allowed?(*args) + end + end + + shared_examples 'successfully displays link to issue and with css class' do |action| + it 'returns link' do + link = "<a class=\"#{css_class}\" href=\"/#{new_issue.project.full_path}/issues/#{new_issue.iid}\">(#{action})</a>" + + expect(helper.issue_closed_link(issue, user, css_class: css_class)).to match(link) + end + end + + shared_examples 'does not display link' do + it 'returns nil' do + expect(helper.issue_closed_link(issue, user)).to be_nil + end + end + + context 'with linked issue' do + context 'with moved issue' do + before do + issue.update(moved_to: new_issue) + end + + context 'when user has permission to see new issue' do + let(:user) { project.owner } + let(:css_class) { 'text-white text-underline' } + + it_behaves_like 'successfully displays link to issue and with css class', 'moved' + end + + context 'when user has no permission to see new issue' do + let(:user) { guest } + + it_behaves_like 'does not display link' + end + end + + context 'with duplicated issue' do + before do + issue.update(duplicated_to: new_issue) + end + + context 'when user has permission to see new issue' do + let(:user) { project.owner } + let(:css_class) { 'text-white text-underline' } + + it_behaves_like 'successfully displays link to issue and with css class', 'duplicated' + end + + context 'when user has no permission to see new issue' do + let(:user) { guest } + + it_behaves_like 'does not display link' + end + end + end + + context 'without linked issue' do + let(:user) { project.owner } + + before do + issue.update(moved_to: nil, duplicated_to: nil) + end + + it_behaves_like 'does not display link' + end + end end diff --git a/spec/helpers/onboarding_experiment_helper_spec.rb b/spec/helpers/onboarding_experiment_helper_spec.rb index 5b7d9b1c2e6..cada91bff3c 100644 --- a/spec/helpers/onboarding_experiment_helper_spec.rb +++ b/spec/helpers/onboarding_experiment_helper_spec.rb @@ -4,17 +4,17 @@ require 'spec_helper' describe OnboardingExperimentHelper, type: :helper do describe '.allow_access_to_onboarding?' do - context "when we're not gitlab.com" do + context "when we're not gitlab.com or dev env" do it 'returns false' do - allow(::Gitlab).to receive(:com?).and_return(false) + allow(::Gitlab).to receive(:dev_env_or_com?).and_return(false) expect(helper.allow_access_to_onboarding?).to be(false) end end - context "when we're gitlab.com" do + context "when we're gitlab.com or dev env" do before do - allow(::Gitlab).to receive(:com?).and_return(true) + allow(::Gitlab).to receive(:dev_env_or_com?).and_return(true) end context 'and the :user_onboarding feature is not enabled' do diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js index 8c7820ddb52..00bc552bd7d 100644 --- a/spec/javascripts/gl_dropdown_spec.js +++ b/spec/javascripts/gl_dropdown_spec.js @@ -243,14 +243,23 @@ describe('glDropdown', function describeDropdown() { }); describe('renderItem', () => { + function dropdownWithOptions(options) { + const $dropdownDiv = $('<div />'); + + $dropdownDiv.glDropdown(options); + + return $dropdownDiv.data('glDropdown'); + } + + function basicDropdown() { + return dropdownWithOptions({}); + } + describe('without selected value', () => { let dropdown; beforeEach(() => { - const dropdownOptions = {}; - const $dropdownDiv = $('<div />'); - $dropdownDiv.glDropdown(dropdownOptions); - dropdown = $dropdownDiv.data('glDropdown'); + dropdown = basicDropdown(); }); it('marks items without ID as active', () => { @@ -275,6 +284,35 @@ describe('glDropdown', function describeDropdown() { expect(link).not.toHaveClass('is-active'); }); }); + + it('should return an empty .separator li when when appropriate', () => { + const dropdown = basicDropdown(); + const sep = { type: 'separator' }; + const li = dropdown.renderItem(sep); + + expect(li).toHaveClass('separator'); + expect(li.childNodes.length).toEqual(0); + }); + + it('should return an empty .divider li when when appropriate', () => { + const dropdown = basicDropdown(); + const div = { type: 'divider' }; + const li = dropdown.renderItem(div); + + expect(li).toHaveClass('divider'); + expect(li.childNodes.length).toEqual(0); + }); + + it('should return a .dropdown-header li with the correct content when when appropriate', () => { + const dropdown = basicDropdown(); + const text = 'My Header'; + const header = { type: 'header', content: text }; + const li = dropdown.renderItem(header); + + expect(li).toHaveClass('dropdown-header'); + expect(li.childNodes.length).toEqual(1); + expect(li.textContent).toEqual(text); + }); }); it('should keep selected item after selecting a second time', () => { diff --git a/spec/javascripts/jobs/components/environments_block_spec.js b/spec/javascripts/jobs/components/environments_block_spec.js index 0866ddd21d8..4bbc5f5a348 100644 --- a/spec/javascripts/jobs/components/environments_block_spec.js +++ b/spec/javascripts/jobs/components/environments_block_spec.js @@ -18,6 +18,8 @@ describe('Environments block', () => { name: 'environment', }; + const lastDeployment = { iid: 'deployment', deployable: { build_path: 'bar' } }; + afterEach(() => { vm.$destroy(); }); @@ -45,7 +47,7 @@ describe('Environments block', () => { deploymentStatus: { status: 'out_of_date', environment: Object.assign({}, environment, { - last_deployment: { iid: 'deployment', deployable: { build_path: 'bar' } }, + last_deployment: lastDeployment, }), }, iconStatus: status, @@ -99,10 +101,7 @@ describe('Environments block', () => { deploymentStatus: { status: 'creating', environment: Object.assign({}, environment, { - last_deployment: { - iid: 'deployment', - deployable: { build_path: 'foo' }, - }, + last_deployment: lastDeployment, }), }, iconStatus: status, @@ -112,7 +111,7 @@ describe('Environments block', () => { 'This job is creating a deployment to environment and will overwrite the latest deployment.', ); - expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('foo'); + expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('bar'); }); }); @@ -146,4 +145,71 @@ describe('Environments block', () => { }); }); }); + + describe('with a cluster', () => { + it('renders the cluster link', () => { + const cluster = { + name: 'the-cluster', + path: '/the-cluster-path', + }; + vm = mountComponent(Component, { + deploymentStatus: { + status: 'last', + environment: Object.assign({}, environment, { + last_deployment: { + ...lastDeployment, + cluster, + }, + }), + }, + iconStatus: status, + }); + + expect(vm.$el.textContent.trim()).toContain('Cluster the-cluster was used.'); + + expect(vm.$el.querySelector('.js-job-cluster-link').getAttribute('href')).toEqual( + '/the-cluster-path', + ); + }); + + describe('when the cluster is missing the path', () => { + it('renders the name without a link', () => { + const cluster = { + name: 'the-cluster', + }; + vm = mountComponent(Component, { + deploymentStatus: { + status: 'last', + environment: Object.assign({}, environment, { + last_deployment: { + ...lastDeployment, + cluster, + }, + }), + }, + iconStatus: status, + }); + + expect(vm.$el.textContent.trim()).toContain('Cluster the-cluster was used.'); + + expect(vm.$el.querySelector('.js-job-cluster-link')).toBeNull(); + }); + }); + }); + + describe('without a cluster', () => { + it('does not render a cluster link', () => { + vm = mountComponent(Component, { + deploymentStatus: { + status: 'last', + environment: Object.assign({}, environment, { + last_deployment: lastDeployment, + }), + }, + iconStatus: status, + }); + + expect(vm.$el.querySelector('.js-job-cluster-link')).toBeNull(); + }); + }); }); diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index c9fde06cbae..6b5a355e598 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -14,6 +14,7 @@ describe Gitlab::Email::Receiver do allow(handler).to receive(:execute) allow(handler).to receive(:metrics_params) + allow(handler).to receive(:metrics_event) end it "finds the mail key" do @@ -46,4 +47,12 @@ describe Gitlab::Email::Receiver do expect { receiver.execute }.to raise_error(Gitlab::Email::AutoGeneratedEmailError) end end + + it "requires all handlers to have a unique metric_event" do + events = Gitlab::Email::Handler.handlers.map do |handler| + handler.new(Mail::Message.new, 'gitlabhq/gitlabhq+auth_token').metrics_event + end + + expect(events.uniq.count).to eq events.count + end end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index b9c21b3a7bd..d584cdbe280 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -943,7 +943,7 @@ describe Gitlab::GitAccess do changes = ['6f6d7e7ed 570e7b2ab refs/heads/master', '6f6d7e7ed 570e7b2ab refs/heads/feature'] # There is still an N+1 query with protected branches - expect { access.check('git-receive-pack', changes) }.not_to exceed_query_limit(control_count).with_threshold(1) + expect { access.check('git-receive-pack', changes) }.not_to exceed_query_limit(control_count).with_threshold(2) end it 'raises TimeoutError when #check_single_change_access raises a timeout error' do diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb index b06d05c1c7f..6013fb78bc7 100644 --- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb +++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb @@ -23,6 +23,7 @@ describe Gitlab::HookData::IssueBuilder do last_edited_by_id milestone_id moved_to_id + duplicated_to_id project_id relative_position state diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index e496ab4cd35..6d573a4f39a 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -14,6 +14,7 @@ issues: - todos - user_agent_detail - moved_to +- duplicated_to - events - merge_requests_closing_issues - metrics diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index e9750d23c53..2f178648838 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -19,6 +19,7 @@ Issue: - closed_by_id - due_date - moved_to_id +- duplicated_to_id - lock_version - milestone_id - weight diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb index 6d1d7e48326..6ce002ad70e 100644 --- a/spec/lib/gitlab/url_blocker_spec.rb +++ b/spec/lib/gitlab/url_blocker_spec.rb @@ -30,8 +30,12 @@ describe Gitlab::UrlBlocker do context 'when URI is internal' do let(:import_url) { 'http://localhost' } + before do + stub_dns(import_url, ip_address: '127.0.0.1') + end + it_behaves_like 'validates URI and hostname' do - let(:expected_uri) { 'http://[::1]' } + let(:expected_uri) { 'http://127.0.0.1' } let(:expected_hostname) { 'localhost' } end end @@ -347,6 +351,7 @@ describe Gitlab::UrlBlocker do end before do + allow(ApplicationSetting).to receive(:current).and_return(ApplicationSetting.new) stub_application_setting(outbound_local_requests_whitelist: whitelist) end @@ -384,9 +389,15 @@ describe Gitlab::UrlBlocker do it_behaves_like 'allows local requests', { allow_localhost: false, allow_local_network: false } it 'whitelists IP when dns_rebind_protection is disabled' do - stub_domain_resolv('example.com', '192.168.1.1') do - expect(described_class).not_to be_blocked_url("http://example.com", - url_blocker_attributes.merge(dns_rebind_protection: false)) + url = "http://example.com" + attrs = url_blocker_attributes.merge(dns_rebind_protection: false) + + stub_domain_resolv('example.com', '192.168.1.2') do + expect(described_class).not_to be_blocked_url(url, attrs) + end + + stub_domain_resolv('example.com', '192.168.1.3') do + expect(described_class).to be_blocked_url(url, attrs) end end end @@ -437,6 +448,51 @@ describe Gitlab::UrlBlocker do url_blocker_attributes) end end + + shared_examples 'dns rebinding checks' do + shared_examples 'whitelists the domain' do + let(:whitelist) { [domain] } + let(:url) { "http://#{domain}" } + + before do + stub_env('RSPEC_ALLOW_INVALID_URLS', 'false') + end + + it do + expect(described_class).not_to be_blocked_url(url, dns_rebind_protection: dns_rebind_value) + end + end + + context 'when dns_rebinding_setting is' do + context 'enabled' do + let(:dns_rebind_value) { true } + + it_behaves_like 'whitelists the domain' + end + + context 'disabled' do + let(:dns_rebind_value) { false } + + it_behaves_like 'whitelists the domain' + end + end + end + + context 'when the domain cannot be resolved' do + let(:domain) { 'foobar.x' } + + it_behaves_like 'dns rebinding checks' + end + + context 'when the domain can be resolved' do + let(:domain) { 'example.com' } + + before do + stub_dns(url, ip_address: '93.184.216.34') + end + + it_behaves_like 'dns rebinding checks' + end end context 'with ip ranges in whitelist' do diff --git a/spec/lib/gitlab/url_blockers/url_whitelist_spec.rb b/spec/lib/gitlab/url_blockers/url_whitelist_spec.rb new file mode 100644 index 00000000000..906e0f0ba3d --- /dev/null +++ b/spec/lib/gitlab/url_blockers/url_whitelist_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::UrlBlockers::UrlWhitelist do + include StubRequests + + let(:whitelist) { [] } + + before do + allow(ApplicationSetting).to receive(:current).and_return(ApplicationSetting.new) + stub_application_setting(outbound_local_requests_whitelist: whitelist) + end + + describe '#domain_whitelisted?' do + let(:whitelist) do + [ + 'www.example.com', + 'example.com' + ] + end + + it 'returns true if domains present in whitelist' do + aggregate_failures do + whitelist.each do |domain| + expect(described_class).to be_domain_whitelisted(domain) + end + + ['subdomain.example.com', 'example.org'].each do |domain| + expect(described_class).not_to be_domain_whitelisted(domain) + end + end + end + + it 'returns false when domain is blank' do + expect(described_class).not_to be_domain_whitelisted(nil) + end + end + + describe '#ip_whitelisted?' do + let(:whitelist) do + [ + '0.0.0.0', + '127.0.0.1', + '192.168.1.1', + '0:0:0:0:0:ffff:192.168.1.2', + '::ffff:c0a8:102', + 'fc00:bf8b:e62c:abcd:abcd:aaaa:aaaa:aaaa', + '0:0:0:0:0:ffff:169.254.169.254', + '::ffff:a9fe:a9fe', + '::ffff:a9fe:a864', + 'fe80::c800:eff:fe74:8' + ] + end + + it 'returns true if ips present in whitelist' do + aggregate_failures do + whitelist.each do |ip_address| + expect(described_class).to be_ip_whitelisted(ip_address) + end + + ['172.16.2.2', '127.0.0.2', 'fe80::c800:eff:fe74:9'].each do |ip_address| + expect(described_class).not_to be_ip_whitelisted(ip_address) + end + end + end + + it 'returns false when ip is blank' do + expect(described_class).not_to be_ip_whitelisted(nil) + end + end +end diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb index 589dac61528..ccb5cb3aa43 100644 --- a/spec/lib/gitlab_spec.rb +++ b/spec/lib/gitlab_spec.rb @@ -21,23 +21,23 @@ describe Gitlab do context 'when a REVISION file exists' do before do expect(File).to receive(:exist?) - .with(described_class.root.join('REVISION')) - .and_return(true) + .with(described_class.root.join('REVISION')) + .and_return(true) end it 'returns the actual Git revision' do expect(File).to receive(:read) - .with(described_class.root.join('REVISION')) - .and_return("abc123\n") + .with(described_class.root.join('REVISION')) + .and_return("abc123\n") expect(described_class.revision).to eq('abc123') end it 'memoizes the revision' do expect(File).to receive(:read) - .once - .with(described_class.root.join('REVISION')) - .and_return("abc123\n") + .once + .with(described_class.root.join('REVISION')) + .and_return("abc123\n") 2.times { described_class.revision } end @@ -47,8 +47,8 @@ describe Gitlab do context 'when the Git command succeeds' do before do expect(Gitlab::Popen).to receive(:popen_with_detail) - .with(cmd) - .and_return(Gitlab::Popen::Result.new(cmd, 'abc123', '', double(success?: true))) + .with(cmd) + .and_return(Gitlab::Popen::Result.new(cmd, 'abc123', '', double(success?: true))) end it 'returns the actual Git revision' do @@ -59,8 +59,8 @@ describe Gitlab do context 'when the Git command fails' do before do expect(Gitlab::Popen).to receive(:popen_with_detail) - .with(cmd) - .and_return(Gitlab::Popen::Result.new(cmd, '', 'fatal: Not a git repository', double('Process::Status', success?: false))) + .with(cmd) + .and_return(Gitlab::Popen::Result.new(cmd, '', 'fatal: Not a git repository', double('Process::Status', success?: false))) end it 'returns "Unknown"' do @@ -123,6 +123,27 @@ describe Gitlab do end end + describe '.dev_env_or_com?' do + it 'is true when on .com' do + allow(described_class).to receive(:com?).and_return(true) + + expect(described_class.dev_env_or_com?).to eq true + end + + it 'is true when dev env' do + allow(described_class).to receive(:com?).and_return(false) + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) + + expect(described_class.dev_env_or_com?).to eq true + end + + it 'is false when not dev or com' do + allow(described_class).to receive(:com?).and_return(false) + + expect(described_class.dev_env_or_com?).to eq false + end + end + describe '.ee?' do before do described_class.instance_variable_set(:@is_ee, nil) @@ -138,12 +159,12 @@ describe Gitlab do allow(described_class) .to receive(:root) - .and_return(root) + .and_return(root) allow(root) .to receive(:join) - .with('ee/app/models/license.rb') - .and_return(license_path) + .with('ee/app/models/license.rb') + .and_return(license_path) expect(described_class.ee?).to eq(true) end @@ -154,12 +175,12 @@ describe Gitlab do allow(described_class) .to receive(:root) - .and_return(Pathname.new('dummy')) + .and_return(Pathname.new('dummy')) allow(root) .to receive(:join) - .with('ee/app/models/license.rb') - .and_return(license_path) + .with('ee/app/models/license.rb') + .and_return(license_path) expect(described_class.ee?).to eq(false) end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 2e7d78d77a8..9c58d307c4c 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -7,6 +7,10 @@ describe Issue do describe "Associations" do it { is_expected.to belong_to(:milestone) } + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:moved_to).class_name('Issue') } + it { is_expected.to belong_to(:duplicated_to).class_name('Issue') } + it { is_expected.to belong_to(:closed_by).class_name('User') } it { is_expected.to have_many(:assignees) } end @@ -310,6 +314,22 @@ describe Issue do end end + describe '#duplicated?' do + let(:issue) { create(:issue) } + subject { issue.duplicated? } + + context 'issue not duplicated' do + it { is_expected.to eq false } + end + + context 'issue already duplicated' do + let(:duplicated_to_issue) { create(:issue) } + let(:issue) { create(:issue, duplicated_to: duplicated_to_issue) } + + it { is_expected.to eq true } + end + end + describe '#suggested_branch_name' do let(:repository) { double } diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 11234982dd4..fde1b096c76 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -195,7 +195,6 @@ describe MergeRequest do with_them do it "validates source_branch" do subject = build(:merge_request, source_branch: branch_name, target_branch: 'master') - subject.valid? expect(subject.errors.added?(:source_branch)).to eq(!valid) @@ -203,7 +202,6 @@ describe MergeRequest do it "validates target_branch" do subject = build(:merge_request, source_branch: 'master', target_branch: branch_name) - subject.valid? expect(subject.errors.added?(:target_branch)).to eq(!valid) diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index 5c9a5b73ee5..e58f1b7d9dc 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -1,4 +1,4 @@ -require "spec_helper" +require 'spec_helper' describe 'Git HTTP requests' do include ProjectForksHelper diff --git a/spec/rubocop/cop/graphql/authorize_types_spec.rb b/spec/rubocop/cop/graphql/authorize_types_spec.rb index eae3e176d64..af4315ecd34 100644 --- a/spec/rubocop/cop/graphql/authorize_types_spec.rb +++ b/spec/rubocop/cop/graphql/authorize_types_spec.rb @@ -11,6 +11,23 @@ describe RuboCop::Cop::Graphql::AuthorizeTypes do subject(:cop) { described_class.new } + context 'when NOT in a type folder' do + before do + allow(cop).to receive(:in_type?).and_return(false) + end + + it 'does not add an offense even though there is no authorize call' do + expect_no_offenses(<<~TYPE.strip) + module Types + class AType < BaseObject + field :a_thing + field :another_thing + end + end + TYPE + end + end + context 'when in a type folder' do before do allow(cop).to receive(:in_type?).and_return(true) diff --git a/spec/rubocop/cop/graphql/descriptions_spec.rb b/spec/rubocop/cop/graphql/descriptions_spec.rb new file mode 100644 index 00000000000..8cfdc05172d --- /dev/null +++ b/spec/rubocop/cop/graphql/descriptions_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'rubocop' +require 'rubocop/rspec/support' +require_relative '../../../../rubocop/cop/graphql/descriptions' + +describe RuboCop::Cop::Graphql::Descriptions do + include RuboCop::RSpec::ExpectOffense + include CopHelper + + subject(:cop) { described_class.new } + + context 'fields' do + it 'adds an offense when there is no field description' do + inspect_source(<<~TYPE) + module Types + class FakeType < BaseObject + field :a_thing, + GraphQL::STRING_TYPE, + null: false + end + end + TYPE + + expect(cop.offenses.size).to eq 1 + end + + it 'does not add an offense for fields with a description' do + expect_no_offenses(<<~TYPE.strip) + module Types + class FakeType < BaseObject + graphql_name 'FakeTypeName' + + argument :a_thing, + GraphQL::STRING_TYPE, + null: false, + description: 'A descriptive description' + end + end + TYPE + end + end + + context 'arguments' do + it 'adds an offense when there is no argument description' do + inspect_source(<<~TYPE) + module Types + class FakeType < BaseObject + argument :a_thing, + GraphQL::STRING_TYPE, + null: false + end + end + TYPE + + expect(cop.offenses.size).to eq 1 + end + + it 'does not add an offense for arguments with a description' do + expect_no_offenses(<<~TYPE.strip) + module Types + class FakeType < BaseObject + graphql_name 'FakeTypeName' + + argument :a_thing, + GraphQL::STRING_TYPE, + null: false, + description: 'Behold! A description' + end + end + TYPE + end + end +end diff --git a/spec/serializers/cluster_basic_entity_spec.rb b/spec/serializers/cluster_basic_entity_spec.rb new file mode 100644 index 00000000000..6762eb6ab3d --- /dev/null +++ b/spec/serializers/cluster_basic_entity_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe ClusterBasicEntity do + describe '#as_json' do + subject { described_class.new(cluster, request: request).as_json } + let(:maintainer) { create(:user) } + let(:developer) { create(:user) } + let(:current_user) { maintainer } + let(:request) { double(:request, current_user: current_user) } + let(:project) { create(:project) } + let(:cluster) { create(:cluster, name: 'the-cluster', projects: [project]) } + + before do + project.add_maintainer(maintainer) + project.add_developer(developer) + end + + it 'matches cluster_basic entity schema' do + expect(subject.as_json).to match_schema('cluster_basic') + end + + it 'exposes the cluster details' do + expect(subject[:name]).to eq('the-cluster') + expect(subject[:path]).to eq("/#{project.full_path}/clusters/#{cluster.id}") + end + + context 'when the user does not have permission to view the cluster' do + let(:current_user) { developer } + + it 'does not include the path' do + expect(subject[:path]).to be_nil + end + end + end +end diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb index 0e05b3c84f4..53278062de9 100644 --- a/spec/serializers/issue_entity_spec.rb +++ b/spec/serializers/issue_entity_spec.rb @@ -50,4 +50,44 @@ describe IssueEntity do end end end + + context 'when issue got duplicated' do + let(:private_project) { create(:project, :private) } + let(:member) { create(:user) } + let(:issue) { create(:issue, project: project) } + let(:new_issue) { create(:issue, project: private_project) } + + before do + Issues::DuplicateService + .new(project, member) + .execute(issue, new_issue) + end + + context 'when user cannot read new issue' do + let(:non_member) { create(:user) } + + it 'does not return duplicated_to_id' do + request = double('request', current_user: non_member) + + response = described_class.new(issue, request: request).as_json + + expect(response[:duplicated_to_id]).to be_nil + end + end + + context 'when user can read target project' do + before do + project.add_developer(member) + private_project.add_developer(member) + end + + it 'returns duplicated duplicated_to_id' do + request = double('request', current_user: member) + + response = described_class.new(issue, request: request).as_json + + expect(response[:duplicated_to_id]).to eq(issue.duplicated_to_id) + end + end + end end diff --git a/spec/services/issues/duplicate_service_spec.rb b/spec/services/issues/duplicate_service_spec.rb index 2c9581b993c..41a151b0ca1 100644 --- a/spec/services/issues/duplicate_service_spec.rb +++ b/spec/services/issues/duplicate_service_spec.rb @@ -77,6 +77,12 @@ describe Issues::DuplicateService do subject.execute(duplicate_issue, canonical_issue) end + + it 'updates duplicate issue with canonical issue id' do + subject.execute(duplicate_issue, canonical_issue) + + expect(duplicate_issue.reload.duplicated_to).to eq(canonical_issue) + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 47f09bf14d0..7d44b2340f3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -32,6 +32,8 @@ end require 'rainbow/ext/string' Rainbow.enabled = false +require_relative('../ee/spec/spec_helper') if Gitlab.ee? + # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. # Requires helpers, and shared contexts/examples first since they're used in other support files diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb index 041ffa25535..5da707b11f9 100644 --- a/spec/support/db_cleaner.rb +++ b/spec/support/db_cleaner.rb @@ -13,3 +13,5 @@ module DbCleaner DatabaseCleaner[:active_record, { connection: ActiveRecord::Base }] end end + +DbCleaner.prepend_if_ee('EE::DbCleaner') diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index beb346b2855..4d2ad165fd6 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -172,6 +172,31 @@ module GraphqlHelpers post_graphql(mutation.query, current_user: current_user, variables: mutation.variables) end + # this implements GraphQL multipart request v2 + # https://github.com/jaydenseric/graphql-multipart-request-spec/tree/v2.0.0-alpha.2 + # this is simplified and do not support file deduplication + def mutation_to_apollo_uploads_param(mutation, files: []) + operations = { 'query' => mutation.query, 'variables' => mutation.variables } + map = {} + extracted_files = {} + + files.each_with_index do |file_path, idx| + apollo_idx = (idx + 1).to_s + parent_dig_path = file_path[0..-2] + file_key = file_path[-1] + + parent = operations['variables'] + parent = parent.dig(*parent_dig_path) unless parent_dig_path.empty? + + extracted_files[apollo_idx] = parent[file_key] + parent[file_key] = nil + + map[apollo_idx] = ["variables.#{file_path.join('.')}"] + end + + { operations: operations.to_json, map: map.to_json }.merge(extracted_files) + end + # Raises an error if no data is found def graphql_data json_response['data'] || (raise NoData, graphql_errors) diff --git a/spec/support/helpers/ldap_helpers.rb b/spec/support/helpers/ldap_helpers.rb index dce8a3803f5..0549c56c753 100644 --- a/spec/support/helpers/ldap_helpers.rb +++ b/spec/support/helpers/ldap_helpers.rb @@ -70,3 +70,5 @@ module LdapHelpers .to receive(:ldap_search).and_raise(Gitlab::Auth::LDAP::LDAPConnectionError) end end + +LdapHelpers.include_if_ee('EE::LdapHelpers') diff --git a/spec/support/helpers/license_helper.rb b/spec/support/helpers/license_helper.rb index 4aaad55a8ef..a1defba9ccb 100644 --- a/spec/support/helpers/license_helper.rb +++ b/spec/support/helpers/license_helper.rb @@ -6,3 +6,5 @@ module LicenseHelpers # do nothing end end + +LicenseHelpers.prepend_if_ee('EE::LicenseHelpers') diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb index 2b508ee6f2c..140ad173d38 100644 --- a/spec/support/helpers/login_helpers.rb +++ b/spec/support/helpers/login_helpers.rb @@ -211,3 +211,5 @@ module LoginHelpers allow(Gitlab::Auth::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', external_groups: groups, args: {} } }) end end + +LoginHelpers.prepend_if_ee('EE::LoginHelpers') diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb index 2727ab7fb1e..a3145ed91bd 100644 --- a/spec/support/helpers/migrations_helpers.rb +++ b/spec/support/helpers/migrations_helpers.rb @@ -133,3 +133,5 @@ module MigrationsHelpers end end end + +MigrationsHelpers.prepend_if_ee('EE::MigrationsHelpers') diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb index f364e4fd158..0dc6e851190 100644 --- a/spec/support/helpers/stub_configuration.rb +++ b/spec/support/helpers/stub_configuration.rb @@ -149,3 +149,5 @@ end require_relative '../../../ee/spec/support/helpers/ee/stub_configuration' if Dir.exist?("#{__dir__}/../../../ee") + +StubConfiguration.prepend_if_ee('EE::StubConfiguration') diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb index 7d10cffe920..e3dde888277 100644 --- a/spec/support/helpers/stub_gitlab_calls.rb +++ b/spec/support/helpers/stub_gitlab_calls.rb @@ -139,3 +139,5 @@ module StubGitlabCalls JSON.parse f end end + +StubGitlabCalls.prepend_if_ee('EE::StubGitlabCalls') diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb index e5b8bb712bb..3f7002b8768 100644 --- a/spec/support/helpers/stub_object_storage.rb +++ b/spec/support/helpers/stub_object_storage.rb @@ -78,3 +78,5 @@ end require_relative '../../../ee/spec/support/helpers/ee/stub_object_storage' if Dir.exist?("#{__dir__}/../../../ee") + +StubObjectStorage.prepend_if_ee('EE::StubObjectStorage') diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 8ca362ce2df..3274651ef19 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -418,3 +418,8 @@ module TestEnv true end end + +require_relative('../../../ee/spec/support/helpers/ee/test_env') if Gitlab.ee? + +::TestEnv.prepend_if_ee('::EE::TestEnv') +::TestEnv.extend_if_ee('::EE::TestEnv') diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb index fdbfe53fa39..40007a14b85 100644 --- a/spec/support/helpers/workhorse_helpers.rb +++ b/spec/support/helpers/workhorse_helpers.rb @@ -29,7 +29,8 @@ module WorkhorseHelpers post(url, params: workhorse_params, - headers: workhorse_rewritten_fields_header('file' => file.path) + headers: workhorse_rewritten_fields_header(file_key => file.path), + env: { 'CONTENT_TYPE' => 'multipart/form-data' } ) end diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index 12e8fa83a60..3245f8418b1 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -230,3 +230,5 @@ module RSpec::Matchers::DSL::Macros end end end + +MarkdownMatchers.prepend_if_ee('EE::MarkdownMatchers') diff --git a/spec/views/projects/issues/show.html.haml_spec.rb b/spec/views/projects/issues/show.html.haml_spec.rb index 1ca9eaf8fdb..d734d0b4a20 100644 --- a/spec/views/projects/issues/show.html.haml_spec.rb +++ b/spec/views/projects/issues/show.html.haml_spec.rb @@ -56,7 +56,41 @@ describe 'projects/issues/show' do end end - it 'shows "Closed" if an issue has not been moved' do + context 'when the issue was duplicated' do + let(:new_issue) { create(:issue, project: project, author: user) } + + before do + issue.duplicated_to = new_issue + end + + context 'when user can see the duplicated issue' do + before do + project.add_developer(user) + end + + it 'shows "Closed (duplicated)" if an issue has been duplicated' do + render + + expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (duplicated)') + end + + it 'links "duplicated" to the new issue the original issue was duplicated to' do + render + + expect(rendered).to have_selector("a[href=\"#{issue_path(new_issue)}\"]", text: 'duplicated') + end + end + + context 'when user cannot see duplicated issue' do + it 'does not show duplicated issue link' do + render + + expect(rendered).not_to have_selector("a[href=\"#{issue_path(new_issue)}\"]", text: 'duplicated') + end + end + end + + it 'shows "Closed" if an issue has not been moved or duplicated' do render expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed') diff --git a/spec/views/shared/_label_row.html.haml_spec.rb b/spec/views/shared/_label_row.html.haml_spec.rb index 4cce13aa37c..0764f8480c8 100644 --- a/spec/views/shared/_label_row.html.haml_spec.rb +++ b/spec/views/shared/_label_row.html.haml_spec.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true require 'spec_helper' describe 'shared/_label_row.html.haml' do |