summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorJacob Schatz <jschatz@gitlab.com>2018-02-23 20:36:28 +0000
committerJacob Schatz <jschatz@gitlab.com>2018-02-23 20:36:28 +0000
commita90a22a7743028a260d7b1f3105b80700a10287e (patch)
treebebc8c433c47000c0eac1426150de70a74206992 /spec
parent11aa990da7794038aef09dd023b85e81b5ac6c4f (diff)
parent296a4e6825a3528917bb385123cdf62ae3d1944e (diff)
downloadgitlab-ce-a90a22a7743028a260d7b1f3105b80700a10287e.tar.gz
Merge branch 'master' into 'boards-bundle-refactor'
# Conflicts: # config/webpack.config.js
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb10
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb1
-rw-r--r--spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb146
-rw-r--r--spec/controllers/projects/clusters/gcp_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests/creations_controller_spec.rb64
-rw-r--r--spec/controllers/projects/pages_domains_controller_spec.rb41
-rw-r--r--spec/controllers/projects/prometheus/metrics_controller_spec.rb (renamed from spec/controllers/projects/prometheus_controller_spec.rb)16
-rw-r--r--spec/controllers/projects/uploads_controller_spec.rb8
-rw-r--r--spec/controllers/search_controller_spec.rb26
-rw-r--r--spec/controllers/users_controller_spec.rb25
-rw-r--r--spec/factories/pages_domains.rb21
-rw-r--r--spec/features/admin/admin_runners_spec.rb4
-rw-r--r--spec/features/auto_deploy_spec.rb77
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb2
-rw-r--r--spec/features/projects/new_project_spec.rb14
-rw-r--r--spec/features/projects/pages_spec.rb2
-rw-r--r--spec/features/projects/show_project_spec.rb317
-rw-r--r--spec/features/projects_spec.rb18
-rw-r--r--spec/features/runners_spec.rb14
-rw-r--r--spec/features/signup_spec.rb103
-rw-r--r--spec/features/tags/master_views_tags_spec.rb2
-rw-r--r--spec/features/users/login_spec.rb (renamed from spec/features/login_spec.rb)20
-rw-r--r--spec/features/users/logout_spec.rb (renamed from spec/features/logout_spec.rb)0
-rw-r--r--spec/features/users/projects_spec.rb29
-rw-r--r--spec/features/users/show_spec.rb17
-rw-r--r--spec/features/users/signup_spec.rb135
-rw-r--r--spec/features/users/user_browses_projects_on_user_page_spec.rb (renamed from spec/features/user_page_spec.rb)24
-rw-r--r--spec/features/users_spec.rb114
-rw-r--r--spec/finders/concerns/finder_methods_spec.rb70
-rw-r--r--spec/finders/concerns/finder_with_cross_project_access_spec.rb118
-rw-r--r--spec/finders/events_finder_spec.rb8
-rw-r--r--spec/finders/milestones_finder_spec.rb8
-rw-r--r--spec/finders/snippets_finder_spec.rb24
-rw-r--r--spec/finders/user_recent_events_finder_spec.rb31
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json5
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json5
-rw-r--r--spec/helpers/dashboard_helper_spec.rb24
-rw-r--r--spec/helpers/explore_helper_spec.rb18
-rw-r--r--spec/helpers/groups_helper_spec.rb35
-rw-r--r--spec/helpers/issues_helper_spec.rb15
-rw-r--r--spec/helpers/nav_helper_spec.rb53
-rw-r--r--spec/helpers/preferences_helper_spec.rb97
-rw-r--r--spec/helpers/projects_helper_spec.rb52
-rw-r--r--spec/helpers/users_helper_spec.rb13
-rw-r--r--spec/javascripts/pipelines/async_button_spec.js11
-rw-r--r--spec/lib/banzai/commit_renderer_spec.rb2
-rw-r--r--spec/lib/banzai/filter/issuable_state_filter_spec.rb8
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb2
-rw-r--r--spec/lib/banzai/redactor_spec.rb51
-rw-r--r--spec/lib/banzai/reference_parser/issue_parser_spec.rb47
-rw-r--r--spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb262
-rw-r--r--spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb407
-rw-r--r--spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb32
-rw-r--r--spec/lib/gitlab/contributions_calendar_spec.rb13
-rw-r--r--spec/lib/gitlab/cross_project_access/check_collection_spec.rb55
-rw-r--r--spec/lib/gitlab/cross_project_access/check_info_spec.rb111
-rw-r--r--spec/lib/gitlab/cross_project_access/class_methods_spec.rb46
-rw-r--r--spec/lib/gitlab/cross_project_access_spec.rb84
-rw-r--r--spec/lib/gitlab/diff/highlight_spec.rb22
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb58
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb14
-rw-r--r--spec/lib/gitlab/gitaly_client/repository_service_spec.rb26
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb91
-rw-r--r--spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb6
-rw-r--r--spec/lib/gitlab/prometheus_client_spec.rb28
-rw-r--r--spec/lib/gitlab/quick_actions/command_definition_spec.rb26
-rw-r--r--spec/lib/gitlab/quick_actions/dsl_spec.rb2
-rw-r--r--spec/lib/gitlab/sql/pattern_spec.rb6
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb3
-rw-r--r--spec/mailers/emails/pages_domains_spec.rb71
-rw-r--r--spec/migrations/enqueue_verify_pages_domain_workers_spec.rb23
-rw-r--r--spec/migrations/track_untracked_uploads_spec.rb2
-rw-r--r--spec/models/ability_spec.rb95
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/concerns/protected_ref_access_spec.rb31
-rw-r--r--spec/models/issue_spec.rb50
-rw-r--r--spec/models/notification_recipient_spec.rb16
-rw-r--r--spec/models/pages_domain_spec.rb144
-rw-r--r--spec/models/project_services/prometheus_service_spec.rb8
-rw-r--r--spec/models/project_spec.rb7
-rw-r--r--spec/policies/issuable_policy_spec.rb8
-rw-r--r--spec/policies/issue_policy_spec.rb88
-rw-r--r--spec/presenters/project_presenter_spec.rb397
-rw-r--r--spec/requests/api/internal_spec.rb2
-rw-r--r--spec/requests/git_http_spec.rb8
-rw-r--r--spec/routing/routing_spec.rb16
-rw-r--r--spec/services/merge_requests/create_from_issue_service_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb72
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb16
-rw-r--r--spec/services/todo_service_spec.rb3
-rw-r--r--spec/services/verify_pages_domain_service_spec.rb270
-rw-r--r--spec/spec_helper.rb24
-rw-r--r--spec/support/migrations_helpers/track_untracked_uploads_helpers.rb128
-rw-r--r--spec/support/prometheus/additional_metrics_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb13
-rw-r--r--spec/support/snippet_visibility.rb18
-rw-r--r--spec/support/track_untracked_uploads_helpers.rb16
-rw-r--r--spec/views/shared/projects/_project.html.haml_spec.rb1
-rw-r--r--spec/workers/pages_domain_verification_cron_worker_spec.rb21
-rw-r--r--spec/workers/pages_domain_verification_worker_spec.rb27
-rw-r--r--spec/workers/stuck_import_jobs_worker_spec.rb58
101 files changed, 3880 insertions, 1014 deletions
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 73fff6eb5ca..b7257fac608 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -109,15 +109,17 @@ describe AutocompleteController do
end
context 'limited users per page' do
- let(:per_page) { 2 }
-
before do
+ 25.times do
+ create(:user)
+ end
+
sign_in(user)
- get(:users, per_page: per_page)
+ get(:users)
end
it { expect(json_response).to be_kind_of(Array) }
- it { expect(json_response.size).to eq(per_page) }
+ it { expect(json_response.size).to eq(20) }
end
context 'unauthenticated user' do
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index 79bbc29e80d..4770e187db6 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -86,6 +86,7 @@ describe Boards::IssuesController do
context 'with unauthorized user' do
before do
+ allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_issue, project).and_return(false)
end
diff --git a/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
new file mode 100644
index 00000000000..27f558e1b5d
--- /dev/null
+++ b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
@@ -0,0 +1,146 @@
+require 'spec_helper'
+
+describe ControllerWithCrossProjectAccessCheck do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in user
+ end
+
+ render_views
+
+ context 'When reading cross project is not allowed' do
+ before do
+ allow(Ability).to receive(:allowed).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :read_cross_project, :global)
+ .and_return(false)
+ end
+
+ describe '#requires_cross_project_access' do
+ controller(ApplicationController) do
+ # `described_class` is not available in this context
+ include ControllerWithCrossProjectAccessCheck # rubocop:disable RSpec/DescribedClass
+
+ requires_cross_project_access :index, show: false,
+ unless: -> { unless_condition },
+ if: -> { if_condition }
+
+ def index
+ render nothing: true
+ end
+
+ def show
+ render nothing: true
+ end
+
+ def unless_condition
+ false
+ end
+
+ def if_condition
+ true
+ end
+ end
+
+ it 'renders a 404 with trying to access a cross project page' do
+ message = "This page is unavailable because you are not allowed to read "\
+ "information across multiple projects."
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(response.body).to match(/#{message}/)
+ end
+
+ it 'is skipped when the `if` condition returns false' do
+ expect(controller).to receive(:if_condition).and_return(false)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'is skipped when the `unless` condition returns true' do
+ expect(controller).to receive(:unless_condition).and_return(true)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'correctly renders an action that does not require cross project access' do
+ get :show, id: 'nothing'
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ describe '#skip_cross_project_access_check' do
+ controller(ApplicationController) do
+ # `described_class` is not available in this context
+ include ControllerWithCrossProjectAccessCheck # rubocop:disable RSpec/DescribedClass
+
+ requires_cross_project_access
+
+ skip_cross_project_access_check index: true, show: false,
+ unless: -> { unless_condition },
+ if: -> { if_condition }
+
+ def index
+ render nothing: true
+ end
+
+ def show
+ render nothing: true
+ end
+
+ def edit
+ render nothing: true
+ end
+
+ def unless_condition
+ false
+ end
+
+ def if_condition
+ true
+ end
+ end
+
+ it 'renders a success when the check is skipped' do
+ get :index
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'is executed when the `if` condition returns false' do
+ expect(controller).to receive(:if_condition).and_return(false)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'is executed when the `unless` condition returns true' do
+ expect(controller).to receive(:unless_condition).and_return(true)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'does not skip the check on an action that is not skipped' do
+ get :show, id: 'hello'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'does not skip the check on an action that was not defined to skip' do
+ get :edit, id: 'hello'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb
index 775f9db1c6e..e14ba29fa70 100644
--- a/spec/controllers/projects/clusters/gcp_controller_spec.rb
+++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb
@@ -161,7 +161,7 @@ describe Projects::Clusters::GcpController do
it 'renders the cluster form with an error' do
go
- expect(response).to set_flash[:alert]
+ expect(response).to set_flash.now[:alert]
expect(response).to render_template('new')
end
end
diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
index 92db7284e0e..24310b847e8 100644
--- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
@@ -17,7 +17,7 @@ describe Projects::MergeRequests::CreationsController do
before do
fork_project.add_master(user)
-
+ Projects::ForkService.new(project, user).execute(fork_project)
sign_in(user)
end
@@ -125,4 +125,66 @@ describe Projects::MergeRequests::CreationsController do
end
end
end
+
+ describe 'GET #branch_to' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ end
+
+ it 'fetches the commit if a user has access' do
+ expect(Ability).to receive(:allowed?).with(user, :read_project, project) { true }
+
+ get :branch_to,
+ namespace_id: fork_project.namespace,
+ project_id: fork_project,
+ target_project_id: project.id,
+ ref: 'master'
+
+ expect(assigns(:commit)).not_to be_nil
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'does not load the commit when the user cannot read the project' do
+ expect(Ability).to receive(:allowed?).with(user, :read_project, project) { false }
+
+ get :branch_to,
+ namespace_id: fork_project.namespace,
+ project_id: fork_project,
+ target_project_id: project.id,
+ ref: 'master'
+
+ expect(assigns(:commit)).to be_nil
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ describe 'GET #update_branches' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ end
+
+ it 'lists the branches of another fork if the user has access' do
+ expect(Ability).to receive(:allowed?).with(user, :read_project, project) { true }
+
+ get :update_branches,
+ namespace_id: fork_project.namespace,
+ project_id: fork_project,
+ target_project_id: project.id
+
+ expect(assigns(:target_branches)).not_to be_empty
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'does not list branches when the user cannot read the project' do
+ expect(Ability).to receive(:allowed?).with(user, :read_project, project) { false }
+
+ get :update_branches,
+ namespace_id: fork_project.namespace,
+ project_id: fork_project,
+ target_project_id: project.id
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(assigns(:target_branches)).to eq([])
+ end
+ end
end
diff --git a/spec/controllers/projects/pages_domains_controller_spec.rb b/spec/controllers/projects/pages_domains_controller_spec.rb
index e9e7d357d9c..2192fd5cae2 100644
--- a/spec/controllers/projects/pages_domains_controller_spec.rb
+++ b/spec/controllers/projects/pages_domains_controller_spec.rb
@@ -46,7 +46,46 @@ describe Projects::PagesDomainsController do
post(:create, request_params.merge(pages_domain: pages_domain_params))
end.to change { PagesDomain.count }.by(1)
- expect(response).to redirect_to(project_pages_path(project))
+ created_domain = PagesDomain.reorder(:id).last
+
+ expect(created_domain).to be_present
+ expect(response).to redirect_to(project_pages_domain_path(project, created_domain))
+ end
+ end
+
+ describe 'POST verify' do
+ let(:params) { request_params.merge(id: pages_domain.domain) }
+
+ def stub_service
+ service = double(:service)
+
+ expect(VerifyPagesDomainService).to receive(:new) { service }
+
+ service
+ end
+
+ it 'handles verification success' do
+ expect(stub_service).to receive(:execute).and_return(status: :success)
+
+ post :verify, params
+
+ expect(response).to redirect_to project_pages_domain_path(project, pages_domain)
+ expect(flash[:notice]).to eq('Successfully verified domain ownership')
+ end
+
+ it 'handles verification failure' do
+ expect(stub_service).to receive(:execute).and_return(status: :failed)
+
+ post :verify, params
+
+ expect(response).to redirect_to project_pages_domain_path(project, pages_domain)
+ expect(flash[:alert]).to eq('Failed to verify domain ownership')
+ end
+
+ it 'returns a 404 response for an unknown domain' do
+ post :verify, request_params.merge(id: 'unknown-domain')
+
+ expect(response).to have_gitlab_http_status(404)
end
end
diff --git a/spec/controllers/projects/prometheus_controller_spec.rb b/spec/controllers/projects/prometheus/metrics_controller_spec.rb
index bbfe78d305a..f17f819feee 100644
--- a/spec/controllers/projects/prometheus_controller_spec.rb
+++ b/spec/controllers/projects/prometheus/metrics_controller_spec.rb
@@ -1,20 +1,20 @@
-require('spec_helper')
+require 'spec_helper'
-describe Projects::PrometheusController do
+describe Projects::Prometheus::MetricsController do
let(:user) { create(:user) }
- let!(:project) { create(:project) }
+ let(:project) { create(:project) }
let(:prometheus_service) { double('prometheus_service') }
before do
allow(controller).to receive(:project).and_return(project)
- allow(project).to receive(:prometheus_service).and_return(prometheus_service)
+ allow(project).to receive(:find_or_initialize_service).with('prometheus').and_return(prometheus_service)
project.add_master(user)
sign_in(user)
end
- describe 'GET #active_metrics' do
+ describe 'GET #active_common' do
context 'when prometheus metrics are enabled' do
context 'when data is not present' do
before do
@@ -22,7 +22,7 @@ describe Projects::PrometheusController do
end
it 'returns no content response' do
- get :active_metrics, project_params(format: :json)
+ get :active_common, project_params(format: :json)
expect(response).to have_gitlab_http_status(204)
end
@@ -36,7 +36,7 @@ describe Projects::PrometheusController do
end
it 'returns no content response' do
- get :active_metrics, project_params(format: :json)
+ get :active_common, project_params(format: :json)
expect(response).to have_gitlab_http_status(200)
expect(json_response).to eq(sample_response.deep_stringify_keys)
@@ -45,7 +45,7 @@ describe Projects::PrometheusController do
context 'when requesting non json response' do
it 'returns not found response' do
- get :active_metrics, project_params
+ get :active_common, project_params
expect(response).to have_gitlab_http_status(404)
end
diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb
index d572085661d..eca9baed9c9 100644
--- a/spec/controllers/projects/uploads_controller_spec.rb
+++ b/spec/controllers/projects/uploads_controller_spec.rb
@@ -7,4 +7,12 @@ describe Projects::UploadsController do
end
it_behaves_like 'handle uploads'
+
+ context 'when the URL the old style, without /-/system' do
+ it 'responds with a redirect to the login page' do
+ get :show, namespace_id: 'project', project_id: 'avatar', filename: 'foo.png', secret: 'bar'
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
end
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 37f961d0c94..30c06ddf744 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -16,6 +16,32 @@ describe SearchController do
expect(assigns[:search_objects].first).to eq note
end
+ context 'when the user cannot read cross project' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :read_cross_project, :global) { false }
+ end
+
+ it 'still allows accessing the search page' do
+ get :show
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'still blocks searches without a project_id' do
+ get :show, search: 'hello'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'allows searches with a project_id' do
+ get :show, search: 'hello', project_id: create(:project, :public).id
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
context 'on restricted projects' do
context 'when signed out' do
before do
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 2898c4b119e..b0acf4a49ac 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -74,6 +74,31 @@ describe UsersController do
end
end
end
+
+ context 'json with events' do
+ let(:project) { create(:project) }
+ before do
+ project.add_developer(user)
+ Gitlab::DataBuilder::Push.build_sample(project, user)
+
+ sign_in(user)
+ end
+
+ it 'loads events' do
+ get :show, username: user, format: :json
+
+ expect(assigns(:events)).not_to be_empty
+ end
+
+ it 'hides events if the user cannot read cross project' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ get :show, username: user, format: :json
+
+ expect(assigns(:events)).to be_empty
+ end
+ end
end
describe 'GET #calendar' do
diff --git a/spec/factories/pages_domains.rb b/spec/factories/pages_domains.rb
index 61b04708da2..35b44e1c52e 100644
--- a/spec/factories/pages_domains.rb
+++ b/spec/factories/pages_domains.rb
@@ -1,6 +1,25 @@
FactoryBot.define do
factory :pages_domain, class: 'PagesDomain' do
- domain 'my.domain.com'
+ sequence(:domain) { |n| "my#{n}.domain.com" }
+ verified_at { Time.now }
+ enabled_until { 1.week.from_now }
+
+ trait :disabled do
+ verified_at nil
+ enabled_until nil
+ end
+
+ trait :unverified do
+ verified_at nil
+ end
+
+ trait :reverify do
+ enabled_until { 1.hour.from_now }
+ end
+
+ trait :expired do
+ enabled_until { 1.hour.ago }
+ end
trait :with_certificate do
certificate '-----BEGIN CERTIFICATE-----
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index a01c129defd..7eeed7da998 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -19,7 +19,7 @@ describe "Admin Runners" do
end
it 'has all necessary texts' do
- expect(page).to have_text "How to setup"
+ expect(page).to have_text "Setup a shared Runner manually"
expect(page).to have_text "Runners with last contact more than a minute ago: 1"
end
@@ -54,7 +54,7 @@ describe "Admin Runners" do
end
it 'has all necessary texts including no runner message' do
- expect(page).to have_text "How to setup"
+ expect(page).to have_text "Setup a shared Runner manually"
expect(page).to have_text "Runners with last contact more than a minute ago: 0"
expect(page).to have_text 'No runners found'
end
diff --git a/spec/features/auto_deploy_spec.rb b/spec/features/auto_deploy_spec.rb
deleted file mode 100644
index 9aef68b7156..00000000000
--- a/spec/features/auto_deploy_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'spec_helper'
-
-describe 'Auto deploy' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
-
- shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
- context 'when no deployment service is active' do
- before do
- trun_off
- end
-
- it 'does not show a button to set up auto deploy' do
- visit project_path(project)
- expect(page).to have_no_content('Set up auto deploy')
- end
- end
-
- context 'when a deployment service is active' do
- before do
- trun_on
- visit project_path(project)
- end
-
- it 'shows a button to set up auto deploy' do
- expect(page).to have_link('Set up auto deploy')
- end
-
- it 'includes OpenShift as an available template', :js do
- click_link 'Set up auto deploy'
- click_button 'Apply a GitLab CI Yaml template'
-
- within '.gitlab-ci-yml-selector' do
- expect(page).to have_content('OpenShift')
- end
- end
-
- it 'creates a merge request using "auto-deploy" branch', :js do
- click_link 'Set up auto deploy'
- click_button 'Apply a GitLab CI Yaml template'
- within '.gitlab-ci-yml-selector' do
- click_on 'OpenShift'
- end
- wait_for_requests
- click_button 'Commit changes'
-
- expect(page).to have_content('New Merge Request From auto-deploy into master')
- end
- end
- end
-
- context 'when user configured kubernetes from Integration > Kubernetes' do
- before do
- create :kubernetes_service, project: project
- project.add_master(user)
- sign_in user
- end
-
- let(:trun_on) { project.deployment_platform.update!(active: true) }
- let(:trun_off) { project.deployment_platform.update!(active: false) }
-
- it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
- end
-
- context 'when user configured kubernetes from CI/CD > Clusters' do
- before do
- create(:cluster, :provided_by_gcp, projects: [project])
- project.add_master(user)
- sign_in user
- end
-
- let(:trun_on) { project.deployment_platform.cluster.update!(enabled: true) }
- let(:trun_off) { project.deployment_platform.cluster.update!(enabled: false) }
-
- it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
- end
-end
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 8ac9821b879..7f1d1934103 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -11,7 +11,7 @@ feature 'project owner sees a link to create a license file in empty project', :
scenario 'project master creates a license file from a template' do
visit project_path(project)
- click_on 'LICENSE'
+ click_on 'Add License'
expect(page).to have_content('New file')
expect(current_path).to eq(
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index 6f097ad16c7..b5104747d00 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -140,7 +140,7 @@ feature 'New project' do
find('#import-project-tab').click
end
- context 'from git repository url' do
+ context 'from git repository url, "Repo by URL"' do
before do
first('.import_git').click
end
@@ -157,6 +157,18 @@ feature 'New project' do
expect(git_import_instructions).to be_visible
expect(git_import_instructions).to have_content 'Git repository URL'
end
+
+ it 'keeps "Import project" tab open after form validation error' do
+ collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
+
+ fill_in 'project_import_url', with: collision_project.http_url_to_repo
+ fill_in 'project_path', with: collision_project.path
+
+ click_on 'Create project'
+
+ expect(page).to have_css('#import-project-pane.active')
+ expect(page).not_to have_css('.toggle-import-form.hide')
+ end
end
context 'from GitHub' do
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 3f1ef0b2a47..a96f2c186a4 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -60,7 +60,6 @@ feature 'Pages' do
fill_in 'Domain', with: 'my.test.domain.com'
click_button 'Create New Domain'
- expect(page).to have_content('Domains (1)')
expect(page).to have_content('my.test.domain.com')
end
end
@@ -159,7 +158,6 @@ feature 'Pages' do
fill_in 'Key (PEM)', with: certificate_key
click_button 'Create New Domain'
- expect(page).to have_content('Domains (1)')
expect(page).to have_content('my.test.domain.com')
end
end
diff --git a/spec/features/projects/show_project_spec.rb b/spec/features/projects/show_project_spec.rb
index 0b94c9eae5d..0a014e9f080 100644
--- a/spec/features/projects/show_project_spec.rb
+++ b/spec/features/projects/show_project_spec.rb
@@ -17,4 +17,321 @@ describe 'Project show page', :feature do
expect(page).to have_content("This project was scheduled for deletion, but failed with the following message: #{project.delete_error}")
end
end
+
+ describe 'stat button existence' do
+ # For "New file", "Add License" functionality,
+ # see spec/features/projects/files/project_owner_creates_license_file_spec.rb
+ # see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+
+ let(:user) { create(:user) }
+
+ describe 'empty project' do
+ let(:project) { create(:project, :public, :empty_repo) }
+ let(:presenter) { project.present(current_user: user) }
+
+ describe 'as a normal user' do
+ before do
+ sign_in(user)
+
+ visit project_path(project)
+ end
+
+ it 'no Auto DevOps button if can not manage pipelines' do
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ end
+ end
+
+ it '"Auto DevOps enabled" button not linked' do
+ project.create_auto_devops!(enabled: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_text('Auto DevOps enabled')
+ end
+ end
+ end
+
+ describe 'as a master' do
+ before do
+ project.add_master(user)
+ sign_in(user)
+
+ visit project_path(project)
+ end
+
+ it '"New file" button linked to new file page' do
+ page.within('.project-stats') 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)
+ end
+ end
+
+ it '"Add License" button linked to new file populated for a license' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Add License', href: presenter.add_license_path)
+ end
+ end
+
+ describe 'Auto DevOps button' do
+ it '"Enable Auto DevOps" button linked to settings page' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ end
+ end
+
+ it '"Auto DevOps enabled" anchor linked to settings page' do
+ project.create_auto_devops!(enabled: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ end
+ end
+ end
+
+ describe 'Kubernetes cluster button' do
+ it '"Add Kubernetes cluster" button linked to clusters page' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project))
+ end
+ end
+
+ it '"Kubernetes cluster" anchor linked to cluster page' do
+ cluster = create(:cluster, :provided_by_gcp, projects: [project])
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster))
+ end
+ end
+ end
+ end
+ end
+
+ describe 'populated project' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:presenter) { project.present(current_user: user) }
+
+ describe 'as a normal user' do
+ before do
+ sign_in(user)
+
+ visit project_path(project)
+ end
+
+ it 'no Auto DevOps button if can not manage pipelines' do
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ end
+ end
+
+ it '"Auto DevOps enabled" button not linked' do
+ project.create_auto_devops!(enabled: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_text('Auto DevOps enabled')
+ end
+ end
+
+ it 'no Kubernetes cluster button if can not manage clusters' do
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Add Kubernetes cluster')
+ expect(page).not_to have_link('Kubernetes configured')
+ end
+ end
+ end
+
+ describe 'as a master' do
+ before do
+ allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(false)
+ project.add_master(user)
+ sign_in(user)
+
+ visit project_path(project)
+ end
+
+ it 'no "Add Changelog" button if the project already has a changelog' do
+ expect(project.repository.changelog).not_to be_nil
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Add Changelog')
+ end
+ end
+
+ it 'no "Add License" button if the project already has a license' do
+ expect(project.repository.license_blob).not_to be_nil
+
+ page.within('.project-stats') 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
+ expect(project.repository.contribution_guide).not_to be_nil
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Add Contribution guide')
+ end
+ end
+
+ describe 'GitLab CI configuration button' do
+ it '"Set up CI/CD" button linked to new file populated for a .gitlab-ci.yml' do
+ expect(project.repository.gitlab_ci_yml).to be_nil
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path)
+ end
+ end
+
+ it 'no "Set up CI/CD" button if the project already has a .gitlab-ci.yml' do
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add .gitlab-ci.yml",
+ file_path: '.gitlab-ci.yml',
+ file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ ).execute
+
+ expect(project.repository.gitlab_ci_yml).not_to be_nil
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Set up CI/CD')
+ end
+ end
+
+ it 'no "Set up CI/CD" button if the project has Auto DevOps enabled' do
+ project.create_auto_devops!(enabled: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Set up CI/CD')
+ end
+ end
+ end
+
+ describe 'Auto DevOps button' do
+ it '"Enable Auto DevOps" button linked to settings page' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ end
+ end
+
+ it '"Enable Auto DevOps" button linked to settings page' do
+ project.create_auto_devops!(enabled: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ end
+ end
+
+ it 'no Auto DevOps button if Auto DevOps callout is shown' do
+ allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(true)
+
+ visit project_path(project)
+
+ expect(page).to have_selector('.js-autodevops-banner')
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ end
+ end
+
+ it 'no "Enable Auto DevOps" button when .gitlab-ci.yml already exists' do
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add .gitlab-ci.yml",
+ file_path: '.gitlab-ci.yml',
+ file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ ).execute
+
+ expect(project.repository.gitlab_ci_yml).not_to be_nil
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ end
+ end
+ end
+
+ describe 'Kubernetes cluster button' do
+ it '"Add Kubernetes cluster" button linked to clusters page' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project))
+ end
+ end
+
+ it '"Kubernetes cluster" button linked to cluster page' do
+ cluster = create(:cluster, :provided_by_gcp, projects: [project])
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster))
+ end
+ end
+ end
+
+ describe '"Set up Koding" button' do
+ it 'no "Set up Koding" button if Koding disabled' do
+ stub_application_setting(koding_enabled?: false)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Set up Koding')
+ end
+ end
+
+ it 'no "Set up Koding" button if the project already has a .koding.yml' do
+ stub_application_setting(koding_enabled?: true)
+ allow(Gitlab::CurrentSettings.current_application_settings).to receive(:koding_url).and_return('http://koding.example.com')
+ expect(project.repository.changelog).not_to be_nil
+ allow_any_instance_of(Repository).to receive(:koding_yml).and_return(project.repository.changelog)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Set up Koding')
+ end
+ end
+
+ it '"Set up Koding" button linked to new file populated for a .koding.yml' do
+ stub_application_setting(koding_enabled?: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Set up Koding', href: presenter.add_koding_stack_path)
+ end
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index b66a7dea598..645d12da09f 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -25,6 +25,24 @@ feature 'Project' do
end
end
+ describe 'shows tip about push to create git command' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in user
+ visit new_project_path
+ end
+
+ it 'shows the command in a popover', :js do
+ page.within '.profile-settings-sidebar' do
+ click_link 'Show command'
+ end
+
+ expect(page).to have_css('.popover .push-to-create-popover #push_to_create_tip')
+ expect(page).to have_content 'Private projects can be created in your personal namespace with:'
+ end
+ end
+
describe 'description' do
let(:project) { create(:project, :repository) }
let(:path) { project_path(project) }
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index aec9de6c7ca..df65c2d2f83 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -7,6 +7,20 @@ feature 'Runners' do
sign_in(user)
end
+ context 'when user opens runners page' do
+ given(:project) { create(:project) }
+
+ background do
+ project.add_master(user)
+ end
+
+ scenario 'user can see a button to install runners on kubernetes clusters' do
+ visit runners_path(project)
+
+ expect(page).to have_link('Install Runner on Kubernetes', href: project_clusters_path(project))
+ end
+ end
+
context 'when a project has enabled shared_runners' do
given(:project) { create(:project) }
diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb
deleted file mode 100644
index 917fad74ef1..00000000000
--- a/spec/features/signup_spec.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-require 'spec_helper'
-
-feature 'Signup' do
- describe 'signup with no errors' do
- context "when sending confirmation email" do
- before do
- stub_application_setting(send_user_confirmation_email: true)
- end
-
- it 'creates the user account and sends a confirmation email' do
- user = build(:user)
-
- visit root_path
-
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: user.email
- fill_in 'new_user_email_confirmation', with: user.email
- fill_in 'new_user_password', with: user.password
- click_button "Register"
-
- expect(current_path).to eq users_almost_there_path
- expect(page).to have_content("Please check your email to confirm your account")
- end
- end
-
- context "when sigining up with different cased emails" do
- it "creates the user successfully" do
- user = build(:user)
-
- visit root_path
-
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: user.email
- fill_in 'new_user_email_confirmation', with: user.email.capitalize
- fill_in 'new_user_password', with: user.password
- click_button "Register"
-
- expect(current_path).to eq dashboard_projects_path
- expect(page).to have_content("Welcome! You have signed up successfully.")
- end
- end
-
- context "when not sending confirmation email" do
- before do
- stub_application_setting(send_user_confirmation_email: false)
- end
-
- it 'creates the user account and goes to dashboard' do
- user = build(:user)
-
- visit root_path
-
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: user.email
- fill_in 'new_user_email_confirmation', with: user.email
- fill_in 'new_user_password', with: user.password
- click_button "Register"
-
- expect(current_path).to eq dashboard_projects_path
- expect(page).to have_content("Welcome! You have signed up successfully.")
- end
- end
- end
-
- describe 'signup with errors' do
- it "displays the errors" do
- existing_user = create(:user)
- user = build(:user)
-
- visit root_path
-
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: existing_user.email
- fill_in 'new_user_password', with: user.password
- click_button "Register"
-
- expect(current_path).to eq user_registration_path
- expect(page).to have_content("errors prohibited this user from being saved")
- expect(page).to have_content("Email has already been taken")
- expect(page).to have_content("Email confirmation doesn't match")
- end
-
- it 'does not redisplay the password' do
- existing_user = create(:user)
- user = build(:user)
-
- visit root_path
-
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: existing_user.email
- fill_in 'new_user_password', with: user.password
- click_button "Register"
-
- expect(current_path).to eq user_registration_path
- expect(page.body).not_to match(/#{user.password}/)
- end
- end
-end
diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb
index 4662367d843..b625e7065cc 100644
--- a/spec/features/tags/master_views_tags_spec.rb
+++ b/spec/features/tags/master_views_tags_spec.rb
@@ -13,7 +13,7 @@ feature 'Master views tags' do
before do
visit project_path(project)
- click_on '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/features/login_spec.rb b/spec/features/users/login_spec.rb
index 6dfabcc7225..6ef235cf870 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -1,6 +1,26 @@
require 'spec_helper'
feature 'Login' do
+ scenario 'Successful user signin invalidates password reset token' do
+ user = create(:user)
+
+ expect(user.reset_password_token).to be_nil
+
+ visit new_user_password_path
+ fill_in 'user_email', with: user.email
+ click_button 'Reset password'
+
+ user.reload
+ expect(user.reset_password_token).not_to be_nil
+
+ find('a[href="#login-pane"]').click
+ gitlab_sign_in(user)
+ expect(current_path).to eq root_path
+
+ user.reload
+ expect(user.reset_password_token).to be_nil
+ end
+
describe 'initial login after setup' do
it 'allows the initial admin to create a password' do
# This behavior is dependent on there only being one user
diff --git a/spec/features/logout_spec.rb b/spec/features/users/logout_spec.rb
index 635729efa53..635729efa53 100644
--- a/spec/features/logout_spec.rb
+++ b/spec/features/users/logout_spec.rb
diff --git a/spec/features/users/projects_spec.rb b/spec/features/users/projects_spec.rb
deleted file mode 100644
index f079771cee1..00000000000
--- a/spec/features/users/projects_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'spec_helper'
-
-describe 'Projects tab on a user profile', :js do
- let(:user) { create(:user) }
- let!(:project) { create(:project, namespace: user.namespace) }
- let!(:project2) { create(:project, namespace: user.namespace) }
-
- before do
- allow(Project).to receive(:default_per_page).and_return(1)
-
- sign_in(user)
-
- visit user_path(user)
-
- page.within('.user-profile-nav') do
- click_link('Personal projects')
- end
-
- wait_for_requests
- end
-
- it 'paginates results' do
- expect(page).to have_content(project2.name)
-
- click_link('Next')
-
- expect(page).to have_content(project.name)
- end
-end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
new file mode 100644
index 00000000000..b5bbb2c0ea5
--- /dev/null
+++ b/spec/features/users/show_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe 'User page' do
+ let(:user) { create(:user) }
+
+ it 'shows all the tabs' do
+ visit(user_path(user))
+
+ page.within '.nav-links' do
+ expect(page).to have_link('Activity')
+ expect(page).to have_link('Groups')
+ expect(page).to have_link('Contributed projects')
+ expect(page).to have_link('Personal projects')
+ expect(page).to have_link('Snippets')
+ end
+ end
+end
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
new file mode 100644
index 00000000000..5d539f0ccbe
--- /dev/null
+++ b/spec/features/users/signup_spec.rb
@@ -0,0 +1,135 @@
+require 'spec_helper'
+
+describe 'Signup' do
+ let(:new_user) { build_stubbed(:user) }
+
+ describe 'username validation', :js do
+ before do
+ visit root_path
+ click_link 'Register'
+ end
+
+ it 'does not show an error border if the username is available' do
+ fill_in 'new_user_username', with: 'new-user'
+ wait_for_requests
+
+ expect(find('.username')).not_to have_css '.gl-field-error-outline'
+ end
+
+ it 'does not show an error border if the username contains dots (.)' do
+ fill_in 'new_user_username', with: 'new.user.username'
+ wait_for_requests
+
+ expect(find('.username')).not_to have_css '.gl-field-error-outline'
+ end
+
+ it 'shows an error border if the username already exists' do
+ existing_user = create(:user)
+
+ fill_in 'new_user_username', with: existing_user.username
+ wait_for_requests
+
+ expect(find('.username')).to have_css '.gl-field-error-outline'
+ end
+
+ it 'shows an error border if the username contains special characters' do
+ fill_in 'new_user_username', with: 'new$user!username'
+ wait_for_requests
+
+ expect(find('.username')).to have_css '.gl-field-error-outline'
+ end
+ end
+
+ context 'with no errors' do
+ context "when sending confirmation email" do
+ before do
+ stub_application_setting(send_user_confirmation_email: true)
+ end
+
+ it 'creates the user account and sends a confirmation email' do
+ visit root_path
+
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+ fill_in 'new_user_email_confirmation', with: new_user.email
+ fill_in 'new_user_password', with: new_user.password
+
+ expect { click_button 'Register' }.to change { User.count }.by(1)
+
+ expect(current_path).to eq users_almost_there_path
+ expect(page).to have_content("Please check your email to confirm your account")
+ end
+ end
+
+ context "when sigining up with different cased emails" do
+ it "creates the user successfully" do
+ visit root_path
+
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+ fill_in 'new_user_email_confirmation', with: new_user.email.capitalize
+ fill_in 'new_user_password', with: new_user.password
+ click_button "Register"
+
+ expect(current_path).to eq dashboard_projects_path
+ expect(page).to have_content("Welcome! You have signed up successfully.")
+ end
+ end
+
+ context "when not sending confirmation email" do
+ before do
+ stub_application_setting(send_user_confirmation_email: false)
+ end
+
+ it 'creates the user account and goes to dashboard' do
+ visit root_path
+
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+ fill_in 'new_user_email_confirmation', with: new_user.email
+ fill_in 'new_user_password', with: new_user.password
+ click_button "Register"
+
+ expect(current_path).to eq dashboard_projects_path
+ expect(page).to have_content("Welcome! You have signed up successfully.")
+ end
+ end
+ end
+
+ context 'with errors' do
+ it "displays the errors" do
+ existing_user = create(:user)
+
+ visit root_path
+
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: existing_user.email
+ fill_in 'new_user_password', with: new_user.password
+ click_button "Register"
+
+ expect(current_path).to eq user_registration_path
+ expect(page).to have_content("errors prohibited this user from being saved")
+ expect(page).to have_content("Email has already been taken")
+ expect(page).to have_content("Email confirmation doesn't match")
+ end
+
+ it 'does not redisplay the password' do
+ existing_user = create(:user)
+
+ visit root_path
+
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: existing_user.email
+ fill_in 'new_user_password', with: new_user.password
+ click_button "Register"
+
+ expect(current_path).to eq user_registration_path
+ expect(page.body).not_to match(/#{new_user.password}/)
+ end
+ end
+end
diff --git a/spec/features/user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index 19c587e53c8..a70637c8370 100644
--- a/spec/features/user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'User page', :js do
+describe 'Users > User browses projects on user page', :js do
let!(:user) { create :user }
let!(:private_project) do
create :project, :private, name: 'private', namespace: user.namespace do |project|
@@ -26,6 +26,28 @@ describe 'User page', :js do
end
end
+ it 'paginates projects', :js do
+ project = create(:project, namespace: user.namespace)
+ project2 = create(:project, namespace: user.namespace)
+ allow(Project).to receive(:default_per_page).and_return(1)
+
+ sign_in(user)
+
+ visit user_path(user)
+
+ page.within('.user-profile-nav') do
+ click_link('Personal projects')
+ end
+
+ wait_for_requests
+
+ expect(page).to have_content(project2.name)
+
+ click_link('Next')
+
+ expect(page).to have_content(project.name)
+ end
+
context 'when not signed in' do
it 'renders user public project' do
visit user_path(user)
diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb
deleted file mode 100644
index a9973cdf214..00000000000
--- a/spec/features/users_spec.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-require 'spec_helper'
-
-feature 'Users', :js do
- let(:user) { create(:user, username: 'user1', name: 'User 1', email: 'user1@gitlab.com') }
-
- scenario 'GET /users/sign_in creates a new user account' do
- visit new_user_session_path
- click_link 'Register'
- fill_in 'new_user_name', with: 'Name Surname'
- fill_in 'new_user_username', with: 'Great'
- fill_in 'new_user_email', with: 'name@mail.com'
- fill_in 'new_user_email_confirmation', with: 'name@mail.com'
- fill_in 'new_user_password', with: 'password1234'
- expect { click_button 'Register' }.to change { User.count }.by(1)
- end
-
- scenario 'Successful user signin invalidates password reset token' do
- expect(user.reset_password_token).to be_nil
-
- visit new_user_password_path
- fill_in 'user_email', with: user.email
- click_button 'Reset password'
-
- user.reload
- expect(user.reset_password_token).not_to be_nil
-
- find('a[href="#login-pane"]').click
- gitlab_sign_in(user)
- expect(current_path).to eq root_path
-
- user.reload
- expect(user.reset_password_token).to be_nil
- end
-
- scenario 'Should show one error if email is already taken' do
- visit new_user_session_path
- click_link 'Register'
- fill_in 'new_user_name', with: 'Another user name'
- fill_in 'new_user_username', with: 'anotheruser'
- fill_in 'new_user_email', with: user.email
- fill_in 'new_user_email_confirmation', with: user.email
- fill_in 'new_user_password', with: '12341234'
- expect { click_button 'Register' }.to change { User.count }.by(0)
- expect(page).to have_text('Email has already been taken')
- expect(number_of_errors_on_page(page)).to be(1), 'errors on page:\n #{errors_on_page page}'
- end
-
- describe 'redirect alias routes' do
- before do
- expect(user).to be_persisted
- end
-
- scenario '/u/user1 redirects to user page' do
- visit '/u/user1'
-
- expect(current_path).to eq user_path(user)
- expect(page).to have_text(user.name)
- end
-
- scenario '/u/user1/groups redirects to user groups page' do
- visit '/u/user1/groups'
-
- expect(current_path).to eq user_groups_path(user)
- end
-
- scenario '/u/user1/projects redirects to user projects page' do
- visit '/u/user1/projects'
-
- expect(current_path).to eq user_projects_path(user)
- end
- end
-
- feature 'username validation' do
- let(:loading_icon) { '.fa.fa-spinner' }
- let(:username_input) { 'new_user_username' }
-
- before do
- visit new_user_session_path
- click_link 'Register'
- end
-
- scenario 'doesn\'t show an error border if the username is available' do
- fill_in username_input, with: 'new-user'
- wait_for_requests
- expect(find('.username')).not_to have_css '.gl-field-error-outline'
- end
-
- scenario 'does not show an error border if the username contains dots (.)' do
- fill_in username_input, with: 'new.user.username'
- wait_for_requests
- expect(find('.username')).not_to have_css '.gl-field-error-outline'
- end
-
- scenario 'shows an error border if the username already exists' do
- fill_in username_input, with: user.username
- wait_for_requests
- expect(find('.username')).to have_css '.gl-field-error-outline'
- end
-
- scenario 'shows an error border if the username contains special characters' do
- fill_in username_input, with: 'new$user!username'
- wait_for_requests
- expect(find('.username')).to have_css '.gl-field-error-outline'
- end
- end
-
- def errors_on_page(page)
- page.find('#error_explanation').find('ul').all('li').map { |item| item.text }.join("\n")
- end
-
- def number_of_errors_on_page(page)
- page.find('#error_explanation').find('ul').all('li').count
- end
-end
diff --git a/spec/finders/concerns/finder_methods_spec.rb b/spec/finders/concerns/finder_methods_spec.rb
new file mode 100644
index 00000000000..a4ad331f613
--- /dev/null
+++ b/spec/finders/concerns/finder_methods_spec.rb
@@ -0,0 +1,70 @@
+require 'spec_helper'
+
+describe FinderMethods do
+ let(:finder_class) do
+ Class.new do
+ include FinderMethods
+
+ attr_reader :current_user
+
+ def initialize(user)
+ @current_user = user
+ end
+
+ def execute
+ Project.all
+ end
+ end
+ end
+
+ let(:user) { create(:user) }
+ let(:finder) { finder_class.new(user) }
+ let(:authorized_project) { create(:project) }
+ let(:unauthorized_project) { create(:project) }
+
+ before do
+ authorized_project.add_developer(user)
+ end
+
+ describe '#find_by!' do
+ it 'returns the project if the user has access' do
+ expect(finder.find_by!(id: authorized_project.id)).to eq(authorized_project)
+ end
+
+ it 'raises not found when the project is not found' do
+ expect { finder.find_by!(id: 0) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'raises not found the user does not have access' do
+ expect { finder.find_by!(id: unauthorized_project.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ describe '#find' do
+ it 'returns the project if the user has access' do
+ expect(finder.find(authorized_project.id)).to eq(authorized_project)
+ end
+
+ it 'raises not found when the project is not found' do
+ expect { finder.find(0) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'raises not found the user does not have access' do
+ expect { finder.find(unauthorized_project.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ describe '#find_by' do
+ it 'returns the project if the user has access' do
+ expect(finder.find_by(id: authorized_project.id)).to eq(authorized_project)
+ end
+
+ it 'returns nil when the project is not found' do
+ expect(finder.find_by(id: 0)).to be_nil
+ end
+
+ it 'returns nil when the user does not have access' do
+ expect(finder.find_by(id: unauthorized_project.id)).to be_nil
+ end
+ end
+end
diff --git a/spec/finders/concerns/finder_with_cross_project_access_spec.rb b/spec/finders/concerns/finder_with_cross_project_access_spec.rb
new file mode 100644
index 00000000000..c784fb87972
--- /dev/null
+++ b/spec/finders/concerns/finder_with_cross_project_access_spec.rb
@@ -0,0 +1,118 @@
+require 'spec_helper'
+
+describe FinderWithCrossProjectAccess do
+ let(:finder_class) do
+ Class.new do
+ prepend FinderWithCrossProjectAccess
+ include FinderMethods
+
+ requires_cross_project_access if: -> { requires_access? }
+
+ attr_reader :current_user
+
+ def initialize(user)
+ @current_user = user
+ end
+
+ def execute
+ Issue.all
+ end
+ end
+ end
+
+ let(:user) { create(:user) }
+ subject(:finder) { finder_class.new(user) }
+ let!(:result) { create(:issue) }
+
+ before do
+ result.project.add_master(user)
+ end
+
+ def expect_access_check_on_result
+ expect(finder).not_to receive(:requires_access?)
+ expect(Ability).to receive(:allowed?).with(user, :read_issue, result).and_call_original
+ end
+
+ context 'when the user cannot read cross project' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project)
+ .and_return(false)
+ end
+
+ describe '#execute' do
+ it 'returns a issue if the check is disabled' do
+ expect(finder).to receive(:requires_access?).and_return(false)
+
+ expect(finder.execute).to include(result)
+ end
+
+ it 'returns an empty relation when the check is enabled' do
+ expect(finder).to receive(:requires_access?).and_return(true)
+
+ expect(finder.execute).to be_empty
+ end
+
+ it 'only queries once when check is enabled' do
+ expect(finder).to receive(:requires_access?).and_return(true)
+
+ expect { finder.execute }.not_to exceed_query_limit(1)
+ end
+
+ it 'only queries once when check is disabled' do
+ expect(finder).to receive(:requires_access?).and_return(false)
+
+ expect { finder.execute }.not_to exceed_query_limit(1)
+ end
+ end
+
+ describe '#find' do
+ it 'checks the accessibility of the subject directly' do
+ expect_access_check_on_result
+
+ finder.find(result.id)
+ end
+
+ it 'returns the issue' do
+ expect(finder.find(result.id)).to eq(result)
+ end
+ end
+
+ describe '#find_by' do
+ it 'checks the accessibility of the subject directly' do
+ expect_access_check_on_result
+
+ finder.find_by(id: result.id)
+ end
+ end
+
+ describe '#find_by!' do
+ it 'checks the accessibility of the subject directly' do
+ expect_access_check_on_result
+
+ finder.find_by!(id: result.id)
+ end
+
+ it 're-enables the check after the find failed' do
+ finder.find_by!(id: 9999) rescue ActiveRecord::RecordNotFound
+
+ expect(finder.instance_variable_get(:@should_skip_cross_project_check))
+ .to eq(false)
+ end
+ end
+ end
+
+ context 'when the user can read cross project' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project)
+ .and_return(true)
+ end
+
+ it 'returns the result' do
+ expect(finder).not_to receive(:requires_access?)
+
+ expect(finder.execute).to include(result)
+ end
+ end
+end
diff --git a/spec/finders/events_finder_spec.rb b/spec/finders/events_finder_spec.rb
index 18d6c0cfd74..62968e83292 100644
--- a/spec/finders/events_finder_spec.rb
+++ b/spec/finders/events_finder_spec.rb
@@ -26,6 +26,14 @@ describe EventsFinder do
expect(events).not_to include(opened_merge_request_event)
end
+
+ it 'returns nothing when the current user cannot read cross project' do
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ events = described_class.new(source: user, current_user: user).execute
+
+ expect(events).to be_empty
+ end
end
context 'when targeting a project' do
diff --git a/spec/finders/milestones_finder_spec.rb b/spec/finders/milestones_finder_spec.rb
index 0b3cf7ece5f..656d120311a 100644
--- a/spec/finders/milestones_finder_spec.rb
+++ b/spec/finders/milestones_finder_spec.rb
@@ -70,4 +70,12 @@ describe MilestonesFinder do
expect(result.to_a).to contain_exactly(milestone_1)
end
end
+
+ describe '#find_by' do
+ it 'finds a single milestone' do
+ finder = described_class.new(project_ids: [project_1.id], state: 'all')
+
+ expect(finder.find_by(iid: milestone_3.iid)).to eq(milestone_3)
+ end
+ end
end
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index 54a07eccaba..1ae0bd988f2 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -162,8 +162,26 @@ describe SnippetsFinder do
end
end
- describe "#execute" do
- # Snippet visibility scenarios are included in more details in spec/support/snippet_visibility.rb
- include_examples 'snippet visibility', described_class
+ describe '#execute' do
+ let(:project) { create(:project, :public) }
+ let!(:project_snippet) { create(:project_snippet, :public, project: project) }
+ let!(:personal_snippet) { create(:personal_snippet, :public) }
+ let(:user) { create(:user) }
+ subject(:finder) { described_class.new(user) }
+
+ it 'returns project- and personal snippets' do
+ expect(finder.execute).to contain_exactly(project_snippet, personal_snippet)
+ end
+
+ context 'when the user cannot read cross project' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ end
+
+ it 'returns only personal snippets when the user cannot read cross project' do
+ expect(finder.execute).to contain_exactly(personal_snippet)
+ end
+ end
end
end
diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb
new file mode 100644
index 00000000000..3ca0f7c3c89
--- /dev/null
+++ b/spec/finders/user_recent_events_finder_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe UserRecentEventsFinder do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:project_owner) { project.creator }
+ let!(:event) { create(:event, project: project, author: project_owner) }
+
+ subject(:finder) { described_class.new(user, project_owner) }
+
+ describe '#execute' do
+ it 'does not include the event when a user does not have access to the project' do
+ expect(finder.execute).to be_empty
+ end
+
+ context 'when the user has access to a project' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'includes the event' do
+ expect(finder.execute).to include(event)
+ end
+
+ it 'does not include the event if the user cannot read cross project' do
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ expect(finder.execute).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
index e8c17298b43..ed8ed9085c0 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
@@ -4,6 +4,9 @@
"domain": { "type": "string" },
"url": { "type": "uri" },
"project_id": { "type": "integer" },
+ "verified": { "type": "boolean" },
+ "verification_code": { "type": ["string", "null"] },
+ "enabled_until": { "type": ["date", "null"] },
"certificate_expiration": {
"type": "object",
"properties": {
@@ -14,6 +17,6 @@
"additionalProperties": false
}
},
- "required": ["domain", "url", "project_id"],
+ "required": ["domain", "url", "project_id", "verified", "verification_code", "enabled_until"],
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
index 08db8d47050..b57d544f896 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
@@ -3,6 +3,9 @@
"properties": {
"domain": { "type": "string" },
"url": { "type": "uri" },
+ "verified": { "type": "boolean" },
+ "verification_code": { "type": ["string", "null"] },
+ "enabled_until": { "type": ["date", "null"] },
"certificate": {
"type": "object",
"properties": {
@@ -15,6 +18,6 @@
"additionalProperties": false
}
},
- "required": ["domain", "url"],
+ "required": ["domain", "url", "verified", "verification_code", "enabled_until"],
"additionalProperties": false
}
diff --git a/spec/helpers/dashboard_helper_spec.rb b/spec/helpers/dashboard_helper_spec.rb
new file mode 100644
index 00000000000..7ba24ba2956
--- /dev/null
+++ b/spec/helpers/dashboard_helper_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe DashboardHelper do
+ let(:user) { build(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?) { true }
+ end
+
+ describe '#dashboard_nav_links' do
+ it 'has all the expected links by default' do
+ menu_items = [:projects, :groups, :activity, :milestones, :snippets]
+
+ expect(helper.dashboard_nav_links).to contain_exactly(*menu_items)
+ end
+
+ it 'does not contain cross project elements when the user cannot read cross project' do
+ expect(helper).to receive(:can?).with(user, :read_cross_project) { false }
+
+ expect(helper.dashboard_nav_links).not_to include(:activity, :milestones)
+ end
+ end
+end
diff --git a/spec/helpers/explore_helper_spec.rb b/spec/helpers/explore_helper_spec.rb
new file mode 100644
index 00000000000..12651d80e36
--- /dev/null
+++ b/spec/helpers/explore_helper_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe ExploreHelper do
+ let(:user) { build(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?) { true }
+ end
+
+ describe '#explore_nav_links' do
+ it 'has all the expected links by default' do
+ menu_items = [:projects, :groups, :snippets]
+
+ expect(helper.explore_nav_links).to contain_exactly(*menu_items)
+ end
+ end
+end
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 5f608fe18d9..b48c252acd3 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -201,4 +201,39 @@ describe GroupsHelper do
end
end
end
+
+ describe '#group_sidebar_links' do
+ let(:group) { create(:group, :public) }
+ let(:user) { create(:user) }
+ before do
+ allow(helper).to receive(:current_user) { user }
+ allow(helper).to receive(:can?) { true }
+ helper.instance_variable_set(:@group, group)
+ end
+
+ it 'returns all the expected links' do
+ links = [
+ :overview, :activity, :issues, :labels, :milestones, :merge_requests,
+ :group_members, :settings
+ ]
+
+ expect(helper.group_sidebar_links).to include(*links)
+ end
+
+ it 'includes settings when the user can admin the group' do
+ expect(helper).to receive(:current_user) { user }
+ expect(helper).to receive(:can?).with(user, :admin_group, group) { false }
+
+ expect(helper.group_sidebar_links).not_to include(:settings)
+ end
+
+ it 'excludes cross project features when the user cannot read cross project' do
+ cross_project_features = [:activity, :issues, :labels, :milestones,
+ :merge_requests]
+
+ expect(helper).to receive(:can?).with(user, :read_cross_project) { false }
+
+ expect(helper.group_sidebar_links).not_to include(*cross_project_features)
+ end
+ end
end
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index ddf881a7b6f..aeef5352333 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -113,21 +113,6 @@ describe IssuesHelper do
end
end
- describe "milestone_options" do
- it "gets closed milestone from current issue" do
- closed_milestone = create(:closed_milestone, project: project)
- milestone1 = create(:milestone, project: project)
- milestone2 = create(:milestone, project: project)
- issue.update_attributes(milestone_id: closed_milestone.id)
-
- options = milestone_options(issue)
-
- expect(options).to have_selector('option[selected]', text: closed_milestone.title)
- expect(options).to have_selector('option', text: milestone1.title)
- expect(options).to have_selector('option', text: milestone2.title)
- end
- end
-
describe "#link_to_discussions_to_resolve" do
describe "passing only a merge request" do
let(:merge_request) { create(:merge_request) }
diff --git a/spec/helpers/nav_helper_spec.rb b/spec/helpers/nav_helper_spec.rb
new file mode 100644
index 00000000000..e840c927d59
--- /dev/null
+++ b/spec/helpers/nav_helper_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+describe NavHelper do
+ describe '#header_links' do
+ before do
+ allow(helper).to receive(:session) { {} }
+ end
+
+ context 'when the user is logged in' do
+ let(:user) { build(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?) { true }
+ end
+
+ it 'has all the expected links by default' do
+ menu_items = [:user_dropdown, :search, :issues, :merge_requests, :todos]
+
+ expect(helper.header_links).to contain_exactly(*menu_items)
+ end
+
+ it 'contains the impersonation link while impersonating' do
+ expect(helper).to receive(:session) { { impersonator_id: 1 } }
+
+ expect(helper.header_links).to include(:admin_impersonation)
+ end
+
+ context 'when the user cannot read cross project' do
+ before do
+ allow(helper).to receive(:can?).with(user, :read_cross_project) { false }
+ end
+
+ it 'does not contain cross project elements when the user cannot read cross project' do
+ expect(helper.header_links).not_to include(:issues, :merge_requests, :todos, :search)
+ end
+
+ it 'shows the search box when the user cannot read cross project and he is visiting a project' do
+ helper.instance_variable_set(:@project, create(:project))
+
+ expect(helper.header_links).to include(:search)
+ end
+ end
+ end
+
+ it 'returns only the sign in and search when the user is not logged in' do
+ allow(helper).to receive(:current_user).and_return(nil)
+ allow(helper).to receive(:can?).with(nil, :read_cross_project) { true }
+
+ expect(helper.header_links).to contain_exactly(:sign_in, :search)
+ end
+ end
+end
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 749aa25e632..e2a0c4322ff 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -77,103 +77,6 @@ describe PreferencesHelper do
end
end
- describe '#default_project_view' do
- context 'user not signed in' do
- before do
- helper.instance_variable_set(:@project, project)
- stub_user
- end
-
- context 'when repository is empty' do
- let(:project) { create(:project_empty_repo, :public) }
-
- it 'returns activity if user has repository access' do
- allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(true)
-
- expect(helper.default_project_view).to eq('activity')
- end
-
- it 'returns activity if user does not have repository access' do
- allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(false)
-
- expect(helper.default_project_view).to eq('activity')
- end
- end
-
- context 'when repository is not empty' do
- let(:project) { create(:project, :public, :repository) }
-
- it 'returns files and readme if user has repository access' do
- allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(true)
-
- expect(helper.default_project_view).to eq('files')
- end
-
- it 'returns activity if user does not have repository access' do
- allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(false)
-
- expect(helper.default_project_view).to eq('activity')
- end
- end
- end
-
- context 'user signed in' do
- let(:user) { create(:user, :readme) }
- let(:project) { create(:project, :public, :repository) }
-
- before do
- helper.instance_variable_set(:@project, project)
- allow(helper).to receive(:current_user).and_return(user)
- end
-
- context 'when the user is allowed to see the code' do
- it 'returns the project view' do
- allow(helper).to receive(:can?).with(user, :download_code, project).and_return(true)
-
- expect(helper.default_project_view).to eq('readme')
- end
- end
-
- context 'with wikis enabled and the right policy for the user' do
- before do
- project.project_feature.update_attribute(:issues_access_level, 0)
- allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
- end
-
- it 'returns wiki if the user has the right policy' do
- allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(true)
-
- expect(helper.default_project_view).to eq('wiki')
- end
-
- it 'returns customize_workflow if the user does not have the right policy' do
- allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
-
- expect(helper.default_project_view).to eq('customize_workflow')
- end
- end
-
- context 'with issues as a feature available' do
- it 'return issues' do
- allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
- allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
-
- expect(helper.default_project_view).to eq('projects/issues/issues')
- end
- end
-
- context 'with no activity, no wikies and no issues' do
- it 'returns customize_workflow as default' do
- project.project_feature.update_attribute(:issues_access_level, 0)
- allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
- allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
-
- expect(helper.default_project_view).to eq('customize_workflow')
- end
- end
- end
- end
-
def stub_user(messages = {})
if messages.empty?
allow(helper).to receive(:current_user).and_return(nil)
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index b67fee2fcc0..ce96e90e2d7 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -75,6 +75,12 @@ describe ProjectsHelper do
describe "#project_list_cache_key", :clean_gitlab_redis_shared_state do
let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?).with(user, :read_cross_project) { true }
+ end
it "includes the route" do
expect(helper.project_list_cache_key(project)).to include(project.route.cache_key)
@@ -106,6 +112,10 @@ describe ProjectsHelper do
expect(helper.project_list_cache_key(project).last).to start_with('v')
end
+ it 'includes wether or not the user can read cross project' do
+ expect(helper.project_list_cache_key(project)).to include('cross-project:true')
+ end
+
it "includes the pipeline status when there is a status" do
create(:ci_pipeline, :success, project: project, sha: project.commit.sha)
@@ -264,32 +274,6 @@ describe ProjectsHelper do
end
end
- describe '#license_short_name' do
- let(:project) { create(:project) }
-
- context 'when project.repository has a license_key' do
- it 'returns the nickname of the license if present' do
- allow(project.repository).to receive(:license_key).and_return('agpl-3.0')
-
- expect(helper.license_short_name(project)).to eq('GNU AGPLv3')
- end
-
- it 'returns the name of the license if nickname is not present' do
- allow(project.repository).to receive(:license_key).and_return('mit')
-
- expect(helper.license_short_name(project)).to eq('MIT License')
- end
- end
-
- context 'when project.repository has no license_key but a license_blob' do
- it 'returns LICENSE' do
- allow(project.repository).to receive(:license_key).and_return(nil)
-
- expect(helper.license_short_name(project)).to eq('LICENSE')
- end
- end
- end
-
describe '#sanitized_import_error' do
let(:project) { create(:project, :repository) }
@@ -462,6 +446,22 @@ describe ProjectsHelper do
end
end
+ describe('#push_to_create_project_command') do
+ let(:user) { create(:user, username: 'john') }
+
+ it 'returns the command to push to create project over HTTP' do
+ allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'http' }
+
+ expect(helper.push_to_create_project_command(user)).to eq('git push --set-upstream http://test.host/john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)')
+ end
+
+ it 'returns the command to push to create project over SSH' do
+ allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'ssh' }
+
+ expect(helper.push_to_create_project_command(user)).to eq('git push --set-upstream git@localhost:john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)')
+ end
+ end
+
describe '#any_projects?' do
let!(:project) { create(:project) }
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index 03f78de8e91..6332217b920 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -14,4 +14,17 @@ describe UsersHelper do
is_expected.to include("title=\"#{user.email}\"")
end
end
+
+ describe '#profile_tabs' do
+ subject(:tabs) { helper.profile_tabs }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?).and_return(true)
+ end
+
+ it 'includes all the expected tabs' do
+ expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets)
+ end
+ end
end
diff --git a/spec/javascripts/pipelines/async_button_spec.js b/spec/javascripts/pipelines/async_button_spec.js
index 8ce33d410a7..e0ea3649646 100644
--- a/spec/javascripts/pipelines/async_button_spec.js
+++ b/spec/javascripts/pipelines/async_button_spec.js
@@ -15,7 +15,8 @@ describe('Pipelines Async Button', () => {
title: 'Foo',
icon: 'repeat',
cssClass: 'bar',
- id: 123,
+ pipelineId: 123,
+ type: 'explode',
},
}).$mount();
});
@@ -39,8 +40,9 @@ describe('Pipelines Async Button', () => {
describe('With confirm dialog', () => {
it('should call the service when confimation is positive', () => {
- eventHub.$on('actionConfirmationModal', (data) => {
- expect(data.id).toEqual(123);
+ eventHub.$on('openConfirmationModal', (data) => {
+ expect(data.pipelineId).toEqual(123);
+ expect(data.type).toEqual('explode');
});
component = new AsyncButtonComponent({
@@ -49,7 +51,8 @@ describe('Pipelines Async Button', () => {
title: 'Foo',
icon: 'fa fa-foo',
cssClass: 'bar',
- id: 123,
+ pipelineId: 123,
+ type: 'explode',
},
}).$mount();
diff --git a/spec/lib/banzai/commit_renderer_spec.rb b/spec/lib/banzai/commit_renderer_spec.rb
index 84adaebdcbe..e7ebb2a332f 100644
--- a/spec/lib/banzai/commit_renderer_spec.rb
+++ b/spec/lib/banzai/commit_renderer_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Banzai::CommitRenderer do
describe '.render' do
it 'renders a commit description and title' do
- user = double(:user)
+ user = build(:user)
project = create(:project, :repository)
expect(Banzai::ObjectRenderer).to receive(:new).with(project, user).and_call_original
diff --git a/spec/lib/banzai/filter/issuable_state_filter_spec.rb b/spec/lib/banzai/filter/issuable_state_filter_spec.rb
index cacb33d3372..17347768a49 100644
--- a/spec/lib/banzai/filter/issuable_state_filter_spec.rb
+++ b/spec/lib/banzai/filter/issuable_state_filter_spec.rb
@@ -77,6 +77,14 @@ describe Banzai::Filter::IssuableStateFilter do
expect(doc.css('a').last.text).to eq("#{closed_issue.to_reference(other_project)} (closed)")
end
+ it 'skips cross project references if the user cannot read cross project' do
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ link = create_link(closed_issue.to_reference(other_project), issue: closed_issue.id, reference_type: 'issue')
+ doc = filter(link, context.merge(project: other_project))
+
+ expect(doc.css('a').last.text).to eq("#{closed_issue.to_reference(other_project)}")
+ end
+
it 'does not append state when filter is not enabled' do
link = create_link('text', issue: closed_issue.id, reference_type: 'issue')
context = { current_user: user }
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 5a7858e77f3..9a2e521fdcf 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -6,7 +6,7 @@ describe Banzai::Filter::RedactorFilter do
it 'ignores non-GFM links' do
html = %(See <a href="https://google.com/">Google</a>)
- doc = filter(html, current_user: double)
+ doc = filter(html, current_user: build(:user))
expect(doc.css('a').length).to eq 1
end
diff --git a/spec/lib/banzai/redactor_spec.rb b/spec/lib/banzai/redactor_spec.rb
index 2424c3fdc66..1fa89137972 100644
--- a/spec/lib/banzai/redactor_spec.rb
+++ b/spec/lib/banzai/redactor_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Banzai::Redactor do
- let(:user) { build(:user) }
+ let(:user) { create(:user) }
let(:project) { build(:project) }
let(:redactor) { described_class.new(project, user) }
@@ -88,6 +88,55 @@ describe Banzai::Redactor do
end
end
+ context 'when the user cannot read cross project' do
+ include ActionView::Helpers::UrlHelper
+ let(:project) { create(:project) }
+ let(:other_project) { create(:project, :public) }
+
+ def create_link(issuable)
+ type = issuable.class.name.underscore.downcase
+ link_to(issuable.to_reference, '',
+ class: 'gfm has-tooltip',
+ title: issuable.title,
+ data: {
+ reference_type: type,
+ "#{type}": issuable.id
+ })
+ end
+
+ before do
+ project.add_developer(user)
+
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global) { false }
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ end
+
+ it 'skips links to issues within the same project' do
+ issue = create(:issue, project: project)
+ link = create_link(issue)
+ doc = Nokogiri::HTML.fragment(link)
+
+ redactor.redact([doc])
+ result = doc.css('a').last
+
+ expect(result['class']).to include('has-tooltip')
+ expect(result['title']).to eq(issue.title)
+ end
+
+ it 'removes info from a cross project reference' do
+ issue = create(:issue, project: other_project)
+ link = create_link(issue)
+ doc = Nokogiri::HTML.fragment(link)
+
+ redactor.redact([doc])
+ result = doc.css('a').last
+
+ expect(result['class']).not_to include('has-tooltip')
+ expect(result['title']).to be_empty
+ end
+ end
+
describe '#redact_nodes' do
it 'redacts an Array of nodes' do
doc = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
index 4cef3bdb24b..0a63567ee40 100644
--- a/spec/lib/banzai/reference_parser/issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
@@ -19,19 +19,58 @@ describe Banzai::ReferenceParser::IssueParser do
it 'returns the nodes when the user can read the issue' do
expect(Ability).to receive(:issues_readable_by_user)
- .with([issue], user)
- .and_return([issue])
+ .with([issue], user)
+ .and_return([issue])
expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end
it 'returns an empty Array when the user can not read the issue' do
expect(Ability).to receive(:issues_readable_by_user)
- .with([issue], user)
- .and_return([])
+ .with([issue], user)
+ .and_return([])
expect(subject.nodes_visible_to_user(user, [link])).to eq([])
end
+
+ context 'when the user cannot read cross project' do
+ let(:issue) { create(:issue) }
+
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global) { false }
+ end
+
+ it 'returns the nodes when the user can read the issue' do
+ expect(Ability).to receive(:allowed?)
+ .with(user, :read_issue_iid, issue)
+ .and_return(true)
+
+ expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+ end
+
+ it 'returns an empty Array when the user can not read the issue' do
+ expect(Ability).to receive(:allowed?)
+ .with(user, :read_issue_iid, issue)
+ .and_return(false)
+
+ expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+ end
+
+ context 'when the issue is not cross project' do
+ let(:issue) { create(:issue, project: project) }
+
+ it 'does not check `can_read_reference` if the issue is not cross project' do
+ expect(Ability).to receive(:issues_readable_by_user)
+ .with([issue], user)
+ .and_return([])
+
+ expect(subject).not_to receive(:can_read_reference?).with(user, issue)
+
+ expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+ end
+ end
+ end
end
context 'when the link does not have a data-issue attribute' do
diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb
new file mode 100644
index 00000000000..c76adcbe2f5
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb
@@ -0,0 +1,262 @@
+require 'spec_helper'
+
+# Rollback DB to 10.5 (later than this was originally written for) because it still needs to work.
+describe Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile, :migration, schema: 20180208183958 do
+ include MigrationsHelpers::TrackUntrackedUploadsHelpers
+
+ let!(:appearances) { table(:appearances) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
+ let!(:routes) { table(:routes) }
+ let!(:uploads) { table(:uploads) }
+
+ before(:all) do
+ ensure_temporary_tracking_table_exists
+ end
+
+ describe '#upload_path' do
+ def assert_upload_path(file_path, expected_upload_path)
+ untracked_file = create_untracked_file(file_path)
+
+ expect(untracked_file.upload_path).to eq(expected_upload_path)
+ end
+
+ context 'for an appearance logo file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/appearance/logo/1/some_logo.jpg', 'uploads/-/system/appearance/logo/1/some_logo.jpg')
+ end
+ end
+
+ context 'for an appearance header_logo file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/appearance/header_logo/1/some_logo.jpg', 'uploads/-/system/appearance/header_logo/1/some_logo.jpg')
+ end
+ end
+
+ context 'for a pre-Markdown Note attachment file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/note/attachment/1234/some_attachment.pdf', 'uploads/-/system/note/attachment/1234/some_attachment.pdf')
+ end
+ end
+
+ context 'for a user avatar file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/user/avatar/1234/avatar.jpg', 'uploads/-/system/user/avatar/1234/avatar.jpg')
+ end
+ end
+
+ context 'for a group avatar file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/group/avatar/1234/avatar.jpg', 'uploads/-/system/group/avatar/1234/avatar.jpg')
+ end
+ end
+
+ context 'for a project avatar file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/project/avatar/1234/avatar.jpg', 'uploads/-/system/project/avatar/1234/avatar.jpg')
+ end
+ end
+
+ context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
+ it 'returns the file path relative to the project directory in uploads' do
+ project = create_project
+ random_hex = SecureRandom.hex
+
+ assert_upload_path("/#{get_full_path(project)}/#{random_hex}/Some file.jpg", "#{random_hex}/Some file.jpg")
+ end
+ end
+ end
+
+ describe '#uploader' do
+ def assert_uploader(file_path, expected_uploader)
+ untracked_file = create_untracked_file(file_path)
+
+ expect(untracked_file.uploader).to eq(expected_uploader)
+ end
+
+ context 'for an appearance logo file path' do
+ it 'returns AttachmentUploader as a string' do
+ assert_uploader('/-/system/appearance/logo/1/some_logo.jpg', 'AttachmentUploader')
+ end
+ end
+
+ context 'for an appearance header_logo file path' do
+ it 'returns AttachmentUploader as a string' do
+ assert_uploader('/-/system/appearance/header_logo/1/some_logo.jpg', 'AttachmentUploader')
+ end
+ end
+
+ context 'for a pre-Markdown Note attachment file path' do
+ it 'returns AttachmentUploader as a string' do
+ assert_uploader('/-/system/note/attachment/1234/some_attachment.pdf', 'AttachmentUploader')
+ end
+ end
+
+ context 'for a user avatar file path' do
+ it 'returns AvatarUploader as a string' do
+ assert_uploader('/-/system/user/avatar/1234/avatar.jpg', 'AvatarUploader')
+ end
+ end
+
+ context 'for a group avatar file path' do
+ it 'returns AvatarUploader as a string' do
+ assert_uploader('/-/system/group/avatar/1234/avatar.jpg', 'AvatarUploader')
+ end
+ end
+
+ context 'for a project avatar file path' do
+ it 'returns AvatarUploader as a string' do
+ assert_uploader('/-/system/project/avatar/1234/avatar.jpg', 'AvatarUploader')
+ end
+ end
+
+ context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
+ it 'returns FileUploader as a string' do
+ project = create_project
+
+ assert_uploader("/#{get_full_path(project)}/#{SecureRandom.hex}/Some file.jpg", 'FileUploader')
+ end
+ end
+ end
+
+ describe '#model_type' do
+ def assert_model_type(file_path, expected_model_type)
+ untracked_file = create_untracked_file(file_path)
+
+ expect(untracked_file.model_type).to eq(expected_model_type)
+ end
+
+ context 'for an appearance logo file path' do
+ it 'returns Appearance as a string' do
+ assert_model_type('/-/system/appearance/logo/1/some_logo.jpg', 'Appearance')
+ end
+ end
+
+ context 'for an appearance header_logo file path' do
+ it 'returns Appearance as a string' do
+ assert_model_type('/-/system/appearance/header_logo/1/some_logo.jpg', 'Appearance')
+ end
+ end
+
+ context 'for a pre-Markdown Note attachment file path' do
+ it 'returns Note as a string' do
+ assert_model_type('/-/system/note/attachment/1234/some_attachment.pdf', 'Note')
+ end
+ end
+
+ context 'for a user avatar file path' do
+ it 'returns User as a string' do
+ assert_model_type('/-/system/user/avatar/1234/avatar.jpg', 'User')
+ end
+ end
+
+ context 'for a group avatar file path' do
+ it 'returns Namespace as a string' do
+ assert_model_type('/-/system/group/avatar/1234/avatar.jpg', 'Namespace')
+ end
+ end
+
+ context 'for a project avatar file path' do
+ it 'returns Project as a string' do
+ assert_model_type('/-/system/project/avatar/1234/avatar.jpg', 'Project')
+ end
+ end
+
+ context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
+ it 'returns Project as a string' do
+ project = create_project
+
+ assert_model_type("/#{get_full_path(project)}/#{SecureRandom.hex}/Some file.jpg", 'Project')
+ end
+ end
+ end
+
+ describe '#model_id' do
+ def assert_model_id(file_path, expected_model_id)
+ untracked_file = create_untracked_file(file_path)
+
+ expect(untracked_file.model_id).to eq(expected_model_id)
+ end
+
+ context 'for an appearance logo file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/appearance/logo/1/some_logo.jpg', 1)
+ end
+ end
+
+ context 'for an appearance header_logo file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/appearance/header_logo/1/some_logo.jpg', 1)
+ end
+ end
+
+ context 'for a pre-Markdown Note attachment file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/note/attachment/1234/some_attachment.pdf', 1234)
+ end
+ end
+
+ context 'for a user avatar file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/user/avatar/1234/avatar.jpg', 1234)
+ end
+ end
+
+ context 'for a group avatar file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/group/avatar/1234/avatar.jpg', 1234)
+ end
+ end
+
+ context 'for a project avatar file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/project/avatar/1234/avatar.jpg', 1234)
+ end
+ end
+
+ context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
+ it 'returns the ID as a string' do
+ project = create_project
+
+ assert_model_id("/#{get_full_path(project)}/#{SecureRandom.hex}/Some file.jpg", project.id)
+ end
+ end
+ end
+
+ describe '#file_size' do
+ context 'for an appearance logo file path' do
+ let(:appearance) { create_or_update_appearance(logo: true) }
+ let(:untracked_file) { described_class.create!(path: get_uploads(appearance, 'Appearance').first.path) }
+
+ it 'returns the file size' do
+ expect(untracked_file.file_size).to eq(1062)
+ end
+ end
+
+ context 'for a project avatar file path' do
+ let(:project) { create_project(avatar: true) }
+ let(:untracked_file) { described_class.create!(path: get_uploads(project, 'Project').first.path) }
+
+ it 'returns the file size' do
+ expect(untracked_file.file_size).to eq(1062)
+ end
+ end
+
+ context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
+ let(:project) { create_project }
+ let(:untracked_file) { create_untracked_file("/#{get_full_path(project)}/#{get_uploads(project, 'Project').first.path}") }
+
+ before do
+ add_markdown_attachment(project)
+ end
+
+ it 'returns the file size' do
+ expect(untracked_file.file_size).to eq(1062)
+ end
+ end
+ end
+
+ def create_untracked_file(path_relative_to_upload_dir)
+ described_class.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}#{path_relative_to_upload_dir}")
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
index c8fa252439a..0d2074eed22 100644
--- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
@@ -1,17 +1,19 @@
require 'spec_helper'
-# This migration is using UploadService, which sets uploads.secret that is only
-# added to the DB schema in 20180129193323. Since the test isn't isolated, we
-# just use the latest schema when testing this migration.
-# Ideally, the test should not use factories nor UploadService, and rely on the
-# `table` helper instead.
-describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migration, schema: 20180129193323 do
- include TrackUntrackedUploadsHelpers
+# Rollback DB to 10.5 (later than this was originally written for) because it still needs to work.
+describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migration, schema: 20180208183958 do
+ include MigrationsHelpers::TrackUntrackedUploadsHelpers
subject { described_class.new }
- let!(:untracked_files_for_uploads) { described_class::UntrackedFile }
- let!(:uploads) { described_class::Upload }
+ let!(:appearances) { table(:appearances) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:notes) { table(:notes) }
+ let!(:projects) { table(:projects) }
+ let!(:routes) { table(:routes) }
+ let!(:untracked_files_for_uploads) { table(:untracked_files_for_uploads) }
+ let!(:uploads) { table(:uploads) }
+ let!(:users) { table(:users) }
before do
ensure_temporary_tracking_table_exists
@@ -19,30 +21,30 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
end
context 'with untracked files and tracked files in untracked_files_for_uploads' do
- let!(:appearance) { create_or_update_appearance(logo: uploaded_file, header_logo: uploaded_file) }
- let!(:user1) { create(:user, :with_avatar) }
- let!(:user2) { create(:user, :with_avatar) }
- let!(:project1) { create(:project, :legacy_storage, :with_avatar) }
- let!(:project2) { create(:project, :legacy_storage, :with_avatar) }
+ let!(:appearance) { create_or_update_appearance(logo: true, header_logo: true) }
+ let!(:user1) { create_user(avatar: true) }
+ let!(:user2) { create_user(avatar: true) }
+ let!(:project1) { create_project(avatar: true) }
+ let!(:project2) { create_project(avatar: true) }
before do
- UploadService.new(project1, uploaded_file, FileUploader).execute # Markdown upload
- UploadService.new(project2, uploaded_file, FileUploader).execute # Markdown upload
+ add_markdown_attachment(project1)
+ add_markdown_attachment(project2)
# File records created by PrepareUntrackedUploads
- untracked_files_for_uploads.create!(path: appearance.uploads.first.path)
- untracked_files_for_uploads.create!(path: appearance.uploads.last.path)
- untracked_files_for_uploads.create!(path: user1.uploads.first.path)
- untracked_files_for_uploads.create!(path: user2.uploads.first.path)
- untracked_files_for_uploads.create!(path: project1.uploads.first.path)
- untracked_files_for_uploads.create!(path: project2.uploads.first.path)
- untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project1.full_path}/#{project1.uploads.last.path}")
- untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project2.full_path}/#{project2.uploads.last.path}")
+ untracked_files_for_uploads.create!(path: get_uploads(appearance, 'Appearance').first.path)
+ untracked_files_for_uploads.create!(path: get_uploads(appearance, 'Appearance').last.path)
+ untracked_files_for_uploads.create!(path: get_uploads(user1, 'User').first.path)
+ untracked_files_for_uploads.create!(path: get_uploads(user2, 'User').first.path)
+ untracked_files_for_uploads.create!(path: get_uploads(project1, 'Project').first.path)
+ untracked_files_for_uploads.create!(path: get_uploads(project2, 'Project').first.path)
+ untracked_files_for_uploads.create!(path: "#{legacy_project_uploads_dir(project1).sub("#{MigrationsHelpers::TrackUntrackedUploadsHelpers::PUBLIC_DIR}/", '')}/#{get_uploads(project1, 'Project').last.path}")
+ untracked_files_for_uploads.create!(path: "#{legacy_project_uploads_dir(project2).sub("#{MigrationsHelpers::TrackUntrackedUploadsHelpers::PUBLIC_DIR}/", '')}/#{get_uploads(project2, 'Project').last.path}")
# Untrack 4 files
- user2.uploads.delete_all
- project2.uploads.delete_all # 2 files: avatar and a Markdown upload
- appearance.uploads.where("path like '%header_logo%'").delete_all
+ get_uploads(user2, 'User').delete_all
+ get_uploads(project2, 'Project').delete_all # 2 files: avatar and a Markdown upload
+ get_uploads(appearance, 'Appearance').where("path like '%header_logo%'").delete_all
end
it 'adds untracked files to the uploads table' do
@@ -50,9 +52,9 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
subject.perform(1, untracked_files_for_uploads.reorder(:id).last.id)
end.to change { uploads.count }.from(4).to(8)
- expect(user2.uploads.count).to eq(1)
- expect(project2.uploads.count).to eq(2)
- expect(appearance.uploads.count).to eq(2)
+ expect(get_uploads(user2, 'User').count).to eq(1)
+ expect(get_uploads(project2, 'Project').count).to eq(2)
+ expect(get_uploads(appearance, 'Appearance').count).to eq(2)
end
it 'deletes rows after processing them' do
@@ -66,9 +68,9 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
it 'does not create duplicate uploads of already tracked files' do
subject.perform(1, untracked_files_for_uploads.last.id)
- expect(user1.uploads.count).to eq(1)
- expect(project1.uploads.count).to eq(2)
- expect(appearance.uploads.count).to eq(2)
+ expect(get_uploads(user1, 'User').count).to eq(1)
+ expect(get_uploads(project1, 'Project').count).to eq(2)
+ expect(get_uploads(appearance, 'Appearance').count).to eq(2)
end
it 'uses the start and end batch ids [only 1st half]' do
@@ -80,11 +82,11 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
subject.perform(start_id, end_id)
end.to change { uploads.count }.from(4).to(6)
- expect(user1.uploads.count).to eq(1)
- expect(user2.uploads.count).to eq(1)
- expect(appearance.uploads.count).to eq(2)
- expect(project1.uploads.count).to eq(2)
- expect(project2.uploads.count).to eq(0)
+ expect(get_uploads(user1, 'User').count).to eq(1)
+ expect(get_uploads(user2, 'User').count).to eq(1)
+ expect(get_uploads(appearance, 'Appearance').count).to eq(2)
+ expect(get_uploads(project1, 'Project').count).to eq(2)
+ expect(get_uploads(project2, 'Project').count).to eq(0)
# Only 4 have been either confirmed or added to uploads
expect(untracked_files_for_uploads.count).to eq(4)
@@ -99,11 +101,11 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
subject.perform(start_id, end_id)
end.to change { uploads.count }.from(4).to(6)
- expect(user1.uploads.count).to eq(1)
- expect(user2.uploads.count).to eq(0)
- expect(appearance.uploads.count).to eq(1)
- expect(project1.uploads.count).to eq(2)
- expect(project2.uploads.count).to eq(2)
+ expect(get_uploads(user1, 'User').count).to eq(1)
+ expect(get_uploads(user2, 'User').count).to eq(0)
+ expect(get_uploads(appearance, 'Appearance').count).to eq(1)
+ expect(get_uploads(project1, 'Project').count).to eq(2)
+ expect(get_uploads(project2, 'Project').count).to eq(2)
# Only 4 have been either confirmed or added to uploads
expect(untracked_files_for_uploads.count).to eq(4)
@@ -122,7 +124,7 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
end
it 'does not block a whole batch because of one bad path' do
- untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project2.full_path}/._7d37bf4c747916390e596744117d5d1a")
+ untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{get_full_path(project2)}/._7d37bf4c747916390e596744117d5d1a")
expect(untracked_files_for_uploads.count).to eq(9)
expect(uploads.count).to eq(4)
@@ -133,7 +135,7 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
end
it 'an unparseable path is shown in error output' do
- bad_path = "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project2.full_path}/._7d37bf4c747916390e596744117d5d1a"
+ bad_path = "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{get_full_path(project2)}/._7d37bf4c747916390e596744117d5d1a"
untracked_files_for_uploads.create!(path: bad_path)
expect(Rails.logger).to receive(:error).with(/Error parsing path "#{bad_path}":/)
@@ -152,363 +154,100 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
describe 'upload outcomes for each path pattern' do
shared_examples_for 'non_markdown_file' do
- let!(:expected_upload_attrs) { model.uploads.first.attributes.slice('path', 'uploader', 'size', 'checksum') }
+ let!(:expected_upload_attrs) { model_uploads.first.attributes.slice('path', 'uploader', 'size', 'checksum') }
let!(:untracked_file) { untracked_files_for_uploads.create!(path: expected_upload_attrs['path']) }
before do
- model.uploads.delete_all
+ model_uploads.delete_all
end
it 'creates an Upload record' do
expect do
subject.perform(1, untracked_files_for_uploads.last.id)
- end.to change { model.reload.uploads.count }.from(0).to(1)
+ end.to change { model_uploads.count }.from(0).to(1)
- expect(model.uploads.first.attributes).to include(expected_upload_attrs)
+ expect(model_uploads.first.attributes).to include(expected_upload_attrs)
end
end
context 'for an appearance logo file path' do
- let(:model) { create_or_update_appearance(logo: uploaded_file) }
+ let(:model) { create_or_update_appearance(logo: true) }
+ let(:model_uploads) { get_uploads(model, 'Appearance') }
it_behaves_like 'non_markdown_file'
end
context 'for an appearance header_logo file path' do
- let(:model) { create_or_update_appearance(header_logo: uploaded_file) }
+ let(:model) { create_or_update_appearance(header_logo: true) }
+ let(:model_uploads) { get_uploads(model, 'Appearance') }
it_behaves_like 'non_markdown_file'
end
context 'for a pre-Markdown Note attachment file path' do
- let(:model) { create(:note, :with_attachment) }
- let!(:expected_upload_attrs) { Upload.where(model_type: 'Note', model_id: model.id).first.attributes.slice('path', 'uploader', 'size', 'checksum') }
+ let(:model) { create_note(attachment: true) }
+ let!(:expected_upload_attrs) { get_uploads(model, 'Note').first.attributes.slice('path', 'uploader', 'size', 'checksum') }
let!(:untracked_file) { untracked_files_for_uploads.create!(path: expected_upload_attrs['path']) }
before do
- Upload.where(model_type: 'Note', model_id: model.id).delete_all
+ get_uploads(model, 'Note').delete_all
end
# Can't use the shared example because Note doesn't have an `uploads` association
it 'creates an Upload record' do
expect do
subject.perform(1, untracked_files_for_uploads.last.id)
- end.to change { Upload.where(model_type: 'Note', model_id: model.id).count }.from(0).to(1)
+ end.to change { get_uploads(model, 'Note').count }.from(0).to(1)
- expect(Upload.where(model_type: 'Note', model_id: model.id).first.attributes).to include(expected_upload_attrs)
+ expect(get_uploads(model, 'Note').first.attributes).to include(expected_upload_attrs)
end
end
context 'for a user avatar file path' do
- let(:model) { create(:user, :with_avatar) }
+ let(:model) { create_user(avatar: true) }
+ let(:model_uploads) { get_uploads(model, 'User') }
it_behaves_like 'non_markdown_file'
end
context 'for a group avatar file path' do
- let(:model) { create(:group, :with_avatar) }
+ let(:model) { create_group(avatar: true) }
+ let(:model_uploads) { get_uploads(model, 'Namespace') }
it_behaves_like 'non_markdown_file'
end
context 'for a project avatar file path' do
- let(:model) { create(:project, :legacy_storage, :with_avatar) }
+ let(:model) { create_project(avatar: true) }
+ let(:model_uploads) { get_uploads(model, 'Project') }
it_behaves_like 'non_markdown_file'
end
context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- let(:model) { create(:project, :legacy_storage) }
+ let(:model) { create_project }
before do
# Upload the file
- UploadService.new(model, uploaded_file, FileUploader).execute
+ add_markdown_attachment(model)
# Create the untracked_files_for_uploads record
- untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{model.full_path}/#{model.uploads.first.path}")
+ untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{get_full_path(model)}/#{get_uploads(model, 'Project').first.path}")
# Save the expected upload attributes
- @expected_upload_attrs = model.reload.uploads.first.attributes.slice('path', 'uploader', 'size', 'checksum')
+ @expected_upload_attrs = get_uploads(model, 'Project').first.attributes.slice('path', 'uploader', 'size', 'checksum')
# Untrack the file
- model.reload.uploads.delete_all
+ get_uploads(model, 'Project').delete_all
end
it 'creates an Upload record' do
expect do
subject.perform(1, untracked_files_for_uploads.last.id)
- end.to change { model.reload.uploads.count }.from(0).to(1)
+ end.to change { get_uploads(model, 'Project').count }.from(0).to(1)
- expect(model.uploads.first.attributes).to include(@expected_upload_attrs)
+ expect(get_uploads(model, 'Project').first.attributes).to include(@expected_upload_attrs)
end
end
end
end
-
-describe Gitlab::BackgroundMigration::PopulateUntrackedUploads::UntrackedFile do
- include TrackUntrackedUploadsHelpers
-
- let(:upload_class) { Gitlab::BackgroundMigration::PopulateUntrackedUploads::Upload }
-
- before(:all) do
- ensure_temporary_tracking_table_exists
- end
-
- describe '#upload_path' do
- def assert_upload_path(file_path, expected_upload_path)
- untracked_file = create_untracked_file(file_path)
-
- expect(untracked_file.upload_path).to eq(expected_upload_path)
- end
-
- context 'for an appearance logo file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/appearance/logo/1/some_logo.jpg', 'uploads/-/system/appearance/logo/1/some_logo.jpg')
- end
- end
-
- context 'for an appearance header_logo file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/appearance/header_logo/1/some_logo.jpg', 'uploads/-/system/appearance/header_logo/1/some_logo.jpg')
- end
- end
-
- context 'for a pre-Markdown Note attachment file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/note/attachment/1234/some_attachment.pdf', 'uploads/-/system/note/attachment/1234/some_attachment.pdf')
- end
- end
-
- context 'for a user avatar file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/user/avatar/1234/avatar.jpg', 'uploads/-/system/user/avatar/1234/avatar.jpg')
- end
- end
-
- context 'for a group avatar file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/group/avatar/1234/avatar.jpg', 'uploads/-/system/group/avatar/1234/avatar.jpg')
- end
- end
-
- context 'for a project avatar file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/project/avatar/1234/avatar.jpg', 'uploads/-/system/project/avatar/1234/avatar.jpg')
- end
- end
-
- context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- it 'returns the file path relative to the project directory in uploads' do
- project = create(:project, :legacy_storage)
- random_hex = SecureRandom.hex
-
- assert_upload_path("/#{project.full_path}/#{random_hex}/Some file.jpg", "#{random_hex}/Some file.jpg")
- end
- end
- end
-
- describe '#uploader' do
- def assert_uploader(file_path, expected_uploader)
- untracked_file = create_untracked_file(file_path)
-
- expect(untracked_file.uploader).to eq(expected_uploader)
- end
-
- context 'for an appearance logo file path' do
- it 'returns AttachmentUploader as a string' do
- assert_uploader('/-/system/appearance/logo/1/some_logo.jpg', 'AttachmentUploader')
- end
- end
-
- context 'for an appearance header_logo file path' do
- it 'returns AttachmentUploader as a string' do
- assert_uploader('/-/system/appearance/header_logo/1/some_logo.jpg', 'AttachmentUploader')
- end
- end
-
- context 'for a pre-Markdown Note attachment file path' do
- it 'returns AttachmentUploader as a string' do
- assert_uploader('/-/system/note/attachment/1234/some_attachment.pdf', 'AttachmentUploader')
- end
- end
-
- context 'for a user avatar file path' do
- it 'returns AvatarUploader as a string' do
- assert_uploader('/-/system/user/avatar/1234/avatar.jpg', 'AvatarUploader')
- end
- end
-
- context 'for a group avatar file path' do
- it 'returns AvatarUploader as a string' do
- assert_uploader('/-/system/group/avatar/1234/avatar.jpg', 'AvatarUploader')
- end
- end
-
- context 'for a project avatar file path' do
- it 'returns AvatarUploader as a string' do
- assert_uploader('/-/system/project/avatar/1234/avatar.jpg', 'AvatarUploader')
- end
- end
-
- context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- it 'returns FileUploader as a string' do
- project = create(:project, :legacy_storage)
-
- assert_uploader("/#{project.full_path}/#{SecureRandom.hex}/Some file.jpg", 'FileUploader')
- end
- end
- end
-
- describe '#model_type' do
- def assert_model_type(file_path, expected_model_type)
- untracked_file = create_untracked_file(file_path)
-
- expect(untracked_file.model_type).to eq(expected_model_type)
- end
-
- context 'for an appearance logo file path' do
- it 'returns Appearance as a string' do
- assert_model_type('/-/system/appearance/logo/1/some_logo.jpg', 'Appearance')
- end
- end
-
- context 'for an appearance header_logo file path' do
- it 'returns Appearance as a string' do
- assert_model_type('/-/system/appearance/header_logo/1/some_logo.jpg', 'Appearance')
- end
- end
-
- context 'for a pre-Markdown Note attachment file path' do
- it 'returns Note as a string' do
- assert_model_type('/-/system/note/attachment/1234/some_attachment.pdf', 'Note')
- end
- end
-
- context 'for a user avatar file path' do
- it 'returns User as a string' do
- assert_model_type('/-/system/user/avatar/1234/avatar.jpg', 'User')
- end
- end
-
- context 'for a group avatar file path' do
- it 'returns Namespace as a string' do
- assert_model_type('/-/system/group/avatar/1234/avatar.jpg', 'Namespace')
- end
- end
-
- context 'for a project avatar file path' do
- it 'returns Project as a string' do
- assert_model_type('/-/system/project/avatar/1234/avatar.jpg', 'Project')
- end
- end
-
- context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- it 'returns Project as a string' do
- project = create(:project, :legacy_storage)
-
- assert_model_type("/#{project.full_path}/#{SecureRandom.hex}/Some file.jpg", 'Project')
- end
- end
- end
-
- describe '#model_id' do
- def assert_model_id(file_path, expected_model_id)
- untracked_file = create_untracked_file(file_path)
-
- expect(untracked_file.model_id).to eq(expected_model_id)
- end
-
- context 'for an appearance logo file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/appearance/logo/1/some_logo.jpg', 1)
- end
- end
-
- context 'for an appearance header_logo file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/appearance/header_logo/1/some_logo.jpg', 1)
- end
- end
-
- context 'for a pre-Markdown Note attachment file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/note/attachment/1234/some_attachment.pdf', 1234)
- end
- end
-
- context 'for a user avatar file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/user/avatar/1234/avatar.jpg', 1234)
- end
- end
-
- context 'for a group avatar file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/group/avatar/1234/avatar.jpg', 1234)
- end
- end
-
- context 'for a project avatar file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/project/avatar/1234/avatar.jpg', 1234)
- end
- end
-
- context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- it 'returns the ID as a string' do
- project = create(:project, :legacy_storage)
-
- assert_model_id("/#{project.full_path}/#{SecureRandom.hex}/Some file.jpg", project.id)
- end
- end
- end
-
- describe '#file_size' do
- context 'for an appearance logo file path' do
- let(:appearance) { create_or_update_appearance(logo: uploaded_file) }
- let(:untracked_file) { described_class.create!(path: appearance.uploads.first.path) }
-
- it 'returns the file size' do
- expect(untracked_file.file_size).to eq(35255)
- end
-
- it 'returns the same thing that CarrierWave would return' do
- expect(untracked_file.file_size).to eq(appearance.logo.size)
- end
- end
-
- context 'for a project avatar file path' do
- let(:project) { create(:project, :legacy_storage, avatar: uploaded_file) }
- let(:untracked_file) { described_class.create!(path: project.uploads.first.path) }
-
- it 'returns the file size' do
- expect(untracked_file.file_size).to eq(35255)
- end
-
- it 'returns the same thing that CarrierWave would return' do
- expect(untracked_file.file_size).to eq(project.avatar.size)
- end
- end
-
- context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- let(:project) { create(:project, :legacy_storage) }
- let(:untracked_file) { create_untracked_file("/#{project.full_path}/#{project.uploads.first.path}") }
-
- before do
- UploadService.new(project, uploaded_file, FileUploader).execute
- end
-
- it 'returns the file size' do
- expect(untracked_file.file_size).to eq(35255)
- end
-
- it 'returns the same thing that CarrierWave would return' do
- expect(untracked_file.file_size).to eq(project.uploads.first.size)
- end
- end
- end
-
- def create_untracked_file(path_relative_to_upload_dir)
- described_class.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}#{path_relative_to_upload_dir}")
- end
-end
diff --git a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
index ca77e64ae40..35750d89c35 100644
--- a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
@@ -1,10 +1,16 @@
require 'spec_helper'
-describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq, :migration, schema: 20180129193323 do
- include TrackUntrackedUploadsHelpers
- include MigrationsHelpers
-
- let!(:untracked_files_for_uploads) { described_class::UntrackedFile }
+# Rollback DB to 10.5 (later than this was originally written for) because it still needs to work.
+describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq, :migration, schema: 20180208183958 do
+ include MigrationsHelpers::TrackUntrackedUploadsHelpers
+
+ let!(:untracked_files_for_uploads) { table(:untracked_files_for_uploads) }
+ let!(:appearances) { table(:appearances) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
+ let!(:routes) { table(:routes) }
+ let!(:uploads) { table(:uploads) }
+ let!(:users) { table(:users) }
around do |example|
# Especially important so the follow-up migration does not get run
@@ -15,19 +21,17 @@ describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq, :migrat
shared_examples 'prepares the untracked_files_for_uploads table' do
context 'when files were uploaded before and after hashed storage was enabled' do
- let!(:appearance) { create_or_update_appearance(logo: uploaded_file, header_logo: uploaded_file) }
- let!(:user) { create(:user, :with_avatar) }
- let!(:project1) { create(:project, :with_avatar, :legacy_storage) }
- let(:project2) { create(:project) } # instantiate after enabling hashed_storage
+ let!(:appearance) { create_or_update_appearance(logo: true, header_logo: true) }
+ let!(:user) { create_user(avatar: true) }
+ let!(:project1) { create_project(avatar: true) }
+ let(:project2) { create_project } # instantiate after enabling hashed_storage
before do
# Markdown upload before enabling hashed_storage
- UploadService.new(project1, uploaded_file, FileUploader).execute
-
- stub_application_setting(hashed_storage_enabled: true)
+ add_markdown_attachment(project1)
# Markdown upload after enabling hashed_storage
- UploadService.new(project2, uploaded_file, FileUploader).execute
+ add_markdown_attachment(project2, hashed_storage: true)
end
it 'has a path field long enough for really long paths' do
@@ -61,7 +65,7 @@ describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq, :migrat
it 'does not add hashed files to the untracked_files_for_uploads table' do
described_class.new.perform
- hashed_file_path = project2.uploads.where(uploader: 'FileUploader').first.path
+ hashed_file_path = get_uploads(project2, 'Project').where(uploader: 'FileUploader').first.path
expect(untracked_files_for_uploads.where("path like '%#{hashed_file_path}%'").exists?).to be_falsey
end
diff --git a/spec/lib/gitlab/contributions_calendar_spec.rb b/spec/lib/gitlab/contributions_calendar_spec.rb
index f1655854486..49a179ba875 100644
--- a/spec/lib/gitlab/contributions_calendar_spec.rb
+++ b/spec/lib/gitlab/contributions_calendar_spec.rb
@@ -118,6 +118,19 @@ describe Gitlab::ContributionsCalendar do
expect(calendar.events_by_date(today)).to contain_exactly(e1)
expect(calendar(contributor).events_by_date(today)).to contain_exactly(e1, e2, e3)
end
+
+ context 'when the user cannot read read cross project' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ end
+
+ it 'does not return any events' do
+ create_event(public_project, today)
+
+ expect(calendar(user).events_by_date(today)).to be_empty
+ end
+ end
end
describe '#starting_year' do
diff --git a/spec/lib/gitlab/cross_project_access/check_collection_spec.rb b/spec/lib/gitlab/cross_project_access/check_collection_spec.rb
new file mode 100644
index 00000000000..a9e7575240e
--- /dev/null
+++ b/spec/lib/gitlab/cross_project_access/check_collection_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+describe Gitlab::CrossProjectAccess::CheckCollection do
+ subject(:collection) { described_class.new }
+
+ describe '#add_collection' do
+ it 'merges the checks of 2 collections' do
+ initial_check = double('check')
+ collection.add_check(initial_check)
+
+ other_collection = described_class.new
+ other_check = double('other_check')
+ other_collection.add_check(other_check)
+
+ shared_check = double('shared check')
+ other_collection.add_check(shared_check)
+ collection.add_check(shared_check)
+
+ collection.add_collection(other_collection)
+
+ expect(collection.checks).to contain_exactly(initial_check, shared_check, other_check)
+ end
+ end
+
+ describe '#should_run?' do
+ def fake_check(run, skip)
+ check = double("Check: run=#{run} - skip={skip}")
+ allow(check).to receive(:should_run?).and_return(run)
+ allow(check).to receive(:should_skip?).and_return(skip)
+ allow(check).to receive(:skip).and_return(skip)
+
+ check
+ end
+
+ it 'returns true if one of the check says it should run' do
+ check = fake_check(true, false)
+ other_check = fake_check(false, false)
+
+ collection.add_check(check)
+ collection.add_check(other_check)
+
+ expect(collection.should_run?(double)).to be_truthy
+ end
+
+ it 'returns false if one of the check says it should be skipped' do
+ check = fake_check(true, false)
+ other_check = fake_check(false, true)
+
+ collection.add_check(check)
+ collection.add_check(other_check)
+
+ expect(collection.should_run?(double)).to be_falsey
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cross_project_access/check_info_spec.rb b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
new file mode 100644
index 00000000000..bc9dbf2bece
--- /dev/null
+++ b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
@@ -0,0 +1,111 @@
+require 'spec_helper'
+
+describe Gitlab::CrossProjectAccess::CheckInfo do
+ let(:dummy_controller) { double }
+
+ before do
+ allow(dummy_controller).to receive(:action_name).and_return('index')
+ end
+
+ describe '#should_run?' do
+ it 'runs when an action is defined' do
+ info = described_class.new({ index: true }, nil, nil, false)
+
+ expect(info.should_run?(dummy_controller)).to be_truthy
+ end
+
+ it 'runs when the action is missing' do
+ info = described_class.new({}, nil, nil, false)
+
+ expect(info.should_run?(dummy_controller)).to be_truthy
+ end
+
+ it 'does not run when the action is excluded' do
+ info = described_class.new({ index: false }, nil, nil, false)
+
+ expect(info.should_run?(dummy_controller)).to be_falsy
+ end
+
+ it 'runs when the `if` conditional is true' do
+ info = described_class.new({}, -> { true }, nil, false)
+
+ expect(info.should_run?(dummy_controller)).to be_truthy
+ end
+
+ it 'does not run when the if condition is false' do
+ info = described_class.new({}, -> { false }, nil, false)
+
+ expect(info.should_run?(dummy_controller)).to be_falsy
+ end
+
+ it 'does not run when the `unless` check is true' do
+ info = described_class.new({}, nil, -> { true }, false)
+
+ expect(info.should_run?(dummy_controller)).to be_falsy
+ end
+
+ it 'runs when the `unless` check is false' do
+ info = described_class.new({}, nil, -> { false }, false)
+
+ expect(info.should_run?(dummy_controller)).to be_truthy
+ end
+
+ it 'returns the the oposite of #should_skip? when the check is a skip' do
+ info = described_class.new({}, nil, nil, true)
+
+ expect(info).to receive(:should_skip?).with(dummy_controller).and_return(false)
+ expect(info.should_run?(dummy_controller)).to be_truthy
+ end
+ end
+
+ describe '#should_skip?' do
+ it 'skips when an action is defined' do
+ info = described_class.new({ index: true }, nil, nil, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_truthy
+ end
+
+ it 'does not skip when the action is not defined' do
+ info = described_class.new({}, nil, nil, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_falsy
+ end
+
+ it 'does not skip when the action is excluded' do
+ info = described_class.new({ index: false }, nil, nil, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_falsy
+ end
+
+ it 'skips when the `if` conditional is true' do
+ info = described_class.new({ index: true }, -> { true }, nil, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_truthy
+ end
+
+ it 'does not skip the `if` conditional is false' do
+ info = described_class.new({ index: true }, -> { false }, nil, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_falsy
+ end
+
+ it 'does not skip when the `unless` check is true' do
+ info = described_class.new({ index: true }, nil, -> { true }, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_falsy
+ end
+
+ it 'skips when `unless` check is false' do
+ info = described_class.new({ index: true }, nil, -> { false }, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_truthy
+ end
+
+ it 'returns the the oposite of #should_run? when the check is not a skip' do
+ info = described_class.new({}, nil, nil, false)
+
+ expect(info).to receive(:should_run?).with(dummy_controller).and_return(false)
+ expect(info.should_skip?(dummy_controller)).to be_truthy
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cross_project_access/class_methods_spec.rb b/spec/lib/gitlab/cross_project_access/class_methods_spec.rb
new file mode 100644
index 00000000000..5349685e633
--- /dev/null
+++ b/spec/lib/gitlab/cross_project_access/class_methods_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Gitlab::CrossProjectAccess::ClassMethods do
+ let(:dummy_class) do
+ Class.new do
+ extend Gitlab::CrossProjectAccess::ClassMethods
+ end
+ end
+ let(:dummy_proc) { lambda { false } }
+
+ describe '#requires_cross_project_access' do
+ it 'creates a correct check when a hash is passed' do
+ expect(Gitlab::CrossProjectAccess)
+ .to receive(:add_check).with(dummy_class,
+ actions: { hello: true, world: false },
+ positive_condition: dummy_proc,
+ negative_condition: dummy_proc)
+
+ dummy_class.requires_cross_project_access(
+ hello: true, world: false, if: dummy_proc, unless: dummy_proc
+ )
+ end
+
+ it 'creates a correct check when an array is passed' do
+ expect(Gitlab::CrossProjectAccess)
+ .to receive(:add_check).with(dummy_class,
+ actions: { hello: true, world: true },
+ positive_condition: nil,
+ negative_condition: nil)
+
+ dummy_class.requires_cross_project_access(:hello, :world)
+ end
+
+ it 'creates a correct check when an array and a hash is passed' do
+ expect(Gitlab::CrossProjectAccess)
+ .to receive(:add_check).with(dummy_class,
+ actions: { hello: true, world: true },
+ positive_condition: dummy_proc,
+ negative_condition: dummy_proc)
+
+ dummy_class.requires_cross_project_access(
+ :hello, :world, if: dummy_proc, unless: dummy_proc
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cross_project_access_spec.rb b/spec/lib/gitlab/cross_project_access_spec.rb
new file mode 100644
index 00000000000..614b0473c7e
--- /dev/null
+++ b/spec/lib/gitlab/cross_project_access_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+
+describe Gitlab::CrossProjectAccess do
+ let(:super_class) { Class.new }
+ let(:descendant_class) { Class.new(super_class) }
+ let(:current_instance) { described_class.new }
+
+ before do
+ allow(described_class).to receive(:instance).and_return(current_instance)
+ end
+
+ describe '#add_check' do
+ it 'keeps track of the properties to check' do
+ expect do
+ described_class.add_check(super_class,
+ actions: { index: true },
+ positive_condition: -> { true },
+ negative_condition: -> { false })
+ end.to change { described_class.checks.size }.by(1)
+ end
+
+ it 'builds the check correctly' do
+ check_collection = described_class.add_check(super_class,
+ actions: { index: true },
+ positive_condition: -> { 'positive' },
+ negative_condition: -> { 'negative' })
+
+ check = check_collection.checks.first
+
+ expect(check.actions).to eq(index: true)
+ expect(check.positive_condition.call).to eq('positive')
+ expect(check.negative_condition.call).to eq('negative')
+ end
+
+ it 'merges the checks of a parent class into existing checks of a subclass' do
+ subclass_collection = described_class.add_check(descendant_class)
+
+ expect(subclass_collection).to receive(:add_collection).and_call_original
+
+ described_class.add_check(super_class)
+ end
+
+ it 'merges the existing checks of a superclass into the checks of a subclass' do
+ super_collection = described_class.add_check(super_class)
+ descendant_collection = described_class.add_check(descendant_class)
+
+ expect(descendant_collection.checks).to include(*super_collection.checks)
+ end
+ end
+
+ describe '#find_check' do
+ it 'returns a check when it was defined for a superclass' do
+ expected_check = described_class.add_check(super_class,
+ actions: { index: true },
+ positive_condition: -> { 'positive' },
+ negative_condition: -> { 'negative' })
+
+ expect(described_class.find_check(descendant_class.new))
+ .to eq(expected_check)
+ end
+
+ it 'caches the result for a subclass' do
+ described_class.add_check(super_class,
+ actions: { index: true },
+ positive_condition: -> { 'positive' },
+ negative_condition: -> { 'negative' })
+
+ expect(described_class.instance).to receive(:closest_parent).once.and_call_original
+
+ 2.times { described_class.find_check(descendant_class.new) }
+ end
+
+ it 'returns the checks for the closest class if there are more checks available' do
+ described_class.add_check(super_class,
+ actions: { index: true })
+ expected_check = described_class.add_check(descendant_class,
+ actions: { index: true, show: false })
+
+ check = described_class.find_check(descendant_class.new)
+
+ expect(check).to eq(expected_check)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb
index cd602ccab8e..73d60c021c8 100644
--- a/spec/lib/gitlab/diff/highlight_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_spec.rb
@@ -72,6 +72,28 @@ describe Gitlab::Diff::Highlight do
expect(subject[5].text).to eq(code)
expect(subject[5].text).to be_html_safe
end
+
+ context 'when the inline diff marker has an invalid range' do
+ before do
+ allow_any_instance_of(Gitlab::Diff::InlineDiffMarker).to receive(:mark).and_raise(RangeError)
+ end
+
+ it 'keeps the original rich line' do
+ code = %q{+ raise RuntimeError, "System commands must be given as an array of strings"}
+
+ expect(subject[5].text).to eq(code)
+ expect(subject[5].text).not_to be_html_safe
+ end
+
+ it 'reports to Sentry if configured' do
+ allow(Gitlab::Sentry).to receive(:enabled?).and_return(true)
+
+ expect(Gitlab::Sentry).to receive(:context)
+ expect(Raven).to receive(:capture_exception)
+
+ subject
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 67271d769a0..d601a383a98 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -600,6 +600,33 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ describe '#branch_names_contains_sha' do
+ shared_examples 'returning the right branches' do
+ let(:head_id) { repository.rugged.head.target.oid }
+ let(:new_branch) { head_id }
+
+ before do
+ repository.create_branch(new_branch, 'master')
+ end
+
+ after do
+ repository.delete_branch(new_branch)
+ end
+
+ it 'displays that branch' do
+ expect(repository.branch_names_contains_sha(head_id)).to include('master', new_branch)
+ end
+ end
+
+ context 'when Gitaly is enabled' do
+ it_behaves_like 'returning the right branches'
+ end
+
+ context 'when Gitaly is disabled', :disable_gitaly do
+ it_behaves_like 'returning the right branches'
+ end
+ end
+
describe "#refs_hash" do
subject { repository.refs_hash }
@@ -2204,7 +2231,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
context 'sparse checkout', :skip_gitaly_mock do
let(:expected_files) { %w(files files/js files/js/application.js) }
- before do
+ it 'checks out only the files in the diff' do
allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args|
m.call(*args) do
worktree_path = args[0]
@@ -2216,11 +2243,34 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect(Dir[files_pattern]).to eq(expected)
end
end
- end
- it 'checkouts only the files in the diff' do
subject
end
+
+ context 'when the diff contains a rename' do
+ let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged }
+ let(:end_sha) { new_commit_move_file(repo).oid }
+
+ after do
+ # Erase our commits so other tests get the original repo
+ repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
+ repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID)
+ end
+
+ it 'does not include the renamed file in the sparse checkout' do
+ allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args|
+ m.call(*args) do
+ worktree_path = args[0]
+ files_pattern = File.join(worktree_path, '**', '*')
+
+ expect(Dir[files_pattern]).not_to include('CHANGELOG')
+ expect(Dir[files_pattern]).not_to include('encoding/CHANGELOG')
+ end
+ end
+
+ subject
+ end
+ end
end
context 'with an ASCII-8BIT diff', :skip_gitaly_mock do
@@ -2230,7 +2280,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
allow(repository).to receive(:run_git!).and_call_original
allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
- expect(subject.length).to eq(40)
+ expect(subject).to match(/\h{40}/)
end
end
end
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 186b2d9279d..215f1ecc9c5 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::GitAccessWiki do
let(:access) { described_class.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :wiki_repo) }
let(:user) { create(:user) }
let(:changes) { ['6f6d7e7ed 570e7b2ab refs/heads/master'] }
let(:redirected_path) { nil }
@@ -48,6 +48,18 @@ describe Gitlab::GitAccessWiki do
it 'give access to download wiki code' do
expect { subject }.not_to raise_error
end
+
+ context 'when the wiki repository does not exist' do
+ it 'returns not found' do
+ wiki_repo = project.wiki.repository
+ FileUtils.rm_rf(wiki_repo.path)
+
+ # Sanity check for rm_rf
+ expect(wiki_repo.exists?).to eq(false)
+
+ expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'A repository for this project does not exist yet.')
+ end
+ end
end
context 'when wiki feature is disabled' do
diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
index cbc7ce1c1b0..c50e73cecfc 100644
--- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
@@ -84,4 +84,30 @@ describe Gitlab::GitalyClient::RepositoryService do
expect(client.has_local_branches?).to be(true)
end
end
+
+ describe '#rebase_in_progress?' do
+ let(:rebase_id) { 1 }
+
+ it 'sends a repository_rebase_in_progress message' do
+ expect_any_instance_of(Gitaly::RepositoryService::Stub)
+ .to receive(:is_rebase_in_progress)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(double(in_progress: true))
+
+ client.rebase_in_progress?(rebase_id)
+ end
+ end
+
+ describe '#squash_in_progress?' do
+ let(:squash_id) { 1 }
+
+ it 'sends a repository_squash_in_progress message' do
+ expect_any_instance_of(Gitaly::RepositoryService::Stub)
+ .to receive(:is_squash_in_progress)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(double(in_progress: true))
+
+ client.squash_in_progress?(squash_id)
+ end
+ end
end
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index 60a134be939..b24c9882c0c 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -3,19 +3,30 @@ require 'spec_helper'
describe Gitlab::Middleware::Go do
let(:app) { double(:app) }
let(:middleware) { described_class.new(app) }
+ let(:env) do
+ {
+ 'rack.input' => '',
+ 'REQUEST_METHOD' => 'GET'
+ }
+ end
describe '#call' do
describe 'when go-get=0' do
+ before do
+ env['QUERY_STRING'] = 'go-get=0'
+ end
+
it 'skips go-import generation' do
- env = { 'rack.input' => '',
- 'QUERY_STRING' => 'go-get=0' }
expect(app).to receive(:call).with(env).and_return('no-go')
middleware.call(env)
end
end
describe 'when go-get=1' do
- let(:current_user) { nil }
+ before do
+ env['QUERY_STRING'] = 'go-get=1'
+ env['PATH_INFO'] = "/#{path}"
+ end
shared_examples 'go-get=1' do |enabled_protocol:|
context 'with simple 2-segment project path' do
@@ -54,21 +65,75 @@ describe Gitlab::Middleware::Go do
project.update_attribute(:visibility_level, Project::PRIVATE)
end
- context 'with access to the project' do
+ shared_examples 'unauthorized' do
+ it 'returns the 2-segment group path' do
+ expect_response_with_path(go, enabled_protocol, group.full_path)
+ end
+ end
+
+ context 'when not authenticated' do
+ it_behaves_like 'unauthorized'
+ end
+
+ context 'when authenticated' do
let(:current_user) { project.creator }
before do
project.team.add_master(current_user)
end
- it 'returns the full project path' do
- expect_response_with_path(go, enabled_protocol, project.full_path)
+ shared_examples 'authenticated' do
+ context 'with access to the project' do
+ it 'returns the full project path' do
+ expect_response_with_path(go, enabled_protocol, project.full_path)
+ end
+ end
+
+ context 'without access to the project' do
+ before do
+ project.team.find_member(current_user).destroy
+ end
+
+ it_behaves_like 'unauthorized'
+ end
end
- end
- context 'without access to the project' do
- it 'returns the 2-segment group path' do
- expect_response_with_path(go, enabled_protocol, group.full_path)
+ context 'using warden' do
+ before do
+ env['warden'] = double(authenticate: current_user)
+ end
+
+ context 'when active' do
+ it_behaves_like 'authenticated'
+ end
+
+ context 'when blocked' do
+ before do
+ current_user.block!
+ end
+
+ it_behaves_like 'unauthorized'
+ end
+ end
+
+ context 'using a personal access token' do
+ let(:personal_access_token) { create(:personal_access_token, user: current_user) }
+
+ before do
+ env['HTTP_PRIVATE_TOKEN'] = personal_access_token.token
+ end
+
+ context 'with api scope' do
+ it_behaves_like 'authenticated'
+ end
+
+ context 'with read_user scope' do
+ before do
+ personal_access_token.update_attribute(:scopes, [:read_user])
+ end
+
+ it_behaves_like 'unauthorized'
+ end
end
end
end
@@ -138,12 +203,6 @@ describe Gitlab::Middleware::Go do
end
def go
- env = {
- 'rack.input' => '',
- 'QUERY_STRING' => 'go-get=1',
- 'PATH_INFO' => "/#{path}",
- 'warden' => double(authenticate: current_user)
- }
middleware.call(env)
end
diff --git a/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb b/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb
index 2b488101496..c95719eff1d 100644
--- a/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb
+++ b/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb
@@ -24,7 +24,7 @@ describe Gitlab::Prometheus::Queries::MatchedMetricsQuery do
context 'with one group where two metrics is found' do
before do
- allow(metric_group_class).to receive(:all).and_return([simple_metric_group])
+ allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group])
allow(client).to receive(:label_values).and_return(metric_names)
end
@@ -70,7 +70,7 @@ describe Gitlab::Prometheus::Queries::MatchedMetricsQuery do
context 'with one group where only one metric is found' do
before do
- allow(metric_group_class).to receive(:all).and_return([simple_metric_group])
+ allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group])
allow(client).to receive(:label_values).and_return('metric_a')
end
@@ -99,7 +99,7 @@ describe Gitlab::Prometheus::Queries::MatchedMetricsQuery do
let(:second_metric_group) { simple_metric_group(name: 'nameb', metrics: simple_metrics(added_metric_name: 'metric_c')) }
before do
- allow(metric_group_class).to receive(:all).and_return([simple_metric_group, second_metric_group])
+ allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group, second_metric_group])
allow(client).to receive(:label_values).and_return('metric_c')
end
diff --git a/spec/lib/gitlab/prometheus_client_spec.rb b/spec/lib/gitlab/prometheus_client_spec.rb
index 5d86007f71f..4c3b8deefb9 100644
--- a/spec/lib/gitlab/prometheus_client_spec.rb
+++ b/spec/lib/gitlab/prometheus_client_spec.rb
@@ -19,41 +19,41 @@ describe Gitlab::PrometheusClient do
# - execute_query: A query call
shared_examples 'failure response' do
context 'when request returns 400 with an error message' do
- it 'raises a Gitlab::PrometheusError error' do
+ it 'raises a Gitlab::PrometheusClient::Error error' do
req_stub = stub_prometheus_request(query_url, status: 400, body: { error: 'bar!' })
expect { execute_query }
- .to raise_error(Gitlab::PrometheusError, 'bar!')
+ .to raise_error(Gitlab::PrometheusClient::Error, 'bar!')
expect(req_stub).to have_been_requested
end
end
context 'when request returns 400 without an error message' do
- it 'raises a Gitlab::PrometheusError error' do
+ it 'raises a Gitlab::PrometheusClient::Error error' do
req_stub = stub_prometheus_request(query_url, status: 400)
expect { execute_query }
- .to raise_error(Gitlab::PrometheusError, 'Bad data received')
+ .to raise_error(Gitlab::PrometheusClient::Error, 'Bad data received')
expect(req_stub).to have_been_requested
end
end
context 'when request returns 500' do
- it 'raises a Gitlab::PrometheusError error' do
+ it 'raises a Gitlab::PrometheusClient::Error error' do
req_stub = stub_prometheus_request(query_url, status: 500, body: { message: 'FAIL!' })
expect { execute_query }
- .to raise_error(Gitlab::PrometheusError, '500 - {"message":"FAIL!"}')
+ .to raise_error(Gitlab::PrometheusClient::Error, '500 - {"message":"FAIL!"}')
expect(req_stub).to have_been_requested
end
end
context 'when request returns non json data' do
- it 'raises a Gitlab::PrometheusError error' do
+ it 'raises a Gitlab::PrometheusClient::Error error' do
req_stub = stub_prometheus_request(query_url, status: 200, body: 'not json')
expect { execute_query }
- .to raise_error(Gitlab::PrometheusError, 'Parsing response failed')
+ .to raise_error(Gitlab::PrometheusClient::Error, 'Parsing response failed')
expect(req_stub).to have_been_requested
end
end
@@ -65,27 +65,27 @@ describe Gitlab::PrometheusClient do
subject { described_class.new(RestClient::Resource.new(prometheus_url)) }
context 'exceptions are raised' do
- it 'raises a Gitlab::PrometheusError error when a SocketError is rescued' do
+ it 'raises a Gitlab::PrometheusClient::Error error when a SocketError is rescued' do
req_stub = stub_prometheus_request_with_exception(prometheus_url, SocketError)
expect { subject.send(:get, '/', {}) }
- .to raise_error(Gitlab::PrometheusError, "Can't connect to #{prometheus_url}")
+ .to raise_error(Gitlab::PrometheusClient::Error, "Can't connect to #{prometheus_url}")
expect(req_stub).to have_been_requested
end
- it 'raises a Gitlab::PrometheusError error when a SSLError is rescued' do
+ it 'raises a Gitlab::PrometheusClient::Error error when a SSLError is rescued' do
req_stub = stub_prometheus_request_with_exception(prometheus_url, OpenSSL::SSL::SSLError)
expect { subject.send(:get, '/', {}) }
- .to raise_error(Gitlab::PrometheusError, "#{prometheus_url} contains invalid SSL data")
+ .to raise_error(Gitlab::PrometheusClient::Error, "#{prometheus_url} contains invalid SSL data")
expect(req_stub).to have_been_requested
end
- it 'raises a Gitlab::PrometheusError error when a RestClient::Exception is rescued' do
+ it 'raises a Gitlab::PrometheusClient::Error error when a RestClient::Exception is rescued' do
req_stub = stub_prometheus_request_with_exception(prometheus_url, RestClient::Exception)
expect { subject.send(:get, '/', {}) }
- .to raise_error(Gitlab::PrometheusError, "Network connection error")
+ .to raise_error(Gitlab::PrometheusClient::Error, "Network connection error")
expect(req_stub).to have_been_requested
end
end
diff --git a/spec/lib/gitlab/quick_actions/command_definition_spec.rb b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
index f44a562dc63..b03c1e23ca3 100644
--- a/spec/lib/gitlab/quick_actions/command_definition_spec.rb
+++ b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
@@ -40,7 +40,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
describe "#available?" do
- let(:opts) { { go: false } }
+ let(:opts) { OpenStruct.new(go: false) }
context "when the command has a condition block" do
before do
@@ -78,7 +78,7 @@ describe Gitlab::QuickActions::CommandDefinition do
it "doesn't execute the command" do
expect(context).not_to receive(:instance_exec)
- subject.execute(context, {}, nil)
+ subject.execute(context, nil)
expect(context.run).to be false
end
@@ -95,7 +95,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
it "doesn't execute the command" do
- subject.execute(context, {}, nil)
+ subject.execute(context, nil)
expect(context.run).to be false
end
@@ -109,7 +109,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is provided an argument" do
it "executes the command" do
- subject.execute(context, {}, true)
+ subject.execute(context, true)
expect(context.run).to be true
end
@@ -117,7 +117,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is not provided an argument" do
it "executes the command" do
- subject.execute(context, {}, nil)
+ subject.execute(context, nil)
expect(context.run).to be true
end
@@ -131,7 +131,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is provided an argument" do
it "executes the command" do
- subject.execute(context, {}, true)
+ subject.execute(context, true)
expect(context.run).to be true
end
@@ -139,7 +139,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is not provided an argument" do
it "doesn't execute the command" do
- subject.execute(context, {}, nil)
+ subject.execute(context, nil)
expect(context.run).to be false
end
@@ -153,7 +153,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is provided an argument" do
it "executes the command" do
- subject.execute(context, {}, true)
+ subject.execute(context, true)
expect(context.run).to be true
end
@@ -161,7 +161,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is not provided an argument" do
it "executes the command" do
- subject.execute(context, {}, nil)
+ subject.execute(context, nil)
expect(context.run).to be true
end
@@ -175,7 +175,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
it 'executes the command passing the parsed param' do
- subject.execute(context, {}, 'something ')
+ subject.execute(context, 'something ')
expect(context.received_arg).to eq('something')
end
@@ -192,7 +192,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
it 'returns nil' do
- result = subject.explain({}, {}, nil)
+ result = subject.explain({}, nil)
expect(result).to be_nil
end
@@ -204,7 +204,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
it 'returns this static string' do
- result = subject.explain({}, {}, nil)
+ result = subject.explain({}, nil)
expect(result).to eq 'Explanation'
end
@@ -216,7 +216,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
it 'invokes the proc' do
- result = subject.explain({}, {}, 'explanation')
+ result = subject.explain({}, 'explanation')
expect(result).to eq 'Dynamic explanation'
end
diff --git a/spec/lib/gitlab/quick_actions/dsl_spec.rb b/spec/lib/gitlab/quick_actions/dsl_spec.rb
index ff59dc48bcb..067a30fd7e2 100644
--- a/spec/lib/gitlab/quick_actions/dsl_spec.rb
+++ b/spec/lib/gitlab/quick_actions/dsl_spec.rb
@@ -76,7 +76,7 @@ describe Gitlab::QuickActions::Dsl do
expect(dynamic_description_def.name).to eq(:dynamic_description)
expect(dynamic_description_def.aliases).to eq([])
- expect(dynamic_description_def.to_h(noteable: 'issue')[:description]).to eq('A dynamic description for ISSUE')
+ expect(dynamic_description_def.to_h(OpenStruct.new(noteable: 'issue'))[:description]).to eq('A dynamic description for ISSUE')
expect(dynamic_description_def.explanation).to eq('')
expect(dynamic_description_def.params).to eq(['The first argument', 'The second argument'])
expect(dynamic_description_def.condition_block).to be_nil
diff --git a/spec/lib/gitlab/sql/pattern_spec.rb b/spec/lib/gitlab/sql/pattern_spec.rb
index ef51e3cc8df..5b5052de372 100644
--- a/spec/lib/gitlab/sql/pattern_spec.rb
+++ b/spec/lib/gitlab/sql/pattern_spec.rb
@@ -154,6 +154,12 @@ describe Gitlab::SQL::Pattern do
it 'returns a single equality condition' do
expect(fuzzy_arel_match.to_sql).to match(/title.*I?LIKE 'fo'/)
end
+
+ it 'uses LOWER instead of ILIKE when LOWER is enabled' do
+ rel = Issue.fuzzy_arel_match(:title, query, lower_exact_match: true)
+
+ expect(rel.to_sql).to match(/LOWER\(.*title.*\).*=.*'fo'/)
+ end
end
context 'with two words both equal to 3 chars' do
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index f65e41dfea3..db9d9158b29 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -115,6 +115,9 @@ describe GoogleApi::CloudPlatform::Client do
"initial_node_count": cluster_size,
"node_config": {
"machine_type": machine_type
+ },
+ "legacy_abac": {
+ "enabled": true
}
}
} )
diff --git a/spec/mailers/emails/pages_domains_spec.rb b/spec/mailers/emails/pages_domains_spec.rb
new file mode 100644
index 00000000000..fe428ea657d
--- /dev/null
+++ b/spec/mailers/emails/pages_domains_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+require 'email_spec'
+
+describe Emails::PagesDomains do
+ include EmailSpec::Matchers
+ include_context 'gitlab email notification'
+
+ set(:project) { create(:project) }
+ set(:domain) { create(:pages_domain, project: project) }
+ set(:user) { project.owner }
+
+ shared_examples 'a pages domain email' do
+ it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'it should not have Gmail Actions links'
+ it_behaves_like 'a user cannot unsubscribe through footer link'
+
+ it 'has the expected content' do
+ aggregate_failures do
+ is_expected.to have_subject(email_subject)
+ is_expected.to have_body_text(project.human_name)
+ is_expected.to have_body_text(domain.domain)
+ is_expected.to have_body_text domain.url
+ is_expected.to have_body_text project_pages_domain_url(project, domain)
+ is_expected.to have_body_text help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+ end
+ end
+ end
+
+ describe '#pages_domain_enabled_email' do
+ let(:email_subject) { "#{project.path} | GitLab Pages domain '#{domain.domain}' has been enabled" }
+
+ subject { Notify.pages_domain_enabled_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it { is_expected.to have_body_text 'has been enabled' }
+ end
+
+ describe '#pages_domain_disabled_email' do
+ let(:email_subject) { "#{project.path} | GitLab Pages domain '#{domain.domain}' has been disabled" }
+
+ subject { Notify.pages_domain_disabled_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it { is_expected.to have_body_text 'has been disabled' }
+ end
+
+ describe '#pages_domain_verification_succeeded_email' do
+ let(:email_subject) { "#{project.path} | Verification succeeded for GitLab Pages domain '#{domain.domain}'" }
+
+ subject { Notify.pages_domain_verification_succeeded_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it { is_expected.to have_body_text 'successfully verified' }
+ end
+
+ describe '#pages_domain_verification_failed_email' do
+ let(:email_subject) { "#{project.path} | ACTION REQUIRED: Verification failed for GitLab Pages domain '#{domain.domain}'" }
+
+ subject { Notify.pages_domain_verification_failed_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it 'says verification has failed and when the domain is enabled until' do
+ is_expected.to have_body_text 'Verification has failed'
+ is_expected.to have_body_text domain.enabled_until.strftime('%F %T')
+ end
+ end
+end
diff --git a/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb b/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb
new file mode 100644
index 00000000000..afcaefa0591
--- /dev/null
+++ b/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180216121030_enqueue_verify_pages_domain_workers')
+
+describe EnqueueVerifyPagesDomainWorkers, :sidekiq, :migration do
+ around do |example|
+ Sidekiq::Testing.fake! do
+ example.run
+ end
+ end
+
+ describe '#up' do
+ it 'enqueues a verification worker for every domain' do
+ domains = 1.upto(3).map { |i| PagesDomain.create!(domain: "my#{i}.domain.com") }
+
+ expect { migrate! }.to change(PagesDomainVerificationWorker.jobs, :size).by(3)
+
+ enqueued_ids = PagesDomainVerificationWorker.jobs.map { |job| job['args'] }
+ expected_ids = domains.map { |domain| [domain.id] }
+
+ expect(enqueued_ids).to match_array(expected_ids)
+ end
+ end
+end
diff --git a/spec/migrations/track_untracked_uploads_spec.rb b/spec/migrations/track_untracked_uploads_spec.rb
index fe4d5b8a279..2fccfb3f12c 100644
--- a/spec/migrations/track_untracked_uploads_spec.rb
+++ b/spec/migrations/track_untracked_uploads_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20171103140253_track_untracked_uploads')
describe TrackUntrackedUploads, :migration, :sidekiq do
- include TrackUntrackedUploadsHelpers
+ include MigrationsHelpers::TrackUntrackedUploadsHelpers
it 'correctly schedules the follow-up background migration' do
Sidekiq::Testing.fake! do
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
index 38fb98d4f50..cd175dba6da 100644
--- a/spec/models/ability_spec.rb
+++ b/spec/models/ability_spec.rb
@@ -204,6 +204,78 @@ describe Ability do
end
end
+ describe '.merge_requests_readable_by_user' do
+ context 'with an admin' do
+ it 'returns all merge requests' do
+ user = build(:user, admin: true)
+ merge_request = build(:merge_request)
+
+ expect(described_class.merge_requests_readable_by_user([merge_request], user))
+ .to eq([merge_request])
+ end
+ end
+
+ context 'without a user' do
+ it 'returns merge_requests that are publicly visible' do
+ hidden_merge_request = build(:merge_request)
+ visible_merge_request = build(:merge_request, source_project: build(:project, :public))
+
+ merge_requests = described_class
+ .merge_requests_readable_by_user([hidden_merge_request, visible_merge_request])
+
+ expect(merge_requests).to eq([visible_merge_request])
+ end
+ end
+
+ context 'with a user' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:cross_project_merge_request) do
+ create(:merge_request, source_project: create(:project, :public))
+ end
+ let(:other_merge_request) { create(:merge_request) }
+ let(:all_merge_requests) do
+ [merge_request, cross_project_merge_request, other_merge_request]
+ end
+
+ subject(:readable_merge_requests) do
+ described_class.merge_requests_readable_by_user(all_merge_requests, user)
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns projects visible to the user' do
+ expect(readable_merge_requests).to contain_exactly(merge_request, cross_project_merge_request)
+ end
+
+ context 'when a user cannot read cross project and a filter is passed' do
+ before do
+ allow(described_class).to receive(:allowed?).and_call_original
+ expect(described_class).to receive(:allowed?).with(user, :read_cross_project) { false }
+ end
+
+ subject(:readable_merge_requests) do
+ read_cross_project_filter = -> (merge_requests) do
+ merge_requests.select { |mr| mr.source_project == project }
+ end
+ described_class.merge_requests_readable_by_user(
+ all_merge_requests, user,
+ filters: { read_cross_project: read_cross_project_filter }
+ )
+ end
+
+ it 'returns only MRs of the specified project without checking access on others' do
+ expect(described_class).not_to receive(:allowed?).with(user, :read_merge_request, cross_project_merge_request)
+
+ expect(readable_merge_requests).to contain_exactly(merge_request)
+ end
+ end
+ end
+ end
+
describe '.issues_readable_by_user' do
context 'with an admin user' do
it 'returns all given issues' do
@@ -250,6 +322,29 @@ describe Ability do
expect(issues).to eq([visible_issue])
end
end
+
+ context 'when the user cannot read cross project' do
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue) }
+ let(:other_project_issue) { create(:issue) }
+ let(:project) { issue.project }
+
+ before do
+ project.add_developer(user)
+
+ allow(described_class).to receive(:allowed?).and_call_original
+ allow(described_class).to receive(:allowed?).with(user, :read_cross_project, any_args) { false }
+ end
+
+ it 'excludes issues from other projects whithout checking separatly when passing a scope' do
+ expect(described_class).not_to receive(:allowed?).with(user, :read_issue, other_project_issue)
+
+ filters = { read_cross_project: -> (issues) { issues.where(project: project) } }
+ result = described_class.issues_readable_by_user(Issue.all, user, filters: filters)
+
+ expect(result).to contain_exactly(issue)
+ end
+ end
end
describe '.project_disabled_features_rules' do
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 2b6b6a61182..c27313ed88b 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -277,7 +277,7 @@ describe Ci::Build do
allow_any_instance_of(Project).to receive(:jobs_cache_index).and_return(1)
end
- it { is_expected.to be_an(Array).and all(include(key: "key_1")) }
+ it { is_expected.to be_an(Array).and all(include(key: "key-1")) }
end
context 'when project does not have jobs_cache_index' do
diff --git a/spec/models/concerns/protected_ref_access_spec.rb b/spec/models/concerns/protected_ref_access_spec.rb
new file mode 100644
index 00000000000..a62ca391e25
--- /dev/null
+++ b/spec/models/concerns/protected_ref_access_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe ProtectedRefAccess do
+ subject(:protected_ref_access) do
+ create(:protected_branch, :masters_can_push).push_access_levels.first
+ end
+
+ let(:project) { protected_ref_access.project }
+
+ describe '#check_access' do
+ it 'is always true for admins' do
+ admin = create(:admin)
+
+ expect(protected_ref_access.check_access(admin)).to be_truthy
+ end
+
+ it 'is true for masters' do
+ master = create(:user)
+ project.add_master(master)
+
+ expect(protected_ref_access.check_access(master)).to be_truthy
+ end
+
+ it 'is for developers of the project' do
+ developer = create(:user)
+ project.add_developer(developer)
+
+ expect(protected_ref_access.check_access(developer)).to be_falsy
+ end
+ end
+end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index f5c9f551e65..feed7968f09 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -221,27 +221,55 @@ describe Issue do
end
describe '#referenced_merge_requests' do
- it 'returns the referenced merge requests' do
- project = create(:project, :public)
-
- mr1 = create(:merge_request,
- source_project: project,
- source_branch: 'master',
- target_branch: 'feature')
+ let(:project) { create(:project, :public) }
+ let(:issue) do
+ create(:issue, description: merge_request.to_reference, project: project)
+ end
+ let!(:merge_request) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: 'master',
+ target_branch: 'feature')
+ end
+ it 'returns the referenced merge requests' do
mr2 = create(:merge_request,
source_project: project,
source_branch: 'feature',
target_branch: 'master')
- issue = create(:issue, description: mr1.to_reference, project: project)
-
create(:note_on_issue,
noteable: issue,
note: mr2.to_reference,
project_id: project.id)
- expect(issue.referenced_merge_requests).to eq([mr1, mr2])
+ expect(issue.referenced_merge_requests).to eq([merge_request, mr2])
+ end
+
+ it 'returns cross project referenced merge requests' do
+ other_project = create(:project, :public)
+ cross_project_merge_request = create(:merge_request, source_project: other_project)
+ create(:note_on_issue,
+ noteable: issue,
+ note: cross_project_merge_request.to_reference(issue.project),
+ project_id: issue.project.id)
+
+ expect(issue.referenced_merge_requests).to eq([merge_request, cross_project_merge_request])
+ end
+
+ it 'excludes cross project references if the user cannot read cross project' do
+ user = create(:user)
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ other_project = create(:project, :public)
+ cross_project_merge_request = create(:merge_request, source_project: other_project)
+ create(:note_on_issue,
+ noteable: issue,
+ note: cross_project_merge_request.to_reference(issue.project),
+ project_id: issue.project.id)
+
+ expect(issue.referenced_merge_requests(user)).to eq([merge_request])
end
end
@@ -309,7 +337,7 @@ describe Issue do
end
describe '#related_branches' do
- let(:user) { build(:admin) }
+ let(:user) { create(:admin) }
before do
allow(subject.project.repository).to receive(:branch_names)
diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb
new file mode 100644
index 00000000000..eda0e1da835
--- /dev/null
+++ b/spec/models/notification_recipient_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe NotificationRecipient do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+ let(:target) { create(:issue, project: project) }
+
+ subject(:recipient) { described_class.new(user, :watch, target: target, project: project) }
+
+ it 'denies access to a target when cross project access is denied' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(false)
+
+ expect(recipient.has_access?).to be_falsy
+ end
+end
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index 9d12f96c642..95713d8b85b 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -1,6 +1,10 @@
require 'spec_helper'
describe PagesDomain do
+ using RSpec::Parameterized::TableSyntax
+
+ subject(:pages_domain) { described_class.new }
+
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
@@ -64,19 +68,51 @@ describe PagesDomain do
end
end
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:verification_code) }
+ end
+
+ describe '#verification_code' do
+ subject { pages_domain.verification_code }
+
+ it 'is set automatically with 128 bits of SecureRandom data' do
+ expect(SecureRandom).to receive(:hex).with(16) { 'verification code' }
+
+ is_expected.to eq('verification code')
+ end
+ end
+
+ describe '#keyed_verification_code' do
+ subject { pages_domain.keyed_verification_code }
+
+ it { is_expected.to eq("gitlab-pages-verification-code=#{pages_domain.verification_code}") }
+ end
+
+ describe '#verification_domain' do
+ subject { pages_domain.verification_domain }
+
+ it { is_expected.to be_nil }
+
+ it 'is a well-known subdomain if the domain is present' do
+ pages_domain.domain = 'example.com'
+
+ is_expected.to eq('_gitlab-pages-verification-code.example.com')
+ end
+ end
+
describe '#url' do
subject { domain.url }
context 'without the certificate' do
let(:domain) { build(:pages_domain, certificate: '') }
- it { is_expected.to eq('http://my.domain.com') }
+ it { is_expected.to eq("http://#{domain.domain}") }
end
context 'with a certificate' do
let(:domain) { build(:pages_domain, :with_certificate) }
- it { is_expected.to eq('https://my.domain.com') }
+ it { is_expected.to eq("https://#{domain.domain}") }
end
end
@@ -154,4 +190,108 @@ describe PagesDomain do
# We test only existence of output, since the output is long
it { is_expected.not_to be_empty }
end
+
+ describe '#update_daemon' do
+ it 'runs when the domain is created' do
+ domain = build(:pages_domain)
+
+ expect(domain).to receive(:update_daemon)
+
+ domain.save!
+ end
+
+ it 'runs when the domain is destroyed' do
+ domain = create(:pages_domain)
+
+ expect(domain).to receive(:update_daemon)
+
+ domain.destroy!
+ end
+
+ it 'delegates to Projects::UpdatePagesConfigurationService' do
+ service = instance_double('Projects::UpdatePagesConfigurationService')
+ expect(Projects::UpdatePagesConfigurationService).to receive(:new) { service }
+ expect(service).to receive(:execute)
+
+ create(:pages_domain)
+ end
+
+ context 'configuration updates when attributes change' do
+ set(:project1) { create(:project) }
+ set(:project2) { create(:project) }
+ set(:domain) { create(:pages_domain) }
+
+ where(:attribute, :old_value, :new_value, :update_expected) do
+ now = Time.now
+ future = now + 1.day
+
+ :project | nil | :project1 | true
+ :project | :project1 | :project1 | false
+ :project | :project1 | :project2 | true
+ :project | :project1 | nil | true
+
+ # domain can't be set to nil
+ :domain | 'a.com' | 'a.com' | false
+ :domain | 'a.com' | 'b.com' | true
+
+ # verification_code can't be set to nil
+ :verification_code | 'foo' | 'foo' | false
+ :verification_code | 'foo' | 'bar' | false
+
+ :verified_at | nil | now | false
+ :verified_at | now | now | false
+ :verified_at | now | future | false
+ :verified_at | now | nil | false
+
+ :enabled_until | nil | now | true
+ :enabled_until | now | now | false
+ :enabled_until | now | future | false
+ :enabled_until | now | nil | true
+ end
+
+ with_them do
+ it 'runs if a relevant attribute has changed' do
+ a = old_value.is_a?(Symbol) ? send(old_value) : old_value
+ b = new_value.is_a?(Symbol) ? send(new_value) : new_value
+
+ domain.update!(attribute => a)
+
+ if update_expected
+ expect(domain).to receive(:update_daemon)
+ else
+ expect(domain).not_to receive(:update_daemon)
+ end
+
+ domain.update!(attribute => b)
+ end
+ end
+
+ context 'TLS configuration' do
+ set(:domain_with_tls) { create(:pages_domain, :with_key, :with_certificate) }
+
+ let(:cert1) { domain_with_tls.certificate }
+ let(:cert2) { cert1 + ' ' }
+ let(:key1) { domain_with_tls.key }
+ let(:key2) { key1 + ' ' }
+
+ it 'updates when added' do
+ expect(domain).to receive(:update_daemon)
+
+ domain.update!(key: key1, certificate: cert1)
+ end
+
+ it 'updates when changed' do
+ expect(domain_with_tls).to receive(:update_daemon)
+
+ domain_with_tls.update!(key: key2, certificate: cert2)
+ end
+
+ it 'updates when removed' do
+ expect(domain_with_tls).to receive(:update_daemon)
+
+ domain_with_tls.update!(key: nil, certificate: nil)
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb
index ed17e019d42..6693e5783a5 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/project_services/prometheus_service_spec.rb
@@ -223,8 +223,8 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
context 'with cluster for all environments without prometheus installed' do
context 'without environment supplied' do
- it 'raises PrometheusError because cluster was not found' do
- expect { service.client }.to raise_error(Gitlab::PrometheusError, /couldn't find cluster with Prometheus installed/)
+ it 'raises PrometheusClient::Error because cluster was not found' do
+ expect { service.client }.to raise_error(Gitlab::PrometheusClient::Error, /couldn't find cluster with Prometheus installed/)
end
end
@@ -242,8 +242,8 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
context 'with prod environment supplied' do
let!(:environment) { create(:environment, project: project, name: 'prod') }
- it 'raises PrometheusError because cluster was not found' do
- expect { service.client }.to raise_error(Gitlab::PrometheusError, /couldn't find cluster with Prometheus installed/)
+ it 'raises PrometheusClient::Error because cluster was not found' do
+ expect { service.client }.to raise_error(Gitlab::PrometheusClient::Error, /couldn't find cluster with Prometheus installed/)
end
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index ee04d74d848..56c2d7b953e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1473,6 +1473,13 @@ describe Project do
expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end
+
+ it 'returns false when the repo is not empty' do
+ project.add_master(user)
+ expect(project).to receive(:empty_repo?).and_return(false)
+
+ expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
+ end
end
describe '#container_registry_url' do
diff --git a/spec/policies/issuable_policy_spec.rb b/spec/policies/issuable_policy_spec.rb
index 2cf669e8191..d1bf98995e7 100644
--- a/spec/policies/issuable_policy_spec.rb
+++ b/spec/policies/issuable_policy_spec.rb
@@ -1,12 +1,14 @@
require 'spec_helper'
describe IssuablePolicy, models: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project) }
+ let(:policies) { described_class.new(user, issue) }
+
describe '#rules' do
context 'when discussion is locked for the issuable' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project, discussion_locked: true) }
- let(:policies) { described_class.new(user, issue) }
context 'when the user is not a project member' do
it 'can not create a note' do
diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb
index a4af9361ea6..793b724bfca 100644
--- a/spec/policies/issue_policy_spec.rb
+++ b/spec/policies/issue_policy_spec.rb
@@ -30,41 +30,41 @@ describe IssuePolicy do
end
it 'does not allow non-members to read issues' do
- expect(permissions(non_member, issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(non_member, issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(non_member, issue)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(non_member, issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows guests to read issues' do
- expect(permissions(guest, issue)).to be_allowed(:read_issue)
+ expect(permissions(guest, issue)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(guest, issue)).to be_disallowed(:update_issue, :admin_issue)
- expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(guest, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
it 'allows reporters to read, update, and admin issues' do
- expect(permissions(reporter, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporters from group links to read, update, and admin issues' do
- expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue authors to read and update their issues' do
- expect(permissions(author, issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(author, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(author, issue)).to be_disallowed(:admin_issue)
- expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(author, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
it 'allows issue assignees to read and update their issues' do
- expect(permissions(assignee, issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(assignee, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(assignee, issue)).to be_disallowed(:admin_issue)
- expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(assignee, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
@@ -73,37 +73,37 @@ describe IssuePolicy do
let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) }
it 'does not allow non-members to read confidential issues' do
- expect(permissions(non_member, confidential_issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(non_member, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(non_member, confidential_issue)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(non_member, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'does not allow guests to read confidential issues' do
- expect(permissions(guest, confidential_issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(guest, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(guest, confidential_issue)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(guest, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporters to read, update, and admin confidential issues' do
- expect(permissions(reporter, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter, confidential_issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporters from group links to read, update, and admin confidential issues' do
- expect(permissions(reporter_from_group_link, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue authors to read and update their confidential issues' do
- expect(permissions(author, confidential_issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(author, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(author, confidential_issue)).to be_disallowed(:admin_issue)
- expect(permissions(author, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(author, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue assignees to read and update their confidential issues' do
- expect(permissions(assignee, confidential_issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(assignee, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(assignee, confidential_issue)).to be_disallowed(:admin_issue)
- expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
end
end
@@ -123,36 +123,36 @@ describe IssuePolicy do
end
it 'allows guests to read issues' do
- expect(permissions(guest, issue)).to be_allowed(:read_issue)
+ expect(permissions(guest, issue)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(guest, issue)).to be_disallowed(:update_issue, :admin_issue)
- expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(guest, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
it 'allows reporters to read, update, and admin issues' do
- expect(permissions(reporter, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporters from group links to read, update, and admin issues' do
- expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue authors to read and update their issues' do
- expect(permissions(author, issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(author, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(author, issue)).to be_disallowed(:admin_issue)
- expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(author, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
it 'allows issue assignees to read and update their issues' do
- expect(permissions(assignee, issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(assignee, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(assignee, issue)).to be_disallowed(:admin_issue)
- expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(assignee, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
@@ -161,32 +161,32 @@ describe IssuePolicy do
let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) }
it 'does not allow guests to read confidential issues' do
- expect(permissions(guest, confidential_issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(guest, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(guest, confidential_issue)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(guest, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporters to read, update, and admin confidential issues' do
- expect(permissions(reporter, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter, confidential_issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporter from group links to read, update, and admin confidential issues' do
- expect(permissions(reporter_from_group_link, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue authors to read and update their confidential issues' do
- expect(permissions(author, confidential_issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(author, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(author, confidential_issue)).to be_disallowed(:admin_issue)
- expect(permissions(author, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(author, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue assignees to read and update their confidential issues' do
- expect(permissions(assignee, confidential_issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(assignee, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(assignee, confidential_issue)).to be_disallowed(:admin_issue)
- expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
end
end
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
new file mode 100644
index 00000000000..f8c93d91ec5
--- /dev/null
+++ b/spec/presenters/project_presenter_spec.rb
@@ -0,0 +1,397 @@
+require 'spec_helper'
+
+describe ProjectPresenter do
+ let(:user) { create(:user) }
+
+ describe '#license_short_name' do
+ let(:project) { create(:project) }
+ let(:presenter) { described_class.new(project, current_user: user) }
+
+ context 'when project.repository has a license_key' do
+ it 'returns the nickname of the license if present' do
+ allow(project.repository).to receive(:license_key).and_return('agpl-3.0')
+
+ expect(presenter.license_short_name).to eq('GNU AGPLv3')
+ end
+
+ it 'returns the name of the license if nickname is not present' do
+ allow(project.repository).to receive(:license_key).and_return('mit')
+
+ expect(presenter.license_short_name).to eq('MIT License')
+ end
+ end
+
+ context 'when project.repository has no license_key but a license_blob' do
+ it 'returns LICENSE' do
+ allow(project.repository).to receive(:license_key).and_return(nil)
+
+ expect(presenter.license_short_name).to eq('LICENSE')
+ end
+ end
+ end
+
+ describe '#default_view' do
+ let(:presenter) { described_class.new(project, current_user: user) }
+
+ context 'user not signed in' do
+ let(:user) { nil }
+
+ context 'when repository is empty' do
+ let(:project) { create(:project_empty_repo, :public) }
+
+ it 'returns activity if user has repository access' do
+ allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(true)
+
+ expect(presenter.default_view).to eq('activity')
+ end
+
+ it 'returns activity if user does not have repository access' do
+ allow(project).to receive(:can?).with(nil, :download_code, project).and_return(false)
+
+ expect(presenter.default_view).to eq('activity')
+ end
+ end
+
+ context 'when repository is not empty' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it 'returns files and readme if user has repository access' do
+ allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(true)
+
+ expect(presenter.default_view).to eq('files')
+ end
+
+ it 'returns activity if user does not have repository access' do
+ allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(false)
+
+ expect(presenter.default_view).to eq('activity')
+ end
+ end
+ end
+
+ context 'user signed in' do
+ let(:user) { create(:user, :readme) }
+ let(:project) { create(:project, :public, :repository) }
+
+ context 'when the user is allowed to see the code' do
+ it 'returns the project view' do
+ allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(true)
+
+ expect(presenter.default_view).to eq('readme')
+ end
+ end
+
+ context 'with wikis enabled and the right policy for the user' do
+ before do
+ project.project_feature.update_attribute(:issues_access_level, 0)
+ allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ end
+
+ it 'returns wiki if the user has the right policy' do
+ allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(true)
+
+ expect(presenter.default_view).to eq('wiki')
+ end
+
+ it 'returns customize_workflow if the user does not have the right policy' do
+ allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(presenter.default_view).to eq('customize_workflow')
+ end
+ end
+
+ context 'with issues as a feature available' do
+ it 'return issues' do
+ allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(presenter.default_view).to eq('projects/issues/issues')
+ end
+ end
+
+ context 'with no activity, no wikies and no issues' do
+ it 'returns customize_workflow as default' do
+ project.project_feature.update_attribute(:issues_access_level, 0)
+ allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(presenter.default_view).to eq('customize_workflow')
+ end
+ end
+ end
+ end
+
+ describe '#can_current_user_push_code?' do
+ let(:project) { create(:project, :repository) }
+ let(:presenter) { described_class.new(project, current_user: user) }
+
+ context 'empty repo' do
+ let(:project) { create(:project) }
+
+ it 'returns true if user can push_code' do
+ project.add_developer(user)
+
+ expect(presenter.can_current_user_push_code?).to be(true)
+ end
+
+ it 'returns false if user cannot push_code' do
+ project.add_reporter(user)
+
+ expect(presenter.can_current_user_push_code?).to be(false)
+ end
+ end
+
+ context 'not empty repo' do
+ let(:project) { create(:project, :repository) }
+
+ it 'returns true if user can push to default branch' do
+ project.add_developer(user)
+
+ expect(presenter.can_current_user_push_code?).to be(true)
+ end
+
+ it 'returns false if default branch is protected' do
+ project.add_developer(user)
+ create(:protected_branch, project: project, name: project.default_branch)
+
+ expect(presenter.can_current_user_push_code?).to be(false)
+ end
+ end
+ end
+
+ context 'statistics anchors' do
+ let(:project) { create(:project, :repository) }
+ let(:presenter) { described_class.new(project, current_user: user) }
+
+ describe '#files_anchor_data' do
+ it 'returns files data' do
+ expect(presenter.files_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Files (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 eq(OpenStruct.new(enabled: true,
+ label: 'Commits (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 eq(OpenStruct.new(enabled: true,
+ label: "Branches (#{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 eq(OpenStruct.new(enabled: true,
+ label: "Tags (#{project.repository.tags.size})",
+ link: presenter.project_tags_path(project)))
+ end
+ end
+
+ describe '#new_file_anchor_data' do
+ it 'returns new file data if user can push' do
+ project.add_developer(user)
+
+ expect(presenter.new_file_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: "New file",
+ link: presenter.project_new_blob_path(project, 'master'),
+ class_modifier: 'new'))
+ end
+
+ it 'returns nil if user cannot push' do
+ expect(presenter.new_file_anchor_data).to be_nil
+ end
+ end
+
+ describe '#readme_anchor_data' do
+ context 'when user can push and README does not exists' do
+ it 'returns anchor data' do
+ project.add_developer(user)
+ allow(project.repository).to receive(:readme).and_return(nil)
+
+ expect(presenter.readme_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: 'Add Readme',
+ link: presenter.add_readme_path))
+ end
+ end
+
+ context 'when README exists' do
+ it 'returns anchor data' do
+ allow(project.repository).to receive(:readme).and_return(double(name: 'readme'))
+
+ expect(presenter.readme_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Readme',
+ link: presenter.readme_path))
+ end
+ end
+ end
+
+ describe '#changelog_anchor_data' do
+ context 'when user can push and CHANGELOG does not exists' 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 eq(OpenStruct.new(enabled: false,
+ label: 'Add Changelog',
+ link: presenter.add_changelog_path))
+ end
+ end
+
+ context 'when CHANGELOG exists' do
+ it 'returns anchor data' do
+ allow(project.repository).to receive(:changelog).and_return(double(name: 'foo'))
+
+ expect(presenter.changelog_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Changelog',
+ link: presenter.changelog_path))
+ end
+ end
+ end
+
+ describe '#license_anchor_data' do
+ context 'when user can push and LICENSE does not exists' 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 eq(OpenStruct.new(enabled: false,
+ label: 'Add License',
+ link: presenter.add_license_path))
+ end
+ end
+
+ context 'when LICENSE exists' do
+ it 'returns anchor data' do
+ allow(project.repository).to receive(:license_blob).and_return(double(name: 'foo'))
+
+ expect(presenter.license_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 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
+ 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 eq(OpenStruct.new(enabled: false,
+ label: 'Add Contribution guide',
+ link: presenter.add_contribution_guide_path))
+ end
+ end
+
+ context 'when CONTRIBUTING exists' 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 eq(OpenStruct.new(enabled: true,
+ label: 'Contribution guide',
+ link: presenter.contribution_guide_path))
+ end
+ end
+ end
+
+ describe '#autodevops_anchor_data' do
+ context 'when Auto Devops is enabled' do
+ it 'returns anchor data' do
+ allow(project).to receive(:auto_devops_enabled?).and_return(true)
+
+ expect(presenter.autodevops_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Auto DevOps enabled',
+ link: nil))
+ end
+ end
+
+ context 'when user can admin pipeline and CI yml does not exists' do
+ it 'returns anchor data' do
+ project.add_master(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 eq(OpenStruct.new(enabled: false,
+ label: 'Enable Auto DevOps',
+ link: presenter.project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings')))
+ end
+ end
+ end
+
+ describe '#kubernetes_cluster_anchor_data' do
+ context 'when user can create Kubernetes cluster' do
+ it 'returns link to cluster if only one exists' do
+ project.add_master(user)
+ cluster = create(:cluster, projects: [project])
+
+ expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Kubernetes configured',
+ link: presenter.project_cluster_path(project, cluster)))
+ end
+
+ it 'returns link to clusters page if more than one exists' do
+ project.add_master(user)
+ create(:cluster, projects: [project])
+ create(:cluster, projects: [project])
+
+ expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Kubernetes configured',
+ link: presenter.project_clusters_path(project)))
+ end
+
+ it 'returns link to create a cluster if no cluster exists' do
+ project.add_master(user)
+
+ expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: 'Add Kubernetes cluster',
+ link: presenter.new_project_cluster_path(project)))
+ end
+ end
+
+ context 'when user cannot create Kubernetes cluster' do
+ it 'returns nil' do
+ expect(presenter.kubernetes_cluster_anchor_data).to be_nil
+ end
+ end
+ end
+
+ describe '#koding_anchor_data' do
+ it 'returns link to setup Koding if user can push and no koding YML exists' do
+ project.add_developer(user)
+ allow(project.repository).to receive(:koding_yml).and_return(nil)
+ allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(true)
+
+ expect(presenter.koding_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: 'Set up Koding',
+ link: presenter.add_koding_stack_path))
+ end
+
+ it 'returns nil if user cannot push' do
+ expect(presenter.koding_anchor_data).to be_nil
+ end
+
+ it 'returns nil if koding is not enabled' do
+ project.add_developer(user)
+ allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(false)
+
+ expect(presenter.koding_anchor_data).to be_nil
+ end
+
+ it 'returns nil if koding YML already exists' do
+ project.add_developer(user)
+ allow(project.repository).to receive(:koding_yml).and_return(double)
+ allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(true)
+
+ expect(presenter.koding_anchor_data).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index c7df6251d74..827f4c04324 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::Internal do
let(:user) { create(:user) }
let(:key) { create(:key, user: user) }
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :repository, :wiki_repo) }
let(:secret_token) { Gitlab::Shell.secret_token }
let(:gl_repository) { "project-#{project.id}" }
let(:reference_counter) { double('ReferenceCounter') }
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 942e5b2bb1b..c6fdda203ad 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -150,7 +150,7 @@ describe 'Git HTTP requests' do
let(:path) { "/#{wiki.repository.full_path}.git" }
context "when the project is public" do
- let(:project) { create(:project, :repository, :public, :wiki_enabled) }
+ let(:project) { create(:project, :wiki_repo, :public, :wiki_enabled) }
it_behaves_like 'pushes require Basic HTTP Authentication'
@@ -177,7 +177,7 @@ describe 'Git HTTP requests' do
end
context 'but the repo is disabled' do
- let(:project) { create(:project, :repository, :public, :repository_disabled, :wiki_enabled) }
+ let(:project) { create(:project, :wiki_repo, :public, :repository_disabled, :wiki_enabled) }
it_behaves_like 'pulls are allowed'
it_behaves_like 'pushes are allowed'
@@ -198,7 +198,7 @@ describe 'Git HTTP requests' do
end
context "when the project is private" do
- let(:project) { create(:project, :repository, :private, :wiki_enabled) }
+ let(:project) { create(:project, :wiki_repo, :private, :wiki_enabled) }
it_behaves_like 'pulls require Basic HTTP Authentication'
it_behaves_like 'pushes require Basic HTTP Authentication'
@@ -210,7 +210,7 @@ describe 'Git HTTP requests' do
end
context 'but the repo is disabled' do
- let(:project) { create(:project, :repository, :private, :repository_disabled, :wiki_enabled) }
+ let(:project) { create(:project, :wiki_repo, :private, :repository_disabled, :wiki_enabled) }
it 'allows clones' do
download(path, user: user.username, password: user.password) do |response|
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 91aefa84d0e..56d025f0176 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -37,6 +37,22 @@ describe UsersController, "routing" do
it "to #calendar_activities" do
expect(get("/users/User/calendar_activities")).to route_to('users#calendar_activities', username: 'User')
end
+
+ describe 'redirect alias routes' do
+ include RSpec::Rails::RequestExampleGroup
+
+ it '/u/user1 redirects to /user1' do
+ expect(get("/u/user1")).to redirect_to('/user1')
+ end
+
+ it '/u/user1/groups redirects to /user1/groups' do
+ expect(get("/u/user1/groups")).to redirect_to('/users/user1/groups')
+ end
+
+ it '/u/user1/projects redirects to /user1/projects' do
+ expect(get("/u/user1/projects")).to redirect_to('/users/user1/projects')
+ end
+ end
end
# search GET /search(.:format) search#show
diff --git a/spec/services/merge_requests/create_from_issue_service_spec.rb b/spec/services/merge_requests/create_from_issue_service_spec.rb
index 75553afc033..38d84cf0ceb 100644
--- a/spec/services/merge_requests/create_from_issue_service_spec.rb
+++ b/spec/services/merge_requests/create_from_issue_service_spec.rb
@@ -24,7 +24,7 @@ describe MergeRequests::CreateFromIssueService do
end
it 'delegates issue search to IssuesFinder' do
- expect_any_instance_of(IssuesFinder).to receive(:execute).once.and_call_original
+ expect_any_instance_of(IssuesFinder).to receive(:find_by).once.and_call_original
described_class.new(project, user, issue_iid: -1).execute
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 836ffb7cea0..62fdf870090 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1678,6 +1678,78 @@ describe NotificationService, :mailer do
end
end
+ describe 'Pages domains' do
+ set(:project) { create(:project) }
+ set(:domain) { create(:pages_domain, project: project) }
+ set(:u_blocked) { create(:user, :blocked) }
+ set(:u_silence) { create_user_with_notification(:disabled, 'silent', project) }
+ set(:u_owner) { project.owner }
+ set(:u_master1) { create(:user) }
+ set(:u_master2) { create(:user) }
+ set(:u_developer) { create(:user) }
+
+ before do
+ project.add_master(u_blocked)
+ project.add_master(u_silence)
+ project.add_master(u_master1)
+ project.add_master(u_master2)
+ project.add_developer(u_developer)
+
+ reset_delivered_emails!
+ end
+
+ %i[
+ pages_domain_enabled
+ pages_domain_disabled
+ pages_domain_verification_succeeded
+ pages_domain_verification_failed
+ ].each do |sym|
+ describe "##{sym}" do
+ subject(:notify!) { notification.send(sym, domain) }
+
+ it 'emails current watching masters' do
+ expect(Notify).to receive(:"#{sym}_email").at_least(:once).and_call_original
+
+ notify!
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+
+ it 'emails nobody if the project is missing' do
+ domain.project = nil
+
+ notify!
+
+ should_not_email_anyone
+ end
+ end
+ end
+
+ describe '#pages_domain_verification_failed' do
+ it 'emails current watching masters' do
+ notification.pages_domain_verification_failed(domain)
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+ end
+
+ describe '#pages_domain_enabled' do
+ it 'emails current watching masters' do
+ notification.pages_domain_enabled(domain)
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+ end
+
+ describe '#pages_domain_disabled' do
+ it 'emails current watching masters' do
+ notification.pages_domain_disabled(domain)
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+ end
+ end
+
def build_team(project)
@u_watcher = create_global_setting_for(create(:user), :watch)
@u_participating = create_global_setting_for(create(:user), :participating)
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index ae160d104f1..f793f55e51b 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -522,6 +522,22 @@ describe QuickActions::InterpretService do
let(:issuable) { merge_request }
end
+ context 'only group milestones available' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :public, namespace: group) }
+ let(:milestone) { create(:milestone, group: group, title: '10.0') }
+
+ it_behaves_like 'milestone command' do
+ let(:content) { "/milestone %#{milestone.title}" }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'milestone command' do
+ let(:content) { "/milestone %#{milestone.title}" }
+ let(:issuable) { merge_request }
+ end
+ end
+
it_behaves_like 'remove_milestone command' do
let(:content) { '/remove_milestone' }
let(:issuable) { issue }
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 5e6c24f5730..562b89e6767 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -943,7 +943,8 @@ describe TodoService do
described_class.new.mark_todos_as_done_by_ids(todo, john_doe)
- expect_any_instance_of(TodosFinder).not_to receive(:execute)
+ # Make sure no TodosFinder is inialized to perform counting
+ expect(TodosFinder).not_to receive(:new)
expect(john_doe.todos_done_count).to eq(1)
expect(john_doe.todos_pending_count).to eq(1)
diff --git a/spec/services/verify_pages_domain_service_spec.rb b/spec/services/verify_pages_domain_service_spec.rb
new file mode 100644
index 00000000000..576db1dde2d
--- /dev/null
+++ b/spec/services/verify_pages_domain_service_spec.rb
@@ -0,0 +1,270 @@
+require 'spec_helper'
+
+describe VerifyPagesDomainService do
+ using RSpec::Parameterized::TableSyntax
+ include EmailHelpers
+
+ let(:error_status) { { status: :error, message: "Couldn't verify #{domain.domain}" } }
+
+ subject(:service) { described_class.new(domain) }
+
+ describe '#execute' do
+ context 'verification code recognition (verified domain)' do
+ where(:domain_sym, :code_sym) do
+ :domain | :verification_code
+ :domain | :keyed_verification_code
+
+ :verification_domain | :verification_code
+ :verification_domain | :keyed_verification_code
+ end
+
+ with_them do
+ set(:domain) { create(:pages_domain) }
+
+ let(:domain_name) { domain.send(domain_sym) }
+ let(:verification_code) { domain.send(code_sym) }
+
+ it 'verifies and enables the domain' do
+ stub_resolver(domain_name => ['something else', verification_code])
+
+ expect(service.execute).to eq(status: :success)
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'verifies and enables when the code is contained partway through a TXT record' do
+ stub_resolver(domain_name => "something #{verification_code} else")
+
+ expect(service.execute).to eq(status: :success)
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'does not verify when the code is not present' do
+ stub_resolver(domain_name => 'something else')
+
+ expect(service.execute).to eq(error_status)
+
+ expect(domain).not_to be_verified
+ expect(domain).to be_enabled
+ end
+ end
+
+ context 'verified domain' do
+ set(:domain) { create(:pages_domain) }
+
+ it 'unverifies (but does not disable) when the right code is not present' do
+ stub_resolver(domain.domain => 'something else')
+
+ expect(service.execute).to eq(error_status)
+ expect(domain).not_to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'unverifies (but does not disable) when no records are present' do
+ stub_resolver
+
+ expect(service.execute).to eq(error_status)
+ expect(domain).not_to be_verified
+ expect(domain).to be_enabled
+ end
+ end
+
+ context 'expired domain' do
+ set(:domain) { create(:pages_domain, :expired) }
+
+ it 'verifies and enables when the right code is present' do
+ stub_resolver(domain.domain => domain.keyed_verification_code)
+
+ expect(service.execute).to eq(status: :success)
+
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'disables when the right code is not present' do
+ error_status[:message] += '. It is now disabled.'
+
+ stub_resolver
+
+ expect(service.execute).to eq(error_status)
+
+ expect(domain).not_to be_verified
+ expect(domain).not_to be_enabled
+ end
+ end
+ end
+
+ context 'timeout behaviour' do
+ let(:domain) { create(:pages_domain) }
+
+ it 'sets a timeout on the DNS query' do
+ expect(stub_resolver).to receive(:timeouts=).with(described_class::RESOLVER_TIMEOUT_SECONDS)
+
+ service.execute
+ end
+ end
+
+ context 'email notifications' do
+ let(:notification_service) { instance_double('NotificationService') }
+
+ where(:factory, :verification_succeeds, :expected_notification) do
+ nil | true | nil
+ nil | false | :verification_failed
+ :reverify | true | nil
+ :reverify | false | :verification_failed
+ :unverified | true | :verification_succeeded
+ :unverified | false | nil
+ :expired | true | nil
+ :expired | false | :disabled
+ :disabled | true | :enabled
+ :disabled | false | nil
+ end
+
+ with_them do
+ let(:domain) { create(:pages_domain, *[factory].compact) }
+
+ before do
+ allow(service).to receive(:notification_service) { notification_service }
+
+ if verification_succeeds
+ stub_resolver(domain.domain => domain.verification_code)
+ else
+ stub_resolver
+ end
+ end
+
+ it 'sends a notification if appropriate' do
+ if expected_notification
+ expect(notification_service).to receive(:"pages_domain_#{expected_notification}").with(domain)
+ end
+
+ service.execute
+ end
+ end
+
+ context 'pages verification disabled' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ before do
+ stub_application_setting(pages_domain_verification_enabled: false)
+ allow(service).to receive(:notification_service) { notification_service }
+ end
+
+ it 'skips email notifications' do
+ expect(notification_service).not_to receive(:pages_domain_enabled)
+
+ service.execute
+ end
+ end
+ end
+
+ context 'pages configuration updates' do
+ context 'enabling a disabled domain' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ it 'schedules an update' do
+ stub_resolver(domain.domain => domain.verification_code)
+
+ expect(domain).to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+
+ context 'verifying an enabled domain' do
+ let(:domain) { create(:pages_domain) }
+
+ it 'schedules an update' do
+ stub_resolver(domain.domain => domain.verification_code)
+
+ expect(domain).not_to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+
+ context 'disabling an expired domain' do
+ let(:domain) { create(:pages_domain, :expired) }
+
+ it 'schedules an update' do
+ stub_resolver
+
+ expect(domain).to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+
+ context 'failing to verify a disabled domain' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ it 'does not schedule an update' do
+ stub_resolver
+
+ expect(domain).not_to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+ end
+
+ context 'no verification code' do
+ let(:domain) { create(:pages_domain) }
+
+ it 'returns an error' do
+ domain.verification_code = ''
+
+ disallow_resolver!
+
+ expect(service.execute).to eq(status: :error, message: "No verification code set for #{domain.domain}")
+ end
+ end
+
+ context 'pages domain verification is disabled' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ before do
+ stub_application_setting(pages_domain_verification_enabled: false)
+ end
+
+ it 'extends domain validity by unconditionally reverifying' do
+ disallow_resolver!
+
+ service.execute
+
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'does not shorten any grace period' do
+ grace = Time.now + 1.year
+ domain.update!(enabled_until: grace)
+ disallow_resolver!
+
+ service.execute
+
+ expect(domain.enabled_until).to be_like_time(grace)
+ end
+ end
+ end
+
+ def disallow_resolver!
+ expect(Resolv::DNS).not_to receive(:open)
+ end
+
+ def stub_resolver(stubbed_lookups = {})
+ resolver = instance_double('Resolv::DNS')
+ allow(resolver).to receive(:timeouts=)
+
+ expect(Resolv::DNS).to receive(:open).and_yield(resolver)
+
+ allow(resolver).to receive(:getresources) { [] }
+ stubbed_lookups.each do |domain, records|
+ records = Array(records).map { |txt| Resolv::DNS::Resource::IN::TXT.new(txt) }
+ allow(resolver).to receive(:getresources).with(domain, Resolv::DNS::Resource::IN::TXT) { records }
+ end
+
+ resolver
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 85de0a14631..c0f3366fb52 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -154,6 +154,22 @@ RSpec.configure do |config|
Sidekiq.redis(&:flushall)
end
+ # The :each scope runs "inside" the example, so this hook ensures the DB is in the
+ # correct state before any examples' before hooks are called. This prevents a
+ # problem where `ScheduleIssuesClosedAtTypeChange` (or any migration that depends
+ # on background migrations being run inline during test setup) can be broken by
+ # altering Sidekiq behavior in an unrelated spec like so:
+ #
+ # around do |example|
+ # Sidekiq::Testing.fake! do
+ # example.run
+ # end
+ # end
+ config.before(:context, :migration) do
+ schema_migrate_down!
+ end
+
+ # Each example may call `migrate!`, so we must ensure we are migrated down every time
config.before(:each, :migration) do
schema_migrate_down!
end
@@ -169,6 +185,14 @@ RSpec.configure do |config|
config.around(:each, :postgresql) do |example|
example.run if Gitlab::Database.postgresql?
end
+
+ # This makes sure the `ApplicationController#can?` method is stubbed with the
+ # original implementation for all view specs.
+ config.before(:each, type: :view) do
+ allow(view).to receive(:can?) do |*args|
+ Ability.allowed?(*args)
+ end
+ end
end
# add simpler way to match asset paths containing digest strings
diff --git a/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb b/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb
new file mode 100644
index 00000000000..016bcfa9b1b
--- /dev/null
+++ b/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb
@@ -0,0 +1,128 @@
+module MigrationsHelpers
+ module TrackUntrackedUploadsHelpers
+ PUBLIC_DIR = File.join(Rails.root, 'tmp', 'tests', 'public')
+ UPLOADS_DIR = File.join(PUBLIC_DIR, 'uploads')
+ SYSTEM_DIR = File.join(UPLOADS_DIR, '-', 'system')
+ UPLOAD_FILENAME = 'image.png'.freeze
+ FIXTURE_FILE_PATH = File.join(Rails.root, 'spec', 'fixtures', 'dk.png')
+ FIXTURE_CHECKSUM = 'b804383982bb89b00e828e3f44c038cc991d3d1768009fc39ba8e2c081b9fb75'.freeze
+
+ def create_or_update_appearance(logo: false, header_logo: false)
+ appearance = appearances.first_or_create(title: 'foo', description: 'bar', logo: (UPLOAD_FILENAME if logo), header_logo: (UPLOAD_FILENAME if header_logo))
+
+ add_upload(appearance, 'Appearance', 'logo', 'AttachmentUploader') if logo
+ add_upload(appearance, 'Appearance', 'header_logo', 'AttachmentUploader') if header_logo
+
+ appearance
+ end
+
+ def create_group(avatar: false)
+ index = unique_index(:group)
+ group = namespaces.create(name: "group#{index}", path: "group#{index}", avatar: (UPLOAD_FILENAME if avatar))
+
+ add_upload(group, 'Group', 'avatar', 'AvatarUploader') if avatar
+
+ group
+ end
+
+ def create_note(attachment: false)
+ note = notes.create(attachment: (UPLOAD_FILENAME if attachment))
+
+ add_upload(note, 'Note', 'attachment', 'AttachmentUploader') if attachment
+
+ note
+ end
+
+ def create_project(avatar: false)
+ group = create_group
+ project = projects.create(namespace_id: group.id, path: "project#{unique_index(:project)}", avatar: (UPLOAD_FILENAME if avatar))
+ routes.create(path: "#{group.path}/#{project.path}", source_id: project.id, source_type: 'Project') # so Project.find_by_full_path works
+
+ add_upload(project, 'Project', 'avatar', 'AvatarUploader') if avatar
+
+ project
+ end
+
+ def create_user(avatar: false)
+ user = users.create(email: "foo#{unique_index(:user)}@bar.com", avatar: (UPLOAD_FILENAME if avatar), projects_limit: 100)
+
+ add_upload(user, 'User', 'avatar', 'AvatarUploader') if avatar
+
+ user
+ end
+
+ def unique_index(name = :unnamed)
+ @unique_index ||= {}
+ @unique_index[name] ||= 0
+ @unique_index[name] += 1
+ end
+
+ def add_upload(model, model_type, attachment_type, uploader)
+ file_path = upload_file_path(model, model_type, attachment_type)
+ path_relative_to_public = file_path.sub("#{PUBLIC_DIR}/", '')
+ create_file(file_path)
+
+ uploads.create!(
+ size: 1062,
+ path: path_relative_to_public,
+ model_id: model.id,
+ model_type: model_type == 'Group' ? 'Namespace' : model_type,
+ uploader: uploader,
+ checksum: FIXTURE_CHECKSUM
+ )
+ end
+
+ def add_markdown_attachment(project, hashed_storage: false)
+ project_dir = hashed_storage ? hashed_project_uploads_dir(project) : legacy_project_uploads_dir(project)
+ attachment_dir = File.join(project_dir, SecureRandom.hex)
+ attachment_file_path = File.join(attachment_dir, UPLOAD_FILENAME)
+ project_attachment_path_relative_to_project = attachment_file_path.sub("#{project_dir}/", '')
+ create_file(attachment_file_path)
+
+ uploads.create!(
+ size: 1062,
+ path: project_attachment_path_relative_to_project,
+ model_id: project.id,
+ model_type: 'Project',
+ uploader: 'FileUploader',
+ checksum: FIXTURE_CHECKSUM
+ )
+ end
+
+ def legacy_project_uploads_dir(project)
+ namespace = namespaces.find_by(id: project.namespace_id)
+ File.join(UPLOADS_DIR, namespace.path, project.path)
+ end
+
+ def hashed_project_uploads_dir(project)
+ File.join(UPLOADS_DIR, '@hashed', 'aa', 'aaaaaaaaaaaa')
+ end
+
+ def upload_file_path(model, model_type, attachment_type)
+ dir = File.join(upload_dir(model_type.downcase, attachment_type.to_s), model.id.to_s)
+ File.join(dir, UPLOAD_FILENAME)
+ end
+
+ def upload_dir(model_type, attachment_type)
+ File.join(SYSTEM_DIR, model_type, attachment_type)
+ end
+
+ def create_file(path)
+ File.delete(path) if File.exist?(path)
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.cp(FIXTURE_FILE_PATH, path)
+ end
+
+ def get_uploads(model, model_type)
+ uploads.where(model_type: model_type, model_id: model.id)
+ end
+
+ def get_full_path(project)
+ routes.find_by(source_id: project.id, source_type: 'Project').path
+ end
+
+ def ensure_temporary_tracking_table_exists
+ Gitlab::BackgroundMigration::PrepareUntrackedUploads.new.send(:ensure_temporary_tracking_table_exists)
+ end
+ end
+end
diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb
index dbbd4ad4d40..c7c3346d39e 100644
--- a/spec/support/prometheus/additional_metrics_shared_examples.rb
+++ b/spec/support/prometheus/additional_metrics_shared_examples.rb
@@ -12,11 +12,12 @@ RSpec.shared_examples 'additional metrics query' do
let(:client) { double('prometheus_client') }
let(:query_result) { described_class.new(client).query(*query_params) }
- let(:environment) { create(:environment, slug: 'environment-slug') }
+ let(:project) { create(:project) }
+ let(:environment) { create(:environment, slug: 'environment-slug', project: project) }
before do
allow(client).to receive(:label_values).and_return(metric_names)
- allow(metric_group_class).to receive(:all).and_return([simple_metric_group(metrics: [simple_metric])])
+ allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group(metrics: [simple_metric])])
end
context 'metrics query context' do
@@ -24,13 +25,14 @@ RSpec.shared_examples 'additional metrics query' do
shared_examples 'query context containing environment slug and filter' do
it 'contains ci_environment_slug' do
- expect(subject).to receive(:query_metrics).with(hash_including(ci_environment_slug: environment.slug))
+ expect(subject).to receive(:query_metrics).with(project, hash_including(ci_environment_slug: environment.slug))
subject.query(*query_params)
end
it 'contains environment filter' do
expect(subject).to receive(:query_metrics).with(
+ project,
hash_including(
environment_filter: "container_name!=\"POD\",environment=\"#{environment.slug}\""
)
@@ -48,7 +50,7 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter'
it 'query context contains kube_namespace' do
- expect(subject).to receive(:query_metrics).with(hash_including(kube_namespace: kube_namespace))
+ expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: kube_namespace))
subject.query(*query_params)
end
@@ -72,7 +74,7 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter'
it 'query context contains empty kube_namespace' do
- expect(subject).to receive(:query_metrics).with(hash_including(kube_namespace: ''))
+ expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: ''))
subject.query(*query_params)
end
@@ -81,7 +83,7 @@ RSpec.shared_examples 'additional metrics query' do
context 'with one group where two metrics is found' do
before do
- allow(metric_group_class).to receive(:all).and_return([simple_metric_group])
+ allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group])
end
context 'some queries return results' do
@@ -117,7 +119,7 @@ RSpec.shared_examples 'additional metrics query' do
let(:metrics) { [simple_metric(queries: [simple_query])] }
before do
- allow(metric_group_class).to receive(:all).and_return(
+ allow(metric_group_class).to receive(:common_metrics).and_return(
[
simple_metric_group(name: 'group_a', metrics: [simple_metric(queries: [simple_query])]),
simple_metric_group(name: 'group_b', metrics: [simple_metric(title: 'title_b', queries: [simple_query('b')])])
diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
index 7ce80c82439..ea7dbade171 100644
--- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
@@ -89,6 +89,19 @@ shared_examples 'handle uploads' do
end
end
+ context "when neither the uploader nor the model exists" do
+ before do
+ allow_any_instance_of(Upload).to receive(:build_uploader).and_return(nil)
+ allow(controller).to receive(:find_model).and_return(nil)
+ end
+
+ it "responds with status 404" do
+ show_upload
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
context "when the file doesn't exist" do
before do
allow_any_instance_of(FileUploader).to receive(:exists?).and_return(false)
diff --git a/spec/support/snippet_visibility.rb b/spec/support/snippet_visibility.rb
index 1cb904823d2..3a7c69b7877 100644
--- a/spec/support/snippet_visibility.rb
+++ b/spec/support/snippet_visibility.rb
@@ -252,6 +252,15 @@ RSpec.shared_examples 'snippet visibility' do
results = described_class.new(user).execute
expect(results.include?(snippet)).to eq(outcome)
end
+
+ it 'returns no snippets when the user cannot read cross project' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ snippets = described_class.new(user).execute
+
+ expect(snippets).to be_empty
+ end
end
end
end
@@ -298,6 +307,15 @@ RSpec.shared_examples 'snippet visibility' do
results = described_class.new(user).execute
expect(results.include?(snippet)).to eq(outcome)
end
+
+ it 'should return personal snippets when the user cannot read cross project' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ results = described_class.new(user).execute
+
+ expect(results.include?(snippet)).to eq(outcome)
+ end
end
end
end
diff --git a/spec/support/track_untracked_uploads_helpers.rb b/spec/support/track_untracked_uploads_helpers.rb
deleted file mode 100644
index a8b3ed1f41c..00000000000
--- a/spec/support/track_untracked_uploads_helpers.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module TrackUntrackedUploadsHelpers
- def uploaded_file
- fixture_path = Rails.root.join('spec/fixtures/rails_sample.jpg')
- fixture_file_upload(fixture_path)
- end
-
- def ensure_temporary_tracking_table_exists
- Gitlab::BackgroundMigration::PrepareUntrackedUploads.new.send(:ensure_temporary_tracking_table_exists)
- end
-
- def create_or_update_appearance(attrs)
- a = Appearance.first_or_initialize(title: 'foo', description: 'bar')
- a.update!(attrs)
- a
- end
-end
diff --git a/spec/views/shared/projects/_project.html.haml_spec.rb b/spec/views/shared/projects/_project.html.haml_spec.rb
index f0a4f153699..3b14045e61f 100644
--- a/spec/views/shared/projects/_project.html.haml_spec.rb
+++ b/spec/views/shared/projects/_project.html.haml_spec.rb
@@ -5,6 +5,7 @@ describe 'shared/projects/_project.html.haml' do
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
+ allow(view).to receive(:can?) { true }
end
it 'should render creator avatar if project has a creator' do
diff --git a/spec/workers/pages_domain_verification_cron_worker_spec.rb b/spec/workers/pages_domain_verification_cron_worker_spec.rb
new file mode 100644
index 00000000000..8f780428c82
--- /dev/null
+++ b/spec/workers/pages_domain_verification_cron_worker_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe PagesDomainVerificationCronWorker do
+ subject(:worker) { described_class.new }
+
+ describe '#perform' do
+ it 'enqueues a PagesDomainVerificationWorker for domains needing verification' do
+ verified = create(:pages_domain)
+ reverify = create(:pages_domain, :reverify)
+ disabled = create(:pages_domain, :disabled)
+
+ [reverify, disabled].each do |domain|
+ expect(PagesDomainVerificationWorker).to receive(:perform_async).with(domain.id)
+ end
+
+ expect(PagesDomainVerificationWorker).not_to receive(:perform_async).with(verified.id)
+
+ worker.perform
+ end
+ end
+end
diff --git a/spec/workers/pages_domain_verification_worker_spec.rb b/spec/workers/pages_domain_verification_worker_spec.rb
new file mode 100644
index 00000000000..372fc95ab4a
--- /dev/null
+++ b/spec/workers/pages_domain_verification_worker_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe PagesDomainVerificationWorker do
+ subject(:worker) { described_class.new }
+
+ let(:domain) { create(:pages_domain) }
+
+ describe '#perform' do
+ it 'does nothing for a non-existent domain' do
+ domain.destroy
+
+ expect(VerifyPagesDomainService).not_to receive(:new)
+
+ expect { worker.perform(domain.id) }.not_to raise_error
+ end
+
+ it 'delegates to VerifyPagesDomainService' do
+ service = double(:service)
+ expected_domain = satisfy { |obj| obj == domain }
+
+ expect(VerifyPagesDomainService).to receive(:new).with(expected_domain) { service }
+ expect(service).to receive(:execute)
+
+ worker.perform(domain.id)
+ end
+ end
+end
diff --git a/spec/workers/stuck_import_jobs_worker_spec.rb b/spec/workers/stuck_import_jobs_worker_spec.rb
index a82eb54ffe4..069514552b1 100644
--- a/spec/workers/stuck_import_jobs_worker_spec.rb
+++ b/spec/workers/stuck_import_jobs_worker_spec.rb
@@ -2,35 +2,59 @@ require 'spec_helper'
describe StuckImportJobsWorker do
let(:worker) { described_class.new }
- let(:exclusive_lease_uuid) { SecureRandom.uuid }
- before do
- allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(exclusive_lease_uuid)
- end
+ shared_examples 'project import job detection' do
+ context 'when the job has completed' do
+ context 'when the import status was already updated' do
+ before do
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids) do
+ project.import_start
+ project.import_finish
- describe 'with started import_status' do
- let(:project) { create(:project, :import_started, import_jid: '123') }
+ [project.import_jid]
+ end
+ end
+
+ it 'does not mark the project as failed' do
+ worker.perform
+
+ expect(project.reload.import_status).to eq('finished')
+ end
+ end
+
+ context 'when the import status was not updated' do
+ before do
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([project.import_jid])
+ end
- describe 'long running import' do
- it 'marks the project as failed' do
- allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return(['123'])
+ it 'marks the project as failed' do
+ worker.perform
- expect { worker.perform }.to change { project.reload.import_status }.to('failed')
+ expect(project.reload.import_status).to eq('failed')
+ end
end
end
- describe 'running import' do
- it 'does not mark the project as failed' do
+ context 'when the job is still in Sidekiq' do
+ before do
allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([])
+ end
+ it 'does not mark the project as failed' do
expect { worker.perform }.not_to change { project.reload.import_status }
end
+ end
+ end
- describe 'import without import_jid' do
- it 'marks the project as failed' do
- expect { worker.perform }.to change { project.reload.import_status }.to('failed')
- end
- end
+ describe 'with scheduled import_status' do
+ it_behaves_like 'project import job detection' do
+ let(:project) { create(:project, :import_scheduled, import_jid: '123') }
+ end
+ end
+
+ describe 'with started import_status' do
+ it_behaves_like 'project import job detection' do
+ let(:project) { create(:project, :import_started, import_jid: '123') }
end
end
end