diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 11:18:50 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 11:18:50 +0000 |
commit | 8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch) | |
tree | a77e7fe7a93de11213032ed4ab1f33a3db51b738 /spec/requests/api/graphql/project | |
parent | 00b35af3db1abfe813a778f643dad221aad51fca (diff) | |
download | gitlab-ce-8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781.tar.gz |
Add latest changes from gitlab-org/gitlab@13-1-stable-ee
Diffstat (limited to 'spec/requests/api/graphql/project')
13 files changed, 796 insertions, 5 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 new file mode 100644 index 00000000000..4c048caaeee --- /dev/null +++ b/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'getting Alert Management Alert Assignees' 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(:params) { {} } + + let(:fields) do + <<~QUERY + nodes { + iid + assignees { + nodes { + username + } + } + } + QUERY + end + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('alertManagementAlerts', params, fields) + ) + end + + let(:alerts) { graphql_data.dig('project', 'alertManagementAlerts', 'nodes') } + let(:assignees) { alerts.map { |alert| [alert['iid'], alert['assignees']['nodes']] }.to_h } + let(:first_assignees) { assignees[first_alert.iid.to_s] } + let(:second_assignees) { assignees[second_alert.iid.to_s] } + + before do + project.add_developer(current_user) + end + + it 'returns the correct assignees' do + post_graphql(query, current_user: current_user) + + expect(first_assignees.length).to eq(1) + expect(first_assignees.first).to include('username' => current_user.username) + expect(second_assignees).to be_empty + end + + it 'applies appropriate filters for non-visible users' do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?).with(current_user, :read_user, current_user).and_return(false) + + post_graphql(query, current_user: current_user) + + expect(first_assignees).to be_empty + expect(second_assignees).to be_empty + end + + it 'avoids N+1 queries' do + base_count = ActiveRecord::QueryRecorder.new do + post_graphql(query, current_user: current_user) + end + + # An N+1 would mean a new alert would increase the query count + third_alert = create(:alert_management_alert, project: project, assignees: [current_user]) + + expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(base_count) + + third_assignees = assignees[third_alert.iid.to_s] + + expect(third_assignees.length).to eq(1) + expect(third_assignees.first).to include('username' => current_user.username) + 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 new file mode 100644 index 00000000000..df6bfa8c97b --- /dev/null +++ b/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require 'spec_helper' + +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(:params) { {} } + + let(:fields) do + <<~QUERY + nodes { + iid + notes { + nodes { + id + } + } + } + QUERY + end + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('alertManagementAlerts', params, fields) + ) + end + + let(:alerts_result) { graphql_data.dig('project', 'alertManagementAlerts', 'nodes') } + let(:notes_result) { alerts_result.map { |alert| [alert['iid'], alert['notes']['nodes']] }.to_h } + let(:first_notes_result) { notes_result[first_alert.iid.to_s] } + let(:second_notes_result) { notes_result[second_alert.iid.to_s] } + + before do + project.add_developer(current_user) + end + + it 'returns the notes ordered by createdAt' do + post_graphql(query, current_user: current_user) + + expect(first_notes_result.length).to eq(2) + expect(first_notes_result.first).to include('id' => first_system_note.to_global_id.to_s) + expect(first_notes_result.second).to include('id' => second_system_note.to_global_id.to_s) + expect(second_notes_result).to be_empty + end + + it 'avoids N+1 queries' do + base_count = ActiveRecord::QueryRecorder.new do + post_graphql(query, current_user: current_user) + end + + # An N+1 would mean a new alert would increase the query count + create(:alert_management_alert, project: project) + + expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(base_count) + expect(alerts_result.length).to eq(3) + 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 ffd328429ef..a0d1ff7efc5 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 @@ -56,6 +56,22 @@ describe 'getting Alert Management Alert counts by status' do 'ignored' => 0 ) end + + context 'with search criteria' do + let(:params) { { search: alert_1.title } } + + it_behaves_like 'a working graphql query' + it 'returns the correct counts for each status' do + expect(alert_counts).to eq( + 'open' => 0, + 'all' => 1, + 'triggered' => 0, + 'acknowledged' => 0, + 'resolved' => 1, + 'ignored' => 0 + ) + end + end end end end 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 c226e659364..c591895f295 100644 --- a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb @@ -10,12 +10,13 @@ describe 'getting Alert Management Alerts' do let_it_be(:resolved_alert) { create(:alert_management_alert, :all_fields, :resolved, project: project, issue: nil, severity: :low) } let_it_be(:triggered_alert) { create(:alert_management_alert, :all_fields, project: project, severity: :critical, payload: payload) } let_it_be(:other_project_alert) { create(:alert_management_alert, :all_fields) } + let(:params) { {} } let(:fields) do <<~QUERY nodes { - #{all_graphql_fields_for('AlertManagementAlert'.classify)} + #{all_graphql_fields_for('AlertManagementAlert', excluded: ['assignees'])} } QUERY end diff --git a/spec/requests/api/graphql/project/container_expiration_policy_spec.rb b/spec/requests/api/graphql/project/container_expiration_policy_spec.rb new file mode 100644 index 00000000000..d0563f9ff05 --- /dev/null +++ b/spec/requests/api/graphql/project/container_expiration_policy_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe 'getting a repository in a project' do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:current_user) { project.owner } + let_it_be(:container_expiration_policy) { project.container_expiration_policy } + + let(:fields) do + <<~QUERY + #{all_graphql_fields_for('container_expiration_policy'.classify)} + QUERY + end + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('containerExpirationPolicy', {}, fields) + ) + end + + before do + stub_config(registry: { enabled: true }) + post_graphql(query, current_user: current_user) + end + + it_behaves_like 'a working graphql query' +end diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb index 91fce3eed92..3128f527356 100644 --- a/spec/requests/api/graphql/project/issues_spec.rb +++ b/spec/requests/api/graphql/project/issues_spec.rb @@ -124,7 +124,7 @@ describe 'getting an issue list for a project' do graphql_query_for( 'project', { 'fullPath' => sort_project.full_path }, - "issues(#{params}) { #{page_info} edges { node { iid dueDate } } }" + query_graphql_field('issues', params, "#{page_info} edges { node { iid dueDate} }") ) end diff --git a/spec/requests/api/graphql/project/jira_import_spec.rb b/spec/requests/api/graphql/project/jira_import_spec.rb index e063068eb1a..7be14696963 100644 --- a/spec/requests/api/graphql/project/jira_import_spec.rb +++ b/spec/requests/api/graphql/project/jira_import_spec.rb @@ -7,9 +7,30 @@ describe 'query Jira import data' do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project, :private, :import_started, import_type: 'jira') } - let_it_be(:jira_import1) { create(:jira_import_state, :finished, project: project, jira_project_key: 'AA', user: current_user, created_at: 2.days.ago) } - let_it_be(:jira_import2) { create(:jira_import_state, :finished, project: project, jira_project_key: 'BB', user: current_user, created_at: 5.days.ago) } - + let_it_be(:jira_import1) do + create( + :jira_import_state, :finished, + project: project, + jira_project_key: 'AA', + user: current_user, + created_at: 2.days.ago, + failed_to_import_count: 2, + imported_issues_count: 2, + total_issue_count: 4 + ) + end + let_it_be(:jira_import2) do + create( + :jira_import_state, :finished, + project: project, + jira_project_key: 'BB', + user: current_user, + created_at: 5.days.ago, + failed_to_import_count: 1, + imported_issues_count: 2, + total_issue_count: 3 + ) + end let(:query) do %( query { @@ -23,6 +44,9 @@ describe 'query Jira import data' do scheduledBy { username } + importedIssuesCount + failedToImportCount + totalIssueCount } } } @@ -64,10 +88,16 @@ describe 'query Jira import data' do it 'retuns list of jira imports' do jira_proket_keys = jira_imports.map {|ji| ji['jiraProjectKey']} usernames = jira_imports.map {|ji| ji.dig('scheduledBy', 'username')} + imported_issues_count = jira_imports.map {|ji| ji.dig('importedIssuesCount')} + failed_issues_count = jira_imports.map {|ji| ji.dig('failedToImportCount')} + total_issue_count = jira_imports.map {|ji| ji.dig('totalIssueCount')} expect(jira_imports.size).to eq 2 expect(jira_proket_keys).to eq %w(BB AA) expect(usernames).to eq [current_user.username, current_user.username] + expect(imported_issues_count).to eq [2, 2] + expect(failed_issues_count).to eq [1, 2] + expect(total_issue_count).to eq [3, 4] end end diff --git a/spec/requests/api/graphql/project/jira_projects_spec.rb b/spec/requests/api/graphql/project/jira_projects_spec.rb new file mode 100644 index 00000000000..d67c89f18c9 --- /dev/null +++ b/spec/requests/api/graphql/project/jira_projects_spec.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'query Jira projects' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:project) { create(:project) } + + include_context 'jira projects request context' + + let(:services) { graphql_data_at(:project, :services, :edges) } + let(:jira_projects) { services.first.dig('node', 'projects', 'nodes') } + let(:projects_query) { 'projects' } + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + services(active: true, type: JIRA_SERVICE) { + edges { + node { + ... on JiraService { + %{projects_query} { + nodes { + key + name + projectId + } + } + } + } + } + } + } + } + ) % { projects_query: projects_query } + end + + context 'when user does not have access' do + it_behaves_like 'unauthorized users cannot read services' + end + + context 'when user can access project services' do + before do + project.add_maintainer(current_user) + post_graphql(query, current_user: current_user) + end + + it_behaves_like 'a working graphql query' + + it 'retuns list of jira projects' do + project_keys = jira_projects.map { |jp| jp['key'] } + project_names = jira_projects.map { |jp| jp['name'] } + project_ids = jira_projects.map { |jp| jp['projectId'] } + + expect(jira_projects.size).to eq(2) + expect(project_keys).to eq(%w(EX ABC)) + expect(project_names).to eq(%w(Example Alphabetical)) + expect(project_ids).to eq([10000, 10001]) + end + + context 'with pagination' do + context 'when fetching limited number of projects' do + shared_examples_for 'fetches first project' do + it 'retuns first project from list of fetched projects' do + project_keys = jira_projects.map { |jp| jp['key'] } + project_names = jira_projects.map { |jp| jp['name'] } + project_ids = jira_projects.map { |jp| jp['projectId'] } + + expect(jira_projects.size).to eq(1) + expect(project_keys).to eq(%w(EX)) + expect(project_names).to eq(%w(Example)) + expect(project_ids).to eq([10000]) + end + end + + context 'without cursor' do + let(:projects_query) { 'projects(first: 1)' } + + 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 +end diff --git a/spec/requests/api/graphql/project/labels_query_spec.rb b/spec/requests/api/graphql/project/labels_query_spec.rb new file mode 100644 index 00000000000..ecc43e0a3db --- /dev/null +++ b/spec/requests/api/graphql/project/labels_query_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'getting project label information' do + include GraphqlHelpers + + let_it_be(:project) { create(:project, :public) } + let_it_be(:label_factory) { :label } + let_it_be(:label_attrs) { { project: project } } + + it_behaves_like 'querying a GraphQL type with labels' do + let(:path_prefix) { ['project'] } + + def make_query(fields) + graphql_query_for('project', { full_path: project.full_path }, fields) + 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 8d8c31c335d..643532bf2e2 100644 --- a/spec/requests/api/graphql/project/merge_request_spec.rb +++ b/spec/requests/api/graphql/project/merge_request_spec.rb @@ -37,6 +37,30 @@ describe 'getting merge request information nested in a project' do expect(merge_request_graphql_data['webUrl']).to be_present end + it 'includes author' do + post_graphql(query, current_user: current_user) + + expect(merge_request_graphql_data['author']['username']).to eq(merge_request.author.username) + end + + it 'includes correct mergedAt value when merged' do + time = 1.week.ago + merge_request.mark_as_merged + merge_request.metrics.update_columns(merged_at: time) + + post_graphql(query, current_user: current_user) + retrieved = merge_request_graphql_data['mergedAt'] + + expect(Time.zone.parse(retrieved)).to be_within(1.second).of(time) + end + + it 'includes nil mergedAt value when not merged' do + post_graphql(query, current_user: current_user) + retrieved = merge_request_graphql_data['mergedAt'] + + expect(retrieved).to be_nil + end + context 'permissions on the merge request' do it 'includes the permissions for the current user on a public project' do expected_permissions = { diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb new file mode 100644 index 00000000000..49fdfe29874 --- /dev/null +++ b/spec/requests/api/graphql/project/merge_requests_spec.rb @@ -0,0 +1,174 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'getting merge request listings nested in a project' do + include GraphqlHelpers + + let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:current_user) { create(:user) } + + let_it_be(:label) { create(:label) } + let_it_be(:merge_request_a) { create(:labeled_merge_request, :unique_branches, source_project: project, labels: [label]) } + let_it_be(:merge_request_b) { create(:merge_request, :closed, :unique_branches, source_project: project) } + let_it_be(:merge_request_c) { create(:labeled_merge_request, :closed, :unique_branches, source_project: project, labels: [label]) } + let_it_be(:merge_request_d) { create(:merge_request, :locked, :unique_branches, source_project: project) } + + let(:results) { graphql_data.dig('project', 'mergeRequests', 'nodes') } + + let(:search_params) { nil } + + def query_merge_requests(fields) + graphql_query_for( + :project, + { full_path: project.full_path }, + query_graphql_field(:merge_requests, search_params, [ + query_graphql_field(:nodes, nil, fields) + ]) + ) + end + + let(:query) do + query_merge_requests(all_graphql_fields_for('MergeRequest', max_depth: 1)) + end + + it_behaves_like 'a working graphql query' do + before do + post_graphql(query, current_user: current_user) + end + end + + # The following tests are needed to guarantee that we have correctly annotated + # all the gitaly calls. Selecting combinations of fields may mask this due to + # memoization. + context 'requesting a single field' do + let_it_be(:fresh_mr) { create(:merge_request, :unique_branches, source_project: project) } + let(:search_params) { { iids: [fresh_mr.iid.to_s] } } + + before do + project.repository.expire_branches_cache + end + + context 'selecting any single scalar field' do + where(:field) do + scalar_fields_of('MergeRequest').map { |name| [name] } + end + + with_them do + it_behaves_like 'a working graphql query' do + let(:query) do + query_merge_requests([:iid, field].uniq) + end + + before do + post_graphql(query, current_user: current_user) + end + + it 'selects the correct MR' do + expect(results).to contain_exactly(a_hash_including('iid' => fresh_mr.iid.to_s)) + end + end + end + end + + context 'selecting any single nested field' do + where(:field, :subfield, :is_connection) do + nested_fields_of('MergeRequest').flat_map do |name, field| + type = field_type(field) + is_connection = type.name.ends_with?('Connection') + type = field_type(type.fields['nodes']) if is_connection + + type.fields + .select { |_, field| !nested_fields?(field) && !required_arguments?(field) } + .map(&:first) + .map { |subfield| [name, subfield, is_connection] } + end + end + + with_them do + it_behaves_like 'a working graphql query' do + let(:query) do + fld = is_connection ? query_graphql_field(:nodes, nil, [subfield]) : subfield + query_merge_requests([:iid, query_graphql_field(field, nil, [fld])]) + end + + before do + post_graphql(query, current_user: current_user) + end + + it 'selects the correct MR' do + expect(results).to contain_exactly(a_hash_including('iid' => fresh_mr.iid.to_s)) + end + end + end + end + end + + shared_examples 'searching with parameters' do + let(:expected) do + mrs.map { |mr| a_hash_including('iid' => mr.iid.to_s, 'title' => mr.title) } + end + + it 'finds the right mrs' do + post_graphql(query, current_user: current_user) + + expect(results).to match_array(expected) + end + end + + context 'there are no search params' do + let(:search_params) { nil } + let(:mrs) { [merge_request_a, merge_request_b, merge_request_c, merge_request_d] } + + it_behaves_like 'searching with parameters' + end + + context 'the search params do not match anything' do + let(:search_params) { { iids: %w(foo bar baz) } } + let(:mrs) { [] } + + it_behaves_like 'searching with parameters' + end + + context 'searching by iids' do + let(:search_params) { { iids: mrs.map(&:iid).map(&:to_s) } } + let(:mrs) { [merge_request_a, merge_request_c] } + + it_behaves_like 'searching with parameters' + end + + context 'searching by state' do + let(:search_params) { { state: :closed } } + let(:mrs) { [merge_request_b, merge_request_c] } + + it_behaves_like 'searching with parameters' + end + + context 'searching by source_branch' do + let(:search_params) { { source_branches: mrs.map(&:source_branch) } } + let(:mrs) { [merge_request_b, merge_request_c] } + + it_behaves_like 'searching with parameters' + end + + context 'searching by target_branch' do + let(:search_params) { { target_branches: mrs.map(&:target_branch) } } + let(:mrs) { [merge_request_a, merge_request_d] } + + it_behaves_like 'searching with parameters' + end + + context 'searching by label' do + let(:search_params) { { labels: [label.title] } } + let(:mrs) { [merge_request_a, merge_request_c] } + + it_behaves_like 'searching with parameters' + end + + context 'searching by combination' do + let(:search_params) { { state: :closed, labels: [label.title] } } + let(:mrs) { [merge_request_c] } + + it_behaves_like 'searching with parameters' + end +end diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb new file mode 100644 index 00000000000..bed9a18577f --- /dev/null +++ b/spec/requests/api/graphql/project/pipeline_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'getting pipeline information nested in a project' do + include GraphqlHelpers + + let(:project) { create(:project, :repository, :public) } + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:current_user) { create(:user) } + let(:pipeline_graphql_data) { graphql_data['project']['pipeline'] } + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('pipeline', iid: pipeline.iid.to_s) + ) + end + + it_behaves_like 'a working graphql query' do + before do + post_graphql(query, current_user: current_user) + end + end + + it 'contains pipeline information' do + post_graphql(query, current_user: current_user) + + expect(pipeline_graphql_data).not_to be_nil + end +end diff --git a/spec/requests/api/graphql/project/release_spec.rb b/spec/requests/api/graphql/project/release_spec.rb new file mode 100644 index 00000000000..f8624a97a2b --- /dev/null +++ b/spec/requests/api/graphql/project/release_spec.rb @@ -0,0 +1,206 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'pp' + +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(:current_user) { developer } + + 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) + end + + 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 + + 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 + + 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 + + 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 + + describe 'assets' do + describe 'assetsCount' do + let(:path) { path_prefix + %w[assets] } + let(:release_fields) do + query_graphql_field(:assets, nil, 'assetsCount') + end + + it 'returns the number of assets associated to the release' do + post_query + + expect(data).to eq({ 'assetsCount' => release.sources.size + release.links.size }) + 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 }')) + 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 + + 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 + + describe 'evidences' do + let(:path) { path_prefix + %w[evidences] } + let(:release_fields) do + query_graphql_field(:evidences, nil, 'nodes { id sha filepath collectedAt }') + end + + context 'for a developer' do + it 'finds all evidence fields' do + post_query + + evidence = release.evidences.first.present + expected = { + 'id' => global_id_of(evidence), + 'sha' => evidence.sha, + 'filepath' => evidence.filepath, + 'collectedAt' => evidence.collected_at.utc.iso8601 + } + + expect(data["nodes"].first).to eq(expected) + end + end + + context 'for a guest' do + let(:current_user) { create :user } + + before do + project.add_guest(current_user) + end + + it 'denies access' do + post_query + + expect(data['node']).to be_nil + end + end + end + end +end |