diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 15:26:25 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 15:26:25 +0300 |
commit | a09983ae35713f5a2bbb100981116d31ce99826e (patch) | |
tree | 2ee2af7bd104d57086db360a7e6d8c9d5d43667a /spec/controllers/projects | |
parent | 18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff) |
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'spec/controllers/projects')
28 files changed, 961 insertions, 474 deletions
diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 5645e25b741..da4faad2a39 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -200,6 +200,48 @@ RSpec.describe Projects::ClustersController do end end + describe 'GET #prometheus_proxy' do + let(:proxyable) do + create(:cluster, :provided_by_gcp, projects: [project]) + end + + it_behaves_like 'metrics dashboard prometheus api proxy' do + let(:proxyable_params) do + { + id: proxyable.id.to_s, + namespace_id: project.namespace.full_path, + project_id: project.name + } + end + + context 'with anonymous user' do + let(:prometheus_body) { nil } + + before do + sign_out(user) + end + + it 'redirects to signin page' do + get :prometheus_proxy, params: prometheus_proxy_params + + expect(response).to redirect_to(new_user_session_path) + end + end + end + end + + it_behaves_like 'GET #metrics_dashboard for dashboard', 'Cluster health' do + let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } + + let(:metrics_dashboard_req_params) do + { + id: cluster.id, + namespace_id: project.namespace.full_path, + project_id: project.name + } + end + end + describe 'POST create for new cluster' do let(:legacy_abac_param) { 'true' } let(:params) do diff --git a/spec/controllers/projects/cycle_analytics_controller_spec.rb b/spec/controllers/projects/cycle_analytics_controller_spec.rb index 8feb964cdde..ec853b74b9b 100644 --- a/spec/controllers/projects/cycle_analytics_controller_spec.rb +++ b/spec/controllers/projects/cycle_analytics_controller_spec.rb @@ -25,6 +25,13 @@ RSpec.describe Projects::CycleAnalyticsController do end end + context 'tracking visits to html page' do + it_behaves_like 'tracking unique visits', :show do + let(:request_params) { { namespace_id: project.namespace, project_id: project } } + let(:target_id) { 'p_analytics_valuestream' } + end + end + describe 'cycle analytics not set up flag' do context 'with no data' do it 'is true' do diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb index 85dd86d91e9..c6532e83441 100644 --- a/spec/controllers/projects/deployments_controller_spec.rb +++ b/spec/controllers/projects/deployments_controller_spec.rb @@ -36,6 +36,52 @@ RSpec.describe Projects::DeploymentsController do expect(response).to be_ok expect(response).to match_response_schema('deployments') end + + context 'anonymous user' do + let(:anonymous_user) { create(:user) } + + before do + sign_in(anonymous_user) + end + + context 'project and metrics dashboard are public' do + before do + project.update!( + visibility_level: Gitlab::VisibilityLevel::PUBLIC, + project_feature_attributes: { + metrics_dashboard_access_level: Gitlab::VisibilityLevel::PUBLIC + } + ) + end + + it 'returns a list with deployments information' do + create(:deployment, :success, environment: environment) + + get :index, params: deployment_params + + expect(response).to be_ok + end + end + + context 'project and metrics dashboard are private' do + before do + project.update!( + visibility_level: Gitlab::VisibilityLevel::PRIVATE, + project_feature_attributes: { + metrics_dashboard_access_level: Gitlab::VisibilityLevel::PRIVATE + } + ) + end + + it 'responds with not found' do + create(:deployment, :success, environment: environment) + + get :index, params: deployment_params + + expect(response).to be_not_found + end + end + end end describe 'GET #metrics' do diff --git a/spec/controllers/projects/discussions_controller_spec.rb b/spec/controllers/projects/discussions_controller_spec.rb index f2efd40afdb..f9d16e761cb 100644 --- a/spec/controllers/projects/discussions_controller_spec.rb +++ b/spec/controllers/projects/discussions_controller_spec.rb @@ -182,7 +182,8 @@ RSpec.describe Projects::DiscussionsController do it "unresolves the discussion" do delete :unresolve, params: request_params - expect(note.reload.discussion.resolved?).to be false + # discussion is memoized and reload doesn't clear the memoization + expect(Note.find(note.id).discussion.resolved?).to be false end it "returns status 200" do diff --git a/spec/controllers/projects/environments/prometheus_api_controller_spec.rb b/spec/controllers/projects/environments/prometheus_api_controller_spec.rb index 17952aa0683..68d50cf19f0 100644 --- a/spec/controllers/projects/environments/prometheus_api_controller_spec.rb +++ b/spec/controllers/projects/environments/prometheus_api_controller_spec.rb @@ -3,215 +3,73 @@ require 'spec_helper' RSpec.describe Projects::Environments::PrometheusApiController do - let_it_be(:project) { create(:project) } - let_it_be(:environment) { create(:environment, project: project) } let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:proxyable) { create(:environment, project: project) } before do project.add_reporter(user) sign_in(user) end - describe 'GET #proxy' do - let(:prometheus_proxy_service) { instance_double(Prometheus::ProxyService) } - - let(:expected_params) do - ActionController::Parameters.new( - environment_params( - proxy_path: 'query', - controller: 'projects/environments/prometheus_api', - action: 'proxy' - ) - ).permit! - end - - context 'with valid requests' do - before do - allow(Prometheus::ProxyService).to receive(:new) - .with(environment, 'GET', 'query', expected_params) - .and_return(prometheus_proxy_service) - - allow(prometheus_proxy_service).to receive(:execute) - .and_return(service_result) + describe 'GET #prometheus_proxy' do + it_behaves_like 'metrics dashboard prometheus api proxy' do + let(:proxyable_params) do + { + id: proxyable.id.to_s, + namespace_id: project.namespace.full_path, + project_id: project.name + } end - context 'with success result' do - let(:service_result) { { status: :success, body: prometheus_body } } + context 'with variables' do let(:prometheus_body) { '{"status":"success"}' } - let(:prometheus_json_body) { Gitlab::Json.parse(prometheus_body) } + let(:pod_name) { "pod1" } - it 'returns prometheus response' do - get :proxy, params: environment_params - - expect(Prometheus::ProxyService).to have_received(:new) - .with(environment, 'GET', 'query', expected_params) - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to eq(prometheus_json_body) + before do + expected_params[:query] = %{up{pod_name="#{pod_name}"}} + expected_params[:variables] = { 'pod_name' => pod_name } end - context 'with format string' do - before do - expected_params[:query] = %{up{environment="#{environment.slug}"}} - end - - it 'replaces variables with values' do - get :proxy, params: environment_params.merge(query: 'up{environment="{{ci_environment_slug}}"}') - - expect(Prometheus::ProxyService).to have_received(:new) - .with(environment, 'GET', 'query', expected_params) - end - - context 'with nil query' do - let(:params_without_query) do - environment_params.except(:query) - end - - before do - expected_params.delete(:query) - end - - it 'does not raise error' do - get :proxy, params: params_without_query + it 'replaces variables with values' do + get :prometheus_proxy, params: prometheus_proxy_params.merge( + query: 'up{pod_name="{{pod_name}}"}', variables: { 'pod_name' => pod_name } + ) - expect(Prometheus::ProxyService).to have_received(:new) - .with(environment, 'GET', 'query', expected_params) - end - end + expect(response).to have_gitlab_http_status(:success) + expect(Prometheus::ProxyService).to have_received(:new) + .with(proxyable, 'GET', 'query', expected_params) end - context 'with variables' do - let(:pod_name) { "pod1" } - - before do - expected_params[:query] = %{up{pod_name="#{pod_name}"}} - expected_params[:variables] = { 'pod_name' => pod_name } - end - - it 'replaces variables with values' do - get :proxy, params: environment_params.merge( - query: 'up{pod_name="{{pod_name}}"}', variables: { 'pod_name' => pod_name } + context 'with invalid variables' do + let(:params_with_invalid_variables) do + prometheus_proxy_params.merge( + query: 'up{pod_name="{{pod_name}}"}', variables: ['a'] ) - - expect(response).to have_gitlab_http_status(:success) - expect(Prometheus::ProxyService).to have_received(:new) - .with(environment, 'GET', 'query', expected_params) - end - - context 'with invalid variables' do - let(:params_with_invalid_variables) do - environment_params.merge( - query: 'up{pod_name="{{pod_name}}"}', variables: ['a'] - ) - end - - it 'returns 400' do - get :proxy, params: params_with_invalid_variables - - expect(response).to have_gitlab_http_status(:bad_request) - expect(Prometheus::ProxyService).not_to receive(:new) - end - end - end - end - - context 'with nil result' do - let(:service_result) { nil } - - it 'returns 204 no_content' do - get :proxy, params: environment_params - - expect(json_response['status']).to eq(_('processing')) - expect(json_response['message']).to eq(_('Not ready yet. Try again later.')) - expect(response).to have_gitlab_http_status(:no_content) - end - end - - context 'with 404 result' do - let(:service_result) { { http_status: 404, status: :success, body: '{"body": "value"}' } } - - it 'returns body' do - get :proxy, params: environment_params - - expect(response).to have_gitlab_http_status(:not_found) - expect(json_response['body']).to eq('value') - end - end - - context 'with error result' do - context 'with http_status' do - let(:service_result) do - { http_status: :service_unavailable, status: :error, message: 'error message' } - end - - it 'sets the http response status code' do - get :proxy, params: environment_params - - expect(response).to have_gitlab_http_status(:service_unavailable) - expect(json_response['status']).to eq('error') - expect(json_response['message']).to eq('error message') end - end - - context 'without http_status' do - let(:service_result) { { status: :error, message: 'error message' } } - it 'returns bad_request' do - get :proxy, params: environment_params + it 'returns 400' do + get :prometheus_proxy, params: params_with_invalid_variables expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['status']).to eq('error') - expect(json_response['message']).to eq('error message') + expect(Prometheus::ProxyService).not_to receive(:new) end end end - end - context 'with inappropriate requests' do context 'with anonymous user' do + let(:prometheus_body) { nil } + before do sign_out(user) end it 'redirects to signin page' do - get :proxy, params: environment_params + get :prometheus_proxy, params: prometheus_proxy_params expect(response).to redirect_to(new_user_session_path) end end - - context 'without correct permissions' do - before do - project.team.truncate - end - - it 'returns 404' do - get :proxy, params: environment_params - - expect(response).to have_gitlab_http_status(:not_found) - end - end end - - context 'with invalid environment id' do - let(:other_environment) { create(:environment) } - - it 'returns 404' do - get :proxy, params: environment_params(id: other_environment.id) - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - - private - - def environment_params(params = {}) - { - id: environment.id.to_s, - namespace_id: project.namespace.full_path, - project_id: project.name, - proxy_path: 'query', - query: '1' - }.merge(params) end end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index cca4b597f4c..85ec1f7396d 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -546,28 +546,20 @@ RSpec.describe Projects::EnvironmentsController do end describe 'GET #metrics_dashboard' do - shared_examples_for 'correctly formatted response' do |status_code| - it 'returns a json object with the correct keys' do - get :metrics_dashboard, params: environment_params(dashboard_params) - - # Exlcude `all_dashboards` to handle separately. - found_keys = json_response.keys - ['all_dashboards'] - - expect(response).to have_gitlab_http_status(status_code) - expect(found_keys).to contain_exactly(*expected_keys) - end - end + let(:metrics_dashboard_req_params) { environment_params(dashboard_params) } shared_examples_for '200 response' do - let(:expected_keys) { %w(dashboard status metrics_data) } - - it_behaves_like 'correctly formatted response', :ok + it_behaves_like 'GET #metrics_dashboard correctly formatted response' do + let(:expected_keys) { %w(dashboard status metrics_data) } + let(:status_code) { :ok } + end end shared_examples_for 'error response' do |status_code| - let(:expected_keys) { %w(message status) } - - it_behaves_like 'correctly formatted response', status_code + it_behaves_like 'GET #metrics_dashboard correctly formatted response' do + let(:expected_keys) { %w(message status) } + let(:status_code) { status_code } + end end shared_examples_for 'includes all dashboards' do @@ -581,29 +573,14 @@ RSpec.describe Projects::EnvironmentsController do end shared_examples_for 'the default dashboard' do - it_behaves_like '200 response' it_behaves_like 'includes all dashboards' - - it 'is the default dashboard' do - get :metrics_dashboard, params: environment_params(dashboard_params) - - expect(json_response['dashboard']['dashboard']).to eq('Environment metrics') - end + it_behaves_like 'GET #metrics_dashboard for dashboard', 'Environment metrics' end shared_examples_for 'the specified dashboard' do |expected_dashboard| - it_behaves_like '200 response' it_behaves_like 'includes all dashboards' - it 'has the correct name' do - get :metrics_dashboard, params: environment_params(dashboard_params) - - dashboard_name = json_response['dashboard']['dashboard'] - - # 'Environment metrics' is the default dashboard. - expect(dashboard_name).not_to eq('Environment metrics') - expect(dashboard_name).to eq(expected_dashboard) - end + it_behaves_like 'GET #metrics_dashboard for dashboard', expected_dashboard context 'when the dashboard cannot not be processed' do before do diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb index 12cef6bea09..49def8f80b0 100644 --- a/spec/controllers/projects/graphs_controller_spec.rb +++ b/spec/controllers/projects/graphs_controller_spec.rb @@ -80,6 +80,15 @@ RSpec.describe Projects::GraphsController do expect(assigns[:daily_coverage_options]).to be_nil end end + + it_behaves_like 'tracking unique visits', :charts do + before do + sign_in(user) + end + + let(:request_params) { { namespace_id: project.namespace.path, project_id: project.path, id: 'master' } } + let(:target_id) { 'p_analytics_repo' } + end end context 'when languages were previously detected' do diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb index 29cfd1c352e..029b4210f19 100644 --- a/spec/controllers/projects/imports_controller_spec.rb +++ b/spec/controllers/projects/imports_controller_spec.rb @@ -8,33 +8,15 @@ RSpec.describe Projects::ImportsController do before do sign_in(user) - project.add_maintainer(user) end describe 'GET #show' do - context 'when repository does not exists' do - it 'renders template' do - get :show, params: { namespace_id: project.namespace.to_param, project_id: project } - - expect(response).to render_template :show - end - - it 'sets flash.now if params is present' do - get :show, params: { namespace_id: project.namespace.to_param, project_id: project, continue: { to: '/', notice_now: 'Started' } } - - expect(flash.now[:notice]).to eq 'Started' + context 'when the user has maintainer rights' do + before do + project.add_maintainer(user) end - end - - context 'when repository exists' do - let(:project) { create(:project_empty_repo, import_url: 'https://github.com/vim/vim.git') } - let(:import_state) { project.import_state } - - context 'when import is in progress' do - before do - import_state.update(status: :started) - end + context 'when repository does not exists' do it 'renders template' do get :show, params: { namespace_id: project.namespace.to_param, project_id: project } @@ -42,82 +24,138 @@ RSpec.describe Projects::ImportsController do end it 'sets flash.now if params is present' do - get :show, params: { namespace_id: project.namespace.to_param, project_id: project, continue: { to: '/', notice_now: 'In progress' } } + get :show, params: { namespace_id: project.namespace.to_param, project_id: project, continue: { to: '/', notice_now: 'Started' } } - expect(flash.now[:notice]).to eq 'In progress' + expect(flash.now[:notice]).to eq 'Started' end end - context 'when import failed' do - before do - import_state.update(status: :failed) - end + context 'when repository exists' do + let(:project) { create(:project_empty_repo, import_url: 'https://github.com/vim/vim.git') } + let(:import_state) { project.import_state } - it 'redirects to new_namespace_project_import_path' do - get :show, params: { namespace_id: project.namespace.to_param, project_id: project } + context 'when import is in progress' do + before do + import_state.update(status: :started) + end - expect(response).to redirect_to new_project_import_path(project) - end - end + it 'renders template' do + get :show, params: { namespace_id: project.namespace.to_param, project_id: project } - context 'when import finished' do - before do - import_state.update(status: :finished) + expect(response).to render_template :show + end + + it 'sets flash.now if params is present' do + get :show, params: { namespace_id: project.namespace.to_param, project_id: project, continue: { to: '/', notice_now: 'In progress' } } + + expect(flash.now[:notice]).to eq 'In progress' + end end - context 'when project is a fork' do - it 'redirects to namespace_project_path' do - allow_any_instance_of(Project).to receive(:forked?).and_return(true) + context 'when import failed' do + before do + import_state.update(status: :failed) + end + it 'redirects to new_namespace_project_import_path' do get :show, params: { namespace_id: project.namespace.to_param, project_id: project } - expect(flash[:notice]).to eq 'The project was successfully forked.' - expect(response).to redirect_to project_path(project) + expect(response).to redirect_to new_project_import_path(project) end end - context 'when project is external' do - it 'redirects to namespace_project_path' do - get :show, params: { namespace_id: project.namespace.to_param, project_id: project } + context 'when import finished' do + before do + import_state.update(status: :finished) + end - expect(flash[:notice]).to eq 'The project was successfully imported.' - expect(response).to redirect_to project_path(project) + context 'when project is a fork' do + it 'redirects to namespace_project_path' do + allow_any_instance_of(Project).to receive(:forked?).and_return(true) + + get :show, params: { namespace_id: project.namespace.to_param, project_id: project } + + expect(flash[:notice]).to eq 'The project was successfully forked.' + expect(response).to redirect_to project_path(project) + end end - end - context 'when continue params is present' do - let(:params) do - { - to: project_path(project), - notice: 'Finished' - } + context 'when project is external' do + it 'redirects to namespace_project_path' do + get :show, params: { namespace_id: project.namespace.to_param, project_id: project } + + expect(flash[:notice]).to eq 'The project was successfully imported.' + expect(response).to redirect_to project_path(project) + end end - it 'redirects to internal params[:to]' do - get :show, params: { namespace_id: project.namespace.to_param, project_id: project, continue: params } + context 'when continue params is present' do + let(:params) do + { + to: project_path(project), + notice: 'Finished' + } + end + + it 'redirects to internal params[:to]' do + get :show, params: { namespace_id: project.namespace.to_param, project_id: project, continue: params } + + expect(flash[:notice]).to eq params[:notice] + expect(response).to redirect_to params[:to] + end - expect(flash[:notice]).to eq params[:notice] - expect(response).to redirect_to params[:to] + it 'does not redirect to external params[:to]' do + params[:to] = "//google.com" + + get :show, params: { namespace_id: project.namespace.to_param, project_id: project, continue: params } + expect(response).not_to redirect_to params[:to] + end end + end - it 'does not redirect to external params[:to]' do - params[:to] = "//google.com" + context 'when import never happened' do + before do + import_state.update(status: :none) + end - get :show, params: { namespace_id: project.namespace.to_param, project_id: project, continue: params } - expect(response).not_to redirect_to params[:to] + it 'redirects to namespace_project_path' do + get :show, params: { namespace_id: project.namespace.to_param, project_id: project } + + expect(response).to redirect_to project_path(project) end end end + end + + context 'when project is in group' do + let(:project) { create(:project_empty_repo, import_url: 'https://github.com/vim/vim.git', namespace: group) } + + context 'when user has developer access to group and import is in progress' do + let(:import_state) { project.import_state } - context 'when import never happened' do before do - import_state.update(status: :none) + group.add_developer(user) + import_state.update!(status: :started) end - it 'redirects to namespace_project_path' do - get :show, params: { namespace_id: project.namespace.to_param, project_id: project } + context 'when group allows developers to create projects' do + let(:group) { create(:group, project_creation_level: Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) } - expect(response).to redirect_to project_path(project) + it 'renders template' do + get :show, params: { namespace_id: project.namespace.to_param, project_id: project } + + expect(response).to render_template :show + end + end + + context 'when group prohibits developers to create projects' do + let(:group) { create(:group, project_creation_level: Gitlab::Access::MAINTAINER_PROJECT_ACCESS) } + + it 'returns 404 response' do + get :show, params: { namespace_id: project.namespace.to_param, project_id: project } + + expect(response).to have_gitlab_http_status(:not_found) + end end end end @@ -128,6 +166,7 @@ RSpec.describe Projects::ImportsController do let(:project) { create(:project) } before do + project.add_maintainer(user) allow(RepositoryImportWorker).to receive(:perform_async) post :create, params: { project: params, namespace_id: project.namespace.to_param, project_id: project } diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index bcd1a53bd47..f9580c79390 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -237,7 +237,7 @@ RSpec.describe Projects::IssuesController do context 'external issue tracker' do let!(:service) do - create(:custom_issue_tracker_service, project: project, title: 'Custom Issue Tracker', new_issue_url: 'http://test.com') + create(:custom_issue_tracker_service, project: project, new_issue_url: 'http://test.com') end before do @@ -1564,6 +1564,43 @@ RSpec.describe Projects::IssuesController do end end + describe 'GET service_desk' do + let_it_be(:project) { create(:project_empty_repo, :public) } + let_it_be(:support_bot) { User.support_bot } + let_it_be(:other_user) { create(:user) } + let_it_be(:service_desk_issue_1) { create(:issue, project: project, author: support_bot) } + let_it_be(:service_desk_issue_2) { create(:issue, project: project, author: support_bot, assignees: [other_user]) } + let_it_be(:other_user_issue) { create(:issue, project: project, author: other_user) } + + def get_service_desk(extra_params = {}) + get :service_desk, params: extra_params.merge(namespace_id: project.namespace, project_id: project) + end + + it 'adds an author filter for the support bot user' do + get_service_desk + + expect(assigns(:issues)).to contain_exactly(service_desk_issue_1, service_desk_issue_2) + end + + it 'does not allow any other author to be set' do + get_service_desk(author_username: other_user.username) + + expect(assigns(:issues)).to contain_exactly(service_desk_issue_1, service_desk_issue_2) + end + + it 'supports other filters' do + get_service_desk(assignee_username: other_user.username) + + expect(assigns(:issues)).to contain_exactly(service_desk_issue_2) + end + + it 'allows an assignee to be specified by id' do + get_service_desk(assignee_id: other_user.id) + + expect(assigns(:users)).to contain_exactly(other_user, support_bot) + end + end + describe 'GET #discussions' do let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) } diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 44dcb0caab2..818b1c30b37 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -646,109 +646,6 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do end end - describe 'GET legacy trace.json' do - before do - stub_feature_flags(job_log_json: false) - get_trace - end - - context 'when job has a trace artifact' do - let(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) } - - it 'returns a trace' do - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['id']).to eq job.id - expect(json_response['status']).to eq job.status - expect(json_response['state']).to be_present - expect(json_response['append']).not_to be_nil - expect(json_response['truncated']).not_to be_nil - expect(json_response['size']).to be_present - expect(json_response['total']).to be_present - expect(json_response['html']).to eq(job.trace.html) - end - end - - context 'when job has a trace' do - let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) } - - it 'returns a trace' do - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['id']).to eq job.id - expect(json_response['status']).to eq job.status - expect(json_response['html']).to eq('<span>BUILD TRACE</span>') - end - end - - context 'when job has no traces' do - let(:job) { create(:ci_build, pipeline: pipeline) } - - it 'returns no traces' do - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['id']).to eq job.id - expect(json_response['status']).to eq job.status - expect(json_response['html']).to be_nil - end - end - - context 'when job has a trace with ANSI sequence and Unicode' do - let(:job) { create(:ci_build, :unicode_trace_live, pipeline: pipeline) } - - it 'returns a trace with Unicode' do - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['id']).to eq job.id - expect(json_response['status']).to eq job.status - expect(json_response['html']).to include("ヾ(´༎ຶД༎ຶ`)ノ") - end - end - - context 'when trace artifact is in ObjectStorage' do - let(:url) { 'http://object-storage/trace' } - let(:file_path) { expand_fixture_path('trace/sample_trace') } - let!(:job) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) } - - before do - allow_any_instance_of(JobArtifactUploader).to receive(:file_storage?) { false } - allow_any_instance_of(JobArtifactUploader).to receive(:url) { url } - allow_any_instance_of(JobArtifactUploader).to receive(:size) { File.size(file_path) } - end - - context 'when there are no network issues' do - before do - stub_remote_url_206(url, file_path) - - get_trace - end - - it 'returns a trace' do - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['id']).to eq job.id - expect(json_response['status']).to eq job.status - expect(json_response['html']).to eq(job.trace.html) - end - end - - context 'when there is a network issue' do - before do - stub_remote_url_500(url) - end - - it 'returns a trace' do - expect { get_trace }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError) - end - end - end - - def get_trace - get :trace, - params: { - namespace_id: project.namespace, - project_id: project, - id: job.id - }, - format: :json - end - end - describe 'GET status.json' do let(:job) { create(:ci_build, pipeline: pipeline) } let(:status) { job.detailed_status(double('user')) } diff --git a/spec/controllers/projects/logs_controller_spec.rb b/spec/controllers/projects/logs_controller_spec.rb index 1eb5a6fcc12..0f34e536064 100644 --- a/spec/controllers/projects/logs_controller_spec.rb +++ b/spec/controllers/projects/logs_controller_spec.rb @@ -104,6 +104,34 @@ RSpec.describe Projects::LogsController do expect(response.headers['Poll-Interval']).to eq('3000') end + context 'with gitlab managed apps logs' do + it 'uses cluster finder services to select cluster', :aggregate_failures do + cluster_list = [cluster] + service_params = { params: ActionController::Parameters.new(pod_name: pod_name).permit! } + request_params = { + namespace_id: project.namespace, + project_id: project, + cluster_id: cluster.id, + pod_name: pod_name, + format: :json + } + + expect_next_instance_of(ClusterAncestorsFinder, project, user) do |finder| + expect(finder).to receive(:execute).and_return(cluster_list) + expect(cluster_list).to receive(:find).and_call_original + end + + expect_next_instance_of(service, cluster, Gitlab::Kubernetes::Helm::NAMESPACE, service_params) do |instance| + expect(instance).to receive(:execute).and_return(service_result) + end + + get endpoint, params: request_params + + expect(response).to have_gitlab_http_status(:success) + expect(json_response).to eq(service_result_json) + end + end + context 'when service is processing' do let(:service_result) { nil } diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb index 02b4c2d1da9..217447c2ad6 100644 --- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb @@ -91,6 +91,17 @@ RSpec.describe Projects::MergeRequests::DiffsController do end end + shared_examples "diff note on-demand position creation" do + it "updates diff discussion positions" do + service = double("service") + + expect(Discussions::CaptureDiffNotePositionsService).to receive(:new).with(merge_request).and_return(service) + expect(service).to receive(:execute) + + go + end + end + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } @@ -146,6 +157,7 @@ RSpec.describe Projects::MergeRequests::DiffsController do it_behaves_like 'persisted preferred diff view cookie' it_behaves_like 'cached diff collection' + it_behaves_like 'diff note on-demand position creation' end describe 'GET diffs_metadata' do diff --git a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb index 7d74e872d29..af39d4dec72 100644 --- a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb @@ -331,8 +331,10 @@ RSpec.describe Projects::MergeRequests::DraftsController do notes = merge_request.notes.reload expect(notes.pluck(:note)).to include(*drafts.map(&:note)) - expect(note.discussion.notes.last.note).to eq(draft_reply.note) - expect(diff_note.discussion.notes.last.note).to eq(diff_draft_reply.note) + + # discussion is memoized and reload doesn't clear the memoization + expect(Note.find(note.id).discussion.notes.last.note).to eq(draft_reply.note) + expect(Note.find(diff_note.id).discussion.notes.last.note).to eq(diff_draft_reply.note) end it 'can publish just a single draft note' do @@ -376,7 +378,8 @@ RSpec.describe Projects::MergeRequests::DraftsController do post :publish, params: params - discussion = note.discussion + # discussion is memoized and reload doesn't clear the memoization + discussion = Note.find(note.id).discussion expect(discussion.notes.last.note).to eq(draft_reply.note) expect(discussion.resolved?).to eq(false) diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 382593fd7cb..4327e0bbb7a 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -442,7 +442,7 @@ RSpec.describe Projects::MergeRequestsController do merge_request.update(squash: false) merge_with_sha(squash: '1') - expect(merge_request.reload.squash).to be_truthy + expect(merge_request.reload.squash_on_merge?).to be_truthy end end @@ -451,7 +451,7 @@ RSpec.describe Projects::MergeRequestsController do merge_request.update(squash: true) merge_with_sha(squash: '0') - expect(merge_request.reload.squash).to be_falsey + expect(merge_request.reload.squash_on_merge?).to be_falsey end end diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index b3a83723189..9728fad417e 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -38,9 +38,9 @@ RSpec.describe Projects::NotesController do end it 'passes last_fetched_at from headers to NotesFinder and MergeIntoNotesService' do - last_fetched_at = 3.hours.ago.to_i + last_fetched_at = Time.zone.at(3.hours.ago.to_i) # remove nanoseconds - request.headers['X-Last-Fetched-At'] = last_fetched_at + request.headers['X-Last-Fetched-At'] = microseconds(last_fetched_at) expect(NotesFinder).to receive(:new) .with(anything, hash_including(last_fetched_at: last_fetched_at)) @@ -84,6 +84,81 @@ RSpec.describe Projects::NotesController do end end + context 'for multiple pages of notes', :aggregate_failures do + # 3 pages worth: 1 normal page, 1 oversized due to clashing updated_at, + # and a final, short page + let!(:page_1) { create_list(:note, 2, noteable: issue, project: project, updated_at: 3.days.ago) } + let!(:page_2) { create_list(:note, 3, noteable: issue, project: project, updated_at: 2.days.ago) } + let!(:page_3) { create_list(:note, 2, noteable: issue, project: project, updated_at: 1.day.ago) } + + # Include a resource event in the middle page as well + let!(:resource_event) { create(:resource_state_event, issue: issue, user: user, created_at: 2.days.ago) } + + let(:page_1_boundary) { microseconds(page_1.last.updated_at + NotesFinder::FETCH_OVERLAP) } + let(:page_2_boundary) { microseconds(page_2.last.updated_at + NotesFinder::FETCH_OVERLAP) } + + around do |example| + Timecop.freeze do + example.run + end + end + + before do + stub_const('Gitlab::UpdatedNotesPaginator::LIMIT', 2) + end + + context 'feature flag enabled' do + before do + stub_feature_flags(paginated_notes: true) + end + + it 'returns the first page of notes' do + get :index, params: request_params + + expect(json_response['notes'].count).to eq(page_1.count) + expect(json_response['more']).to be_truthy + expect(json_response['last_fetched_at']).to eq(page_1_boundary) + expect(response.headers['Poll-Interval'].to_i).to eq(1) + end + + it 'returns the second page of notes' do + request.headers['X-Last-Fetched-At'] = page_1_boundary + + get :index, params: request_params + + expect(json_response['notes'].count).to eq(page_2.count + 1) # resource event + expect(json_response['more']).to be_truthy + expect(json_response['last_fetched_at']).to eq(page_2_boundary) + expect(response.headers['Poll-Interval'].to_i).to eq(1) + end + + it 'returns the final page of notes' do + request.headers['X-Last-Fetched-At'] = page_2_boundary + + get :index, params: request_params + + expect(json_response['notes'].count).to eq(page_3.count) + expect(json_response['more']).to be_falsy + expect(json_response['last_fetched_at']).to eq(microseconds(Time.zone.now)) + expect(response.headers['Poll-Interval'].to_i).to be > 1 + end + end + + context 'feature flag disabled' do + before do + stub_feature_flags(paginated_notes: false) + end + + it 'returns all notes' do + get :index, params: request_params + + expect(json_response['notes'].count).to eq((page_1 + page_2 + page_3).size + 1) + expect(json_response['more']).to be_falsy + expect(json_response['last_fetched_at']).to eq(microseconds(Time.zone.now)) + end + end + end + context 'for a discussion note' do let(:project) { create(:project, :repository) } let!(:note) { create(:discussion_note_on_merge_request, project: project) } @@ -870,4 +945,9 @@ RSpec.describe Projects::NotesController do end end end + + # Convert a time to an integer number of microseconds + def microseconds(time) + (time.to_i * 1_000_000) + time.usec + end end diff --git a/spec/controllers/projects/stages_controller_spec.rb b/spec/controllers/projects/pipelines/stages_controller_spec.rb index dcf8607ae18..6e8c08d95a1 100644 --- a/spec/controllers/projects/stages_controller_spec.rb +++ b/spec/controllers/projects/pipelines/stages_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::StagesController do +RSpec.describe Projects::Pipelines::StagesController do let(:user) { create(:user) } let(:project) { create(:project, :repository) } @@ -60,7 +60,7 @@ RSpec.describe Projects::StagesController do post :play_manual, params: { namespace_id: project.namespace, project_id: project, - id: pipeline.id, + pipeline_id: pipeline.id, stage_name: stage_name }, format: :json end diff --git a/spec/controllers/projects/pipelines/tests_controller_spec.rb b/spec/controllers/projects/pipelines/tests_controller_spec.rb new file mode 100644 index 00000000000..e2abd1238c5 --- /dev/null +++ b/spec/controllers/projects/pipelines/tests_controller_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::Pipelines::TestsController do + let(:user) { create(:user) } + let(:project) { create(:project, :public, :repository) } + let(:pipeline) { create(:ci_pipeline, project: project) } + + before do + sign_in(user) + end + + describe 'GET #summary.json' do + context 'when pipeline has build report results' do + let(:pipeline) { create(:ci_pipeline, :with_report_results, project: project) } + + it 'renders test report summary data' do + get_tests_summary_json + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['total_count']).to eq(2) + end + end + + context 'when pipeline does not have build report results' do + it 'renders test report summary data' do + get_tests_summary_json + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['total_count']).to eq(0) + end + end + + context 'when feature is disabled' do + before do + stub_feature_flags(build_report_summary: false) + end + + it 'returns 404' do + get_tests_summary_json + + expect(response).to have_gitlab_http_status(:not_found) + expect(response.body).to be_empty + end + end + end + + describe 'GET #show.json' do + context 'when pipeline has build report results' do + let(:pipeline) { create(:ci_pipeline, :with_report_results, project: project) } + let(:suite_name) { 'test' } + let(:build_ids) { pipeline.latest_builds.pluck(:id) } + + it 'renders test suite data' do + get_tests_show_json(build_ids) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['name']).to eq('test') + end + end + + context 'when pipeline does not have build report results' do + let(:pipeline) { create(:ci_empty_pipeline) } + let(:suite_name) { 'test' } + + it 'renders 404' do + get_tests_show_json([]) + + expect(response).to have_gitlab_http_status(:not_found) + expect(response.body).to be_empty + end + end + + context 'when feature is disabled' do + let(:suite_name) { 'test' } + + before do + stub_feature_flags(build_report_summary: false) + end + + it 'returns 404' do + get_tests_show_json([]) + + expect(response).to have_gitlab_http_status(:not_found) + expect(response.body).to be_empty + end + end + end + + def get_tests_summary_json + get :summary, + params: { + namespace_id: project.namespace, + project_id: project, + pipeline_id: pipeline.id + }, + format: :json + end + + def get_tests_show_json(build_ids) + get :show, + params: { + namespace_id: project.namespace, + project_id: project, + pipeline_id: pipeline.id, + suite_name: suite_name, + build_ids: build_ids + }, + format: :json + end +end diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index ca09d2b1428..872f0e97b09 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -37,16 +37,13 @@ RSpec.describe Projects::PipelinesController do expect(json_response).to include('pipelines') expect(json_response['pipelines'].count).to eq 6 expect(json_response['count']['all']).to eq '6' - expect(json_response['count']['running']).to eq '2' - expect(json_response['count']['pending']).to eq '1' - expect(json_response['count']['finished']).to eq '3' json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages| expect(stages.count).to eq 3 end end - it 'does not execute N+1 queries' do + it 'executes N+1 queries' do get_pipelines_index_json control_count = ActiveRecord::QueryRecorder.new do @@ -56,10 +53,31 @@ RSpec.describe Projects::PipelinesController do create_all_pipeline_types # There appears to be one extra query for Pipelines#has_warnings? for some reason - expect { get_pipelines_index_json }.not_to exceed_query_limit(control_count + 1) + expect { get_pipelines_index_json }.not_to exceed_query_limit(control_count + 7) expect(response).to have_gitlab_http_status(:ok) expect(json_response['pipelines'].count).to eq 12 end + + context 'with build_report_summary turned off' do + before do + stub_feature_flags(build_report_summary: false) + end + + it 'does not execute N+1 queries' do + get_pipelines_index_json + + control_count = ActiveRecord::QueryRecorder.new do + get_pipelines_index_json + end.count + + create_all_pipeline_types + + # There appears to be one extra query for Pipelines#has_warnings? for some reason + expect { get_pipelines_index_json }.not_to exceed_query_limit(control_count + 1) + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['pipelines'].count).to eq 12 + end + end end it 'does not include coverage data for the pipelines' do @@ -77,9 +95,9 @@ RSpec.describe Projects::PipelinesController do expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original - # ListCommitsByOid, RepositoryExists, HasLocalBranches + # ListCommitsByOid, RepositoryExists, HasLocalBranches, ListCommitsByRefNames expect { get_pipelines_index_json } - .to change { Gitlab::GitalyClient.get_request_count }.by(3) + .to change { Gitlab::GitalyClient.get_request_count }.by(4) end end @@ -101,23 +119,27 @@ RSpec.describe Projects::PipelinesController do end end - context 'filter by scope' do - it 'returns matched pipelines' do - get_pipelines_index_json(scope: 'running') + context 'when user tries to access legacy scope via URL' do + it 'redirects to all pipelines with that status instead' do + get_pipelines_index_html(scope: 'running') - check_pipeline_response(returned: 2, all: 6, running: 2, pending: 1, finished: 3) + expect(response).to redirect_to(project_pipelines_path(project, status: 'running', format: :html)) end + end + context 'filter by scope' do context 'scope is branches or tags' do before do create(:ci_pipeline, :failed, project: project, ref: 'v1.0.0', tag: true) + create(:ci_pipeline, :failed, project: project, ref: 'master', tag: false) + create(:ci_pipeline, :failed, project: project, ref: 'feature', tag: false) end context 'when scope is branches' do it 'returns matched pipelines' do get_pipelines_index_json(scope: 'branches') - check_pipeline_response(returned: 1, all: 7, running: 2, pending: 1, finished: 4) + check_pipeline_response(returned: 2, all: 9) end end @@ -125,7 +147,7 @@ RSpec.describe Projects::PipelinesController do it 'returns matched pipelines' do get_pipelines_index_json(scope: 'tags') - check_pipeline_response(returned: 1, all: 7, running: 2, pending: 1, finished: 4) + check_pipeline_response(returned: 1, all: 9) end end end @@ -138,7 +160,7 @@ RSpec.describe Projects::PipelinesController do it 'returns matched pipelines' do get_pipelines_index_json(username: user.username) - check_pipeline_response(returned: 1, all: 1, running: 1, pending: 0, finished: 0) + check_pipeline_response(returned: 1, all: 1) end end @@ -146,7 +168,7 @@ RSpec.describe Projects::PipelinesController do it 'returns empty' do get_pipelines_index_json(username: 'invalid-username') - check_pipeline_response(returned: 0, all: 0, running: 0, pending: 0, finished: 0) + check_pipeline_response(returned: 0, all: 0) end end end @@ -158,7 +180,7 @@ RSpec.describe Projects::PipelinesController do it 'returns matched pipelines' do get_pipelines_index_json(ref: 'branch-1') - check_pipeline_response(returned: 1, all: 1, running: 1, pending: 0, finished: 0) + check_pipeline_response(returned: 1, all: 1) end end @@ -166,7 +188,7 @@ RSpec.describe Projects::PipelinesController do it 'returns empty list' do get_pipelines_index_json(ref: 'invalid-ref') - check_pipeline_response(returned: 0, all: 0, running: 0, pending: 0, finished: 0) + check_pipeline_response(returned: 0, all: 0) end end end @@ -176,15 +198,7 @@ RSpec.describe Projects::PipelinesController do it 'returns matched pipelines' do get_pipelines_index_json(status: 'success') - check_pipeline_response(returned: 1, all: 1, running: 0, pending: 0, finished: 1) - end - - context 'when filter by unrelated scope' do - it 'returns empty list' do - get_pipelines_index_json(status: 'success', scope: 'running') - - check_pipeline_response(returned: 0, all: 1, running: 0, pending: 0, finished: 1) - end + check_pipeline_response(returned: 1, all: 1) end end @@ -192,7 +206,7 @@ RSpec.describe Projects::PipelinesController do it 'returns empty list' do get_pipelines_index_json(status: 'manual') - check_pipeline_response(returned: 0, all: 0, running: 0, pending: 0, finished: 0) + check_pipeline_response(returned: 0, all: 0) end end @@ -200,11 +214,19 @@ RSpec.describe Projects::PipelinesController do it 'returns all list' do get_pipelines_index_json(status: 'invalid-status') - check_pipeline_response(returned: 6, all: 6, running: 2, pending: 1, finished: 3) + check_pipeline_response(returned: 6, all: 6) end end end + def get_pipelines_index_html(params = {}) + get :index, params: { + namespace_id: project.namespace, + project_id: project + }.merge(params), + format: :html + end + def get_pipelines_index_json(params = {}) get :index, params: { namespace_id: project.namespace, @@ -234,7 +256,8 @@ RSpec.describe Projects::PipelinesController do user = create(:user) pipeline = create(:ci_empty_pipeline, status: status, project: project, - sha: sha, + sha: sha.id, + ref: sha.id.first(8), user: user, merge_request: merge_request) @@ -260,15 +283,12 @@ RSpec.describe Projects::PipelinesController do ) end - def check_pipeline_response(returned:, all:, running:, pending:, finished:) + def check_pipeline_response(returned:, all:) aggregate_failures do expect(response).to match_response_schema('pipeline') expect(json_response['pipelines'].count).to eq returned expect(json_response['count']['all'].to_i).to eq all - expect(json_response['count']['running'].to_i).to eq running - expect(json_response['count']['pending'].to_i).to eq pending - expect(json_response['count']['finished'].to_i).to eq finished end end end @@ -689,6 +709,15 @@ RSpec.describe Projects::PipelinesController do end end + describe 'GET #charts' do + let(:pipeline) { create(:ci_pipeline, project: project) } + + it_behaves_like 'tracking unique visits', :charts do + let(:request_params) { { namespace_id: project.namespace, project_id: project, id: pipeline.id } } + let(:target_id) { 'p_analytics_pipelines' } + end + end + describe 'POST create' do let(:project) { create(:project, :public, :repository) } diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 7457e4c5023..40a220d57a7 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -106,6 +106,29 @@ RSpec.describe Projects::ProjectMembersController do expect(response).to redirect_to(project_project_members_path(project)) end end + + context 'adding project bot' do + let_it_be(:project_bot) { create(:user, :project_bot) } + + before do + project.add_maintainer(user) + + unrelated_project = create(:project) + unrelated_project.add_maintainer(project_bot) + end + + it 'returns error' do + post :create, params: { + namespace_id: project.namespace, + project_id: project, + user_ids: project_bot.id, + access_level: Gitlab::Access::GUEST + } + + expect(flash[:alert]).to include('project bots cannot be added to other groups / projects') + expect(response).to redirect_to(project_project_members_path(project)) + end + end end describe 'PUT update' do diff --git a/spec/controllers/projects/refs_controller_spec.rb b/spec/controllers/projects/refs_controller_spec.rb index a6a4aff7ce9..d10351feb9e 100644 --- a/spec/controllers/projects/refs_controller_spec.rb +++ b/spec/controllers/projects/refs_controller_spec.rb @@ -41,19 +41,12 @@ RSpec.describe Projects::RefsController do expect { xhr_get }.not_to raise_error end - it 'renders 404 for non-JS requests' do + it 'renders 404 for HTML requests' do xhr_get expect(response).to be_not_found end - it 'renders JS' do - expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original - - xhr_get(:js) - expect(response).to be_successful - end - context 'when json is requested' do it 'renders JSON' do expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb index 96c38c1b726..45beccfeef5 100644 --- a/spec/controllers/projects/releases_controller_spec.rb +++ b/spec/controllers/projects/releases_controller_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Projects::ReleasesController do + include AccessMatchersForController + let!(:project) { create(:project, :repository, :public) } let_it_be(:private_project) { create(:project, :repository, :private) } let_it_be(:developer) { create(:user) } @@ -118,6 +120,15 @@ RSpec.describe Projects::ReleasesController do end end + describe 'GET #new' do + let(:request) do + get :new, params: { namespace_id: project.namespace, project_id: project } + end + + it { expect { request }.to be_denied_for(:reporter).of(project) } + it { expect { request }.to be_allowed_for(:developer).of(project) } + end + describe 'GET #edit' do subject do get :edit, params: { namespace_id: project.namespace, project_id: project, tag: tag } diff --git a/spec/controllers/projects/service_desk_controller_spec.rb b/spec/controllers/projects/service_desk_controller_spec.rb new file mode 100644 index 00000000000..1c4d6665414 --- /dev/null +++ b/spec/controllers/projects/service_desk_controller_spec.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::ServiceDeskController do + let_it_be(:project) do + create(:project, :private, :custom_repo, service_desk_enabled: true, + files: { '.gitlab/issue_templates/service_desk.md' => 'template' }) + end + + let_it_be(:user) { create(:user) } + + before do + allow(Gitlab::IncomingEmail).to receive(:enabled?) { true } + allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true } + + project.add_maintainer(user) + sign_in(user) + end + + describe 'GET service desk properties' do + it 'returns service_desk JSON data' do + get :show, params: { namespace_id: project.namespace.to_param, project_id: project }, format: :json + + expect(json_response["service_desk_address"]).to match(/\A[^@]+@[^@]+\z/) + expect(json_response["service_desk_enabled"]).to be_truthy + expect(response).to have_gitlab_http_status(:ok) + end + + context 'when user is not project maintainer' do + let(:guest) { create(:user) } + + it 'renders 404' do + project.add_guest(guest) + sign_in(guest) + + get :show, params: { namespace_id: project.namespace.to_param, project_id: project }, format: :json + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when issue template is present' do + it 'returns template_file_missing as false' do + create(:service_desk_setting, project: project, issue_template_key: 'service_desk') + + get :show, params: { namespace_id: project.namespace.to_param, project_id: project }, format: :json + + response_hash = Gitlab::Json.parse(response.body) + expect(response_hash['template_file_missing']).to eq(false) + end + end + + context 'when issue template file becomes outdated' do + it 'returns template_file_missing as true' do + service = ServiceDeskSetting.new(project_id: project.id, issue_template_key: 'deleted') + service.save!(validate: false) + + get :show, params: { namespace_id: project.namespace.to_param, project_id: project }, format: :json + + expect(json_response['template_file_missing']).to eq(true) + end + end + end + + describe 'PUT service desk properties' do + it 'toggles services desk incoming email' do + project.update!(service_desk_enabled: false) + + put :update, params: { namespace_id: project.namespace.to_param, + project_id: project, + service_desk_enabled: true }, format: :json + + expect(json_response["service_desk_address"]).to be_present + expect(json_response["service_desk_enabled"]).to be_truthy + expect(response).to have_gitlab_http_status(:ok) + end + + it 'sets issue_template_key' do + put :update, params: { namespace_id: project.namespace.to_param, + project_id: project, + issue_template_key: 'service_desk' }, format: :json + + settings = project.service_desk_setting + expect(settings).to be_present + expect(settings.issue_template_key).to eq('service_desk') + expect(json_response['template_file_missing']).to eq(false) + expect(json_response['issue_template_key']).to eq('service_desk') + end + + it 'returns an error when update of service desk settings fails' do + put :update, params: { namespace_id: project.namespace.to_param, + project_id: project, + issue_template_key: 'invalid key' }, format: :json + + expect(response).to have_gitlab_http_status(:unprocessable_entity) + expect(json_response['message']).to eq('Issue template key is empty or does not exist') + end + + context 'when user cannot admin the project' do + let(:other_user) { create(:user) } + + it 'renders 404' do + sign_in(other_user) + put :update, params: { namespace_id: project.namespace.to_param, project_id: project, service_desk_enabled: true }, format: :json + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end +end diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index 04c74dfdefe..e8a23dcfafb 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -137,7 +137,7 @@ RSpec.describe Projects::ServicesController do let(:params) { project_params(service: service_params) } let(:message) { 'Jira activated.' } - let(:redirect_url) { project_settings_integrations_path(project) } + let(:redirect_url) { edit_project_service_path(project, service) } before do put :update, params: params @@ -179,6 +179,23 @@ RSpec.describe Projects::ServicesController do it_behaves_like 'service update' end + + context 'wehn param `inherit_from_id` is set to empty string' do + let(:service_params) { { inherit_from_id: '' } } + + it 'sets inherit_from_id to nil' do + expect(service.reload.inherit_from_id).to eq(nil) + end + end + + context 'wehn param `inherit_from_id` is set to some value' do + let(:instance_service) { create(:jira_service, :instance) } + let(:service_params) { { inherit_from_id: instance_service.id } } + + it 'sets inherit_from_id to value' do + expect(service.reload.inherit_from_id).to eq(instance_service.id) + end + end end describe 'as JSON' do diff --git a/spec/controllers/projects/settings/operations_controller_spec.rb b/spec/controllers/projects/settings/operations_controller_spec.rb index 6b440e910ad..d4f3c5d0c9b 100644 --- a/spec/controllers/projects/settings/operations_controller_spec.rb +++ b/spec/controllers/projects/settings/operations_controller_spec.rb @@ -151,7 +151,8 @@ RSpec.describe Projects::Settings::OperationsController do incident_management_setting_attributes: { create_issue: 'false', send_email: 'false', - issue_template_key: 'some-other-template' + issue_template_key: 'some-other-template', + pagerduty_active: 'true' } } end @@ -159,7 +160,6 @@ RSpec.describe Projects::Settings::OperationsController do it_behaves_like 'PATCHable' context 'updating each incident management setting' do - let(:project) { create(:project) } let(:new_incident_management_settings) { {} } before do @@ -185,6 +185,98 @@ RSpec.describe Projects::Settings::OperationsController do it_behaves_like 'a gitlab tracking event', { issue_template_key: nil }, 'disabled_issue_template_on_alerts' it_behaves_like 'a gitlab tracking event', { send_email: '1' }, 'enabled_sending_emails' it_behaves_like 'a gitlab tracking event', { send_email: '0' }, 'disabled_sending_emails' + it_behaves_like 'a gitlab tracking event', { pagerduty_active: '1' }, 'enabled_pagerduty_webhook' + it_behaves_like 'a gitlab tracking event', { pagerduty_active: '0' }, 'disabled_pagerduty_webhook' + end + end + + describe 'POST #reset_pagerduty_token' do + before do + project.add_maintainer(user) + end + + context 'with existing incident management setting has active PagerDuty webhook' do + let!(:incident_management_setting) do + create(:project_incident_management_setting, project: project, pagerduty_active: true) + end + + let!(:old_token) { incident_management_setting.pagerduty_token } + + it 'returns newly reset token' do + reset_pagerduty_token + + new_token = incident_management_setting.reload.pagerduty_token + new_webhook_url = project_incidents_pagerduty_url(project, token: new_token) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['pagerduty_webhook_url']).to eq(new_webhook_url) + expect(json_response['pagerduty_token']).to eq(new_token) + expect(old_token).not_to eq(new_token) + end + end + + context 'without existing incident management setting' do + it 'does not reset a token' do + reset_pagerduty_token + + new_webhook_url = project_incidents_pagerduty_url(project, token: nil) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['pagerduty_webhook_url']).to eq(new_webhook_url) + expect(project.incident_management_setting.pagerduty_token).to be_nil + end + end + + context 'when update fails' do + let(:operations_update_service) { spy(:operations_update_service) } + let(:pagerduty_token_params) do + { incident_management_setting_attributes: { regenerate_token: true } } + end + + before do + expect(::Projects::Operations::UpdateService) + .to receive(:new).with(project, user, pagerduty_token_params) + .and_return(operations_update_service) + expect(operations_update_service).to receive(:execute) + .and_return(status: :error) + end + + it 'returns unprocessable_entity' do + reset_pagerduty_token + + expect(response).to have_gitlab_http_status(:unprocessable_entity) + expect(json_response).to be_empty + end + end + + context 'with insufficient permissions' do + before do + project.add_reporter(user) + end + + it 'returns 404' do + reset_pagerduty_token + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'as an anonymous user' do + before do + sign_out(user) + end + + it 'returns a redirect' do + reset_pagerduty_token + + expect(response).to have_gitlab_http_status(:redirect) + end + end + + private + + def reset_pagerduty_token + post :reset_pagerduty_token, params: project_params(project), format: :json end end end @@ -296,9 +388,7 @@ RSpec.describe Projects::Settings::OperationsController do end end - describe 'POST reset_alerting_token' do - let(:project) { create(:project) } - + describe 'POST #reset_alerting_token' do before do project.add_maintainer(user) end diff --git a/spec/controllers/projects/snippets/blobs_controller_spec.rb b/spec/controllers/projects/snippets/blobs_controller_spec.rb new file mode 100644 index 00000000000..ca656705e07 --- /dev/null +++ b/spec/controllers/projects/snippets/blobs_controller_spec.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::Snippets::BlobsController do + using RSpec::Parameterized::TableSyntax + include SnippetHelpers + + let_it_be(:author) { create(:user) } + let_it_be(:developer) { create(:user) } + let_it_be(:other_user) { create(:user) } + + let(:visibility) { :public } + let(:project_visibility) { :public } + let(:project) { create(:project, project_visibility) } + let(:snippet) { create(:project_snippet, visibility, :repository, project: project, author: author) } + + before do + project.add_maintainer(author) + project.add_developer(developer) + end + + describe 'GET #raw' do + let(:filepath) { 'file1' } + let(:ref) { TestEnv::BRANCH_SHA['snippet/single-file'] } + let(:inline) { nil } + + subject do + get(:raw, + params: { + namespace_id: project.namespace, + project_id: project, + snippet_id: snippet, + path: filepath, + ref: ref, + inline: inline + }) + end + + context 'with a snippet without a repository' do + let(:snippet) { create(:project_snippet, visibility, project: project, author: author) } + + it_behaves_like 'raw snippet without repository', :not_found + end + + where(:project_visibility_level, :snippet_visibility_level, :user, :status) do + :public | :public | :author | :ok + :public | :public | :developer | :ok + :public | :public | :other_user | :ok + :public | :public | nil | :ok + + :public | :private | :author | :ok + :public | :private | :developer | :ok + :public | :private | :other_user | :not_found + :public | :private | nil | :not_found + + :private | :public | :author | :ok + :private | :public | :developer | :ok + :private | :public | :other_user | :not_found + :private | :public | nil | :redirect + + :private | :private | :author | :ok + :private | :private | :developer | :ok + :private | :private | :other_user | :not_found + :private | :private | nil | :redirect + end + + with_them do + let(:visibility) { snippet_visibility_level } + let(:project_visibility) { project_visibility_level } + + before do + sign_in_as(user) + + subject + end + + it 'responds with correct status' do + expect(response).to have_gitlab_http_status(status) + end + end + + it_behaves_like 'raw snippet blob' + end +end diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index 8bbfaa8d327..6fcb24da3cd 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -15,14 +15,18 @@ RSpec.describe Projects::SnippetsController do end describe 'GET #index' do + let(:base_params) do + { + namespace_id: project.namespace, + project_id: project + } + end + + subject { get :index, params: base_params } + it_behaves_like 'paginated collection' do let(:collection) { project.snippets } - let(:params) do - { - namespace_id: project.namespace, - project_id: project - } - end + let(:params) { base_params } before do create(:project_snippet, :public, project: project, author: user) @@ -35,7 +39,11 @@ RSpec.describe Projects::SnippetsController do .to receive(:new).with(nil, project: project) .and_return(service) - get :index, params: { namespace_id: project.namespace, project_id: project } + subject + end + + it_behaves_like 'snippets sort order' do + let(:params) { base_params } end context 'when the project snippet is private' do @@ -43,7 +51,7 @@ RSpec.describe Projects::SnippetsController do context 'when anonymous' do it 'does not include the private snippet' do - get :index, params: { namespace_id: project.namespace, project_id: project } + subject expect(assigns(:snippets)).not_to include(project_snippet) expect(response).to have_gitlab_http_status(:ok) @@ -56,7 +64,7 @@ RSpec.describe Projects::SnippetsController do end it 'renders the snippet' do - get :index, params: { namespace_id: project.namespace, project_id: project } + subject expect(assigns(:snippets)).to include(project_snippet) expect(response).to have_gitlab_http_status(:ok) @@ -69,7 +77,7 @@ RSpec.describe Projects::SnippetsController do end it 'renders the snippet' do - get :index, params: { namespace_id: project.namespace, project_id: project } + subject expect(assigns(:snippets)).to include(project_snippet) expect(response).to have_gitlab_http_status(:ok) diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index f6ec04d4dd7..8e4e275bdbe 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -89,34 +89,6 @@ RSpec.describe Projects::TreeController do end end - describe "GET show" do - context 'lfs_blob_ids instance variable' do - let(:id) { 'master' } - - context 'with vue tree view enabled' do - before do - get(:show, params: { namespace_id: project.namespace.to_param, project_id: project, id: id }) - end - - it 'is not set' do - expect(assigns[:lfs_blob_ids]).to be_nil - end - end - - context 'with vue tree view disabled' do - before do - stub_feature_flags(vue_file_list: false) - - get(:show, params: { namespace_id: project.namespace.to_param, project_id: project, id: id }) - end - - it 'is set' do - expect(assigns[:lfs_blob_ids]).not_to be_nil - end - end - end - end - describe 'GET show with whitespace in ref' do render_views diff --git a/spec/controllers/projects/wikis_controller_spec.rb b/spec/controllers/projects/wikis_controller_spec.rb index 4e58822b613..7243588681d 100644 --- a/spec/controllers/projects/wikis_controller_spec.rb +++ b/spec/controllers/projects/wikis_controller_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Projects::WikisController do it_behaves_like 'wiki controller actions' do - let(:container) { create(:project, :public, :repository, namespace: user.namespace) } + let(:container) { create(:project, :public, namespace: user.namespace) } let(:routing_params) { { namespace_id: container.namespace, project_id: container } } end end |