diff options
Diffstat (limited to 'spec')
29 files changed, 419 insertions, 283 deletions
diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb index d2e46d15730..683c57a97f8 100644 --- a/spec/features/clusters/cluster_detail_page_spec.rb +++ b/spec/features/clusters/cluster_detail_page_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe 'Clusterable > Show page' do + include KubernetesHelpers + let(:current_user) { create(:user) } let(:cluster_ingress_help_text_selector) { '.js-ingress-domain-help-text' } let(:hide_modifier_selector) { '.hide' } @@ -83,6 +85,7 @@ describe 'Clusterable > Show page' do shared_examples 'editing a user-provided cluster' do before do + stub_kubeclient_discover(cluster.platform.api_url) clusterable.add_maintainer(current_user) visit cluster_path end diff --git a/spec/features/groups/clusters/user_spec.rb b/spec/features/groups/clusters/user_spec.rb index b661b5cbaef..84a8691a7f2 100644 --- a/spec/features/groups/clusters/user_spec.rb +++ b/spec/features/groups/clusters/user_spec.rb @@ -14,6 +14,7 @@ describe 'User Cluster', :js do allow(Groups::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 } allow_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute) + allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected) end context 'when user does not have a cluster and visits cluster index page' do diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb index fe4f737a7da..31cc09ae911 100644 --- a/spec/features/projects/clusters/user_spec.rb +++ b/spec/features/projects/clusters/user_spec.rb @@ -12,6 +12,7 @@ describe 'User Cluster', :js do allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 } allow_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute) + allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected) end context 'when user does not have a cluster and visits cluster index page' do diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb index af56cb0d4ee..d96e243d96b 100644 --- a/spec/features/projects/settings/operations_settings_spec.rb +++ b/spec/features/projects/settings/operations_settings_spec.rb @@ -46,6 +46,9 @@ describe 'Projects > Settings > For a forked project', :js do wait_for_requests + within '.js-error-tracking-settings' do + click_button('Expand') + end expect(page).to have_content('Sentry API URL') expect(page.body).to include('Error Tracking') expect(page).to have_button('Connect') @@ -86,6 +89,9 @@ describe 'Projects > Settings > For a forked project', :js do wait_for_requests + within '.js-error-tracking-settings' do + click_button('Expand') + end check('Active') fill_in('error-tracking-api-host', with: 'http://sentry.example.com') fill_in('error-tracking-token', with: 'token') diff --git a/spec/frontend/clusters/clusters_bundle_spec.js b/spec/frontend/clusters/clusters_bundle_spec.js index 73897107f67..66b22fa2681 100644 --- a/spec/frontend/clusters/clusters_bundle_spec.js +++ b/spec/frontend/clusters/clusters_bundle_spec.js @@ -209,6 +209,22 @@ describe('Clusters', () => { expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); }); }); + + describe('when cluster is unreachable', () => { + it('should show the unreachable warning container', () => { + cluster.updateContainer(null, 'unreachable'); + + expect(cluster.unreachableContainer.classList.contains('hidden')).toBe(false); + }); + }); + + describe('when cluster has an authentication failure', () => { + it('should show the authentication failure warning container', () => { + cluster.updateContainer(null, 'authentication_failure'); + + expect(cluster.authenticationFailureContainer.classList.contains('hidden')).toBe(false); + }); + }); }); describe('installApplication', () => { diff --git a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap index f0b72343b6e..1b4564303e4 100644 --- a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap +++ b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap @@ -16,7 +16,9 @@ exports[`Repository table row component renders table row 1`] = ` <a class="str-truncated" > + test + </a> <!----> diff --git a/spec/frontend/repository/components/table/index_spec.js b/spec/frontend/repository/components/table/index_spec.js index 6f52cffe077..827927e6d9a 100644 --- a/spec/frontend/repository/components/table/index_spec.js +++ b/spec/frontend/repository/components/table/index_spec.js @@ -3,18 +3,19 @@ import { GlLoadingIcon } from '@gitlab/ui'; import Table from '~/repository/components/table/index.vue'; let vm; +let $apollo; + +function factory(path, data = () => ({})) { + $apollo = { + query: jest.fn().mockReturnValue(Promise.resolve({ data: data() })), + }; -function factory(path, loading = false) { vm = shallowMount(Table, { propsData: { path, }, mocks: { - $apollo: { - queries: { - files: { loading }, - }, - }, + $apollo, }, }); } @@ -39,9 +40,41 @@ describe('Repository table component', () => { ); }); - it('renders loading icon', () => { - factory('/', true); + it('shows loading icon', () => { + factory('/'); + + vm.setData({ isLoadingFiles: true }); + + expect(vm.find(GlLoadingIcon).isVisible()).toBe(true); + }); + + describe('normalizeData', () => { + it('normalizes edge nodes', () => { + const output = vm.vm.normalizeData('blobs', [{ node: '1' }, { node: '2' }]); + + expect(output).toEqual(['1', '2']); + }); + }); + + describe('hasNextPage', () => { + it('returns undefined when hasNextPage is false', () => { + const output = vm.vm.hasNextPage({ + trees: { pageInfo: { hasNextPage: false } }, + submodules: { pageInfo: { hasNextPage: false } }, + blobs: { pageInfo: { hasNextPage: false } }, + }); + + expect(output).toBe(undefined); + }); + + it('returns pageInfo object when hasNextPage is true', () => { + const output = vm.vm.hasNextPage({ + trees: { pageInfo: { hasNextPage: false } }, + submodules: { pageInfo: { hasNextPage: false } }, + blobs: { pageInfo: { hasNextPage: true, nextCursor: 'test' } }, + }); - expect(vm.find(GlLoadingIcon).exists()).toBe(true); + expect(output).toEqual({ hasNextPage: true, nextCursor: 'test' }); + }); }); }); diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js index 216128dce25..6b4508c418e 100644 --- a/spec/frontend/repository/components/table/row_spec.js +++ b/spec/frontend/repository/components/table/row_spec.js @@ -29,9 +29,10 @@ describe('Repository table row component', () => { it('renders table row', () => { factory({ - id: 1, + id: '1', path: 'test', type: 'file', + currentPath: '/', }); expect(vm.element).toMatchSnapshot(); @@ -39,14 +40,15 @@ describe('Repository table row component', () => { it.each` type | component | componentName - ${'folder'} | ${RouterLinkStub} | ${'RouterLink'} + ${'tree'} | ${RouterLinkStub} | ${'RouterLink'} ${'file'} | ${'a'} | ${'hyperlink'} ${'commit'} | ${'a'} | ${'hyperlink'} `('renders a $componentName for type $type', ({ type, component }) => { factory({ - id: 1, + id: '1', path: 'test', type, + currentPath: '/', }); expect(vm.find(component).exists()).toBe(true); @@ -54,14 +56,15 @@ describe('Repository table row component', () => { it.each` type | pushes - ${'folder'} | ${true} + ${'tree'} | ${true} ${'file'} | ${false} ${'commit'} | ${false} - `('pushes new router if type $type is folder', ({ type, pushes }) => { + `('pushes new router if type $type is tree', ({ type, pushes }) => { factory({ - id: 1, + id: '1', path: 'test', type, + currentPath: '/', }); vm.trigger('click'); @@ -75,9 +78,10 @@ describe('Repository table row component', () => { it('renders commit ID for submodule', () => { factory({ - id: 1, + id: '1', path: 'test', type: 'commit', + currentPath: '/', }); expect(vm.find('.commit-sha').text()).toContain('1'); diff --git a/spec/frontend/repository/utils/icon_spec.js b/spec/frontend/repository/utils/icon_spec.js index 52787327bef..3d84705f7ea 100644 --- a/spec/frontend/repository/utils/icon_spec.js +++ b/spec/frontend/repository/utils/icon_spec.js @@ -6,7 +6,7 @@ describe('getIconName', () => { // file types it.each` type | path | icon - ${'folder'} | ${''} | ${'folder'} + ${'tree'} | ${''} | ${'folder'} ${'commit'} | ${''} | ${'archive'} ${'file'} | ${'test.pdf'} | ${'file-pdf-o'} ${'file'} | ${'test.jpg'} | ${'file-image-o'} diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js index cef40117304..f28d2c2a882 100644 --- a/spec/javascripts/jobs/components/job_app_spec.js +++ b/spec/javascripts/jobs/components/job_app_spec.js @@ -90,9 +90,12 @@ describe('Job App ', () => { describe('triggered job', () => { beforeEach(() => { + const aYearAgo = new Date(); + aYearAgo.setFullYear(aYearAgo.getFullYear() - 1); + mock .onGet(props.endpoint) - .replyOnce(200, Object.assign({}, job, { started: '2017-05-24T10:59:52.000+01:00' })); + .replyOnce(200, Object.assign({}, job, { started: aYearAgo.toISOString() })); vm = mountComponentWithStore(Component, { props, store }); }); diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index 4c94e4fdae0..f0a5dc8d0d7 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -295,6 +295,25 @@ describe Banzai::Filter::MilestoneReferenceFilter do end end + shared_examples 'references with HTML entities' do + before do + milestone.update!(title: '<html>') + end + + it 'links to a valid reference' do + doc = reference_filter('See %"<html>"') + + expect(doc.css('a').first.attr('href')).to eq urls.milestone_url(milestone) + expect(doc.text).to eq 'See %<html>' + end + + it 'ignores invalid milestone names and escapes entities' do + act = %(Milestone %"<non valid>") + + expect(reference_filter(act).to_html).to eq act + end + end + shared_context 'project milestones' do let(:reference) { milestone.to_reference(format: :iid) } @@ -307,6 +326,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do it_behaves_like 'cross-project / cross-namespace complete reference' it_behaves_like 'cross-project / same-namespace complete reference' it_behaves_like 'cross project shorthand reference' + it_behaves_like 'references with HTML entities' end shared_context 'group milestones' do @@ -317,6 +337,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do it_behaves_like 'String-based single-word references' it_behaves_like 'String-based multi-word references in quotes' it_behaves_like 'referencing a milestone in a link href' + it_behaves_like 'references with HTML entities' it 'does not support references by IID' do doc = reference_filter("See #{Milestone.reference_prefix}#{milestone.iid}") diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index fd2a29e4ddb..092e9f242b7 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -190,7 +190,6 @@ describe Gitlab::Ci::Config do let(:remote_file_content) do <<~HEREDOC variables: - AUTO_DEVOPS_DOMAIN: domain.example.com POSTGRES_USER: user POSTGRES_PASSWORD: testing-password POSTGRES_ENABLED: "true" @@ -232,7 +231,6 @@ describe Gitlab::Ci::Config do "bundle install --jobs $(nproc) \"${FLAGS[@]}\"" ] variables = { - AUTO_DEVOPS_DOMAIN: "domain.example.com", POSTGRES_USER: "user", POSTGRES_PASSWORD: "testing-password", POSTGRES_ENABLED: "true", diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb index 67e4c289906..c663cf42a83 100644 --- a/spec/lib/gitlab/import_export/members_mapper_spec.rb +++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb @@ -73,6 +73,13 @@ describe Gitlab::ImportExport::MembersMapper do expect(user2.authorized_project?(project)).to be true end + it 'maps an owner as a maintainer' do + exported_members.first['access_level'] = ProjectMember::OWNER + + expect(members_mapper.map[exported_user_id]).to eq(user2.id) + expect(ProjectMember.find_by_user_id(user2.id).access_level).to eq(ProjectMember::MAINTAINER) + end + context 'user is not an admin' do let(:user) { create(:user) } diff --git a/spec/lib/gitlab/omniauth_initializer_spec.rb b/spec/lib/gitlab/omniauth_initializer_spec.rb index d808b4d49e0..f9c0daf1ef1 100644 --- a/spec/lib/gitlab/omniauth_initializer_spec.rb +++ b/spec/lib/gitlab/omniauth_initializer_spec.rb @@ -38,6 +38,28 @@ describe Gitlab::OmniauthInitializer do subject.execute([hash_config]) end + it 'normalizes a String strategy_class' do + hash_config = { 'name' => 'hash', 'args' => { strategy_class: 'OmniAuth::Strategies::OAuth2Generic' } } + + expect(devise_config).to receive(:omniauth).with(:hash, strategy_class: OmniAuth::Strategies::OAuth2Generic) + + subject.execute([hash_config]) + end + + it 'allows a class to be specified in strategy_class' do + hash_config = { 'name' => 'hash', 'args' => { strategy_class: OmniAuth::Strategies::OAuth2Generic } } + + expect(devise_config).to receive(:omniauth).with(:hash, strategy_class: OmniAuth::Strategies::OAuth2Generic) + + subject.execute([hash_config]) + end + + it 'throws an error for an invalid strategy_class' do + hash_config = { 'name' => 'hash', 'args' => { strategy_class: 'OmniAuth::Strategies::Bogus' } } + + expect { subject.execute([hash_config]) }.to raise_error(NameError) + end + it 'configures fail_with_empty_uid for shibboleth' do shibboleth_config = { 'name' => 'shibboleth', 'args' => {} } diff --git a/spec/lib/quality/test_level_spec.rb b/spec/lib/quality/test_level_spec.rb new file mode 100644 index 00000000000..3465c3a050b --- /dev/null +++ b/spec/lib/quality/test_level_spec.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Quality::TestLevel do + describe '#pattern' do + context 'when level is unit' do + it 'returns a pattern' do + expect(subject.pattern(:unit)) + .to eq("spec/{bin,config,db,dependencies,factories,finders,frontend,graphql,helpers,initializers,javascripts,lib,migrations,models,policies,presenters,rack_servers,routing,rubocop,serializers,services,sidekiq,tasks,uploaders,validators,views,workers,elastic_integration}{,/**/}*_spec.rb") + end + end + + context 'when level is integration' do + it 'returns a pattern' do + expect(subject.pattern(:integration)) + .to eq("spec/{controllers,mailers,requests}{,/**/}*_spec.rb") + end + end + + context 'when level is system' do + it 'returns a pattern' do + expect(subject.pattern(:system)) + .to eq("spec/{features}{,/**/}*_spec.rb") + end + end + + context 'with a prefix' do + it 'returns a pattern' do + expect(described_class.new('ee/').pattern(:system)) + .to eq("ee/spec/{features}{,/**/}*_spec.rb") + end + end + + describe 'performance' do + it 'memoizes the pattern for a given level' do + expect(subject.pattern(:system).object_id).to eq(subject.pattern(:system).object_id) + end + + it 'freezes the pattern for a given level' do + expect(subject.pattern(:system)).to be_frozen + end + end + end + + describe '#regexp' do + context 'when level is unit' do + it 'returns a regexp' do + expect(subject.regexp(:unit)) + .to eq(%r{spec/(bin|config|db|dependencies|factories|finders|frontend|graphql|helpers|initializers|javascripts|lib|migrations|models|policies|presenters|rack_servers|routing|rubocop|serializers|services|sidekiq|tasks|uploaders|validators|views|workers|elastic_integration)}) + end + end + + context 'when level is integration' do + it 'returns a regexp' do + expect(subject.regexp(:integration)) + .to eq(%r{spec/(controllers|mailers|requests)}) + end + end + + context 'when level is system' do + it 'returns a regexp' do + expect(subject.regexp(:system)) + .to eq(%r{spec/(features)}) + end + end + + context 'with a prefix' do + it 'returns a regexp' do + expect(described_class.new('ee/').regexp(:system)) + .to eq(%r{ee/spec/(features)}) + end + end + + describe 'performance' do + it 'memoizes the regexp for a given level' do + expect(subject.regexp(:system).object_id).to eq(subject.regexp(:system).object_id) + end + + it 'freezes the regexp for a given level' do + expect(subject.regexp(:system)).to be_frozen + end + end + end + + describe '#level_for' do + it 'returns the correct level for a unit test' do + expect(subject.level_for('spec/models/abuse_report_spec.rb')).to eq(:unit) + end + + it 'returns the correct level for an integration test' do + expect(subject.level_for('spec/mailers/abuse_report_mailer_spec.rb')).to eq(:integration) + end + + it 'returns the correct level for a system test' do + expect(subject.level_for('spec/features/abuse_report_spec.rb')).to eq(:system) + end + + it 'raises an error for an unknown level' do + expect { subject.level_for('spec/unknown/foo_spec.rb') } + .to raise_error(described_class::UnknownTestLevelError, + %r{Test level for spec/unknown/foo_spec.rb couldn't be set. Please rename the file properly or change the test level detection regexes in .+/lib/quality/test_level.rb.}) + end + end +end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 5f2e8aa0baa..bc81c34f7ab 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -2604,30 +2604,6 @@ describe Ci::Build do it { is_expected.to include(ci_config_path) } end - context 'when using auto devops' do - context 'and is enabled' do - before do - project.create_auto_devops!(enabled: true, domain: 'example.com') - end - - it "includes AUTO_DEVOPS_DOMAIN" do - is_expected.to include( - { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true, masked: false }) - end - end - - context 'and is disabled' do - before do - project.create_auto_devops!(enabled: false, domain: 'example.com') - end - - it "includes AUTO_DEVOPS_DOMAIN" do - is_expected.not_to include( - { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true, masked: false }) - end - end - end - context 'when pipeline variable overrides build variable' do before do build.yaml_variables = [{ key: 'MYVAR', value: 'myvar', public: true }] diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 3ee8c340bfe..4739e62289a 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -2,7 +2,10 @@ require 'spec_helper' -describe Clusters::Cluster do +describe Clusters::Cluster, :use_clean_rails_memory_store_caching do + include ReactiveCachingHelpers + include KubernetesHelpers + it_behaves_like 'having unique enum values' subject { build(:cluster) } @@ -23,7 +26,6 @@ describe Clusters::Cluster do it { is_expected.to delegate_method(:status).to(:provider) } it { is_expected.to delegate_method(:status_reason).to(:provider) } - it { is_expected.to delegate_method(:status_name).to(:provider) } it { is_expected.to delegate_method(:on_creation?).to(:provider) } it { is_expected.to delegate_method(:active?).to(:platform_kubernetes).with_prefix } it { is_expected.to delegate_method(:rbac?).to(:platform_kubernetes).with_prefix } @@ -501,28 +503,6 @@ describe Clusters::Cluster do end end - describe '#created?' do - let(:cluster) { create(:cluster, :provided_by_gcp) } - - subject { cluster.created? } - - context 'when status_name is :created' do - before do - allow(cluster).to receive_message_chain(:provider, :status_name).and_return(:created) - end - - it { is_expected.to eq(true) } - end - - context 'when status_name is not :created' do - before do - allow(cluster).to receive_message_chain(:provider, :status_name).and_return(:creating) - end - - it { is_expected.to eq(false) } - end - end - describe '#allow_user_defined_namespace?' do let(:cluster) { create(:cluster, :provided_by_gcp) } @@ -557,62 +537,15 @@ describe Clusters::Cluster do end context 'with no domain on cluster' do - context 'with a project cluster' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - context 'with domain set at instance level' do - before do - stub_application_setting(auto_devops_domain: 'global_domain.com') - - it { is_expected.to eq('global_domain.com') } - end - end - - context 'with domain set on ProjectAutoDevops' do - before do - auto_devops = project.build_auto_devops(domain: 'legacy-ado-domain.com') - auto_devops.save - end - - it { is_expected.to eq('legacy-ado-domain.com') } - end - - context 'with domain set as environment variable on project' do - before do - variable = project.variables.build(key: 'AUTO_DEVOPS_DOMAIN', value: 'project-ado-domain.com') - variable.save - end + let(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } - it { is_expected.to eq('project-ado-domain.com') } + context 'with domain set at instance level' do + before do + stub_application_setting(auto_devops_domain: 'global_domain.com') end - context 'with domain set as environment variable on the group project' do - let(:group) { create(:group) } - - before do - project.update(parent_id: group.id) - variable = group.variables.build(key: 'AUTO_DEVOPS_DOMAIN', value: 'group-ado-domain.com') - variable.save - end - - it { is_expected.to eq('group-ado-domain.com') } - end - end - - context 'with a group cluster' do - let(:cluster) { create(:cluster, :group, :provided_by_gcp) } - - context 'with domain set as environment variable for the group' do - let(:group) { cluster.group } - - before do - variable = group.variables.build(key: 'AUTO_DEVOPS_DOMAIN', value: 'group-ado-domain.com') - variable.save - end - - it { is_expected.to eq('group-ado-domain.com') } - end + it { is_expected.to eq('global_domain.com') } end end end @@ -664,4 +597,139 @@ describe Clusters::Cluster do it { is_expected.to be_truthy } end end + + describe '#status_name' do + subject { cluster.status_name } + + context 'the cluster has a provider' do + let(:cluster) { create(:cluster, :provided_by_gcp) } + + before do + cluster.provider.make_errored! + end + + it { is_expected.to eq :errored } + end + + context 'there is a cached connection status' do + let(:cluster) { create(:cluster, :provided_by_user) } + + before do + allow(cluster).to receive(:connection_status).and_return(:connected) + end + + it { is_expected.to eq :connected } + end + + context 'there is no connection status in the cache' do + let(:cluster) { create(:cluster, :provided_by_user) } + + before do + allow(cluster).to receive(:connection_status).and_return(nil) + end + + it { is_expected.to eq :created } + end + end + + describe '#connection_status' do + let(:cluster) { create(:cluster) } + let(:status) { :connected } + + subject { cluster.connection_status } + + it { is_expected.to be_nil } + + context 'with a cached status' do + before do + stub_reactive_cache(cluster, connection_status: status) + end + + it { is_expected.to eq(status) } + end + end + + describe '#calculate_reactive_cache' do + subject { cluster.calculate_reactive_cache } + + context 'cluster is disabled' do + let(:cluster) { create(:cluster, :disabled) } + + it 'does not populate the cache' do + expect(cluster).not_to receive(:retrieve_connection_status) + + is_expected.to be_nil + end + end + + context 'cluster is enabled' do + let(:cluster) { create(:cluster, :provided_by_user, :group) } + + context 'connection to the cluster is successful' do + before do + stub_kubeclient_discover(cluster.platform.api_url) + end + + it { is_expected.to eq(connection_status: :connected) } + end + + context 'cluster cannot be reached' do + before do + allow(cluster.kubeclient.core_client).to receive(:discover) + .and_raise(SocketError) + end + + it { is_expected.to eq(connection_status: :unreachable) } + end + + context 'cluster cannot be authenticated to' do + before do + allow(cluster.kubeclient.core_client).to receive(:discover) + .and_raise(OpenSSL::X509::CertificateError.new("Certificate error")) + end + + it { is_expected.to eq(connection_status: :authentication_failure) } + end + + describe 'Kubeclient::HttpError' do + let(:error_code) { 403 } + let(:error_message) { "Forbidden" } + + before do + allow(cluster.kubeclient.core_client).to receive(:discover) + .and_raise(Kubeclient::HttpError.new(error_code, error_message, nil)) + end + + it { is_expected.to eq(connection_status: :authentication_failure) } + + context 'generic timeout' do + let(:error_message) { 'Timed out connecting to server'} + + it { is_expected.to eq(connection_status: :unreachable) } + end + + context 'gateway timeout' do + let(:error_message) { '504 Gateway Timeout for GET https://kubernetes.example.com/api/v1'} + + it { is_expected.to eq(connection_status: :unreachable) } + end + end + + context 'an uncategorised error is raised' do + before do + allow(cluster.kubeclient.core_client).to receive(:discover) + .and_raise(StandardError) + end + + it { is_expected.to eq(connection_status: :unknown_failure) } + + it 'notifies Sentry' do + expect(Gitlab::Sentry).to receive(:track_acceptable_exception) + .with(instance_of(StandardError), hash_including(extra: { cluster_id: cluster.id })) + + subject + end + end + end + end end diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index b81e5610e2c..7bdd2367a68 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -14,65 +14,9 @@ describe ProjectAutoDevops do it { is_expected.to respond_to(:created_at) } it { is_expected.to respond_to(:updated_at) } - describe '#has_domain?' do - context 'when domain is defined' do - let(:auto_devops) { build_stubbed(:project_auto_devops, project: project, domain: 'domain.com') } - - it { expect(auto_devops).to have_domain } - end - - context 'when domain is empty' do - let(:auto_devops) { build_stubbed(:project_auto_devops, project: project, domain: '') } - - context 'when there is an instance domain specified' do - before do - allow(Gitlab::CurrentSettings).to receive(:auto_devops_domain).and_return('example.com') - end - - it { expect(auto_devops).to have_domain } - end - - context 'when there is no instance domain specified' do - before do - allow(Gitlab::CurrentSettings).to receive(:auto_devops_domain).and_return(nil) - end - - it { expect(auto_devops).not_to have_domain } - end - end - end - describe '#predefined_variables' do let(:auto_devops) { build_stubbed(:project_auto_devops, project: project, domain: domain) } - context 'when domain is defined' do - let(:domain) { 'example.com' } - - it 'returns AUTO_DEVOPS_DOMAIN' do - expect(auto_devops.predefined_variables).to include(domain_variable) - end - end - - context 'when domain is not defined' do - let(:domain) { nil } - - context 'when there is an instance domain specified' do - before do - allow(Gitlab::CurrentSettings).to receive(:auto_devops_domain).and_return('example.com') - end - - it { expect(auto_devops.predefined_variables).to include(domain_variable) } - end - - context 'when there is no instance domain specified' do - before do - allow(Gitlab::CurrentSettings).to receive(:auto_devops_domain).and_return(nil) - end - - it { expect(auto_devops.predefined_variables).not_to include(domain_variable) } - end - end - context 'when deploy_strategy is manual' do let(:auto_devops) { build_stubbed(:project_auto_devops, :manual_deployment, project: project) } let(:expected_variables) do @@ -105,10 +49,6 @@ describe ProjectAutoDevops do .not_to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED") end end - - def domain_variable - { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true } - end end describe '#create_gitlab_deploy_token' do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 425096d7e80..08662231fdf 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3975,64 +3975,6 @@ describe Project do end end - describe '#auto_devops_variables' do - set(:project) { create(:project) } - - subject { project.auto_devops_variables } - - context 'when enabled in instance settings' do - before do - stub_application_setting(auto_devops_enabled: true) - end - - context 'when domain is empty' do - before do - stub_application_setting(auto_devops_domain: nil) - end - - it 'variables does not include AUTO_DEVOPS_DOMAIN' do - is_expected.not_to include(domain_variable) - end - end - - context 'when domain is configured' do - before do - stub_application_setting(auto_devops_domain: 'example.com') - end - - it 'variables includes AUTO_DEVOPS_DOMAIN' do - is_expected.to include(domain_variable) - end - end - end - - context 'when explicitly enabled' do - context 'when domain is empty' do - before do - create(:project_auto_devops, project: project, domain: nil) - end - - it 'variables does not include AUTO_DEVOPS_DOMAIN' do - is_expected.not_to include(domain_variable) - end - end - - context 'when domain is configured' do - before do - create(:project_auto_devops, project: project, domain: 'example.com') - end - - it 'variables includes AUTO_DEVOPS_DOMAIN' do - is_expected.to include(domain_variable) - end - end - end - - def domain_variable - { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true } - end - end - describe '#latest_successful_builds_for' do let(:project) { build(:project) } diff --git a/spec/presenters/clusters/cluster_presenter_spec.rb b/spec/presenters/clusters/cluster_presenter_spec.rb index 42701a5f8d1..7054a70e2ed 100644 --- a/spec/presenters/clusters/cluster_presenter_spec.rb +++ b/spec/presenters/clusters/cluster_presenter_spec.rb @@ -158,46 +158,6 @@ describe Clusters::ClusterPresenter do it { is_expected.to include(cluster.name) } end - describe '#can_toggle_cluster' do - let(:user) { create(:user) } - - before do - allow(cluster).to receive(:current_user).and_return(user) - end - - subject { described_class.new(cluster).can_toggle_cluster? } - - context 'when user can update' do - before do - allow_any_instance_of(described_class).to receive(:can?).with(user, :update_cluster, cluster).and_return(true) - end - - context 'when cluster is created' do - before do - allow(cluster).to receive(:created?).and_return(true) - end - - it { is_expected.to eq(true) } - end - - context 'when cluster is not created' do - before do - allow(cluster).to receive(:created?).and_return(false) - end - - it { is_expected.to eq(false) } - end - end - - context 'when user can not update' do - before do - allow_any_instance_of(described_class).to receive(:can?).with(user, :update_cluster, cluster).and_return(false) - end - - it { is_expected.to eq(false) } - end - end - describe '#cluster_type_description' do subject { described_class.new(cluster).cluster_type_description } diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb index cc07869a744..55b1419a004 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/variables_spec.rb @@ -43,6 +43,7 @@ describe API::Variables do expect(response).to have_gitlab_http_status(200) expect(json_response['value']).to eq(variable.value) expect(json_response['protected']).to eq(variable.protected?) + expect(json_response['masked']).to eq(variable.masked?) expect(json_response['variable_type']).to eq('env_var') end @@ -74,13 +75,14 @@ describe API::Variables do context 'authorized user with proper permissions' do it 'creates variable' do expect do - post api("/projects/#{project.id}/variables", user), params: { key: 'TEST_VARIABLE_2', value: 'PROTECTED_VALUE_2', protected: true } + post api("/projects/#{project.id}/variables", user), params: { key: 'TEST_VARIABLE_2', value: 'PROTECTED_VALUE_2', protected: true, masked: true } end.to change {project.variables.count}.by(1) expect(response).to have_gitlab_http_status(201) expect(json_response['key']).to eq('TEST_VARIABLE_2') expect(json_response['value']).to eq('PROTECTED_VALUE_2') expect(json_response['protected']).to be_truthy + expect(json_response['masked']).to be_truthy expect(json_response['variable_type']).to eq('env_var') end @@ -93,6 +95,7 @@ describe API::Variables do expect(json_response['key']).to eq('TEST_VARIABLE_2') expect(json_response['value']).to eq('VALUE_2') expect(json_response['protected']).to be_falsey + expect(json_response['masked']).to be_falsey expect(json_response['variable_type']).to eq('file') end diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb index a12646ea222..89adbc77a7f 100644 --- a/spec/requests/rack_attack_global_spec.rb +++ b/spec/requests/rack_attack_global_spec.rb @@ -182,6 +182,17 @@ describe 'Rack Attack global throttles' do end end end + + it 'logs RackAttack info into structured logs' do + requests_per_period.times do + get url_that_does_not_require_authentication + expect(response).to have_http_status 200 + end + + expect(Gitlab::AuthLogger).to receive(:error).once + + get url_that_does_not_require_authentication + end end context 'when the throttle is disabled' do @@ -327,6 +338,17 @@ describe 'Rack Attack global throttles' do expect_rejection { get url_that_requires_authentication } end + + it 'logs RackAttack info into structured logs' do + requests_per_period.times do + get url_that_requires_authentication + expect(response).to have_http_status 200 + end + + expect(Gitlab::AuthLogger).to receive(:error).once + + get url_that_requires_authentication + end end context 'when the throttle is disabled' do diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 4d33c6f4094..2420817e1f7 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -132,7 +132,7 @@ describe SystemNoteService do end it 'sets the note text' do - link = "http://localhost/#{project.full_path}/-/tags/#{tag_name}" + link = "/#{project.full_path}/-/tags/#{tag_name}" expect(subject.note).to eq "tagged commit #{noteable.sha} to [`#{tag_name}`](#{link})" end @@ -1139,7 +1139,7 @@ describe SystemNoteService do diff_id = merge_request.merge_request_diff.id line_code = change_position.line_code(project.repository) - expect(subject.note).to include(diffs_project_merge_request_url(project, merge_request, diff_id: diff_id, anchor: line_code)) + expect(subject.note).to include(diffs_project_merge_request_path(project, merge_request, diff_id: diff_id, anchor: line_code)) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 69589c9aa33..390a869d93f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -44,6 +44,8 @@ Dir[Rails.root.join("spec/support/shared_contexts/*.rb")].each { |f| require f } Dir[Rails.root.join("spec/support/shared_examples/*.rb")].each { |f| require f } Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } +quality_level = Quality::TestLevel.new + RSpec.configure do |config| config.use_transactional_fixtures = false config.use_instantiated_fixtures = false @@ -55,9 +57,10 @@ RSpec.configure do |config| config.infer_spec_type_from_file_location! config.full_backtrace = !!ENV['CI'] - config.define_derived_metadata(file_path: %r{/spec/}) do |metadata| + config.define_derived_metadata(file_path: %r{(ee)?/spec/.+_spec\.rb\z}) do |metadata| location = metadata[:location] + metadata[:level] = quality_level.level_for(location) metadata[:api] = true if location =~ %r{/spec/requests/api/} # do not overwrite type if it's already set diff --git a/spec/support/shared_examples/finders/assignees_filter_spec.rb b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb index 782a2d97746..782a2d97746 100644 --- a/spec/support/shared_examples/finders/assignees_filter_spec.rb +++ b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb index a248f60d23e..a248f60d23e 100644 --- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb +++ b/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb diff --git a/spec/support/shared_examples/models/chat_service_spec.rb b/spec/support/shared_examples/models/chat_service_shared_examples.rb index 0a302e7d030..0a302e7d030 100644 --- a/spec/support/shared_examples/models/chat_service_spec.rb +++ b/spec/support/shared_examples/models/chat_service_shared_examples.rb diff --git a/spec/support/shared_examples/models/update_project_statistics_spec.rb b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb index 7a04e940ee5..7a04e940ee5 100644 --- a/spec/support/shared_examples/models/update_project_statistics_spec.rb +++ b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb diff --git a/spec/support/shared_examples/requests/api/issues_shared_example_spec.rb b/spec/support/shared_examples/requests/api/issues_shared_examples.rb index 1133e95e44e..1133e95e44e 100644 --- a/spec/support/shared_examples/requests/api/issues_shared_example_spec.rb +++ b/spec/support/shared_examples/requests/api/issues_shared_examples.rb |