diff options
Diffstat (limited to 'spec/controllers')
-rw-r--r-- | spec/controllers/concerns/group_tree_spec.rb | 89 | ||||
-rw-r--r-- | spec/controllers/dashboard/groups_controller_spec.rb | 23 | ||||
-rw-r--r-- | spec/controllers/explore/groups_controller_spec.rb | 23 | ||||
-rw-r--r-- | spec/controllers/groups/children_controller_spec.rb | 286 | ||||
-rw-r--r-- | spec/controllers/groups_controller_spec.rb | 108 | ||||
-rw-r--r-- | spec/controllers/projects/pipelines_controller_spec.rb | 32 |
6 files changed, 478 insertions, 83 deletions
diff --git a/spec/controllers/concerns/group_tree_spec.rb b/spec/controllers/concerns/group_tree_spec.rb new file mode 100644 index 00000000000..ba84fbf8564 --- /dev/null +++ b/spec/controllers/concerns/group_tree_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +describe GroupTree do + let(:group) { create(:group, :public) } + let(:user) { create(:user) } + + controller(ApplicationController) do + # `described_class` is not available in this context + include GroupTree # rubocop:disable RSpec/DescribedClass + + def index + render_group_tree GroupsFinder.new(current_user).execute + end + end + + before do + group.add_owner(user) + sign_in(user) + end + + describe 'GET #index' do + it 'filters groups' do + other_group = create(:group, name: 'filter') + other_group.add_owner(user) + + get :index, filter: 'filt', format: :json + + expect(assigns(:groups)).to contain_exactly(other_group) + end + + context 'for subgroups', :nested_groups do + it 'only renders root groups when no parent was given' do + create(:group, :public, parent: group) + + get :index, format: :json + + expect(assigns(:groups)).to contain_exactly(group) + end + + it 'contains only the subgroup when a parent was given' do + subgroup = create(:group, :public, parent: group) + + get :index, parent_id: group.id, format: :json + + expect(assigns(:groups)).to contain_exactly(subgroup) + end + + it 'allows filtering for subgroups and includes the parents for rendering' do + subgroup = create(:group, :public, parent: group, name: 'filter') + + get :index, filter: 'filt', format: :json + + expect(assigns(:groups)).to contain_exactly(group, subgroup) + end + + it 'does not include groups the user does not have access to' do + parent = create(:group, :private) + subgroup = create(:group, :private, parent: parent, name: 'filter') + subgroup.add_developer(user) + _other_subgroup = create(:group, :private, parent: parent, name: 'filte') + + get :index, filter: 'filt', format: :json + + expect(assigns(:groups)).to contain_exactly(parent, subgroup) + end + end + + context 'json content' do + it 'shows groups as json' do + get :index, format: :json + + expect(json_response.first['id']).to eq(group.id) + end + + context 'nested groups', :nested_groups do + it 'expands the tree when filtering' do + subgroup = create(:group, :public, parent: group, name: 'filter') + + get :index, filter: 'filt', format: :json + + children_response = json_response.first['children'] + + expect(json_response.first['id']).to eq(group.id) + expect(children_response.first['id']).to eq(subgroup.id) + end + end + end + end +end diff --git a/spec/controllers/dashboard/groups_controller_spec.rb b/spec/controllers/dashboard/groups_controller_spec.rb new file mode 100644 index 00000000000..fb9d3efbac0 --- /dev/null +++ b/spec/controllers/dashboard/groups_controller_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe Dashboard::GroupsController do + let(:user) { create(:user) } + + before do + sign_in(user) + end + + it 'renders group trees' do + expect(described_class).to include(GroupTree) + end + + it 'only includes projects the user is a member of' do + member_of_group = create(:group) + member_of_group.add_developer(user) + create(:group, :public) + + get :index + + expect(assigns(:groups)).to contain_exactly(member_of_group) + end +end diff --git a/spec/controllers/explore/groups_controller_spec.rb b/spec/controllers/explore/groups_controller_spec.rb new file mode 100644 index 00000000000..9e0ad9ea86f --- /dev/null +++ b/spec/controllers/explore/groups_controller_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe Explore::GroupsController do + let(:user) { create(:user) } + + before do + sign_in(user) + end + + it 'renders group trees' do + expect(described_class).to include(GroupTree) + end + + it 'includes public projects' do + member_of_group = create(:group) + member_of_group.add_developer(user) + public_group = create(:group, :public) + + get :index + + expect(assigns(:groups)).to contain_exactly(member_of_group, public_group) + end +end diff --git a/spec/controllers/groups/children_controller_spec.rb b/spec/controllers/groups/children_controller_spec.rb new file mode 100644 index 00000000000..4262d474e59 --- /dev/null +++ b/spec/controllers/groups/children_controller_spec.rb @@ -0,0 +1,286 @@ +require 'spec_helper' + +describe Groups::ChildrenController do + let(:group) { create(:group, :public) } + let(:user) { create(:user) } + let!(:group_member) { create(:group_member, group: group, user: user) } + + describe 'GET #index' do + context 'for projects' do + let!(:public_project) { create(:project, :public, namespace: group) } + let!(:private_project) { create(:project, :private, namespace: group) } + + context 'as a user' do + before do + sign_in(user) + end + + it 'shows all children' do + get :index, group_id: group.to_param, format: :json + + expect(assigns(:children)).to contain_exactly(public_project, private_project) + end + + context 'being member of private subgroup' do + it 'shows public and private children the user is member of' do + group_member.destroy! + private_project.add_guest(user) + + get :index, group_id: group.to_param, format: :json + + expect(assigns(:children)).to contain_exactly(public_project, private_project) + end + end + end + + context 'as a guest' do + it 'shows the public children' do + get :index, group_id: group.to_param, format: :json + + expect(assigns(:children)).to contain_exactly(public_project) + end + end + end + + context 'for subgroups', :nested_groups 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) } + let!(:private_project) { create(:project, :private, namespace: group) } + + context 'as a user' do + before do + sign_in(user) + end + + it 'shows all children' do + get :index, group_id: group.to_param, format: :json + + expect(assigns(:children)).to contain_exactly(public_subgroup, private_subgroup, public_project, private_project) + end + + context 'being member of private subgroup' do + it 'shows public and private children the user is member of' do + group_member.destroy! + private_subgroup.add_guest(user) + private_project.add_guest(user) + + get :index, group_id: group.to_param, format: :json + + expect(assigns(:children)).to contain_exactly(public_subgroup, private_subgroup, public_project, private_project) + end + end + end + + context 'as a guest' do + it 'shows the public children' do + get :index, group_id: group.to_param, format: :json + + expect(assigns(:children)).to contain_exactly(public_subgroup, public_project) + end + end + + context 'filtering children' do + it 'expands the tree for matching projects' do + project = create(:project, :public, namespace: public_subgroup, name: 'filterme') + + get :index, group_id: group.to_param, filter: 'filter', format: :json + + group_json = json_response.first + project_json = group_json['children'].first + + expect(group_json['id']).to eq(public_subgroup.id) + expect(project_json['id']).to eq(project.id) + end + + it 'expands the tree for matching subgroups' do + matched_group = create(:group, :public, parent: public_subgroup, name: 'filterme') + + get :index, group_id: group.to_param, filter: 'filter', format: :json + + group_json = json_response.first + matched_group_json = group_json['children'].first + + expect(group_json['id']).to eq(public_subgroup.id) + expect(matched_group_json['id']).to eq(matched_group.id) + end + + it 'merges the trees correctly' do + shared_subgroup = create(:group, :public, parent: group, path: 'hardware') + matched_project_1 = create(:project, :public, namespace: shared_subgroup, name: 'mobile-soc') + + l2_subgroup = create(:group, :public, parent: shared_subgroup, path: 'broadcom') + l3_subgroup = create(:group, :public, parent: l2_subgroup, path: 'wifi-group') + matched_project_2 = create(:project, :public, namespace: l3_subgroup, name: 'mobile') + + get :index, group_id: group.to_param, filter: 'mobile', format: :json + + shared_group_json = json_response.first + expect(shared_group_json['id']).to eq(shared_subgroup.id) + + matched_project_1_json = shared_group_json['children'].detect { |child| child['type'] == 'project' } + expect(matched_project_1_json['id']).to eq(matched_project_1.id) + + l2_subgroup_json = shared_group_json['children'].detect { |child| child['type'] == 'group' } + expect(l2_subgroup_json['id']).to eq(l2_subgroup.id) + + l3_subgroup_json = l2_subgroup_json['children'].first + expect(l3_subgroup_json['id']).to eq(l3_subgroup.id) + + matched_project_2_json = l3_subgroup_json['children'].first + expect(matched_project_2_json['id']).to eq(matched_project_2.id) + end + + it 'expands the tree upto a specified parent' do + subgroup = create(:group, :public, parent: group) + l2_subgroup = create(:group, :public, parent: subgroup) + create(:project, :public, namespace: l2_subgroup, name: 'test') + + get :index, group_id: subgroup.to_param, filter: 'test', format: :json + + expect(response).to have_http_status(200) + end + + it 'returns an array with one element when only one result is matched' do + create(:project, :public, namespace: group, name: 'match') + + get :index, group_id: group.to_param, filter: 'match', format: :json + + expect(json_response).to be_kind_of(Array) + expect(json_response.size).to eq(1) + end + + it 'returns an empty array when there are no search results' do + subgroup = create(:group, :public, parent: group) + l2_subgroup = create(:group, :public, parent: subgroup) + create(:project, :public, namespace: l2_subgroup, name: 'no-match') + + get :index, group_id: subgroup.to_param, filter: 'test', format: :json + + expect(json_response).to eq([]) + end + + it 'includes pagination headers' do + 2.times { |i| create(:group, :public, parent: public_subgroup, name: "filterme#{i}") } + + get :index, group_id: group.to_param, filter: 'filter', per_page: 1, format: :json + + expect(response).to include_pagination_headers + end + end + + context 'queries per rendered element', :request_store do + # We need to make sure the following counts are preloaded + # otherwise they will cause an extra query + # 1. Count of visible projects in the element + # 2. Count of visible subgroups in the element + # 3. Count of members of a group + let(:expected_queries_per_group) { 0 } + let(:expected_queries_per_project) { 0 } + + def get_list + get :index, group_id: group.to_param, format: :json + end + + it 'queries the expected amount for a group row' do + control = ActiveRecord::QueryRecorder.new { get_list } + + _new_group = create(:group, :public, parent: group) + + expect { get_list }.not_to exceed_query_limit(control).with_threshold(expected_queries_per_group) + end + + it 'queries the expected amount for a project row' do + control = ActiveRecord::QueryRecorder.new { get_list } + _new_project = create(:project, :public, namespace: group) + + expect { get_list }.not_to exceed_query_limit(control).with_threshold(expected_queries_per_project) + end + + context 'when rendering hierarchies' do + # When loading hierarchies we load the all the ancestors for matched projects + # in 1 separate query + let(:extra_queries_for_hierarchies) { 1 } + + def get_filtered_list + get :index, group_id: group.to_param, filter: 'filter', format: :json + end + + it 'queries the expected amount when nested rows are increased for a group' do + matched_group = create(:group, :public, parent: group, name: 'filterme') + + control = ActiveRecord::QueryRecorder.new { get_filtered_list } + + matched_group.update!(parent: public_subgroup) + + expect { get_filtered_list }.not_to exceed_query_limit(control).with_threshold(extra_queries_for_hierarchies) + end + + it 'queries the expected amount when a new group match is added' do + create(:group, :public, parent: public_subgroup, name: 'filterme') + + control = ActiveRecord::QueryRecorder.new { get_filtered_list } + + create(:group, :public, parent: public_subgroup, name: 'filterme2') + create(:group, :public, parent: public_subgroup, name: 'filterme3') + + expect { get_filtered_list }.not_to exceed_query_limit(control).with_threshold(extra_queries_for_hierarchies) + end + + it 'queries the expected amount when nested rows are increased for a project' do + matched_project = create(:project, :public, namespace: group, name: 'filterme') + + control = ActiveRecord::QueryRecorder.new { get_filtered_list } + + matched_project.update!(namespace: public_subgroup) + + expect { get_filtered_list }.not_to exceed_query_limit(control).with_threshold(extra_queries_for_hierarchies) + end + end + end + end + + context 'pagination' do + let(:per_page) { 3 } + + before do + allow(Kaminari.config).to receive(:default_per_page).and_return(per_page) + end + + context 'with only projects' do + let!(:other_project) { create(:project, :public, namespace: group) } + let!(:first_page_projects) { create_list(:project, per_page, :public, namespace: group ) } + + it 'has projects on the first page' do + get :index, group_id: group.to_param, sort: 'id_desc', format: :json + + expect(assigns(:children)).to contain_exactly(*first_page_projects) + end + + it 'has projects on the second page' do + get :index, group_id: group.to_param, sort: 'id_desc', page: 2, format: :json + + expect(assigns(:children)).to contain_exactly(other_project) + end + end + + context 'with subgroups and projects', :nested_groups 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) } + + it 'contains all subgroups' do + get :index, group_id: group.to_param, sort: 'id_asc', format: :json + + expect(assigns(:children)).to contain_exactly(*first_page_subgroups) + end + + it 'contains the project and group on the second page' do + get :index, group_id: group.to_param, sort: 'id_asc', page: 2, format: :json + + expect(assigns(:children)).to contain_exactly(other_subgroup, *next_page_projects.take(per_page - 1)) + end + end + end + end +end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index b0564e27a68..e7631d4d709 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -1,4 +1,4 @@ -require 'rails_helper' +require 'spec_helper' describe GroupsController do let(:user) { create(:user) } @@ -150,42 +150,6 @@ describe GroupsController do end end - describe 'GET #subgroups', :nested_groups do - let!(:public_subgroup) { create(:group, :public, parent: group) } - let!(:private_subgroup) { create(:group, :private, parent: group) } - - context 'as a user' do - before do - sign_in(user) - end - - it 'shows all subgroups' do - get :subgroups, id: group.to_param - - expect(assigns(:nested_groups)).to contain_exactly(public_subgroup, private_subgroup) - end - - context 'being member of private subgroup' do - it 'shows public and private subgroups the user is member of' do - group_member.destroy! - private_subgroup.add_guest(user) - - get :subgroups, id: group.to_param - - expect(assigns(:nested_groups)).to contain_exactly(public_subgroup, private_subgroup) - end - end - end - - context 'as a guest' do - it 'shows the public subgroups' do - get :subgroups, id: group.to_param - - expect(assigns(:nested_groups)).to contain_exactly(public_subgroup) - end - end - end - describe 'GET #issues' do let(:issue_1) { create(:issue, project: project) } let(:issue_2) { create(:issue, project: project) } @@ -425,62 +389,62 @@ describe GroupsController do end end end - end - context 'for a POST request' do - context 'when requesting the canonical path with different casing' do - it 'does not 404' do - post :update, id: group.to_param.upcase, group: { path: 'new_path' } + context 'for a POST request' do + context 'when requesting the canonical path with different casing' do + it 'does not 404' do + post :update, id: group.to_param.upcase, group: { path: 'new_path' } - expect(response).not_to have_http_status(404) - end + expect(response).not_to have_http_status(404) + end - it 'does not redirect to the correct casing' do - post :update, id: group.to_param.upcase, group: { path: 'new_path' } + it 'does not redirect to the correct casing' do + post :update, id: group.to_param.upcase, group: { path: 'new_path' } - expect(response).not_to have_http_status(301) + expect(response).not_to have_http_status(301) + end end - end - context 'when requesting a redirected path' do - let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } + context 'when requesting a redirected path' do + let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } - it 'returns not found' do - post :update, id: redirect_route.path, group: { path: 'new_path' } + it 'returns not found' do + post :update, id: redirect_route.path, group: { path: 'new_path' } - expect(response).to have_http_status(404) + expect(response).to have_http_status(404) + end end end - end - context 'for a DELETE request' do - context 'when requesting the canonical path with different casing' do - it 'does not 404' do - delete :destroy, id: group.to_param.upcase + context 'for a DELETE request' do + context 'when requesting the canonical path with different casing' do + it 'does not 404' do + delete :destroy, id: group.to_param.upcase - expect(response).not_to have_http_status(404) - end + expect(response).not_to have_http_status(404) + end - it 'does not redirect to the correct casing' do - delete :destroy, id: group.to_param.upcase + it 'does not redirect to the correct casing' do + delete :destroy, id: group.to_param.upcase - expect(response).not_to have_http_status(301) + expect(response).not_to have_http_status(301) + end end - end - context 'when requesting a redirected path' do - let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } + context 'when requesting a redirected path' do + let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } - it 'returns not found' do - delete :destroy, id: redirect_route.path + it 'returns not found' do + delete :destroy, id: redirect_route.path - expect(response).to have_http_status(404) + expect(response).to have_http_status(404) + end end end end - end - def group_moved_message(redirect_route, group) - "Group '#{redirect_route.path}' was moved to '#{group.full_path}'. Please update any links and bookmarks that may still have the old path." + def group_moved_message(redirect_route, group) + "Group '#{redirect_route.path}' was moved to '#{group.full_path}'. Please update any links and bookmarks that may still have the old path." + end end end diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 167e80ed9cd..67b53d2acce 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -3,32 +3,36 @@ require 'spec_helper' describe Projects::PipelinesController do include ApiHelpers - let(:user) { create(:user) } - let(:project) { create(:project, :public) } + set(:user) { create(:user) } + set(:project) { create(:project, :public, :repository) } let(:feature) { ProjectFeature::DISABLED } before do stub_not_protect_default_branch project.add_developer(user) - project.project_feature.update( - builds_access_level: feature) + project.project_feature.update(builds_access_level: feature) sign_in(user) end describe 'GET index.json' do before do - create(:ci_empty_pipeline, status: 'pending', project: project) - create(:ci_empty_pipeline, status: 'running', project: project) - create(:ci_empty_pipeline, status: 'created', project: project) - create(:ci_empty_pipeline, status: 'success', project: project) + branch_head = project.commit + parent = branch_head.parent - get :index, namespace_id: project.namespace, - project_id: project, - format: :json + create(:ci_empty_pipeline, status: 'pending', project: project, sha: branch_head.id) + create(:ci_empty_pipeline, status: 'running', project: project, sha: branch_head.id) + create(:ci_empty_pipeline, status: 'created', project: project, sha: parent.id) + create(:ci_empty_pipeline, status: 'success', project: project, sha: parent.id) + end + + subject do + get :index, namespace_id: project.namespace, project_id: project, format: :json end it 'returns JSON with serialized pipelines' do + subject + expect(response).to have_http_status(:ok) expect(response).to match_response_schema('pipeline') @@ -39,6 +43,12 @@ describe Projects::PipelinesController do expect(json_response['count']['pending']).to eq 1 expect(json_response['count']['finished']).to eq 1 end + + context 'when performing gitaly calls', :request_store do + it 'limits the Gitaly requests' do + expect { subject }.to change { Gitlab::GitalyClient.get_request_count }.by(10) + end + end end describe 'GET show JSON' do |