diff options
Diffstat (limited to 'spec/requests/api/graphql/project')
6 files changed, 294 insertions, 10 deletions
diff --git a/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb index 708fa96986c..31fef75f679 100644 --- a/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb +++ b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb @@ -94,7 +94,7 @@ RSpec.describe 'getting incident timeline events' do 'id' => promoted_from_note.to_global_id.to_s, 'body' => promoted_from_note.note }, - 'editable' => false, + 'editable' => true, 'action' => timeline_event.action, 'occurredAt' => timeline_event.occurred_at.iso8601, 'createdAt' => timeline_event.created_at.iso8601, diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb index f358ec3e53f..69e14eace66 100644 --- a/spec/requests/api/graphql/project/issues_spec.rb +++ b/spec/requests/api/graphql/project/issues_spec.rb @@ -635,6 +635,18 @@ RSpec.describe 'getting an issue list for a project' do include_examples 'N+1 query check' end + context 'when requesting `closed_as_duplicate_of`' do + let(:requested_fields) { 'closedAsDuplicateOf { id }' } + let(:issue_a_dup) { create(:issue, project: project) } + let(:issue_b_dup) { create(:issue, project: project) } + + before do + issue_a.update!(duplicated_to_id: issue_a_dup) + issue_b.update!(duplicated_to_id: issue_a_dup) + end + + include_examples 'N+1 query check' + end end def issues_ids diff --git a/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb b/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb index 820a5d818c7..4dc272b5c2e 100644 --- a/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb +++ b/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'Query.project.mergeRequests.pipelines' do let_it_be(:project) { create(:project, :public, :repository) } let_it_be(:author) { create(:user) } + let_it_be(:mr_nodes_path) { [:data, :project, :merge_requests, :nodes] } let_it_be(:merge_requests) do [ create(:merge_request, author: author, source_project: project), @@ -33,8 +34,49 @@ RSpec.describe 'Query.project.mergeRequests.pipelines' do GQL end - def run_query(first = nil) - post_graphql(query, current_user: author, variables: { path: project.full_path, first: first }) + before do + merge_requests.first(2).each do |mr| + shas = mr.recent_diff_head_shas + + shas.each do |sha| + create(:ci_pipeline, :success, project: project, ref: mr.source_branch, sha: sha) + end + end + end + + it 'produces correct results' do + r = run_query(3) + + nodes = graphql_dig_at(r, *mr_nodes_path) + + expect(nodes).to all(match('iid' => be_present, 'pipelines' => include('count' => be_a(Integer)))) + expect(graphql_dig_at(r, *mr_nodes_path, :pipelines, :count)).to contain_exactly(1, 1, 0) + end + + it 'is scalable', :request_store, :use_clean_rails_memory_store_caching do + baseline = ActiveRecord::QueryRecorder.new { run_query(1) } + + expect { run_query(2) }.not_to exceed_query_limit(baseline) + end + end + + describe '.nodes' do + let(:query) do + <<~GQL + query($path: ID!, $first: Int) { + project(fullPath: $path) { + mergeRequests(first: $first) { + nodes { + iid + pipelines { + count + nodes { id } + } + } + } + } + } + GQL end before do @@ -48,18 +90,27 @@ RSpec.describe 'Query.project.mergeRequests.pipelines' do end it 'produces correct results' do - run_query(2) - - p_nodes = graphql_data_at(:project, :merge_requests, :nodes) + r = run_query - expect(p_nodes).to all(match('iid' => be_present, 'pipelines' => match('count' => 1))) + expect(graphql_dig_at(r, *mr_nodes_path, :pipelines, :nodes, :id).uniq.size).to eq 3 end it 'is scalable', :request_store, :use_clean_rails_memory_store_caching do - # warm up - run_query + baseline = ActiveRecord::QueryRecorder.new { run_query(1) } - expect { run_query(2) }.to(issue_same_number_of_queries_as { run_query(1) }.ignoring_cached_queries) + expect { run_query(2) }.not_to exceed_query_limit(baseline) end + + it 'requests merge_request_diffs at most once' do + r = ActiveRecord::QueryRecorder.new { run_query(2) } + + expect(r.log.grep(/merge_request_diffs/)).to contain_exactly(a_string_including('SELECT')) + end + end + + def run_query(first = nil) + run_with_clean_state(query, + context: { current_user: author }, + variables: { path: project.full_path, first: first }) end end diff --git a/spec/requests/api/graphql/project/milestones_spec.rb b/spec/requests/api/graphql/project/milestones_spec.rb index 3e8948d83b1..d1ee157fc74 100644 --- a/spec/requests/api/graphql/project/milestones_spec.rb +++ b/spec/requests/api/graphql/project/milestones_spec.rb @@ -59,6 +59,27 @@ RSpec.describe 'getting milestone listings nested in a project' do end end + context 'the user does not have access' do + let_it_be(:project) { create(:project) } + let_it_be(:milestones) { create_list(:milestone, 2, project: project) } + + it 'is nil' do + post_graphql(query, current_user: current_user) + + expect(graphql_data_at(:project)).to be_nil + end + + context 'the user has access' do + let(:expected) { milestones } + + before do + project.add_guest(current_user) + end + + it_behaves_like 'searching with parameters' + end + end + context 'there are no search params' do let(:search_params) { nil } let(:expected) { all_milestones } diff --git a/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb b/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb new file mode 100644 index 00000000000..a025c57d4b8 --- /dev/null +++ b/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe 'getting the packages cleanup policy linked to a project' do + using RSpec::Parameterized::TableSyntax + include GraphqlHelpers + + let_it_be_with_reload(:project) { create(:project) } + let_it_be(:current_user) { project.first_owner } + + let(:fields) do + <<~QUERY + #{all_graphql_fields_for('packages_cleanup_policy'.classify)} + QUERY + end + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('packagesCleanupPolicy', {}, fields) + ) + end + + subject { post_graphql(query, current_user: current_user) } + + it_behaves_like 'a working graphql query' do + before do + subject + end + end + + context 'with an existing policy' do + let_it_be(:policy) { create(:packages_cleanup_policy, project: project) } + + it_behaves_like 'a working graphql query' do + before do + subject + end + end + end + + context 'with different permissions' do + let_it_be(:current_user) { create(:user) } + + let(:packages_cleanup_policy_response) { graphql_data_at('project', 'packagesCleanupPolicy') } + + where(:visibility, :role, :policy_visible) do + :private | :maintainer | true + :private | :developer | false + :private | :reporter | false + :private | :guest | false + :private | :anonymous | false + :public | :maintainer | true + :public | :developer | false + :public | :reporter | false + :public | :guest | false + :public | :anonymous | false + end + + with_them do + before do + project.update!(visibility: visibility.to_s) + project.add_user(current_user, role) unless role == :anonymous + end + + it 'return the proper response' do + subject + + if policy_visible + expect(packages_cleanup_policy_response) + .to eq('keepNDuplicatedPackageFiles' => 'ALL_PACKAGE_FILES', 'nextRunAt' => nil) + else + expect(packages_cleanup_policy_response).to be_blank + end + end + end + end +end diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb new file mode 100644 index 00000000000..66742fcbeb6 --- /dev/null +++ b/spec/requests/api/graphql/project/work_items_spec.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting an work item list for a project' do + include GraphqlHelpers + + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository, :public, group: group) } + let_it_be(:current_user) { create(:user) } + + let_it_be(:item1) { create(:work_item, project: project, discussion_locked: true, title: 'item1') } + let_it_be(:item2) { create(:work_item, project: project, title: 'item2') } + let_it_be(:confidential_item) { create(:work_item, confidential: true, project: project, title: 'item3') } + let_it_be(:other_item) { create(:work_item) } + + let(:items_data) { graphql_data['project']['workItems']['edges'] } + let(:item_filter_params) { {} } + + let(:fields) do + <<~QUERY + edges { + node { + #{all_graphql_fields_for('workItems'.classify)} + } + } + QUERY + end + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('workItems', item_filter_params, fields) + ) + end + + it_behaves_like 'a working graphql query' do + before do + post_graphql(query, current_user: current_user) + end + end + + context 'when the user does not have access to the item' do + before do + project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE) + end + + it 'returns an empty list' do + post_graphql(query) + + expect(items_data).to eq([]) + end + end + + context 'when work_items flag is disabled' do + before do + stub_feature_flags(work_items: false) + end + + it 'returns an empty list' do + post_graphql(query) + + expect(items_data).to eq([]) + end + end + + it 'returns only items visible to user' do + post_graphql(query, current_user: current_user) + + expect(item_ids).to eq([item2.to_global_id.to_s, item1.to_global_id.to_s]) + end + + context 'when the user can see confidential items' do + before do + project.add_developer(current_user) + end + + it 'returns also confidential items' do + post_graphql(query, current_user: current_user) + + expect(item_ids).to eq([confidential_item.to_global_id.to_s, item2.to_global_id.to_s, item1.to_global_id.to_s]) + end + end + + describe 'sorting and pagination' do + let(:data_path) { [:project, :work_items] } + + def pagination_query(params) + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('workItems', params, "#{page_info} nodes { id }") + ) + end + + before do + project.add_developer(current_user) + end + + context 'when sorting by title ascending' do + it_behaves_like 'sorted paginated query' do + let(:sort_param) { :TITLE_ASC } + let(:first_param) { 2 } + let(:all_records) { [item1, item2, confidential_item].map { |item| item.to_global_id.to_s } } + end + end + + context 'when sorting by title descending' do + it_behaves_like 'sorted paginated query' do + let(:sort_param) { :TITLE_DESC } + let(:first_param) { 2 } + let(:all_records) { [confidential_item, item2, item1].map { |item| item.to_global_id.to_s } } + end + end + end + + def item_ids + graphql_dig_at(items_data, :node, :id) + end +end |