diff options
Diffstat (limited to 'spec/support/shared_examples/requests')
12 files changed, 378 insertions, 43 deletions
diff --git a/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb b/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb index 59e641e2af6..2170025824f 100644 --- a/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb +++ b/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb @@ -1,21 +1,98 @@ # frozen_string_literal: true RSpec.shared_examples 'GET resource access tokens available' do - let_it_be(:active_resource_access_token) { create(:personal_access_token, user: bot_user) } + let_it_be(:active_resource_access_token) { create(:personal_access_token, user: access_token_user) } - it 'retrieves active resource access tokens' do - subject + it 'retrieves active access tokens' do + get_access_tokens - token_entities = assigns(:active_resource_access_tokens) + token_entities = assigns(:active_access_tokens) expect(token_entities.length).to eq(1) expect(token_entities[0][:name]).to eq(active_resource_access_token.name) end it 'lists all available scopes' do - subject + get_access_tokens expect(assigns(:scopes)).to eq(Gitlab::Auth.resource_bot_scopes) end + + it 'returns for json response' do + get_access_tokens_json + + expect(json_response.count).to eq(1) + end +end + +RSpec.shared_examples 'GET access tokens are paginated and ordered' do + before do + create(:personal_access_token, user: access_token_user) + end + + context "when multiple access tokens are returned" do + before do + allow(Kaminari.config).to receive(:default_per_page).and_return(1) + create(:personal_access_token, user: access_token_user) + end + + it "returns paginated response", :aggregate_failures do + get_access_tokens_with_page + expect(assigns(:active_access_tokens).count).to eq(1) + + expect_header('X-Per-Page', '1') + expect_header('X-Page', '1') + expect_header('X-Next-Page', '2') + expect_header('X-Total', '2') + end + end + + context "when access_token_pagination feature flag is disabled" do + before do + stub_feature_flags(access_token_pagination: false) + create(:personal_access_token, user: access_token_user) + end + + it "returns all tokens in system" do + get_access_tokens_with_page + expect(assigns(:active_access_tokens).count).to eq(2) + end + end + + context "when tokens returned are ordered" do + let(:expires_1_day_from_now) { 1.day.from_now.to_date } + let(:expires_2_day_from_now) { 2.days.from_now.to_date } + + before do + create(:personal_access_token, user: access_token_user, name: "Token1", expires_at: expires_1_day_from_now) + create(:personal_access_token, user: access_token_user, name: "Token2", expires_at: expires_2_day_from_now) + end + + it "orders token list ascending on expires_at" do + get_access_tokens + + first_token = assigns(:active_access_tokens).first.as_json + expect(first_token['name']).to eq("Token1") + expect(first_token['expires_at']).to eq(expires_1_day_from_now.strftime("%Y-%m-%d")) + end + + it "orders tokens on id in case token has same expires_at" do + create(:personal_access_token, user: access_token_user, name: "Token3", expires_at: expires_1_day_from_now) + + get_access_tokens + + first_token = assigns(:active_access_tokens).first.as_json + expect(first_token['name']).to eq("Token3") + expect(first_token['expires_at']).to eq(expires_1_day_from_now.strftime("%Y-%m-%d")) + + second_token = assigns(:active_access_tokens).second.as_json + expect(second_token['name']).to eq("Token1") + expect(second_token['expires_at']).to eq(expires_1_day_from_now.strftime("%Y-%m-%d")) + end + end + + def expect_header(header_name, header_val) + expect(response.headers[header_name]).to eq(header_val) + end end RSpec.shared_examples 'POST resource access tokens available' do @@ -83,7 +160,7 @@ end RSpec.shared_examples 'PUT resource access tokens available' do it 'calls delete user worker' do - expect(DeleteUserWorker).to receive(:perform_async).with(user.id, bot_user.id, skip_authorization: true) + expect(DeleteUserWorker).to receive(:perform_async).with(user.id, access_token_user.id, skip_authorization: true) subject end @@ -91,34 +168,12 @@ RSpec.shared_examples 'PUT resource access tokens available' do it 'removes membership of bot user' do subject - expect(resource.reload.bots).not_to include(bot_user) + expect(resource.reload.bots).not_to include(access_token_user) end - context 'when user_destroy_with_limited_execution_time_worker is enabled' do - it 'creates GhostUserMigration records to handle migration in a worker' do - expect { subject }.to( - change { Users::GhostUserMigration.count }.from(0).to(1)) - end - end - - context 'when user_destroy_with_limited_execution_time_worker is disabled' do - before do - stub_feature_flags(user_destroy_with_limited_execution_time_worker: false) - end - - it 'converts issuables of the bot user to ghost user' do - issue = create(:issue, author: bot_user) - - subject - - expect(issue.reload.author.ghost?).to be true - end - - it 'deletes project bot user' do - subject - - expect(User.exists?(bot_user.id)).to be_falsy - end + it 'creates GhostUserMigration records to handle migration in a worker' do + expect { subject }.to( + change { Users::GhostUserMigration.count }.from(0).to(1)) end context 'when unsuccessful' do diff --git a/spec/support/shared_examples/requests/api/discussions_shared_examples.rb b/spec/support/shared_examples/requests/api/discussions_shared_examples.rb index 32562aef8d2..f577e2ad323 100644 --- a/spec/support/shared_examples/requests/api/discussions_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/discussions_shared_examples.rb @@ -15,7 +15,7 @@ RSpec.shared_examples 'with cross-reference system notes' do new_merge_request.project.add_developer(user) hidden_merge_request = create(:merge_request) - new_cross_reference = "test commit #{hidden_merge_request.project.commit}" + new_cross_reference = "test commit #{hidden_merge_request.project.commit.to_reference(project)}" new_note = create(:system_note, noteable: merge_request, project: project, note: new_cross_reference) create(:system_note_metadata, note: new_note, action: 'cross_reference') end diff --git a/spec/support/shared_examples/requests/api/graphql/issuable_search_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/issuable_search_shared_examples.rb index 22805cf7aed..bb492425fd7 100644 --- a/spec/support/shared_examples/requests/api/graphql/issuable_search_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/issuable_search_shared_examples.rb @@ -1,13 +1,15 @@ # frozen_string_literal: true # Requires `query(params)` , `user`, `issuable_data` and `issuable` bindings -RSpec.shared_examples 'query with a search term' do +RSpec.shared_examples 'query with a search term' do |fields = [:DESCRIPTION]| + let(:search_term) { 'bar' } + let(:ids) { graphql_dig_at(issuable_data, :node, :id) } + it 'returns only matching issuables' do - filter_params = { search: 'bar', in: [:DESCRIPTION] } + filter_params = { search: search_term, in: fields } graphql_query = query(filter_params) post_graphql(graphql_query, current_user: user) - ids = graphql_dig_at(issuable_data, :node, :id) expect(ids).to contain_exactly(issuable.to_global_id.to_s) end diff --git a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb new file mode 100644 index 00000000000..5469fd80a4f --- /dev/null +++ b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb @@ -0,0 +1,170 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'graphql issue list request spec' do + it_behaves_like 'a working graphql query' do + before do + post_query + end + end + + describe 'filters' do + context 'when filtering by assignees' do + context 'when both assignee_username filters are provided' do + let(:issue_filter_params) do + { assignee_username: current_user.username, assignee_usernames: [current_user.username] } + end + + it 'returns a mutually exclusive param error' do + post_query + + expect_graphql_errors_to_include( + 'only one of [assigneeUsernames, assigneeUsername] arguments is allowed at the same time.' + ) + end + end + + context 'when filtering by a negated argument' do + let(:issue_filter_params) { { not: { assignee_usernames: [current_user.username] } } } + + it 'returns correctly filtered issues' do + post_query + + expect(issue_ids).to match_array(expected_negated_assignee_issues.map { |i| i.to_gid.to_s }) + end + end + end + + context 'when filtering by unioned arguments' do + let(:issue_filter_params) { { or: { assignee_usernames: [current_user.username, another_user.username] } } } + + it 'returns correctly filtered issues' do + post_query + + expect(issue_ids).to match_array(expected_unioned_assignee_issues.map { |i| i.to_gid.to_s }) + end + + context 'when argument is blank' do + let(:issue_filter_params) { { or: {} } } + + it 'does not raise an error' do + post_query + + expect_graphql_errors_to_be_empty + end + end + + context 'when feature flag is disabled' do + it 'returns an error' do + stub_feature_flags(or_issuable_queries: false) + + post_query + + expect_graphql_errors_to_include( + "'or' arguments are only allowed when the `or_issuable_queries` feature flag is enabled." + ) + end + end + end + + context 'when filtering by a blank negated argument' do + let(:issue_filter_params) { { not: {} } } + + it 'does not raise an error' do + post_query + + expect_graphql_errors_to_be_empty + end + end + + context 'when filtering by reaction emoji' do + using RSpec::Parameterized::TableSyntax + + where(:value, :issue_list) do + 'thumbsup' | lazy { voted_issues } + 'ANY' | lazy { voted_issues } + 'any' | lazy { voted_issues } + 'AnY' | lazy { voted_issues } + 'NONE' | lazy { no_award_issues } + 'thumbsdown' | lazy { [] } + end + + with_them do + let(:issue_filter_params) { { my_reaction_emoji: value } } + let(:gids) { to_gid_list(issue_list) } + + it 'returns correctly filtered issues' do + post_query + + expect(issue_ids).to match_array(gids) + end + end + end + + context 'when filtering by search' do + it_behaves_like 'query with a search term', [:TITLE] do + let(:search_term) { search_title_term } + let(:issuable_data) { issues_data } + let(:user) { current_user } + let(:issuable) { title_search_issue } + let(:ids) { issue_ids } + end + end + end + + describe 'sorting and pagination' do + context 'when sorting by severity' do + context 'when ascending' do + it_behaves_like 'sorted paginated query' do + let(:sort_param) { :SEVERITY_ASC } + let(:first_param) { 2 } + let(:all_records) { to_gid_list(expected_severity_sorted_asc) } + end + end + + context 'when descending' do + it_behaves_like 'sorted paginated query' do + let(:sort_param) { :SEVERITY_DESC } + let(:first_param) { 2 } + let(:all_records) { to_gid_list(expected_severity_sorted_asc.reverse) } + end + end + end + + context 'when sorting by priority' do + context 'when ascending' do + it_behaves_like 'sorted paginated query' do + let(:sort_param) { :PRIORITY_ASC } + let(:first_param) { 2 } + let(:all_records) { to_gid_list(expected_priority_sorted_asc) } + end + end + + context 'when descending' do + it_behaves_like 'sorted paginated query' do + let(:sort_param) { :PRIORITY_DESC } + let(:first_param) { 2 } + let(:all_records) { to_gid_list(expected_priority_sorted_desc) } + end + end + end + end + + it 'includes a web_url' do + post_query + + expect(issues_data[0]['webUrl']).to be_present + end + + it 'includes discussion locked' do + post_query + + expect(issues_data).to contain_exactly( + *locked_discussion_issues.map { |i| hash_including('id' => i.to_gid.to_s, 'discussionLocked' => true) }, + *unlocked_discussion_issues.map { |i| hash_including('id' => i.to_gid.to_s, 'discussionLocked' => false) } + ) + end + + def to_gid_list(instance_list) + instance_list.map { |instance| instance.to_gid.to_s } + end +end diff --git a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb index 1b609915f32..fb4aacfd7a9 100644 --- a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb @@ -114,7 +114,7 @@ RSpec.shared_examples 'group and project packages query' do end [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC, :CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order| - context "#{order}" do + context order.to_s do let(:sorted_packages) { packages_order_map.fetch(order) } it_behaves_like 'sorted paginated query' do diff --git a/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb b/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb new file mode 100644 index 00000000000..54cc13fac94 --- /dev/null +++ b/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'perform graphql requests for AccessLevel type objects' do |access_level_kind| + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:current_user) { create(:user, maintainer_projects: [project]) } + let_it_be(:variables) { { path: project.full_path } } + + let(:fields) { all_graphql_fields_for("#{access_level_kind.to_s.classify}AccessLevel", max_depth: 2) } + let(:access_levels) { protected_branch.public_send("#{access_level_kind}_access_levels") } + let(:access_levels_count) { access_levels.size } + let(:maintainer_access_level) { access_levels.for_role.first } + let(:maintainer_access_level_data) { access_levels_data.first } + let(:access_levels_data) do + graphql_data_at('project', + 'branchRules', + 'nodes', + 0, + 'branchProtection', + "#{access_level_kind.to_s.camelize(:lower)}AccessLevels", + 'nodes') + end + + let(:query) do + <<~GQL + query($path: ID!) { + project(fullPath: $path) { + branchRules(first: 1) { + nodes { + branchProtection { + #{access_level_kind.to_s.camelize(:lower)}AccessLevels { + nodes { + #{fields} + } + } + } + } + } + } + } + GQL + end + + context 'when request AccessLevel type objects as a guest user' do + let_it_be(:protected_branch) { create(:protected_branch, project: project) } + + 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(access_levels_data).not_to be_present } + end + + context 'when request AccessLevel type objects as a maintainer' do + let_it_be(:protected_branch) do + create(:protected_branch, "maintainers_can_#{access_level_kind}", project: project) + end + + before do + post_graphql(query, current_user: current_user, variables: variables) + end + + it_behaves_like 'a working graphql query' + + it 'returns all the access level attributes' do + expect(maintainer_access_level_data['accessLevel']).to eq(maintainer_access_level.access_level) + expect(maintainer_access_level_data['accessLevelDescription']).to eq(maintainer_access_level.humanize) + expect(maintainer_access_level_data.dig('group', 'name')).to be_nil + expect(maintainer_access_level_data.dig('user', 'name')).to be_nil + end + end +end diff --git a/spec/support/shared_examples/requests/api/issues_shared_examples.rb b/spec/support/shared_examples/requests/api/issues_shared_examples.rb index 991dbced02d..6328fb9cd8a 100644 --- a/spec/support/shared_examples/requests/api/issues_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/issues_shared_examples.rb @@ -37,7 +37,7 @@ RSpec.shared_examples 'labeled issues with labels and label_name params' do context 'negation' do context 'array of labeled issues when all labels match with negation' do - let(:params) { { labels: "#{label.title},#{label_b.title}", not: { labels: "#{label_c.title}" } } } + let(:params) { { labels: "#{label.title},#{label_b.title}", not: { labels: label_c.title.to_s } } } it_behaves_like 'returns negated label names' end diff --git a/spec/support/shared_examples/requests/api/members_shared_examples.rb b/spec/support/shared_examples/requests/api/members_shared_examples.rb index fce75c29971..9136f60eb93 100644 --- a/spec/support/shared_examples/requests/api/members_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/members_shared_examples.rb @@ -11,3 +11,11 @@ RSpec.shared_examples 'a 404 response when source is private' do expect(response).to have_gitlab_http_status(:not_found) end end + +RSpec.shared_examples 'a 403 response when user does not have rights to manage members of a specific access level' do + it 'returns 403' do + route + + expect(response).to have_gitlab_http_status(:forbidden) + end +end diff --git a/spec/support/shared_examples/requests/api/multiple_and_scoped_issue_boards_shared_examples.rb b/spec/support/shared_examples/requests/api/multiple_and_scoped_issue_boards_shared_examples.rb index fa111ca5811..d749479544d 100644 --- a/spec/support/shared_examples/requests/api/multiple_and_scoped_issue_boards_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/multiple_and_scoped_issue_boards_shared_examples.rb @@ -5,6 +5,7 @@ RSpec.shared_examples 'multiple and scoped issue boards' do |route_definition| context 'multiple issue boards' do before do + stub_feature_flags(apollo_boards: false) board_parent.add_reporter(user) stub_licensed_features(multiple_group_issue_boards: true) end diff --git a/spec/support/shared_examples/requests/api/notes_shared_examples.rb b/spec/support/shared_examples/requests/api/notes_shared_examples.rb index 8479493911b..11f9565989f 100644 --- a/spec/support/shared_examples/requests/api/notes_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/notes_shared_examples.rb @@ -179,7 +179,8 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name| end end - if parent_type == 'projects' + case parent_type + when 'projects' context 'by a project owner' do let(:user) { project.first_owner } @@ -211,7 +212,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name| expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time) end end - elsif parent_type == 'groups' + when 'groups' context 'by a group owner' do it 'sets the creation time on the new note' do post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params @@ -288,7 +289,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name| end it 'allows user in allow-list to create notes' do - stub_application_setting(notes_create_limit_allowlist: ["#{user.username}"]) + stub_application_setting(notes_create_limit_allowlist: [user.username.to_s]) subject expect(response).to have_gitlab_http_status(:created) diff --git a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb index 11e19d8d067..a9b44015206 100644 --- a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb @@ -221,6 +221,27 @@ RSpec.shared_examples 'rejects PyPI access with unknown group id' do end end +RSpec.shared_examples 'allow access for everyone with public package_registry_access_level' do + context 'with private project but public access to package registry' do + before do + project.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE) + project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC) + end + + context 'as non-member user' do + let(:headers) { basic_auth_header(user.username, personal_access_token.token) } + + it_behaves_like 'returning response status', :success + end + + context 'as anonymous' do + let(:headers) { {} } + + it_behaves_like 'returning response status', :success + end + end +end + RSpec.shared_examples 'pypi simple API endpoint' do using RSpec::Parameterized::TableSyntax diff --git a/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb index 544a0ed8fdd..bdff2c65691 100644 --- a/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb @@ -63,9 +63,9 @@ RSpec.shared_examples 'redirects to version download' do |user_type, status, add it 'returns a valid response' do subject - expect(request.url).to include 'module-1/system/download' + expect(request.url).to include "#{package.name}/download" expect(response.headers).to include 'Location' - expect(response.headers['Location']).to include 'module-1/system/1.0.1/download' + expect(response.headers['Location']).to include "#{package.name}/1.0.1/download" end end end |