diff options
author | Igor <idrozdov@gitlab.com> | 2019-08-05 15:06:02 +0000 |
---|---|---|
committer | Igor <idrozdov@gitlab.com> | 2019-08-05 15:06:02 +0000 |
commit | 7efb062c3c3c7b44113d0dc0fe78fc9b8e95bd7c (patch) | |
tree | a12bde9bbeffcc0c365d3a29339d0389dcefdd8f /spec/controllers | |
parent | 2bd1320f86b8cfd5d60199c5f7f0caa1cc2aa66b (diff) | |
parent | 3dfc89ade452ad7f0185653b30ed1d4bb2544fb0 (diff) | |
download | gitlab-ce-id-test-codeowners.tar.gz |
Merge branch 'master' into 'id-test-codeowners'id-test-codeowners
# Conflicts:
# .gitlab/CODEOWNERS
Diffstat (limited to 'spec/controllers')
37 files changed, 1046 insertions, 195 deletions
diff --git a/spec/controllers/admin/clusters/applications_controller_spec.rb b/spec/controllers/admin/clusters/applications_controller_spec.rb index cf202d88acc..9d6edcd80c0 100644 --- a/spec/controllers/admin/clusters/applications_controller_spec.rb +++ b/spec/controllers/admin/clusters/applications_controller_spec.rb @@ -84,7 +84,7 @@ describe Admin::Clusters::ApplicationsController do patch :update, params: params end - let!(:application) { create(:clusters_applications_cert_managers, :installed, cluster: cluster) } + let!(:application) { create(:clusters_applications_cert_manager, :installed, cluster: cluster) } let(:application_name) { application.name } let(:params) { { application: application_name, id: cluster.id, email: "new-email@example.com" } } diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 509d8944e3a..1123563c1e3 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -68,5 +68,13 @@ describe Admin::GroupsController do post :update, params: { id: group.to_param, group: { project_creation_level: ::Gitlab::Access::NO_ONE_PROJECT_ACCESS } } end.to change { group.reload.project_creation_level }.to(::Gitlab::Access::NO_ONE_PROJECT_ACCESS) end + + it 'updates the subgroup_creation_level successfully' do + expect do + post :update, + params: { id: group.to_param, + group: { subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS } } + end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end end end diff --git a/spec/controllers/admin/requests_profiles_controller_spec.rb b/spec/controllers/admin/requests_profiles_controller_spec.rb index 10850cb4603..345f7720c25 100644 --- a/spec/controllers/admin/requests_profiles_controller_spec.rb +++ b/spec/controllers/admin/requests_profiles_controller_spec.rb @@ -10,38 +10,63 @@ describe Admin::RequestsProfilesController do end describe '#show' do - let(:basename) { "profile_#{Time.now.to_i}.html" } let(:tmpdir) { Dir.mktmpdir('profiler-test') } let(:test_file) { File.join(tmpdir, basename) } - let(:profile) { Gitlab::RequestProfiler::Profile.new(basename) } - let(:sample_data) do - <<~HTML - <!DOCTYPE html> - <html> - <body> - <h1>My First Heading</h1> - <p>My first paragraph.</p> - </body> - </html> - HTML + + subject do + get :show, params: { name: basename } end before do stub_const('Gitlab::RequestProfiler::PROFILES_DIR', tmpdir) - output = File.open(test_file, 'w') - output.write(sample_data) - output.close + File.write(test_file, sample_data) end after do - File.unlink(test_file) + FileUtils.rm_rf(tmpdir) end - it 'loads an HTML profile' do - get :show, params: { name: basename } + context 'when loading HTML profile' do + let(:basename) { "profile_#{Time.now.to_i}_execution.html" } + + let(:sample_data) do + '<html> <body> <h1>Heading</h1> <p>paragraph.</p> </body> </html>' + end + + it 'renders the data' do + subject + + expect(response).to have_gitlab_http_status(200) + expect(response.body).to eq(sample_data) + end + end + + context 'when loading TXT profile' do + let(:basename) { "profile_#{Time.now.to_i}_memory.txt" } + + let(:sample_data) do + <<~TXT + Total allocated: 112096396 bytes (1080431 objects) + Total retained: 10312598 bytes (53567 objects) + TXT + end + + it 'renders the data' do + subject + + expect(response).to have_gitlab_http_status(200) + expect(response.body).to eq(sample_data) + end + end + + context 'when loading PDF profile' do + let(:basename) { "profile_#{Time.now.to_i}_anything.pdf" } + + let(:sample_data) { 'mocked pdf content' } - expect(response).to have_gitlab_http_status(200) - expect(response.body).to eq(sample_data) + it 'fails to render the data' do + expect { subject }.to raise_error(ActionController::UrlGenerationError, /No route matches.*unmatched constraints:/) + end end end end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 89a0eba66f7..d7428f8b52c 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -279,6 +279,12 @@ describe Admin::UsersController do expect(warden.user).to eq(user) end + it 'logs the beginning of the impersonation event' do + expect(Gitlab::AppLogger).to receive(:info).with("User #{admin.username} has started impersonating #{user.username}").and_call_original + + post :impersonate, params: { id: user.username } + end + it "redirects to root" do post :impersonate, params: { id: user.username } diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 3f1c0ae8ac4..6cdd61e7abd 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -222,6 +222,20 @@ describe AutocompleteController do expect(response_user_ids).to contain_exactly(non_member.id) end end + + context 'merge_request_iid parameter included' do + before do + sign_in(user) + end + + it 'includes can_merge option to users' do + merge_request = create(:merge_request, source_project: project) + + get(:users, params: { merge_request_iid: merge_request.iid, project_id: project.id }) + + expect(json_response.first).to have_key('can_merge') + end + end end context 'GET projects' do @@ -295,28 +309,6 @@ describe AutocompleteController do end end - context 'authorized projects with offset' do - before do - authorized_project2 = create(:project) - authorized_project3 = create(:project) - - authorized_project.add_maintainer(user) - authorized_project2.add_maintainer(user) - authorized_project3.add_maintainer(user) - end - - describe 'GET #projects with project ID and offset_id' do - before do - get(:projects, params: { project_id: project.id, offset_id: authorized_project.id }) - end - - it 'returns projects' do - expect(json_response).to be_kind_of(Array) - expect(json_response.size).to eq 2 # Of a total of 3 - end - end - end - context 'authorized projects without admin_issue ability' do before do authorized_project.add_guest(user) diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb index 246d6f9e0f9..d54f7ad33cf 100644 --- a/spec/controllers/boards/issues_controller_spec.rb +++ b/spec/controllers/boards/issues_controller_spec.rb @@ -85,7 +85,7 @@ describe Boards::IssuesController do expect { list_issues(user: user, board: group_board, list: list3) }.not_to exceed_query_limit(control_count + (2 * 8 - 1)) end - it 'avoids N+1 database queries when adding a subgroup, project, and issue', :nested_groups do + it 'avoids N+1 database queries when adding a subgroup, project, and issue' do create(:project, group: sub_group_1) create(:labeled_issue, project: project, labels: [development]) control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: group_board, list: list3) }.count @@ -160,7 +160,7 @@ describe Boards::IssuesController do end end - describe 'PUT move_multiple' do + describe 'PUT bulk_move' do let(:todo) { create(:group_label, group: group, name: 'Todo') } let(:development) { create(:group_label, group: group, name: 'Development') } let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user } @@ -196,6 +196,20 @@ describe Boards::IssuesController do sign_in(signed_in_user) end + it 'responds as expected' do + put :bulk_move, params: move_issues_params + expect(response).to have_gitlab_http_status(expected_status) + + if expected_status == 200 + expect(json_response).to include( + 'count' => move_issues_params[:ids].size, + 'success' => true + ) + + expect(json_response['issues'].pluck('id')).to match_array(move_issues_params[:ids]) + end + end + it 'moves issues as expected' do put :bulk_move, params: move_issues_params expect(response).to have_gitlab_http_status(expected_status) diff --git a/spec/controllers/chaos_controller_spec.rb b/spec/controllers/chaos_controller_spec.rb new file mode 100644 index 00000000000..bafd4a70862 --- /dev/null +++ b/spec/controllers/chaos_controller_spec.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ChaosController do + describe '#leakmem' do + it 'calls synchronously' do + expect(Gitlab::Chaos).to receive(:leak_mem).with(100, 30.seconds) + + get :leakmem + + expect(response).to have_gitlab_http_status(200) + end + + it 'call synchronously with params' do + expect(Gitlab::Chaos).to receive(:leak_mem).with(1, 2.seconds) + + get :leakmem, params: { memory_mb: 1, duration_s: 2 } + + expect(response).to have_gitlab_http_status(200) + end + + it 'calls asynchronously' do + expect(Chaos::LeakMemWorker).to receive(:perform_async).with(100, 30.seconds) + + get :leakmem, params: { async: 1 } + + expect(response).to have_gitlab_http_status(200) + end + end + + describe '#cpu_spin' do + it 'calls synchronously' do + expect(Gitlab::Chaos).to receive(:cpu_spin).with(30.seconds) + + get :cpu_spin + + expect(response).to have_gitlab_http_status(200) + end + + it 'calls synchronously with params' do + expect(Gitlab::Chaos).to receive(:cpu_spin).with(3.seconds) + + get :cpu_spin, params: { duration_s: 3 } + + expect(response).to have_gitlab_http_status(200) + end + + it 'calls asynchronously' do + expect(Chaos::CpuSpinWorker).to receive(:perform_async).with(30.seconds) + + get :cpu_spin, params: { async: 1 } + + expect(response).to have_gitlab_http_status(200) + end + end + + describe '#db_spin' do + it 'calls synchronously' do + expect(Gitlab::Chaos).to receive(:db_spin).with(30.seconds, 1.second) + + get :db_spin + + expect(response).to have_gitlab_http_status(200) + end + + it 'calls synchronously with params' do + expect(Gitlab::Chaos).to receive(:db_spin).with(4.seconds, 5.seconds) + + get :db_spin, params: { duration_s: 4, interval_s: 5 } + + expect(response).to have_gitlab_http_status(200) + end + + it 'calls asynchronously' do + expect(Chaos::DbSpinWorker).to receive(:perform_async).with(30.seconds, 1.second) + + get :db_spin, params: { async: 1 } + + expect(response).to have_gitlab_http_status(200) + end + end + + describe '#sleep' do + it 'calls synchronously' do + expect(Gitlab::Chaos).to receive(:sleep).with(30.seconds) + + get :sleep + + expect(response).to have_gitlab_http_status(200) + end + + it 'calls synchronously with params' do + expect(Gitlab::Chaos).to receive(:sleep).with(5.seconds) + + get :sleep, params: { duration_s: 5 } + + expect(response).to have_gitlab_http_status(200) + end + + it 'calls asynchronously' do + expect(Chaos::SleepWorker).to receive(:perform_async).with(30.seconds) + + get :sleep, params: { async: 1 } + + expect(response).to have_gitlab_http_status(200) + end + end + + describe '#kill' do + it 'calls synchronously' do + expect(Gitlab::Chaos).to receive(:kill).with(no_args) + + get :kill + + expect(response).to have_gitlab_http_status(200) + end + + it 'calls asynchronously' do + expect(Chaos::KillWorker).to receive(:perform_async).with(no_args) + + get :kill, params: { async: 1 } + + expect(response).to have_gitlab_http_status(200) + end + end +end diff --git a/spec/controllers/concerns/group_tree_spec.rb b/spec/controllers/concerns/group_tree_spec.rb index aa3cd690e3f..835c3d9b3af 100644 --- a/spec/controllers/concerns/group_tree_spec.rb +++ b/spec/controllers/concerns/group_tree_spec.rb @@ -30,7 +30,7 @@ describe GroupTree do expect(assigns(:groups)).to contain_exactly(other_group) end - context 'for subgroups', :nested_groups do + context 'for subgroups' do it 'only renders root groups when no parent was given' do create(:group, :public, parent: group) @@ -85,7 +85,7 @@ describe GroupTree do expect(json_response.first['id']).to eq(group.id) end - context 'nested groups', :nested_groups do + context 'nested groups' do it 'expands the tree when filtering' do subgroup = create(:group, :public, parent: group, name: 'filter') diff --git a/spec/controllers/concerns/issuable_actions_spec.rb b/spec/controllers/concerns/issuable_actions_spec.rb new file mode 100644 index 00000000000..7b0b4497f3f --- /dev/null +++ b/spec/controllers/concerns/issuable_actions_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe IssuableActions do + let(:project) { double('project') } + let(:user) { double('user') } + let(:issuable) { double('issuable') } + let(:finder_params_for_issuable) { {} } + let(:notes_result) { double('notes_result') } + let(:discussion_serializer) { double('discussion_serializer') } + + let(:controller) do + klass = Class.new do + attr_reader :current_user, :project, :issuable + + def self.before_action(action, params = nil) + end + + include IssuableActions + + def initialize(issuable, project, user, finder_params) + @issuable = issuable + @project = project + @current_user = user + @finder_params = finder_params + end + + def finder_params_for_issuable + @finder_params + end + + def params + { + notes_filter: 1 + } + end + + def prepare_notes_for_rendering(notes) + [] + end + + def render(options) + end + end + + klass.new(issuable, project, user, finder_params_for_issuable) + end + + describe '#discussions' do + before do + allow(user).to receive(:set_notes_filter) + allow(user).to receive(:user_preference) + allow(discussion_serializer).to receive(:represent) + end + + it 'instantiates and calls NotesFinder as expected' do + expect(Discussion).to receive(:build_collection).and_return([]) + expect(DiscussionSerializer).to receive(:new).and_return(discussion_serializer) + expect(NotesFinder).to receive(:new).with(user, finder_params_for_issuable).and_call_original + + expect_any_instance_of(NotesFinder).to receive(:execute).and_return(notes_result) + + expect(notes_result).to receive_messages(inc_relations_for_view: notes_result, includes: notes_result, fresh: notes_result) + + controller.discussions + end + end +end diff --git a/spec/controllers/dashboard/groups_controller_spec.rb b/spec/controllers/dashboard/groups_controller_spec.rb index 48373d29412..20a0951423b 100644 --- a/spec/controllers/dashboard/groups_controller_spec.rb +++ b/spec/controllers/dashboard/groups_controller_spec.rb @@ -26,7 +26,7 @@ describe Dashboard::GroupsController do expect(assigns(:groups)).to contain_exactly(member_of_group) end - context 'when rendering an expanded hierarchy with public groups you are not a member of', :nested_groups do + context 'when rendering an expanded hierarchy with public groups you are not a member of' do let!(:top_level_result) { create(:group, name: 'chef-top') } let!(:top_level_a) { create(:group, name: 'top-a') } let!(:sub_level_result_a) { create(:group, name: 'chef-sub-a', parent: top_level_a) } diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb index 4de537ae6f8..67939aa4e6a 100644 --- a/spec/controllers/dashboard/milestones_controller_spec.rb +++ b/spec/controllers/dashboard/milestones_controller_spec.rb @@ -47,6 +47,8 @@ describe Dashboard::MilestonesController do describe "#index" do let(:public_group) { create(:group, :public) } let!(:public_milestone) { create(:milestone, group: public_group) } + let!(:closed_group_milestone) { create(:milestone, group: group, state: 'closed') } + let!(:closed_project_milestone) { create(:milestone, project: project, state: 'closed') } render_views @@ -59,6 +61,15 @@ describe Dashboard::MilestonesController do expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name) end + it 'returns closed group and project milestones to which the user belongs' do + get :index, params: { state: 'closed' }, format: :json + + expect(response).to have_gitlab_http_status(200) + expect(json_response.size).to eq(2) + expect(json_response.map { |i| i["name"] }).to match_array([closed_group_milestone.name, closed_project_milestone.name]) + expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name) + end + it 'searches legacy project milestones by title when search_title is given' do project_milestone = create(:milestone, title: 'Project milestone title', project: project) @@ -77,11 +88,11 @@ describe Dashboard::MilestonesController do expect(response.body).not_to include(project_milestone.title) end - it 'shows counts of group and project milestones to which the user belongs to' do + it 'shows counts of open and closed group and project milestones to which the user belongs to' do get :index expect(response.body).to include("Open\n<span class=\"badge badge-pill\">2</span>") - expect(response.body).to include("Closed\n<span class=\"badge badge-pill\">0</span>") + expect(response.body).to include("Closed\n<span class=\"badge badge-pill\">2</span>") end context 'external authorization' do diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index c19a752b07b..9937bdf4061 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -7,6 +7,27 @@ describe GraphqlController do stub_feature_flags(graphql: true) end + describe 'ArgumentError' do + let(:user) { create(:user) } + let(:message) { 'green ideas sleep furiously' } + + before do + sign_in(user) + end + + it 'handles argument errors' do + allow(subject).to receive(:execute) do + raise Gitlab::Graphql::Errors::ArgumentError, message + end + + post :execute + + expect(json_response).to include( + 'errors' => include(a_hash_including('message' => message)) + ) + end + end + describe 'POST #execute' do context 'when user is logged in' do let(:user) { create(:user) } diff --git a/spec/controllers/groups/children_controller_spec.rb b/spec/controllers/groups/children_controller_spec.rb index 02fb971bd9a..bced300a24c 100644 --- a/spec/controllers/groups/children_controller_spec.rb +++ b/spec/controllers/groups/children_controller_spec.rb @@ -46,7 +46,7 @@ describe Groups::ChildrenController do end end - context 'for subgroups', :nested_groups do + context 'for subgroups' do let!(:public_subgroup) { create(:group, :public, parent: group) } let!(:private_subgroup) { create(:group, :private, parent: group) } let!(:public_project) { create(:project, :public, namespace: group) } @@ -292,7 +292,7 @@ describe Groups::ChildrenController do end end - context 'with subgroups and projects', :nested_groups do + context 'with subgroups and projects' do let!(:first_page_subgroups) { create_list(:group, per_page, :public, parent: group) } let!(:other_subgroup) { create(:group, :public, parent: group) } let!(:next_page_projects) { create_list(:project, per_page, :public, namespace: group) } diff --git a/spec/controllers/groups/clusters/applications_controller_spec.rb b/spec/controllers/groups/clusters/applications_controller_spec.rb index 16a63536ea6..21533d1c89a 100644 --- a/spec/controllers/groups/clusters/applications_controller_spec.rb +++ b/spec/controllers/groups/clusters/applications_controller_spec.rb @@ -91,7 +91,7 @@ describe Groups::Clusters::ApplicationsController do patch :update, params: params.merge(group_id: group) end - let!(:application) { create(:clusters_applications_cert_managers, :installed, cluster: cluster) } + let!(:application) { create(:clusters_applications_cert_manager, :installed, cluster: cluster) } let(:application_name) { application.name } let(:params) { { application: application_name, id: cluster.id, email: "new-email@example.com" } } diff --git a/spec/controllers/groups/labels_controller_spec.rb b/spec/controllers/groups/labels_controller_spec.rb index 3cc6fc6f066..98a4c50fc49 100644 --- a/spec/controllers/groups/labels_controller_spec.rb +++ b/spec/controllers/groups/labels_controller_spec.rb @@ -24,7 +24,7 @@ describe Groups::LabelsController do expect(label_ids).to match_array([label_1.title, group_label_1.title]) end - context 'with ancestor group', :nested_groups do + context 'with ancestor group' do set(:subgroup) { create(:group, parent: group) } set(:subgroup_label_1) { create(:group_label, group: subgroup, title: 'subgroup_label_1') } @@ -32,7 +32,7 @@ describe Groups::LabelsController do subgroup.add_owner(user) end - it 'returns ancestor group labels', :nested_groups do + it 'returns ancestor group labels' do get :index, params: { group_id: subgroup, include_ancestor_groups: true, only_group_labels: true }, format: :json label_ids = json_response.map {|label| label['title']} diff --git a/spec/controllers/groups/uploads_controller_spec.rb b/spec/controllers/groups/uploads_controller_spec.rb index 0f99a957581..60342bf8e3d 100644 --- a/spec/controllers/groups/uploads_controller_spec.rb +++ b/spec/controllers/groups/uploads_controller_spec.rb @@ -10,6 +10,11 @@ describe Groups::UploadsController do { group_id: model } end + let(:other_model) { create(:group, :public) } + let(:other_params) do + { group_id: other_model } + end + it_behaves_like 'handle uploads' do let(:uploader_class) { NamespaceFileUploader } end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index d2faef5b12b..404e61c5271 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -89,7 +89,7 @@ describe GroupsController do end describe 'GET #new' do - context 'when creating subgroups', :nested_groups do + context 'when creating subgroups' do [true, false].each do |can_create_group_status| context "and can_create_group is #{can_create_group_status}" do before do @@ -166,7 +166,7 @@ describe GroupsController do end end - context 'when creating subgroups', :nested_groups do + context 'when creating subgroups' do [true, false].each do |can_create_group_status| context "and can_create_group is #{can_create_group_status}" do context 'and logged in as Owner' do @@ -584,7 +584,7 @@ describe GroupsController do end end - describe 'PUT transfer', :postgresql do + describe 'PUT transfer' do before do sign_in(user) end diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb index dbfacf4e42e..43c910da7a5 100644 --- a/spec/controllers/help_controller_spec.rb +++ b/spec/controllers/help_controller_spec.rb @@ -111,7 +111,7 @@ describe HelpController do it 'renders the raw file' do get :show, params: { - path: 'user/project/img/labels_default' + path: 'user/project/img/labels_default_v12_1' }, format: :png expect(response).to be_success diff --git a/spec/controllers/ide_controller_spec.rb b/spec/controllers/ide_controller_spec.rb new file mode 100644 index 00000000000..0462f9520d5 --- /dev/null +++ b/spec/controllers/ide_controller_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe IdeController do + let(:user) { create(:user) } + + before do + sign_in(user) + end + + it 'increases the views counter' do + expect(Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_views_count) + + get :index + end +end diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb index 64a66502732..38388c21749 100644 --- a/spec/controllers/import/bitbucket_controller_spec.rb +++ b/spec/controllers/import/bitbucket_controller_spec.rb @@ -231,7 +231,7 @@ describe Import::BitbucketController do end end - context 'user has chosen an existing nested namespace and name for the project', :postgresql do + context 'user has chosen an existing nested namespace and name for the project' do let(:parent_namespace) { create(:group, name: 'foo') } let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) } let(:test_name) { 'test_name' } @@ -250,7 +250,7 @@ describe Import::BitbucketController do end end - context 'user has chosen a non-existent nested namespaces and name for the project', :postgresql do + context 'user has chosen a non-existent nested namespaces and name for the project' do let(:test_name) { 'test_name' } it 'takes the selected namespace and name' do @@ -281,7 +281,7 @@ describe Import::BitbucketController do end end - context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do + context 'user has chosen existent and non-existent nested namespaces and name for the project' do let(:test_name) { 'test_name' } let!(:parent_namespace) { create(:group, name: 'foo') } diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index 059354870b5..5675798ac33 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -33,6 +33,16 @@ describe Import::GithubController do expect(response).to have_http_status(200) end + + context 'when importing a CI/CD project' do + it 'always prompts for an access token' do + allow(controller).to receive(:github_import_configured?).and_return(true) + + get :new, params: { ci_cd_only: true } + + expect(response).to render_template(:new) + end + end end describe "GET callback" do diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb index 5af7572e74e..e465eca6c71 100644 --- a/spec/controllers/import/gitlab_controller_spec.rb +++ b/spec/controllers/import/gitlab_controller_spec.rb @@ -197,7 +197,7 @@ describe Import::GitlabController do end end - context 'user has chosen an existing nested namespace for the project', :postgresql do + context 'user has chosen an existing nested namespace for the project' do let(:parent_namespace) { create(:group, name: 'foo') } let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) } @@ -215,7 +215,7 @@ describe Import::GitlabController do end end - context 'user has chosen a non-existent nested namespaces for the project', :postgresql do + context 'user has chosen a non-existent nested namespaces for the project' do let(:test_name) { 'test_name' } it 'takes the selected namespace and name' do @@ -246,7 +246,7 @@ describe Import::GitlabController do end end - context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do + context 'user has chosen existent and non-existent nested namespaces and name for the project' do let(:test_name) { 'test_name' } let!(:parent_namespace) { create(:group, name: 'foo') } diff --git a/spec/controllers/projects/badges_controller_spec.rb b/spec/controllers/projects/badges_controller_spec.rb index 5ec8d8d41d7..4ae29ba7f54 100644 --- a/spec/controllers/projects/badges_controller_spec.rb +++ b/spec/controllers/projects/badges_controller_spec.rb @@ -7,51 +7,115 @@ describe Projects::BadgesController do let!(:pipeline) { create(:ci_empty_pipeline) } let(:user) { create(:user) } - before do - project.add_maintainer(user) - sign_in(user) - end + shared_examples 'a badge resource' do |badge_type| + context 'when pipelines are public' do + before do + project.update!(public_builds: true) + end + + context 'when project is public' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + end + + it "returns the #{badge_type} badge to unauthenticated users" do + get_badge(badge_type) + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when project is restricted' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + project.add_guest(user) + sign_in(user) + end + + it "returns the #{badge_type} badge to guest users" do + get_badge(badge_type) + + expect(response).to have_gitlab_http_status(:ok) + end + end + end - it 'requests the pipeline badge successfully' do - get_badge(:pipeline) + context 'format' do + before do + project.add_maintainer(user) + sign_in(user) + end - expect(response).to have_gitlab_http_status(:ok) - end + it 'renders the `flat` badge layout by default' do + get_badge(badge_type) - it 'requests the coverage badge successfully' do - get_badge(:coverage) + expect(response).to render_template('projects/badges/badge') + end - expect(response).to have_gitlab_http_status(:ok) - end + context 'when style param is set to `flat`' do + it 'renders the `flat` badge layout' do + get_badge(badge_type, 'flat') - it 'renders the `flat` badge layout by default' do - get_badge(:coverage) + expect(response).to render_template('projects/badges/badge') + end + end - expect(response).to render_template('projects/badges/badge') - end + context 'when style param is set to an invalid type' do + it 'renders the `flat` (default) badge layout' do + get_badge(badge_type, 'xxx') + + expect(response).to render_template('projects/badges/badge') + end + end - context 'when style param is set to `flat`' do - it 'renders the `flat` badge layout' do - get_badge(:coverage, 'flat') + context 'when style param is set to `flat-square`' do + it 'renders the `flat-square` badge layout' do + get_badge(badge_type, 'flat-square') - expect(response).to render_template('projects/badges/badge') + expect(response).to render_template('projects/badges/badge_flat-square') + end + end end - end - context 'when style param is set to an invalid type' do - it 'renders the `flat` (default) badge layout' do - get_badge(:coverage, 'xxx') + context 'when pipelines are not public' do + before do + project.update!(public_builds: false) + end - expect(response).to render_template('projects/badges/badge') + context 'when project is public' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + end + + it 'returns 404 to unauthenticated users' do + get_badge(badge_type) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when project is restricted to the user' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + project.add_guest(user) + sign_in(user) + end + + it 'defaults to project permissions' do + get_badge(:coverage) + + expect(response).to have_gitlab_http_status(:not_found) + end + end end end - context 'when style param is set to `flat-square`' do - it 'renders the `flat-square` badge layout' do - get_badge(:coverage, 'flat-square') + describe '#pipeline' do + it_behaves_like 'a badge resource', :pipeline + end - expect(response).to render_template('projects/badges/badge_flat-square') - end + describe '#coverage' do + it_behaves_like 'a badge resource', :coverage end def get_badge(badge, style = nil) diff --git a/spec/controllers/projects/cycle_analytics/events_controller_spec.rb b/spec/controllers/projects/cycle_analytics/events_controller_spec.rb new file mode 100644 index 00000000000..8fc3ae0aa32 --- /dev/null +++ b/spec/controllers/projects/cycle_analytics/events_controller_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::CycleAnalytics::EventsController do + let(:project) { create(:project, :repository) } + let(:user) { create(:user) } + + before do + sign_in(user) + project.add_maintainer(user) + end + + describe 'cycle analytics not set up flag' do + context 'with no data' do + it 'is empty' do + get_issue + + expect(response).to be_success + expect(JSON.parse(response.body)['events']).to be_empty + end + end + + context 'with data' do + let(:milestone) { create(:milestone, project: project, created_at: 10.days.ago) } + let(:issue) { create(:issue, project: project, created_at: 9.days.ago) } + + before do + issue.update(milestone: milestone) + end + + it 'is not empty' do + get_issue + + expect(response).to be_success + end + + it 'contains event detais' do + get_issue + + events = JSON.parse(response.body)['events'] + + expect(events).not_to be_empty + expect(events.first).to include('title', 'author', 'iid', 'total_time', 'created_at', 'url') + expect(events.first['title']).to eq(issue.title) + end + + context 'with data older than start date' do + it 'is empty' do + get_issue(additional_params: { cycle_analytics: { start_date: 7 } }) + + expect(response).to be_success + + expect(JSON.parse(response.body)['events']).to be_empty + end + end + end + end + + def get_issue(additional_params: {}) + params = additional_params.merge(namespace_id: project.namespace, project_id: project) + get(:issue, params: params, format: :json) + end +end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index 4c2c6160c62..8872e8d38e7 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe Projects::EnvironmentsController do + include MetricsDashboardHelpers + set(:user) { create(:user) } set(:project) { create(:project) } @@ -445,131 +447,186 @@ describe Projects::EnvironmentsController do end end - describe 'metrics_dashboard' do - context 'when prometheus endpoint is disabled' do - before do - stub_feature_flags(environment_metrics_use_prometheus_endpoint: false) - 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) - it 'responds with status code 403' do - get :metrics_dashboard, params: environment_params(format: :json) + # Exlcude `all_dashboards` to handle separately. + found_keys = json_response.keys - ['all_dashboards'] - expect(response).to have_gitlab_http_status(:forbidden) + expect(response).to have_gitlab_http_status(status_code) + expect(found_keys).to contain_exactly(*expected_keys) end end - shared_examples_for '200 response' do |contains_all_dashboards: false| + shared_examples_for '200 response' do let(:expected_keys) { %w(dashboard status) } - before do - expected_keys << 'all_dashboards' if contains_all_dashboards - end - - it 'returns a json representation of the environment dashboard' do - get :metrics_dashboard, params: environment_params(dashboard_params) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.keys).to contain_exactly(*expected_keys) - expect(json_response['dashboard']).to be_an_instance_of(Hash) - end + it_behaves_like 'correctly formatted response', :ok end - shared_examples_for 'error response' do |status_code, contains_all_dashboards: false| + shared_examples_for 'error response' do |status_code| let(:expected_keys) { %w(message status) } - before do - expected_keys << 'all_dashboards' if contains_all_dashboards - end + it_behaves_like 'correctly formatted response', status_code + end - it 'returns an error response' do + shared_examples_for 'includes all dashboards' do + it 'includes info for all findable dashboard' do get :metrics_dashboard, params: environment_params(dashboard_params) - expect(response).to have_gitlab_http_status(status_code) - expect(json_response.keys).to contain_exactly(*expected_keys) + expect(json_response).to have_key('all_dashboards') + expect(json_response['all_dashboards']).to be_an_instance_of(Array) + expect(json_response['all_dashboards']).to all( include('path', 'default', 'display_name') ) end end - shared_examples_for 'has all dashboards' do - it 'includes an index of all available dashboards' do + shared_examples_for 'the default dashboard' do + all_dashboards = Feature.enabled?(:environment_metrics_show_multiple_dashboards) + + it_behaves_like '200 response' + it_behaves_like 'includes all dashboards' if all_dashboards + + it 'is the default dashboard' do get :metrics_dashboard, params: environment_params(dashboard_params) - expect(json_response.keys).to include('all_dashboards') - expect(json_response['all_dashboards']).to be_an_instance_of(Array) - expect(json_response['all_dashboards']).to all( include('path', 'default') ) + expect(json_response['dashboard']['dashboard']).to eq('Environment metrics') end end - context 'when multiple dashboards is disabled' do - before do - stub_feature_flags(environment_metrics_show_multiple_dashboards: false) - end + shared_examples_for 'the specified dashboard' do |expected_dashboard| + it_behaves_like '200 response' + it_behaves_like 'includes all dashboards' - let(:dashboard_params) { { format: :json } } + it 'has the correct name' do + get :metrics_dashboard, params: environment_params(dashboard_params) - it_behaves_like '200 response' + dashboard_name = json_response['dashboard']['dashboard'] - context 'when the dashboard could not be provided' do + # 'Environment metrics' is the default dashboard. + expect(dashboard_name).not_to eq('Environment metrics') + expect(dashboard_name).to eq(expected_dashboard) + end + + context 'when the dashboard cannot not be processed' do before do allow(YAML).to receive(:safe_load).and_return({}) end it_behaves_like 'error response', :unprocessable_entity end - - context 'when a dashboard param is specified' do - let(:dashboard_params) { { format: :json, dashboard: '.gitlab/dashboards/not_there_dashboard.yml' } } - - it_behaves_like '200 response' - end end - context 'when multiple dashboards is enabled' do - let(:dashboard_params) { { format: :json } } + shared_examples_for 'the default dynamic dashboard' do + it_behaves_like '200 response' - it_behaves_like '200 response', contains_all_dashboards: true - it_behaves_like 'has all dashboards' + it 'contains only the Memory and CPU charts' do + get :metrics_dashboard, params: environment_params(dashboard_params) - context 'when a dashboard could not be provided' do - before do - allow(YAML).to receive(:safe_load).and_return({}) - end + dashboard = json_response['dashboard'] + panel_group = dashboard['panel_groups'].first + titles = panel_group['panels'].map { |panel| panel['title'] } - it_behaves_like 'error response', :unprocessable_entity, contains_all_dashboards: true - it_behaves_like 'has all dashboards' + expect(dashboard['dashboard']).to be_nil + expect(dashboard['panel_groups'].length).to eq 1 + expect(panel_group['group']).to be_nil + expect(titles).to eq ['Memory Usage (Total)', 'Core Usage (Total)'] end + end - context 'when a dashboard param is specified' do - let(:dashboard_params) { { format: :json, dashboard: '.gitlab/dashboards/test.yml' } } + shared_examples_for 'dashboard can be specified' do + context 'when dashboard is specified' do + let(:dashboard_path) { '.gitlab/dashboards/test.yml' } + let(:dashboard_params) { { format: :json, dashboard: dashboard_path } } + + it_behaves_like 'error response', :not_found - context 'when the dashboard is available' do + context 'when the project dashboard is available' do let(:dashboard_yml) { fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml') } - let(:dashboard_file) { { '.gitlab/dashboards/test.yml' => dashboard_yml } } - let(:project) { create(:project, :custom_repo, files: dashboard_file) } + let(:project) { project_with_dashboard(dashboard_path, dashboard_yml) } let(:environment) { create(:environment, name: 'production', project: project) } - it_behaves_like '200 response', contains_all_dashboards: true - it_behaves_like 'has all dashboards' + it_behaves_like 'the specified dashboard', 'Test Dashboard' end - context 'when the dashboard does not exist' do - it_behaves_like 'error response', :not_found, contains_all_dashboards: true - it_behaves_like 'has all dashboards' + context 'when the specified dashboard is the default dashboard' do + let(:dashboard_path) { ::Metrics::Dashboard::SystemDashboardService::SYSTEM_DASHBOARD_PATH } + + it_behaves_like 'the default dashboard' end end + end - context 'when the dashboard is intended for embedding' do + shared_examples_for 'dashboard can be embedded' do + context 'when the embedded flag is included' do let(:dashboard_params) { { format: :json, embedded: true } } - it_behaves_like '200 response' + it_behaves_like 'the default dynamic dashboard' - context 'when a dashboard path is provided' do - let(:dashboard_params) { { format: :json, dashboard: '.gitlab/dashboards/test.yml', embedded: true } } + context 'when the dashboard is specified' do + let(:dashboard_params) { { format: :json, embedded: true, dashboard: '.gitlab/dashboards/fake.yml' } } - # The dashboard path should simple be ignored. - it_behaves_like '200 response' + # The dashboard param should be ignored. + it_behaves_like 'the default dynamic dashboard' end end end + + shared_examples_for 'dashboard cannot be specified' do + context 'when dashboard is specified' do + let(:dashboard_params) { { format: :json, dashboard: '.gitlab/dashboards/test.yml' } } + + it_behaves_like 'the default dashboard' + end + end + + shared_examples_for 'dashboard cannot be embedded' do + context 'when the embedded flag is included' do + let(:dashboard_params) { { format: :json, embedded: true } } + + it_behaves_like 'the default dashboard' + end + end + + let(:dashboard_params) { { format: :json } } + + it_behaves_like 'the default dashboard' + it_behaves_like 'dashboard can be specified' + it_behaves_like 'dashboard can be embedded' + + context 'when multiple dashboards is enabled and embedding metrics is disabled' do + before do + stub_feature_flags(gfm_embedded_metrics: false) + end + + it_behaves_like 'the default dashboard' + it_behaves_like 'dashboard can be specified' + it_behaves_like 'dashboard cannot be embedded' + end + + context 'when multiple dashboards is disabled and embedding metrics is enabled' do + before do + stub_feature_flags(environment_metrics_show_multiple_dashboards: false) + end + + it_behaves_like 'the default dashboard' + it_behaves_like 'dashboard cannot be specified' + it_behaves_like 'dashboard can be embedded' + end + + context 'when multiple dashboards and embedding metrics are disabled' do + before do + stub_feature_flags( + environment_metrics_show_multiple_dashboards: false, + gfm_embedded_metrics: false + ) + end + + it_behaves_like 'the default dashboard' + it_behaves_like 'dashboard cannot be specified' + it_behaves_like 'dashboard cannot be embedded' + end end describe 'GET #search' do diff --git a/spec/controllers/projects/error_tracking_controller_spec.rb b/spec/controllers/projects/error_tracking_controller_spec.rb index 844c61f1ace..d11ef24ef96 100644 --- a/spec/controllers/projects/error_tracking_controller_spec.rb +++ b/spec/controllers/projects/error_tracking_controller_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require 'spec_helper' describe Projects::ErrorTrackingController do set(:project) { create(:project) } diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 32d14dce936..fab47aa4701 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -251,15 +251,13 @@ describe Projects::IssuesController do end end - describe 'Redirect after sign in' do + # This spec runs as a request-style spec in order to invoke the + # Rails router. A controller-style spec matches the wrong route, and + # session['user_return_to'] becomes incorrect. + describe 'Redirect after sign in', type: :request do context 'with an AJAX request' do it 'does not store the visited URL' do - get :show, params: { - format: :json, - namespace_id: project.namespace, - project_id: project, - id: issue.iid - }, xhr: true + get project_issue_path(project, issue), xhr: true expect(session['user_return_to']).to be_blank end @@ -267,14 +265,9 @@ describe Projects::IssuesController do context 'without an AJAX request' do it 'stores the visited URL' do - get :show, - params: { - namespace_id: project.namespace.to_param, - project_id: project, - id: issue.iid - } + get project_issue_path(project, issue) - expect(session['user_return_to']).to eq("/#{project.namespace.to_param}/#{project.to_param}/issues/#{issue.iid}") + expect(session['user_return_to']).to eq(project_issue_path(project, issue)) end end end @@ -1260,6 +1253,28 @@ describe Projects::IssuesController do sign_in(user) end + context do + it_behaves_like 'discussions provider' do + let!(:author) { create(:user) } + let!(:project) { create(:project) } + + let!(:issue) { create(:issue, project: project, author: user) } + + let!(:note_on_issue1) { create(:discussion_note_on_issue, noteable: issue, project: issue.project, author: create(:user)) } + let!(:note_on_issue2) { create(:discussion_note_on_issue, noteable: issue, project: issue.project, author: create(:user)) } + + let(:requested_iid) { issue.iid } + let(:expected_discussion_count) { 3 } + let(:expected_discussion_ids) do + [ + issue.notes.first.discussion_id, + note_on_issue1.discussion_id, + note_on_issue2.discussion_id + ] + end + end + end + it 'returns discussion json' do get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issue.iid } diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 901402aa5fd..f076a5e769f 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -158,7 +158,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do get_show_json json_response.dig('pipeline', 'details', 'stages').tap do |stages| - expect(stages.map(&:keys).flatten) + expect(stages.flat_map(&:keys)) .to eq %w[name title status path dropdown_path] end end @@ -546,7 +546,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do expect(response).to have_gitlab_http_status(:ok) expect(json_response['id']).to eq job.id expect(json_response['status']).to eq job.status - expect(json_response['html']).to eq('<span class="">BUILD TRACE</span>') + expect(json_response['html']).to eq('<span>BUILD TRACE</span>') end end @@ -676,6 +676,8 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do end describe 'POST play' do + let(:variable_attributes) { [] } + before do project.add_developer(user) @@ -698,6 +700,14 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do it 'transits to pending' do expect(job.reload).to be_pending end + + context 'when job variables are specified' do + let(:variable_attributes) { [{ key: 'first', secret_value: 'first' }] } + + it 'assigns the job variables' do + expect(job.reload.job_variables.map(&:key)).to contain_exactly('first') + end + end end context 'when job is not playable' do @@ -712,7 +722,8 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do post :play, params: { namespace_id: project.namespace, project_id: project, - id: job.id + id: job.id, + job_variables_attributes: variable_attributes } end end diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb index 5fefad86ef3..3816e1c7a31 100644 --- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb @@ -212,4 +212,46 @@ describe Projects::MergeRequests::CreationsController do expect(response).to have_gitlab_http_status(200) end end + + describe 'POST create' do + let(:params) do + { + namespace_id: fork_project.namespace.to_param, + project_id: fork_project, + merge_request: { + title: 'Test merge request', + source_branch: 'remove-submodule', + target_branch: 'master' + } + } + end + + it 'creates merge request' do + expect do + post_request(params) + end.to change { MergeRequest.count }.by(1) + end + + context 'when the merge request is not created from the web ide' do + it 'counter is not increased' do + expect(Gitlab::UsageDataCounters::WebIdeCounter).not_to receive(:increment_merge_requests_count) + + post_request(params) + end + end + + context 'when the merge request is created from the web ide' do + let(:nav_source) { { nav_source: 'webide' } } + + it 'counter is increased' do + expect(Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_merge_requests_count) + + post_request(params.merge(nav_source)) + end + end + + def post_request(merge_request_params) + post :create, params: merge_request_params + end + end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index f11880122b1..b1dc6a65dd4 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -621,10 +621,100 @@ describe Projects::MergeRequestsController do format: :json end - it 'responds with serialized pipelines' do - expect(json_response['pipelines']).not_to be_empty - expect(json_response['count']['all']).to eq 1 - expect(response).to include_pagination_headers + context 'with "enabled" builds on a public project' do + let(:project) { create(:project, :repository, :public) } + + context 'for a project owner' do + it 'responds with serialized pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + + context 'for an unassociated user' do + let(:user) { create :user } + + it 'responds with no pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + end + + context 'with private builds on a public project' do + let(:project) { create(:project, :repository, :public, :builds_private) } + + context 'for a project owner' do + it 'responds with serialized pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + + context 'for an unassociated user' do + let(:user) { create :user } + + it 'responds with no pipelines' do + expect(json_response['pipelines']).to be_empty + expect(json_response['count']['all']).to eq(0) + expect(response).to include_pagination_headers + end + end + + context 'from a project fork' do + let(:fork_user) { create :user } + let(:forked_project) { fork_project(project, fork_user, repository: true) } # Forked project carries over :builds_private + let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: forked_project) } + + context 'with private builds' do + context 'for the target project member' do + it 'does not respond with serialized pipelines' do + expect(json_response['pipelines']).to be_empty + expect(json_response['count']['all']).to eq(0) + expect(response).to include_pagination_headers + end + end + + context 'for the source project member' do + let(:user) { fork_user } + + it 'responds with serialized pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + end + + context 'with public builds' do + let(:forked_project) do + fork_project(project, fork_user, repository: true).tap do |new_project| + new_project.project_feature.update(builds_access_level: ProjectFeature::ENABLED) + end + end + + context 'for the target project member' do + it 'does not respond with serialized pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + + context 'for the source project member' do + let(:user) { fork_user } + + it 'responds with serialized pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + end + end end end @@ -885,10 +975,9 @@ describe Projects::MergeRequestsController do environment2 = create(:environment, project: forked) create(:deployment, :succeed, environment: environment2, sha: sha, ref: 'master', deployable: build) - # TODO address the last 11 queries + # TODO address the last 5 queries # See https://gitlab.com/gitlab-org/gitlab-ce/issues/63952 (5 queries) - # And https://gitlab.com/gitlab-org/gitlab-ce/issues/64105 (6 queries) - leeway = 11 + leeway = 5 expect { get_ci_environments_status }.not_to exceed_all_query_limit(control_count + leeway) end end @@ -1121,6 +1210,22 @@ describe Projects::MergeRequestsController do end end end + + context do + it_behaves_like 'discussions provider' do + let!(:author) { create(:user) } + let!(:project) { create(:project) } + + let!(:merge_request) { create(:merge_request, source_project: project) } + + let!(:mr_note1) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) } + let!(:mr_note2) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) } + + let(:requested_iid) { merge_request.iid } + let(:expected_discussion_count) { 2 } + let(:expected_discussion_ids) { [mr_note1.discussion_id, mr_note2.discussion_id] } + end + end end describe 'GET edit' do diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 767cee7d54a..9b2025b836c 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -115,7 +115,7 @@ describe Projects::MilestonesController do end end - context 'with nested groups', :nested_groups do + context 'with nested groups' do let!(:subgroup) { create(:group, :public, parent: group) } let!(:subgroup_milestone) { create(:milestone, group: subgroup) } diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 98aea9056dc..9ab565dc2e8 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -43,7 +43,7 @@ describe Projects::NotesController do request.headers['X-Last-Fetched-At'] = last_fetched_at expect(NotesFinder).to receive(:new) - .with(anything, anything, hash_including(last_fetched_at: last_fetched_at)) + .with(anything, hash_including(last_fetched_at: last_fetched_at)) .and_call_original get :index, params: request_params diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 97acd47b4da..8ee3168273f 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe Projects::RawController do + include RepoHelpers + let(:project) { create(:project, :public, :repository) } describe 'GET #show' do @@ -46,5 +48,98 @@ describe Projects::RawController do let(:filename) { 'lfs_object.iso' } let(:filepath) { "be93687/files/lfs/#{filename}" } end + + context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_cache do + let(:file_path) { 'master/README.md' } + + before do + stub_application_setting(raw_blob_request_limit: 5) + end + + it 'prevents from accessing the raw file' do + execute_raw_requests(requests: 6, project: project, file_path: file_path) + + expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.') + expect(response).to redirect_to(project_blob_path(project, file_path)) + end + + it 'logs the event on auth.log' do + attributes = { + message: 'Action_Rate_Limiter_Request', + env: :raw_blob_request_limit, + ip: '0.0.0.0', + request_method: 'GET', + fullpath: "/#{project.full_path}/raw/#{file_path}" + } + + expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once + + execute_raw_requests(requests: 6, project: project, file_path: file_path) + end + + context 'when the request uses a different version of a commit' do + it 'prevents from accessing the raw file' do + # 3 times with the normal sha + commit_sha = project.repository.commit.sha + file_path = "#{commit_sha}/README.md" + + execute_raw_requests(requests: 3, project: project, file_path: file_path) + + # 3 times with the modified version + modified_sha = commit_sha.gsub(commit_sha[0..5], commit_sha[0..5].upcase) + modified_path = "#{modified_sha}/README.md" + + execute_raw_requests(requests: 3, project: project, file_path: modified_path) + + expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.') + expect(response).to redirect_to(project_blob_path(project, modified_path)) + end + end + + context 'when the throttling has been disabled' do + before do + stub_application_setting(raw_blob_request_limit: 0) + end + + it 'does not prevent from accessing the raw file' do + execute_raw_requests(requests: 10, project: project, file_path: file_path) + + expect(response).to have_gitlab_http_status(200) + end + end + + context 'with case-sensitive files' do + it 'prevents from accessing the specific file' do + create_file_in_repo(project, 'master', 'master', 'readme.md', 'Add readme.md') + create_file_in_repo(project, 'master', 'master', 'README.md', 'Add README.md') + + commit_sha = project.repository.commit.sha + file_path = "#{commit_sha}/readme.md" + + # Accessing downcase version of readme + execute_raw_requests(requests: 6, project: project, file_path: file_path) + + expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.') + expect(response).to redirect_to(project_blob_path(project, file_path)) + + # Accessing upcase version of readme + file_path = "#{commit_sha}/README.md" + + execute_raw_requests(requests: 1, project: project, file_path: file_path) + + expect(response).to have_gitlab_http_status(200) + end + end + end + end + + def execute_raw_requests(requests:, project:, file_path:) + requests.times do + get :show, params: { + namespace_id: project.namespace, + project_id: project, + id: file_path + } + end end end diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index 8fca9e680dd..fcab4d73dca 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -77,6 +77,53 @@ describe Projects::RepositoriesController do expect(response).to have_gitlab_http_status(404) end end + + describe 'caching' do + it 'sets appropriate caching headers' do + get_archive + + expect(response).to have_gitlab_http_status(200) + expect(response.header['ETag']).to be_present + expect(response.header['Cache-Control']).to include('max-age=60, private') + end + + context 'when project is public' do + let(:project) { create(:project, :repository, :public) } + + it 'sets appropriate caching headers' do + get_archive + + expect(response).to have_gitlab_http_status(200) + expect(response.header['ETag']).to be_present + expect(response.header['Cache-Control']).to include('max-age=60, public') + end + end + + context 'when ref is a commit SHA' do + it 'max-age is set to 3600 in Cache-Control header' do + get_archive('ddd0f15ae83993f5cb66a927a28673882e99100b') + + expect(response).to have_gitlab_http_status(200) + expect(response.header['Cache-Control']).to include('max-age=3600') + end + end + + context 'when If-None-Modified header is set' do + it 'returns a 304 status' do + # Get the archive cached first + get_archive + + request.headers['If-None-Match'] = response.headers['ETag'] + get_archive + + expect(response).to have_gitlab_http_status(304) + end + end + + def get_archive(id = 'feature') + get :archive, params: { namespace_id: project.namespace, project_id: project, id: id }, format: 'zip' + end + end end end end diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb index 776c1270977..661ed9840b1 100644 --- a/spec/controllers/projects/uploads_controller_spec.rb +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -10,6 +10,11 @@ describe Projects::UploadsController do { namespace_id: model.namespace.to_param, project_id: model } end + let(:other_model) { create(:project, :public) } + let(:other_params) do + { namespace_id: other_model.namespace.to_param, project_id: other_model } + end + it_behaves_like 'handle uploads' context 'when the URL the old style, without /-/system' do diff --git a/spec/controllers/projects/wikis_controller_spec.rb b/spec/controllers/projects/wikis_controller_spec.rb index a7e5a79b51d..fbca1d5740f 100644 --- a/spec/controllers/projects/wikis_controller_spec.rb +++ b/spec/controllers/projects/wikis_controller_spec.rb @@ -31,6 +31,47 @@ describe Projects::WikisController do end end + describe 'GET #history' do + before do + allow(controller) + .to receive(:can?) + .with(any_args) + .and_call_original + + # The :create_wiki permission is irrelevant to reading history. + expect(controller) + .not_to receive(:can?) + .with(anything, :create_wiki, any_args) + + allow(controller) + .to receive(:can?) + .with(anything, :read_wiki, any_args) + .and_return(allow_read_wiki) + end + + shared_examples 'fetching history' do |expected_status| + before do + get :history, params: { namespace_id: project.namespace, project_id: project, id: wiki_title } + end + + it "returns status #{expected_status}" do + expect(response).to have_http_status(expected_status) + end + end + + it_behaves_like 'fetching history', :ok do + let(:allow_read_wiki) { true } + + it 'assigns @page_versions' do + expect(assigns(:page_versions)).to be_present + end + end + + it_behaves_like 'fetching history', :not_found do + let(:allow_read_wiki) { false } + end + end + describe 'GET #show' do render_views diff --git a/spec/controllers/user_callouts_controller_spec.rb b/spec/controllers/user_callouts_controller_spec.rb index babc93a83e5..07eaff2da09 100644 --- a/spec/controllers/user_callouts_controller_spec.rb +++ b/spec/controllers/user_callouts_controller_spec.rb @@ -13,7 +13,7 @@ describe UserCalloutsController do subject { post :create, params: { feature_name: feature_name }, format: :json } context 'with valid feature name' do - let(:feature_name) { UserCallout.feature_names.keys.first } + let(:feature_name) { UserCallout.feature_names.first.first } context 'when callout entry does not exist' do it 'creates a callout entry with dismissed state' do @@ -28,7 +28,7 @@ describe UserCalloutsController do end context 'when callout entry already exists' do - let!(:callout) { create(:user_callout, feature_name: UserCallout.feature_names.keys.first, user: user) } + let!(:callout) { create(:user_callout, feature_name: UserCallout.feature_names.first.first, user: user) } it 'returns success' do subject |