diff options
author | Alex Hanselka <alex@gitlab.com> | 2018-12-10 12:23:35 -0800 |
---|---|---|
committer | Alex Hanselka <alex@gitlab.com> | 2018-12-10 12:23:35 -0800 |
commit | 2d7dc668506a0576e231fbe290c89e47cf088300 (patch) | |
tree | b227b60894474ba2c9a8864b66e4df65763b35b8 /spec | |
parent | d2120ff1e705799752e7d9704cae3f1896d8e186 (diff) | |
parent | 9655a602ac0d186e10c44f7b6bcdfc0f14ac7b6a (diff) | |
download | gitlab-ce-2d7dc668506a0576e231fbe290c89e47cf088300.tar.gz |
Merge branch 'code-freeze-20181207' into 11-6-stable-prepare-rc5
* code-freeze-20181207: (85 commits)
Changed frontmatter filtering to support YAML, JSON, TOML, and arbitrary languages
Disable docs lint internal_links check
Documentation cleanup
Allow public forks to be deduplicated
Prettifies
[CE] - Add milestones autocomplete for epics
Fixes linting errors
Reorganize Jobs Variables feature spec
CE Port of "Web Terminal FE"
Extract context in JobsController spec
Add specs for TriggerVariableEntity
Extract context in JobsController spec
Allows user to override default issuer email for cert manager
Add specs for TriggerVariableEntity
Adds toggle behavior - Adds coverage for hide/reveal toggle button behavior
Backports changes made to One notification per code review
Further design iteration on project overview
Fix transaction pollution in Shard.by_name
Show primary button when all labels are prioritized
Consistent feature name in all docs
...
Diffstat (limited to 'spec')
73 files changed, 2018 insertions, 403 deletions
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index ac92b2ca657..c2bd7fd9808 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -460,6 +460,14 @@ describe ApplicationController do expect(controller.last_payload.has_key?(:response)).to be_falsey end + it 'does log correlation id' do + Gitlab::CorrelationId.use_id('new-id') do + get :index + end + + expect(controller.last_payload).to include('correlation_id' => 'new-id') + end + context '422 errors' do it 'logs a response with a string' do response = spy(ActionDispatch::Response, status: 422, body: 'Hello world', content_type: 'application/json', cookies: {}) diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index f6c85102830..4b0dc4c9b69 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -226,9 +226,10 @@ describe GroupsController do end context 'searching' do - # Remove as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/52271 before do + # Remove in https://gitlab.com/gitlab-org/gitlab-ce/issues/54643 stub_feature_flags(use_cte_for_group_issues_search: false) + stub_feature_flags(use_subquery_for_group_issues_search: true) end it 'works with popularity sort' do diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 51a7cc63cef..fca313dafb1 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -401,18 +401,56 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do context 'with variables' do before do create(:ci_pipeline_variable, pipeline: pipeline, key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1') + end - get_show(id: job.id, format: :json) + context 'user is a maintainer' do + before do + project.add_maintainer(user) + + get_show(id: job.id, format: :json) + end + + it 'returns a job_detail' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + end + + it 'exposes trigger information and variables' do + expect(json_response['trigger']['short_token']).to eq 'toke' + expect(json_response['trigger']['variables'].length).to eq 1 + end + + it 'exposes correct variable properties' do + first_variable = json_response['trigger']['variables'].first + + expect(first_variable['key']).to eq "TRIGGER_KEY_1" + expect(first_variable['value']).to eq "TRIGGER_VALUE_1" + expect(first_variable['public']).to eq false + end end - it 'exposes trigger information and variables' do - expect(response).to have_gitlab_http_status(:ok) - expect(response).to match_response_schema('job/job_details') - expect(json_response['trigger']['short_token']).to eq 'toke' - expect(json_response['trigger']['variables'].length).to eq 1 - expect(json_response['trigger']['variables'].first['key']).to eq "TRIGGER_KEY_1" - expect(json_response['trigger']['variables'].first['value']).to eq "TRIGGER_VALUE_1" - expect(json_response['trigger']['variables'].first['public']).to eq false + context 'user is not a mantainer' do + before do + get_show(id: job.id, format: :json) + end + + it 'returns a job_detail' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + end + + it 'exposes trigger information and variables' do + expect(json_response['trigger']['short_token']).to eq 'toke' + expect(json_response['trigger']['variables'].length).to eq 1 + end + + it 'exposes correct variable properties' do + first_variable = json_response['trigger']['variables'].first + + expect(first_variable['key']).to eq "TRIGGER_KEY_1" + expect(first_variable['value']).to be_nil + expect(first_variable['public']).to eq false + end end end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index e62523c65c9..7f15da859e5 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -290,6 +290,20 @@ describe Projects::MergeRequestsController do it_behaves_like 'update invalid issuable', MergeRequest end + + context 'two merge requests with the same source branch' do + it 'does not allow a closed merge request to be reopened if another one is open' do + merge_request.close! + create(:merge_request, source_project: merge_request.source_project, source_branch: merge_request.source_branch) + + update_merge_request(state_event: 'reopen') + + errors = assigns[:merge_request].errors + + expect(errors[:validate_branches]).to include(/Another open merge request already exists for this source branch/) + expect(merge_request.reload).to be_closed + end + end end describe 'POST merge' do diff --git a/spec/controllers/projects/settings/repository_controller_spec.rb b/spec/controllers/projects/settings/repository_controller_spec.rb index 69ec971bb75..70f79a47e63 100644 --- a/spec/controllers/projects/settings/repository_controller_spec.rb +++ b/spec/controllers/projects/settings/repository_controller_spec.rb @@ -19,12 +19,14 @@ describe Projects::Settings::RepositoryController do end describe 'PUT cleanup' do + before do + allow(RepositoryCleanupWorker).to receive(:perform_async) + end + def do_put! object_map = fixture_file_upload('spec/fixtures/bfg_object_map.txt') - Sidekiq::Testing.fake! do - put :cleanup, namespace_id: project.namespace, project_id: project, project: { object_map: object_map } - end + put :cleanup, namespace_id: project.namespace, project_id: project, project: { object_map: object_map } end context 'feature enabled' do @@ -34,7 +36,7 @@ describe Projects::Settings::RepositoryController do do_put! expect(response).to redirect_to project_settings_repository_path(project) - expect(RepositoryCleanupWorker.jobs.count).to eq(1) + expect(RepositoryCleanupWorker).to have_received(:perform_async).once end end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 7849bec4762..576191a5788 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -279,7 +279,7 @@ describe ProjectsController do expected_query = /#{public_project.fork_network.find_forks_in(other_user.namespace).to_sql}/ expect { get(:show, namespace_id: public_project.namespace, id: public_project) } - .not_to exceed_query_limit(1).for_query(expected_query) + .not_to exceed_query_limit(2).for_query(expected_query) end end end diff --git a/spec/factories/pool_repositories.rb b/spec/factories/pool_repositories.rb index 2ed0844ed47..265a4643f46 100644 --- a/spec/factories/pool_repositories.rb +++ b/spec/factories/pool_repositories.rb @@ -1,5 +1,26 @@ FactoryBot.define do factory :pool_repository do - shard + shard { Shard.by_name("default") } + state :none + + before(:create) do |pool| + pool.source_project = create(:project, :repository) + end + + trait :scheduled do + state :scheduled + end + + trait :failed do + state :failed + end + + trait :ready do + state :ready + + after(:create) do |pool| + pool.create_object_pool + end + end end end diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb index 71d715237f5..8918a7b7b9c 100644 --- a/spec/features/projects/clusters/applications_spec.rb +++ b/spec/features/projects/clusters/applications_spec.rb @@ -70,6 +70,44 @@ describe 'Clusters Applications', :js do end end + context 'when user installs Cert Manager' do + before do + allow(ClusterInstallAppWorker).to receive(:perform_async) + allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) + allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) + + create(:clusters_applications_helm, :installed, cluster: cluster) + + page.within('.js-cluster-application-row-cert_manager') do + click_button 'Install' + end + end + + it 'shows status transition' do + def email_form_value + page.find('.js-email').value + end + + page.within('.js-cluster-application-row-cert_manager') do + expect(email_form_value).to eq(cluster.user.email) + expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install') + + page.find('.js-email').set("new_email@example.org") + Clusters::Cluster.last.application_cert_manager.make_installing! + + expect(email_form_value).to eq('new_email@example.org') + expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') + + Clusters::Cluster.last.application_cert_manager.make_installed! + + expect(email_form_value).to eq('new_email@example.org') + expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installed') + end + + expect(page).to have_content('Cert-Manager was successfully installed on your Kubernetes cluster') + end + end + context 'when user installs Ingress' do context 'when user installs application: Ingress' do before do diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index d7c4abffddd..651c02c7ecc 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -346,44 +346,85 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do describe 'Variables' do let(:trigger_request) { create(:ci_trigger_request) } + let(:job) { create(:ci_build, pipeline: pipeline, trigger_request: trigger_request) } - let(:job) do - create :ci_build, pipeline: pipeline, trigger_request: trigger_request - end + context 'when user is a maintainer' do + shared_examples 'no reveal button variables behavior' do + it 'renders a hidden value with no reveal values button', :js do + expect(page).to have_content('Token') + expect(page).to have_content('Variables') + + expect(page).not_to have_css('.js-reveal-variables') + + expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1') + expect(page).to have_selector('.js-build-value', text: '••••••') + end + end + + context 'when variables are stored in trigger_request' do + before do + trigger_request.update_attribute(:variables, { 'TRIGGER_KEY_1' => 'TRIGGER_VALUE_1' } ) + + visit project_job_path(project, job) + end + + it_behaves_like 'no reveal button variables behavior' + end - shared_examples 'expected variables behavior' do - it 'shows variable key and value after click', :js do - expect(page).to have_content('Token') - expect(page).to have_css('.js-reveal-variables') - expect(page).not_to have_css('.js-build-variable') - expect(page).not_to have_css('.js-build-value') + context 'when variables are stored in pipeline_variables' do + before do + create(:ci_pipeline_variable, pipeline: pipeline, key: 'TRIGGER_KEY_1', value: 'TRIGGER_VALUE_1') - click_button 'Reveal Variables' + visit project_job_path(project, job) + end - expect(page).not_to have_css('.js-reveal-variables') - expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1') - expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1') + it_behaves_like 'no reveal button variables behavior' end end - context 'when variables are stored in trigger_request' do + context 'when user is a maintainer' do before do - trigger_request.update_attribute(:variables, { 'TRIGGER_KEY_1' => 'TRIGGER_VALUE_1' } ) + project.add_maintainer(user) + end - visit project_job_path(project, job) + shared_examples 'reveal button variables behavior' do + it 'renders a hidden value with a reveal values button', :js do + expect(page).to have_content('Token') + expect(page).to have_content('Variables') + + expect(page).to have_css('.js-reveal-variables') + + expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1') + expect(page).to have_selector('.js-build-value', text: '••••••') + end + + it 'reveals values on button click', :js do + click_button 'Reveal values' + + expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1') + expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1') + end end - it_behaves_like 'expected variables behavior' - end + context 'when variables are stored in trigger_request' do + before do + trigger_request.update_attribute(:variables, { 'TRIGGER_KEY_1' => 'TRIGGER_VALUE_1' } ) - context 'when variables are stored in pipeline_variables' do - before do - create(:ci_pipeline_variable, pipeline: pipeline, key: 'TRIGGER_KEY_1', value: 'TRIGGER_VALUE_1') + visit project_job_path(project, job) + end - visit project_job_path(project, job) + it_behaves_like 'reveal button variables behavior' end - it_behaves_like 'expected variables behavior' + context 'when variables are stored in pipeline_variables' do + before do + create(:ci_pipeline_variable, pipeline: pipeline, key: 'TRIGGER_KEY_1', value: 'TRIGGER_VALUE_1') + + visit project_job_path(project, job) + end + + it_behaves_like 'reveal button variables behavior' + end end end diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb index 996040fde02..055a0c83a11 100644 --- a/spec/features/projects/labels/update_prioritization_spec.rb +++ b/spec/features/projects/labels/update_prioritization_spec.rb @@ -115,6 +115,21 @@ describe 'Prioritize labels' do end end + it 'user can see a primary button when there are only prioritized labels', :js do + visit project_labels_path(project) + + page.within('.other-labels') do + all('.js-toggle-priority').each do |el| + el.click + end + wait_for_requests + end + + page.within('.breadcrumbs-container') do + expect(page).to have_link('New label') + end + end + it 'shows a help message about prioritized labels' do visit project_labels_path(project) diff --git a/spec/features/projects/show/developer_views_empty_project_instructions_spec.rb b/spec/features/projects/show/developer_views_empty_project_instructions_spec.rb index 227bdf524fe..8ba91fe7fd7 100644 --- a/spec/features/projects/show/developer_views_empty_project_instructions_spec.rb +++ b/spec/features/projects/show/developer_views_empty_project_instructions_spec.rb @@ -10,54 +10,9 @@ describe 'Projects > Show > Developer views empty project instructions' do sign_in(developer) end - context 'without an SSH key' do - it 'defaults to HTTP' do - visit_project - - expect_instructions_for('http') - end - - it 'switches to SSH', :js do - visit_project - - select_protocol('SSH') - - expect_instructions_for('ssh') - end - end - - context 'with an SSH key' do - before do - create(:personal_key, user: developer) - end - - it 'defaults to SSH' do - visit_project - - expect_instructions_for('ssh') - end - - it 'switches to HTTP', :js do - visit_project - - select_protocol('HTTP') - - expect_instructions_for('http') - end - end - - def visit_project + it 'displays "git clone" instructions' do visit project_path(project) - end - - def select_protocol(protocol) - find('#clone-dropdown').click - find(".#{protocol.downcase}-selector").click - end - - def expect_instructions_for(protocol) - msg = :"#{protocol.downcase}_url_to_repo" - expect(page).to have_content("git clone #{project.send(msg)}") + expect(page).to have_content("git clone") end end diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb index 546619e88ec..88f3397608f 100644 --- a/spec/features/projects/show/user_manages_notifications_spec.rb +++ b/spec/features/projects/show/user_manages_notifications_spec.rb @@ -8,13 +8,18 @@ describe 'Projects > Show > User manages notifications', :js do visit project_path(project) end - it 'changes the notification setting' do + def click_notifications_button first('.notifications-btn').click + end + + it 'changes the notification setting' do + click_notifications_button click_link 'On mention' - page.within '#notifications-button' do - expect(page).to have_content 'On mention' - end + wait_for_requests + + click_notifications_button + expect(find('.update-notification.is-active')).to have_content('On mention') end context 'custom notification settings' do @@ -38,7 +43,7 @@ describe 'Projects > Show > User manages notifications', :js do end it 'shows notification settings checkbox' do - first('.notifications-btn').click + click_notifications_button page.find('a[data-notification-level="custom"]').click page.within('.custom-notifications-form') do diff --git a/spec/features/projects/show/user_sees_collaboration_links_spec.rb b/spec/features/projects/show/user_sees_collaboration_links_spec.rb index 7b3711531c6..24777788248 100644 --- a/spec/features/projects/show/user_sees_collaboration_links_spec.rb +++ b/spec/features/projects/show/user_sees_collaboration_links_spec.rb @@ -21,18 +21,6 @@ describe 'Projects > Show > Collaboration links' do end end - # The project header - page.within('.project-home-panel') do - aggregate_failures 'dropdown links in the project home panel' do - expect(page).to have_link('New issue') - expect(page).to have_link('New merge request') - expect(page).to have_link('New snippet') - expect(page).to have_link('New file') - expect(page).to have_link('New branch') - expect(page).to have_link('New tag') - end - end - # The dropdown above the tree page.within('.repo-breadcrumb') do aggregate_failures 'dropdown links above the repo tree' do @@ -61,17 +49,6 @@ describe 'Projects > Show > Collaboration links' do end end - page.within('.project-home-panel') do - aggregate_failures 'dropdown links' do - expect(page).not_to have_link('New issue') - expect(page).not_to have_link('New merge request') - expect(page).not_to have_link('New snippet') - expect(page).not_to have_link('New file') - expect(page).not_to have_link('New branch') - expect(page).not_to have_link('New tag') - end - end - page.within('.repo-breadcrumb') do aggregate_failures 'dropdown links' do expect(page).not_to have_link('New file') diff --git a/spec/features/projects/show/user_sees_git_instructions_spec.rb b/spec/features/projects/show/user_sees_git_instructions_spec.rb index 9a82fee1b5d..ffa80235083 100644 --- a/spec/features/projects/show/user_sees_git_instructions_spec.rb +++ b/spec/features/projects/show/user_sees_git_instructions_spec.rb @@ -29,7 +29,7 @@ describe 'Projects > Show > User sees Git instructions' do expect(element.text).to include(project.http_url_to_repo) end - expect(page).to have_field('project_clone', with: project.http_url_to_repo) unless user_has_ssh_key + expect(page).to have_field('http_project_clone', with: project.http_url_to_repo) unless user_has_ssh_key end end @@ -41,7 +41,7 @@ describe 'Projects > Show > User sees Git instructions' do expect(page).to have_content(project.title) end - expect(page).to have_field('project_clone', with: project.http_url_to_repo) unless user_has_ssh_key + expect(page).to have_field('http_project_clone', with: project.http_url_to_repo) unless user_has_ssh_key end end diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb index df2b492ae6b..dcca1d388c7 100644 --- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb +++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb @@ -21,7 +21,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do end it 'no Auto DevOps button if can not manage pipelines' do - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).not_to have_link('Enable Auto DevOps') expect(page).not_to have_link('Auto DevOps enabled') end @@ -30,7 +30,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do it '"Auto DevOps enabled" button not linked' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_text('Auto DevOps enabled') end end @@ -45,19 +45,19 @@ describe 'Projects > Show > User sees setup shortcut buttons' do end it '"New file" button linked to new file page' do - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_link('New file', href: project_new_blob_path(project, project.default_branch || 'master')) end end - it '"Add Readme" button linked to new file populated for a readme' do - page.within('.project-stats') do - expect(page).to have_link('Add Readme', href: presenter.add_readme_path) + it '"Add README" button linked to new file populated for a README' do + page.within('.project-buttons') do + expect(page).to have_link('Add README', href: presenter.add_readme_path) end end it '"Add license" button linked to new file populated for a license' do - page.within('.project-metadata') do + page.within('.project-stats') do expect(page).to have_link('Add license', href: presenter.add_license_path) end end @@ -67,7 +67,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do it '"Auto DevOps enabled" anchor linked to settings page' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) end end @@ -77,7 +77,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do let(:project) { create(:project, :public, :empty_repo, auto_devops_attributes: { enabled: false }) } it '"Enable Auto DevOps" button linked to settings page' do - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) end end @@ -86,7 +86,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do describe 'Kubernetes cluster button' do it '"Add Kubernetes cluster" button linked to clusters page' do - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project)) end end @@ -96,7 +96,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster)) end end @@ -119,7 +119,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do it '"Auto DevOps enabled" button not linked' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_text('Auto DevOps enabled') end end @@ -129,14 +129,14 @@ describe 'Projects > Show > User sees setup shortcut buttons' do let(:project) { create(:project, :public, :repository, auto_devops_attributes: { enabled: false }) } it 'no Auto DevOps button if can not manage pipelines' do - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).not_to have_link('Enable Auto DevOps') expect(page).not_to have_link('Auto DevOps enabled') end end it 'no Kubernetes cluster button if can not manage clusters' do - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).not_to have_link('Add Kubernetes cluster') expect(page).not_to have_link('Kubernetes configured') end @@ -151,59 +151,59 @@ describe 'Projects > Show > User sees setup shortcut buttons' do sign_in(user) end - context 'Readme button' do + context 'README button' do before do allow(Project).to receive(:find_by_full_path) .with(project.full_path, follow_redirects: true) .and_return(project) end - context 'when the project has a populated Readme' do - it 'show the "Readme" anchor' do + context 'when the project has a populated README' do + it 'show the "README" anchor' do visit project_path(project) expect(project.repository.readme).not_to be_nil - page.within('.project-stats') do - expect(page).not_to have_link('Add Readme', href: presenter.add_readme_path) - expect(page).to have_link('Readme', href: presenter.readme_path) + page.within('.project-buttons') do + expect(page).not_to have_link('Add README', href: presenter.add_readme_path) + expect(page).to have_link('README', href: presenter.readme_path) end end - context 'when the project has an empty Readme' do - it 'show the "Readme" anchor' do + context 'when the project has an empty README' do + it 'show the "README" anchor' do allow(project.repository).to receive(:readme).and_return(fake_blob(path: 'README.md', data: '', size: 0)) visit project_path(project) - page.within('.project-stats') do - expect(page).not_to have_link('Add Readme', href: presenter.add_readme_path) - expect(page).to have_link('Readme', href: presenter.readme_path) + page.within('.project-buttons') do + expect(page).not_to have_link('Add README', href: presenter.add_readme_path) + expect(page).to have_link('README', href: presenter.readme_path) end end end end - context 'when the project does not have a Readme' do - it 'shows the "Add Readme" button' do + context 'when the project does not have a README' do + it 'shows the "Add README" button' do allow(project.repository).to receive(:readme).and_return(nil) visit project_path(project) - page.within('.project-stats') do - expect(page).to have_link('Add Readme', href: presenter.add_readme_path) + page.within('.project-buttons') do + expect(page).to have_link('Add README', href: presenter.add_readme_path) end end end end - it 'no "Add Changelog" button if the project already has a changelog' do + it 'no "Add CHANGELOG" button if the project already has a changelog' do visit project_path(project) expect(project.repository.changelog).not_to be_nil - page.within('.project-stats') do - expect(page).not_to have_link('Add Changelog') + page.within('.project-buttons') do + expect(page).not_to have_link('Add CHANGELOG') end end @@ -212,18 +212,18 @@ describe 'Projects > Show > User sees setup shortcut buttons' do expect(project.repository.license_blob).not_to be_nil - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).not_to have_link('Add license') end end - it 'no "Add Contribution guide" button if the project already has a contribution guide' do + it 'no "Add CONTRIBUTING" button if the project already has a contribution guide' do visit project_path(project) expect(project.repository.contribution_guide).not_to be_nil - page.within('.project-stats') do - expect(page).not_to have_link('Add Contribution guide') + page.within('.project-buttons') do + expect(page).not_to have_link('Add CONTRIBUTING') end end @@ -232,7 +232,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do it 'no "Set up CI/CD" button if the project has Auto DevOps enabled' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).not_to have_link('Set up CI/CD') end end @@ -246,7 +246,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do expect(project.repository.gitlab_ci_yml).to be_nil - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path) end end @@ -266,7 +266,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).not_to have_link('Set up CI/CD') end end @@ -278,7 +278,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do it '"Auto DevOps enabled" anchor linked to settings page' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) end end @@ -290,7 +290,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do it '"Enable Auto DevOps" button linked to settings page' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) end end @@ -302,7 +302,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do expect(page).to have_selector('.js-autodevops-banner') - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).not_to have_link('Enable Auto DevOps') expect(page).not_to have_link('Auto DevOps enabled') end @@ -323,7 +323,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).not_to have_link('Enable Auto DevOps') expect(page).not_to have_link('Auto DevOps enabled') end @@ -335,7 +335,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do it '"Add Kubernetes cluster" button linked to clusters page' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project)) end end @@ -345,7 +345,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do visit project_path(project) - page.within('.project-stats') do + page.within('.project-buttons') do expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster)) end end diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb index 3f4fe549f3e..36cfeb5ed84 100644 --- a/spec/features/tags/master_views_tags_spec.rb +++ b/spec/features/tags/master_views_tags_spec.rb @@ -13,7 +13,7 @@ describe 'Maintainer views tags' do before do visit project_path(project) - click_on 'Add Readme' + click_on 'Add README' fill_in :commit_message, with: 'Add a README file', visible: true click_button 'Commit changes' visit project_tags_path(project) diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index 515f6f70b99..80f7232f282 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -640,4 +640,131 @@ describe IssuesFinder do end end end + + describe '#use_subquery_for_search?' do + let(:finder) { described_class.new(nil, params) } + + before do + allow(Gitlab::Database).to receive(:postgresql?).and_return(true) + stub_feature_flags(use_subquery_for_group_issues_search: true) + end + + context 'when there is no search param' do + let(:params) { { attempt_group_search_optimizations: true } } + + it 'returns false' do + expect(finder.use_subquery_for_search?).to be_falsey + end + end + + context 'when the database is not Postgres' do + let(:params) { { search: 'foo', attempt_group_search_optimizations: true } } + + before do + allow(Gitlab::Database).to receive(:postgresql?).and_return(false) + end + + it 'returns false' do + expect(finder.use_subquery_for_search?).to be_falsey + end + end + + context 'when the attempt_group_search_optimizations param is falsey' do + let(:params) { { search: 'foo' } } + + it 'returns false' do + expect(finder.use_subquery_for_search?).to be_falsey + end + end + + context 'when the use_subquery_for_group_issues_search flag is disabled' do + let(:params) { { search: 'foo', attempt_group_search_optimizations: true } } + + before do + stub_feature_flags(use_subquery_for_group_issues_search: false) + end + + it 'returns false' do + expect(finder.use_subquery_for_search?).to be_falsey + end + end + + context 'when all conditions are met' do + let(:params) { { search: 'foo', attempt_group_search_optimizations: true } } + + it 'returns true' do + expect(finder.use_subquery_for_search?).to be_truthy + end + end + end + + describe '#use_cte_for_search?' do + let(:finder) { described_class.new(nil, params) } + + before do + allow(Gitlab::Database).to receive(:postgresql?).and_return(true) + stub_feature_flags(use_cte_for_group_issues_search: true) + stub_feature_flags(use_subquery_for_group_issues_search: false) + end + + context 'when there is no search param' do + let(:params) { { attempt_group_search_optimizations: true } } + + it 'returns false' do + expect(finder.use_cte_for_search?).to be_falsey + end + end + + context 'when the database is not Postgres' do + let(:params) { { search: 'foo', attempt_group_search_optimizations: true } } + + before do + allow(Gitlab::Database).to receive(:postgresql?).and_return(false) + end + + it 'returns false' do + expect(finder.use_cte_for_search?).to be_falsey + end + end + + context 'when the attempt_group_search_optimizations param is falsey' do + let(:params) { { search: 'foo' } } + + it 'returns false' do + expect(finder.use_cte_for_search?).to be_falsey + end + end + + context 'when the use_cte_for_group_issues_search flag is disabled' do + let(:params) { { search: 'foo', attempt_group_search_optimizations: true } } + + before do + stub_feature_flags(use_cte_for_group_issues_search: false) + end + + it 'returns false' do + expect(finder.use_cte_for_search?).to be_falsey + end + end + + context 'when use_subquery_for_search? is true' do + let(:params) { { search: 'foo', attempt_group_search_optimizations: true } } + + before do + stub_feature_flags(use_subquery_for_group_issues_search: true) + end + + it 'returns false' do + expect(finder.use_cte_for_search?).to be_falsey + end + end + + context 'when all conditions are met' do + let(:params) { { search: 'foo', attempt_group_search_optimizations: true } } + + it 'returns true' do + expect(finder.use_cte_for_search?).to be_truthy + end + end + end end diff --git a/spec/fixtures/api/schemas/cluster_status.json b/spec/fixtures/api/schemas/cluster_status.json index ccef17a6615..3d9e0628f63 100644 --- a/spec/fixtures/api/schemas/cluster_status.json +++ b/spec/fixtures/api/schemas/cluster_status.json @@ -32,7 +32,8 @@ }, "status_reason": { "type": ["string", "null"] }, "external_ip": { "type": ["string", "null"] }, - "hostname": { "type": ["string", "null"] } + "hostname": { "type": ["string", "null"] }, + "email": { "type": ["string", "null"] } }, "required" : [ "name", "status" ] } diff --git a/spec/fixtures/api/schemas/job/trigger.json b/spec/fixtures/api/schemas/job/trigger.json index 1c7e9cc7693..807178c662c 100644 --- a/spec/fixtures/api/schemas/job/trigger.json +++ b/spec/fixtures/api/schemas/job/trigger.json @@ -12,12 +12,11 @@ "type": "object", "required": [ "key", - "value", "public" ], "properties": { "key": { "type": "string" }, - "value": { "type": "string" }, + "value": { "type": "string", "optional": true }, "public": { "type": "boolean" } }, "additionalProperties": false diff --git a/spec/initializers/lograge_spec.rb b/spec/initializers/lograge_spec.rb new file mode 100644 index 00000000000..af54a777373 --- /dev/null +++ b/spec/initializers/lograge_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'lograge', type: :request do + let(:headers) { { 'X-Request-ID' => 'new-correlation-id' } } + + context 'for API requests' do + subject { get("/api/v4/endpoint", {}, headers) } + + it 'logs to api_json log' do + # we assert receiving parameters by grape logger + expect_any_instance_of(Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp).to receive(:call) + .with(anything, anything, anything, a_hash_including("correlation_id" => "new-correlation-id")) + .and_call_original + + subject + end + end + + context 'for Controller requests' do + subject { get("/", {}, headers) } + + it 'logs to production_json log' do + # formatter receives a hash with correlation id + expect(Lograge.formatter).to receive(:call) + .with(a_hash_including("correlation_id" => "new-correlation-id")) + .and_call_original + + # a log file receives a line with correlation id + expect(Lograge.logger).to receive(:send) + .with(anything, include('"correlation_id":"new-correlation-id"')) + .and_call_original + + subject + end + end +end diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js index e46edec9abb..14ef1193984 100644 --- a/spec/javascripts/clusters/components/applications_spec.js +++ b/spec/javascripts/clusters/components/applications_spec.js @@ -176,6 +176,54 @@ describe('Applications', () => { }); }); + describe('Cert-Manager application', () => { + describe('when not installed', () => { + it('renders email & allows editing', () => { + vm = mountComponent(Applications, { + applications: { + helm: { title: 'Helm Tiller', status: 'installed' }, + ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, + cert_manager: { + title: 'Cert-Manager', + email: 'before@example.com', + status: 'installable', + }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' }, + knative: { title: 'Knative', hostname: '', status: 'installable' }, + }, + }); + + expect(vm.$el.querySelector('.js-email').value).toEqual('before@example.com'); + expect(vm.$el.querySelector('.js-email').getAttribute('readonly')).toBe(null); + }); + }); + + describe('when installed', () => { + it('renders email in readonly', () => { + vm = mountComponent(Applications, { + applications: { + helm: { title: 'Helm Tiller', status: 'installed' }, + ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, + cert_manager: { + title: 'Cert-Manager', + email: 'after@example.com', + status: 'installed', + }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' }, + knative: { title: 'Knative', hostname: '', status: 'installable' }, + }, + }); + + expect(vm.$el.querySelector('.js-email').value).toEqual('after@example.com'); + expect(vm.$el.querySelector('.js-email').getAttribute('readonly')).toEqual('readonly'); + }); + }); + }); + describe('Jupyter application', () => { describe('with ingress installed with ip & jupyter installable', () => { it('renders hostname active input', () => { diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js index 540d7f30858..3c3d9977ffb 100644 --- a/spec/javascripts/clusters/services/mock_data.js +++ b/spec/javascripts/clusters/services/mock_data.js @@ -42,6 +42,7 @@ const CLUSTERS_MOCK_DATA = { name: 'cert_manager', status: APPLICATION_STATUS.ERROR, status_reason: 'Cannot connect', + email: 'test@example.com', }, ], }, @@ -86,6 +87,7 @@ const CLUSTERS_MOCK_DATA = { name: 'cert_manager', status: APPLICATION_STATUS.ERROR, status_reason: 'Cannot connect', + email: 'test@example.com', }, ], }, diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js index 7ea0878ad45..1ca55549094 100644 --- a/spec/javascripts/clusters/stores/clusters_store_spec.js +++ b/spec/javascripts/clusters/stores/clusters_store_spec.js @@ -115,6 +115,7 @@ describe('Clusters Store', () => { statusReason: mockResponseData.applications[6].status_reason, requestStatus: null, requestReason: null, + email: mockResponseData.applications[6].email, }, }, }); diff --git a/spec/javascripts/diffs/mock_data/diff_discussions.js b/spec/javascripts/diffs/mock_data/diff_discussions.js index 5ffe5a366ba..44313caba29 100644 --- a/spec/javascripts/diffs/mock_data/diff_discussions.js +++ b/spec/javascripts/diffs/mock_data/diff_discussions.js @@ -489,8 +489,6 @@ export default { diff_discussion: true, truncated_diff_lines: '<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="1">\n1\n</td>\n<td class="line_content new noteable_line"><span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n</td>\n</tr>\n<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="2">\n2\n</td>\n<td class="line_content new noteable_line"><span id="LC2" class="line" lang="plaintext"></span>\n</td>\n</tr>\n', - image_diff_html: - '<div class="image js-replaced-image" data="">\n<div class="two-up view">\n<div class="wrap">\n<div class="frame deleted">\n<img alt="CHANGELOG" src="http://localhost:3000/gitlab-org/gitlab-test/raw/e63f41fe459e62e1228fcef60d7189127aeba95a/CHANGELOG" />\n</div>\n<p class="image-info hide">\n<span class="meta-filesize">22.3 KB</span>\n|\n<strong>W:</strong>\n<span class="meta-width"></span>\n|\n<strong>H:</strong>\n<span class="meta-height"></span>\n</p>\n</div>\n<div class="wrap">\n<div class="added frame js-image-frame" data-note-type="DiffNote" data-position="{"base_sha":"e63f41fe459e62e1228fcef60d7189127aeba95a","start_sha":"d9eaefe5a676b820c57ff18cf5b68316025f7962","head_sha":"c48ee0d1bf3b30453f5b32250ce03134beaa6d13","old_path":"CHANGELOG","new_path":"CHANGELOG","position_type":"text","old_line":null,"new_line":2}">\n<img alt="CHANGELOG" draggable="false" src="http://localhost:3000/gitlab-org/gitlab-test/raw/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG" />\n</div>\n\n<p class="image-info hide">\n<span class="meta-filesize">22.3 KB</span>\n|\n<strong>W:</strong>\n<span class="meta-width"></span>\n|\n<strong>H:</strong>\n<span class="meta-height"></span>\n</p>\n</div>\n</div>\n<div class="swipe view hide">\n<div class="swipe-frame">\n<div class="frame deleted">\n<img alt="CHANGELOG" src="http://localhost:3000/gitlab-org/gitlab-test/raw/e63f41fe459e62e1228fcef60d7189127aeba95a/CHANGELOG" />\n</div>\n<div class="swipe-wrap">\n<div class="added frame js-image-frame" data-note-type="DiffNote" data-position="{"base_sha":"e63f41fe459e62e1228fcef60d7189127aeba95a","start_sha":"d9eaefe5a676b820c57ff18cf5b68316025f7962","head_sha":"c48ee0d1bf3b30453f5b32250ce03134beaa6d13","old_path":"CHANGELOG","new_path":"CHANGELOG","position_type":"text","old_line":null,"new_line":2}">\n<img alt="CHANGELOG" draggable="false" src="http://localhost:3000/gitlab-org/gitlab-test/raw/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG" />\n</div>\n\n</div>\n<span class="swipe-bar">\n<span class="top-handle"></span>\n<span class="bottom-handle"></span>\n</span>\n</div>\n</div>\n<div class="onion-skin view hide">\n<div class="onion-skin-frame">\n<div class="frame deleted">\n<img alt="CHANGELOG" src="http://localhost:3000/gitlab-org/gitlab-test/raw/e63f41fe459e62e1228fcef60d7189127aeba95a/CHANGELOG" />\n</div>\n<div class="added frame js-image-frame" data-note-type="DiffNote" data-position="{"base_sha":"e63f41fe459e62e1228fcef60d7189127aeba95a","start_sha":"d9eaefe5a676b820c57ff18cf5b68316025f7962","head_sha":"c48ee0d1bf3b30453f5b32250ce03134beaa6d13","old_path":"CHANGELOG","new_path":"CHANGELOG","position_type":"text","old_line":null,"new_line":2}">\n<img alt="CHANGELOG" draggable="false" src="http://localhost:3000/gitlab-org/gitlab-test/raw/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG" />\n</div>\n\n<div class="controls">\n<div class="transparent"></div>\n<div class="drag-track">\n<div class="dragger" style="left: 0px;"></div>\n</div>\n<div class="opaque"></div>\n</div>\n</div>\n</div>\n</div>\n<div class="view-modes hide">\n<ul class="view-modes-menu">\n<li class="two-up" data-mode="two-up">2-up</li>\n<li class="swipe" data-mode="swipe">Swipe</li>\n<li class="onion-skin" data-mode="onion-skin">Onion skin</li>\n</ul>\n</div>\n', }; export const imageDiffDiscussions = [ diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js index 4b339a0553f..55ce19927e0 100644 --- a/spec/javascripts/diffs/store/actions_spec.js +++ b/spec/javascripts/diffs/store/actions_spec.js @@ -29,6 +29,7 @@ import actions, { } from '~/diffs/store/actions'; import * as types from '~/diffs/store/mutation_types'; import axios from '~/lib/utils/axios_utils'; +import mockDiffFile from 'spec/diffs/mock_data/diff_file'; import testAction from '../../helpers/vuex_action_helper'; describe('DiffsStoreActions', () => { @@ -607,11 +608,18 @@ describe('DiffsStoreActions', () => { }); describe('saveDiffDiscussion', () => { - beforeEach(() => { - spyOnDependency(actions, 'getNoteFormData').and.returnValue('testData'); - }); - it('dispatches actions', done => { + const commitId = 'something'; + const formData = { + diffFile: { ...mockDiffFile }, + noteableData: {}, + }; + const note = {}; + const state = { + commit: { + id: commitId, + }, + }; const dispatch = jasmine.createSpy('dispatch').and.callFake(name => { switch (name) { case 'saveNote': @@ -625,11 +633,19 @@ describe('DiffsStoreActions', () => { } }); - saveDiffDiscussion({ dispatch }, { note: {}, formData: {} }) + saveDiffDiscussion({ state, dispatch }, { note, formData }) .then(() => { - expect(dispatch.calls.argsFor(0)).toEqual(['saveNote', 'testData', { root: true }]); - expect(dispatch.calls.argsFor(1)).toEqual(['updateDiscussion', 'test', { root: true }]); - expect(dispatch.calls.argsFor(2)).toEqual(['assignDiscussionsToDiff', ['discussion']]); + const { calls } = dispatch; + + expect(calls.count()).toBe(5); + expect(calls.argsFor(0)).toEqual(['saveNote', jasmine.any(Object), { root: true }]); + + const postData = calls.argsFor(0)[1]; + + expect(postData.data.note.commit_id).toBe(commitId); + + expect(calls.argsFor(1)).toEqual(['updateDiscussion', 'test', { root: true }]); + expect(calls.argsFor(2)).toEqual(['assignDiscussionsToDiff', ['discussion']]); }) .then(done) .catch(done.fail); diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js index 717f983da65..f096638e3d6 100644 --- a/spec/javascripts/diffs/store/utils_spec.js +++ b/spec/javascripts/diffs/store/utils_spec.js @@ -150,7 +150,7 @@ describe('DiffsStoreUtils', () => { note: { noteable_type: options.noteableType, noteable_id: options.noteableData.id, - commit_id: '', + commit_id: undefined, type: DIFF_NOTE_TYPE, line_code: options.noteTargetLine.line_code, note: options.note, @@ -209,7 +209,7 @@ describe('DiffsStoreUtils', () => { note: { noteable_type: options.noteableType, noteable_id: options.noteableData.id, - commit_id: '', + commit_id: undefined, type: LEGACY_DIFF_NOTE_TYPE, line_code: options.noteTargetLine.line_code, note: options.note, diff --git a/spec/javascripts/jobs/components/trigger_block_spec.js b/spec/javascripts/jobs/components/trigger_block_spec.js index 7254851a9e7..448197b82c0 100644 --- a/spec/javascripts/jobs/components/trigger_block_spec.js +++ b/spec/javascripts/jobs/components/trigger_block_spec.js @@ -31,8 +31,8 @@ describe('Trigger block', () => { }); describe('with variables', () => { - describe('reveal variables', () => { - it('reveals variables on click', done => { + describe('hide/reveal variables', () => { + it('should toggle variables on click', done => { vm = mountComponent(Component, { trigger: { short_token: 'bd7e', @@ -48,6 +48,10 @@ describe('Trigger block', () => { vm.$nextTick() .then(() => { expect(vm.$el.querySelector('.js-build-variables')).not.toBeNull(); + expect(vm.$el.querySelector('.js-reveal-variables').textContent.trim()).toEqual( + 'Hide values', + ); + expect(vm.$el.querySelector('.js-build-variables').textContent).toContain( 'UPLOAD_TO_GCS', ); @@ -58,6 +62,26 @@ describe('Trigger block', () => { ); expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('true'); + + vm.$el.querySelector('.js-reveal-variables').click(); + }) + .then(vm.$nextTick) + .then(() => { + expect(vm.$el.querySelector('.js-reveal-variables').textContent.trim()).toEqual( + 'Reveal values', + ); + + expect(vm.$el.querySelector('.js-build-variables').textContent).toContain( + 'UPLOAD_TO_GCS', + ); + + expect(vm.$el.querySelector('.js-build-value').textContent).toContain('••••••'); + + expect(vm.$el.querySelector('.js-build-variables').textContent).toContain( + 'UPLOAD_TO_S3', + ); + + expect(vm.$el.querySelector('.js-build-value').textContent).toContain('••••••'); }) .then(done) .catch(done.fail); diff --git a/spec/javascripts/lib/utils/dom_utils_spec.js b/spec/javascripts/lib/utils/dom_utils_spec.js index 1fb2e4584a0..2bcf37f35c7 100644 --- a/spec/javascripts/lib/utils/dom_utils_spec.js +++ b/spec/javascripts/lib/utils/dom_utils_spec.js @@ -1,4 +1,6 @@ -import { addClassIfElementExists } from '~/lib/utils/dom_utils'; +import { addClassIfElementExists, canScrollUp, canScrollDown } from '~/lib/utils/dom_utils'; + +const TEST_MARGIN = 5; describe('DOM Utils', () => { describe('addClassIfElementExists', () => { @@ -34,4 +36,54 @@ describe('DOM Utils', () => { addClassIfElementExists(childElement, className); }); }); + + describe('canScrollUp', () => { + [1, 100].forEach(scrollTop => { + it(`is true if scrollTop is > 0 (${scrollTop})`, () => { + expect(canScrollUp({ scrollTop })).toBe(true); + }); + }); + + [0, -10].forEach(scrollTop => { + it(`is false if scrollTop is <= 0 (${scrollTop})`, () => { + expect(canScrollUp({ scrollTop })).toBe(false); + }); + }); + + it('is true if scrollTop is > margin', () => { + expect(canScrollUp({ scrollTop: TEST_MARGIN + 1 }, TEST_MARGIN)).toBe(true); + }); + + it('is false if scrollTop is <= margin', () => { + expect(canScrollUp({ scrollTop: TEST_MARGIN }, TEST_MARGIN)).toBe(false); + }); + }); + + describe('canScrollDown', () => { + let element; + + beforeEach(() => { + element = { scrollTop: 7, offsetHeight: 22, scrollHeight: 30 }; + }); + + it('is true if element can be scrolled down', () => { + expect(canScrollDown(element)).toBe(true); + }); + + it('is false if element cannot be scrolled down', () => { + element.scrollHeight -= 1; + + expect(canScrollDown(element)).toBe(false); + }); + + it('is true if element can be scrolled down, with margin given', () => { + element.scrollHeight += TEST_MARGIN; + + expect(canScrollDown(element, TEST_MARGIN)).toBe(true); + }); + + it('is false if element cannot be scrolled down, with margin given', () => { + expect(canScrollDown(element, TEST_MARGIN)).toBe(false); + }); + }); }); diff --git a/spec/lib/banzai/filter/front_matter_filter_spec.rb b/spec/lib/banzai/filter/front_matter_filter_spec.rb new file mode 100644 index 00000000000..3071dc7cf21 --- /dev/null +++ b/spec/lib/banzai/filter/front_matter_filter_spec.rb @@ -0,0 +1,140 @@ +require 'rails_helper' + +describe Banzai::Filter::FrontMatterFilter do + include FilterSpecHelper + + it 'allows for `encoding:` before the front matter' do + content = <<~MD + # encoding: UTF-8 + --- + foo: foo + bar: bar + --- + + # Header + + Content + MD + + output = filter(content) + + expect(output).not_to match 'encoding' + end + + it 'converts YAML front matter to a fenced code block' do + content = <<~MD + --- + foo: :foo_symbol + bar: :bar_symbol + --- + + # Header + + Content + MD + + output = filter(content) + + aggregate_failures do + expect(output).not_to include '---' + expect(output).to include "```yaml\nfoo: :foo_symbol\n" + end + end + + it 'converts TOML frontmatter to a fenced code block' do + content = <<~MD + +++ + foo = :foo_symbol + bar = :bar_symbol + +++ + + # Header + + Content + MD + + output = filter(content) + + aggregate_failures do + expect(output).not_to include '+++' + expect(output).to include "```toml\nfoo = :foo_symbol\n" + end + end + + it 'converts JSON front matter to a fenced code block' do + content = <<~MD + ;;; + { + "foo": ":foo_symbol", + "bar": ":bar_symbol" + } + ;;; + + # Header + + Content + MD + + output = filter(content) + + aggregate_failures do + expect(output).not_to include ';;;' + expect(output).to include "```json\n{\n \"foo\": \":foo_symbol\",\n" + end + end + + it 'converts arbitrary front matter to a fenced code block' do + content = <<~MD + ---arbitrary + foo = :foo_symbol + bar = :bar_symbol + --- + + # Header + + Content + MD + + output = filter(content) + + aggregate_failures do + expect(output).not_to include '---arbitrary' + expect(output).to include "```arbitrary\nfoo = :foo_symbol\n" + end + end + + context 'on content without front matter' do + it 'returns the content unmodified' do + content = <<~MD + # This is some Markdown + + It has no YAML front matter to parse. + MD + + expect(filter(content)).to eq content + end + end + + context 'on front matter without content' do + it 'converts YAML front matter to a fenced code block' do + content = <<~MD + --- + foo: :foo_symbol + bar: :bar_symbol + --- + MD + + output = filter(content) + + aggregate_failures do + expect(output).to eq <<~MD + ```yaml + foo: :foo_symbol + bar: :bar_symbol + ``` + + MD + end + end + end +end diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index 91d4a60ba95..1a87cfa5b45 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -351,21 +351,50 @@ describe Banzai::Filter::MilestoneReferenceFilter do end context 'group context' do - let(:context) { { project: nil, group: create(:group) } } - let(:milestone) { create(:milestone, project: project) } + let(:group) { create(:group) } + let(:context) { { project: nil, group: group } } - it 'links to a valid reference' do - reference = "#{project.full_path}%#{milestone.iid}" + context 'when project milestone' do + let(:milestone) { create(:milestone, project: project) } - result = reference_filter("See #{reference}", context) + it 'links to a valid reference' do + reference = "#{project.full_path}%#{milestone.iid}" - expect(result.css('a').first.attr('href')).to eq(urls.milestone_url(milestone)) + result = reference_filter("See #{reference}", context) + + expect(result.css('a').first.attr('href')).to eq(urls.milestone_url(milestone)) + end + + it 'ignores internal references' do + exp = act = "See %#{milestone.iid}" + + expect(reference_filter(act, context).to_html).to eq exp + end end - it 'ignores internal references' do - exp = act = "See %#{milestone.iid}" + context 'when group milestone' do + let(:group_milestone) { create(:milestone, title: 'group_milestone', group: group) } - expect(reference_filter(act, context).to_html).to eq exp + context 'for subgroups', :nested_groups do + let(:sub_group) { create(:group, parent: group) } + let(:sub_group_milestone) { create(:milestone, title: 'sub_group_milestone', group: sub_group) } + + it 'links to a valid reference of subgroup and group milestones' do + [group_milestone, sub_group_milestone].each do |milestone| + reference = "%#{milestone.title}" + + result = reference_filter("See #{reference}", { project: nil, group: sub_group }) + + expect(result.css('a').first.attr('href')).to eq(urls.milestone_url(milestone)) + end + end + end + + it 'ignores internal references' do + exp = act = "See %#{group_milestone.iid}" + + expect(reference_filter(act, context).to_html).to eq exp + end end end diff --git a/spec/lib/banzai/filter/yaml_front_matter_filter_spec.rb b/spec/lib/banzai/filter/yaml_front_matter_filter_spec.rb deleted file mode 100644 index 9f1b862ef19..00000000000 --- a/spec/lib/banzai/filter/yaml_front_matter_filter_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -require 'rails_helper' - -describe Banzai::Filter::YamlFrontMatterFilter do - include FilterSpecHelper - - it 'allows for `encoding:` before the frontmatter' do - content = <<-MD.strip_heredoc - # encoding: UTF-8 - --- - foo: foo - --- - - # Header - - Content - MD - - output = filter(content) - - expect(output).not_to match 'encoding' - end - - it 'converts YAML frontmatter to a fenced code block' do - content = <<-MD.strip_heredoc - --- - bar: :bar_symbol - --- - - # Header - - Content - MD - - output = filter(content) - - aggregate_failures do - expect(output).not_to include '---' - expect(output).to include "```yaml\nbar: :bar_symbol\n```" - end - end - - context 'on content without frontmatter' do - it 'returns the content unmodified' do - content = <<-MD.strip_heredoc - # This is some Markdown - - It has no YAML frontmatter to parse. - MD - - expect(filter(content)).to eq content - end - end -end diff --git a/spec/lib/gitlab/background_migration/backfill_hashed_project_repositories_spec.rb b/spec/lib/gitlab/background_migration/backfill_hashed_project_repositories_spec.rb new file mode 100644 index 00000000000..b6c1edbbf8b --- /dev/null +++ b/spec/lib/gitlab/background_migration/backfill_hashed_project_repositories_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::BackgroundMigration::BackfillHashedProjectRepositories, :migration, schema: 20181130102132 do + let(:namespaces) { table(:namespaces) } + let(:project_repositories) { table(:project_repositories) } + let(:projects) { table(:projects) } + let(:shards) { table(:shards) } + let(:group) { namespaces.create!(name: 'foo', path: 'foo') } + let(:shard) { shards.create!(name: 'default') } + + describe described_class::ShardFinder do + describe '#find_shard_id' do + it 'creates a new shard when it does not exist yet' do + expect { subject.find_shard_id('other') }.to change(shards, :count).by(1) + end + + it 'returns the shard when it exists' do + shards.create(id: 5, name: 'other') + + shard_id = subject.find_shard_id('other') + + expect(shard_id).to eq(5) + end + + it 'only queries the database once to retrieve shards' do + subject.find_shard_id('default') + + expect { subject.find_shard_id('default') }.not_to exceed_query_limit(0) + end + end + end + + describe described_class::Project do + describe '.on_hashed_storage' do + it 'finds projects with repository on hashed storage' do + projects.create!(id: 1, name: 'foo', path: 'foo', namespace_id: group.id, storage_version: 1) + projects.create!(id: 2, name: 'bar', path: 'bar', namespace_id: group.id, storage_version: 2) + projects.create!(id: 3, name: 'baz', path: 'baz', namespace_id: group.id, storage_version: 0) + projects.create!(id: 4, name: 'zoo', path: 'zoo', namespace_id: group.id, storage_version: nil) + + expect(described_class.on_hashed_storage.pluck(:id)).to match_array([1, 2]) + end + end + + describe '.without_project_repository' do + it 'finds projects which do not have a projects_repositories entry' do + projects.create!(id: 1, name: 'foo', path: 'foo', namespace_id: group.id) + projects.create!(id: 2, name: 'bar', path: 'bar', namespace_id: group.id) + project_repositories.create!(project_id: 2, disk_path: '@phony/foo/bar', shard_id: shard.id) + + expect(described_class.without_project_repository.pluck(:id)).to contain_exactly(1) + end + end + end + + describe '#perform' do + it 'creates a project_repository row for projects on hashed storage that need one' do + projects.create!(id: 1, name: 'foo', path: 'foo', namespace_id: group.id, storage_version: 1) + projects.create!(id: 2, name: 'bar', path: 'bar', namespace_id: group.id, storage_version: 2) + + expect { described_class.new.perform(1, projects.last.id) }.to change(project_repositories, :count).by(2) + end + + it 'does nothing for projects on hashed storage that have already a project_repository row' do + projects.create!(id: 1, name: 'foo', path: 'foo', namespace_id: group.id, storage_version: 1) + project_repositories.create!(project_id: 1, disk_path: '@phony/foo/bar', shard_id: shard.id) + + expect { described_class.new.perform(1, projects.last.id) }.not_to change(project_repositories, :count) + end + + it 'does nothing for projects on legacy storage' do + projects.create!(name: 'foo', path: 'foo', namespace_id: group.id, storage_version: 0) + + expect { described_class.new.perform(1, projects.last.id) }.not_to change(project_repositories, :count) + end + + it 'inserts rows in a single query' do + projects.create!(name: 'foo', path: 'foo', namespace_id: group.id, storage_version: 1, repository_storage: shard.name) + + control_count = ActiveRecord::QueryRecorder.new { described_class.new.perform(1, projects.last.id) } + + projects.create!(name: 'bar', path: 'bar', namespace_id: group.id, storage_version: 1, repository_storage: shard.name) + projects.create!(name: 'zoo', path: 'zoo', namespace_id: group.id, storage_version: 1, repository_storage: shard.name) + + expect { described_class.new.perform(1, projects.last.id) }.not_to exceed_query_limit(control_count) + end + end +end diff --git a/spec/lib/gitlab/branch_push_merge_commit_analyzer_spec.rb b/spec/lib/gitlab/branch_push_merge_commit_analyzer_spec.rb new file mode 100644 index 00000000000..1e969542975 --- /dev/null +++ b/spec/lib/gitlab/branch_push_merge_commit_analyzer_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::BranchPushMergeCommitAnalyzer do + let(:project) { create(:project, :repository) } + let(:oldrev) { 'merge-commit-analyze-before' } + let(:newrev) { 'merge-commit-analyze-after' } + let(:commits) { project.repository.commits_between(oldrev, newrev).reverse } + + subject { described_class.new(commits) } + + describe '#get_merge_commit' do + let(:expected_merge_commits) do + { + '646ece5cfed840eca0a4feb21bcd6a81bb19bda3' => '646ece5cfed840eca0a4feb21bcd6a81bb19bda3', + '29284d9bcc350bcae005872d0be6edd016e2efb5' => '29284d9bcc350bcae005872d0be6edd016e2efb5', + '5f82584f0a907f3b30cfce5bb8df371454a90051' => '29284d9bcc350bcae005872d0be6edd016e2efb5', + '8a994512e8c8f0dfcf22bb16df6e876be7a61036' => '29284d9bcc350bcae005872d0be6edd016e2efb5', + '689600b91aabec706e657e38ea706ece1ee8268f' => '29284d9bcc350bcae005872d0be6edd016e2efb5', + 'db46a1c5a5e474aa169b6cdb7a522d891bc4c5f9' => 'db46a1c5a5e474aa169b6cdb7a522d891bc4c5f9' + } + end + + it 'returns correct merge commit SHA for each commit' do + expected_merge_commits.each do |commit, merge_commit| + expect(subject.get_merge_commit(commit)).to eq(merge_commit) + end + end + + context 'when one parent has two children' do + let(:oldrev) { '1adbdefe31288f3bbe4b614853de4908a0b6f792' } + let(:newrev) { '5f82584f0a907f3b30cfce5bb8df371454a90051' } + + let(:expected_merge_commits) do + { + '5f82584f0a907f3b30cfce5bb8df371454a90051' => '5f82584f0a907f3b30cfce5bb8df371454a90051', + '8a994512e8c8f0dfcf22bb16df6e876be7a61036' => '5f82584f0a907f3b30cfce5bb8df371454a90051', + '689600b91aabec706e657e38ea706ece1ee8268f' => '689600b91aabec706e657e38ea706ece1ee8268f' + } + end + + it 'returns correct merge commit SHA for each commit' do + expected_merge_commits.each do |commit, merge_commit| + expect(subject.get_merge_commit(commit)).to eq(merge_commit) + end + end + end + + context 'when relevant_commit_ids is provided' do + let(:relevant_commit_id) { '8a994512e8c8f0dfcf22bb16df6e876be7a61036' } + subject { described_class.new(commits, relevant_commit_ids: [relevant_commit_id]) } + + it 'returns correct merge commit' do + expected_merge_commits.each do |commit, merge_commit| + subject = described_class.new(commits, relevant_commit_ids: [commit]) + expect(subject.get_merge_commit(commit)).to eq(merge_commit) + end + end + end + end +end diff --git a/spec/lib/gitlab/correlation_id_spec.rb b/spec/lib/gitlab/correlation_id_spec.rb new file mode 100644 index 00000000000..584d1f48386 --- /dev/null +++ b/spec/lib/gitlab/correlation_id_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +describe Gitlab::CorrelationId do + describe '.use_id' do + it 'yields when executed' do + expect { |blk| described_class.use_id('id', &blk) }.to yield_control + end + + it 'stacks correlation ids' do + described_class.use_id('id1') do + described_class.use_id('id2') do |current_id| + expect(current_id).to eq('id2') + end + end + end + + it 'for missing correlation id it generates random one' do + described_class.use_id('id1') do + described_class.use_id(nil) do |current_id| + expect(current_id).not_to be_empty + expect(current_id).not_to eq('id1') + end + end + end + end + + describe '.current_id' do + subject { described_class.current_id } + + it 'returns last correlation id' do + described_class.use_id('id1') do + described_class.use_id('id2') do + is_expected.to eq('id2') + end + end + end + end + + describe '.current_or_new_id' do + subject { described_class.current_or_new_id } + + context 'when correlation id is set' do + it 'returns last correlation id' do + described_class.use_id('id1') do + is_expected.to eq('id1') + end + end + end + + context 'when correlation id is missing' do + it 'returns a new correlation id' do + expect(described_class).to receive(:new_id) + .and_call_original + + is_expected.not_to be_empty + end + end + end + + describe '.ids' do + subject { described_class.send(:ids) } + + it 'returns empty list if not correlation is used' do + is_expected.to be_empty + end + + it 'returns list if correlation ids are used' do + described_class.use_id('id1') do + described_class.use_id('id2') do + is_expected.to eq(%w(id1 id2)) + end + end + end + end +end diff --git a/spec/lib/gitlab/git/object_pool_spec.rb b/spec/lib/gitlab/git/object_pool_spec.rb new file mode 100644 index 00000000000..363c2aa67af --- /dev/null +++ b/spec/lib/gitlab/git/object_pool_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Git::ObjectPool do + let(:pool_repository) { create(:pool_repository) } + let(:source_repository) { pool_repository.source_project.repository } + + subject { pool_repository.object_pool } + + describe '#storage' do + it "equals the pool repository's shard name" do + expect(subject.storage).not_to be_nil + expect(subject.storage).to eq(pool_repository.shard_name) + end + end + + describe '#create' do + before do + subject.create + end + + context "when the pool doesn't exist yet" do + it 'creates the pool' do + expect(subject.exists?).to be(true) + end + end + + context 'when the pool already exists' do + it 'raises an FailedPrecondition' do + expect do + subject.create + end.to raise_error(GRPC::FailedPrecondition) + end + end + end + + describe '#exists?' do + context "when the object pool doesn't exist" do + it 'returns false' do + expect(subject.exists?).to be(false) + end + end + + context 'when the object pool exists' do + let(:pool) { create(:pool_repository, :ready) } + + subject { pool.object_pool } + + it 'returns true' do + expect(subject.exists?).to be(true) + end + end + end + + describe '#link' do + let!(:pool_repository) { create(:pool_repository, :ready) } + + context 'when no remotes are set' do + it 'sets a remote' do + subject.link(source_repository) + + repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Rugged::Repository.new(subject.repository.path) + end + + expect(repo.remotes.count).to be(1) + expect(repo.remotes.first.name).to eq(source_repository.object_pool_remote_name) + end + end + + context 'when the remote is already set' do + before do + subject.link(source_repository) + end + + it "doesn't raise an error" do + subject.link(source_repository) + + repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Rugged::Repository.new(subject.repository.path) + end + + expect(repo.remotes.count).to be(1) + expect(repo.remotes.first.name).to eq(source_repository.object_pool_remote_name) + end + end + end +end diff --git a/spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb b/spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb new file mode 100644 index 00000000000..149b7ec5bb0 --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::GitalyClient::ObjectPoolService do + let(:pool_repository) { create(:pool_repository) } + let(:project) { create(:project, :repository) } + let(:raw_repository) { project.repository.raw } + let(:object_pool) { pool_repository.object_pool } + + subject { described_class.new(object_pool) } + + before do + subject.create(raw_repository) + end + + describe '#create' do + it 'exists on disk' do + expect(object_pool.repository.exists?).to be(true) + end + + context 'when the pool already exists' do + it 'returns an error' do + expect do + subject.create(raw_repository) + end.to raise_error(GRPC::FailedPrecondition) + end + end + end + + describe '#delete' do + it 'removes the repository from disk' do + subject.delete + + expect(object_pool.repository.exists?).to be(false) + end + + context 'when called twice' do + it "doesn't raise an error" do + subject.delete + + expect { object_pool.delete }.not_to raise_error + end + end + end +end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 7df129da95a..bae5b21c26f 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -287,6 +287,7 @@ project: - statistics - container_repositories - uploads +- file_uploads - import_state - members_and_requesters - build_trace_section_names diff --git a/spec/lib/gitlab/json_logger_spec.rb b/spec/lib/gitlab/json_logger_spec.rb index 0a62785f880..cff7dd58c8c 100644 --- a/spec/lib/gitlab/json_logger_spec.rb +++ b/spec/lib/gitlab/json_logger_spec.rb @@ -7,6 +7,10 @@ describe Gitlab::JsonLogger do let(:now) { Time.now } describe '#format_message' do + before do + allow(Gitlab::CorrelationId).to receive(:current_id).and_return('new-correlation-id') + end + it 'formats strings' do output = subject.format_message('INFO', now, 'test', 'Hello world') data = JSON.parse(output) @@ -14,6 +18,7 @@ describe Gitlab::JsonLogger do expect(data['severity']).to eq('INFO') expect(data['time']).to eq(now.utc.iso8601(3)) expect(data['message']).to eq('Hello world') + expect(data['correlation_id']).to eq('new-correlation-id') end it 'formats hashes' do @@ -24,6 +29,7 @@ describe Gitlab::JsonLogger do expect(data['time']).to eq(now.utc.iso8601(3)) expect(data['hello']).to eq(1) expect(data['message']).to be_nil + expect(data['correlation_id']).to eq('new-correlation-id') end end end diff --git a/spec/lib/gitlab/sentry_spec.rb b/spec/lib/gitlab/sentry_spec.rb index d3b41b27b80..1128eaf8560 100644 --- a/spec/lib/gitlab/sentry_spec.rb +++ b/spec/lib/gitlab/sentry_spec.rb @@ -19,14 +19,15 @@ describe Gitlab::Sentry do end it 'raises the exception if it should' do - expect(described_class).to receive(:should_raise?).and_return(true) + expect(described_class).to receive(:should_raise_for_dev?).and_return(true) expect { described_class.track_exception(exception) } .to raise_error(RuntimeError) end context 'when exceptions should not be raised' do before do - allow(described_class).to receive(:should_raise?).and_return(false) + allow(described_class).to receive(:should_raise_for_dev?).and_return(false) + allow(Gitlab::CorrelationId).to receive(:current_id).and_return('cid') end it 'logs the exception with all attributes passed' do @@ -35,8 +36,14 @@ describe Gitlab::Sentry do issue_url: 'http://gitlab.com/gitlab-org/gitlab-ce/issues/1' } + expected_tags = { + correlation_id: 'cid' + } + expect(Raven).to receive(:capture_exception) - .with(exception, extra: a_hash_including(expected_extras)) + .with(exception, + tags: a_hash_including(expected_tags), + extra: a_hash_including(expected_extras)) described_class.track_exception( exception, @@ -58,6 +65,7 @@ describe Gitlab::Sentry do before do allow(described_class).to receive(:enabled?).and_return(true) + allow(Gitlab::CorrelationId).to receive(:current_id).and_return('cid') end it 'calls Raven.capture_exception' do @@ -66,8 +74,14 @@ describe Gitlab::Sentry do issue_url: 'http://gitlab.com/gitlab-org/gitlab-ce/issues/1' } + expected_tags = { + correlation_id: 'cid' + } + expect(Raven).to receive(:capture_exception) - .with(exception, extra: a_hash_including(expected_extras)) + .with(exception, + tags: a_hash_including(expected_tags), + extra: a_hash_including(expected_extras)) described_class.track_acceptable_exception( exception, diff --git a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb index 2421b1e5a1a..f773f370ee2 100644 --- a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb +++ b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb @@ -12,7 +12,8 @@ describe Gitlab::SidekiqLogging::StructuredLogger do "queue_namespace" => "cronjob", "jid" => "da883554ee4fe414012f5f42", "created_at" => timestamp.to_f, - "enqueued_at" => timestamp.to_f + "enqueued_at" => timestamp.to_f, + "correlation_id" => 'cid' } end let(:logger) { double() } diff --git a/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb b/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb new file mode 100644 index 00000000000..a138ad7c910 --- /dev/null +++ b/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::SidekiqMiddleware::CorrelationInjector do + class TestWorker + include ApplicationWorker + end + + before do |example| + Sidekiq.client_middleware do |chain| + chain.add described_class + end + end + + after do |example| + Sidekiq.client_middleware do |chain| + chain.remove described_class + end + + Sidekiq::Queues.clear_all + end + + around do |example| + Sidekiq::Testing.fake! do + example.run + end + end + + it 'injects into payload the correlation id' do + expect_any_instance_of(described_class).to receive(:call).and_call_original + + Gitlab::CorrelationId.use_id('new-correlation-id') do + TestWorker.perform_async(1234) + end + + expected_job_params = { + "class" => "TestWorker", + "args" => [1234], + "correlation_id" => "new-correlation-id" + } + + expect(Sidekiq::Queues.jobs_by_worker).to a_hash_including( + "TestWorker" => a_collection_containing_exactly( + a_hash_including(expected_job_params))) + end +end diff --git a/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb b/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb new file mode 100644 index 00000000000..94ae4ffa184 --- /dev/null +++ b/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::SidekiqMiddleware::CorrelationLogger do + class TestWorker + include ApplicationWorker + end + + before do |example| + Sidekiq::Testing.server_middleware do |chain| + chain.add described_class + end + end + + after do |example| + Sidekiq::Testing.server_middleware do |chain| + chain.remove described_class + end + end + + it 'injects into payload the correlation id' do + expect_any_instance_of(described_class).to receive(:call).and_call_original + + expect_any_instance_of(TestWorker).to receive(:perform).with(1234) do + expect(Gitlab::CorrelationId.current_id).to eq('new-correlation-id') + end + + Sidekiq::Client.push( + 'queue' => 'test', + 'class' => TestWorker, + 'args' => [1234], + 'correlation_id' => 'new-correlation-id') + end +end diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb index 77b07cf1ac9..35415030154 100644 --- a/spec/models/appearance_spec.rb +++ b/spec/models/appearance_spec.rb @@ -20,7 +20,7 @@ describe Appearance do end context 'with uploads' do - it_behaves_like 'model with mounted uploader', false do + it_behaves_like 'model with uploads', false do let(:model_object) { create(:appearance, :with_logo) } let(:upload_attribute) { :logo } let(:uploader_class) { AttachmentUploader } diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 4cdcae5f670..89f78f629d4 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1925,7 +1925,7 @@ describe Ci::Build do context 'when token is empty' do before do - build.token = nil + build.update_columns(token: nil, token_encrypted: nil) end it { is_expected.to be_nil} @@ -2141,7 +2141,7 @@ describe Ci::Build do end before do - build.token = 'my-token' + build.set_token('my-token') build.yaml_variables = [] end diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb index 97e50809647..47daa79873e 100644 --- a/spec/models/clusters/applications/runner_spec.rb +++ b/spec/models/clusters/applications/runner_spec.rb @@ -18,7 +18,7 @@ describe Clusters::Applications::Runner do let(:application) { create(:clusters_applications_runner, :scheduled, version: '0.1.30') } it 'updates the application version' do - expect(application.reload.version).to eq('0.1.38') + expect(application.reload.version).to eq('0.1.39') end end end @@ -46,7 +46,7 @@ describe Clusters::Applications::Runner do it 'should be initialized with 4 arguments' do expect(subject.name).to eq('runner') expect(subject.chart).to eq('runner/gitlab-runner') - expect(subject.version).to eq('0.1.38') + expect(subject.version).to eq('0.1.39') expect(subject).not_to be_rbac expect(subject.repository).to eq('https://charts.gitlab.io') expect(subject.files).to eq(gitlab_runner.files) @@ -64,7 +64,7 @@ describe Clusters::Applications::Runner do let(:gitlab_runner) { create(:clusters_applications_runner, :errored, runner: ci_runner, version: '0.1.13') } it 'should be initialized with the locked version' do - expect(subject.version).to eq('0.1.38') + expect(subject.version).to eq('0.1.39') end end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 2a0039a0635..a2d2d77746d 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -204,7 +204,7 @@ describe Commit do message = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis id blandit. Vivamus egestas lacinia lacus, sed rutrum mauris.' allow(commit).to receive(:safe_message).and_return(message) - expect(commit.title).to eq('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis…') + expect(commit.title).to eq('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id...') end it "truncates a message with a newline before 80 characters at the newline" do diff --git a/spec/models/concerns/discussion_on_diff_spec.rb b/spec/models/concerns/discussion_on_diff_spec.rb index 8cd129dc851..73eb7a1160d 100644 --- a/spec/models/concerns/discussion_on_diff_spec.rb +++ b/spec/models/concerns/discussion_on_diff_spec.rb @@ -12,6 +12,34 @@ describe DiscussionOnDiff do expect(truncated_lines.count).to be <= DiffDiscussion::NUMBER_OF_TRUNCATED_DIFF_LINES end + + context 'with truncated diff lines diff limit set' do + let(:truncated_lines) do + subject.truncated_diff_lines( + diff_limit: diff_limit + ) + end + + context 'when diff limit is higher than default' do + let(:diff_limit) { DiffDiscussion::NUMBER_OF_TRUNCATED_DIFF_LINES + 1 } + + it 'returns fewer lines than the default' do + expect(subject.diff_lines.count).to be > diff_limit + + expect(truncated_lines.count).to be <= DiffDiscussion::NUMBER_OF_TRUNCATED_DIFF_LINES + end + end + + context 'when diff_limit is lower than default' do + let(:diff_limit) { 3 } + + it 'returns fewer lines than the default' do + expect(subject.diff_lines.count).to be > DiffDiscussion::NUMBER_OF_TRUNCATED_DIFF_LINES + + expect(truncated_lines.count).to be <= diff_limit + end + end + end end context "when some diff lines are meta" do diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb index 0cdf430e9ab..55d83bc3a6b 100644 --- a/spec/models/concerns/token_authenticatable_spec.rb +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -351,3 +351,89 @@ describe PersonalAccessToken, 'TokenAuthenticatable' do end end end + +describe Ci::Build, 'TokenAuthenticatable' do + let(:token_field) { :token } + let(:build) { FactoryBot.build(:ci_build) } + + it_behaves_like 'TokenAuthenticatable' + + describe 'generating new token' do + context 'token is not generated yet' do + describe 'token field accessor' do + it 'makes it possible to access token' do + expect(build.token).to be_nil + + build.save! + + expect(build.token).to be_present + end + end + + describe "ensure_token" do + subject { build.ensure_token } + + it { is_expected.to be_a String } + it { is_expected.not_to be_blank } + + it 'does not persist token' do + expect(build).not_to be_persisted + end + end + + describe 'ensure_token!' do + it 'persists a new token' do + expect(build.ensure_token!).to eq build.reload.token + expect(build).to be_persisted + end + + it 'persists new token as an encrypted string' do + build.ensure_token! + + encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(build.token) + + expect(build.read_attribute('token_encrypted')).to eq encrypted + end + + it 'does not persist a token in a clear text' do + build.ensure_token! + + expect(build.read_attribute('token')).to be_nil + end + end + end + + describe '#reset_token!' do + it 'persists a new token' do + build.save! + + build.token.yield_self do |previous_token| + build.reset_token! + + expect(build.token).not_to eq previous_token + expect(build.token).to be_a String + end + end + end + end + + describe 'setting a new token' do + subject { build.set_token('0123456789') } + + it 'returns the token' do + expect(subject).to eq '0123456789' + end + + it 'writes a new encrypted token' do + expect(build.read_attribute('token_encrypted')).to be_nil + expect(subject).to eq '0123456789' + expect(build.read_attribute('token_encrypted')).to be_present + end + + it 'does not write a new cleartext token' do + expect(build.read_attribute('token')).to be_nil + expect(subject).to eq '0123456789' + expect(build.read_attribute('token')).to be_nil + end + end +end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 87aa5a46c21..e63881242f6 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -739,7 +739,7 @@ describe Group do end context 'with uploads' do - it_behaves_like 'model with mounted uploader', true do + it_behaves_like 'model with uploads', true do let(:model_object) { create(:group, :with_avatar) } let(:upload_attribute) { :avatar } let(:uploader_class) { AttachmentUploader } diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 96561dab1c9..18b54cce834 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -249,7 +249,7 @@ describe Namespace do move_dir_result end - expect(Gitlab::Sentry).to receive(:should_raise?).and_return(false) # like prod + expect(Gitlab::Sentry).to receive(:should_raise_for_dev?).and_return(false) # like prod namespace.update(path: namespace.full_path + '_new') end diff --git a/spec/models/pool_repository_spec.rb b/spec/models/pool_repository_spec.rb index 541e78507e5..3d3878b8c39 100644 --- a/spec/models/pool_repository_spec.rb +++ b/spec/models/pool_repository_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' describe PoolRepository do describe 'associations' do it { is_expected.to belong_to(:shard) } + it { is_expected.to have_one(:source_project) } it { is_expected.to have_many(:member_projects) } end @@ -12,15 +13,14 @@ describe PoolRepository do let!(:pool_repository) { create(:pool_repository) } it { is_expected.to validate_presence_of(:shard) } + it { is_expected.to validate_presence_of(:source_project) } end describe '#disk_path' do it 'sets the hashed disk_path' do pool = create(:pool_repository) - elements = File.split(pool.disk_path) - - expect(elements).to all( match(/\d{2,}/) ) + expect(pool.disk_path).to match(%r{\A@pools/\h{2}/\h{2}/\h{64}}) end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 50920d9d1fc..1c85411dc3b 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3898,7 +3898,7 @@ describe Project do end context 'with uploads' do - it_behaves_like 'model with mounted uploader', true do + it_behaves_like 'model with uploads', true do let(:model_object) { create(:project, :with_avatar) } let(:upload_attribute) { :avatar } let(:uploader_class) { AttachmentUploader } @@ -4092,6 +4092,44 @@ describe Project do end end + describe '#git_objects_poolable?' do + subject { project } + + context 'when the feature flag is turned off' do + before do + stub_feature_flags(object_pools: false) + end + + let(:project) { create(:project, :repository, :public) } + + it { is_expected.not_to be_git_objects_poolable } + end + + context 'when the feature flag is enabled' do + context 'when not using hashed storage' do + let(:project) { create(:project, :legacy_storage, :public, :repository) } + + it { is_expected.not_to be_git_objects_poolable } + end + + context 'when the project is not public' do + let(:project) { create(:project, :private) } + + it { is_expected.not_to be_git_objects_poolable } + end + + context 'when objects are poolable' do + let(:project) { create(:project, :repository, :public) } + + before do + stub_application_setting(hashed_storage_enabled: true) + end + + it { is_expected.to be_git_objects_poolable } + end + end + end + def rugged_config rugged_repo(project.repository).config end diff --git a/spec/models/uploads/fog_spec.rb b/spec/models/uploads/fog_spec.rb new file mode 100644 index 00000000000..4a44cf5ab0f --- /dev/null +++ b/spec/models/uploads/fog_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Uploads::Fog do + let(:data_store) { described_class.new } + + before do + stub_uploads_object_storage(FileUploader) + end + + describe '#available?' do + subject { data_store.available? } + + context 'when object storage is enabled' do + it { is_expected.to be_truthy } + end + + context 'when object storage is disabled' do + before do + stub_uploads_object_storage(FileUploader, enabled: false) + end + + it { is_expected.to be_falsy } + end + end + + context 'model with uploads' do + let(:project) { create(:project) } + let(:relation) { project.uploads } + + describe '#keys' do + let!(:uploads) { create_list(:upload, 2, :object_storage, uploader: FileUploader, model: project) } + subject { data_store.keys(relation) } + + it 'returns keys' do + is_expected.to match_array(relation.pluck(:path)) + end + end + + describe '#delete_keys' do + let(:keys) { data_store.keys(relation) } + let!(:uploads) { create_list(:upload, 2, :with_file, :issuable_upload, model: project) } + subject { data_store.delete_keys(keys) } + + before do + uploads.each { |upload| upload.build_uploader.migrate!(2) } + end + + it 'deletes multiple data' do + paths = relation.pluck(:path) + + ::Fog::Storage.new(FileUploader.object_store_credentials).tap do |connection| + paths.each do |path| + expect(connection.get_object('uploads', path)[:body]).not_to be_nil + end + end + + subject + + ::Fog::Storage.new(FileUploader.object_store_credentials).tap do |connection| + paths.each do |path| + expect { connection.get_object('uploads', path)[:body] }.to raise_error(Excon::Error::NotFound) + end + end + end + end + end +end diff --git a/spec/models/uploads/local_spec.rb b/spec/models/uploads/local_spec.rb new file mode 100644 index 00000000000..3468399f370 --- /dev/null +++ b/spec/models/uploads/local_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Uploads::Local do + let(:data_store) { described_class.new } + + before do + stub_uploads_object_storage(FileUploader) + end + + context 'model with uploads' do + let(:project) { create(:project) } + let(:relation) { project.uploads } + + describe '#keys' do + let!(:uploads) { create_list(:upload, 2, uploader: FileUploader, model: project) } + subject { data_store.keys(relation) } + + it 'returns keys' do + is_expected.to match_array(relation.map(&:absolute_path)) + end + end + + describe '#delete_keys' do + let(:keys) { data_store.keys(relation) } + let!(:uploads) { create_list(:upload, 2, :with_file, :issuable_upload, model: project) } + subject { data_store.delete_keys(keys) } + + it 'deletes multiple data' do + paths = relation.map(&:absolute_path) + + paths.each do |path| + expect(File.exist?(path)).to be_truthy + end + + subject + + paths.each do |path| + expect(File.exist?(path)).to be_falsey + end + end + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 6cb27246f06..ff075e65c76 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -3231,7 +3231,7 @@ describe User do end context 'with uploads' do - it_behaves_like 'model with mounted uploader', false do + it_behaves_like 'model with uploads', false do let(:model_object) { create(:user, :with_avatar) } let(:upload_attribute) { :avatar } let(:uploader_class) { AttachmentUploader } diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb index 7b0192fa9c8..456de5f1b9a 100644 --- a/spec/presenters/project_presenter_spec.rb +++ b/spec/presenters/project_presenter_spec.rb @@ -165,32 +165,32 @@ describe ProjectPresenter do describe '#files_anchor_data' do it 'returns files data' do - expect(presenter.files_anchor_data).to have_attributes(enabled: true, - label: 'Files (0 Bytes)', + expect(presenter.files_anchor_data).to have_attributes(is_link: true, + label: a_string_including('0 Bytes'), link: nil) end end describe '#commits_anchor_data' do it 'returns commits data' do - expect(presenter.commits_anchor_data).to have_attributes(enabled: true, - label: 'Commits (0)', + expect(presenter.commits_anchor_data).to have_attributes(is_link: true, + label: a_string_including('0'), link: nil) end end describe '#branches_anchor_data' do it 'returns branches data' do - expect(presenter.branches_anchor_data).to have_attributes(enabled: true, - label: "Branches (0)", + expect(presenter.branches_anchor_data).to have_attributes(is_link: true, + label: a_string_including('0'), link: nil) end end describe '#tags_anchor_data' do it 'returns tags data' do - expect(presenter.tags_anchor_data).to have_attributes(enabled: true, - label: "Tags (0)", + expect(presenter.tags_anchor_data).to have_attributes(is_link: true, + label: a_string_including('0'), link: nil) end end @@ -202,32 +202,32 @@ describe ProjectPresenter do describe '#files_anchor_data' do it 'returns files data' do - expect(presenter.files_anchor_data).to have_attributes(enabled: true, - label: 'Files (0 Bytes)', + expect(presenter.files_anchor_data).to have_attributes(is_link: true, + label: a_string_including('0 Bytes'), link: presenter.project_tree_path(project)) end end describe '#commits_anchor_data' do it 'returns commits data' do - expect(presenter.commits_anchor_data).to have_attributes(enabled: true, - label: 'Commits (0)', + expect(presenter.commits_anchor_data).to have_attributes(is_link: true, + label: a_string_including('0'), link: presenter.project_commits_path(project, project.repository.root_ref)) end end describe '#branches_anchor_data' do it 'returns branches data' do - expect(presenter.branches_anchor_data).to have_attributes(enabled: true, - label: "Branches (#{project.repository.branches.size})", + expect(presenter.branches_anchor_data).to have_attributes(is_link: true, + label: a_string_including("#{project.repository.branches.size}"), link: presenter.project_branches_path(project)) end end describe '#tags_anchor_data' do it 'returns tags data' do - expect(presenter.tags_anchor_data).to have_attributes(enabled: true, - label: "Tags (#{project.repository.tags.size})", + expect(presenter.tags_anchor_data).to have_attributes(is_link: true, + label: a_string_including("#{project.repository.tags.size}"), link: presenter.project_tags_path(project)) end end @@ -236,8 +236,8 @@ describe ProjectPresenter do it 'returns new file data if user can push' do project.add_developer(user) - expect(presenter.new_file_anchor_data).to have_attributes(enabled: false, - label: "New file", + expect(presenter.new_file_anchor_data).to have_attributes(is_link: false, + label: a_string_including("New file"), link: presenter.project_new_blob_path(project, 'master'), class_modifier: 'success') end @@ -264,8 +264,8 @@ describe ProjectPresenter do project.add_developer(user) allow(project.repository).to receive(:readme).and_return(nil) - expect(presenter.readme_anchor_data).to have_attributes(enabled: false, - label: 'Add Readme', + expect(presenter.readme_anchor_data).to have_attributes(is_link: false, + label: a_string_including('Add README'), link: presenter.add_readme_path) end end @@ -274,21 +274,21 @@ describe ProjectPresenter do it 'returns anchor data' do allow(project.repository).to receive(:readme).and_return(double(name: 'readme')) - expect(presenter.readme_anchor_data).to have_attributes(enabled: true, - label: 'Readme', + expect(presenter.readme_anchor_data).to have_attributes(is_link: false, + label: a_string_including('README'), link: presenter.readme_path) end end end describe '#changelog_anchor_data' do - context 'when user can push and CHANGELOG does not exists' do + context 'when user can push and CHANGELOG does not exist' do it 'returns anchor data' do project.add_developer(user) allow(project.repository).to receive(:changelog).and_return(nil) - expect(presenter.changelog_anchor_data).to have_attributes(enabled: false, - label: 'Add Changelog', + expect(presenter.changelog_anchor_data).to have_attributes(is_link: false, + label: a_string_including('Add CHANGELOG'), link: presenter.add_changelog_path) end end @@ -297,21 +297,21 @@ describe ProjectPresenter do it 'returns anchor data' do allow(project.repository).to receive(:changelog).and_return(double(name: 'foo')) - expect(presenter.changelog_anchor_data).to have_attributes(enabled: true, - label: 'Changelog', + expect(presenter.changelog_anchor_data).to have_attributes(is_link: false, + label: a_string_including('CHANGELOG'), link: presenter.changelog_path) end end end describe '#license_anchor_data' do - context 'when user can push and LICENSE does not exists' do + context 'when user can push and LICENSE does not exist' do it 'returns anchor data' do project.add_developer(user) allow(project.repository).to receive(:license_blob).and_return(nil) - expect(presenter.license_anchor_data).to have_attributes(enabled: false, - label: 'Add license', + expect(presenter.license_anchor_data).to have_attributes(is_link: true, + label: a_string_including('Add license'), link: presenter.add_license_path) end end @@ -320,21 +320,21 @@ describe ProjectPresenter do it 'returns anchor data' do allow(project.repository).to receive(:license_blob).and_return(double(name: 'foo')) - expect(presenter.license_anchor_data).to have_attributes(enabled: true, - label: presenter.license_short_name, + expect(presenter.license_anchor_data).to have_attributes(is_link: true, + label: a_string_including(presenter.license_short_name), link: presenter.license_path) end end end describe '#contribution_guide_anchor_data' do - context 'when user can push and CONTRIBUTING does not exists' do + context 'when user can push and CONTRIBUTING does not exist' do it 'returns anchor data' do project.add_developer(user) allow(project.repository).to receive(:contribution_guide).and_return(nil) - expect(presenter.contribution_guide_anchor_data).to have_attributes(enabled: false, - label: 'Add Contribution guide', + expect(presenter.contribution_guide_anchor_data).to have_attributes(is_link: false, + label: a_string_including('Add CONTRIBUTING'), link: presenter.add_contribution_guide_path) end end @@ -343,8 +343,8 @@ describe ProjectPresenter do it 'returns anchor data' do allow(project.repository).to receive(:contribution_guide).and_return(double(name: 'foo')) - expect(presenter.contribution_guide_anchor_data).to have_attributes(enabled: true, - label: 'Contribution guide', + expect(presenter.contribution_guide_anchor_data).to have_attributes(is_link: false, + label: a_string_including('CONTRIBUTING'), link: presenter.contribution_guide_path) end end @@ -355,20 +355,20 @@ describe ProjectPresenter do it 'returns anchor data' do allow(project).to receive(:auto_devops_enabled?).and_return(true) - expect(presenter.autodevops_anchor_data).to have_attributes(enabled: true, - label: 'Auto DevOps enabled', + expect(presenter.autodevops_anchor_data).to have_attributes(is_link: false, + label: a_string_including('Auto DevOps enabled'), link: nil) end end - context 'when user can admin pipeline and CI yml does not exists' do + context 'when user can admin pipeline and CI yml does not exist' do it 'returns anchor data' do project.add_maintainer(user) allow(project).to receive(:auto_devops_enabled?).and_return(false) allow(project.repository).to receive(:gitlab_ci_yml).and_return(nil) - expect(presenter.autodevops_anchor_data).to have_attributes(enabled: false, - label: 'Enable Auto DevOps', + expect(presenter.autodevops_anchor_data).to have_attributes(is_link: false, + label: a_string_including('Enable Auto DevOps'), link: presenter.project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) end end @@ -380,8 +380,8 @@ describe ProjectPresenter do project.add_maintainer(user) cluster = create(:cluster, projects: [project]) - expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: true, - label: 'Kubernetes configured', + expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(is_link: false, + label: a_string_including('Kubernetes configured'), link: presenter.project_cluster_path(project, cluster)) end @@ -390,16 +390,16 @@ describe ProjectPresenter do create(:cluster, :production_environment, projects: [project]) create(:cluster, projects: [project]) - expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: true, - label: 'Kubernetes configured', + expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(is_link: false, + label: a_string_including('Kubernetes configured'), link: presenter.project_clusters_path(project)) end it 'returns link to create a cluster if no cluster exists' do project.add_maintainer(user) - expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: false, - label: 'Add Kubernetes cluster', + expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(is_link: false, + label: a_string_including('Add Kubernetes cluster'), link: presenter.new_project_cluster_path(project)) end end diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index 2c40e266f5f..f7916441313 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -5,7 +5,6 @@ require_relative '../../../config/initializers/sentry' describe API::Helpers do include API::APIGuard::HelperMethods include described_class - include SentryHelper include TermsHelper let(:user) { create(:user) } @@ -224,8 +223,15 @@ describe API::Helpers do describe '.handle_api_exception' do before do - allow_any_instance_of(self.class).to receive(:sentry_enabled?).and_return(true) allow_any_instance_of(self.class).to receive(:rack_response) + allow(Gitlab::Sentry).to receive(:enabled?).and_return(true) + + stub_application_setting( + sentry_enabled: true, + sentry_dsn: "dummy://12345:67890@sentry.localdomain/sentry/42" + ) + configure_sentry + Raven.client.configuration.encoding = 'json' end it 'does not report a MethodNotAllowed exception to Sentry' do @@ -241,10 +247,13 @@ describe API::Helpers do exception = RuntimeError.new('test error') allow(exception).to receive(:backtrace).and_return(caller) - expect_any_instance_of(self.class).to receive(:sentry_context) - expect(Raven).to receive(:capture_exception).with(exception, extra: {}) + expect(Raven).to receive(:capture_exception).with(exception, tags: { + correlation_id: 'new-correlation-id' + }, extra: {}) - handle_api_exception(exception) + Gitlab::CorrelationId.use_id('new-correlation-id') do + handle_api_exception(exception) + end end context 'with a personal access token given' do @@ -255,7 +264,6 @@ describe API::Helpers do # We need to stub at a lower level than #sentry_enabled? otherwise # Sentry is not enabled when the request below is made, and the test # would pass even without the fix - expect(Gitlab::Sentry).to receive(:enabled?).twice.and_return(true) expect(ProjectsFinder).to receive(:new).and_raise('Runtime Error!') get api('/projects', personal_access_token: token) @@ -272,17 +280,7 @@ describe API::Helpers do # Sentry events are an array of the form [auth_header, data, options] let(:event_data) { Raven.client.transport.events.first[1] } - before do - stub_application_setting( - sentry_enabled: true, - sentry_dsn: "dummy://12345:67890@sentry.localdomain/sentry/42" - ) - configure_sentry - Raven.client.configuration.encoding = 'json' - end - it 'sends the params, excluding confidential values' do - expect(Gitlab::Sentry).to receive(:enabled?).twice.and_return(true) expect(ProjectsFinder).to receive(:new).and_raise('Runtime Error!') get api('/projects', user), password: 'dont_send_this', other_param: 'send_this' diff --git a/spec/serializers/diff_file_entity_spec.rb b/spec/serializers/diff_file_entity_spec.rb index 7497b8f27bd..073c13c2cbb 100644 --- a/spec/serializers/diff_file_entity_spec.rb +++ b/spec/serializers/diff_file_entity_spec.rb @@ -13,39 +13,6 @@ describe DiffFileEntity do subject { entity.as_json } - shared_examples 'diff file entity' do - it 'exposes correct attributes' do - expect(subject).to include( - :submodule, :submodule_link, :submodule_tree_url, :file_path, - :deleted_file, :old_path, :new_path, :mode_changed, - :a_mode, :b_mode, :text, :old_path_html, - :new_path_html, :highlighted_diff_lines, :parallel_diff_lines, - :blob, :file_hash, :added_lines, :removed_lines, :diff_refs, :content_sha, - :stored_externally, :external_storage, :too_large, :collapsed, :new_file, - :context_lines_path - ) - end - - it 'includes viewer' do - expect(subject[:viewer].with_indifferent_access) - .to match_schema('entities/diff_viewer') - end - - # Converted diff files from GitHub import does not contain blob file - # and content sha. - context 'when diff file does not have a blob and content sha' do - it 'exposes some attributes as nil' do - allow(diff_file).to receive(:content_sha).and_return(nil) - allow(diff_file).to receive(:blob).and_return(nil) - - expect(subject[:context_lines_path]).to be_nil - expect(subject[:view_path]).to be_nil - expect(subject[:highlighted_diff_lines]).to be_nil - expect(subject[:can_modify_blob]).to be_nil - end - end - end - context 'when there is no merge request' do it_behaves_like 'diff file entity' end diff --git a/spec/serializers/discussion_diff_file_entity_spec.rb b/spec/serializers/discussion_diff_file_entity_spec.rb new file mode 100644 index 00000000000..101ac918a98 --- /dev/null +++ b/spec/serializers/discussion_diff_file_entity_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe DiscussionDiffFileEntity do + include RepoHelpers + + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + let(:commit) { project.commit(sample_commit.id) } + let(:diff_refs) { commit.diff_refs } + let(:diff) { commit.raw_diffs.first } + let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: diff_refs, repository: repository) } + let(:entity) { described_class.new(diff_file, request: {}) } + + subject { entity.as_json } + + context 'when there is no merge request' do + it_behaves_like 'diff file discussion entity' + end + + context 'when there is a merge request' do + let(:user) { create(:user) } + let(:request) { EntityRequest.new(project: project, current_user: user) } + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + let(:entity) { described_class.new(diff_file, request: request, merge_request: merge_request) } + + it_behaves_like 'diff file discussion entity' + + it 'exposes additional attributes' do + expect(subject).to include(:edit_path) + end + + it 'exposes no diff lines' do + expect(subject).not_to include(:highlighted_diff_lines, + :parallel_diff_lines) + end + end +end diff --git a/spec/serializers/discussion_entity_spec.rb b/spec/serializers/discussion_entity_spec.rb index 0590304e832..138749b0fdf 100644 --- a/spec/serializers/discussion_entity_spec.rb +++ b/spec/serializers/discussion_entity_spec.rb @@ -74,13 +74,5 @@ describe DiscussionEntity do :active ) end - - context 'when diff file is a image' do - it 'exposes image attributes' do - allow(discussion).to receive(:on_image?).and_return(true) - - expect(subject.keys).to include(:image_diff_html) - end - end end end diff --git a/spec/serializers/trigger_variable_entity_spec.rb b/spec/serializers/trigger_variable_entity_spec.rb new file mode 100644 index 00000000000..66567c05f52 --- /dev/null +++ b/spec/serializers/trigger_variable_entity_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe TriggerVariableEntity do + let(:project) { create(:project) } + let(:request) { double('request') } + let(:user) { create(:user) } + let(:variable) { { key: 'TEST_KEY', value: 'TEST_VALUE' } } + + subject { described_class.new(variable, request: request).as_json } + + before do + allow(request).to receive(:current_user).and_return(user) + allow(request).to receive(:project).and_return(project) + end + + it 'exposes the variable key' do + expect(subject).to include(:key) + end + + context 'when user has access to the value' do + context 'when user is maintainer' do + before do + project.team.add_maintainer(user) + end + + it 'exposes the variable value' do + expect(subject).to include(:value) + end + end + + context 'when user is owner' do + let(:user) { project.owner } + + it 'exposes the variable value' do + expect(subject).to include(:value) + end + end + end + + context 'when user does not have access to the value' do + before do + project.team.add_developer(user) + end + + it 'does not expose the variable value' do + expect(subject).not_to include(:value) + end + end +end diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index e779675744c..87185891470 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -20,9 +20,9 @@ describe Ci::RetryBuildService do CLONE_ACCESSORS = described_class::CLONE_ACCESSORS REJECT_ACCESSORS = - %i[id status user token coverage trace runner artifacts_expire_at - artifacts_file artifacts_metadata artifacts_size created_at - updated_at started_at finished_at queued_at erased_by + %i[id status user token token_encrypted coverage trace runner + artifacts_expire_at artifacts_file artifacts_metadata artifacts_size + created_at updated_at started_at finished_at queued_at erased_by erased_at auto_canceled_by job_artifacts job_artifacts_archive job_artifacts_metadata job_artifacts_trace job_artifacts_junit job_artifacts_sast job_artifacts_dependency_scanning diff --git a/spec/services/clusters/applications/create_service_spec.rb b/spec/services/clusters/applications/create_service_spec.rb index 0bd7719345e..1a2ca23748a 100644 --- a/spec/services/clusters/applications/create_service_spec.rb +++ b/spec/services/clusters/applications/create_service_spec.rb @@ -31,6 +31,31 @@ describe Clusters::Applications::CreateService do subject end + context 'cert manager application' do + let(:params) do + { + application: 'cert_manager', + email: 'test@example.com' + } + end + + before do + allow_any_instance_of(Clusters::Applications::ScheduleInstallationService).to receive(:execute) + end + + it 'creates the application' do + expect do + subject + + cluster.reload + end.to change(cluster, :application_cert_manager) + end + + it 'sets the email' do + expect(subject.email).to eq('test@example.com') + end + end + context 'jupyter application' do let(:params) do { diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index d29a1091d95..1d9c75dedce 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -621,4 +621,77 @@ describe MergeRequests::RefreshService do @fork_build_failed_todo.reload end end + + describe 'updating merge_commit' do + let(:service) { described_class.new(project, user) } + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + + let(:oldrev) { TestEnv::BRANCH_SHA['merge-commit-analyze-before'] } + let(:newrev) { TestEnv::BRANCH_SHA['merge-commit-analyze-after'] } # Pretend branch is now updated + + let!(:merge_request) do + create( + :merge_request, + source_project: project, + source_branch: 'merge-commit-analyze-after', + target_branch: 'merge-commit-analyze-before', + target_project: project, + merge_user: user + ) + end + + let!(:merge_request_side_branch) do + create( + :merge_request, + source_project: project, + source_branch: 'merge-commit-analyze-side-branch', + target_branch: 'merge-commit-analyze-before', + target_project: project, + merge_user: user + ) + end + + subject { service.execute(oldrev, newrev, 'refs/heads/merge-commit-analyze-before') } + + context 'feature enabled' do + before do + stub_feature_flags(branch_push_merge_commit_analyze: true) + end + + it "updates merge requests' merge_commits" do + expect(Gitlab::BranchPushMergeCommitAnalyzer).to receive(:new).and_wrap_original do |original_method, commits| + expect(commits.map(&:id)).to eq(%w{646ece5cfed840eca0a4feb21bcd6a81bb19bda3 29284d9bcc350bcae005872d0be6edd016e2efb5 5f82584f0a907f3b30cfce5bb8df371454a90051 8a994512e8c8f0dfcf22bb16df6e876be7a61036 689600b91aabec706e657e38ea706ece1ee8268f db46a1c5a5e474aa169b6cdb7a522d891bc4c5f9}) + + original_method.call(commits) + end + + subject + + merge_request.reload + merge_request_side_branch.reload + + expect(merge_request.merge_commit.id).to eq('646ece5cfed840eca0a4feb21bcd6a81bb19bda3') + expect(merge_request_side_branch.merge_commit.id).to eq('29284d9bcc350bcae005872d0be6edd016e2efb5') + end + end + + context 'when feature is disabled' do + before do + stub_feature_flags(branch_push_merge_commit_analyze: false) + end + + it "does not trigger analysis" do + expect(Gitlab::BranchPushMergeCommitAnalyzer).not_to receive(:new) + + subject + + merge_request.reload + merge_request_side_branch.reload + + expect(merge_request.merge_commit).to eq(nil) + expect(merge_request_side_branch.merge_commit).to eq(nil) + end + end + end end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index a3d24ae312a..26e8d829345 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' describe Projects::ForkService do include ProjectForksHelper - let(:gitlab_shell) { Gitlab::Shell.new } + include Gitlab::ShellAdapter + context 'when forking a new project' do describe 'fork by user' do before do @@ -235,6 +236,33 @@ describe Projects::ForkService do end end + context 'when forking with object pools' do + let(:fork_from_project) { create(:project, :public) } + let(:forker) { create(:user) } + + before do + stub_feature_flags(object_pools: true) + end + + context 'when no pool exists' do + it 'creates a new object pool' do + forked_project = fork_project(fork_from_project, forker) + + expect(forked_project.pool_repository).to eq(fork_from_project.pool_repository) + end + end + + context 'when a pool already exists' do + let!(:pool_repository) { create(:pool_repository, source_project: fork_from_project) } + + it 'joins the object pool' do + forked_project = fork_project(fork_from_project, forker) + + expect(forked_project.pool_repository).to eq(fork_from_project.pool_repository) + end + end + end + context 'when linking fork to an existing project' do let(:fork_from_project) { create(:project, :public) } let(:fork_to_project) { create(:project, :public) } diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 1f00cdf7e92..d52c40ff4f1 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -54,6 +54,9 @@ module TestEnv 'add_images_and_changes' => '010d106', 'update-gitlab-shell-v-6-0-1' => '2f61d70', 'update-gitlab-shell-v-6-0-3' => 'de78448', + 'merge-commit-analyze-before' => '1adbdef', + 'merge-commit-analyze-side-branch' => '8a99451', + 'merge-commit-analyze-after' => '646ece5', '2-mb-file' => 'bf12d25', 'before-create-delete-modify-move' => '845009f', 'between-create-delete-modify-move' => '3f5f443', diff --git a/spec/support/shared_examples/models/with_uploads_shared_examples.rb b/spec/support/shared_examples/models/with_uploads_shared_examples.rb index 47ad0c6345d..1d11b855459 100644 --- a/spec/support/shared_examples/models/with_uploads_shared_examples.rb +++ b/spec/support/shared_examples/models/with_uploads_shared_examples.rb @@ -1,6 +1,6 @@ require 'spec_helper' -shared_examples_for 'model with mounted uploader' do |supports_fileuploads| +shared_examples_for 'model with uploads' do |supports_fileuploads| describe '.destroy' do before do stub_uploads_object_storage(uploader_class) @@ -8,16 +8,62 @@ shared_examples_for 'model with mounted uploader' do |supports_fileuploads| model_object.public_send(upload_attribute).migrate!(ObjectStorage::Store::REMOTE) end - it 'deletes remote uploads' do - expect_any_instance_of(CarrierWave::Storage::Fog::File).to receive(:delete).and_call_original + context 'with mounted uploader' do + it 'deletes remote uploads' do + expect_any_instance_of(CarrierWave::Storage::Fog::File).to receive(:delete).and_call_original - expect { model_object.destroy }.to change { Upload.count }.by(-1) + expect { model_object.destroy }.to change { Upload.count }.by(-1) + end end - it 'deletes any FileUploader uploads which are not mounted', skip: !supports_fileuploads do - create(:upload, uploader: FileUploader, model: model_object) + context 'with not mounted uploads', :sidekiq, skip: !supports_fileuploads do + context 'with local files' do + let!(:uploads) { create_list(:upload, 2, uploader: FileUploader, model: model_object) } - expect { model_object.destroy }.to change { Upload.count }.by(-2) + it 'deletes any FileUploader uploads which are not mounted' do + expect { model_object.destroy }.to change { Upload.count }.by(-3) + end + + it 'deletes local files' do + expect_any_instance_of(Uploads::Local).to receive(:delete_keys).with(uploads.map(&:absolute_path)) + + model_object.destroy + end + end + + context 'with remote files' do + let!(:uploads) { create_list(:upload, 2, :object_storage, uploader: FileUploader, model: model_object) } + + it 'deletes any FileUploader uploads which are not mounted' do + expect { model_object.destroy }.to change { Upload.count }.by(-3) + end + + it 'deletes remote files' do + expect_any_instance_of(Uploads::Fog).to receive(:delete_keys).with(uploads.map(&:path)) + + model_object.destroy + end + end + + describe 'destroy strategy depending on feature flag' do + let!(:upload) { create(:upload, uploader: FileUploader, model: model_object) } + + it 'does not destroy uploads by default' do + expect(model_object).to receive(:delete_uploads) + expect(model_object).not_to receive(:destroy_uploads) + + model_object.destroy + end + + it 'uses before destroy callback if feature flag is disabled' do + stub_feature_flags(fast_destroy_uploads: false) + + expect(model_object).to receive(:destroy_uploads) + expect(model_object).not_to receive(:delete_uploads) + + model_object.destroy + end + end end end end diff --git a/spec/support/shared_examples/serializers/diff_file_entity_examples.rb b/spec/support/shared_examples/serializers/diff_file_entity_examples.rb new file mode 100644 index 00000000000..b8065886c42 --- /dev/null +++ b/spec/support/shared_examples/serializers/diff_file_entity_examples.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +shared_examples 'diff file base entity' do + it 'exposes essential attributes' do + expect(subject).to include(:content_sha, :submodule, :submodule_link, + :submodule_tree_url, :old_path_html, + :new_path_html, :blob, :can_modify_blob, + :file_hash, :file_path, :old_path, :new_path, + :collapsed, :text, :diff_refs, :stored_externally, + :external_storage, :renamed_file, :deleted_file, + :mode_changed, :a_mode, :b_mode, :new_file) + end + + # Converted diff files from GitHub import does not contain blob file + # and content sha. + context 'when diff file does not have a blob and content sha' do + it 'exposes some attributes as nil' do + allow(diff_file).to receive(:content_sha).and_return(nil) + allow(diff_file).to receive(:blob).and_return(nil) + + expect(subject[:context_lines_path]).to be_nil + expect(subject[:view_path]).to be_nil + expect(subject[:highlighted_diff_lines]).to be_nil + expect(subject[:can_modify_blob]).to be_nil + end + end +end + +shared_examples 'diff file entity' do + it_behaves_like 'diff file base entity' + + it 'exposes correct attributes' do + expect(subject).to include(:too_large, :added_lines, :removed_lines, + :context_lines_path, :highlighted_diff_lines, + :parallel_diff_lines) + end + + it 'includes viewer' do + expect(subject[:viewer].with_indifferent_access) + .to match_schema('entities/diff_viewer') + end +end + +shared_examples 'diff file discussion entity' do + it_behaves_like 'diff file base entity' +end diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb index fc1fe5739c3..006c93686d5 100644 --- a/spec/views/projects/_home_panel.html.haml_spec.rb +++ b/spec/views/projects/_home_panel.html.haml_spec.rb @@ -23,7 +23,7 @@ describe 'projects/_home_panel' do it 'makes it possible to set notification level' do render - expect(view).to render_template('shared/notifications/_button') + expect(view).to render_template('projects/buttons/_notifications') expect(rendered).to have_selector('.notification-dropdown') end end diff --git a/spec/workers/object_pool/create_worker_spec.rb b/spec/workers/object_pool/create_worker_spec.rb new file mode 100644 index 00000000000..06416489472 --- /dev/null +++ b/spec/workers/object_pool/create_worker_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ObjectPool::CreateWorker do + let(:pool) { create(:pool_repository, :scheduled) } + + subject { described_class.new } + + describe '#perform' do + context 'when the pool creation is successful' do + it 'marks the pool as ready' do + subject.perform(pool.id) + + expect(pool.reload).to be_ready + end + end + + context 'when a the pool already exists' do + before do + pool.create_object_pool + end + + it 'cleans up the pool' do + expect do + subject.perform(pool.id) + end.to raise_error(GRPC::FailedPrecondition) + + expect(pool.reload.failed?).to be(true) + end + end + + context 'when the server raises an unknown error' do + before do + allow_any_instance_of(PoolRepository).to receive(:create_object_pool).and_raise(GRPC::Internal) + end + + it 'marks the pool as failed' do + expect do + subject.perform(pool.id) + end.to raise_error(GRPC::Internal) + + expect(pool.reload.failed?).to be(true) + end + end + + context 'when the pool creation failed before' do + let(:pool) { create(:pool_repository, :failed) } + + it 'deletes the pool first' do + expect_any_instance_of(PoolRepository).to receive(:delete_object_pool) + + subject.perform(pool.id) + + expect(pool.reload).to be_ready + end + end + end +end diff --git a/spec/workers/object_pool/join_worker_spec.rb b/spec/workers/object_pool/join_worker_spec.rb new file mode 100644 index 00000000000..906bc22c8d2 --- /dev/null +++ b/spec/workers/object_pool/join_worker_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ObjectPool::JoinWorker do + let(:pool) { create(:pool_repository, :ready) } + let(:project) { pool.source_project } + let(:repository) { project.repository } + + subject { described_class.new } + + describe '#perform' do + context "when the pool is not joinable" do + let(:pool) { create(:pool_repository, :scheduled) } + + it "doesn't raise an error" do + expect do + subject.perform(pool.id, project.id) + end.not_to raise_error + end + end + + context 'when the pool has been joined before' do + before do + pool.link_repository(repository) + end + + it 'succeeds in joining' do + expect do + subject.perform(pool.id, project.id) + end.not_to raise_error + end + end + end +end diff --git a/spec/workers/prune_web_hook_logs_worker_spec.rb b/spec/workers/prune_web_hook_logs_worker_spec.rb index d7d64a1f641..b3ec71d4a00 100644 --- a/spec/workers/prune_web_hook_logs_worker_spec.rb +++ b/spec/workers/prune_web_hook_logs_worker_spec.rb @@ -5,18 +5,20 @@ describe PruneWebHookLogsWorker do before do hook = create(:project_hook) - 5.times do - create(:web_hook_log, web_hook: hook, created_at: 5.months.ago) - end - + create(:web_hook_log, web_hook: hook, created_at: 5.months.ago) + create(:web_hook_log, web_hook: hook, created_at: 4.months.ago) + create(:web_hook_log, web_hook: hook, created_at: 91.days.ago) + create(:web_hook_log, web_hook: hook, created_at: 89.days.ago) + create(:web_hook_log, web_hook: hook, created_at: 2.months.ago) + create(:web_hook_log, web_hook: hook, created_at: 1.month.ago) create(:web_hook_log, web_hook: hook, response_status: '404') end - it 'removes all web hook logs older than one month' do + it 'removes all web hook logs older than 90 days' do described_class.new.perform - expect(WebHookLog.count).to eq(1) - expect(WebHookLog.first.response_status).to eq('404') + expect(WebHookLog.count).to eq(4) + expect(WebHookLog.last.response_status).to eq('404') end end end diff --git a/spec/workers/remove_old_web_hook_logs_worker_spec.rb b/spec/workers/remove_old_web_hook_logs_worker_spec.rb deleted file mode 100644 index 6d26ba5dfa0..00000000000 --- a/spec/workers/remove_old_web_hook_logs_worker_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'spec_helper' - -describe RemoveOldWebHookLogsWorker do - subject { described_class.new } - - describe '#perform' do - let!(:week_old_record) { create(:web_hook_log, created_at: Time.now - 1.week) } - let!(:three_days_old_record) { create(:web_hook_log, created_at: Time.now - 3.days) } - let!(:one_day_old_record) { create(:web_hook_log, created_at: Time.now - 1.day) } - - it 'removes web hook logs older than 2 days' do - subject.perform - - expect(WebHookLog.all).to include(one_day_old_record) - expect(WebHookLog.all).not_to include(week_old_record, three_days_old_record) - end - end -end |