From a09983ae35713f5a2bbb100981116d31ce99826e Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 20 Jul 2020 12:26:25 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-2-stable-ee --- .../admin/application_settings_controller_spec.rb | 7 + spec/controllers/admin/clusters_controller_spec.rb | 33 ++++ spec/controllers/admin/jobs_controller_spec.rb | 32 ++++ spec/controllers/admin/services_controller_spec.rb | 22 ++- spec/controllers/application_controller_spec.rb | 22 +++ spec/controllers/autocomplete_controller_spec.rb | 50 +++++ .../config_spec.rb | 53 ++++++ .../controller_with_feature_category_spec.rb | 66 +++++++ .../controllers/concerns/metrics_dashboard_spec.rb | 33 +++- spec/controllers/concerns/renders_commits_spec.rb | 2 +- .../concerns/sorting_preference_spec.rb | 4 +- .../dashboard/projects_controller_spec.rb | 42 ++++- .../dashboard/snippets_controller_spec.rb | 4 +- .../controllers/dashboard/todos_controller_spec.rb | 9 + spec/controllers/dashboard_controller_spec.rb | 8 +- spec/controllers/every_controller_spec.rb | 82 ++++++++ .../controllers/groups/clusters_controller_spec.rb | 40 ++++ spec/controllers/groups/imports_controller_spec.rb | 2 +- spec/controllers/groups/runners_controller_spec.rb | 127 +++++++++++++ .../groups/settings/ci_cd_controller_spec.rb | 15 +- spec/controllers/groups_controller_spec.rb | 36 +++- .../import/bitbucket_controller_spec.rb | 48 +---- .../import/bitbucket_server_controller_spec.rb | 81 +++----- spec/controllers/import/fogbugz_controller_spec.rb | 23 +-- spec/controllers/import/gitlab_controller_spec.rb | 23 +-- .../instance_statistics/cohorts_controller_spec.rb | 7 + .../dev_ops_score_controller_spec.rb | 13 ++ spec/controllers/invites_controller_spec.rb | 37 +++- .../oauth/applications_controller_spec.rb | 16 ++ spec/controllers/profiles/keys_controller_spec.rb | 65 ------- .../projects/clusters_controller_spec.rb | 42 +++++ .../projects/cycle_analytics_controller_spec.rb | 7 + .../projects/deployments_controller_spec.rb | 46 +++++ .../projects/discussions_controller_spec.rb | 3 +- .../environments/prometheus_api_controller_spec.rb | 206 ++++----------------- .../projects/environments_controller_spec.rb | 45 ++--- .../controllers/projects/graphs_controller_spec.rb | 9 + .../projects/imports_controller_spec.rb | 171 ++++++++++------- .../controllers/projects/issues_controller_spec.rb | 39 +++- spec/controllers/projects/jobs_controller_spec.rb | 103 ----------- spec/controllers/projects/logs_controller_spec.rb | 28 +++ .../merge_requests/diffs_controller_spec.rb | 12 ++ .../merge_requests/drafts_controller_spec.rb | 9 +- .../projects/merge_requests_controller_spec.rb | 4 +- spec/controllers/projects/notes_controller_spec.rb | 84 ++++++++- .../projects/pipelines/stages_controller_spec.rb | 72 +++++++ .../projects/pipelines/tests_controller_spec.rb | 112 +++++++++++ .../projects/pipelines_controller_spec.rb | 95 ++++++---- .../projects/project_members_controller_spec.rb | 23 +++ spec/controllers/projects/refs_controller_spec.rb | 9 +- .../projects/releases_controller_spec.rb | 11 ++ .../projects/service_desk_controller_spec.rb | 111 +++++++++++ .../projects/services_controller_spec.rb | 19 +- .../settings/operations_controller_spec.rb | 100 +++++++++- .../projects/snippets/blobs_controller_spec.rb | 85 +++++++++ .../projects/snippets_controller_spec.rb | 28 ++- .../controllers/projects/stages_controller_spec.rb | 72 ------- spec/controllers/projects/tree_controller_spec.rb | 28 --- spec/controllers/projects/wikis_controller_spec.rb | 2 +- spec/controllers/projects_controller_spec.rb | 91 ++++----- .../experience_levels_controller_spec.rb | 47 +++-- spec/controllers/search_controller_spec.rb | 5 + spec/controllers/snippets/blobs_controller_spec.rb | 61 ++++++ spec/controllers/snippets_controller_spec.rb | 6 + spec/controllers/users_controller_spec.rb | 65 +++++++ 65 files changed, 2015 insertions(+), 837 deletions(-) create mode 100644 spec/controllers/admin/jobs_controller_spec.rb create mode 100644 spec/controllers/concerns/controller_with_feature_category/config_spec.rb create mode 100644 spec/controllers/concerns/controller_with_feature_category_spec.rb create mode 100644 spec/controllers/every_controller_spec.rb create mode 100644 spec/controllers/projects/pipelines/stages_controller_spec.rb create mode 100644 spec/controllers/projects/pipelines/tests_controller_spec.rb create mode 100644 spec/controllers/projects/service_desk_controller_spec.rb create mode 100644 spec/controllers/projects/snippets/blobs_controller_spec.rb delete mode 100644 spec/controllers/projects/stages_controller_spec.rb create mode 100644 spec/controllers/snippets/blobs_controller_spec.rb (limited to 'spec/controllers') diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb index 8ab29a72477..7b8528009d8 100644 --- a/spec/controllers/admin/application_settings_controller_spec.rb +++ b/spec/controllers/admin/application_settings_controller_spec.rb @@ -128,6 +128,13 @@ RSpec.describe Admin::ApplicationSettingsController do expect(ApplicationSetting.current.repository_storages_weighted_default).to eq(75) end + it "updates default_branch_name setting" do + put :update, params: { application_setting: { default_branch_name: "example_branch_name" } } + + expect(response).to redirect_to(general_admin_application_settings_path) + expect(ApplicationSetting.current.default_branch_name).to eq("example_branch_name") + end + context 'external policy classification settings' do let(:settings) do { diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index d899e86ae5f..2e0ee671d3f 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -171,6 +171,39 @@ RSpec.describe Admin::ClustersController do end end + it_behaves_like 'GET #metrics_dashboard for dashboard', 'Cluster health' do + let(:cluster) { create(:cluster, :instance, :provided_by_gcp) } + + let(:metrics_dashboard_req_params) do + { + id: cluster.id + } + end + end + + describe 'GET #prometheus_proxy' do + let(:user) { admin } + let(:proxyable) do + create(:cluster, :instance, :provided_by_gcp) + end + + it_behaves_like 'metrics dashboard prometheus api proxy' do + context 'with anonymous user' do + let(:prometheus_body) { nil } + + before do + sign_out(admin) + end + + it 'returns 404' do + get :prometheus_proxy, params: prometheus_proxy_params + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + end + describe 'POST #create_gcp' do let(:legacy_abac_param) { 'true' } let(:params) do diff --git a/spec/controllers/admin/jobs_controller_spec.rb b/spec/controllers/admin/jobs_controller_spec.rb new file mode 100644 index 00000000000..2d1482f40d4 --- /dev/null +++ b/spec/controllers/admin/jobs_controller_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Admin::JobsController do + describe 'GET #index' do + context 'with an authenticated admin user' do + it 'paginates builds without a total count', :aggregate_failures do + stub_const("Admin::JobsController::BUILDS_PER_PAGE", 1) + + sign_in(create(:admin)) + create_list(:ci_build, 2) + + get :index + + expect(response).to have_gitlab_http_status(:ok) + expect(assigns(:builds)).to be_a(Kaminari::PaginatableWithoutCount) + expect(assigns(:builds).count).to be(1) + end + end + + context 'without admin access' do + it 'returns `not_found`' do + sign_in(create(:user)) + + get :index + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end +end diff --git a/spec/controllers/admin/services_controller_spec.rb b/spec/controllers/admin/services_controller_spec.rb index 2ad4989af4f..8e78cc75369 100644 --- a/spec/controllers/admin/services_controller_spec.rb +++ b/spec/controllers/admin/services_controller_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Admin::ServicesController do end describe 'GET #edit' do - let!(:service) do + let(:service) do create(:jira_service, :template) end @@ -19,6 +19,26 @@ RSpec.describe Admin::ServicesController do expect(response).to have_gitlab_http_status(:ok) end + + context 'when integration does not exists' do + it 'redirects to the admin application integration page' do + get :edit, params: { id: 'invalid' } + + expect(response).to redirect_to(admin_application_settings_services_path) + end + end + + context 'when instance integration exists' do + before do + create(:jira_service, :instance) + end + + it 'redirects to the admin application integration page' do + get :edit, params: { id: service.id } + + expect(response).to redirect_to(admin_application_settings_services_path) + end + end end describe "#update" do diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 4002b7aca63..aec629ba330 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -968,4 +968,26 @@ RSpec.describe ApplicationController do end end end + + describe 'locale' do + let(:user) { create(:user, preferred_language: 'uk') } + + controller(described_class) do + def index + :ok + end + end + + before do + sign_in(user) + + allow(Gitlab::I18n).to receive(:with_locale).and_call_original + end + + it "sets user's locale" do + expect(Gitlab::I18n).to receive(:with_locale).with('uk') + + get :index + end + end end diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index aeb3f4dcb17..e7c0bc43e86 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -365,6 +365,56 @@ RSpec.describe AutocompleteController do end end + context 'GET deploy_keys_with_owners' do + let!(:deploy_key) { create(:deploy_key, user: user) } + let!(:deploy_keys_project) { create(:deploy_keys_project, :write_access, project: project, deploy_key: deploy_key) } + + context 'unauthorized user' do + it 'returns a not found response' do + get(:deploy_keys_with_owners, params: { project_id: project.id }) + + expect(response).to have_gitlab_http_status(:redirect) + end + end + + context 'when the user who can read the project is logged in' do + before do + sign_in(user) + end + + it 'renders the deploy key in a json payload, with its owner' do + get(:deploy_keys_with_owners, params: { project_id: project.id }) + + expect(json_response.count).to eq(1) + expect(json_response.first['title']).to eq(deploy_key.title) + expect(json_response.first['owner']['id']).to eq(deploy_key.user.id) + end + + context 'with an unknown project' do + it 'returns a not found response' do + get(:deploy_keys_with_owners, params: { project_id: 9999 }) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'and the user cannot read the owner of the key' do + before do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?).with(user, :read_user, deploy_key.user).and_return(false) + end + + it 'returns a payload without owner' do + get(:deploy_keys_with_owners, params: { project_id: project.id }) + + expect(json_response.count).to eq(1) + expect(json_response.first['title']).to eq(deploy_key.title) + expect(json_response.first['owner']).to be_nil + end + end + end + end + context 'Get merge_request_target_branches' do let!(:merge_request) { create(:merge_request, source_project: project, target_branch: 'feature') } diff --git a/spec/controllers/concerns/controller_with_feature_category/config_spec.rb b/spec/controllers/concerns/controller_with_feature_category/config_spec.rb new file mode 100644 index 00000000000..9b8ffd2baab --- /dev/null +++ b/spec/controllers/concerns/controller_with_feature_category/config_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require "fast_spec_helper" +require "rspec-parameterized" +require_relative "../../../../app/controllers/concerns/controller_with_feature_category/config" + +RSpec.describe ControllerWithFeatureCategory::Config do + describe "#matches?" do + using RSpec::Parameterized::TableSyntax + + where(:only_actions, :except_actions, :if_proc, :unless_proc, :test_action, :expected) do + nil | nil | nil | nil | "action" | true + [:included] | nil | nil | nil | "action" | false + [:included] | nil | nil | nil | "included" | true + nil | [:excluded] | nil | nil | "excluded" | false + nil | nil | true | nil | "action" | true + [:included] | nil | true | nil | "action" | false + [:included] | nil | true | nil | "included" | true + nil | [:excluded] | true | nil | "excluded" | false + nil | nil | false | nil | "action" | false + [:included] | nil | false | nil | "action" | false + [:included] | nil | false | nil | "included" | false + nil | [:excluded] | false | nil | "excluded" | false + nil | nil | nil | true | "action" | false + [:included] | nil | nil | true | "action" | false + [:included] | nil | nil | true | "included" | false + nil | [:excluded] | nil | true | "excluded" | false + nil | nil | nil | false | "action" | true + [:included] | nil | nil | false | "action" | false + [:included] | nil | nil | false | "included" | true + nil | [:excluded] | nil | false | "excluded" | false + nil | nil | true | false | "action" | true + [:included] | nil | true | false | "action" | false + [:included] | nil | true | false | "included" | true + nil | [:excluded] | true | false | "excluded" | false + nil | nil | false | true | "action" | false + [:included] | nil | false | true | "action" | false + [:included] | nil | false | true | "included" | false + nil | [:excluded] | false | true | "excluded" | false + end + + with_them do + let(:config) do + if_to_proc = if_proc.nil? ? nil : -> (_) { if_proc } + unless_to_proc = unless_proc.nil? ? nil : -> (_) { unless_proc } + + described_class.new(:category, only_actions, except_actions, if_to_proc, unless_to_proc) + end + + specify { expect(config.matches?(test_action)).to be(expected) } + end + end +end diff --git a/spec/controllers/concerns/controller_with_feature_category_spec.rb b/spec/controllers/concerns/controller_with_feature_category_spec.rb new file mode 100644 index 00000000000..e603a7d14c4 --- /dev/null +++ b/spec/controllers/concerns/controller_with_feature_category_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require_relative "../../../app/controllers/concerns/controller_with_feature_category" +require_relative "../../../app/controllers/concerns/controller_with_feature_category/config" + +RSpec.describe ControllerWithFeatureCategory do + describe ".feature_category_for_action" do + let(:base_controller) do + Class.new do + include ControllerWithFeatureCategory + end + end + + let(:controller) do + Class.new(base_controller) do + feature_category :baz + feature_category :foo, except: %w(update edit) + feature_category :bar, only: %w(index show) + feature_category :quux, only: %w(destroy) + feature_category :quuz, only: %w(destroy) + end + end + + let(:subclass) do + Class.new(controller) do + feature_category :qux, only: %w(index) + end + end + + it "is nil when nothing was defined" do + expect(base_controller.feature_category_for_action("hello")).to be_nil + end + + it "returns the expected category", :aggregate_failures do + expect(controller.feature_category_for_action("update")).to eq(:baz) + expect(controller.feature_category_for_action("hello")).to eq(:foo) + expect(controller.feature_category_for_action("index")).to eq(:bar) + end + + it "returns the closest match for categories defined in subclasses" do + expect(subclass.feature_category_for_action("index")).to eq(:qux) + expect(subclass.feature_category_for_action("show")).to eq(:bar) + end + + it "returns the last defined feature category when multiple match" do + expect(controller.feature_category_for_action("destroy")).to eq(:quuz) + end + + it "raises an error when using including and excluding the same action" do + expect do + Class.new(base_controller) do + feature_category :hello, only: [:world], except: [:world] + end + end.to raise_error(%r(cannot configure both `only` and `except`)) + end + + it "raises an error when using unknown arguments" do + expect do + Class.new(base_controller) do + feature_category :hello, hello: :world + end + end.to raise_error(%r(unknown arguments)) + end + end +end diff --git a/spec/controllers/concerns/metrics_dashboard_spec.rb b/spec/controllers/concerns/metrics_dashboard_spec.rb index 39ddf687dca..f0c9874965e 100644 --- a/spec/controllers/concerns/metrics_dashboard_spec.rb +++ b/spec/controllers/concerns/metrics_dashboard_spec.rb @@ -76,6 +76,22 @@ RSpec.describe MetricsDashboard do end end + context 'when dashboard path includes encoded characters' do + let(:params) { { dashboard_path: 'dashboard%26copy.yml' } } + + before do + allow(controller) + .to receive(:metrics_dashboard_params) + .and_return(params) + end + + it 'decodes dashboard path' do + expect(::Gitlab::Metrics::Dashboard::Finder).to receive(:find).with(anything, anything, hash_including(dashboard_path: 'dashboard©.yml')) + + json_response + end + end + context 'when parameters are provided and the list of all dashboards is required' do before do allow(controller).to receive(:include_all_dashboards?).and_return(true) @@ -88,13 +104,28 @@ RSpec.describe MetricsDashboard do context 'in all_dashboard list' do let(:system_dashboard) { json_response['all_dashboards'].find { |dashboard| dashboard["system_dashboard"] == true } } - let(:project_dashboard) { json_response['all_dashboards'].find { |dashboard| dashboard["system_dashboard"] == false } } + + let(:project_dashboard) do + json_response['all_dashboards'].find do |dashboard| + dashboard['path'] == '.gitlab/dashboards/test.yml' + end + end it 'includes project_blob_path only for project dashboards' do expect(system_dashboard['project_blob_path']).to be_nil expect(project_dashboard['project_blob_path']).to eq("/#{project.namespace.path}/#{project.name}/-/blob/master/.gitlab/dashboards/test.yml") end + it 'allows editing only for project dashboards' do + expect(system_dashboard['can_edit']).to be(false) + expect(project_dashboard['can_edit']).to be(true) + end + + it 'includes out_of_the_box_dashboard key' do + expect(system_dashboard['out_of_the_box_dashboard']).to be(true) + expect(project_dashboard['out_of_the_box_dashboard']).to be(false) + end + describe 'project permissions' do using RSpec::Parameterized::TableSyntax diff --git a/spec/controllers/concerns/renders_commits_spec.rb b/spec/controllers/concerns/renders_commits_spec.rb index 0bffb39d608..7be5f75c19d 100644 --- a/spec/controllers/concerns/renders_commits_spec.rb +++ b/spec/controllers/concerns/renders_commits_spec.rb @@ -46,7 +46,7 @@ RSpec.describe RendersCommits do it 'avoids N + 1' do stub_const("MergeRequestDiff::COMMITS_SAFE_SIZE", 5) - control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do + control_count = ActiveRecord::QueryRecorder.new do go end.count diff --git a/spec/controllers/concerns/sorting_preference_spec.rb b/spec/controllers/concerns/sorting_preference_spec.rb index 4f9506d4675..c0091e8b694 100644 --- a/spec/controllers/concerns/sorting_preference_spec.rb +++ b/spec/controllers/concerns/sorting_preference_spec.rb @@ -75,7 +75,7 @@ RSpec.describe SortingPreference do it 'sets the cookie with the right values and flags' do subject - expect(cookies['issue_sort']).to eq(value: 'popularity', secure: false, httponly: false) + expect(cookies['issue_sort']).to eq(expires: nil, value: 'popularity', secure: false, httponly: false) end end @@ -86,7 +86,7 @@ RSpec.describe SortingPreference do it 'sets the cookie with the right values and flags' do subject - expect(cookies['issue_sort']).to eq(value: 'created_asc', secure: false, httponly: false) + expect(cookies['issue_sort']).to eq(expires: nil, value: 'created_asc', secure: false, httponly: false) end end end diff --git a/spec/controllers/dashboard/projects_controller_spec.rb b/spec/controllers/dashboard/projects_controller_spec.rb index ee043fde0ff..1e1d9519f78 100644 --- a/spec/controllers/dashboard/projects_controller_spec.rb +++ b/spec/controllers/dashboard/projects_controller_spec.rb @@ -5,13 +5,14 @@ require 'spec_helper' RSpec.describe Dashboard::ProjectsController do include ExternalAuthorizationServiceHelpers + let_it_be(:user) { create(:user) } + describe '#index' do context 'user not logged in' do it_behaves_like 'authenticates sessionless user', :index, :atom end context 'user logged in' do - let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:project2) { create(:project) } @@ -71,8 +72,6 @@ RSpec.describe Dashboard::ProjectsController do context 'json requests' do render_views - let(:user) { create(:user) } - before do sign_in(user) end @@ -114,16 +113,14 @@ RSpec.describe Dashboard::ProjectsController do end context 'atom requests' do - let(:user) { create(:user) } - before do sign_in(user) end describe '#index' do - context 'project pagination' do - let(:projects) { create_list(:project, 2, creator: user) } + let_it_be(:projects) { create_list(:project, 2, creator: user) } + context 'project pagination' do before do allow(Kaminari.config).to receive(:default_per_page).and_return(1) @@ -138,6 +135,37 @@ RSpec.describe Dashboard::ProjectsController do expect(assigns(:events).count).to eq(2) end end + + describe 'rendering' do + include DesignManagementTestHelpers + render_views + + let(:project) { projects.first } + let!(:design_event) { create(:design_event, project: project) } + let!(:wiki_page_event) { create(:wiki_page_event, project: project) } + let!(:issue_event) { create(:closed_issue_event, project: project) } + let(:design) { design_event.design } + let(:wiki_page) { wiki_page_event.wiki_page } + let(:issue) { issue_event.issue } + + before do + enable_design_management + project.add_developer(user) + end + + it 'renders all kinds of event without error', :aggregate_failures do + get :index, format: :atom + + expect(assigns(:events)).to include(design_event, wiki_page_event, issue_event) + expect(response).to render_template('dashboard/projects/index') + expect(response.body).to include( + "uploaded design #{design.to_reference}", + "created wiki page #{wiki_page.title}", + "joined project #{project.full_name}", + "closed issue #{issue.to_reference}" + ) + end + end end end end diff --git a/spec/controllers/dashboard/snippets_controller_spec.rb b/spec/controllers/dashboard/snippets_controller_spec.rb index 3c316d07408..d981f738e70 100644 --- a/spec/controllers/dashboard/snippets_controller_spec.rb +++ b/spec/controllers/dashboard/snippets_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Dashboard::SnippetsController do - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } before do sign_in(user) @@ -26,5 +26,7 @@ RSpec.describe Dashboard::SnippetsController do get :index end + + it_behaves_like 'snippets sort order' end end diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb index f0aa351bee0..2e3328ae4d2 100644 --- a/spec/controllers/dashboard/todos_controller_spec.rb +++ b/spec/controllers/dashboard/todos_controller_spec.rb @@ -42,6 +42,15 @@ RSpec.describe Dashboard::TodosController do expect(response).to have_gitlab_http_status(:ok) end + + context 'tracking visits' do + let_it_be(:authorized_project) { create(:project, :public) } + + it_behaves_like 'tracking unique visits', :index do + let(:request_params) { { project_id: authorized_project.id } } + let(:target_id) { 'u_analytics_todos' } + end + end end context "with render_views" do diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index d27817c0a82..c838affa239 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -24,15 +24,20 @@ RSpec.describe DashboardController do end describe "GET activity as JSON" do + include DesignManagementTestHelpers render_views let(:user) { create(:user) } let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) } + let(:other_project) { create(:project, :public) } before do + enable_design_management create(:event, :created, project: project, target: create(:issue)) create(:wiki_page_event, :created, project: project) create(:wiki_page_event, :updated, project: project) + create(:design_event, project: project) + create(:design_event, author: user, project: other_project) sign_in(user) @@ -42,12 +47,13 @@ RSpec.describe DashboardController do context 'when user has permission to see the event' do before do project.add_developer(user) + other_project.add_developer(user) end it 'returns count' do get :activity, params: { format: :json } - expect(json_response['count']).to eq(3) + expect(json_response['count']).to eq(6) end end diff --git a/spec/controllers/every_controller_spec.rb b/spec/controllers/every_controller_spec.rb new file mode 100644 index 00000000000..4785ee9ed8f --- /dev/null +++ b/spec/controllers/every_controller_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe "Every controller" do + context "feature categories" do + let_it_be(:feature_categories) do + YAML.load_file(Rails.root.join('config', 'feature_categories.yml')).map(&:to_sym).to_set + end + + let_it_be(:controller_actions) do + # This will return tuples of all controller actions defined in the routes + # Only for controllers inheriting ApplicationController + # Excluding controllers from gems (OAuth, Sidekiq) + Rails.application.routes.routes + .map { |route| route.required_defaults.presence } + .compact + .select { |route| route[:controller].present? && route[:action].present? } + .map { |route| [constantize_controller(route[:controller]), route[:action]] } + .reject { |route| route.first.nil? || !route.first.include?(ControllerWithFeatureCategory) } + end + + let_it_be(:routes_without_category) do + controller_actions.map do |controller, action| + "#{controller}##{action}" unless controller.feature_category_for_action(action) + end.compact + end + + it "has feature categories" do + pending("We'll work on defining categories for all controllers: "\ + "https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/463") + + expect(routes_without_category).to be_empty, "#{routes_without_category.first(10)} did not have a category" + end + + it "completed controllers don't get new routes without categories" do + completed_controllers = [Projects::MergeRequestsController].map(&:to_s) + + newly_introduced_missing_category = routes_without_category.select do |route| + completed_controllers.any? { |controller| route.start_with?(controller) } + end + + expect(newly_introduced_missing_category).to be_empty + end + + it "recognizes the feature categories" do + routes_unknown_category = controller_actions.map do |controller, action| + used_category = controller.feature_category_for_action(action) + next unless used_category + next if used_category == :not_owned + + ["#{controller}##{action}", used_category] unless feature_categories.include?(used_category) + end.compact + + expect(routes_unknown_category).to be_empty, "#{routes_unknown_category.first(10)} had an unknown category" + end + + it "doesn't define or exclude categories on removed actions", :aggregate_failures do + controller_actions.group_by(&:first).each do |controller, controller_action| + existing_actions = controller_action.map(&:last) + used_actions = actions_defined_in_feature_category_config(controller) + non_existing_used_actions = used_actions - existing_actions + + expect(non_existing_used_actions).to be_empty, + "#{controller} used #{non_existing_used_actions} to define feature category, but the route does not exist" + end + end + end + + def constantize_controller(name) + "#{name.camelize}Controller".constantize + rescue NameError + nil # some controllers, like the omniauth ones are dynamic + end + + def actions_defined_in_feature_category_config(controller) + feature_category_configs = controller.send(:class_attributes)[:feature_category_config] + feature_category_configs.map do |config| + Array(config.send(:only)) + Array(config.send(:except)) + end.flatten.uniq.map(&:to_s) + end +end diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index 6765cf0990a..1593e1290c4 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -192,6 +192,46 @@ RSpec.describe Groups::ClustersController do end end + it_behaves_like 'GET #metrics_dashboard for dashboard', 'Cluster health' do + let(:cluster) { create(:cluster, :provided_by_gcp, cluster_type: :group_type, groups: [group]) } + + let(:metrics_dashboard_req_params) do + { + id: cluster.id, + group_id: group.name + } + end + end + + describe 'GET #prometheus_proxy' do + let(:proxyable) do + create(:cluster, :provided_by_gcp, cluster_type: :group_type, groups: [group]) + end + + it_behaves_like 'metrics dashboard prometheus api proxy' do + let(:proxyable_params) do + { + id: proxyable.id.to_s, + group_id: group.name + } + end + + context 'with anonymous user' do + let(:prometheus_body) { nil } + + before do + sign_out(user) + end + + it 'returns 404' do + get :prometheus_proxy, params: prometheus_proxy_params + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + end + describe 'POST create for new cluster' do let(:legacy_abac_param) { 'true' } let(:params) do diff --git a/spec/controllers/groups/imports_controller_spec.rb b/spec/controllers/groups/imports_controller_spec.rb index eb43a62b75b..7372c2e9575 100644 --- a/spec/controllers/groups/imports_controller_spec.rb +++ b/spec/controllers/groups/imports_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Groups::ImportsController do +RSpec.describe Groups::ImportsController do describe 'GET #show' do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group, :private) } diff --git a/spec/controllers/groups/runners_controller_spec.rb b/spec/controllers/groups/runners_controller_spec.rb index 376cd569952..91ff0a53ec7 100644 --- a/spec/controllers/groups/runners_controller_spec.rb +++ b/spec/controllers/groups/runners_controller_spec.rb @@ -6,6 +6,9 @@ RSpec.describe Groups::RunnersController do let(:user) { create(:user) } let(:group) { create(:group) } let(:runner) { create(:ci_runner, :group, groups: [group]) } + let(:project) { create(:project, group: group) } + let(:runner_project) { create(:ci_runner, :project, projects: [project]) } + let(:params_runner_project) { { group_id: group, id: runner_project } } let(:params) { { group_id: group, id: runner } } before do @@ -24,6 +27,13 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:show) end + + it 'renders show with 200 status code project runner' do + get :show, params: { group_id: group, id: runner_project } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:show) + end end context 'when user is not owner' do @@ -36,6 +46,12 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:not_found) end + + it 'renders a 404 project runner' do + get :show, params: { group_id: group, id: runner_project } + + expect(response).to have_gitlab_http_status(:not_found) + end end end @@ -51,6 +67,13 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:edit) end + + it 'renders show with 200 status code project runner' do + get :edit, params: { group_id: group, id: runner_project } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:edit) + end end context 'when user is not owner' do @@ -63,6 +86,12 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:not_found) end + + it 'renders a 404 project runner' do + get :edit, params: { group_id: group, id: runner_project } + + expect(response).to have_gitlab_http_status(:not_found) + end end end @@ -82,6 +111,17 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:found) expect(runner.reload.description).to eq(new_desc) end + + it 'updates the project runner, ticks the queue, and redirects project runner' do + new_desc = runner_project.description.swapcase + + expect do + post :update, params: params_runner_project.merge(runner: { description: new_desc } ) + end.to change { runner_project.ensure_runner_queue_value } + + expect(response).to have_gitlab_http_status(:found) + expect(runner_project.reload.description).to eq(new_desc) + end end context 'when user is not an owner' do @@ -99,6 +139,17 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:not_found) expect(runner.reload.description).to eq(old_desc) end + + it 'rejects the update and responds 404 project runner' do + old_desc = runner_project.description + + expect do + post :update, params: params_runner_project.merge(runner: { description: old_desc.swapcase } ) + end.not_to change { runner_project.ensure_runner_queue_value } + + expect(response).to have_gitlab_http_status(:not_found) + expect(runner_project.reload.description).to eq(old_desc) + end end end @@ -114,6 +165,31 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:found) expect(Ci::Runner.find_by(id: runner.id)).to be_nil end + + it 'destroys the project runner and redirects' do + delete :destroy, params: params_runner_project + + expect(response).to have_gitlab_http_status(:found) + expect(Ci::Runner.find_by(id: runner_project.id)).to be_nil + end + end + + context 'when user is an owner and runner in multiple projects' do + let(:project_2) { create(:project, group: group) } + let(:runner_project_2) { create(:ci_runner, :project, projects: [project, project_2]) } + let(:params_runner_project_2) { { group_id: group, id: runner_project_2 } } + + before do + group.add_owner(user) + end + + it 'does not destroy the project runner' do + delete :destroy, params: params_runner_project_2 + + expect(response).to have_gitlab_http_status(:found) + expect(flash[:alert]).to eq('Runner was not deleted because it is assigned to multiple projects.') + expect(Ci::Runner.find_by(id: runner_project_2.id)).to be_present + end end context 'when user is not an owner' do @@ -127,6 +203,13 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:not_found) expect(Ci::Runner.find_by(id: runner.id)).to be_present end + + it 'responds 404 and does not destroy the project runner' do + delete :destroy, params: params_runner_project + + expect(response).to have_gitlab_http_status(:not_found) + expect(Ci::Runner.find_by(id: runner_project.id)).to be_present + end end end @@ -146,6 +229,17 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:found) expect(runner.reload.active).to eq(true) end + + it 'marks the project runner as active, ticks the queue, and redirects' do + runner_project.update(active: false) + + expect do + post :resume, params: params_runner_project + end.to change { runner_project.ensure_runner_queue_value } + + expect(response).to have_gitlab_http_status(:found) + expect(runner_project.reload.active).to eq(true) + end end context 'when user is not an owner' do @@ -163,6 +257,17 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:not_found) expect(runner.reload.active).to eq(false) end + + it 'responds 404 and does not activate the project runner' do + runner_project.update(active: false) + + expect do + post :resume, params: params_runner_project + end.not_to change { runner_project.ensure_runner_queue_value } + + expect(response).to have_gitlab_http_status(:not_found) + expect(runner_project.reload.active).to eq(false) + end end end @@ -182,6 +287,17 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:found) expect(runner.reload.active).to eq(false) end + + it 'marks the project runner as inactive, ticks the queue, and redirects' do + runner_project.update(active: true) + + expect do + post :pause, params: params_runner_project + end.to change { runner_project.ensure_runner_queue_value } + + expect(response).to have_gitlab_http_status(:found) + expect(runner_project.reload.active).to eq(false) + end end context 'when user is not an owner' do @@ -199,6 +315,17 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:not_found) expect(runner.reload.active).to eq(true) end + + it 'responds 404 and does not update the project runner or queue' do + runner_project.update(active: true) + + expect do + post :pause, params: params + end.not_to change { runner_project.ensure_runner_queue_value } + + expect(response).to have_gitlab_http_status(:not_found) + expect(runner_project.reload.active).to eq(true) + end end end end diff --git a/spec/controllers/groups/settings/ci_cd_controller_spec.rb b/spec/controllers/groups/settings/ci_cd_controller_spec.rb index 55c19de4aa1..f11bb66caab 100644 --- a/spec/controllers/groups/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/groups/settings/ci_cd_controller_spec.rb @@ -5,8 +5,15 @@ require 'spec_helper' RSpec.describe Groups::Settings::CiCdController do include ExternalAuthorizationServiceHelpers - let(:group) { create(:group) } - let(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:sub_group) { create(:group, parent: group) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:project_2) { create(:project, group: sub_group) } + let_it_be(:runner_group) { create(:ci_runner, :group, groups: [group]) } + let_it_be(:runner_project_1) { create(:ci_runner, :project, projects: [project])} + let_it_be(:runner_project_2) { create(:ci_runner, :project, projects: [project_2])} + let_it_be(:runner_project_3) { create(:ci_runner, :project, projects: [project, project_2])} before do sign_in(user) @@ -18,11 +25,12 @@ RSpec.describe Groups::Settings::CiCdController do group.add_owner(user) end - it 'renders show with 200 status code' do + it 'renders show with 200 status code and correct runners' do get :show, params: { group_id: group } expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:show) + expect(assigns(:group_runners)).to match_array([runner_group, runner_project_1, runner_project_2, runner_project_3]) end end @@ -35,6 +43,7 @@ RSpec.describe Groups::Settings::CiCdController do get :show, params: { group_id: group } expect(response).to have_gitlab_http_status(:not_found) + expect(assigns(:group_runners)).to be_nil end end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index dce7105c073..469e58c94e7 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -52,8 +52,6 @@ RSpec.describe GroupsController do expect(assigns(:events).map(&:id)).to contain_exactly(event.id) end end - - it_behaves_like 'namespace storage limit alert' end describe 'GET #show' do @@ -941,7 +939,7 @@ RSpec.describe GroupsController do allow(Gitlab::ApplicationRateLimiter) .to receive(:increment) - .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:group_export][:threshold] + 1) + .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:group_export][:threshold].call + 1) end it 'throttles the endpoint' do @@ -1015,7 +1013,7 @@ RSpec.describe GroupsController do allow(Gitlab::ApplicationRateLimiter) .to receive(:increment) - .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:group_download_export][:threshold] + 1) + .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:group_download_export][:threshold].call + 1) end it 'throttles the endpoint' do @@ -1138,6 +1136,36 @@ RSpec.describe GroupsController do it_behaves_like 'disabled when using an external authorization service' end + describe "GET #activity as JSON" do + include DesignManagementTestHelpers + render_views + + let(:project) { create(:project, :public, group: group) } + let(:other_project) { create(:project, :public, group: group) } + + def get_activity + get :activity, params: { format: :json, id: group.to_param } + end + + before do + enable_design_management + issue = create(:issue, project: project) + create(:event, :created, project: project, target: issue) + create(:design_event, project: project) + create(:design_event, project: other_project) + + sign_in(user) + + request.cookies[:event_filter] = 'all' + end + + it 'returns count' do + get_activity + + expect(json_response['count']).to eq(3) + end + end + describe 'GET #issues' do subject { get :issues, params: { id: group.to_param } } diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb index ec38a635c2d..0427715d1ac 100644 --- a/spec/controllers/import/bitbucket_controller_spec.rb +++ b/spec/controllers/import/bitbucket_controller_spec.rb @@ -58,12 +58,12 @@ RSpec.describe Import::BitbucketController do before do @repo = double(name: 'vim', slug: 'vim', owner: 'asd', full_name: 'asd/vim', clone_url: 'http://test.host/demo/url.git', 'valid?' => true) @invalid_repo = double(name: 'mercurialrepo', slug: 'mercurialrepo', owner: 'asd', full_name: 'asd/mercurialrepo', clone_url: 'http://test.host/demo/mercurialrepo.git', 'valid?' => false) + allow(controller).to receive(:provider_url).and_return('http://demobitbucket.org') assign_session_tokens - stub_feature_flags(new_import_ui: false) end - it_behaves_like 'import controller with new_import_ui feature flag' do + it_behaves_like 'import controller status' do before do allow(controller).to receive(:provider_url).and_return('http://demobitbucket.org') end @@ -75,44 +75,16 @@ RSpec.describe Import::BitbucketController do let(:client_repos_field) { :repos } end - context 'with new_import_ui feature flag enabled' do - before do - stub_feature_flags(new_import_ui: true) - allow(controller).to receive(:provider_url).and_return('http://demobitbucket.org') - end - - it 'returns invalid repos' do - allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo, @invalid_repo]) - - get :status, format: :json - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['incompatible_repos'].length).to eq(1) - expect(json_response.dig("incompatible_repos", 0, "id")).to eq(@invalid_repo.full_name) - expect(json_response['provider_repos'].length).to eq(1) - expect(json_response.dig("provider_repos", 0, "id")).to eq(@repo.full_name) - end - end - - it "assigns variables" do - @project = create(:project, import_type: 'bitbucket', creator_id: user.id) - allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo]) + it 'returns invalid repos' do + allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo, @invalid_repo]) - get :status - - expect(assigns(:already_added_projects)).to eq([@project]) - expect(assigns(:repos)).to eq([@repo]) - expect(assigns(:incompatible_repos)).to eq([]) - end + get :status, format: :json - it "does not show already added project" do - @project = create(:project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim') - allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo]) - - get :status - - expect(assigns(:already_added_projects)).to eq([@project]) - expect(assigns(:repos)).to eq([]) + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['incompatible_repos'].length).to eq(1) + expect(json_response.dig("incompatible_repos", 0, "id")).to eq(@invalid_repo.full_name) + expect(json_response['provider_repos'].length).to eq(1) + expect(json_response.dig("provider_repos", 0, "id")).to eq(@repo.full_name) end context 'when filtering' do diff --git a/spec/controllers/import/bitbucket_server_controller_spec.rb b/spec/controllers/import/bitbucket_server_controller_spec.rb index af471b478fa..bb80de6425f 100644 --- a/spec/controllers/import/bitbucket_server_controller_spec.rb +++ b/spec/controllers/import/bitbucket_server_controller_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Import::BitbucketServerController do let(:user) { create(:user) } let(:project_key) { 'test-project' } let(:repo_slug) { 'some-repo' } + let(:repo_id) { "#{project_key}/#{repo_slug}" } let(:client) { instance_double(BitbucketServer::Client) } def assign_session_tokens @@ -46,7 +47,7 @@ RSpec.describe Import::BitbucketServerController do .to receive(:new).with(project_key, repo_slug, anything, project_name, user.namespace, user, anything) .and_return(double(execute: project)) - post :create, params: { project: project_key, repository: repo_slug }, format: :json + post :create, params: { repo_id: repo_id }, format: :json expect(response).to have_gitlab_http_status(:ok) end @@ -59,20 +60,20 @@ RSpec.describe Import::BitbucketServerController do .to receive(:new).with(project_key, repo_slug, anything, project_name, user.namespace, user, anything) .and_return(double(execute: project)) - post :create, params: { project: project_key, repository: repo_slug, format: :json } + post :create, params: { repo_id: repo_id }, format: :json expect(response).to have_gitlab_http_status(:ok) end end it 'returns an error when an invalid project key is used' do - post :create, params: { project: 'some&project' } + post :create, params: { repo_id: 'some&project/repo' } expect(response).to have_gitlab_http_status(:unprocessable_entity) end it 'returns an error when an invalid repository slug is used' do - post :create, params: { project: 'some-project', repository: 'try*this' } + post :create, params: { repo_id: 'some-project/try*this' } expect(response).to have_gitlab_http_status(:unprocessable_entity) end @@ -80,7 +81,7 @@ RSpec.describe Import::BitbucketServerController do it 'returns an error when the project cannot be found' do allow(client).to receive(:repo).with(project_key, repo_slug).and_return(nil) - post :create, params: { project: project_key, repository: repo_slug }, format: :json + post :create, params: { repo_id: repo_id }, format: :json expect(response).to have_gitlab_http_status(:unprocessable_entity) end @@ -90,15 +91,15 @@ RSpec.describe Import::BitbucketServerController do .to receive(:new).with(project_key, repo_slug, anything, project_name, user.namespace, user, anything) .and_return(double(execute: build(:project))) - post :create, params: { project: project_key, repository: repo_slug }, format: :json + post :create, params: { repo_id: repo_id }, format: :json expect(response).to have_gitlab_http_status(:unprocessable_entity) end it "returns an error when the server can't be contacted" do - expect(client).to receive(:repo).with(project_key, repo_slug).and_raise(::BitbucketServer::Connection::ConnectionError) + allow(client).to receive(:repo).with(project_key, repo_slug).and_raise(::BitbucketServer::Connection::ConnectionError) - post :create, params: { project: project_key, repository: repo_slug }, format: :json + post :create, params: { repo_id: repo_id }, format: :json expect(response).to have_gitlab_http_status(:unprocessable_entity) end @@ -123,7 +124,9 @@ RSpec.describe Import::BitbucketServerController do end it 'sets the session variables' do - post :configure, params: { personal_access_token: token, bitbucket_username: username, bitbucket_server_url: url } + allow(controller).to receive(:allow_local_requests?).and_return(true) + + post :configure, params: { personal_access_token: token, bitbucket_server_username: username, bitbucket_server_url: url } expect(session[:bitbucket_server_url]).to eq(url) expect(session[:bitbucket_server_username]).to eq(username) @@ -145,28 +148,21 @@ RSpec.describe Import::BitbucketServerController do @invalid_repo = double(slug: 'invalid', project_key: 'foobar', full_name: 'asd/foobar', "valid?" => false, browse_url: 'http://bad-repo', name: 'invalid') @created_repo = double(slug: 'created', project_key: 'existing', full_name: 'group/created', "valid?" => true, browse_url: 'http://existing') assign_session_tokens - stub_feature_flags(new_import_ui: false) end - context 'with new_import_ui feature flag enabled' do - before do - stub_feature_flags(new_import_ui: true) - end - - it 'returns invalid repos' do - allow(client).to receive(:repos).with(filter: nil, limit: 25, page_offset: 0).and_return([@repo, @invalid_repo]) + it 'returns invalid repos' do + allow(client).to receive(:repos).with(filter: nil, limit: 25, page_offset: 0).and_return([@repo, @invalid_repo]) - get :status, format: :json + get :status, format: :json - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['incompatible_repos'].length).to eq(1) - expect(json_response.dig("incompatible_repos", 0, "id")).to eq(@invalid_repo.full_name) - expect(json_response['provider_repos'].length).to eq(1) - expect(json_response.dig("provider_repos", 0, "id")).to eq(@repo.full_name) - end + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['incompatible_repos'].length).to eq(1) + expect(json_response.dig("incompatible_repos", 0, "id")).to eq(@invalid_repo.full_name) + expect(json_response['provider_repos'].length).to eq(1) + expect(json_response.dig("provider_repos", 0, "id")).to eq(@repo.full_name) end - it_behaves_like 'import controller with new_import_ui feature flag' do + it_behaves_like 'import controller status' do let(:repo) { @repo } let(:repo_id) { @repo.full_name } let(:import_source) { @repo.browse_url } @@ -174,47 +170,14 @@ RSpec.describe Import::BitbucketServerController do let(:client_repos_field) { :repos } end - it 'assigns repository categories' do - created_project = create(:project, :import_finished, import_type: 'bitbucket_server', creator_id: user.id, import_source: @created_repo.browse_url) - - expect(repos).to receive(:partition).and_return([[@repo, @created_repo], [@invalid_repo]]) - expect(repos).to receive(:current_page).and_return(1) - expect(repos).to receive(:next_page).and_return(2) - expect(repos).to receive(:prev_page).and_return(nil) - expect(client).to receive(:repos).and_return(repos) - - get :status - - expect(assigns(:already_added_projects)).to eq([created_project]) - expect(assigns(:repos)).to eq([@repo]) - expect(assigns(:incompatible_repos)).to eq([@invalid_repo]) - end - context 'when filtering' do let(:filter) { 'test' } it 'passes filter param to bitbucket client' do - expect(repos).to receive(:partition).and_return([[@repo, @created_repo], [@invalid_repo]]) - expect(client).to receive(:repos).with(filter: filter, limit: 25, page_offset: 0).and_return(repos) + expect(client).to receive(:repos).with(filter: filter, limit: 25, page_offset: 0).and_return([@repo]) get :status, params: { filter: filter }, as: :json end end end - - describe 'GET jobs' do - before do - assign_session_tokens - end - - it 'returns a list of imported projects' do - created_project = create(:project, import_type: 'bitbucket_server', creator_id: user.id) - - get :jobs - - expect(json_response.count).to eq(1) - expect(json_response.first['id']).to eq(created_project.id) - expect(json_response.first['import_status']).to eq('none') - end - end end diff --git a/spec/controllers/import/fogbugz_controller_spec.rb b/spec/controllers/import/fogbugz_controller_spec.rb index aabbcb30358..376c089df78 100644 --- a/spec/controllers/import/fogbugz_controller_spec.rb +++ b/spec/controllers/import/fogbugz_controller_spec.rb @@ -82,36 +82,15 @@ RSpec.describe Import::FogbugzController do before do @repo = OpenStruct.new(id: 'demo', name: 'vim') stub_client(valid?: true) - stub_feature_flags(new_import_ui: false) end - it_behaves_like 'import controller with new_import_ui feature flag' do + it_behaves_like 'import controller status' do let(:repo) { @repo } let(:repo_id) { @repo.id } let(:import_source) { @repo.name } let(:provider_name) { 'fogbugz' } let(:client_repos_field) { :repos } end - - it 'assigns variables' do - @project = create(:project, import_type: 'fogbugz', creator_id: user.id) - stub_client(repos: [@repo]) - - get :status - - expect(assigns(:already_added_projects)).to eq([@project]) - expect(assigns(:repos)).to eq([@repo]) - end - - it 'does not show already added project' do - @project = create(:project, import_type: 'fogbugz', creator_id: user.id, import_source: 'vim') - stub_client(repos: [@repo]) - - get :status - - expect(assigns(:already_added_projects)).to eq([@project]) - expect(assigns(:repos)).to eq([]) - end end describe 'POST create' do diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb index 1cd0593f762..42c4348dac2 100644 --- a/spec/controllers/import/gitlab_controller_spec.rb +++ b/spec/controllers/import/gitlab_controller_spec.rb @@ -36,36 +36,15 @@ RSpec.describe Import::GitlabController do before do @repo = OpenStruct.new(id: 1, path: 'vim', path_with_namespace: 'asd/vim', web_url: 'https://gitlab.com/asd/vim') assign_session_token - stub_feature_flags(new_import_ui: false) end - it_behaves_like 'import controller with new_import_ui feature flag' do + it_behaves_like 'import controller status' do let(:repo) { @repo } let(:repo_id) { @repo.id } let(:import_source) { @repo.path_with_namespace } let(:provider_name) { 'gitlab' } let(:client_repos_field) { :projects } end - - it "assigns variables" do - @project = create(:project, import_type: 'gitlab', creator_id: user.id) - stub_client(projects: [@repo]) - - get :status - - expect(assigns(:already_added_projects)).to eq([@project]) - expect(assigns(:repos)).to eq([@repo]) - end - - it "does not show already added project" do - @project = create(:project, import_type: 'gitlab', creator_id: user.id, import_source: 'asd/vim') - stub_client(projects: [@repo]) - - get :status - - expect(assigns(:already_added_projects)).to eq([@project]) - expect(assigns(:repos)).to eq([]) - end end describe "POST create" do diff --git a/spec/controllers/instance_statistics/cohorts_controller_spec.rb b/spec/controllers/instance_statistics/cohorts_controller_spec.rb index b92fcb2575c..c16ac0dced1 100644 --- a/spec/controllers/instance_statistics/cohorts_controller_spec.rb +++ b/spec/controllers/instance_statistics/cohorts_controller_spec.rb @@ -18,4 +18,11 @@ RSpec.describe InstanceStatistics::CohortsController do expect(response).to have_gitlab_http_status(:not_found) end + + describe 'GET #index' do + it_behaves_like 'tracking unique visits', :index do + let(:request_params) { {} } + let(:target_id) { 'i_analytics_cohorts' } + end + end end diff --git a/spec/controllers/instance_statistics/dev_ops_score_controller_spec.rb b/spec/controllers/instance_statistics/dev_ops_score_controller_spec.rb index d729682bef0..c9677a64eef 100644 --- a/spec/controllers/instance_statistics/dev_ops_score_controller_spec.rb +++ b/spec/controllers/instance_statistics/dev_ops_score_controller_spec.rb @@ -4,4 +4,17 @@ require 'spec_helper' RSpec.describe InstanceStatistics::DevOpsScoreController do it_behaves_like 'instance statistics availability' + + describe 'GET #index' do + let(:user) { create(:user) } + + before do + sign_in(user) + end + + it_behaves_like 'tracking unique visits', :index do + let(:request_params) { {} } + let(:target_id) { 'i_analytics_dev_ops_score' } + end + end end diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb index f2821bb67e8..a9e4073780d 100644 --- a/spec/controllers/invites_controller_spec.rb +++ b/spec/controllers/invites_controller_spec.rb @@ -4,21 +4,44 @@ require 'spec_helper' RSpec.describe InvitesController do let(:token) { '123456' } - let(:user) { create(:user) } - let(:member) { create(:project_member, invite_token: token, invite_email: 'test@abc.com', user: user) } + let_it_be(:user) { create(:user) } + let(:member) { create(:project_member, :invited, invite_token: token, invite_email: user.email) } + let(:project_members) { member.source.users } before do controller.instance_variable_set(:@member, member) sign_in(user) end - describe 'GET #accept' do + describe 'GET #show' do + it 'accepts user if invite email matches signed in user' do + expect do + get :show, params: { id: token } + end.to change { project_members.include?(user) }.from(false).to(true) + + expect(response).to have_gitlab_http_status(:found) + expect(flash[:notice]).to include 'You have been granted' + end + + it 'forces re-confirmation if email does not match signed in user' do + member.invite_email = 'bogus@email.com' + + expect do + get :show, params: { id: token } + end.not_to change { project_members.include?(user) } + + expect(response).to have_gitlab_http_status(:ok) + expect(flash[:notice]).to be_nil + end + end + + describe 'POST #accept' do it 'accepts user' do - get :accept, params: { id: token } - member.reload + expect do + post :accept, params: { id: token } + end.to change { project_members.include?(user) }.from(false).to(true) expect(response).to have_gitlab_http_status(:found) - expect(member.user).to eq(user) expect(flash[:notice]).to include 'You have been granted' end end @@ -26,8 +49,8 @@ RSpec.describe InvitesController do describe 'GET #decline' do it 'declines user' do get :decline, params: { id: token } - expect {member.reload}.to raise_error ActiveRecord::RecordNotFound + expect { member.reload }.to raise_error ActiveRecord::RecordNotFound expect(response).to have_gitlab_http_status(:found) expect(flash[:notice]).to include 'You have declined the invitation to join' end diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb index f20204b6718..38f46ee7b15 100644 --- a/spec/controllers/oauth/applications_controller_spec.rb +++ b/spec/controllers/oauth/applications_controller_spec.rb @@ -121,6 +121,22 @@ RSpec.describe Oauth::ApplicationsController do end end + describe 'locale' do + let(:user) { create(:user, preferred_language: 'uk') } + + before do + sign_in(user) + + allow(Gitlab::I18n).to receive(:with_locale).and_call_original + end + + it "sets user's locale" do + expect(Gitlab::I18n).to receive(:with_locale).with('uk') + + get :new + end + end + def disable_user_oauth allow(Gitlab::CurrentSettings.current_application_settings).to receive(:user_oauth_applications?).and_return(false) end diff --git a/spec/controllers/profiles/keys_controller_spec.rb b/spec/controllers/profiles/keys_controller_spec.rb index 258ed62262a..66f6135df1e 100644 --- a/spec/controllers/profiles/keys_controller_spec.rb +++ b/spec/controllers/profiles/keys_controller_spec.rb @@ -20,69 +20,4 @@ RSpec.describe Profiles::KeysController do expect(Key.last.expires_at).to be_like_time(expires_at) end end - - describe "#get_keys" do - describe "non existent user" do - it "does not generally work" do - get :get_keys, params: { username: 'not-existent' } - - expect(response).not_to be_successful - end - end - - describe "user with no keys" do - it "does generally work" do - get :get_keys, params: { username: user.username } - - expect(response).to be_successful - end - - it "renders all keys separated with a new line" do - get :get_keys, params: { username: user.username } - - expect(response.body).to eq("") - end - - it "responds with text/plain content type" do - get :get_keys, params: { username: user.username } - expect(response.content_type).to eq("text/plain") - end - end - - describe "user with keys" do - let!(:key) { create(:key, user: user) } - let!(:another_key) { create(:another_key, user: user) } - let!(:deploy_key) { create(:deploy_key, user: user) } - - it "does generally work" do - get :get_keys, params: { username: user.username } - - expect(response).to be_successful - end - - it "renders all non deploy keys separated with a new line" do - get :get_keys, params: { username: user.username } - - expect(response.body).not_to eq('') - expect(response.body).to eq(user.all_ssh_keys.join("\n")) - - expect(response.body).to include(key.key.sub(' dummy@gitlab.com', '')) - expect(response.body).to include(another_key.key.sub(' dummy@gitlab.com', '')) - - expect(response.body).not_to include(deploy_key.key) - end - - it "does not render the comment of the key" do - get :get_keys, params: { username: user.username } - - expect(response.body).not_to match(/dummy@gitlab.com/) - end - - it "responds with text/plain content type" do - get :get_keys, params: { username: user.username } - - expect(response.content_type).to eq("text/plain") - end - end - end end 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('BUILD TRACE') - 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/pipelines/stages_controller_spec.rb b/spec/controllers/projects/pipelines/stages_controller_spec.rb new file mode 100644 index 00000000000..6e8c08d95a1 --- /dev/null +++ b/spec/controllers/projects/pipelines/stages_controller_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::Pipelines::StagesController do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + + before do + sign_in(user) + end + + describe 'POST #play_manual.json' do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:stage_name) { 'test' } + + before do + create_manual_build(pipeline, 'test', 'rspec 1/2') + create_manual_build(pipeline, 'test', 'rspec 2/2') + + pipeline.reload + end + + context 'when user does not have access' do + it 'returns not authorized' do + play_manual_stage! + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when user has access' do + before do + project.add_maintainer(user) + end + + context 'when the stage does not exists' do + let(:stage_name) { 'deploy' } + + it 'fails to play all manual' do + play_manual_stage! + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when the stage exists' do + it 'starts all manual jobs' do + expect(pipeline.builds.manual.count).to eq(2) + + play_manual_stage! + + expect(response).to have_gitlab_http_status(:ok) + expect(pipeline.builds.manual.count).to eq(0) + end + end + end + + def play_manual_stage! + post :play_manual, params: { + namespace_id: project.namespace, + project_id: project, + pipeline_id: pipeline.id, + stage_name: stage_name + }, format: :json + end + + def create_manual_build(pipeline, stage, name) + create(:ci_build, :manual, pipeline: pipeline, stage: stage, name: name) + end + end +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/stages_controller_spec.rb b/spec/controllers/projects/stages_controller_spec.rb deleted file mode 100644 index dcf8607ae18..00000000000 --- a/spec/controllers/projects/stages_controller_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::StagesController do - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } - - before do - sign_in(user) - end - - describe 'POST #play_manual.json' do - let(:pipeline) { create(:ci_pipeline, project: project) } - let(:stage_name) { 'test' } - - before do - create_manual_build(pipeline, 'test', 'rspec 1/2') - create_manual_build(pipeline, 'test', 'rspec 2/2') - - pipeline.reload - end - - context 'when user does not have access' do - it 'returns not authorized' do - play_manual_stage! - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when user has access' do - before do - project.add_maintainer(user) - end - - context 'when the stage does not exists' do - let(:stage_name) { 'deploy' } - - it 'fails to play all manual' do - play_manual_stage! - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when the stage exists' do - it 'starts all manual jobs' do - expect(pipeline.builds.manual.count).to eq(2) - - play_manual_stage! - - expect(response).to have_gitlab_http_status(:ok) - expect(pipeline.builds.manual.count).to eq(0) - end - end - end - - def play_manual_stage! - post :play_manual, params: { - namespace_id: project.namespace, - project_id: project, - id: pipeline.id, - stage_name: stage_name - }, format: :json - end - - def create_manual_build(pipeline, stage, name) - create(:ci_build, :manual, pipeline: pipeline, stage: stage, name: name) - end - end -end 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 diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 8aae9ef85be..e59493827ba 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -6,7 +6,7 @@ RSpec.describe ProjectsController do include ExternalAuthorizationServiceHelpers include ProjectForksHelper - let(:project) { create(:project) } + let(:project) { create(:project, service_desk_enabled: false) } let(:public_project) { create(:project, :public) } let(:user) { create(:user) } let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') } @@ -86,11 +86,13 @@ RSpec.describe ProjectsController do end describe "GET #activity as JSON" do + include DesignManagementTestHelpers render_views let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) } before do + enable_design_management create(:event, :created, project: project, target: create(:issue)) sign_in(user) @@ -103,11 +105,32 @@ RSpec.describe ProjectsController do project.add_developer(user) end - it 'returns count' do + def get_activity(project) get :activity, params: { namespace_id: project.namespace, id: project, format: :json } + end + + it 'returns count' do + get_activity(project) expect(json_response['count']).to eq(1) end + + context 'design events are visible' do + include DesignManagementTestHelpers + let(:other_project) { create(:project, namespace: user.namespace) } + + before do + enable_design_management + create(:design_event, project: project) + request.cookies[:event_filter] = EventFilter::DESIGNS + end + + it 'returns correct count' do + get_activity(project) + + expect(json_response['count']).to eq(1) + end + end end context 'when user has no permission to see the event' do @@ -350,45 +373,6 @@ RSpec.describe ProjectsController do .not_to exceed_query_limit(2).for_query(expected_query) end end - - context 'lfs_blob_ids instance variable' do - let(:project) { create(:project, :public, :repository) } - - before do - sign_in(user) - end - - context 'with vue tree view enabled' do - before do - get :show, params: { namespace_id: project.namespace, id: project } - 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, id: project } - end - - it 'is set' do - expect(assigns[:lfs_blob_ids]).not_to be_nil - end - end - end - - context 'namespace storage limit' do - let_it_be(:project) { create(:project, :public, :repository ) } - let(:namespace) { project.namespace } - - subject { get :show, params: { namespace_id: namespace, id: project } } - - it_behaves_like 'namespace storage limit alert' - end end describe 'GET edit' do @@ -1192,7 +1176,7 @@ RSpec.describe ProjectsController do before do allow(Gitlab::ApplicationRateLimiter) .to receive(:increment) - .and_return(Gitlab::ApplicationRateLimiter.rate_limits["project_#{action}".to_sym][:threshold] + 1) + .and_return(Gitlab::ApplicationRateLimiter.rate_limits["project_#{action}".to_sym][:threshold].call + 1) end it 'prevents requesting project export' do @@ -1259,7 +1243,7 @@ RSpec.describe ProjectsController do before do allow(Gitlab::ApplicationRateLimiter) .to receive(:increment) - .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:project_download_export][:threshold] + 1) + .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:project_download_export][:threshold].call + 1) end it 'prevents requesting project export' do @@ -1400,6 +1384,27 @@ RSpec.describe ProjectsController do end end + it 'updates Service Desk attributes' do + project.add_maintainer(user) + sign_in(user) + allow(Gitlab::IncomingEmail).to receive(:enabled?) { true } + allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true } + params = { + service_desk_enabled: true + } + + put :update, + params: { + namespace_id: project.namespace, + id: project, + project: params + } + project.reload + + expect(response).to have_gitlab_http_status(:found) + expect(project.service_desk_enabled).to eq(true) + end + def project_moved_message(redirect_route, project) "Project '#{redirect_route.path}' was moved to '#{project.full_path}'. Please update any links and bookmarks that may still have the old path." end diff --git a/spec/controllers/registrations/experience_levels_controller_spec.rb b/spec/controllers/registrations/experience_levels_controller_spec.rb index 1fc728f5de8..5a217a3a684 100644 --- a/spec/controllers/registrations/experience_levels_controller_spec.rb +++ b/spec/controllers/registrations/experience_levels_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Registrations::ExperienceLevelsController do +RSpec.describe Registrations::ExperienceLevelsController do let_it_be(:namespace) { create(:group, path: 'group-path' ) } let_it_be(:user) { create(:user) } @@ -99,34 +99,51 @@ describe Registrations::ExperienceLevelsController do end describe 'applying the chosen level' do - context "when an 'onboarding_issues_settings' cookie does not exist" do - let(:params) { super().merge(experience_level: :novice) } - - it 'does not change the cookie' do - expect { subject }.not_to change { cookies[:onboarding_issues_settings] } - end - end - - context "when an 'onboarding_issues_settings' cookie does exist" do + context 'when a "Learn GitLab" project is available' do before do - request.cookies[:onboarding_issues_settings] = '{}' + allow_next_instance_of(LearnGitlab) do |learn_gitlab| + allow(learn_gitlab).to receive(:available?).and_return(true) + allow(learn_gitlab).to receive(:label).and_return(double(id: 1)) + end end context 'when novice' do let(:params) { super().merge(experience_level: :novice) } - it "adds a 'hideAdvanced' setting to the cookie" do - expect { subject }.to change { Gitlab::Json.parse(cookies[:onboarding_issues_settings])['hideAdvanced'] }.from(nil).to(true) + it 'adds a BoardLabel' do + expect_next_instance_of(Boards::UpdateService) do |service| + expect(service).to receive(:execute) + end + + subject end end context 'when experienced' do let(:params) { super().merge(experience_level: :experienced) } - it 'does not change the cookie' do - expect { subject }.not_to change { cookies[:onboarding_issues_settings] } + it 'does not add a BoardLabel' do + expect(Boards::UpdateService).not_to receive(:new) + + subject + end + end + end + + context 'when no "Learn GitLab" project exists' do + let(:params) { super().merge(experience_level: :novice) } + + before do + allow_next_instance_of(LearnGitlab) do |learn_gitlab| + allow(learn_gitlab).to receive(:available?).and_return(false) end end + + it 'does not add a BoardLabel' do + expect(Boards::UpdateService).not_to receive(:new) + + subject + end end end end diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index bae6bd07b67..0849fb00e73 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -211,4 +211,9 @@ RSpec.describe SearchController do end.to raise_error(ActionController::ParameterMissing) end end + + describe 'GET #autocomplete' do + it_behaves_like 'when the user cannot read cross project', :autocomplete, { term: 'hello' } + it_behaves_like 'with external authorization service enabled', :autocomplete, { term: 'hello' } + end end diff --git a/spec/controllers/snippets/blobs_controller_spec.rb b/spec/controllers/snippets/blobs_controller_spec.rb new file mode 100644 index 00000000000..b9f58587a58 --- /dev/null +++ b/spec/controllers/snippets/blobs_controller_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Snippets::BlobsController do + using RSpec::Parameterized::TableSyntax + include SnippetHelpers + + describe 'GET #raw' do + let_it_be(:author) { create(:user) } + let_it_be(:other_user) { create(:user) } + + let(:visibility) { :public } + let(:snippet) { create(:personal_snippet, visibility, :repository, author: author) } + let(:filepath) { 'file1' } + let(:ref) { TestEnv::BRANCH_SHA['snippet/single-file'] } + let(:inline) { nil } + + subject do + get(:raw, + params: { + snippet_id: snippet, + path: filepath, + ref: ref, + inline: inline + }) + end + + where(:snippet_visibility_level, :user, :status) do + :public | :author | :ok + :public | :other_user | :ok + :public | nil | :ok + + :private | :author | :ok + :private | :other_user | :not_found + :private | nil | :redirect + end + + with_them do + let(:visibility) { snippet_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' + + context 'with a snippet without a repository' do + let(:snippet) { create(:personal_snippet, visibility, author: author) } + + it_behaves_like 'raw snippet without repository', :redirect + end + end +end diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index 70df1faf7dd..92370b3381a 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -6,6 +6,8 @@ RSpec.describe SnippetsController do let_it_be(:user) { create(:user) } describe 'GET #index' do + let(:base_params) { { username: user.username } } + context 'when username parameter is present' do it_behaves_like 'paginated collection' do let(:collection) { Snippet.all } @@ -38,6 +40,10 @@ RSpec.describe SnippetsController do expect(response).to redirect_to(dashboard_snippets_path) end end + + it_behaves_like 'snippets sort order' do + let(:params) { base_params } + end end describe 'GET #new' do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index bec4b24484a..99c3b82bd0d 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -114,6 +114,71 @@ RSpec.describe UsersController do end end + describe "#ssh_keys" do + describe "non existent user" do + it "does not generally work" do + get :ssh_keys, params: { username: 'not-existent' } + + expect(response).not_to be_successful + end + end + + describe "user with no keys" do + it "does generally work" do + get :ssh_keys, params: { username: user.username } + + expect(response).to be_successful + end + + it "renders all keys separated with a new line" do + get :ssh_keys, params: { username: user.username } + + expect(response.body).to eq("") + end + + it "responds with text/plain content type" do + get :ssh_keys, params: { username: user.username } + expect(response.content_type).to eq("text/plain") + end + end + + describe "user with keys" do + let!(:key) { create(:key, user: user) } + let!(:another_key) { create(:another_key, user: user) } + let!(:deploy_key) { create(:deploy_key, user: user) } + + it "does generally work" do + get :ssh_keys, params: { username: user.username } + + expect(response).to be_successful + end + + it "renders all non deploy keys separated with a new line" do + get :ssh_keys, params: { username: user.username } + + expect(response.body).not_to eq('') + expect(response.body).to eq(user.all_ssh_keys.join("\n")) + + expect(response.body).to include(key.key.sub(' dummy@gitlab.com', '')) + expect(response.body).to include(another_key.key.sub(' dummy@gitlab.com', '')) + + expect(response.body).not_to include(deploy_key.key) + end + + it "does not render the comment of the key" do + get :ssh_keys, params: { username: user.username } + + expect(response.body).not_to match(/dummy@gitlab.com/) + end + + it "responds with text/plain content type" do + get :ssh_keys, params: { username: user.username } + + expect(response.content_type).to eq("text/plain") + end + end + end + describe 'GET #calendar' do context 'for user' do let(:project) { create(:project) } -- cgit v1.2.1