diff options
Diffstat (limited to 'spec/requests/api/graphql/project')
14 files changed, 742 insertions, 51 deletions
diff --git a/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb b/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb new file mode 100644 index 00000000000..cb5006ec8e4 --- /dev/null +++ b/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting merge access levels for a branch protection' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + + let(:merge_access_level_data) { merge_access_levels_data[0] } + + let(:merge_access_levels_data) do + graphql_data_at('project', + 'branchRules', + 'nodes', + 0, + 'branchProtection', + 'mergeAccessLevels', + 'nodes') + end + + let(:project) { protected_branch.project } + + let(:merge_access_levels_count) { protected_branch.merge_access_levels.size } + + let(:variables) { { path: project.full_path } } + + let(:fields) { all_graphql_fields_for('MergeAccessLevel') } + + let(:query) do + <<~GQL + query($path: ID!) { + project(fullPath: $path) { + branchRules(first: 1) { + nodes { + branchProtection { + mergeAccessLevels { + nodes { + #{fields} + } + } + } + } + } + } + } + GQL + end + + context 'when the user does not have read_protected_branch abilities' do + let_it_be(:protected_branch) { create(:protected_branch) } + + before do + project.add_guest(current_user) + post_graphql(query, current_user: current_user, variables: variables) + end + + it_behaves_like 'a working graphql query' + + it { expect(merge_access_levels_data).not_to be_present } + end + + shared_examples 'merge access request' do + let(:merge_access) { protected_branch.merge_access_levels.first } + + before do + project.add_maintainer(current_user) + post_graphql(query, current_user: current_user, variables: variables) + end + + it_behaves_like 'a working graphql query' + + it 'returns all merge access levels' do + expect(merge_access_levels_data.size).to eq(merge_access_levels_count) + end + + it 'includes access_level' do + expect(merge_access_level_data['accessLevel']) + .to eq(merge_access.access_level) + end + + it 'includes access_level_description' do + expect(merge_access_level_data['accessLevelDescription']) + .to eq(merge_access.humanize) + end + end + + context 'when the user does have read_protected_branch abilities' do + let(:merge_access) { protected_branch.merge_access_levels.first } + + context 'when no one has access' do + let_it_be(:protected_branch) { create(:protected_branch, :no_one_can_merge) } + + it_behaves_like 'merge access request' + end + + context 'when developers have access' do + let_it_be(:protected_branch) { create(:protected_branch, :developers_can_merge) } + + it_behaves_like 'merge access request' + end + + context 'when maintainers have access' do + let_it_be(:protected_branch) { create(:protected_branch, :maintainers_can_merge) } + + it_behaves_like 'merge access request' + end + end +end diff --git a/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb b/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb new file mode 100644 index 00000000000..59f9c7d61cb --- /dev/null +++ b/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting push access levels for a branch protection' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + + let(:push_access_level_data) { push_access_levels_data[0] } + + let(:push_access_levels_data) do + graphql_data_at('project', + 'branchRules', + 'nodes', + 0, + 'branchProtection', + 'pushAccessLevels', + 'nodes') + end + + let(:project) { protected_branch.project } + + let(:push_access_levels_count) { protected_branch.push_access_levels.size } + + let(:variables) { { path: project.full_path } } + + let(:fields) { all_graphql_fields_for('PushAccessLevel'.classify) } + + let(:query) do + <<~GQL + query($path: ID!) { + project(fullPath: $path) { + branchRules(first: 1) { + nodes { + branchProtection { + pushAccessLevels { + nodes { + #{fields} + } + } + } + } + } + } + } + GQL + end + + context 'when the user does not have read_protected_branch abilities' do + let_it_be(:protected_branch) { create(:protected_branch) } + + before do + project.add_guest(current_user) + post_graphql(query, current_user: current_user, variables: variables) + end + + it_behaves_like 'a working graphql query' + + it { expect(push_access_levels_data).not_to be_present } + end + + shared_examples 'push access request' do + let(:push_access) { protected_branch.push_access_levels.first } + + before do + project.add_maintainer(current_user) + post_graphql(query, current_user: current_user, variables: variables) + end + + it_behaves_like 'a working graphql query' + + it 'returns all push access levels' do + expect(push_access_levels_data.size).to eq(push_access_levels_count) + end + + it 'includes access_level' do + expect(push_access_level_data['accessLevel']) + .to eq(push_access.access_level) + end + + it 'includes access_level_description' do + expect(push_access_level_data['accessLevelDescription']) + .to eq(push_access.humanize) + end + end + + context 'when the user does have read_protected_branch abilities' do + let(:push_access) { protected_branch.push_access_levels.first } + + context 'when no one has access' do + let_it_be(:protected_branch) { create(:protected_branch, :no_one_can_push) } + + it_behaves_like 'push access request' + end + + context 'when developers have access' do + let_it_be(:protected_branch) { create(:protected_branch, :developers_can_push) } + + it_behaves_like 'push access request' + end + + context 'when maintainers have access' do + let_it_be(:protected_branch) { create(:protected_branch, :maintainers_can_push) } + + it_behaves_like 'push access request' + end + end +end diff --git a/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb b/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb new file mode 100644 index 00000000000..8a3f546ef95 --- /dev/null +++ b/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting branch protection for a branch rule' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:branch_rule) { create(:protected_branch) } + let_it_be(:project) { branch_rule.project } + + let(:branch_protection_data) do + graphql_data_at('project', 'branchRules', 'nodes', 0, 'branchProtection') + end + + let(:variables) { { path: project.full_path } } + + let(:fields) { all_graphql_fields_for('BranchProtection') } + + let(:query) do + <<~GQL + query($path: ID!) { + project(fullPath: $path) { + branchRules(first: 1) { + nodes { + branchProtection { + #{fields} + } + } + } + } + } + GQL + end + + context 'when the user does not have read_protected_branch abilities' do + before do + project.add_guest(current_user) + post_graphql(query, current_user: current_user, variables: variables) + end + + it_behaves_like 'a working graphql query' + + it { expect(branch_protection_data).not_to be_present } + end + + context 'when the user does have read_protected_branch abilities' do + before do + project.add_maintainer(current_user) + post_graphql(query, current_user: current_user, variables: variables) + end + + it_behaves_like 'a working graphql query' + + it 'includes allow_force_push' do + expect(branch_protection_data['allowForcePush']).to be_in([true, false]) + expect(branch_protection_data['allowForcePush']).to eq(branch_rule.allow_force_push) + end + end +end diff --git a/spec/requests/api/graphql/project/branch_rules_spec.rb b/spec/requests/api/graphql/project/branch_rules_spec.rb new file mode 100644 index 00000000000..70fb37941e2 --- /dev/null +++ b/spec/requests/api/graphql/project/branch_rules_spec.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting list of branch rules for a project' do + include GraphqlHelpers + + let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:current_user) { create(:user) } + let_it_be(:branch_name_a) { 'branch_name_a' } + let_it_be(:branch_name_b) { 'wildcard-*' } + let_it_be(:branch_rules) { [branch_rule_a, branch_rule_b] } + + let_it_be(:branch_rule_a) do + create(:protected_branch, project: project, name: branch_name_a) + end + + let_it_be(:branch_rule_b) do + create(:protected_branch, project: project, name: branch_name_b) + end + + let(:branch_rules_data) { graphql_data_at('project', 'branchRules', 'edges') } + let(:variables) { { path: project.full_path } } + + let(:fields) do + <<~QUERY + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + cursor + node { + #{all_graphql_fields_for('branch_rules'.classify)} + } + } + QUERY + end + + let(:query) do + <<~GQL + query($path: ID!, $n: Int, $cursor: String) { + project(fullPath: $path) { + branchRules(first: $n, after: $cursor) { #{fields} } + } + } + GQL + end + + context 'when the user does not have read_protected_branch abilities' do + before do + project.add_guest(current_user) + post_graphql(query, current_user: current_user, variables: variables) + end + + it_behaves_like 'a working graphql query' + + it { expect(branch_rules_data).to be_empty } + end + + context 'when the user does have read_protected_branch abilities' do + before do + project.add_maintainer(current_user) + post_graphql(query, current_user: current_user, variables: variables) + end + + it_behaves_like 'a working graphql query' + + it 'includes a name' do + expect(branch_rules_data.dig(0, 'node', 'name')).to be_present + end + + it 'includes created_at and updated_at' do + expect(branch_rules_data.dig(0, 'node', 'createdAt')).to be_present + expect(branch_rules_data.dig(1, 'node', 'updatedAt')).to be_present + end + + context 'when limiting the number of results' do + let(:branch_rule_limit) { 1 } + let(:variables) { { path: project.full_path, n: branch_rule_limit } } + let(:next_variables) do + { path: project.full_path, n: branch_rule_limit, cursor: last_cursor } + end + + it_behaves_like 'a working graphql query' do + it 'only returns N branch_rules' do + expect(branch_rules_data.size).to eq(branch_rule_limit) + expect(has_next_page).to be_truthy + expect(has_prev_page).to be_falsey + post_graphql(query, current_user: current_user, variables: next_variables) + expect(branch_rules_data.size).to eq(branch_rule_limit) + expect(has_next_page).to be_falsey + expect(has_prev_page).to be_truthy + end + end + + context 'when no limit is provided' do + let(:branch_rule_limit) { nil } + + it 'returns all branch_rules' do + expect(branch_rules_data.size).to eq(branch_rules.size) + end + end + end + end + + def pagination_info + graphql_data_at('project', 'branchRules', 'pageInfo') + end + + def has_next_page + pagination_info['hasNextPage'] + end + + def has_prev_page + pagination_info['hasPreviousPage'] + end + + def last_cursor + branch_rules_data.last['cursor'] + end +end diff --git a/spec/requests/api/graphql/project/deployment_spec.rb b/spec/requests/api/graphql/project/deployment_spec.rb new file mode 100644 index 00000000000..e5ef7bcafbf --- /dev/null +++ b/spec/requests/api/graphql/project/deployment_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project Deployment query' do + let_it_be(:project) { create(:project, :private, :repository) } + let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } } + let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } } + let_it_be(:environment) { create(:environment, project: project) } + let_it_be(:deployment) { create(:deployment, environment: environment, project: project) } + + subject { GitlabSchema.execute(query, context: { current_user: user }).as_json } + + let(:user) { developer } + + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + deployment(iid: #{deployment.iid}) { + id + iid + ref + tag + sha + createdAt + updatedAt + finishedAt + status + } + } + } + ) + end + + it 'returns the deployment of the project' do + deployment_data = subject.dig('data', 'project', 'deployment') + + expect(deployment_data['iid']).to eq(deployment.iid.to_s) + end + + context 'when user is guest' do + let(:user) { guest } + + it 'returns nothing' do + deployment_data = subject.dig('data', 'project', 'deployment') + + expect(deployment_data).to be_nil + end + end +end diff --git a/spec/requests/api/graphql/project/environments_spec.rb b/spec/requests/api/graphql/project/environments_spec.rb new file mode 100644 index 00000000000..e5b6aebbf2c --- /dev/null +++ b/spec/requests/api/graphql/project/environments_spec.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project Environments query' do + include GraphqlHelpers + + let_it_be(:project) { create(:project, :private, :repository) } + let_it_be_with_refind(:production) { create(:environment, :production, project: project) } + let_it_be_with_refind(:staging) { create(:environment, :staging, project: project) } + let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } } + + subject { post_graphql(query, current_user: user) } + + let(:user) { developer } + + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + environment(name: "#{production.name}") { + slug + createdAt + updatedAt + autoStopAt + autoDeleteAt + tier + environmentType + } + } + } + ) + end + + it 'returns the specified fields of the environment', :aggregate_failures do + production.update!(auto_stop_at: 1.day.ago, auto_delete_at: 2.days.ago, environment_type: 'review') + + subject + + environment_data = graphql_data.dig('project', 'environment') + expect(environment_data['slug']).to eq(production.slug) + expect(environment_data['createdAt']).to eq(production.created_at.iso8601) + expect(environment_data['updatedAt']).to eq(production.updated_at.iso8601) + expect(environment_data['autoStopAt']).to eq(production.auto_stop_at.iso8601) + expect(environment_data['autoDeleteAt']).to eq(production.auto_delete_at.iso8601) + expect(environment_data['tier']).to eq(production.tier.upcase) + expect(environment_data['environmentType']).to eq(production.environment_type) + end + + describe 'last deployments of environments' do + ::Deployment.statuses.each do |status, _| + let_it_be(:"production_#{status}_deployment") do + create(:deployment, status.to_sym, environment: production, project: project) + end + + let_it_be(:"staging_#{status}_deployment") do + create(:deployment, status.to_sym, environment: staging, project: project) + end + end + + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + environments { + nodes { + name + lastSuccessDeployment: lastDeployment(status: SUCCESS) { + iid + } + lastRunningDeployment: lastDeployment(status: RUNNING) { + iid + } + lastBlockedDeployment: lastDeployment(status: BLOCKED) { + iid + } + } + } + } + } + ) + end + + it 'returns all last deployments of the environment' do + subject + + environments_data = graphql_data_at(:project, :environments, :nodes) + + environments_data.each do |environment_data| + name = environment_data['name'] + success_deployment = public_send(:"#{name}_success_deployment") + running_deployment = public_send(:"#{name}_running_deployment") + blocked_deployment = public_send(:"#{name}_blocked_deployment") + + expect(environment_data['lastSuccessDeployment']['iid']).to eq(success_deployment.iid.to_s) + expect(environment_data['lastRunningDeployment']['iid']).to eq(running_deployment.iid.to_s) + expect(environment_data['lastBlockedDeployment']['iid']).to eq(blocked_deployment.iid.to_s) + end + end + + it 'executes the same number of queries in single environment and multiple environments' do + single_environment_query = + %( + query { + project(fullPath: "#{project.full_path}") { + environment(name: "#{production.name}") { + name + lastSuccessDeployment: lastDeployment(status: SUCCESS) { + iid + } + lastRunningDeployment: lastDeployment(status: RUNNING) { + iid + } + lastBlockedDeployment: lastDeployment(status: BLOCKED) { + iid + } + } + } + } + ) + + baseline = ActiveRecord::QueryRecorder.new do + run_with_clean_state(single_environment_query, context: { current_user: user }) + end + + multi = ActiveRecord::QueryRecorder.new do + run_with_clean_state(query, context: { current_user: user }) + end + + expect(multi).not_to exceed_query_limit(baseline) + end + end +end diff --git a/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb index 8cda61f0628..0444ce43c22 100644 --- a/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb +++ b/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb @@ -11,14 +11,14 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) let_it_be(:developer) { create(:user) } let_it_be(:stranger) { create(:user) } let_it_be(:old_version) do - create(:design_version, issue: issue, - created_designs: create_list(:design, 3, issue: issue)) + create(:design_version, issue: issue, created_designs: create_list(:design, 3, issue: issue)) end let_it_be(:version) do - create(:design_version, issue: issue, - modified_designs: old_version.designs, - created_designs: create_list(:design, 2, issue: issue)) + create(:design_version, + issue: issue, + modified_designs: old_version.designs, + created_designs: create_list(:design, 2, issue: issue)) end let(:current_user) { developer } diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb index 596e023a027..28282860416 100644 --- a/spec/requests/api/graphql/project/issues_spec.rb +++ b/spec/requests/api/graphql/project/issues_spec.rb @@ -27,14 +27,6 @@ RSpec.describe 'getting an issue list for a project' do QUERY end - let(:query) do - graphql_query_for( - 'project', - { 'fullPath' => project.full_path }, - query_graphql_field('issues', issue_filter_params, fields) - ) - end - it_behaves_like 'a working graphql query' do before do post_graphql(query, current_user: current_user) @@ -89,6 +81,14 @@ RSpec.describe 'getting an issue list for a project' do end end + context 'when filtering by search' do + it_behaves_like 'query with a search term' do + let(:issuable_data) { issues_data } + let(:user) { current_user } + let_it_be(:issuable) { create(:issue, project: project, description: 'bar') } + end + end + context 'when limiting the number of results' do let(:query) do <<~GQL @@ -301,7 +301,7 @@ RSpec.describe 'getting an issue list for a project' do let_it_be(:relative_issue5) { create(:issue, project: sort_project, relative_position: 500) } context 'when ascending' do - it_behaves_like 'sorted paginated query' do + it_behaves_like 'sorted paginated query', is_reversible: true do let(:sort_param) { :RELATIVE_POSITION_ASC } let(:first_param) { 2 } let(:all_records) do @@ -679,4 +679,12 @@ RSpec.describe 'getting an issue list for a project' do def issues_ids graphql_dig_at(issues_data, :node, :id) end + + def query(params = issue_filter_params) + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('issues', params, fields) + ) + end end diff --git a/spec/requests/api/graphql/project/job_spec.rb b/spec/requests/api/graphql/project/job_spec.rb new file mode 100644 index 00000000000..6edd4cf753f --- /dev/null +++ b/spec/requests/api/graphql/project/job_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Query.project.job' do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + + let(:job) { create(:ci_build, project: project, name: 'GQL test job') } + + let(:query) do + <<~QUERY + { + project(fullPath: "#{project.full_path}") { + job(id: "#{job.to_global_id}") { + name + } + } + } + QUERY + end + + context 'when the user can read jobs on the project' do + before do + project.add_developer(user) + end + + it 'returns the job that matches the given ID' do + post_graphql(query, current_user: user) + + expect(graphql_data.dig('project', 'job', 'name')).to eq('GQL test job') + end + + context 'when no job matches the given ID' do + let(:job) { create(:ci_build, project: create(:project), name: 'Job from another project') } + + it 'returns null' do + post_graphql(query, current_user: user) + + expect(graphql_data.dig('project', 'job')).to be_nil + end + end + end + + context 'when the user cannot read jobs on the project' do + it 'returns null' do + post_graphql(query, current_user: user) + + expect(graphql_data.dig('project', 'job')).to be_nil + end + end +end diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb index d2f34080be3..6a59df81405 100644 --- a/spec/requests/api/graphql/project/merge_request_spec.rb +++ b/spec/requests/api/graphql/project/merge_request_spec.rb @@ -365,7 +365,7 @@ RSpec.describe 'getting merge request information nested in a project' do expect(interaction_data).to contain_exactly a_hash_including( 'canMerge' => false, 'canUpdate' => can_update, - 'reviewState' => attention_requested, + 'reviewState' => unreviewed, 'reviewed' => false, 'approved' => false ) @@ -398,8 +398,8 @@ RSpec.describe 'getting merge request information nested in a project' do describe 'scalability' do let_it_be(:other_users) { create_list(:user, 3) } - let(:attention_requested) do - { 'reviewState' => 'ATTENTION_REQUESTED' } + let(:unreviewed) do + { 'reviewState' => 'UNREVIEWED' } end let(:reviewed) do @@ -425,15 +425,15 @@ RSpec.describe 'getting merge request information nested in a project' do other_users.each do |user| assign_user(user) - merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user, state: :attention_requested) + merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user) end expect { post_graphql(query) }.not_to exceed_query_limit(baseline) expect(interaction_data).to contain_exactly( - include(attention_requested), - include(attention_requested), - include(attention_requested), + include(unreviewed), + include(unreviewed), + include(unreviewed), include(reviewed) ) end @@ -462,17 +462,17 @@ RSpec.describe 'getting merge request information nested in a project' do it_behaves_like 'when requesting information about MR interactions' do let(:field) { :reviewers } - let(:attention_requested) { 'ATTENTION_REQUESTED' } + let(:unreviewed) { 'UNREVIEWED' } let(:can_update) { false } def assign_user(user) - merge_request.merge_request_reviewers.create!(reviewer: user, state: :attention_requested) + merge_request.merge_request_reviewers.create!(reviewer: user) end end it_behaves_like 'when requesting information about MR interactions' do let(:field) { :assignees } - let(:attention_requested) { nil } + let(:unreviewed) { nil } let(:can_update) { true } # assignees can update MRs def assign_user(user) diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb index 08c6a2d9927..41915d3cdee 100644 --- a/spec/requests/api/graphql/project/pipeline_spec.rb +++ b/spec/requests/api/graphql/project/pipeline_spec.rb @@ -111,7 +111,7 @@ RSpec.describe 'getting pipeline information nested in a project' do name: build_job.name, pipeline: pipeline, stage_idx: 0, - stage: build_job.stage) + stage: build_job.stage_name) end let(:fields) do diff --git a/spec/requests/api/graphql/project/terraform/state_spec.rb b/spec/requests/api/graphql/project/terraform/state_spec.rb index 8f2d2cffef2..5e207ec0963 100644 --- a/spec/requests/api/graphql/project/terraform/state_spec.rb +++ b/spec/requests/api/graphql/project/terraform/state_spec.rb @@ -60,17 +60,17 @@ RSpec.describe 'query a single terraform state' do expect(data).to match a_graphql_entity_for( terraform_state, :name, - 'lockedAt' => terraform_state.locked_at.iso8601, - 'createdAt' => terraform_state.created_at.iso8601, - 'updatedAt' => terraform_state.updated_at.iso8601, - 'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user), + 'lockedAt' => terraform_state.locked_at.iso8601, + 'createdAt' => terraform_state.created_at.iso8601, + 'updatedAt' => terraform_state.updated_at.iso8601, + 'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user), 'latestVersion' => a_graphql_entity_for( latest_version, - 'serial' => eq(latest_version.version), - 'createdAt' => eq(latest_version.created_at.iso8601), - 'updatedAt' => eq(latest_version.updated_at.iso8601), + 'serial' => eq(latest_version.version), + 'createdAt' => eq(latest_version.created_at.iso8601), + 'updatedAt' => eq(latest_version.updated_at.iso8601), 'createdByUser' => a_graphql_entity_for(latest_version.created_by_user), - 'job' => { 'name' => eq(latest_version.build.name) } + 'job' => { 'name' => eq(latest_version.build.name) } ) ) end diff --git a/spec/requests/api/graphql/project/terraform/states_spec.rb b/spec/requests/api/graphql/project/terraform/states_spec.rb index a7ec6f69776..cc3660bcc6b 100644 --- a/spec/requests/api/graphql/project/terraform/states_spec.rb +++ b/spec/requests/api/graphql/project/terraform/states_spec.rb @@ -64,18 +64,18 @@ RSpec.describe 'query terraform states' do expect(data['nodes']).to contain_exactly a_graphql_entity_for( terraform_state, :name, - 'lockedAt' => terraform_state.locked_at.iso8601, - 'createdAt' => terraform_state.created_at.iso8601, - 'updatedAt' => terraform_state.updated_at.iso8601, - 'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user), + 'lockedAt' => terraform_state.locked_at.iso8601, + 'createdAt' => terraform_state.created_at.iso8601, + 'updatedAt' => terraform_state.updated_at.iso8601, + 'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user), 'latestVersion' => a_graphql_entity_for( latest_version, - 'serial' => eq(latest_version.version), - 'downloadPath' => eq(download_path), - 'createdAt' => eq(latest_version.created_at.iso8601), - 'updatedAt' => eq(latest_version.updated_at.iso8601), + 'serial' => eq(latest_version.version), + 'downloadPath' => eq(download_path), + 'createdAt' => eq(latest_version.created_at.iso8601), + 'updatedAt' => eq(latest_version.updated_at.iso8601), 'createdByUser' => a_graphql_entity_for(latest_version.created_by_user), - 'job' => { 'name' => eq(latest_version.build.name) } + 'job' => { 'name' => eq(latest_version.build.name) } ) ) end diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb index 6ef28392b8b..69f8d1cac74 100644 --- a/spec/requests/api/graphql/project/work_items_spec.rb +++ b/spec/requests/api/graphql/project/work_items_spec.rb @@ -10,7 +10,10 @@ RSpec.describe 'getting an work item list for a project' do 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(:item2) do + create(:work_item, project: project, title: 'item2', last_edited_by: current_user, last_edited_at: 1.day.ago) + end + let_it_be(:confidential_item) { create(:work_item, confidential: true, project: project, title: 'item3') } let_it_be(:other_item) { create(:work_item) } @@ -27,14 +30,6 @@ RSpec.describe 'getting an work item list for a project' do 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) @@ -83,6 +78,48 @@ RSpec.describe 'getting an work item list for a project' do end end + context 'when fetching description edit information' do + let(:fields) do + <<~GRAPHQL + nodes { + widgets { + type + ... on WorkItemWidgetDescription { + edited + lastEditedAt + lastEditedBy { + webPath + username + } + } + } + } + GRAPHQL + end + + it 'avoids N+1 queries' do + post_graphql(query, current_user: current_user) # warm-up + + control = ActiveRecord::QueryRecorder.new do + post_graphql(query, current_user: current_user) + end + expect_graphql_errors_to_be_empty + + create_list(:work_item, 3, :last_edited_by_user, last_edited_at: 1.week.ago, project: project) + + expect_graphql_errors_to_be_empty + expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(control) + end + end + + context 'when filtering by search' do + it_behaves_like 'query with a search term' do + let(:issuable_data) { items_data } + let(:user) { current_user } + let_it_be(:issuable) { create(:work_item, project: project, description: 'bar') } + end + end + describe 'sorting and pagination' do let(:data_path) { [:project, :work_items] } @@ -118,4 +155,12 @@ RSpec.describe 'getting an work item list for a project' do def item_ids graphql_dig_at(items_data, :node, :id) end + + def query(params = item_filter_params) + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('workItems', params, fields) + ) + end end |