diff options
author | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2019-06-03 11:52:37 +0000 |
---|---|---|
committer | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2019-06-03 11:52:37 +0000 |
commit | 205f0d0cfe7ff47ead965ff120599dc3f2453974 (patch) | |
tree | a09ff42aab30eb23942dcb470c6779cc0bd8bf80 /spec | |
parent | 3da733dd4f9530b23bca725e54a5346c40c3fc2e (diff) | |
parent | 83a8b779615c968af5afe15a1cbc6903d639f265 (diff) | |
download | gitlab-ce-205f0d0cfe7ff47ead965ff120599dc3f2453974.tar.gz |
Merge branch 'ac-graphql-stats' into 'master'
Add Namespace and ProjectStatistics to GraphQL API
See merge request gitlab-org/gitlab-ce!28277
Diffstat (limited to 'spec')
-rw-r--r-- | spec/graphql/resolvers/namespace_projects_resolver_spec.rb | 69 | ||||
-rw-r--r-- | spec/graphql/types/namespace_type_spec.rb (renamed from spec/graphql/types/namespace_type.rb) | 2 | ||||
-rw-r--r-- | spec/graphql/types/project_statistics_type_spec.rb | 10 | ||||
-rw-r--r-- | spec/graphql/types/project_type_spec.rb | 2 | ||||
-rw-r--r-- | spec/graphql/types/query_type_spec.rb | 12 | ||||
-rw-r--r-- | spec/lib/gitlab/graphql/loaders/batch_project_statistics_loader_spec.rb | 18 | ||||
-rw-r--r-- | spec/models/project_statistics_spec.rb | 14 | ||||
-rw-r--r-- | spec/requests/api/graphql/group_query_spec.rb | 9 | ||||
-rw-r--r-- | spec/requests/api/graphql/namespace/projects_spec.rb | 82 | ||||
-rw-r--r-- | spec/requests/api/graphql/project/project_statistics_spec.rb | 43 |
10 files changed, 256 insertions, 5 deletions
diff --git a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb new file mode 100644 index 00000000000..395e08081d3 --- /dev/null +++ b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::NamespaceProjectsResolver, :nested_groups do + include GraphqlHelpers + + let(:current_user) { create(:user) } + + context "with a group" do + let(:group) { create(:group) } + let(:namespace) { group } + let(:project1) { create(:project, namespace: namespace) } + let(:project2) { create(:project, namespace: namespace) } + let(:nested_group) { create(:group, parent: group) } + let(:nested_project) { create(:project, group: nested_group) } + + before do + project1.add_developer(current_user) + project2.add_developer(current_user) + nested_project.add_developer(current_user) + end + + describe '#resolve' do + it 'finds all projects' do + expect(resolve_projects).to contain_exactly(project1, project2) + end + + it 'finds all projects including the subgroups' do + expect(resolve_projects(include_subgroups: true)).to contain_exactly(project1, project2, nested_project) + end + + context 'with an user namespace' do + let(:namespace) { current_user.namespace } + + it 'finds all projects' do + expect(resolve_projects).to contain_exactly(project1, project2) + end + + it 'finds all projects including the subgroups' do + expect(resolve_projects(include_subgroups: true)).to contain_exactly(project1, project2) + end + end + end + end + + context "when passing a non existent, batch loaded namespace" do + let(:namespace) do + BatchLoader.for("non-existent-path").batch do |_fake_paths, loader, _| + loader.call("non-existent-path", nil) + end + end + + it "returns nil without breaking" do + expect(resolve_projects).to be_empty + end + end + + it 'has an high complexity regardless of arguments' do + field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: described_class, null: false, max_page_size: 100) + + expect(field.to_graphql.complexity.call({}, {}, 1)).to eq 24 + expect(field.to_graphql.complexity.call({}, { include_subgroups: true }, 1)).to eq 24 + end + + def resolve_projects(args = { include_subgroups: false }, context = { current_user: current_user }) + resolve(described_class, obj: namespace, args: args, ctx: context) + end +end diff --git a/spec/graphql/types/namespace_type.rb b/spec/graphql/types/namespace_type_spec.rb index 7cd6a79ae5d..b4144cc4121 100644 --- a/spec/graphql/types/namespace_type.rb +++ b/spec/graphql/types/namespace_type_spec.rb @@ -4,4 +4,6 @@ require 'spec_helper' describe GitlabSchema.types['Namespace'] do it { expect(described_class.graphql_name).to eq('Namespace') } + + it { expect(described_class).to have_graphql_field(:projects) } end diff --git a/spec/graphql/types/project_statistics_type_spec.rb b/spec/graphql/types/project_statistics_type_spec.rb new file mode 100644 index 00000000000..485e194edb1 --- /dev/null +++ b/spec/graphql/types/project_statistics_type_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe GitlabSchema.types['ProjectStatistics'] do + it "has all the required fields" do + is_expected.to have_graphql_fields(:storage_size, :repository_size, :lfs_objects_size, + :build_artifacts_size, :packages_size, :commit_count) + end +end diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 075fa7c7e43..cb5ac2e3cb1 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -19,4 +19,6 @@ describe GitlabSchema.types['Project'] do it { is_expected.to have_graphql_field(:pipelines) } it { is_expected.to have_graphql_field(:repository) } + + it { is_expected.to have_graphql_field(:statistics) } end diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb index b4626955816..af1972a2513 100644 --- a/spec/graphql/types/query_type_spec.rb +++ b/spec/graphql/types/query_type_spec.rb @@ -5,7 +5,17 @@ describe GitlabSchema.types['Query'] do expect(described_class.graphql_name).to eq('Query') end - it { is_expected.to have_graphql_fields(:project, :group, :echo, :metadata) } + it { is_expected.to have_graphql_fields(:project, :namespace, :group, :echo, :metadata) } + + describe 'namespace field' do + subject { described_class.fields['namespace'] } + + it 'finds namespaces by full path' do + is_expected.to have_graphql_arguments(:full_path) + is_expected.to have_graphql_type(Types::NamespaceType) + is_expected.to have_graphql_resolver(Resolvers::NamespaceResolver) + end + end describe 'project field' do subject { described_class.fields['project'] } diff --git a/spec/lib/gitlab/graphql/loaders/batch_project_statistics_loader_spec.rb b/spec/lib/gitlab/graphql/loaders/batch_project_statistics_loader_spec.rb new file mode 100644 index 00000000000..ec2fcad31e5 --- /dev/null +++ b/spec/lib/gitlab/graphql/loaders/batch_project_statistics_loader_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Graphql::Loaders::BatchProjectStatisticsLoader do + describe '#find' do + it 'only queries once for project statistics' do + stats = create_list(:project_statistics, 2) + project1 = stats.first.project + project2 = stats.last.project + + expect do + described_class.new(project1.id).find + described_class.new(project2.id).find + end.not_to exceed_query_limit(1) + end + end +end diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb index f985c114d4b..358873f9a2f 100644 --- a/spec/models/project_statistics_spec.rb +++ b/spec/models/project_statistics_spec.rb @@ -11,6 +11,20 @@ describe ProjectStatistics do it { is_expected.to belong_to(:namespace) } end + describe 'scopes' do + describe '.for_project_ids' do + it 'returns only requested projects' do + stats = create_list(:project_statistics, 3) + project_ids = stats[0..1].map { |s| s.project_id } + expected_ids = stats[0..1].map { |s| s.id } + + requested_stats = described_class.for_project_ids(project_ids).pluck(:id) + + expect(requested_stats).to eq(expected_ids) + end + end + end + describe 'statistics columns' do it "support values up to 8 exabytes" do statistics.update!( diff --git a/spec/requests/api/graphql/group_query_spec.rb b/spec/requests/api/graphql/group_query_spec.rb index 8ff95cc9af2..db9f2ac9dd0 100644 --- a/spec/requests/api/graphql/group_query_spec.rb +++ b/spec/requests/api/graphql/group_query_spec.rb @@ -86,17 +86,18 @@ describe 'getting group information' do end it 'avoids N+1 queries' do - post_graphql(group_query(group1), current_user: admin) - control_count = ActiveRecord::QueryRecorder.new do post_graphql(group_query(group1), current_user: admin) end.count - create(:project, namespace: group1) + queries = [{ query: group_query(group1) }, + { query: group_query(group2) }] expect do - post_graphql(group_query(group1), current_user: admin) + post_multiplex(queries, current_user: admin) end.not_to exceed_query_limit(control_count) + + expect(graphql_errors).to contain_exactly(nil, nil) end end diff --git a/spec/requests/api/graphql/namespace/projects_spec.rb b/spec/requests/api/graphql/namespace/projects_spec.rb new file mode 100644 index 00000000000..e05273da4bd --- /dev/null +++ b/spec/requests/api/graphql/namespace/projects_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'getting projects', :nested_groups do + include GraphqlHelpers + + let(:group) { create(:group) } + let!(:project) { create(:project, namespace: subject) } + let(:nested_group) { create(:group, parent: group) } + let!(:nested_project) { create(:project, group: nested_group) } + let!(:public_project) { create(:project, :public, namespace: subject) } + let(:user) { create(:user) } + let(:include_subgroups) { true } + + subject { group } + + let(:query) do + graphql_query_for( + 'namespace', + { 'fullPath' => subject.full_path }, + <<~QUERY + projects(includeSubgroups: #{include_subgroups}) { + edges { + node { + #{all_graphql_fields_for('Project')} + } + } + } + QUERY + ) + end + + before do + group.add_owner(user) + end + + shared_examples 'a graphql namespace' do + it_behaves_like 'a working graphql query' do + before do + post_graphql(query, current_user: user) + end + end + + it "includes the packages size if the user can read the statistics" do + post_graphql(query, current_user: user) + + count = if include_subgroups + subject.all_projects.count + else + subject.projects.count + end + + expect(graphql_data['namespace']['projects']['edges'].size).to eq(count) + end + + context 'with no user' do + it 'finds only public projects' do + post_graphql(query, current_user: nil) + + expect(graphql_data['namespace']['projects']['edges'].size).to eq(1) + project = graphql_data['namespace']['projects']['edges'][0]['node'] + expect(project['id']).to eq(public_project.id.to_s) + end + end + end + + it_behaves_like 'a graphql namespace' + + context 'when the namespace is a user' do + subject { user.namespace } + let(:include_subgroups) { false } + + it_behaves_like 'a graphql namespace' + end + + context 'when not including subgroups' do + let(:include_subgroups) { false } + + it_behaves_like 'a graphql namespace' + end +end diff --git a/spec/requests/api/graphql/project/project_statistics_spec.rb b/spec/requests/api/graphql/project/project_statistics_spec.rb new file mode 100644 index 00000000000..8683fa1f390 --- /dev/null +++ b/spec/requests/api/graphql/project/project_statistics_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'rendering namespace statistics' do + include GraphqlHelpers + + let(:project) { create(:project) } + let!(:project_statistics) { create(:project_statistics, project: project, packages_size: 5.megabytes) } + let(:user) { create(:user) } + + let(:query) do + graphql_query_for('project', + { 'fullPath' => project.full_path }, + "statistics { #{all_graphql_fields_for('ProjectStatistics')} }") + end + + before do + project.add_reporter(user) + end + + it_behaves_like 'a working graphql query' do + before do + post_graphql(query, current_user: user) + end + end + + it "includes the packages size if the user can read the statistics" do + post_graphql(query, current_user: user) + + expect(graphql_data['project']['statistics']['packagesSize']).to eq(5.megabytes) + end + + context 'when the project is public' do + let(:project) { create(:project, :public) } + + it 'includes the statistics regardless of the user' do + post_graphql(query, current_user: nil) + + expect(graphql_data['project']['statistics']).to be_present + end + end +end |