From 7b384a1f3d2608898318e67d11eea2914889ae81 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 20 Sep 2019 12:11:29 +0000 Subject: Add latest changes from gitlab-org/gitlab@12-3-stable --- .../controllers/dashboard/todos_controller_spec.rb | 4 +- .../projects/artifacts_controller_spec.rb | 111 +----- .../projects/registry/tags_controller_spec.rb | 20 +- .../projects/templates_controller_spec.rb | 40 -- spec/features/clusters/cluster_detail_page_spec.rb | 44 ++- spec/features/container_registry_spec.rb | 4 +- spec/features/issues/gfm_autocomplete_spec.rb | 2 - spec/features/issues_spec.rb | 3 + .../projects/import_export/export_file_spec.rb | 3 +- spec/features/runners_spec.rb | 6 - spec/finders/artifacts_finder_spec.rb | 31 -- spec/frontend/helpers/vue_resource_helper.js | 11 + .../components/import_projects_table_spec.js | 8 +- .../frontend/import_projects/store/actions_spec.js | 64 +--- .../components/log/collapsible_section_spec.js | 60 --- spec/frontend/jobs/components/log/mock_data.js | 70 ---- spec/frontend/tracking_spec.js | 81 +++-- spec/helpers/issuables_helper_spec.rb | 1 + spec/helpers/search_helper_spec.rb | 19 +- .../blob/balsamiq/balsamiq_viewer_spec.js | 63 +--- spec/javascripts/header_spec.js | 8 +- spec/javascripts/helpers/tracking_helper.js | 25 -- spec/javascripts/helpers/vue_resource_helper.js | 11 + .../integrations/integration_settings_form_spec.js | 1 - spec/javascripts/issue_show/components/app_spec.js | 60 +-- spec/javascripts/sidebar/assignee_title_spec.js | 14 +- .../sidebar/confidential_issue_sidebar_spec.js | 13 +- .../sidebar/lock/lock_issue_sidebar_spec.js | 13 +- spec/javascripts/sidebar/subscriptions_spec.js | 9 +- spec/javascripts/test_bundle.js | 9 + spec/javascripts/todos_spec.js | 51 --- .../components/deprecated_modal_2_spec.js | 261 ------------- .../vue_shared/components/gl_modal_spec.js | 261 +++++++++++++ spec/lib/container_registry/client_spec.rb | 65 ---- spec/lib/container_registry/tag_spec.rb | 4 +- .../stage_events/code_stage_start_spec.rb | 22 -- .../stage_events/issue_created_spec.rb | 7 - .../issue_first_mentioned_in_commit_spec.rb | 7 - .../stage_events/issue_stage_end_spec.rb | 7 - .../stage_events/merge_request_created_spec.rb | 7 - ...ge_request_first_deployed_to_production_spec.rb | 7 - .../merge_request_last_build_finished_spec.rb | 7 - .../merge_request_last_build_started_spec.rb | 7 - .../stage_events/merge_request_merged_spec.rb | 7 - .../stage_events/plan_stage_start_spec.rb | 24 -- .../stage_events/stage_event_spec.rb | 7 +- spec/lib/gitlab/gitaly_client_spec.rb | 10 +- .../importer/releases_importer_spec.rb | 40 +- .../base_after_export_strategy_spec.rb | 26 +- spec/lib/gitlab/import_export/all_models.yml | 1 - spec/lib/gitlab/import_export/shared_spec.rb | 29 -- .../gitlab/sidekiq_daemon/memory_killer_spec.rb | 402 --------------------- spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb | 48 +-- spec/lib/gitlab/sidekiq_middleware/monitor_spec.rb | 2 +- spec/lib/gitlab/usage_data_spec.rb | 2 - spec/migrations/sync_issuables_state_id_spec.rb | 37 -- spec/models/clusters/cluster_spec.rb | 9 - spec/models/project_services/jira_service_spec.rb | 2 +- spec/models/release_spec.rb | 1 + spec/models/repository_spec.rb | 4 +- spec/presenters/project_presenter_spec.rb | 22 -- spec/requests/api/commits_spec.rb | 12 - .../api/project_container_repositories_spec.rb | 36 +- spec/requests/api/project_export_spec.rb | 2 +- spec/requests/api/project_import_spec.rb | 47 --- .../delete_tags_service_spec.rb | 120 ------ .../quick_actions/interpret_service_spec.rb | 16 - .../githubish_import_controller_shared_examples.rb | 32 -- spec/support/helpers/wait_for_requests.rb | 6 +- .../cycle_analytics_event_shared_examples.rb | 19 - .../snippet_visibility_shared_examples.rb | 26 +- spec/tasks/gitlab/artifacts/migrate_rake_spec.rb | 42 +-- spec/tasks/gitlab/lfs/migrate_rake_spec.rb | 43 +-- spec/tasks/gitlab/traces_rake_spec.rb | 113 ++++++ spec/tasks/gitlab/uploads/migrate_rake_spec.rb | 25 +- .../object_storage/migrate_uploads_worker_spec.rb | 22 +- spec/workers/object_pool/destroy_worker_spec.rb | 4 +- 77 files changed, 645 insertions(+), 2114 deletions(-) delete mode 100644 spec/finders/artifacts_finder_spec.rb create mode 100644 spec/frontend/helpers/vue_resource_helper.js delete mode 100644 spec/frontend/jobs/components/log/collapsible_section_spec.js delete mode 100644 spec/javascripts/helpers/tracking_helper.js create mode 100644 spec/javascripts/helpers/vue_resource_helper.js delete mode 100644 spec/javascripts/vue_shared/components/deprecated_modal_2_spec.js create mode 100644 spec/javascripts/vue_shared/components/gl_modal_spec.js delete mode 100644 spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb delete mode 100644 spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created_spec.rb delete mode 100644 spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit_spec.rb delete mode 100644 spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end_spec.rb delete mode 100644 spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created_spec.rb delete mode 100644 spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production_spec.rb delete mode 100644 spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished_spec.rb delete mode 100644 spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started_spec.rb delete mode 100644 spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged_spec.rb delete mode 100644 spec/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start_spec.rb delete mode 100644 spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb delete mode 100644 spec/migrations/sync_issuables_state_id_spec.rb delete mode 100644 spec/services/projects/container_repository/delete_tags_service_spec.rb delete mode 100644 spec/support/shared_examples/cycle_analytics_event_shared_examples.rb create mode 100644 spec/tasks/gitlab/traces_rake_spec.rb (limited to 'spec') diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb index 4ce445fe41a..c5af04f72ee 100644 --- a/spec/controllers/dashboard/todos_controller_spec.rb +++ b/spec/controllers/dashboard/todos_controller_spec.rb @@ -131,7 +131,7 @@ describe Dashboard::TodosController do expect(todo.reload).to be_pending expect(response).to have_gitlab_http_status(200) - expect(json_response).to eq({ "count" => 1, "done_count" => 0 }) + expect(json_response).to eq({ "count" => "1", "done_count" => "0" }) end end @@ -145,7 +145,7 @@ describe Dashboard::TodosController do expect(todo.reload).to be_pending end expect(response).to have_gitlab_http_status(200) - expect(json_response).to eq({ 'count' => 2, 'done_count' => 0 }) + expect(json_response).to eq({ 'count' => '2', 'done_count' => '0' }) end end end diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb index c0b01e573b2..6ea82785e98 100644 --- a/spec/controllers/projects/artifacts_controller_spec.rb +++ b/spec/controllers/projects/artifacts_controller_spec.rb @@ -6,7 +6,7 @@ describe Projects::ArtifactsController do let(:user) { project.owner } set(:project) { create(:project, :repository, :public) } - set(:pipeline) do + let(:pipeline) do create(:ci_pipeline, project: project, sha: project.commit.sha, @@ -14,119 +14,12 @@ describe Projects::ArtifactsController do status: 'success') end - let!(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } + let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } before do sign_in(user) end - describe 'GET index' do - subject { get :index, params: { namespace_id: project.namespace, project_id: project } } - - context 'when feature flag is on' do - before do - stub_feature_flags(artifacts_management_page: true) - end - - it 'sets the artifacts variable' do - subject - - expect(assigns(:artifacts)).to contain_exactly(*project.job_artifacts) - end - - it 'sets the total size variable' do - subject - - expect(assigns(:total_size)).to eq(project.job_artifacts.total_size) - end - - describe 'pagination' do - before do - stub_const("#{described_class}::MAX_PER_PAGE", 1) - end - - it 'paginates artifacts' do - subject - - expect(assigns(:artifacts)).to contain_exactly(project.job_artifacts.last) - end - end - end - - context 'when feature flag is off' do - before do - stub_feature_flags(artifacts_management_page: false) - end - - it 'renders no content' do - subject - - expect(response).to have_gitlab_http_status(:no_content) - end - - it 'does not set the artifacts variable' do - subject - - expect(assigns(:artifacts)).to eq(nil) - end - - it 'does not set the total size variable' do - subject - - expect(assigns(:total_size)).to eq(nil) - end - end - end - - describe 'DELETE destroy' do - let!(:artifact) { job.job_artifacts.erasable.first } - - subject { delete :destroy, params: { namespace_id: project.namespace, project_id: project, id: artifact } } - - it 'deletes the artifact' do - expect { subject }.to change { Ci::JobArtifact.count }.by(-1) - expect(artifact).not_to exist - end - - it 'redirects to artifacts index page' do - subject - - expect(response).to redirect_to(project_artifacts_path(project)) - end - - it 'sets the notice' do - subject - - expect(flash[:notice]).to eq('Artifact was successfully deleted.') - end - - context 'when artifact deletion fails' do - before do - allow_any_instance_of(Ci::JobArtifact).to receive(:destroy).and_return(false) - end - - it 'redirects to artifacts index page' do - subject - - expect(response).to redirect_to(project_artifacts_path(project)) - end - - it 'sets the notice' do - subject - - expect(flash[:notice]).to eq('Artifact could not be deleted.') - end - end - - context 'when user is not authorized' do - let(:user) { create(:user) } - - it 'does not delete the artifact' do - expect { subject }.not_to change { Ci::JobArtifact.count } - end - end - end - describe 'GET download' do def download_artifact(extra_params = {}) params = { namespace_id: project.namespace, project_id: project, job_id: job }.merge(extra_params) diff --git a/spec/controllers/projects/registry/tags_controller_spec.rb b/spec/controllers/projects/registry/tags_controller_spec.rb index 8da0d217e9e..c6e063d8229 100644 --- a/spec/controllers/projects/registry/tags_controller_spec.rb +++ b/spec/controllers/projects/registry/tags_controller_spec.rb @@ -10,8 +10,6 @@ describe Projects::Registry::TagsController do create(:container_repository, name: 'image', project: project) end - let(:service) { double('service') } - before do sign_in(user) stub_container_registry_config(enabled: true) @@ -86,17 +84,17 @@ describe Projects::Registry::TagsController do context 'when there is matching tag present' do before do - stub_container_registry_tags(repository: repository.path, tags: %w[rc1], with_manifest: true) + stub_container_registry_tags(repository: repository.path, tags: %w[rc1 test.]) end it 'makes it possible to delete regular tag' do - expect_delete_tags(%w[rc1]) + expect_any_instance_of(ContainerRegistry::Tag).to receive(:delete) destroy_tag('rc1') end it 'makes it possible to delete a tag that ends with a dot' do - expect_delete_tags(%w[test.]) + expect_any_instance_of(ContainerRegistry::Tag).to receive(:delete) destroy_tag('test.') end @@ -127,12 +125,11 @@ describe Projects::Registry::TagsController do stub_container_registry_tags(repository: repository.path, tags: %w[rc1 test.]) end - let(:tags) { %w[tc1 test.] } - it 'makes it possible to delete tags in bulk' do - expect_delete_tags(tags) + allow_any_instance_of(ContainerRegistry::Tag).to receive(:delete) { |*args| ContainerRegistry::Tag.delete(*args) } + expect(ContainerRegistry::Tag).to receive(:delete).exactly(2).times - bulk_destroy_tags(tags) + bulk_destroy_tags(['rc1', 'test.']) end end end @@ -149,9 +146,4 @@ describe Projects::Registry::TagsController do format: :json end end - - def expect_delete_tags(tags, status = :success) - expect(service).to receive(:execute).with(repository) { { status: status } } - expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(repository.project, user, tags: tags) { service } - end end diff --git a/spec/controllers/projects/templates_controller_spec.rb b/spec/controllers/projects/templates_controller_spec.rb index 07b8a36fefc..d5ef2b0e114 100644 --- a/spec/controllers/projects/templates_controller_spec.rb +++ b/spec/controllers/projects/templates_controller_spec.rb @@ -99,44 +99,4 @@ describe Projects::TemplatesController do include_examples 'renders 404 when params are invalid' end end - - describe '#names' do - before do - project.add_developer(user) - sign_in(user) - end - - shared_examples 'template names request' do - it 'returns the template names' do - get(:names, params: { namespace_id: project.namespace, template_type: template_type, project_id: project }, format: :json) - - expect(response).to have_gitlab_http_status(200) - expect(json_response.size).to eq(1) - expect(json_response[0]['name']).to eq(expected_template_name) - end - - it 'fails for user with no access' do - other_user = create(:user) - sign_in(other_user) - - get(:names, params: { namespace_id: project.namespace, template_type: template_type, project_id: project }, format: :json) - - expect(response).to have_gitlab_http_status(404) - end - end - - context 'when querying for issue templates' do - it_behaves_like 'template names request' do - let(:template_type) { 'issue' } - let(:expected_template_name) { 'issue_template' } - end - end - - context 'when querying for merge_request templates' do - it_behaves_like 'template names request' do - let(:template_type) { 'merge_request' } - let(:expected_template_name) { 'merge_request_template' } - end - end - end end diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb index 927862689c1..683c57a97f8 100644 --- a/spec/features/clusters/cluster_detail_page_spec.rb +++ b/spec/features/clusters/cluster_detail_page_spec.rb @@ -13,7 +13,7 @@ describe 'Clusterable > Show page' do sign_in(current_user) end - shared_examples 'show page' do + shared_examples 'editing domain' do before do clusterable.add_maintainer(current_user) end @@ -53,12 +53,6 @@ describe 'Clusterable > Show page' do end end end - - it 'does not show the environments tab' do - visit cluster_path - - expect(page).not_to have_selector('.js-cluster-nav-environments', text: 'Environments') - end end shared_examples 'editing a GCP cluster' do @@ -119,30 +113,42 @@ describe 'Clusterable > Show page' do end context 'when clusterable is a project' do - let(:clusterable) { create(:project) } - let(:cluster_path) { project_cluster_path(clusterable, cluster) } - let(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [clusterable]) } - - it_behaves_like 'show page' + it_behaves_like 'editing domain' do + let(:clusterable) { create(:project) } + let(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [clusterable]) } + let(:cluster_path) { project_cluster_path(clusterable, cluster) } + end - it_behaves_like 'editing a GCP cluster' + it_behaves_like 'editing a GCP cluster' do + let(:clusterable) { create(:project) } + let(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [clusterable]) } + let(:cluster_path) { project_cluster_path(clusterable, cluster) } + end it_behaves_like 'editing a user-provided cluster' do + let(:clusterable) { create(:project) } let(:cluster) { create(:cluster, :provided_by_user, :project, projects: [clusterable]) } + let(:cluster_path) { project_cluster_path(clusterable, cluster) } end end context 'when clusterable is a group' do - let(:clusterable) { create(:group) } - let(:cluster_path) { group_cluster_path(clusterable, cluster) } - let(:cluster) { create(:cluster, :provided_by_gcp, :group, groups: [clusterable]) } - - it_behaves_like 'show page' + it_behaves_like 'editing domain' do + let(:clusterable) { create(:group) } + let(:cluster) { create(:cluster, :provided_by_gcp, :group, groups: [clusterable]) } + let(:cluster_path) { group_cluster_path(clusterable, cluster) } + end - it_behaves_like 'editing a GCP cluster' + it_behaves_like 'editing a GCP cluster' do + let(:clusterable) { create(:group) } + let(:cluster) { create(:cluster, :provided_by_gcp, :group, groups: [clusterable]) } + let(:cluster_path) { group_cluster_path(clusterable, cluster) } + end it_behaves_like 'editing a user-provided cluster' do + let(:clusterable) { create(:group) } let(:cluster) { create(:cluster, :provided_by_user, :group, groups: [clusterable]) } + let(:cluster_path) { group_cluster_path(clusterable, cluster) } end end end diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb index dfd08483430..aefdc4d6d4f 100644 --- a/spec/features/container_registry_spec.rb +++ b/spec/features/container_registry_spec.rb @@ -53,9 +53,7 @@ describe 'Container Registry', :js do find('.js-toggle-repo').click wait_for_requests - service = double('service') - expect(service).to receive(:execute).with(container_repository) { { status: :success } } - expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: ['latest']) { service } + expect_any_instance_of(ContainerRegistry::Tag).to receive(:delete).and_return(true) click_on(class: 'js-delete-registry-row', visible: false) expect(find('.modal .modal-title')).to have_content 'Remove image' diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index 0ff3809a915..cc834df367b 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -27,8 +27,6 @@ describe 'GFM autocomplete', :js do it 'updates issue description with GFM reference' do find('.js-issuable-edit').click - wait_for_requests - simulate_input('#issue-description', "@#{user.name[0...3]}") wait_for_requests diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index f9e83af352d..5bdd9113b06 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -470,6 +470,9 @@ describe 'Issues' do expect(page).to have_content 'None' end + # wait_for_requests does not work with vue-resource at the moment + sleep 1 + expect(issue.reload.assignees).to be_empty end diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb index 7618a2bdea3..a1002f38936 100644 --- a/spec/features/projects/import_export/export_file_spec.rb +++ b/spec/features/projects/import_export/export_file_spec.rb @@ -49,7 +49,8 @@ describe 'Import/Export - project export integration test', :js do expect(page).to have_content('Download export') - expect(project.export_status).to eq(:finished) + expect(file_permissions(project.export_path)).to eq(0700) + expect(project.export_file.path).to include('tar.gz') in_directory_with_expanded_export(project) do |exit_status, tmpdir| diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 0049d3ca7c9..63d21d94b5f 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -272,12 +272,6 @@ describe 'Runners' do expect(page).to have_content 'This group does not provide any group Runners yet' end - - it 'user can see a link to install runners on kubernetes clusters' do - visit group_settings_ci_cd_path(group) - - expect(page).to have_link('Install Runner on Kubernetes', href: group_clusters_path(group)) - end end context 'group with a runner' do diff --git a/spec/finders/artifacts_finder_spec.rb b/spec/finders/artifacts_finder_spec.rb deleted file mode 100644 index b956e2c9515..00000000000 --- a/spec/finders/artifacts_finder_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe ArtifactsFinder do - let(:project) { create(:project) } - - describe '#execute' do - before do - create(:ci_build, :artifacts, project: project) - end - - subject { described_class.new(project, params).execute } - - context 'with empty params' do - let(:params) { {} } - - it 'returns all artifacts belonging to the project' do - expect(subject).to contain_exactly(*project.job_artifacts) - end - end - - context 'with sort param' do - let(:params) { { sort: 'size_desc' } } - - it 'sorts the artifacts' do - expect(subject).to eq(project.job_artifacts.order_by('size_desc')) - end - end - end -end diff --git a/spec/frontend/helpers/vue_resource_helper.js b/spec/frontend/helpers/vue_resource_helper.js new file mode 100644 index 00000000000..0f58af09933 --- /dev/null +++ b/spec/frontend/helpers/vue_resource_helper.js @@ -0,0 +1,11 @@ +// eslint-disable-next-line import/prefer-default-export +export const headersInterceptor = (request, next) => { + next(response => { + const headers = {}; + response.headers.forEach((value, key) => { + headers[key] = value; + }); + // eslint-disable-next-line no-param-reassign + response.headers = headers; + }); +}; diff --git a/spec/frontend/import_projects/components/import_projects_table_spec.js b/spec/frontend/import_projects/components/import_projects_table_spec.js index 708f2758083..17a998d0174 100644 --- a/spec/frontend/import_projects/components/import_projects_table_spec.js +++ b/spec/frontend/import_projects/components/import_projects_table_spec.js @@ -93,7 +93,7 @@ describe('ImportProjectsTable', () => { return vm.$nextTick().then(() => { expect(vm.$el.querySelector('.js-loading-button-icon')).toBeNull(); expect(vm.$el.querySelector('.table')).toBeNull(); - expect(vm.$el.innerText).toMatch(`No ${providerTitle} repositories found`); + expect(vm.$el.innerText).toMatch(`No ${providerTitle} repositories available to import`); }); }); @@ -182,10 +182,4 @@ describe('ImportProjectsTable', () => { expect(vm.$el.querySelector(`.ic-status_${statusObject.icon}`)).not.toBeNull(); }); }); - - it('renders filtering input field', () => { - expect( - vm.$el.querySelector('input[data-qa-selector="githubish_import_filter_field"]'), - ).not.toBeNull(); - }); }); diff --git a/spec/frontend/import_projects/store/actions_spec.js b/spec/frontend/import_projects/store/actions_spec.js index 340b6f02d93..6a7b90788dd 100644 --- a/spec/frontend/import_projects/store/actions_spec.js +++ b/spec/frontend/import_projects/store/actions_spec.js @@ -97,7 +97,6 @@ describe('import_projects store actions', () => { describe('fetchRepos', () => { let mock; - const payload = { imported_projects: [{}], provider_repos: [{}], namespaces: [{}] }; beforeEach(() => { localState.reposPath = `${TEST_HOST}/endpoint.json`; @@ -106,7 +105,8 @@ describe('import_projects store actions', () => { afterEach(() => mock.restore()); - it('dispatches stopJobsPolling, requestRepos and receiveReposSuccess actions on a successful request', done => { + it('dispatches requestRepos and receiveReposSuccess actions on a successful request', done => { + const payload = { imported_projects: [{}], provider_repos: [{}], namespaces: [{}] }; mock.onGet(`${TEST_HOST}/endpoint.json`).reply(200, payload); testAction( @@ -115,7 +115,6 @@ describe('import_projects store actions', () => { localState, [], [ - { type: 'stopJobsPolling' }, { type: 'requestRepos' }, { type: 'receiveReposSuccess', @@ -129,7 +128,7 @@ describe('import_projects store actions', () => { ); }); - it('dispatches stopJobsPolling, requestRepos and receiveReposError actions on an unsuccessful request', done => { + it('dispatches requestRepos and receiveReposSuccess actions on an unsuccessful request', done => { mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500); testAction( @@ -137,39 +136,10 @@ describe('import_projects store actions', () => { null, localState, [], - [{ type: 'stopJobsPolling' }, { type: 'requestRepos' }, { type: 'receiveReposError' }], + [{ type: 'requestRepos' }, { type: 'receiveReposError' }], done, ); }); - - describe('when filtered', () => { - beforeEach(() => { - localState.filter = 'filter'; - }); - - it('fetches repos with filter applied', done => { - mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, payload); - - testAction( - fetchRepos, - null, - localState, - [], - [ - { type: 'stopJobsPolling' }, - { type: 'requestRepos' }, - { - type: 'receiveReposSuccess', - payload: convertObjectPropsToCamelCase(payload, { deep: true }), - }, - { - type: 'fetchJobs', - }, - ], - done, - ); - }); - }); }); describe('requestImport', () => { @@ -279,7 +249,6 @@ describe('import_projects store actions', () => { describe('fetchJobs', () => { let mock; - const updatedProjects = [{ name: 'imported/project' }, { name: 'provider/repo' }]; beforeEach(() => { localState.jobsPath = `${TEST_HOST}/endpoint.json`; @@ -294,6 +263,7 @@ describe('import_projects store actions', () => { afterEach(() => mock.restore()); it('dispatches requestJobs and receiveJobsSuccess actions on a successful request', done => { + const updatedProjects = [{ name: 'imported/project' }, { name: 'provider/repo' }]; mock.onGet(`${TEST_HOST}/endpoint.json`).reply(200, updatedProjects); testAction( @@ -310,29 +280,5 @@ describe('import_projects store actions', () => { done, ); }); - - describe('when filtered', () => { - beforeEach(() => { - localState.filter = 'filter'; - }); - - it('fetches realtime changes with filter applied', done => { - mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, updatedProjects); - - testAction( - fetchJobs, - null, - localState, - [], - [ - { - type: 'receiveJobsSuccess', - payload: convertObjectPropsToCamelCase(updatedProjects, { deep: true }), - }, - ], - done, - ); - }); - }); }); }); diff --git a/spec/frontend/jobs/components/log/collapsible_section_spec.js b/spec/frontend/jobs/components/log/collapsible_section_spec.js deleted file mode 100644 index 6c1ebf0a7c1..00000000000 --- a/spec/frontend/jobs/components/log/collapsible_section_spec.js +++ /dev/null @@ -1,60 +0,0 @@ -import { mount } from '@vue/test-utils'; -import CollpasibleSection from '~/jobs/components/log/collapsible_section.vue'; -import { nestedSectionOpened, nestedSectionClosed } from './mock_data'; - -describe('Job Log Collapsible Section', () => { - let wrapper; - - const traceEndpoint = 'jobs/335'; - - const findCollapsibleLine = () => wrapper.find('.collapsible-line'); - - const createComponent = (props = {}) => { - wrapper = mount(CollpasibleSection, { - sync: true, - propsData: { - ...props, - }, - }); - }; - - afterEach(() => { - wrapper.destroy(); - }); - - describe('with closed nested section', () => { - beforeEach(() => { - createComponent({ - section: nestedSectionClosed, - traceEndpoint, - }); - }); - - it('renders clickable header line', () => { - expect(findCollapsibleLine().attributes('role')).toBe('button'); - }); - }); - - describe('with opened nested section', () => { - beforeEach(() => { - createComponent({ - section: nestedSectionOpened, - traceEndpoint, - }); - }); - - it('renders all sections opened', () => { - expect(wrapper.findAll('.collapsible-line').length).toBe(2); - }); - }); - - it('emits onClickCollapsibleLine on click', () => { - createComponent({ - section: nestedSectionOpened, - traceEndpoint, - }); - - findCollapsibleLine().trigger('click'); - expect(wrapper.emitted('onClickCollapsibleLine').length).toBe(1); - }); -}); diff --git a/spec/frontend/jobs/components/log/mock_data.js b/spec/frontend/jobs/components/log/mock_data.js index 0dae306dcc7..db42644de77 100644 --- a/spec/frontend/jobs/components/log/mock_data.js +++ b/spec/frontend/jobs/components/log/mock_data.js @@ -150,73 +150,3 @@ export const collapsibleTraceIncremental = [ sections: ['section'], }, ]; - -export const nestedSectionClosed = { - offset: 5, - section_header: true, - isHeader: true, - isClosed: true, - line: { - content: [{ text: 'foo' }], - sections: ['prepare-script'], - lineNumber: 1, - }, - section_duration: '00:03', - lines: [ - { - section_header: true, - section_duration: '00:02', - isHeader: true, - isClosed: true, - line: { - offset: 52, - content: [{ text: 'bar' }], - sections: ['prepare-script', 'prepare-script-nested'], - lineNumber: 2, - }, - lines: [ - { - offset: 80, - content: [{ text: 'this is a collapsible nested section' }], - sections: ['prepare-script', 'prepare-script-nested'], - lineNumber: 3, - }, - ], - }, - ], -}; - -export const nestedSectionOpened = { - offset: 5, - section_header: true, - isHeader: true, - isClosed: false, - line: { - content: [{ text: 'foo' }], - sections: ['prepare-script'], - lineNumber: 1, - }, - section_duration: '00:03', - lines: [ - { - section_header: true, - section_duration: '00:02', - isHeader: true, - isClosed: false, - line: { - offset: 52, - content: [{ text: 'bar' }], - sections: ['prepare-script', 'prepare-script-nested'], - lineNumber: 2, - }, - lines: [ - { - offset: 80, - content: [{ text: 'this is a collapsible nested section' }], - sections: ['prepare-script', 'prepare-script-nested'], - lineNumber: 3, - }, - ], - }, - ], -}; diff --git a/spec/frontend/tracking_spec.js b/spec/frontend/tracking_spec.js index 964f8b8787e..dfc068ab6ea 100644 --- a/spec/frontend/tracking_spec.js +++ b/spec/frontend/tracking_spec.js @@ -1,9 +1,10 @@ +import $ from 'jquery'; import { setHTMLFixture } from './helpers/fixtures'; + import Tracking, { initUserTracking } from '~/tracking'; describe('Tracking', () => { let snowplowSpy; - let bindDocumentSpy; beforeEach(() => { window.snowplow = window.snowplow || (() => {}); @@ -16,10 +17,6 @@ describe('Tracking', () => { }); describe('initUserTracking', () => { - beforeEach(() => { - bindDocumentSpy = jest.spyOn(Tracking, 'bindDocument').mockImplementation(() => null); - }); - it('calls through to get a new tracker with the expected options', () => { initUserTracking(); expect(snowplowSpy).toHaveBeenCalledWith('newTracker', '_namespace_', 'app.gitfoo.com', { @@ -53,11 +50,6 @@ describe('Tracking', () => { expect(snowplowSpy).toHaveBeenCalledWith('enableFormTracking'); expect(snowplowSpy).toHaveBeenCalledWith('enableLinkClickTracking'); }); - - it('binds the document event handling', () => { - initUserTracking(); - expect(bindDocumentSpy).toHaveBeenCalled(); - }); }); describe('.event', () => { @@ -70,15 +62,11 @@ describe('Tracking', () => { it('tracks to snowplow (our current tracking system)', () => { Tracking.event('_category_', '_eventName_', { label: '_label_' }); - expect(snowplowSpy).toHaveBeenCalledWith( - 'trackStructEvent', - '_category_', - '_eventName_', - '_label_', - undefined, - undefined, - undefined, - ); + expect(snowplowSpy).toHaveBeenCalledWith('trackStructEvent', '_category_', '_eventName_', { + label: '_label_', + property: '', + value: '', + }); }); it('skips tracking if snowplow is unavailable', () => { @@ -111,70 +99,83 @@ describe('Tracking', () => { }); describe('tracking interface events', () => { - let eventSpy; - - const trigger = (selector, eventName = 'click') => { - const event = new Event(eventName, { bubbles: true }); - document.querySelector(selector).dispatchEvent(event); - }; + let eventSpy = null; + let subject = null; beforeEach(() => { eventSpy = jest.spyOn(Tracking, 'event'); - Tracking.bindDocument('_category_'); // only happens once + subject = new Tracking('_category_'); setHTMLFixture(` -
+
`); }); it('binds to clicks on elements matching [data-track-event]', () => { - trigger('[data-track-event="click_input1"]'); + subject.bind(document); + $('[data-track-event="click_input1"]').click(); expect(eventSpy).toHaveBeenCalledWith('_category_', 'click_input1', { label: '_label_', value: '_value_', + property: '', }); }); it('allows value override with the data-track-value attribute', () => { - trigger('[data-track-event="click_input2"]'); + subject.bind(document); + $('[data-track-event="click_input2"]').click(); expect(eventSpy).toHaveBeenCalledWith('_category_', 'click_input2', { + label: '', value: '_value_override_', + property: '', }); }); it('handles checkbox values correctly', () => { - trigger('[data-track-event="toggle_checkbox"]'); // checking + subject.bind(document); + const $checkbox = $('[data-track-event="toggle_checkbox"]'); + + $checkbox.click(); // unchecking expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_checkbox', { + label: '', + property: '', value: false, }); - trigger('[data-track-event="toggle_checkbox"]'); // unchecking + $checkbox.click(); // checking expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_checkbox', { + label: '', + property: '', value: '_value_', }); }); it('handles bootstrap dropdowns', () => { - trigger('[data-track-event="toggle_dropdown"]', 'show.bs.dropdown'); // showing + new Tracking('_category_').bind(document); + const $dropdown = $('[data-track-event="toggle_dropdown"]'); - expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_dropdown_show', {}); + $dropdown.trigger('show.bs.dropdown'); // showing - trigger('[data-track-event="toggle_dropdown"]', 'hide.bs.dropdown'); // hiding - - expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_dropdown_hide', {}); - }); + expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_dropdown_show', { + label: '', + property: '', + value: '', + }); - it('handles nested elements inside an element with tracking', () => { - trigger('span.nested', 'click'); + $dropdown.trigger('hide.bs.dropdown'); // hiding - expect(eventSpy).toHaveBeenCalledWith('_category_', 'nested_event', {}); + expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_dropdown_hide', { + label: '', + property: '', + value: '', + }); }); }); }); diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 583b8f90db9..3c8179460ac 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -190,6 +190,7 @@ describe IssuablesHelper do issuableRef: "##{issue.iid}", markdownPreviewPath: "/#{@project.full_path}/preview_markdown", markdownDocsPath: '/help/user/markdown', + issuableTemplates: [], lockVersion: issue.lock_version, projectPath: @project.path, projectNamespace: @project.namespace.path, diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index cc058d5b983..e1dc589236b 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -103,17 +103,19 @@ describe SearchHelper do using RSpec::Parameterized::TableSyntax where(:scope, :label) do - 'blobs' | 'code result' 'commits' | 'commit' 'issues' | 'issue' 'merge_requests' | 'merge request' 'milestones' | 'milestone' - 'notes' | 'comment' 'projects' | 'project' - 'snippet_blobs' | 'snippet result' 'snippet_titles' | 'snippet' 'users' | 'user' - 'wiki_blobs' | 'wiki result' + + 'blobs' | 'result' + 'snippet_blobs' | 'result' + 'wiki_blobs' | 'result' + + 'notes' | 'comment' end with_them do @@ -138,15 +140,6 @@ describe SearchHelper do end end - describe 'search_entries_empty_message' do - it 'returns the formatted entry message' do - message = search_entries_empty_message('projects', 'foo') - - expect(message).to eq("We couldn't find any projects matching foo") - expect(message).to be_html_safe - end - end - describe 'search_filter_input_options' do context 'project' do before do diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js index d175c8ba853..fd73fb4bfcc 100644 --- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js +++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js @@ -1,10 +1,8 @@ import sqljs from 'sql.js'; -import axios from '~/lib/utils/axios_utils'; import BalsamiqViewer from '~/blob/balsamiq/balsamiq_viewer'; import ClassSpecHelper from '../../helpers/class_spec_helper'; describe('BalsamiqViewer', () => { - const mockArrayBuffer = new ArrayBuffer(10); let balsamiqViewer; let viewer; @@ -21,65 +19,44 @@ describe('BalsamiqViewer', () => { }); describe('loadFile', () => { - let bv; + let xhr; + let loadFile; const endpoint = 'endpoint'; - const requestSuccess = Promise.resolve({ - data: mockArrayBuffer, - status: 200, - }); beforeEach(() => { - viewer = {}; - bv = new BalsamiqViewer(viewer); - }); + xhr = jasmine.createSpyObj('xhr', ['open', 'send']); - it('should call `axios.get` on `endpoint` param with responseType set to `arraybuffer', () => { - spyOn(axios, 'get').and.returnValue(requestSuccess); - spyOn(bv, 'renderFile').and.stub(); + balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['renderFile']); - bv.loadFile(endpoint); + spyOn(window, 'XMLHttpRequest').and.returnValue(xhr); - expect(axios.get).toHaveBeenCalledWith( - endpoint, - jasmine.objectContaining({ - responseType: 'arraybuffer', - }), - ); + loadFile = BalsamiqViewer.prototype.loadFile.call(balsamiqViewer, endpoint); }); - it('should call `renderFile` on request success', done => { - spyOn(axios, 'get').and.returnValue(requestSuccess); - spyOn(bv, 'renderFile').and.callFake(() => {}); + it('should call .open', () => { + expect(xhr.open).toHaveBeenCalledWith('GET', endpoint, true); + }); - bv.loadFile(endpoint) - .then(() => { - expect(bv.renderFile).toHaveBeenCalledWith(mockArrayBuffer); - }) - .then(done) - .catch(done.fail); + it('should set .responseType', () => { + expect(xhr.responseType).toBe('arraybuffer'); }); - it('should not call `renderFile` on request failure', done => { - spyOn(axios, 'get').and.returnValue(Promise.reject()); - spyOn(bv, 'renderFile'); + it('should call .send', () => { + expect(xhr.send).toHaveBeenCalled(); + }); - bv.loadFile(endpoint) - .then(() => { - done.fail('Expected loadFile to throw error!'); - }) - .catch(() => { - expect(bv.renderFile).not.toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); + it('should return a promise', () => { + expect(loadFile).toEqual(jasmine.any(Promise)); }); }); describe('renderFile', () => { let container; + let loadEvent; let previews; beforeEach(() => { + loadEvent = { target: { response: {} } }; viewer = jasmine.createSpyObj('viewer', ['appendChild']); previews = [document.createElement('ul'), document.createElement('ul')]; @@ -96,11 +73,11 @@ describe('BalsamiqViewer', () => { container = containerElement; }); - BalsamiqViewer.prototype.renderFile.call(balsamiqViewer, mockArrayBuffer); + BalsamiqViewer.prototype.renderFile.call(balsamiqViewer, loadEvent); }); it('should call .initDatabase', () => { - expect(balsamiqViewer.initDatabase).toHaveBeenCalledWith(mockArrayBuffer); + expect(balsamiqViewer.initDatabase).toHaveBeenCalledWith(loadEvent.target.response); }); it('should call .getPreviews', () => { diff --git a/spec/javascripts/header_spec.js b/spec/javascripts/header_spec.js index c36d3be1b22..0ddf589f368 100644 --- a/spec/javascripts/header_spec.js +++ b/spec/javascripts/header_spec.js @@ -20,26 +20,26 @@ describe('Header', function() { }); it('should update todos-count after receiving the todo:toggle event', () => { - triggerToggle(5); + triggerToggle('5'); expect($(todosPendingCount).text()).toEqual('5'); }); it('should hide todos-count when it is 0', () => { - triggerToggle(0); + triggerToggle('0'); expect(isTodosCountHidden()).toEqual(true); }); it('should show todos-count when it is more than 0', () => { - triggerToggle(10); + triggerToggle('10'); expect(isTodosCountHidden()).toEqual(false); }); describe('when todos-count is 1000', () => { beforeEach(() => { - triggerToggle(1000); + triggerToggle('1000'); }); it('should show todos-count', () => { diff --git a/spec/javascripts/helpers/tracking_helper.js b/spec/javascripts/helpers/tracking_helper.js deleted file mode 100644 index 68c1bd2dbca..00000000000 --- a/spec/javascripts/helpers/tracking_helper.js +++ /dev/null @@ -1,25 +0,0 @@ -import Tracking from '~/tracking'; - -export default Tracking; - -let document; -let handlers; - -export function mockTracking(category = '_category_', documentOverride, spyMethod) { - document = documentOverride || window.document; - window.snowplow = () => {}; - Tracking.bindDocument(category, document); - return spyMethod ? spyMethod(Tracking, 'event') : null; -} - -export function unmockTracking() { - window.snowplow = undefined; - handlers.forEach(event => document.removeEventListener(event.name, event.func)); -} - -export function triggerEvent(selectorOrEl, eventName = 'click') { - const event = new Event(eventName, { bubbles: true }); - const el = typeof selectorOrEl === 'string' ? document.querySelector(selectorOrEl) : selectorOrEl; - - el.dispatchEvent(event); -} diff --git a/spec/javascripts/helpers/vue_resource_helper.js b/spec/javascripts/helpers/vue_resource_helper.js new file mode 100644 index 00000000000..0f58af09933 --- /dev/null +++ b/spec/javascripts/helpers/vue_resource_helper.js @@ -0,0 +1,11 @@ +// eslint-disable-next-line import/prefer-default-export +export const headersInterceptor = (request, next) => { + next(response => { + const headers = {}; + response.headers.forEach((value, key) => { + headers[key] = value; + }); + // eslint-disable-next-line no-param-reassign + response.headers = headers; + }); +}; diff --git a/spec/javascripts/integrations/integration_settings_form_spec.js b/spec/javascripts/integrations/integration_settings_form_spec.js index 82d1f815ca8..069e2cb07b5 100644 --- a/spec/javascripts/integrations/integration_settings_form_spec.js +++ b/spec/javascripts/integrations/integration_settings_form_spec.js @@ -126,7 +126,6 @@ describe('IntegrationSettingsForm', () => { spyOn(axios, 'put').and.callThrough(); integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); - // eslint-disable-next-line no-jquery/no-serialize formData = integrationSettingsForm.$form.serialize(); }); diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index c3d1440c34e..2770743937e 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -1,5 +1,3 @@ -/* eslint-disable no-unused-vars */ -import GLDropdown from '~/gl_dropdown'; import Vue from 'vue'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; @@ -54,7 +52,6 @@ describe('Issuable output', () => { markdownDocsPath: '/', projectNamespace: '/', projectPath: '/', - issuableTemplateNamesPath: '/issuable-templates-path', }, }).$mount(); @@ -132,11 +129,11 @@ describe('Issuable output', () => { }); it('does not update formState if form is already open', done => { - vm.updateAndShowForm(); + vm.openForm(); vm.state.titleText = 'testing 123'; - vm.updateAndShowForm(); + vm.openForm(); Vue.nextTick(() => { expect(vm.store.formState.title).not.toBe('testing 123'); @@ -287,7 +284,7 @@ describe('Issuable output', () => { }); }); - it('shows error message from backend if exists', done => { + it('shows error mesage from backend if exists', done => { const msg = 'Custom error message from backend'; spyOn(vm.service, 'updateIssuable').and.callFake( // eslint-disable-next-line prefer-promise-reject-errors @@ -408,20 +405,20 @@ describe('Issuable output', () => { }); }); - describe('updateAndShowForm', () => { + describe('open form', () => { it('shows locked warning if form is open & data is different', done => { vm.$nextTick() .then(() => { - vm.updateAndShowForm(); + vm.openForm(); vm.poll.makeRequest(); - - return new Promise(resolve => { - vm.$watch('formState.lockedWarningVisible', value => { - if (value) resolve(); - }); - }); }) + // Wait for the request + .then(vm.$nextTick) + // Wait for the successCallback to update the store state + .then(vm.$nextTick) + // Wait for the new state to flow to the Vue components + .then(vm.$nextTick) .then(() => { expect(vm.formState.lockedWarningVisible).toEqual(true); expect(vm.formState.lock_version).toEqual(1); @@ -432,41 +429,6 @@ describe('Issuable output', () => { }); }); - describe('requestTemplatesAndShowForm', () => { - beforeEach(() => { - spyOn(vm, 'updateAndShowForm'); - }); - - it('shows the form if template names request is successful', done => { - const mockData = [{ name: 'Bug' }]; - mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData])); - - vm.requestTemplatesAndShowForm() - .then(() => { - expect(vm.updateAndShowForm).toHaveBeenCalledWith(mockData); - }) - .then(done) - .catch(done.fail); - }); - - it('shows the form if template names request failed', done => { - mock - .onGet('/issuable-templates-path') - .reply(() => Promise.reject(new Error('something went wrong'))); - - vm.requestTemplatesAndShowForm() - .then(() => { - expect(document.querySelector('.flash-container .flash-text').textContent).toContain( - 'Error updating issue', - ); - - expect(vm.updateAndShowForm).toHaveBeenCalledWith(); - }) - .then(done) - .catch(done.fail); - }); - }); - describe('show inline edit button', () => { it('should not render by default', () => { expect(vm.$el.querySelector('.title-container .note-action-button')).toBeDefined(); diff --git a/spec/javascripts/sidebar/assignee_title_spec.js b/spec/javascripts/sidebar/assignee_title_spec.js index 6c65a55ff29..7fff7c075d9 100644 --- a/spec/javascripts/sidebar/assignee_title_spec.js +++ b/spec/javascripts/sidebar/assignee_title_spec.js @@ -1,12 +1,13 @@ import Vue from 'vue'; import AssigneeTitle from '~/sidebar/components/assignees/assignee_title.vue'; -import { mockTracking, triggerEvent } from 'spec/helpers/tracking_helper'; describe('AssigneeTitle component', () => { let component; let AssigneeTitleComponent; + let statsSpy; beforeEach(() => { + statsSpy = spyOnDependency(AssigneeTitle, 'trackEvent'); AssigneeTitleComponent = Vue.extend(AssigneeTitle); }); @@ -104,20 +105,15 @@ describe('AssigneeTitle component', () => { expect(component.$el.querySelector('.edit-link')).not.toBeNull(); }); - it('tracks the event when edit is clicked', () => { + it('calls trackEvent when edit is clicked', () => { component = new AssigneeTitleComponent({ propsData: { numberOfAssignees: 0, editable: true, }, }).$mount(); + component.$el.querySelector('.js-sidebar-dropdown-toggle').click(); - const spy = mockTracking('_category_', component.$el, spyOn); - triggerEvent('.js-sidebar-dropdown-toggle'); - - expect(spy).toHaveBeenCalledWith('_category_', 'click_edit_button', { - label: 'right_sidebar', - property: 'assignee', - }); + expect(statsSpy).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js index 50374b8973f..ea9e5677bc5 100644 --- a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js +++ b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js @@ -1,12 +1,13 @@ import Vue from 'vue'; import confidentialIssueSidebar from '~/sidebar/components/confidential/confidential_issue_sidebar.vue'; -import { mockTracking, triggerEvent } from 'spec/helpers/tracking_helper'; describe('Confidential Issue Sidebar Block', () => { let vm1; let vm2; + let statsSpy; beforeEach(() => { + statsSpy = spyOnDependency(confidentialIssueSidebar, 'trackEvent'); const Component = Vue.extend(confidentialIssueSidebar); const service = { update: () => Promise.resolve(true), @@ -69,13 +70,9 @@ describe('Confidential Issue Sidebar Block', () => { }); }); - it('tracks the event when "Edit" is clicked', () => { - const spy = mockTracking('_category_', vm1.$el, spyOn); - triggerEvent('.confidential-edit'); + it('calls trackEvent when "Edit" is clicked', () => { + vm1.$el.querySelector('.confidential-edit').click(); - expect(spy).toHaveBeenCalledWith('_category_', 'click_edit_button', { - label: 'right_sidebar', - property: 'confidentiality', - }); + expect(statsSpy).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js b/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js index decccbb8964..2d930428230 100644 --- a/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js +++ b/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js @@ -1,12 +1,13 @@ import Vue from 'vue'; import lockIssueSidebar from '~/sidebar/components/lock/lock_issue_sidebar.vue'; -import { mockTracking, triggerEvent } from 'spec/helpers/tracking_helper'; describe('LockIssueSidebar', () => { let vm1; let vm2; + let statsSpy; beforeEach(() => { + statsSpy = spyOnDependency(lockIssueSidebar, 'trackEvent'); const Component = Vue.extend(lockIssueSidebar); const mediator = { @@ -60,14 +61,10 @@ describe('LockIssueSidebar', () => { }); }); - it('tracks an event when "Edit" is clicked', () => { - const spy = mockTracking('_category_', vm1.$el, spyOn); - triggerEvent('.lock-edit'); + it('calls trackEvent when "Edit" is clicked', () => { + vm1.$el.querySelector('.lock-edit').click(); - expect(spy).toHaveBeenCalledWith('_category_', 'click_edit_button', { - label: 'right_sidebar', - property: 'lock_issue', - }); + expect(statsSpy).toHaveBeenCalled(); }); it('displays the edit form when opened from collapsed state', done => { diff --git a/spec/javascripts/sidebar/subscriptions_spec.js b/spec/javascripts/sidebar/subscriptions_spec.js index a97608d6b8a..2efa13f3fe8 100644 --- a/spec/javascripts/sidebar/subscriptions_spec.js +++ b/spec/javascripts/sidebar/subscriptions_spec.js @@ -2,13 +2,14 @@ import Vue from 'vue'; import subscriptions from '~/sidebar/components/subscriptions/subscriptions.vue'; import eventHub from '~/sidebar/event_hub'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import { mockTracking } from 'spec/helpers/tracking_helper'; describe('Subscriptions', function() { let vm; let Subscriptions; + let statsSpy; beforeEach(() => { + statsSpy = spyOnDependency(subscriptions, 'trackEvent'); Subscriptions = Vue.extend(subscriptions); }); @@ -52,7 +53,6 @@ describe('Subscriptions', function() { vm = mountComponent(Subscriptions, { subscribed: true }); spyOn(eventHub, '$emit'); spyOn(vm, '$emit'); - spyOn(vm, 'track'); vm.toggleSubscription(); @@ -60,12 +60,11 @@ describe('Subscriptions', function() { expect(vm.$emit).toHaveBeenCalledWith('toggleSubscription', jasmine.any(Object)); }); - it('tracks the event when toggled', () => { + it('calls trackEvent when toggled', () => { vm = mountComponent(Subscriptions, { subscribed: true }); - const spy = mockTracking('_category_', vm.$el, spyOn); vm.toggleSubscription(); - expect(spy).toHaveBeenCalled(); + expect(statsSpy).toHaveBeenCalled(); }); it('onClickCollapsedIcon method emits `toggleSidebar` event on component', () => { diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 191df3cc709..c0a999cfaa6 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -7,6 +7,7 @@ import 'core-js/features/set-immediate'; import 'vendor/jasmine-jquery'; import '~/commons'; import Vue from 'vue'; +import VueResource from 'vue-resource'; import Translate from '~/vue_shared/translate'; import jasmineDiff from 'jasmine-diff'; import { config as testUtilsConfig } from '@vue/test-utils'; @@ -45,6 +46,7 @@ Vue.config.errorHandler = function(err) { fail(err); }; +Vue.use(VueResource); Vue.use(Translate); // enable test fixtures @@ -100,6 +102,13 @@ afterEach(__rewire_reset_all__); // eslint-disable-line // to run our unit tests. beforeEach(done => done()); +const builtinVueHttpInterceptors = Vue.http.interceptors.slice(); + +beforeEach(() => { + // restore interceptors so we have no remaining ones from previous tests + Vue.http.interceptors = builtinVueHttpInterceptors.slice(); +}); + let longRunningTestTimeoutHandle; beforeEach(done => { diff --git a/spec/javascripts/todos_spec.js b/spec/javascripts/todos_spec.js index dc3c547c632..802f54f6a7e 100644 --- a/spec/javascripts/todos_spec.js +++ b/spec/javascripts/todos_spec.js @@ -1,31 +1,18 @@ import $ from 'jquery'; -import MockAdapter from 'axios-mock-adapter'; import Todos from '~/pages/dashboard/todos/index/todos'; import '~/lib/utils/common_utils'; -import '~/gl_dropdown'; -import axios from '~/lib/utils/axios_utils'; -import { addDelimiter } from '~/lib/utils/text_utility'; - -const TEST_COUNT_BIG = 2000; -const TEST_DONE_COUNT_BIG = 7300; describe('Todos', () => { preloadFixtures('todos/todos.html'); let todoItem; - let mock; beforeEach(() => { loadFixtures('todos/todos.html'); todoItem = document.querySelector('.todos-list .todo'); - mock = new MockAdapter(axios); return new Todos(); }); - afterEach(() => { - mock.restore(); - }); - describe('goToTodoUrl', () => { it('opens the todo url', done => { const todoLink = todoItem.dataset.url; @@ -66,43 +53,5 @@ describe('Todos', () => { expect(windowOpenSpy).not.toHaveBeenCalled(); }); }); - - describe('on done todo click', () => { - let onToggleSpy; - - beforeEach(done => { - const el = document.querySelector('.js-done-todo'); - const path = el.dataset.href; - - // Arrange - mock - .onDelete(path) - .replyOnce(200, { count: TEST_COUNT_BIG, done_count: TEST_DONE_COUNT_BIG }); - onToggleSpy = jasmine.createSpy('onToggle'); - $(document).on('todo:toggle', onToggleSpy); - - // Act - el.click(); - - // Wait for axios and HTML to udpate - setImmediate(done); - }); - - it('dispatches todo:toggle', () => { - expect(onToggleSpy).toHaveBeenCalledWith(jasmine.anything(), TEST_COUNT_BIG); - }); - - it('updates pending text', () => { - expect(document.querySelector('.todos-pending .badge').innerHTML).toEqual( - addDelimiter(TEST_COUNT_BIG), - ); - }); - - it('updates done text', () => { - expect(document.querySelector('.todos-done .badge').innerHTML).toEqual( - addDelimiter(TEST_DONE_COUNT_BIG), - ); - }); - }); }); }); diff --git a/spec/javascripts/vue_shared/components/deprecated_modal_2_spec.js b/spec/javascripts/vue_shared/components/deprecated_modal_2_spec.js deleted file mode 100644 index 64fb984d9fc..00000000000 --- a/spec/javascripts/vue_shared/components/deprecated_modal_2_spec.js +++ /dev/null @@ -1,261 +0,0 @@ -import $ from 'jquery'; -import Vue from 'vue'; -import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; - -const modalComponent = Vue.extend(DeprecatedModal2); - -describe('DeprecatedModal2', () => { - let vm; - - afterEach(() => { - vm.$destroy(); - }); - - describe('props', () => { - describe('with id', () => { - const props = { - id: 'my-modal', - }; - - beforeEach(() => { - vm = mountComponent(modalComponent, props); - }); - - it('assigns the id to the modal', () => { - expect(vm.$el.id).toBe(props.id); - }); - }); - - describe('without id', () => { - beforeEach(() => { - vm = mountComponent(modalComponent, {}); - }); - - it('does not add an id attribute to the modal', () => { - expect(vm.$el.hasAttribute('id')).toBe(false); - }); - }); - - describe('with headerTitleText', () => { - const props = { - headerTitleText: 'my title text', - }; - - beforeEach(() => { - vm = mountComponent(modalComponent, props); - }); - - it('sets the modal title', () => { - const modalTitle = vm.$el.querySelector('.modal-title'); - - expect(modalTitle.innerHTML.trim()).toBe(props.headerTitleText); - }); - }); - - describe('with footerPrimaryButtonVariant', () => { - const props = { - footerPrimaryButtonVariant: 'danger', - }; - - beforeEach(() => { - vm = mountComponent(modalComponent, props); - }); - - it('sets the primary button class', () => { - const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type'); - - expect(primaryButton).toHaveClass(`btn-${props.footerPrimaryButtonVariant}`); - }); - }); - - describe('with footerPrimaryButtonText', () => { - const props = { - footerPrimaryButtonText: 'my button text', - }; - - beforeEach(() => { - vm = mountComponent(modalComponent, props); - }); - - it('sets the primary button text', () => { - const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type'); - - expect(primaryButton.innerHTML.trim()).toBe(props.footerPrimaryButtonText); - }); - }); - }); - - it('works with data-toggle="modal"', done => { - setFixtures(` - - - `); - - const modalContainer = document.getElementById('modal-container'); - const modalButton = document.getElementById('modal-button'); - vm = mountComponent( - modalComponent, - { - id: 'my-modal', - }, - modalContainer, - ); - $(vm.$el).on('shown.bs.modal', () => done()); - - modalButton.click(); - }); - - describe('methods', () => { - const dummyEvent = 'not really an event'; - - beforeEach(() => { - vm = mountComponent(modalComponent, {}); - spyOn(vm, '$emit'); - }); - - describe('emitCancel', () => { - it('emits a cancel event', () => { - vm.emitCancel(dummyEvent); - - expect(vm.$emit).toHaveBeenCalledWith('cancel', dummyEvent); - }); - }); - - describe('emitSubmit', () => { - it('emits a submit event', () => { - vm.emitSubmit(dummyEvent); - - expect(vm.$emit).toHaveBeenCalledWith('submit', dummyEvent); - }); - }); - - describe('opened', () => { - it('emits a open event', () => { - vm.opened(); - - expect(vm.$emit).toHaveBeenCalledWith('open'); - }); - }); - - describe('closed', () => { - it('emits a closed event', () => { - vm.closed(); - - expect(vm.$emit).toHaveBeenCalledWith('closed'); - }); - }); - }); - - describe('slots', () => { - const slotContent = 'this should go into the slot'; - const modalWithSlot = slotName => { - let template; - if (slotName) { - template = ` - - - - `; - } else { - template = `${slotContent}`; - } - - return Vue.extend({ - components: { - DeprecatedModal2, - }, - template, - }); - }; - - describe('default slot', () => { - beforeEach(() => { - vm = mountComponent(modalWithSlot()); - }); - - it('sets the modal body', () => { - const modalBody = vm.$el.querySelector('.modal-body'); - - expect(modalBody.innerHTML).toBe(slotContent); - }); - }); - - describe('header slot', () => { - beforeEach(() => { - vm = mountComponent(modalWithSlot('header')); - }); - - it('sets the modal header', () => { - const modalHeader = vm.$el.querySelector('.modal-header'); - - expect(modalHeader.innerHTML).toBe(slotContent); - }); - }); - - describe('title slot', () => { - beforeEach(() => { - vm = mountComponent(modalWithSlot('title')); - }); - - it('sets the modal title', () => { - const modalTitle = vm.$el.querySelector('.modal-title'); - - expect(modalTitle.innerHTML).toBe(slotContent); - }); - }); - - describe('footer slot', () => { - beforeEach(() => { - vm = mountComponent(modalWithSlot('footer')); - }); - - it('sets the modal footer', () => { - const modalFooter = vm.$el.querySelector('.modal-footer'); - - expect(modalFooter.innerHTML).toBe(slotContent); - }); - }); - }); - - describe('handling sizes', () => { - it('should render modal-sm', () => { - vm = mountComponent(modalComponent, { - modalSize: 'sm', - }); - - expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-sm')).toEqual(true); - }); - - it('should render modal-lg', () => { - vm = mountComponent(modalComponent, { - modalSize: 'lg', - }); - - expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-lg')).toEqual(true); - }); - - it('should render modal-xl', () => { - vm = mountComponent(modalComponent, { - modalSize: 'xl', - }); - - expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-xl')).toEqual(true); - }); - - it('should not add modal size classes when md size is passed', () => { - vm = mountComponent(modalComponent, { - modalSize: 'md', - }); - - expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-md')).toEqual(false); - }); - - it('should not add modal size classes by default', () => { - vm = mountComponent(modalComponent, {}); - - expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-sm')).toEqual(false); - expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-lg')).toEqual(false); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js new file mode 100644 index 00000000000..19af8b5d2f7 --- /dev/null +++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js @@ -0,0 +1,261 @@ +import $ from 'jquery'; +import Vue from 'vue'; +import GlModal from '~/vue_shared/components/gl_modal.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; + +const modalComponent = Vue.extend(GlModal); + +describe('GlModal', () => { + let vm; + + afterEach(() => { + vm.$destroy(); + }); + + describe('props', () => { + describe('with id', () => { + const props = { + id: 'my-modal', + }; + + beforeEach(() => { + vm = mountComponent(modalComponent, props); + }); + + it('assigns the id to the modal', () => { + expect(vm.$el.id).toBe(props.id); + }); + }); + + describe('without id', () => { + beforeEach(() => { + vm = mountComponent(modalComponent, {}); + }); + + it('does not add an id attribute to the modal', () => { + expect(vm.$el.hasAttribute('id')).toBe(false); + }); + }); + + describe('with headerTitleText', () => { + const props = { + headerTitleText: 'my title text', + }; + + beforeEach(() => { + vm = mountComponent(modalComponent, props); + }); + + it('sets the modal title', () => { + const modalTitle = vm.$el.querySelector('.modal-title'); + + expect(modalTitle.innerHTML.trim()).toBe(props.headerTitleText); + }); + }); + + describe('with footerPrimaryButtonVariant', () => { + const props = { + footerPrimaryButtonVariant: 'danger', + }; + + beforeEach(() => { + vm = mountComponent(modalComponent, props); + }); + + it('sets the primary button class', () => { + const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type'); + + expect(primaryButton).toHaveClass(`btn-${props.footerPrimaryButtonVariant}`); + }); + }); + + describe('with footerPrimaryButtonText', () => { + const props = { + footerPrimaryButtonText: 'my button text', + }; + + beforeEach(() => { + vm = mountComponent(modalComponent, props); + }); + + it('sets the primary button text', () => { + const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type'); + + expect(primaryButton.innerHTML.trim()).toBe(props.footerPrimaryButtonText); + }); + }); + }); + + it('works with data-toggle="modal"', done => { + setFixtures(` + + + `); + + const modalContainer = document.getElementById('modal-container'); + const modalButton = document.getElementById('modal-button'); + vm = mountComponent( + modalComponent, + { + id: 'my-modal', + }, + modalContainer, + ); + $(vm.$el).on('shown.bs.modal', () => done()); + + modalButton.click(); + }); + + describe('methods', () => { + const dummyEvent = 'not really an event'; + + beforeEach(() => { + vm = mountComponent(modalComponent, {}); + spyOn(vm, '$emit'); + }); + + describe('emitCancel', () => { + it('emits a cancel event', () => { + vm.emitCancel(dummyEvent); + + expect(vm.$emit).toHaveBeenCalledWith('cancel', dummyEvent); + }); + }); + + describe('emitSubmit', () => { + it('emits a submit event', () => { + vm.emitSubmit(dummyEvent); + + expect(vm.$emit).toHaveBeenCalledWith('submit', dummyEvent); + }); + }); + + describe('opened', () => { + it('emits a open event', () => { + vm.opened(); + + expect(vm.$emit).toHaveBeenCalledWith('open'); + }); + }); + + describe('closed', () => { + it('emits a closed event', () => { + vm.closed(); + + expect(vm.$emit).toHaveBeenCalledWith('closed'); + }); + }); + }); + + describe('slots', () => { + const slotContent = 'this should go into the slot'; + const modalWithSlot = slotName => { + let template; + if (slotName) { + template = ` + + + + `; + } else { + template = `${slotContent}`; + } + + return Vue.extend({ + components: { + GlModal, + }, + template, + }); + }; + + describe('default slot', () => { + beforeEach(() => { + vm = mountComponent(modalWithSlot()); + }); + + it('sets the modal body', () => { + const modalBody = vm.$el.querySelector('.modal-body'); + + expect(modalBody.innerHTML).toBe(slotContent); + }); + }); + + describe('header slot', () => { + beforeEach(() => { + vm = mountComponent(modalWithSlot('header')); + }); + + it('sets the modal header', () => { + const modalHeader = vm.$el.querySelector('.modal-header'); + + expect(modalHeader.innerHTML).toBe(slotContent); + }); + }); + + describe('title slot', () => { + beforeEach(() => { + vm = mountComponent(modalWithSlot('title')); + }); + + it('sets the modal title', () => { + const modalTitle = vm.$el.querySelector('.modal-title'); + + expect(modalTitle.innerHTML).toBe(slotContent); + }); + }); + + describe('footer slot', () => { + beforeEach(() => { + vm = mountComponent(modalWithSlot('footer')); + }); + + it('sets the modal footer', () => { + const modalFooter = vm.$el.querySelector('.modal-footer'); + + expect(modalFooter.innerHTML).toBe(slotContent); + }); + }); + }); + + describe('handling sizes', () => { + it('should render modal-sm', () => { + vm = mountComponent(modalComponent, { + modalSize: 'sm', + }); + + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-sm')).toEqual(true); + }); + + it('should render modal-lg', () => { + vm = mountComponent(modalComponent, { + modalSize: 'lg', + }); + + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-lg')).toEqual(true); + }); + + it('should render modal-xl', () => { + vm = mountComponent(modalComponent, { + modalSize: 'xl', + }); + + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-xl')).toEqual(true); + }); + + it('should not add modal size classes when md size is passed', () => { + vm = mountComponent(modalComponent, { + modalSize: 'md', + }); + + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-md')).toEqual(false); + }); + + it('should not add modal size classes by default', () => { + vm = mountComponent(modalComponent, {}); + + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-sm')).toEqual(false); + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-lg')).toEqual(false); + }); + }); +}); diff --git a/spec/lib/container_registry/client_spec.rb b/spec/lib/container_registry/client_spec.rb index 3782c30e88a..6c2b338bfcd 100644 --- a/spec/lib/container_registry/client_spec.rb +++ b/spec/lib/container_registry/client_spec.rb @@ -73,69 +73,4 @@ describe ContainerRegistry::Client do expect(response).to eq('Successfully redirected') end end - - def stub_upload(path, content, digest, status = 200) - stub_request(:post, "http://container-registry/v2/#{path}/blobs/uploads/") - .to_return(status: status, body: "", headers: { 'location' => 'http://container-registry/next_upload?id=someid' }) - - stub_request(:put, "http://container-registry/next_upload?digest=#{digest}&id=someid") - .with(body: content) - .to_return(status: status, body: "", headers: {}) - end - - describe '#upload_blob' do - subject { client.upload_blob('path', 'content', 'sha256:123') } - - context 'with successful uploads' do - it 'starts the upload and posts the blob' do - stub_upload('path', 'content', 'sha256:123') - - expect(subject).to be_success - end - end - - context 'with a failed upload' do - before do - stub_upload('path', 'content', 'sha256:123', 400) - end - - it 'returns nil' do - expect(subject).to be nil - end - end - end - - describe '#generate_empty_manifest' do - subject { client.generate_empty_manifest('path') } - - let(:result_manifest) do - { - schemaVersion: 2, - mediaType: 'application/vnd.docker.distribution.manifest.v2+json', - config: { - mediaType: 'application/vnd.docker.container.image.v1+json', - size: 21, - digest: 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3' - } - } - end - - it 'uploads a random image and returns the manifest' do - stub_upload('path', "{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') - - expect(subject).to eq(result_manifest) - end - end - - describe '#put_tag' do - subject { client.put_tag('path', 'tagA', { foo: :bar }) } - - it 'uploads the manifest and returns the digest' do - stub_request(:put, "http://container-registry/v2/path/manifests/tagA") - .with(body: "{\n \"foo\": \"bar\"\n}") - .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:123' }) - - expect(subject).to eq 'sha256:123' - end - end end diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb index 3115dfe852f..110f006536b 100644 --- a/spec/lib/container_registry/tag_spec.rb +++ b/spec/lib/container_registry/tag_spec.rb @@ -179,7 +179,7 @@ describe ContainerRegistry::Tag do end end - describe '#unsafe_delete' do + describe '#delete' do before do stub_request(:delete, 'http://registry.gitlab/v2/group/test/manifests/sha256:digest') .with(headers: headers) @@ -187,7 +187,7 @@ describe ContainerRegistry::Tag do end it 'correctly deletes the tag' do - expect(tag.unsafe_delete).to be_truthy + expect(tag.delete).to be_truthy end end end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb deleted file mode 100644 index 29c8d548754..00000000000 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Analytics::CycleAnalytics::StageEvents::CodeStageStart do - let(:subject) { described_class.new({}) } - let(:project) { create(:project) } - - it_behaves_like 'cycle analytics event' - - it 'needs connection with an issue via merge_requests_closing_issues table' do - issue = create(:issue, project: project) - merge_request = create(:merge_request, source_project: project) - create(:merge_requests_closing_issues, issue: issue, merge_request: merge_request) - - other_merge_request = create(:merge_request, source_project: project, source_branch: 'a', target_branch: 'master') - - records = subject.apply_query_customization(MergeRequest.all) - expect(records).to eq([merge_request]) - expect(records).not_to include(other_merge_request) - end -end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created_spec.rb deleted file mode 100644 index efdef91c5a2..00000000000 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Analytics::CycleAnalytics::StageEvents::IssueCreated do - it_behaves_like 'cycle analytics event' -end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit_spec.rb deleted file mode 100644 index 50883e1c1e2..00000000000 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstMentionedInCommit do - it_behaves_like 'cycle analytics event' -end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end_spec.rb deleted file mode 100644 index 85062db370a..00000000000 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Analytics::CycleAnalytics::StageEvents::IssueStageEnd do - it_behaves_like 'cycle analytics event' -end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created_spec.rb deleted file mode 100644 index 7858b810661..00000000000 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestCreated do - it_behaves_like 'cycle analytics event' -end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production_spec.rb deleted file mode 100644 index ba9d8be5a2c..00000000000 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestFirstDeployedToProduction do - it_behaves_like 'cycle analytics event' -end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished_spec.rb deleted file mode 100644 index 8e83e10ef96..00000000000 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastBuildFinished do - it_behaves_like 'cycle analytics event' -end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started_spec.rb deleted file mode 100644 index 9f6b430a320..00000000000 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastBuildStarted do - it_behaves_like 'cycle analytics event' -end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged_spec.rb deleted file mode 100644 index ce2aa0a60db..00000000000 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestMerged do - it_behaves_like 'cycle analytics event' -end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start_spec.rb deleted file mode 100644 index cb63139f0a8..00000000000 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Analytics::CycleAnalytics::StageEvents::PlanStageStart do - let(:subject) { described_class.new({}) } - let(:project) { create(:project) } - - it_behaves_like 'cycle analytics event' - - it 'filters issues where first_associated_with_milestone_at or first_added_to_board_at is filled' do - issue1 = create(:issue, project: project) - issue1.metrics.update!(first_added_to_board_at: 1.month.ago, first_mentioned_in_commit_at: 2.months.ago) - - issue2 = create(:issue, project: project) - issue2.metrics.update!(first_associated_with_milestone_at: 1.month.ago, first_mentioned_in_commit_at: 2.months.ago) - - issue_without_metrics = create(:issue, project: project) - - records = subject.apply_query_customization(Issue.all) - expect(records).to match_array([issue1, issue2]) - expect(records).not_to include(issue_without_metrics) - end -end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb index b05faf5d813..29f4be76a65 100644 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb +++ b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb @@ -3,11 +3,8 @@ require 'spec_helper' describe Gitlab::Analytics::CycleAnalytics::StageEvents::StageEvent do - let(:instance) { described_class.new({}) } - it { expect(described_class).to respond_to(:name) } it { expect(described_class).to respond_to(:identifier) } - it { expect(instance).to respond_to(:object_type) } - it { expect(instance).to respond_to(:timestamp_projection) } - it { expect(instance).to respond_to(:apply_query_customization) } + + it { expect(described_class.new({})).to respond_to(:object_type) } end diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index ea3bb12d049..1c5f72a4396 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -182,24 +182,24 @@ describe Gitlab::GitalyClient do end it 'sets the gitaly-session-id in the metadata' do - results = described_class.request_kwargs('default', timeout: 1) + results = described_class.request_kwargs('default', nil) expect(results[:metadata]).to include('gitaly-session-id') end context 'when RequestStore is not enabled' do it 'sets a different gitaly-session-id per request' do - gitaly_session_id = described_class.request_kwargs('default', timeout: 1)[:metadata]['gitaly-session-id'] + gitaly_session_id = described_class.request_kwargs('default', nil)[:metadata]['gitaly-session-id'] - expect(described_class.request_kwargs('default', timeout: 1)[:metadata]['gitaly-session-id']).not_to eq(gitaly_session_id) + expect(described_class.request_kwargs('default', nil)[:metadata]['gitaly-session-id']).not_to eq(gitaly_session_id) end end context 'when RequestStore is enabled', :request_store do it 'sets the same gitaly-session-id on every outgoing request metadata' do - gitaly_session_id = described_class.request_kwargs('default', timeout: 1)[:metadata]['gitaly-session-id'] + gitaly_session_id = described_class.request_kwargs('default', nil)[:metadata]['gitaly-session-id'] 3.times do - expect(described_class.request_kwargs('default', timeout: 1)[:metadata]['gitaly-session-id']).to eq(gitaly_session_id) + expect(described_class.request_kwargs('default', nil)[:metadata]['gitaly-session-id']).to eq(gitaly_session_id) end end end diff --git a/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb b/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb index a1585fd2eb3..0e5419e6c5e 100644 --- a/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb @@ -4,17 +4,17 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do let(:project) { create(:project) } let(:client) { double(:client) } let(:importer) { described_class.new(project, client) } - let(:github_release_name) { 'Initial Release' } let(:created_at) { Time.new(2017, 1, 1, 12, 00) } + let(:updated_at) { Time.new(2017, 1, 1, 12, 15) } let(:released_at) { Time.new(2017, 1, 1, 12, 00) } - let(:github_release) do + let(:release) do double( - :github_release, + :release, tag_name: '1.0', - name: github_release_name, body: 'This is my release', created_at: created_at, + updated_at: updated_at, published_at: released_at ) end @@ -25,7 +25,7 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do tag_name: '1.0', description: 'This is my release', created_at: created_at, - updated_at: created_at, + updated_at: updated_at, released_at: released_at } @@ -37,8 +37,8 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do end describe '#build_releases' do - it 'returns an Array containing release rows' do - expect(importer).to receive(:each_release).and_return([github_release]) + it 'returns an Array containnig release rows' do + expect(importer).to receive(:each_release).and_return([release]) rows = importer.build_releases @@ -49,13 +49,13 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do it 'does not create releases that already exist' do create(:release, project: project, tag: '1.0', description: '1.0') - expect(importer).to receive(:each_release).and_return([github_release]) + expect(importer).to receive(:each_release).and_return([release]) expect(importer.build_releases).to be_empty end it 'uses a default release description if none is provided' do - expect(github_release).to receive(:body).and_return('') - expect(importer).to receive(:each_release).and_return([github_release]) + expect(release).to receive(:body).and_return('') + expect(importer).to receive(:each_release).and_return([release]) release = importer.build_releases.first @@ -64,7 +64,7 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do end describe '#build' do - let(:release_hash) { importer.build(github_release) } + let(:release_hash) { importer.build(release) } it 'returns the attributes of the release as a Hash' do expect(release_hash).to be_an_instance_of(Hash) @@ -88,17 +88,13 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do end it 'includes the updated timestamp' do - expect(release_hash[:updated_at]).to eq(created_at) - end - - it 'includes the release name' do - expect(release_hash[:name]).to eq(github_release_name) + expect(release_hash[:updated_at]).to eq(updated_at) end end end describe '#each_release' do - let(:github_release) { double(:github_release) } + let(:release) { double(:release) } before do allow(project).to receive(:import_source).and_return('foo/bar') @@ -106,7 +102,7 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do allow(client) .to receive(:releases) .with('foo/bar') - .and_return([github_release].to_enum) + .and_return([release].to_enum) end it 'returns an Enumerator' do @@ -114,19 +110,19 @@ describe Gitlab::GithubImport::Importer::ReleasesImporter do end it 'yields every release to the Enumerator' do - expect(importer.each_release.next).to eq(github_release) + expect(importer.each_release.next).to eq(release) end end describe '#description_for' do it 'returns the description when present' do - expect(importer.description_for(github_release)).to eq(github_release.body) + expect(importer.description_for(release)).to eq(release.body) end it 'returns a generated description when one is not present' do - allow(github_release).to receive(:body).and_return('') + allow(release).to receive(:body).and_return('') - expect(importer.description_for(github_release)).to eq('Release for tag 1.0') + expect(importer.description_for(release)).to eq('Release for tag 1.0') end end end diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb index a3d2880182d..9a442de2900 100644 --- a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb +++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb @@ -24,22 +24,14 @@ describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do service.execute(user, project) - expect(service.locks_present?).to be_truthy + expect(lock_path_exist?).to be_truthy end context 'when the method succeeds' do it 'removes the lock file' do service.execute(user, project) - expect(service.locks_present?).to be_falsey - end - - it 'removes the archive path' do - FileUtils.mkdir_p(shared.archive_path) - - service.execute(user, project) - - expect(File.exist?(shared.archive_path)).to be_falsey + expect(lock_path_exist?).to be_falsey end end @@ -70,21 +62,13 @@ describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do service.execute(user, project) end - - it 'removes the archive path' do - FileUtils.mkdir_p(shared.archive_path) - - service.execute(user, project) - - expect(File.exist?(shared.archive_path)).to be_falsey - end end context 'when an exception is raised' do it 'removes the lock' do expect { service.execute(user, project) }.to raise_error(NotImplementedError) - expect(service.locks_present?).to be_falsey + expect(lock_path_exist?).to be_falsey end end end @@ -113,4 +97,8 @@ describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do expect(described_class.new(params).to_json).to eq result end end + + def lock_path_exist? + File.exist?(described_class.lock_file_path(project)) + end end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 3b43ff3a4e1..d3be1e86539 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -349,7 +349,6 @@ project: - members_and_requesters - build_trace_section_names - build_trace_chunks -- job_artifacts - root_of_fork_network - fork_network_member - fork_network diff --git a/spec/lib/gitlab/import_export/shared_spec.rb b/spec/lib/gitlab/import_export/shared_spec.rb index da2c96dcf51..2c288cff6ef 100644 --- a/spec/lib/gitlab/import_export/shared_spec.rb +++ b/spec/lib/gitlab/import_export/shared_spec.rb @@ -5,35 +5,6 @@ describe Gitlab::ImportExport::Shared do let(:project) { build(:project) } subject { project.import_export_shared } - context 'with a repository on disk' do - let(:project) { create(:project, :repository) } - let(:base_path) { %(/tmp/project_exports/#{project.disk_path}/) } - - describe '#archive_path' do - it 'uses a random hash to avoid conflicts' do - expect(subject.archive_path).to match(/#{base_path}\h{32}/) - end - - it 'memoizes the path' do - path = subject.archive_path - - 2.times { expect(subject.archive_path).to eq(path) } - end - end - - describe '#export_path' do - it 'uses a random hash relative to project path' do - expect(subject.export_path).to match(/#{base_path}\h{32}\/\h{32}/) - end - - it 'memoizes the path' do - path = subject.export_path - - 2.times { expect(subject.export_path).to eq(path) } - end - end - end - describe '#error' do let(:error) { StandardError.new('Error importing into /my/folder Permission denied @ unlink_internal - /var/opt/gitlab/gitlab-rails/shared/a/b/c/uploads/file') } diff --git a/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb b/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb deleted file mode 100644 index 756c7947df0..00000000000 --- a/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb +++ /dev/null @@ -1,402 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::SidekiqDaemon::MemoryKiller do - let(:memory_killer) { described_class.new } - let(:pid) { 12345 } - - before do - allow(memory_killer).to receive(:pid).and_return(pid) - allow(Sidekiq.logger).to receive(:info) - allow(Sidekiq.logger).to receive(:warn) - end - - describe '#start_working' do - subject { memory_killer.send(:start_working) } - - before do - # let enabled? return 3 times: true, true, false - allow(memory_killer).to receive(:enabled?).and_return(true, true, false) - end - - context 'when structured logging is used' do - it 'logs start message once' do - expect(Sidekiq.logger).to receive(:info).once - .with( - class: described_class.to_s, - action: 'start', - pid: pid, - message: 'Starting Gitlab::SidekiqDaemon::MemoryKiller Daemon') - - subject - end - - it 'logs StandardError message twice' do - expect(Sidekiq.logger).to receive(:warn).twice - .with( - class: described_class.to_s, - pid: pid, - message: "Exception from start_working: My Exception") - - expect(memory_killer).to receive(:rss_within_range?).twice.and_raise(StandardError, 'My Exception') - - expect { subject }.not_to raise_exception - end - - it 'logs exception message once and raise execption and log stop message' do - expect(Sidekiq.logger).to receive(:warn).once - .with( - class: described_class.to_s, - pid: pid, - message: "Exception from start_working: My Exception") - - expect(memory_killer).to receive(:rss_within_range?).once.and_raise(Exception, 'My Exception') - - expect(Sidekiq.logger).to receive(:warn).once - .with( - class: described_class.to_s, - action: 'stop', - pid: pid, - message: 'Stopping Gitlab::SidekiqDaemon::MemoryKiller Daemon') - - expect { subject }.to raise_exception - end - - it 'logs stop message once' do - expect(Sidekiq.logger).to receive(:warn).once - .with( - class: described_class.to_s, - action: 'stop', - pid: pid, - message: 'Stopping Gitlab::SidekiqDaemon::MemoryKiller Daemon') - - subject - end - end - - it 'not invoke restart_sidekiq when rss in range' do - expect(memory_killer).to receive(:rss_within_range?).twice.and_return(true) - - expect(memory_killer).not_to receive(:restart_sidekiq) - - subject - end - - it 'invoke restart_sidekiq when rss not in range' do - expect(memory_killer).to receive(:rss_within_range?).at_least(:once).and_return(false) - - expect(memory_killer).to receive(:restart_sidekiq).at_least(:once) - - subject - end - end - - describe '#stop_working' do - subject { memory_killer.send(:stop_working)} - - it 'changed enable? to false' do - expect(memory_killer.send(:enabled?)).to be true - subject - expect(memory_killer.send(:enabled?)).to be false - end - end - - describe '#rss_within_range?' do - let(:shutdown_timeout_seconds) { 7 } - let(:check_interval_seconds) { 2 } - let(:grace_balloon_seconds) { 5 } - - subject { memory_killer.send(:rss_within_range?) } - - before do - stub_const("#{described_class}::SHUTDOWN_TIMEOUT_SECONDS", shutdown_timeout_seconds) - stub_const("#{described_class}::CHECK_INTERVAL_SECONDS", check_interval_seconds) - stub_const("#{described_class}::GRACE_BALLOON_SECONDS", grace_balloon_seconds) - allow(Process).to receive(:getpgrp).and_return(pid) - allow(Sidekiq).to receive(:options).and_return(timeout: 9) - end - - it 'return true when everything is within limit' do - expect(memory_killer).to receive(:get_rss).and_return(100) - expect(memory_killer).to receive(:soft_limit_rss).and_return(200) - expect(memory_killer).to receive(:hard_limit_rss).and_return(300) - - expect(Gitlab::Metrics::System).to receive(:monotonic_time).and_call_original - expect(memory_killer).not_to receive(:log_rss_out_of_range) - - expect(subject).to be true - end - - it 'return false when rss exceeds hard_limit_rss' do - expect(memory_killer).to receive(:get_rss).and_return(400) - expect(memory_killer).to receive(:soft_limit_rss).at_least(:once).and_return(200) - expect(memory_killer).to receive(:hard_limit_rss).at_least(:once).and_return(300) - - expect(Gitlab::Metrics::System).to receive(:monotonic_time).and_call_original - - expect(memory_killer).to receive(:log_rss_out_of_range).with(400, 300, 200) - - expect(subject).to be false - end - - it 'return false when rss exceed hard_limit_rss after a while' do - expect(memory_killer).to receive(:get_rss).and_return(250, 400) - expect(memory_killer).to receive(:soft_limit_rss).at_least(:once).and_return(200) - expect(memory_killer).to receive(:hard_limit_rss).at_least(:once).and_return(300) - - expect(Gitlab::Metrics::System).to receive(:monotonic_time).twice.and_call_original - expect(memory_killer).to receive(:sleep).with(check_interval_seconds) - - expect(memory_killer).to receive(:log_rss_out_of_range).with(400, 300, 200) - - expect(subject).to be false - end - - it 'return true when rss below soft_limit_rss after a while within GRACE_BALLOON_SECONDS' do - expect(memory_killer).to receive(:get_rss).and_return(250, 100) - expect(memory_killer).to receive(:soft_limit_rss).and_return(200, 200) - expect(memory_killer).to receive(:hard_limit_rss).and_return(300, 300) - - expect(Gitlab::Metrics::System).to receive(:monotonic_time).twice.and_call_original - expect(memory_killer).to receive(:sleep).with(check_interval_seconds) - - expect(memory_killer).not_to receive(:log_rss_out_of_range) - - expect(subject).to be true - end - - it 'return false when rss exceed soft_limit_rss longer than GRACE_BALLOON_SECONDS' do - expect(memory_killer).to receive(:get_rss).exactly(4).times.and_return(250) - expect(memory_killer).to receive(:soft_limit_rss).exactly(5).times.and_return(200) - expect(memory_killer).to receive(:hard_limit_rss).exactly(5).times.and_return(300) - - expect(Gitlab::Metrics::System).to receive(:monotonic_time).exactly(5).times.and_call_original - expect(memory_killer).to receive(:sleep).exactly(3).times.with(check_interval_seconds).and_call_original - - expect(memory_killer).to receive(:log_rss_out_of_range).with(250, 300, 200) - - expect(subject).to be false - end - end - - describe '#restart_sidekiq' do - let(:shutdown_timeout_seconds) { 7 } - - subject { memory_killer.send(:restart_sidekiq) } - - before do - stub_const("#{described_class}::SHUTDOWN_TIMEOUT_SECONDS", shutdown_timeout_seconds) - allow(Sidekiq).to receive(:options).and_return(timeout: 9) - end - - it 'send signal' do - expect(memory_killer).to receive(:signal_and_wait).with(shutdown_timeout_seconds, 'SIGTSTP', 'stop fetching new jobs').ordered - expect(memory_killer).to receive(:signal_and_wait).with(11, 'SIGTERM', 'gracefully shut down').ordered - expect(memory_killer).to receive(:signal_pgroup).with('SIGKILL', 'die').ordered - - subject - end - end - - describe '#signal_and_wait' do - let(:time) { 7 } - let(:signal) { 'my-signal' } - let(:explanation) { 'my-explanation' } - let(:check_interval_seconds) { 2 } - - subject { memory_killer.send(:signal_and_wait, time, signal, explanation) } - - before do - stub_const("#{described_class}::CHECK_INTERVAL_SECONDS", check_interval_seconds) - end - - it 'send signal and return when all jobs finished' do - expect(Process).to receive(:kill).with(signal, pid).ordered - expect(Gitlab::Metrics::System).to receive(:monotonic_time).and_call_original - - expect(memory_killer).to receive(:enabled?).and_return(true) - expect(memory_killer).to receive(:any_jobs?).and_return(false) - - expect(memory_killer).not_to receive(:sleep) - - subject - end - - it 'send signal and wait till deadline if any job not finished' do - expect(Process).to receive(:kill).with(signal, pid).ordered - expect(Gitlab::Metrics::System).to receive(:monotonic_time).and_call_original.at_least(:once) - - expect(memory_killer).to receive(:enabled?).and_return(true).at_least(:once) - expect(memory_killer).to receive(:any_jobs?).and_return(true).at_least(:once) - - expect(memory_killer).to receive(:sleep).and_call_original.exactly(4).times - - subject - end - end - - describe '#signal_pgroup' do - let(:signal) { 'my-signal' } - let(:explanation) { 'my-explanation' } - - subject { memory_killer.send(:signal_pgroup, signal, explanation) } - - it 'send signal to this proces if it is not group leader' do - expect(Process).to receive(:getpgrp).and_return(pid + 1) - - expect(Sidekiq.logger).to receive(:warn).once - .with( - class: described_class.to_s, - signal: signal, - pid: pid, - message: "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})") - expect(Process).to receive(:kill).with(signal, pid).ordered - - subject - end - - it 'send signal to whole process group as group leader' do - expect(Process).to receive(:getpgrp).and_return(pid) - - expect(Sidekiq.logger).to receive(:warn).once - .with( - class: described_class.to_s, - signal: signal, - pid: pid, - message: "sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})") - expect(Process).to receive(:kill).with(signal, 0).ordered - - subject - end - end - - describe '#log_rss_out_of_range' do - let(:current_rss) { 100 } - let(:soft_limit_rss) { 200 } - let(:hard_limit_rss) { 300 } - let(:reason) { 'rss out of range reason description' } - - subject { memory_killer.send(:log_rss_out_of_range, current_rss, hard_limit_rss, soft_limit_rss) } - - it 'invoke sidekiq logger warn' do - expect(memory_killer).to receive(:out_of_range_description).with(current_rss, hard_limit_rss, soft_limit_rss).and_return(reason) - expect(Sidekiq.logger).to receive(:warn) - .with( - class: described_class.to_s, - pid: pid, - message: 'Sidekiq worker RSS out of range', - current_rss: current_rss, - hard_limit_rss: hard_limit_rss, - soft_limit_rss: soft_limit_rss, - reason: reason) - - subject - end - end - - describe '#out_of_range_description' do - let(:hard_limit) { 300 } - let(:soft_limit) { 200 } - let(:grace_balloon_seconds) { 12 } - - subject { memory_killer.send(:out_of_range_description, rss, hard_limit, soft_limit) } - - context 'when rss > hard_limit' do - let(:rss) { 400 } - - it 'tells reason' do - expect(subject).to eq("current_rss(#{rss}) > hard_limit_rss(#{hard_limit})") - end - end - - context 'when rss <= hard_limit' do - let(:rss) { 300 } - - it 'tells reason' do - stub_const("#{described_class}::GRACE_BALLOON_SECONDS", grace_balloon_seconds) - expect(subject).to eq("current_rss(#{rss}) > soft_limit_rss(#{soft_limit}) longer than GRACE_BALLOON_SECONDS(#{grace_balloon_seconds})") - end - end - end - - describe '#rss_increase_by_jobs' do - let(:running_jobs) { { id1: 'job1', id2: 'job2' } } - - subject { memory_killer.send(:rss_increase_by_jobs) } - - it 'adds up individual rss_increase_by_job' do - expect(Gitlab::SidekiqDaemon::Monitor).to receive_message_chain(:instance, :jobs).and_return(running_jobs) - expect(memory_killer).to receive(:rss_increase_by_job).and_return(11, 22) - expect(subject).to eq(33) - end - - it 'return 0 if no job' do - expect(Gitlab::SidekiqDaemon::Monitor).to receive_message_chain(:instance, :jobs).and_return({}) - expect(subject).to eq(0) - end - end - - describe '#rss_increase_by_job' do - let(:worker_class) { Chaos::SleepWorker } - let(:job) { { worker_class: worker_class, started_at: 321 } } - let(:max_memory_kb) { 100000 } - - subject { memory_killer.send(:rss_increase_by_job, job) } - - before do - stub_const("#{described_class}::DEFAULT_MAX_MEMORY_GROWTH_KB", max_memory_kb) - end - - it 'return 0 if memory_growth_kb return 0' do - expect(memory_killer).to receive(:get_job_options).with(job, 'memory_killer_memory_growth_kb', 0).and_return(0) - expect(memory_killer).to receive(:get_job_options).with(job, 'memory_killer_max_memory_growth_kb', max_memory_kb).and_return(0) - - expect(Time).not_to receive(:now) - expect(subject).to eq(0) - end - - it 'return time factored growth value when it does not exceed max growth limit for whilited job' do - expect(memory_killer).to receive(:get_job_options).with(job, 'memory_killer_memory_growth_kb', 0).and_return(10) - expect(memory_killer).to receive(:get_job_options).with(job, 'memory_killer_max_memory_growth_kb', max_memory_kb).and_return(100) - - expect(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(323) - expect(subject).to eq(20) - end - - it 'return max growth limit when time factored growth value exceed max growth limit for whilited job' do - expect(memory_killer).to receive(:get_job_options).with(job, 'memory_killer_memory_growth_kb', 0).and_return(10) - expect(memory_killer).to receive(:get_job_options).with(job, 'memory_killer_max_memory_growth_kb', max_memory_kb).and_return(100) - - expect(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(332) - expect(subject).to eq(100) - end - end - - describe '#get_job_options' do - let(:worker_class) { Chaos::SleepWorker } - let(:job) { { worker_class: worker_class, started_at: 321 } } - let(:key) { 'my-key' } - let(:default) { 'my-default' } - - subject { memory_killer.send(:get_job_options, job, key, default) } - - it 'return default if key is not defined' do - expect(worker_class).to receive(:sidekiq_options).and_return({ "retry" => 5 }) - - expect(subject).to eq(default) - end - - it 'return default if get StandardError when retrieve sidekiq_options' do - expect(worker_class).to receive(:sidekiq_options).and_raise(StandardError) - - expect(subject).to eq(default) - end - - it 'return right value if sidekiq_options has the key' do - expect(worker_class).to receive(:sidekiq_options).and_return({ key => 10 }) - - expect(subject).to eq(10) - end - end -end diff --git a/spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb b/spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb index 397098ed5a4..acbb09e3542 100644 --- a/spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb +++ b/spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb @@ -8,12 +8,12 @@ describe Gitlab::SidekiqDaemon::Monitor do describe '#within_job' do it 'tracks thread' do blk = proc do - expect(monitor.jobs.dig('jid', :thread)).not_to be_nil + expect(monitor.jobs_thread['jid']).not_to be_nil "OK" end - expect(monitor.within_job('worker_class', 'jid', 'queue', &blk)).to eq("OK") + expect(monitor.within_job('jid', 'queue', &blk)).to eq("OK") end context 'when job is canceled' do @@ -25,34 +25,19 @@ describe Gitlab::SidekiqDaemon::Monitor do it 'does not execute a block' do expect do |blk| - monitor.within_job('worker_class', jid, 'queue', &blk) + monitor.within_job(jid, 'queue', &blk) rescue described_class::CancelledError end.not_to yield_control end it 'raises exception' do - expect { monitor.within_job('worker_class', jid, 'queue') }.to raise_error( + expect { monitor.within_job(jid, 'queue') }.to raise_error( described_class::CancelledError) end end end - describe '#start_working when notification channel not enabled' do - subject { monitor.send(:start_working) } - - it 'return directly' do - allow(monitor).to receive(:notification_channel_enabled?).and_return(nil) - - expect(Sidekiq.logger).not_to receive(:info) - expect(Sidekiq.logger).not_to receive(:warn) - expect(monitor).not_to receive(:enabled?) - expect(monitor).not_to receive(:process_messages) - - subject - end - end - - describe '#start_working when notification channel enabled' do + describe '#start_working' do subject { monitor.send(:start_working) } before do @@ -60,7 +45,6 @@ describe Gitlab::SidekiqDaemon::Monitor do # we toggle `enabled?` flag after the first call stub_const('Gitlab::SidekiqDaemon::Monitor::RECONNECT_TIME', 0) allow(monitor).to receive(:enabled?).and_return(true, false) - allow(monitor).to receive(:notification_channel_enabled?).and_return(1) allow(Sidekiq.logger).to receive(:info) allow(Sidekiq.logger).to receive(:warn) @@ -220,7 +204,7 @@ describe Gitlab::SidekiqDaemon::Monitor do let(:thread) { Thread.new { sleep 1000 } } before do - monitor.jobs[jid] = { worker_class: 'worker_class', thread: thread, started_at: Time.now.to_i } + monitor.jobs_thread[jid] = thread end after do @@ -274,24 +258,4 @@ describe Gitlab::SidekiqDaemon::Monitor do subject end end - - describe '#notification_channel_enabled?' do - subject { monitor.send(:notification_channel_enabled?) } - - it 'return nil when SIDEKIQ_MONITOR_WORKER is not set' do - expect(subject).to be nil - end - - it 'return nil when SIDEKIQ_MONITOR_WORKER set to 0' do - allow(ENV).to receive(:fetch).with('SIDEKIQ_MONITOR_WORKER', 0).and_return("0") - - expect(subject).to be nil - end - - it 'return 1 when SIDEKIQ_MONITOR_WORKER set to 1' do - allow(ENV).to receive(:fetch).with('SIDEKIQ_MONITOR_WORKER', 0).and_return("1") - - expect(subject).to be 1 - end - end end diff --git a/spec/lib/gitlab/sidekiq_middleware/monitor_spec.rb b/spec/lib/gitlab/sidekiq_middleware/monitor_spec.rb index 398144025ea..023df1a6391 100644 --- a/spec/lib/gitlab/sidekiq_middleware/monitor_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/monitor_spec.rb @@ -12,7 +12,7 @@ describe Gitlab::SidekiqMiddleware::Monitor do it 'calls Gitlab::SidekiqDaemon::Monitor' do expect(Gitlab::SidekiqDaemon::Monitor.instance).to receive(:within_job) - .with(anything, 'job-id', 'my-queue') + .with('job-id', 'my-queue') .and_call_original expect { |blk| monitor.call(worker, job, queue, &blk) }.to yield_control diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 842dc4d511c..62787c5abaf 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -55,7 +55,6 @@ describe Gitlab::UsageData do omniauth_enabled reply_by_email_enabled container_registry_enabled - dependency_proxy_enabled gitlab_shared_runners_enabled gitlab_pages git @@ -235,7 +234,6 @@ describe Gitlab::UsageData do expect(subject[:omniauth_enabled]).to eq(Gitlab::Auth.omniauth_enabled?) expect(subject[:reply_by_email_enabled]).to eq(Gitlab::IncomingEmail.enabled?) expect(subject[:container_registry_enabled]).to eq(Gitlab.config.registry.enabled) - expect(subject[:dependency_proxy_enabled]).to eq(Gitlab.config.dependency_proxy.enabled) expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled) end end diff --git a/spec/migrations/sync_issuables_state_id_spec.rb b/spec/migrations/sync_issuables_state_id_spec.rb deleted file mode 100644 index 8d1f8a36ac3..00000000000 --- a/spec/migrations/sync_issuables_state_id_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require Rails.root.join('db', 'post_migrate', '20190911251732_sync_issuables_state_id') - -describe SyncIssuablesStateId, :migration, :sidekiq do - let(:migration) { described_class.new } - - describe '#up' do - let(:issues) { table(:issues) } - let(:namespaces) { table(:namespaces) } - let(:projects) { table(:projects) } - let(:merge_requests) { table(:merge_requests) } - let(:group) { namespaces.create!(name: 'gitlab', path: 'gitlab') } - let(:project) { projects.create!(namespace_id: group.id) } - # These state_ids should be the same defined on Issue/MergeRequest models - let(:state_ids) { { opened: 1, closed: 2, merged: 3, locked: 4 } } - - it 'migrates state column to state_id as integer' do - opened_issue = issues.create!(description: 'first', state: 'opened') - closed_issue = issues.create!(description: 'second', state: 'closed') - opened_merge_request = merge_requests.create!(state: 'opened', target_project_id: project.id, target_branch: 'feature1', source_branch: 'master') - closed_merge_request = merge_requests.create!(state: 'closed', target_project_id: project.id, target_branch: 'feature2', source_branch: 'master') - merged_merge_request = merge_requests.create!(state: 'merged', target_project_id: project.id, target_branch: 'feature3', source_branch: 'master') - locked_merge_request = merge_requests.create!(state: 'locked', target_project_id: project.id, target_branch: 'feature4', source_branch: 'master') - - migrate! - - expect(opened_issue.reload.state_id).to eq(state_ids[:opened]) - expect(closed_issue.reload.state_id).to eq(state_ids[:closed]) - expect(opened_merge_request.reload.state_id).to eq(state_ids[:opened]) - expect(closed_merge_request.reload.state_id).to eq(state_ids[:closed]) - expect(merged_merge_request.reload.state_id).to eq(state_ids[:merged]) - expect(locked_merge_request.reload.state_id).to eq(state_ids[:locked]) - end - end -end diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index b8a6fd6f914..9afbe6328ca 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -38,15 +38,6 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do it { is_expected.to respond_to :project } - describe 'applications have inverse_of: :cluster option' do - let(:cluster) { create(:cluster) } - let!(:helm) { create(:clusters_applications_helm, cluster: cluster) } - - it 'does not do a third query when referencing cluster again' do - expect { cluster.application_helm.cluster }.not_to exceed_query_limit(2) - end - end - describe '.enabled' do subject { described_class.enabled } diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 39c1176f238..b86ab8959a2 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -650,7 +650,7 @@ describe JiraService do end end - describe 'favicon urls' do + describe 'favicon urls', :request_store do it 'includes the standard favicon' do props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title') expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/assets/favicon(?:-\h+).png$} diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index e7a8d27a036..8714c67f29d 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -20,6 +20,7 @@ RSpec.describe Release do describe 'validation' do it { is_expected.to validate_presence_of(:project) } it { is_expected.to validate_presence_of(:description) } + it { is_expected.to validate_presence_of(:name) } context 'when a release exists in the database without a name' do it 'does not require name' do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 011b46c7f1a..6dc47e0e501 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -279,7 +279,7 @@ describe Repository do describe '#commits' do context 'when neither the all flag nor a ref are specified' do it 'returns every commit from default branch' do - expect(repository.commits(nil, limit: 60).size).to eq(37) + expect(repository.commits(limit: 60).size).to eq(37) end end @@ -320,7 +320,7 @@ describe Repository do context "when 'all' flag is set" do it 'returns every commit from the repository' do - expect(repository.commits(nil, all: true, limit: 60).size).to eq(60) + expect(repository.commits(all: true, limit: 60).size).to eq(60) end end end diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb index 2a00548c2c3..5bf80f6e318 100644 --- a/spec/presenters/project_presenter_spec.rb +++ b/spec/presenters/project_presenter_spec.rb @@ -430,26 +430,4 @@ describe ProjectPresenter do ) end end - - describe '#empty_repo_statistics_buttons' do - let(:project) { create(:project, :repository) } - let(:presenter) { described_class.new(project, current_user: user) } - - subject(:empty_repo_statistics_buttons) { presenter.empty_repo_statistics_buttons } - - before do - project.add_developer(user) - allow(project).to receive(:auto_devops_enabled?).and_return(false) - end - - it 'orders the items correctly in an empty project' do - expect(empty_repo_statistics_buttons.map(&:label)).to start_with( - a_string_including('New'), - a_string_including('README'), - a_string_including('CHANGELOG'), - a_string_including('CONTRIBUTING'), - a_string_including('CI/CD') - ) - end - end end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 90ff1d12bf1..5e6ff40e8cf 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -169,18 +169,6 @@ describe API::Commits do end end - context 'first_parent optional parameter' do - it 'returns all first_parent commits' do - commit_count = project.repository.count_commits(ref: SeedRepo::Commit::ID, first_parent: true) - - get api("/projects/#{project_id}/repository/commits", user), params: { ref_name: SeedRepo::Commit::ID, first_parent: 'true' } - - expect(response).to include_pagination_headers - expect(commit_count).to eq(12) - expect(response.headers['X-Total']).to eq(commit_count.to_s) - end - end - context 'with_stats optional parameter' do let(:project) { create(:project, :public, :repository) } diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb index 3ac7ff7656b..f1dc4e6f0b2 100644 --- a/spec/requests/api/project_container_repositories_spec.rb +++ b/spec/requests/api/project_container_repositories_spec.rb @@ -150,7 +150,7 @@ describe API::ProjectContainerRepositories do expect(response).to have_gitlab_http_status(:accepted) end - context 'called multiple times in one hour', :clean_gitlab_redis_shared_state do + context 'called multiple times in one hour' do it 'returns 400 with an error message' do stub_exclusive_lease_taken(lease_key, timeout: 1.hour) subject @@ -202,8 +202,6 @@ describe API::ProjectContainerRepositories do end describe 'DELETE /projects/:id/registry/repositories/:repository_id/tags/:tag_name' do - let(:service) { double('service') } - subject { delete api("/projects/#{project.id}/registry/repositories/#{root_repository.id}/tags/rootA", api_user) } it_behaves_like 'rejected container repository access', :reporter, :forbidden @@ -212,34 +210,18 @@ describe API::ProjectContainerRepositories do context 'for developer' do let(:api_user) { developer } - context 'when there are multiple tags' do - before do - stub_container_registry_tags(repository: root_repository.path, tags: %w(rootA rootB), with_manifest: true) - end - - it 'properly removes tag' do - expect(service).to receive(:execute).with(root_repository) { { status: :success } } - expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(root_repository.project, api_user, tags: %w[rootA]) { service } - - subject - - expect(response).to have_gitlab_http_status(:ok) - end + before do + stub_container_registry_tags(repository: root_repository.path, tags: %w(rootA), with_manifest: true) end - context 'when there\'s only one tag' do - before do - stub_container_registry_tags(repository: root_repository.path, tags: %w(rootA), with_manifest: true) - end - - it 'properly removes tag' do - expect(service).to receive(:execute).with(root_repository) { { status: :success } } - expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(root_repository.project, api_user, tags: %w[rootA]) { service } + it 'properly removes tag' do + expect_any_instance_of(ContainerRegistry::Client) + .to receive(:delete_repository_tag).with(root_repository.path, + 'sha256:4c8e63ca4cb663ce6c688cb06f1c372b088dac5b6d7ad7d49cd620d85cf72a15') - subject + subject - expect(response).to have_gitlab_http_status(:ok) - end + expect(response).to have_gitlab_http_status(:ok) end end end diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb index 7de8935097a..1d2f81a397d 100644 --- a/spec/requests/api/project_export_spec.rb +++ b/spec/requests/api/project_export_spec.rb @@ -30,7 +30,7 @@ describe API::ProjectExport do FileUtils.mkdir_p File.join(project_started.export_path, 'securerandom-hex') # simulate in after export action - FileUtils.touch File.join(project_after_export.import_export_shared.lock_files_path, SecureRandom.hex) + FileUtils.touch Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy.lock_file_path(project_after_export) end after do diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index d2b1fb063b8..594b42bb6c0 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -33,53 +33,6 @@ describe API::ProjectImport do expect(response).to have_gitlab_http_status(201) end - context 'when a name is explicitly set' do - let(:expected_name) { 'test project import' } - - it 'schedules an import using a namespace and a different name' do - stub_import(namespace) - - post api('/projects/import', user), params: { path: 'test-import', file: fixture_file_upload(file), namespace: namespace.id, name: expected_name } - - expect(response).to have_gitlab_http_status(201) - end - - it 'schedules an import using the namespace path and a different name' do - stub_import(namespace) - - post api('/projects/import', user), params: { path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path, name: expected_name } - - expect(response).to have_gitlab_http_status(201) - end - - it 'sets name correctly' do - stub_import(namespace) - - post api('/projects/import', user), params: { path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path, name: expected_name } - - project = Project.find(json_response['id']) - expect(project.name).to eq(expected_name) - end - - it 'sets name correctly with an overwrite' do - stub_import(namespace) - - post api('/projects/import', user), params: { path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path, name: 'new project name', overwrite: true } - - project = Project.find(json_response['id']) - expect(project.name).to eq('new project name') - end - - it 'schedules an import using the path and name explicitly set to nil' do - stub_import(namespace) - - post api('/projects/import', user), params: { path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path, name: nil } - - project = Project.find(json_response['id']) - expect(project.name).to eq('test-import') - end - end - it 'schedules an import at the user namespace level' do stub_import(user.namespace) diff --git a/spec/services/projects/container_repository/delete_tags_service_spec.rb b/spec/services/projects/container_repository/delete_tags_service_spec.rb deleted file mode 100644 index 2ec5850c69e..00000000000 --- a/spec/services/projects/container_repository/delete_tags_service_spec.rb +++ /dev/null @@ -1,120 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Projects::ContainerRepository::DeleteTagsService do - set(:user) { create(:user) } - set(:project) { create(:project, :private) } - set(:repository) { create(:container_repository, :root, project: project) } - - let(:params) { { tags: tags } } - let(:service) { described_class.new(project, user, params) } - - before do - stub_container_registry_config(enabled: true, - api_url: 'http://registry.gitlab', - host_port: 'registry.gitlab') - - stub_container_registry_tags( - repository: repository.path, - tags: %w(latest A Ba Bb C D E)) - - stub_tag_digest('latest', 'sha256:configA') - stub_tag_digest('A', 'sha256:configA') - stub_tag_digest('Ba', 'sha256:configB') - end - - describe '#execute' do - let(:tags) { %w[A] } - subject { service.execute(repository) } - - context 'without permissions' do - it { is_expected.to include(status: :error) } - end - - context 'with permissions' do - before do - project.add_developer(user) - end - - context 'when no params are specified' do - let(:params) { {} } - - it 'does not remove anything' do - expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag) - - is_expected.to include(status: :error) - end - end - - context 'with empty tags' do - let(:tags) { [] } - - it 'does not remove anything' do - expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag) - - is_expected.to include(status: :error) - end - end - - context 'with dummy tags disabled' do - let(:tags) { %w[A Ba] } - - before do - stub_feature_flags(container_registry_smart_delete: false) - end - - it 'deletes tags one by one' do - expect_delete_tag('sha256:configA') - expect_delete_tag('sha256:configB') - is_expected.to include(status: :success) - end - end - - context 'with dummy tags enabled' do - let(:tags) { %w[A Ba] } - - it 'deletes the tags using a dummy image' do - stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') - - stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A") - .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) - - stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba") - .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) - - expect_delete_tag('sha256:dummy') - - is_expected.to include(status: :success) - end - end - end - end - - private - - def stub_tag_digest(tag, digest) - stub_request(:head, "http://registry.gitlab/v2/#{repository.path}/manifests/#{tag}") - .to_return(status: 200, body: "", headers: { 'docker-content-digest' => digest }) - end - - def stub_digest_config(digest, created_at) - allow_any_instance_of(ContainerRegistry::Client) - .to receive(:blob) - .with(repository.path, digest, nil) do - { 'created' => created_at.to_datetime.rfc3339 }.to_json if created_at - end - end - - def stub_upload(content, digest) - expect_any_instance_of(ContainerRegistry::Client) - .to receive(:upload_blob) - .with(repository.path, content, digest) { double(success?: true ) } - end - - def expect_delete_tag(digest) - expect_any_instance_of(ContainerRegistry::Client) - .to receive(:delete_repository_tag) - .with(repository.path, digest) { true } - end -end diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index ec68e1a8cf9..b65ee16c189 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -586,22 +586,6 @@ describe QuickActions::InterpretService do expect(message).to eq('Made this issue confidential.') end - - context 'when issuable is already confidential' do - before do - issuable.update(confidential: true) - end - - it 'does not return the success message' do - _, _, message = service.execute(content, issuable) - - expect(message).to be_empty - end - - it 'is not part of the available commands' do - expect(service.available_commands(issuable)).not_to include(a_hash_including(name: :confidential)) - end - end end shared_examples 'shrug command' do diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb index f23812e7149..718d9857b18 100644 --- a/spec/support/controllers/githubish_import_controller_shared_examples.rb +++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb @@ -139,38 +139,6 @@ shared_examples 'a GitHub-ish import controller: GET status' do expect { get :status, format: :json } .not_to exceed_all_query_limit(control_count) end - - context 'when filtering' do - let(:repo_2) { OpenStruct.new(login: 'emacs', full_name: 'asd/emacs', name: 'emacs', owner: { login: 'owner' }) } - let(:project) { create(:project, import_type: provider, namespace: user.namespace, import_status: :finished, import_source: 'example/repo') } - let(:group) { create(:group) } - - before do - group.add_owner(user) - stub_client(repos: [repo, repo_2, org_repo], orgs: [org], org_repos: [org_repo]) - end - - it 'filters list of repositories by name' do - get :status, params: { filter: 'emacs' }, format: :json - - expect(response).to have_gitlab_http_status(200) - expect(json_response.dig("imported_projects").count).to eq(0) - expect(json_response.dig("provider_repos").count).to eq(1) - expect(json_response.dig("provider_repos", 0, "id")).to eq(repo_2.id) - expect(json_response.dig("namespaces", 0, "id")).to eq(group.id) - end - - context 'when user input contains html' do - let(:expected_filter) { 'test' } - let(:filter) { "#{expected_filter}" } - - it 'sanitizes user input' do - get :status, params: { filter: filter }, format: :json - - expect(assigns(:filter)).to eq(expected_filter) - end - end - end end shared_examples 'a GitHub-ish import controller: POST create' do diff --git a/spec/support/helpers/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb index d5483d0b0a7..30dff1063b5 100644 --- a/spec/support/helpers/wait_for_requests.rb +++ b/spec/support/helpers/wait_for_requests.rb @@ -49,11 +49,11 @@ module WaitForRequests return true unless javascript_test? finished_all_ajax_requests? && - finished_all_axios_requests? + finished_all_vue_resource_requests? end - def finished_all_axios_requests? - Capybara.page.evaluate_script('window.pendingRequests || 0').zero? + def finished_all_vue_resource_requests? + Capybara.page.evaluate_script('window.activeVueResources || 0').zero? end def finished_all_ajax_requests? diff --git a/spec/support/shared_examples/cycle_analytics_event_shared_examples.rb b/spec/support/shared_examples/cycle_analytics_event_shared_examples.rb deleted file mode 100644 index dce1dbe1cd1..00000000000 --- a/spec/support/shared_examples/cycle_analytics_event_shared_examples.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -shared_examples_for 'cycle analytics event' do - let(:instance) { described_class.new({}) } - - it { expect(described_class.name).to be_a_kind_of(String) } - it { expect(described_class.identifier).to be_a_kind_of(Symbol) } - it { expect(instance.object_type.ancestors).to include(ApplicationRecord) } - it { expect(instance).to respond_to(:timestamp_projection) } - - describe '#apply_query_customization' do - it 'expects an ActiveRecord::Relation object as argument and returns a modified version of it' do - input_query = instance.object_type.all - - output_query = instance.apply_query_customization(input_query) - expect(output_query).to be_a_kind_of(ActiveRecord::Relation) - end - end -end diff --git a/spec/support/shared_examples/snippet_visibility_shared_examples.rb b/spec/support/shared_examples/snippet_visibility_shared_examples.rb index e2089ee623a..b5321c6db34 100644 --- a/spec/support/shared_examples/snippet_visibility_shared_examples.rb +++ b/spec/support/shared_examples/snippet_visibility_shared_examples.rb @@ -11,21 +11,13 @@ RSpec.shared_examples 'snippet visibility' do set(:author) { create(:user) } set(:member) { create(:user) } set(:external) { create(:user, :external) } - set(:non_member) { create(:user) } - - set(:project) do - create(:project).tap do |project| - project.add_developer(author) - project.add_developer(member) - end - end context "For project snippets" do let!(:users) do { unauthenticated: nil, external: external, - non_member: non_member, + non_member: create(:user), member: member, author: author } @@ -219,18 +211,14 @@ RSpec.shared_examples 'snippet visibility' do end with_them do - let!(:project_visibility) { project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(project_type.to_s)) } + let!(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel.level_value(project_type.to_s)) } let!(:project_feature) { project.project_feature.update_column(:snippets_access_level, feature_visibility) } let!(:user) { users[user_type] } let!(:snippet) { create(:project_snippet, visibility_level: snippet_type, project: project, author: author) } - let!(:external_member) do - member = project.project_member(external) - - if project.private? - project.add_developer(external) unless member - else - member.delete if member - end + let!(:members) do + project.add_developer(author) + project.add_developer(member) + project.add_developer(external) if project.private? end context "For #{params[:project_type]} project and #{params[:user_type]} users" do @@ -268,7 +256,7 @@ RSpec.shared_examples 'snippet visibility' do { unauthenticated: nil, external: external, - non_member: non_member, + non_member: create(:user), author: author } end diff --git a/spec/tasks/gitlab/artifacts/migrate_rake_spec.rb b/spec/tasks/gitlab/artifacts/migrate_rake_spec.rb index afa9ff50146..be69c10d7c8 100644 --- a/spec/tasks/gitlab/artifacts/migrate_rake_spec.rb +++ b/spec/tasks/gitlab/artifacts/migrate_rake_spec.rb @@ -11,11 +11,10 @@ describe 'gitlab:artifacts namespace rake task' do stub_artifacts_object_storage(enabled: object_storage_enabled) end - describe 'gitlab:artifacts:migrate' do - subject { run_rake_task('gitlab:artifacts:migrate') } + subject { run_rake_task('gitlab:artifacts:migrate') } + context 'job artifacts' do let!(:artifact) { create(:ci_job_artifact, :archive, file_store: store) } - let!(:job_trace) { create(:ci_job_artifact, :trace, file_store: store) } context 'when local storage is used' do let(:store) { ObjectStorage::Store::LOCAL } @@ -28,7 +27,6 @@ describe 'gitlab:artifacts namespace rake task' do subject expect(artifact.reload.file_store).to eq(ObjectStorage::Store::REMOTE) - expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE) end end @@ -39,7 +37,6 @@ describe 'gitlab:artifacts namespace rake task' do subject expect(artifact.reload.file_store).to eq(ObjectStorage::Store::REMOTE) - expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE) end end @@ -48,7 +45,6 @@ describe 'gitlab:artifacts namespace rake task' do subject expect(artifact.reload.file_store).to eq(ObjectStorage::Store::LOCAL) - expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::LOCAL) end end end @@ -61,40 +57,6 @@ describe 'gitlab:artifacts namespace rake task' do subject expect(artifact.reload.file_store).to eq(ObjectStorage::Store::REMOTE) - expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE) - end - end - end - - describe 'gitlab:artifacts:migrate_to_local' do - let(:object_storage_enabled) { true } - - subject { run_rake_task('gitlab:artifacts:migrate_to_local') } - - let!(:artifact) { create(:ci_job_artifact, :archive, file_store: store) } - let!(:job_trace) { create(:ci_job_artifact, :trace, file_store: store) } - - context 'when remote storage is used' do - let(:store) { ObjectStorage::Store::REMOTE } - - context 'and job has remote file store defined' do - it "migrates file to local storage" do - subject - - expect(artifact.reload.file_store).to eq(ObjectStorage::Store::LOCAL) - expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::LOCAL) - end - end - end - - context 'when local storage is used' do - let(:store) { ObjectStorage::Store::LOCAL } - - it 'file stays on local storage' do - subject - - expect(artifact.reload.file_store).to eq(ObjectStorage::Store::LOCAL) - expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::LOCAL) end end end diff --git a/spec/tasks/gitlab/lfs/migrate_rake_spec.rb b/spec/tasks/gitlab/lfs/migrate_rake_spec.rb index a85a0031a6c..66d1a192a96 100644 --- a/spec/tasks/gitlab/lfs/migrate_rake_spec.rb +++ b/spec/tasks/gitlab/lfs/migrate_rake_spec.rb @@ -5,49 +5,32 @@ describe 'gitlab:lfs namespace rake task' do Rake.application.rake_require 'tasks/gitlab/lfs/migrate' end - context 'migration tasks' do + describe 'migrate' do let(:local) { ObjectStorage::Store::LOCAL } let(:remote) { ObjectStorage::Store::REMOTE } + let!(:lfs_object) { create(:lfs_object, :with_file, file_store: local) } - before do - stub_lfs_object_storage(background_upload: false, direct_upload: false) + def lfs_migrate + run_rake_task('gitlab:lfs:migrate') end - describe 'migrate' do - subject { run_rake_task('gitlab:lfs:migrate') } - - let!(:lfs_object) { create(:lfs_object, :with_file) } - - context 'object storage disabled' do - before do - stub_lfs_object_storage(enabled: false) - end - - it "doesn't migrate files" do - expect { subject }.not_to change { lfs_object.reload.file_store } - end + context 'object storage disabled' do + before do + stub_lfs_object_storage(enabled: false) end - context 'object storage enabled' do - it 'migrates local file to object storage' do - expect { subject }.to change { lfs_object.reload.file_store }.from(local).to(remote) - end + it "doesn't migrate files" do + expect { lfs_migrate }.not_to change { lfs_object.reload.file_store } end end - describe 'migrate_to_local' do - subject { run_rake_task('gitlab:lfs:migrate_to_local') } - - let(:lfs_object) { create(:lfs_object, :with_file, :object_storage) } - + context 'object storage enabled' do before do - stub_lfs_object_storage(background_upload: false, direct_upload: true) + stub_lfs_object_storage end - context 'object storage enabled' do - it 'migrates remote files to local storage' do - expect { subject }.to change { lfs_object.reload.file_store }.from(remote).to(local) - end + it 'migrates local file to object storage' do + expect { lfs_migrate }.to change { lfs_object.reload.file_store }.from(local).to(remote) end end end diff --git a/spec/tasks/gitlab/traces_rake_spec.rb b/spec/tasks/gitlab/traces_rake_spec.rb new file mode 100644 index 00000000000..aaf0d7242dd --- /dev/null +++ b/spec/tasks/gitlab/traces_rake_spec.rb @@ -0,0 +1,113 @@ +require 'rake_helper' + +describe 'gitlab:traces rake tasks' do + before do + Rake.application.rake_require 'tasks/gitlab/traces' + end + + describe 'gitlab:traces:archive' do + shared_examples 'passes the job id to worker' do + it do + expect(ArchiveTraceWorker).to receive(:bulk_perform_async).with([[job.id]]) + + run_rake_task('gitlab:traces:archive') + end + end + + shared_examples 'does not pass the job id to worker' do + it do + expect(ArchiveTraceWorker).not_to receive(:bulk_perform_async) + + run_rake_task('gitlab:traces:archive') + end + end + + context 'when trace file stored in default path' do + let!(:job) { create(:ci_build, :success, :trace_live) } + + it_behaves_like 'passes the job id to worker' + end + + context 'when trace is stored in database' do + let!(:job) { create(:ci_build, :success) } + + before do + job.update_column(:trace, 'trace in db') + end + + it_behaves_like 'passes the job id to worker' + end + + context 'when job has trace artifact' do + let!(:job) { create(:ci_build, :success) } + + before do + create(:ci_job_artifact, :trace, job: job) + end + + it_behaves_like 'does not pass the job id to worker' + end + + context 'when job is not finished yet' do + let!(:build) { create(:ci_build, :running, :trace_live) } + + it_behaves_like 'does not pass the job id to worker' + end + end + + describe 'gitlab:traces:migrate' do + let(:object_storage_enabled) { false } + + before do + stub_artifacts_object_storage(enabled: object_storage_enabled) + end + + subject { run_rake_task('gitlab:traces:migrate') } + + let!(:job_trace) { create(:ci_job_artifact, :trace, file_store: store) } + + context 'when local storage is used' do + let(:store) { ObjectStorage::Store::LOCAL } + + context 'and job does not have file store defined' do + let(:object_storage_enabled) { true } + let(:store) { nil } + + it "migrates file to remote storage" do + subject + + expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE) + end + end + + context 'and remote storage is defined' do + let(:object_storage_enabled) { true } + + it "migrates file to remote storage" do + subject + + expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE) + end + end + + context 'and remote storage is not defined' do + it "fails to migrate to remote storage" do + subject + + expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::LOCAL) + end + end + end + + context 'when remote storage is used' do + let(:object_storage_enabled) { true } + let(:store) { ObjectStorage::Store::REMOTE } + + it "file stays on remote storage" do + subject + + expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE) + end + end + end +end diff --git a/spec/tasks/gitlab/uploads/migrate_rake_spec.rb b/spec/tasks/gitlab/uploads/migrate_rake_spec.rb index 8d1e355a7d3..9588e8be5dc 100644 --- a/spec/tasks/gitlab/uploads/migrate_rake_spec.rb +++ b/spec/tasks/gitlab/uploads/migrate_rake_spec.rb @@ -1,42 +1,31 @@ require 'rake_helper' -describe 'gitlab:uploads:migrate and migrate_to_local rake tasks' do +describe 'gitlab:uploads:migrate rake tasks' do let(:model_class) { nil } let(:uploader_class) { nil } let(:mounted_as) { nil } let(:batch_size) { 3 } before do - stub_env('MIGRATION_BATCH_SIZE', batch_size.to_s) + stub_env('BATCH', batch_size.to_s) stub_uploads_object_storage(uploader_class) Rake.application.rake_require 'tasks/gitlab/uploads/migrate' allow(ObjectStorage::MigrateUploadsWorker).to receive(:perform_async) end - def run(task) + def run args = [uploader_class.to_s, model_class.to_s, mounted_as].compact - run_rake_task(task, *args) + run_rake_task("gitlab:uploads:migrate", *args) end shared_examples 'enqueue jobs in batch' do |batch:| - it 'migrates local storage to remote object storage' do + it do expect(ObjectStorage::MigrateUploadsWorker) .to receive(:perform_async).exactly(batch).times - .and_return("A fake job.") + .and_return("A fake job.") - run('gitlab:uploads:migrate') - end - - it 'migrates remote object storage to local storage' do - expect(Upload).to receive(:where).exactly(batch + 1).times { Upload.all } - expect(ObjectStorage::MigrateUploadsWorker) - .to receive(:perform_async) - .with(anything, model_class.name, mounted_as, ObjectStorage::Store::LOCAL) - .exactly(batch).times - .and_return("A fake job.") - - run('gitlab:uploads:migrate_to_local') + run end end diff --git a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb index 6c2544d2efd..da490cb02af 100644 --- a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb +++ b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb @@ -11,8 +11,8 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do let(:uploads) { Upload.all } let(:to_store) { ObjectStorage::Store::REMOTE } - def perform(uploads, store = nil) - described_class.new.perform(uploads.ids, model_class.to_s, mounted_as, store || to_store) + def perform(uploads) + described_class.new.perform(uploads.ids, model_class.to_s, mounted_as, to_store) rescue ObjectStorage::MigrateUploadsWorker::Report::MigrationFailures # swallow end @@ -97,28 +97,12 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do it_behaves_like 'outputs correctly', success: 10 - it 'migrates files to remote storage' do + it 'migrates files' do perform(uploads) expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(0) end - context 'reversed' do - let(:to_store) { ObjectStorage::Store::LOCAL } - - before do - perform(uploads, ObjectStorage::Store::REMOTE) - end - - it 'migrates files to local storage' do - expect(Upload.where(store: ObjectStorage::Store::REMOTE).count).to eq(10) - - perform(uploads) - - expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(10) - end - end - context 'migration is unsuccessful' do before do allow_any_instance_of(ObjectStorage::Concern) diff --git a/spec/workers/object_pool/destroy_worker_spec.rb b/spec/workers/object_pool/destroy_worker_spec.rb index 52d457b4b71..ef74f0ba87c 100644 --- a/spec/workers/object_pool/destroy_worker_spec.rb +++ b/spec/workers/object_pool/destroy_worker_spec.rb @@ -16,9 +16,7 @@ describe ObjectPool::DestroyWorker do subject { described_class.new } it 'requests Gitaly to remove the object pool' do - expect(Gitlab::GitalyClient).to receive(:call) - .with(pool.shard_name, :object_pool_service, :delete_object_pool, - Object, timeout: Gitlab::GitalyClient.long_timeout) + expect(Gitlab::GitalyClient).to receive(:call).with(pool.shard_name, :object_pool_service, :delete_object_pool, Object) subject.perform(pool.id) end -- cgit v1.2.1