diff options
Diffstat (limited to 'spec/requests/api/graphql/project')
31 files changed, 824 insertions, 189 deletions
diff --git a/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb index 4c048caaeee..dd001a73349 100644 --- a/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'getting Alert Management Alert Assignees' do +RSpec.describe 'getting Alert Management Alert Assignees' do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb new file mode 100644 index 00000000000..352a94cfc1d --- /dev/null +++ b/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting Alert Management Alert Assignees' do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:current_user) { create(:user) } + + let(:fields) do + <<~QUERY + nodes { + iid + metricsDashboardUrl + } + QUERY + end + + let(:graphql_query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('alertManagementAlerts', {}, fields) + ) + end + + let(:alerts) { graphql_data.dig('project', 'alertManagementAlerts', 'nodes') } + let(:first_alert) { alerts.first } + + before do + project.add_developer(current_user) + end + + context 'with self-managed prometheus payload' do + include_context 'self-managed prometheus alert attributes' + + before do + create(:alert_management_alert, :prometheus, project: project, payload: payload) + end + + it 'includes the correct metrics dashboard url' do + post_graphql(graphql_query, current_user: current_user) + + expect(first_alert).to include('metricsDashboardUrl' => dashboard_url_for_alert) + end + end + + context 'with gitlab-managed prometheus payload' do + include_context 'gitlab-managed prometheus alert attributes' + + before do + create(:alert_management_alert, :prometheus, project: project, payload: payload, prometheus_alert: prometheus_alert) + end + + it 'includes the correct metrics dashboard url' do + post_graphql(graphql_query, current_user: current_user) + + expect(first_alert).to include('metricsDashboardUrl' => dashboard_url_for_alert) + end + end +end diff --git a/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb index df6bfa8c97b..1350cba119b 100644 --- a/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb @@ -2,15 +2,15 @@ require 'spec_helper' -describe 'getting Alert Management Alert Notes' do +RSpec.describe 'getting Alert Management Alert Notes' do include GraphqlHelpers let_it_be(:project) { create(:project) } let_it_be(:current_user) { create(:user) } let_it_be(:first_alert) { create(:alert_management_alert, project: project, assignees: [current_user]) } let_it_be(:second_alert) { create(:alert_management_alert, project: project) } - let_it_be(:first_system_note) { create(:note_on_alert, noteable: first_alert, project: project) } - let_it_be(:second_system_note) { create(:note_on_alert, noteable: first_alert, project: project) } + let_it_be(:first_system_note) { create(:note_on_alert, :with_system_note_metadata, noteable: first_alert, project: project) } + let_it_be(:second_system_note) { create(:note_on_alert, :with_system_note_metadata, noteable: first_alert, project: project) } let(:params) { {} } @@ -21,6 +21,8 @@ describe 'getting Alert Management Alert Notes' do notes { nodes { id + body + systemNoteIconName } } } @@ -44,7 +46,17 @@ describe 'getting Alert Management Alert Notes' do project.add_developer(current_user) end - it 'returns the notes ordered by createdAt' do + it 'includes expected data' do + post_graphql(query, current_user: current_user) + + expect(first_notes_result.first).to include( + 'id' => first_system_note.to_global_id.to_s, + 'systemNoteIconName' => 'git-merge', + 'body' => first_system_note.note + ) + end + + it 'returns the notes ordered by createdAt with sufficient content' do post_graphql(query, current_user: current_user) expect(first_notes_result.length).to eq(2) @@ -64,4 +76,18 @@ describe 'getting Alert Management Alert Notes' do expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(base_count) expect(alerts_result.length).to eq(3) end + + context 'for non-system notes' do + let_it_be(:user_note) { create(:note_on_alert, noteable: second_alert, project: project) } + + it 'includes expected data' do + post_graphql(query, current_user: current_user) + + expect(second_notes_result.first).to include( + 'id' => user_note.to_global_id.to_s, + 'systemNoteIconName' => nil, + 'body' => user_note.note + ) + end + end end diff --git a/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb b/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb index a0d1ff7efc5..b62215f43fb 100644 --- a/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe 'getting Alert Management Alert counts by status' do +RSpec.describe 'getting Alert Management Alert counts by status' do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb index c591895f295..f050c6873f3 100644 --- a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe 'getting Alert Management Alerts' do +RSpec.describe 'getting Alert Management Alerts' do include GraphqlHelpers let_it_be(:payload) { { 'custom' => { 'alert' => 'payload' } } } @@ -73,12 +73,13 @@ describe 'getting Alert Management Alerts' do 'endedAt' => nil, 'details' => { 'custom.alert' => 'payload' }, 'createdAt' => triggered_alert.created_at.strftime('%Y-%m-%dT%H:%M:%SZ'), - 'updatedAt' => triggered_alert.updated_at.strftime('%Y-%m-%dT%H:%M:%SZ') + 'updatedAt' => triggered_alert.updated_at.strftime('%Y-%m-%dT%H:%M:%SZ'), + 'metricsDashboardUrl' => nil ) expect(second_alert).to include( 'iid' => resolved_alert.iid.to_s, - 'issueIid' => nil, + 'issueIid' => resolved_alert.issue_iid.to_s, 'status' => 'RESOLVED', 'endedAt' => resolved_alert.ended_at.strftime('%Y-%m-%dT%H:%M:%SZ') ) @@ -109,14 +110,14 @@ describe 'getting Alert Management Alerts' do it_behaves_like 'a working graphql query' it 'sorts in the correct order' do - expect(iids).to eq [resolved_alert.iid.to_s, triggered_alert.iid.to_s] + expect(iids).to eq [triggered_alert.iid.to_s, resolved_alert.iid.to_s] end context 'ascending order' do let(:params) { 'sort: SEVERITY_ASC' } it 'sorts in the correct order' do - expect(iids).to eq [triggered_alert.iid.to_s, resolved_alert.iid.to_s] + expect(iids).to eq [resolved_alert.iid.to_s, triggered_alert.iid.to_s] end end end diff --git a/spec/requests/api/graphql/project/base_service_spec.rb b/spec/requests/api/graphql/project/base_service_spec.rb index 8199f331fbf..4dfc242da80 100644 --- a/spec/requests/api/graphql/project/base_service_spec.rb +++ b/spec/requests/api/graphql/project/base_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'query Jira service' do +RSpec.describe 'query Jira service' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/project/container_expiration_policy_spec.rb b/spec/requests/api/graphql/project/container_expiration_policy_spec.rb index d0563f9ff05..b064e4d43e9 100644 --- a/spec/requests/api/graphql/project/container_expiration_policy_spec.rb +++ b/spec/requests/api/graphql/project/container_expiration_policy_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe 'getting a repository in a project' do +RSpec.describe 'getting a repository in a project' do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb b/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb index a1f9fa1f10c..b2b42137acf 100644 --- a/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb +++ b/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe 'getting a detailed sentry error' do +RSpec.describe 'getting a detailed sentry error' do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb b/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb index 06a0bfc0d32..cd84ce9cb96 100644 --- a/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb +++ b/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe 'sentry errors requests' do +RSpec.describe 'sentry errors requests' do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } let_it_be(:project_setting) { create(:project_error_tracking_setting, project: project) } diff --git a/spec/requests/api/graphql/project/grafana_integration_spec.rb b/spec/requests/api/graphql/project/grafana_integration_spec.rb index c9bc6c1a68e..688959e622d 100644 --- a/spec/requests/api/graphql/project/grafana_integration_spec.rb +++ b/spec/requests/api/graphql/project/grafana_integration_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe 'Getting Grafana Integration' do +RSpec.describe 'Getting Grafana Integration' do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } 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 04f445b4318..1b654e660e3 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 @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)' do +RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)' do include GraphqlHelpers include DesignManagementTestHelpers diff --git a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb index 18787bf925d..640ac95cd86 100644 --- a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb +++ b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'Getting versions related to an issue' do +RSpec.describe 'Getting versions related to an issue' do include GraphqlHelpers include DesignManagementTestHelpers diff --git a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb index b6fd0d91bda..e47c025f8b2 100644 --- a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb +++ b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'Getting designs related to an issue' do +RSpec.describe 'Getting designs related to an issue' do include GraphqlHelpers include DesignManagementTestHelpers diff --git a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb index 0207bb9123a..ae5c8363d0f 100644 --- a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb +++ b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'Getting designs related to an issue' do +RSpec.describe 'Getting designs related to an issue' do include GraphqlHelpers include DesignManagementTestHelpers diff --git a/spec/requests/api/graphql/project/issue/notes_spec.rb b/spec/requests/api/graphql/project/issue/notes_spec.rb index bfc89434370..97f5261ef1d 100644 --- a/spec/requests/api/graphql/project/issue/notes_spec.rb +++ b/spec/requests/api/graphql/project/issue/notes_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'getting notes for an issue' do +RSpec.describe 'getting notes for an issue' do include GraphqlHelpers let(:noteable) { create(:issue) } diff --git a/spec/requests/api/graphql/project/issue_spec.rb b/spec/requests/api/graphql/project/issue_spec.rb index 92d2f9d0d31..5f368833181 100644 --- a/spec/requests/api/graphql/project/issue_spec.rb +++ b/spec/requests/api/graphql/project/issue_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'Query.project(fullPath).issue(iid)' do +RSpec.describe 'Query.project(fullPath).issue(iid)' do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb index 3128f527356..cdfff2f50d4 100644 --- a/spec/requests/api/graphql/project/issues_spec.rb +++ b/spec/requests/api/graphql/project/issues_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'getting an issue list for a project' do +RSpec.describe 'getting an issue list for a project' do include GraphqlHelpers let(:project) { create(:project, :repository, :public) } diff --git a/spec/requests/api/graphql/project/jira_import_spec.rb b/spec/requests/api/graphql/project/jira_import_spec.rb index 7be14696963..814965262b6 100644 --- a/spec/requests/api/graphql/project/jira_import_spec.rb +++ b/spec/requests/api/graphql/project/jira_import_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'query Jira import data' do +RSpec.describe 'query Jira import data' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/project/jira_projects_spec.rb b/spec/requests/api/graphql/project/jira_projects_spec.rb index d67c89f18c9..d5f59711ab1 100644 --- a/spec/requests/api/graphql/project/jira_projects_spec.rb +++ b/spec/requests/api/graphql/project/jira_projects_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'query Jira projects' do +RSpec.describe 'query Jira projects' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } @@ -80,34 +80,6 @@ describe 'query Jira projects' do it_behaves_like 'fetches first project' end - - context 'with before cursor' do - let(:projects_query) { 'projects(before: "Mg==", first: 1)' } - - it_behaves_like 'fetches first project' - end - - context 'with after cursor' do - let(:projects_query) { 'projects(after: "MA==", first: 1)' } - - it_behaves_like 'fetches first project' - end - end - - context 'with valid but inexistent after cursor' do - let(:projects_query) { 'projects(after: "MTk==")' } - - it 'retuns empty list of jira projects' do - expect(jira_projects.size).to eq(0) - end - end - - context 'with invalid after cursor' do - let(:projects_query) { 'projects(after: "invalid==")' } - - it 'treats the invalid cursor as no cursor and returns list of jira projects' do - expect(jira_projects.size).to eq(2) - end end end end diff --git a/spec/requests/api/graphql/project/jira_service_spec.rb b/spec/requests/api/graphql/project/jira_service_spec.rb index 4ac598b789f..905a669bf0d 100644 --- a/spec/requests/api/graphql/project/jira_service_spec.rb +++ b/spec/requests/api/graphql/project/jira_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'query Jira service' do +RSpec.describe 'query Jira service' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/project/labels_query_spec.rb b/spec/requests/api/graphql/project/labels_query_spec.rb index ecc43e0a3db..eeaaaaee575 100644 --- a/spec/requests/api/graphql/project/labels_query_spec.rb +++ b/spec/requests/api/graphql/project/labels_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'getting project label information' do +RSpec.describe 'getting project label information' do include GraphqlHelpers let_it_be(:project) { create(:project, :public) } diff --git a/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb b/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb index c616310a72c..dd16b052e0e 100644 --- a/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb +++ b/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'getting notes for a merge request' do +RSpec.describe 'getting notes for a merge request' do include GraphqlHelpers let_it_be(:noteable) { create(:merge_request) } diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb index 643532bf2e2..c39358a2db1 100644 --- a/spec/requests/api/graphql/project/merge_request_spec.rb +++ b/spec/requests/api/graphql/project/merge_request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'getting merge request information nested in a project' do +RSpec.describe 'getting merge request information nested in a project' do include GraphqlHelpers let(:project) { create(:project, :repository, :public) } @@ -43,6 +43,59 @@ describe 'getting merge request information nested in a project' do expect(merge_request_graphql_data['author']['username']).to eq(merge_request.author.username) end + it 'includes diff stats' do + be_natural = an_instance_of(Integer).and(be >= 0) + + post_graphql(query, current_user: current_user) + + sums = merge_request_graphql_data['diffStats'].reduce([0, 0, 0]) do |(a, d, c), node| + a_, d_ = node.values_at('additions', 'deletions') + [a + a_, d + d_, c + a_ + d_] + end + + expect(merge_request_graphql_data).to include( + 'diffStats' => all(a_hash_including('path' => String, 'additions' => be_natural, 'deletions' => be_natural)), + 'diffStatsSummary' => a_hash_including( + 'fileCount' => merge_request.diff_stats.count, + 'additions' => be_natural, + 'deletions' => be_natural, + 'changes' => be_natural + ) + ) + + # diff_stats is consistent with summary + expect(merge_request_graphql_data['diffStatsSummary'] + .values_at('additions', 'deletions', 'changes')).to eq(sums) + + # diff_stats_summary is internally consistent + expect(merge_request_graphql_data['diffStatsSummary'] + .values_at('additions', 'deletions').sum) + .to eq(merge_request_graphql_data.dig('diffStatsSummary', 'changes')) + .and be_positive + end + + context 'requesting a specific diff stat' do + let(:diff_stat) { merge_request.diff_stats.first } + + let(:query) do + graphql_query_for(:project, { full_path: project.full_path }, + query_graphql_field(:merge_request, { iid: merge_request.iid.to_s }, [ + query_graphql_field(:diff_stats, { path: diff_stat.path }, all_graphql_fields_for('DiffStats')) + ]) + ) + end + + it 'includes only the requested stats' do + post_graphql(query, current_user: current_user) + + expect(merge_request_graphql_data).to include( + 'diffStats' => contain_exactly( + a_hash_including('path' => diff_stat.path, 'additions' => diff_stat.additions, 'deletions' => diff_stat.deletions) + ) + ) + end + end + it 'includes correct mergedAt value when merged' do time = 1.week.ago merge_request.mark_as_merged diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb index 49fdfe29874..e2255fdb048 100644 --- a/spec/requests/api/graphql/project/merge_requests_spec.rb +++ b/spec/requests/api/graphql/project/merge_requests_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'getting merge request listings nested in a project' do +RSpec.describe 'getting merge request listings nested in a project' do include GraphqlHelpers let_it_be(:project) { create(:project, :repository, :public) } diff --git a/spec/requests/api/graphql/project/packages_spec.rb b/spec/requests/api/graphql/project/packages_spec.rb new file mode 100644 index 00000000000..88f97f9256b --- /dev/null +++ b/spec/requests/api/graphql/project/packages_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting a package list for a project' do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:current_user) { create(:user) } + let_it_be(:package) { create(:package, project: project) } + let(:packages_data) { graphql_data['project']['packages']['edges'] } + + let(:fields) do + <<~QUERY + edges { + node { + #{all_graphql_fields_for('packages'.classify)} + } + } + QUERY + end + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('packages', {}, fields) + ) + end + + context 'without the need for a license' do + context 'when user has access to the project' do + before do + project.add_reporter(current_user) + post_graphql(query, current_user: current_user) + end + + it_behaves_like 'a working graphql query' + + it 'returns packages successfully' do + expect(packages_data[0]['node']['name']).to eq package.name + end + end + + context 'when the user does not have access to the project/packages' do + before do + post_graphql(query, current_user: current_user) + end + + it_behaves_like 'a working graphql query' + + it 'returns nil' do + expect(graphql_data['project']).to be_nil + end + end + + context 'when the user is not autenthicated' do + before do + post_graphql(query) + end + + it_behaves_like 'a working graphql query' + + it 'returns nil' do + expect(graphql_data['project']).to be_nil + end + end + end +end diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb index bed9a18577f..57b9de25c3d 100644 --- a/spec/requests/api/graphql/project/pipeline_spec.rb +++ b/spec/requests/api/graphql/project/pipeline_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'getting pipeline information nested in a project' do +RSpec.describe 'getting pipeline information nested in a project' do include GraphqlHelpers let(:project) { create(:project, :repository, :public) } diff --git a/spec/requests/api/graphql/project/project_statistics_spec.rb b/spec/requests/api/graphql/project/project_statistics_spec.rb index 05dd5d36c26..c226b10ab51 100644 --- a/spec/requests/api/graphql/project/project_statistics_spec.rb +++ b/spec/requests/api/graphql/project/project_statistics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'rendering project statistics' do +RSpec.describe 'rendering project statistics' do include GraphqlHelpers let(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/release_spec.rb b/spec/requests/api/graphql/project/release_spec.rb index f8624a97a2b..f9c19d9747d 100644 --- a/spec/requests/api/graphql/project/release_spec.rb +++ b/spec/requests/api/graphql/project/release_spec.rb @@ -1,206 +1,374 @@ # frozen_string_literal: true require 'spec_helper' -require 'pp' -describe 'Query.project(fullPath).release(tagName)' do +RSpec.describe 'Query.project(fullPath).release(tagName)' do include GraphqlHelpers include Presentable - let_it_be(:project) { create(:project, :repository) } - let_it_be(:milestone_1) { create(:milestone, project: project) } - let_it_be(:milestone_2) { create(:milestone, project: project) } - let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2]) } - let_it_be(:release_link_1) { create(:release_link, release: release) } - let_it_be(:release_link_2) { create(:release_link, release: release) } let_it_be(:developer) { create(:user) } + let_it_be(:guest) { create(:user) } + let_it_be(:reporter) { create(:user) } + let_it_be(:stranger) { create(:user) } - let(:current_user) { developer } + let(:params_for_issues_and_mrs) { { scope: 'all', state: 'opened', release_tag: release.tag } } + let(:post_query) { post_graphql(query, current_user: current_user) } + let(:path_prefix) { %w[project release] } + let(:data) { graphql_data.dig(*path) } def query(rq = release_fields) graphql_query_for(:project, { fullPath: project.full_path }, query_graphql_field(:release, { tagName: release.tag }, rq)) end - let(:post_query) { post_graphql(query, current_user: current_user) } - let(:path_prefix) { %w[project release] } - - let(:data) { graphql_data.dig(*path) } - before do - project.add_developer(developer) + stub_default_url_options(host: 'www.example.com') end - describe 'scalar fields' do - let(:path) { path_prefix } - let(:release_fields) do - query_graphql_field(%{ - tagName - tagPath - description - descriptionHtml - name - createdAt - releasedAt - }) + shared_examples 'full access to the release field' do + describe 'scalar fields' do + let(:path) { path_prefix } + + let(:release_fields) do + query_graphql_field(%{ + tagName + tagPath + description + descriptionHtml + name + createdAt + releasedAt + }) + end + + before do + post_query + end + + it 'finds all release data' do + expect(data).to eq({ + 'tagName' => release.tag, + 'tagPath' => project_tag_path(project, release.tag), + 'description' => release.description, + 'descriptionHtml' => release.description_html, + 'name' => release.name, + 'createdAt' => release.created_at.iso8601, + 'releasedAt' => release.released_at.iso8601 + }) + end end - before do - post_query + describe 'milestones' do + let(:path) { path_prefix + %w[milestones nodes] } + + let(:release_fields) do + query_graphql_field(:milestones, nil, 'nodes { id title }') + end + + it 'finds all milestones associated to a release' do + post_query + + expected = release.milestones.map do |milestone| + { 'id' => global_id_of(milestone), 'title' => milestone.title } + end + + expect(data).to match_array(expected) + end end - it 'finds all release data' do - expect(data).to eq({ - 'tagName' => release.tag, - 'tagPath' => project_tag_path(project, release.tag), - 'description' => release.description, - 'descriptionHtml' => release.description_html, - 'name' => release.name, - 'createdAt' => release.created_at.iso8601, - 'releasedAt' => release.released_at.iso8601 - }) + describe 'author' do + let(:path) { path_prefix + %w[author] } + + let(:release_fields) do + query_graphql_field(:author, nil, 'id username') + end + + it 'finds the author of the release' do + post_query + + expect(data).to eq( + 'id' => global_id_of(release.author), + 'username' => release.author.username + ) + end end - end - describe 'milestones' do - let(:path) { path_prefix + %w[milestones nodes] } - let(:release_fields) do - query_graphql_field(:milestones, nil, 'nodes { id title }') + describe 'commit' do + let(:path) { path_prefix + %w[commit] } + + let(:release_fields) do + query_graphql_field(:commit, nil, 'sha') + end + + it 'finds the commit associated with the release' do + post_query + + expect(data).to eq('sha' => release.commit.sha) + end end - it 'finds all milestones associated to a release' do - post_query + describe 'assets' do + describe 'count' do + let(:path) { path_prefix + %w[assets] } + + let(:release_fields) do + query_graphql_field(:assets, nil, 'count') + end + + it 'returns the number of assets associated to the release' do + post_query + + expect(data).to eq('count' => release.sources.size + release.links.size) + end + end + + describe 'links' do + let(:path) { path_prefix + %w[assets links nodes] } - expected = release.milestones.map do |milestone| - { 'id' => global_id_of(milestone), 'title' => milestone.title } + let(:release_fields) do + query_graphql_field(:assets, nil, + query_graphql_field(:links, nil, 'nodes { id name url external }')) + end + + it 'finds all release links' do + post_query + + expected = release.links.map do |link| + { + 'id' => global_id_of(link), + 'name' => link.name, + 'url' => link.url, + 'external' => link.external? + } + end + + expect(data).to match_array(expected) + end end - expect(data).to match_array(expected) + describe 'sources' do + let(:path) { path_prefix + %w[assets sources nodes] } + + let(:release_fields) do + query_graphql_field(:assets, nil, + query_graphql_field(:sources, nil, 'nodes { format url }')) + end + + it 'finds all release sources' do + post_query + + expected = release.sources.map do |source| + { + 'format' => source.format, + 'url' => source.url + } + end + + expect(data).to match_array(expected) + end + end + end + + describe 'links' do + let(:path) { path_prefix + %w[links] } + + let(:release_fields) do + query_graphql_field(:links, nil, %{ + selfUrl + mergeRequestsUrl + issuesUrl + }) + end + + it 'finds all release links' do + post_query + + expect(data).to eq( + 'selfUrl' => project_release_url(project, release), + 'mergeRequestsUrl' => project_merge_requests_url(project, params_for_issues_and_mrs), + 'issuesUrl' => project_issues_url(project, params_for_issues_and_mrs) + ) + end + end + + describe 'evidences' do + let(:path) { path_prefix + %w[evidences] } + + let(:release_fields) do + query_graphql_field(:evidences, nil, 'nodes { id sha filepath collectedAt }') + end + + it 'finds all evidence fields' do + post_query + + evidence = release.evidences.first.present + + expect(data["nodes"].first).to eq( + 'id' => global_id_of(evidence), + 'sha' => evidence.sha, + 'filepath' => evidence.filepath, + 'collectedAt' => evidence.collected_at.utc.iso8601 + ) + end + end + end + + shared_examples 'no access to the release field' do + describe 'repository-related fields' do + let(:path) { path_prefix } + + let(:release_fields) do + query_graphql_field('description') + end + + before do + post_query + end + + it 'returns nil' do + expect(data).to eq(nil) + end end end - describe 'author' do - let(:path) { path_prefix + %w[author] } + shared_examples 'access to editUrl' do + let(:path) { path_prefix + %w[links] } + let(:release_fields) do - query_graphql_field(:author, nil, 'id username') + query_graphql_field(:links, nil, 'editUrl') end - it 'finds the author of the release' do + before do post_query + end - expect(data).to eq({ - 'id' => global_id_of(release.author), - 'username' => release.author.username - }) + it 'returns editUrl' do + expect(data).to eq('editUrl' => edit_project_release_url(project, release)) end end - describe 'commit' do - let(:path) { path_prefix + %w[commit] } + shared_examples 'no access to editUrl' do + let(:path) { path_prefix + %w[links] } + let(:release_fields) do - query_graphql_field(:commit, nil, 'sha') + query_graphql_field(:links, nil, 'editUrl') end - it 'finds the commit associated with the release' do + before do post_query + end - expect(data).to eq({ 'sha' => release.commit.sha }) + it 'does not return editUrl' do + expect(data).to eq('editUrl' => nil) end end - describe 'assets' do - describe 'assetsCount' do - let(:path) { path_prefix + %w[assets] } - let(:release_fields) do - query_graphql_field(:assets, nil, 'assetsCount') + describe "ensures that the correct data is returned based on the project's visibility and the user's access level" do + context 'when the project is private' do + let_it_be(:project) { create(:project, :repository, :private) } + let_it_be(:milestone_1) { create(:milestone, project: project) } + let_it_be(:milestone_2) { create(:milestone, project: project) } + let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2]) } + let_it_be(:release_link_1) { create(:release_link, release: release) } + let_it_be(:release_link_2) { create(:release_link, release: release) } + + before_all do + project.add_developer(developer) + project.add_guest(guest) + project.add_reporter(reporter) end - it 'returns the number of assets associated to the release' do - post_query + context 'when the user is not logged in' do + let(:current_user) { stranger } - expect(data).to eq({ 'assetsCount' => release.sources.size + release.links.size }) + it_behaves_like 'no access to the release field' end - end - describe 'links' do - let(:path) { path_prefix + %w[assets links nodes] } - let(:release_fields) do - query_graphql_field(:assets, nil, - query_graphql_field(:links, nil, 'nodes { id name url external }')) + context 'when the user has Guest permissions' do + let(:current_user) { guest } + + it_behaves_like 'no access to the release field' end - it 'finds all release links' do - post_query + context 'when the user has Reporter permissions' do + let(:current_user) { reporter } - expected = release.links.map do |link| - { - 'id' => global_id_of(link), - 'name' => link.name, - 'url' => link.url, - 'external' => link.external? - } - end + it_behaves_like 'full access to the release field' + it_behaves_like 'no access to editUrl' + end - expect(data).to match_array(expected) + context 'when the user has Developer permissions' do + let(:current_user) { developer } + + it_behaves_like 'full access to the release field' + it_behaves_like 'access to editUrl' end end - describe 'sources' do - let(:path) { path_prefix + %w[assets sources nodes] } - let(:release_fields) do - query_graphql_field(:assets, nil, - query_graphql_field(:sources, nil, 'nodes { format url }')) + context 'when the project is public' do + let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:milestone_1) { create(:milestone, project: project) } + let_it_be(:milestone_2) { create(:milestone, project: project) } + let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2]) } + let_it_be(:release_link_1) { create(:release_link, release: release) } + let_it_be(:release_link_2) { create(:release_link, release: release) } + + before_all do + project.add_developer(developer) + project.add_guest(guest) + project.add_reporter(reporter) end - it 'finds all release sources' do - post_query + context 'when the user is not logged in' do + let(:current_user) { stranger } - expected = release.sources.map do |source| - { - 'format' => source.format, - 'url' => source.url - } - end + it_behaves_like 'full access to the release field' + it_behaves_like 'no access to editUrl' + end - expect(data).to match_array(expected) + context 'when the user has Guest permissions' do + let(:current_user) { guest } + + it_behaves_like 'full access to the release field' + it_behaves_like 'no access to editUrl' end - end - describe 'evidences' do - let(:path) { path_prefix + %w[evidences] } - let(:release_fields) do - query_graphql_field(:evidences, nil, 'nodes { id sha filepath collectedAt }') + context 'when the user has Reporter permissions' do + let(:current_user) { reporter } + + it_behaves_like 'full access to the release field' + it_behaves_like 'no access to editUrl' end - context 'for a developer' do - it 'finds all evidence fields' do - post_query + context 'when the user has Reporter permissions' do + let(:current_user) { reporter } - evidence = release.evidences.first.present - expected = { - 'id' => global_id_of(evidence), - 'sha' => evidence.sha, - 'filepath' => evidence.filepath, - 'collectedAt' => evidence.collected_at.utc.iso8601 - } + it_behaves_like 'full access to the release field' + end - expect(data["nodes"].first).to eq(expected) - end + context 'when the user has Developer permissions' do + let(:current_user) { developer } + + it_behaves_like 'full access to the release field' + it_behaves_like 'access to editUrl' end + end + end - context 'for a guest' do - let(:current_user) { create :user } + describe 'ensures that the release data can be contolled by a feature flag' do + context 'when the graphql_release_data feature flag is disabled' do + let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:release) { create(:release, project: project) } - before do - project.add_guest(current_user) - end + let(:current_user) { developer } - it 'denies access' do - post_query + before do + stub_feature_flags(graphql_release_data: false) - expect(data['node']).to be_nil - end + project.add_developer(developer) end + + it_behaves_like 'no access to the release field' end end end diff --git a/spec/requests/api/graphql/project/releases_spec.rb b/spec/requests/api/graphql/project/releases_spec.rb new file mode 100644 index 00000000000..7e418bbaa5b --- /dev/null +++ b/spec/requests/api/graphql/project/releases_spec.rb @@ -0,0 +1,284 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Query.project(fullPath).releases()' do + include GraphqlHelpers + + let_it_be(:stranger) { create(:user) } + let_it_be(:guest) { create(:user) } + let_it_be(:reporter) { create(:user) } + let_it_be(:developer) { create(:user) } + + let(:query) do + graphql_query_for(:project, { fullPath: project.full_path }, + %{ + releases { + nodes { + tagName + tagPath + name + commit { + sha + } + assets { + count + sources { + nodes { + url + } + } + } + evidences { + nodes { + sha + } + } + links { + selfUrl + mergeRequestsUrl + issuesUrl + } + } + } + }) + end + + let(:params_for_issues_and_mrs) { { scope: 'all', state: 'opened', release_tag: release.tag } } + let(:post_query) { post_graphql(query, current_user: current_user) } + + let(:data) { graphql_data.dig('project', 'releases', 'nodes', 0) } + + before do + stub_default_url_options(host: 'www.example.com') + end + + shared_examples 'full access to all repository-related fields' do + describe 'repository-related fields' do + before do + post_query + end + + it 'returns data for fields that are protected in private projects' do + expected_sources = release.sources.map do |s| + { 'url' => s.url } + end + + expected_evidences = release.evidences.map do |e| + { 'sha' => e.sha } + end + + expect(data).to eq( + 'tagName' => release.tag, + 'tagPath' => project_tag_path(project, release.tag), + 'name' => release.name, + 'commit' => { + 'sha' => release.commit.sha + }, + 'assets' => { + 'count' => release.assets_count, + 'sources' => { + 'nodes' => expected_sources + } + }, + 'evidences' => { + 'nodes' => expected_evidences + }, + 'links' => { + 'selfUrl' => project_release_url(project, release), + 'mergeRequestsUrl' => project_merge_requests_url(project, params_for_issues_and_mrs), + 'issuesUrl' => project_issues_url(project, params_for_issues_and_mrs) + } + ) + end + end + end + + shared_examples 'no access to any repository-related fields' do + describe 'repository-related fields' do + before do + post_query + end + + it 'does not return data for fields that expose repository information' do + expect(data).to eq( + 'tagName' => nil, + 'tagPath' => nil, + 'name' => "Release-#{release.id}", + 'commit' => nil, + 'assets' => { + 'count' => release.assets_count(except: [:sources]), + 'sources' => { + 'nodes' => [] + } + }, + 'evidences' => { + 'nodes' => [] + }, + 'links' => nil + ) + end + end + end + + # editUrl is tested separately becuase its permissions + # are slightly different than other release fields + shared_examples 'access to editUrl' do + let(:query) do + graphql_query_for(:project, { fullPath: project.full_path }, + %{ + releases { + nodes { + links { + editUrl + } + } + } + }) + end + + before do + post_query + end + + it 'returns editUrl' do + expect(data).to eq( + 'links' => { + 'editUrl' => edit_project_release_url(project, release) + } + ) + end + end + + shared_examples 'no access to editUrl' do + let(:query) do + graphql_query_for(:project, { fullPath: project.full_path }, + %{ + releases { + nodes { + links { + editUrl + } + } + } + }) + end + + before do + post_query + end + + it 'does not return editUrl' do + expect(data).to eq( + 'links' => { + 'editUrl' => nil + } + ) + end + end + + shared_examples 'no access to any release data' do + before do + post_query + end + + it 'returns nil' do + expect(data).to eq(nil) + end + end + + describe "ensures that the correct data is returned based on the project's visibility and the user's access level" do + context 'when the project is private' do + let_it_be(:project) { create(:project, :repository, :private) } + let_it_be(:release) { create(:release, :with_evidence, project: project) } + + before_all do + project.add_guest(guest) + project.add_reporter(reporter) + project.add_developer(developer) + end + + context 'when the user is not logged in' do + let(:current_user) { stranger } + + it_behaves_like 'no access to any release data' + end + + context 'when the user has Guest permissions' do + let(:current_user) { guest } + + it_behaves_like 'no access to any repository-related fields' + end + + context 'when the user has Reporter permissions' do + let(:current_user) { reporter } + + it_behaves_like 'full access to all repository-related fields' + it_behaves_like 'no access to editUrl' + end + + context 'when the user has Developer permissions' do + let(:current_user) { developer } + + it_behaves_like 'full access to all repository-related fields' + it_behaves_like 'access to editUrl' + end + end + + context 'when the project is public' do + let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:release) { create(:release, :with_evidence, project: project) } + + before_all do + project.add_guest(guest) + project.add_reporter(reporter) + project.add_developer(developer) + end + + context 'when the user is not logged in' do + let(:current_user) { stranger } + + it_behaves_like 'full access to all repository-related fields' + it_behaves_like 'no access to editUrl' + end + + context 'when the user has Guest permissions' do + let(:current_user) { guest } + + it_behaves_like 'full access to all repository-related fields' + it_behaves_like 'no access to editUrl' + end + + context 'when the user has Reporter permissions' do + let(:current_user) { reporter } + + it_behaves_like 'full access to all repository-related fields' + it_behaves_like 'no access to editUrl' + end + + context 'when the user has Developer permissions' do + let(:current_user) { developer } + + it_behaves_like 'full access to all repository-related fields' + it_behaves_like 'access to editUrl' + end + end + end + + describe 'ensures that the release data can be contolled by a feature flag' do + context 'when the graphql_release_data feature flag is disabled' do + let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:release) { create(:release, project: project) } + + let(:current_user) { developer } + + before do + stub_feature_flags(graphql_release_data: false) + + project.add_developer(developer) + end + + it_behaves_like 'no access to any release data' + end + end +end diff --git a/spec/requests/api/graphql/project/repository_spec.rb b/spec/requests/api/graphql/project/repository_spec.rb index 261433a3d6a..bd719a69647 100644 --- a/spec/requests/api/graphql/project/repository_spec.rb +++ b/spec/requests/api/graphql/project/repository_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe 'getting a repository in a project' do +RSpec.describe 'getting a repository in a project' do include GraphqlHelpers let(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/project/tree/tree_spec.rb b/spec/requests/api/graphql/project/tree/tree_spec.rb index 94128cc21ee..bce63d57c38 100644 --- a/spec/requests/api/graphql/project/tree/tree_spec.rb +++ b/spec/requests/api/graphql/project/tree/tree_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe 'getting a tree in a project' do +RSpec.describe 'getting a tree in a project' do include GraphqlHelpers let(:project) { create(:project, :repository) } |