diff options
Diffstat (limited to 'spec/support/shared_examples/requests')
9 files changed, 568 insertions, 109 deletions
diff --git a/spec/support/shared_examples/requests/api/boards_shared_examples.rb b/spec/support/shared_examples/requests/api/boards_shared_examples.rb index 0096aab55e3..8e8edd61ef9 100644 --- a/spec/support/shared_examples/requests/api/boards_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/boards_shared_examples.rb @@ -44,16 +44,35 @@ RSpec.shared_examples 'group and project boards' do |route_definition, ee = fals expect_schema_match_for(response, 'public_api/v4/boards', ee) end + end + end - describe "GET #{route_definition}/:board_id" do - let(:url) { "#{root_url}/#{board.id}" } + describe "GET #{route_definition}/:board_id" do + let(:url) { "#{root_url}/#{board.id}" } - it 'get a single board by id' do - get api(url, user) + it 'get a single board by id' do + get api(url, user) - expect_schema_match_for(response, 'public_api/v4/board', ee) - end - end + expect_schema_match_for(response, 'public_api/v4/board', ee) + end + end + + describe "PUT #{route_definition}/:board_id" do + let(:url) { "#{root_url}/#{board.id}" } + + it 'updates the board name' do + put api(url, user), params: { name: 'changed board name' } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['name']).to eq('changed board name') + end + + it 'updates the issue board booleans' do + put api(url, user), params: { hide_backlog_list: true, hide_closed_list: true } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['hide_backlog_list']).to eq(true) + expect(json_response['hide_closed_list']).to eq(true) end end diff --git a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb index f55043fe64f..83ba72c12aa 100644 --- a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb @@ -37,9 +37,9 @@ RSpec.shared_context 'Debian repository shared context' do |object_type| let(:params) { workhorse_params } let(:auth_headers) { {} } + let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') } let(:workhorse_headers) do if method == :put - workhorse_token = JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } else {} @@ -117,12 +117,13 @@ RSpec.shared_examples 'Debian project repository PUT request' do |user_role, add and_body = body.nil? ? '' : ' and expected body' if status == :created - it 'creates package files' do + it 'creates package files', :aggregate_failures do pending "Debian package creation not implemented" expect { subject } .to change { project.packages.debian.count }.by(1) expect(response).to have_gitlab_http_status(status) + expect(response.media_type).to eq('text/plain') unless body.nil? expect(response.body).to eq(body) @@ -130,7 +131,59 @@ RSpec.shared_examples 'Debian project repository PUT request' do |user_role, add end it_behaves_like 'a package tracking event', described_class.name, 'push_package' else - it "returns #{status}#{and_body}" do + it "returns #{status}#{and_body}", :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(status) + + unless body.nil? + expect(response.body).to eq(body) + end + end + end + end +end + +RSpec.shared_examples 'Debian project repository PUT authorize request' do |user_role, add_member, status, body, is_authorize| + context "for user type #{user_role}" do + before do + project.send("add_#{user_role}", user) if add_member && user_role != :anonymous + end + + and_body = body.nil? ? '' : ' and expected body' + + if status == :created + it 'authorizes package file upload', :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response.media_type).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) + expect(json_response['TempPath']).to eq(Packages::PackageFileUploader.workhorse_local_upload_path) + expect(json_response['RemoteObject']).to be_nil + expect(json_response['MaximumSize']).to be_nil + end + + context 'without a valid token' do + let(:workhorse_token) { 'invalid' } + + it 'rejects request' do + subject + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'bypassing gitlab-workhorse' do + let(:workhorse_headers) { {} } + + it 'rejects request' do + subject + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + else + it "returns #{status}#{and_body}", :aggregate_failures do subject expect(response).to have_gitlab_http_status(status) @@ -194,7 +247,7 @@ RSpec.shared_examples 'Debian project repository GET endpoint' do |success_statu it_behaves_like 'rejects Debian access with unknown project id' end -RSpec.shared_examples 'Debian project repository PUT endpoint' do |success_status, success_body| +RSpec.shared_examples 'Debian project repository PUT endpoint' do |success_status, success_body, is_authorize = false| context 'with valid project' do using RSpec::Parameterized::TableSyntax @@ -221,7 +274,13 @@ RSpec.shared_examples 'Debian project repository PUT endpoint' do |success_statu with_them do include_context 'Debian repository project access', params[:project_visibility_level], params[:user_role], params[:user_token], :basic do - it_behaves_like 'Debian project repository PUT request', params[:user_role], params[:member], params[:expected_status], params[:expected_body] + desired_behavior = if is_authorize + 'Debian project repository PUT authorize request' + else + 'Debian project repository PUT request' + end + + it_behaves_like desired_behavior, params[:user_role], params[:member], params[:expected_status], params[:expected_body] end end end diff --git a/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb index f808d12baf4..7b7d2a33e8c 100644 --- a/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb @@ -1,31 +1,31 @@ # frozen_string_literal: true -RSpec.shared_examples 'handling nuget service requests' do +RSpec.shared_examples 'handling nuget service requests' do |anonymous_requests_example_name: 'process nuget service index request', anonymous_requests_status: :success| subject { get api(url) } - context 'with valid project' do + context 'with valid target' do using RSpec::Parameterized::TableSyntax context 'personal token' do - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do - 'PUBLIC' | :developer | true | true | 'process nuget service index request' | :success - 'PUBLIC' | :guest | true | true | 'process nuget service index request' | :success - 'PUBLIC' | :developer | true | false | 'process nuget service index request' | :success - 'PUBLIC' | :guest | true | false | 'process nuget service index request' | :success - 'PUBLIC' | :developer | false | true | 'process nuget service index request' | :success - 'PUBLIC' | :guest | false | true | 'process nuget service index request' | :success - 'PUBLIC' | :developer | false | false | 'process nuget service index request' | :success - 'PUBLIC' | :guest | false | false | 'process nuget service index request' | :success - 'PUBLIC' | :anonymous | false | true | 'process nuget service index request' | :success - 'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success - 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden - 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found - 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found - 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized + where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do + 'PUBLIC' | :developer | true | true | 'process nuget service index request' | :success + 'PUBLIC' | :guest | true | true | 'process nuget service index request' | :success + 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :developer | false | true | 'process nuget service index request' | :success + 'PUBLIC' | :guest | false | true | 'process nuget service index request' | :success + 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :anonymous | false | true | anonymous_requests_example_name | anonymous_requests_status + 'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success + 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden + 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized + 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized + 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found + 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found + 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized + 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized + 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized end with_them do @@ -35,7 +35,7 @@ RSpec.shared_examples 'handling nuget service requests' do subject { get api(url), headers: headers } before do - project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) + update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false)) end it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] @@ -43,7 +43,7 @@ RSpec.shared_examples 'handling nuget service requests' do end context 'with job token' do - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do + where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do 'PUBLIC' | :developer | true | true | 'process nuget service index request' | :success 'PUBLIC' | :guest | true | true | 'process nuget service index request' | :success 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized @@ -52,7 +52,7 @@ RSpec.shared_examples 'handling nuget service requests' do 'PUBLIC' | :guest | false | true | 'process nuget service index request' | :success 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized - 'PUBLIC' | :anonymous | false | true | 'process nuget service index request' | :success + 'PUBLIC' | :anonymous | false | true | anonymous_requests_example_name | anonymous_requests_status 'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized @@ -71,7 +71,7 @@ RSpec.shared_examples 'handling nuget service requests' do subject { get api(url), headers: headers } before do - project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) + update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false)) end it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] @@ -79,14 +79,18 @@ RSpec.shared_examples 'handling nuget service requests' do end end - it_behaves_like 'deploy token for package GET requests' + it_behaves_like 'deploy token for package GET requests' do + before do + update_visibility_to(Gitlab::VisibilityLevel::PRIVATE) + end + end - it_behaves_like 'rejects nuget access with unknown project id' + it_behaves_like 'rejects nuget access with unknown target id' - it_behaves_like 'rejects nuget access with invalid project id' + it_behaves_like 'rejects nuget access with invalid target id' end -RSpec.shared_examples 'handling nuget metadata requests with package name' do +RSpec.shared_examples 'handling nuget metadata requests with package name' do |anonymous_requests_example_name: 'process nuget metadata request at package name level', anonymous_requests_status: :success| include_context 'with expected presenters dependency groups' let_it_be(:package_name) { 'Dummy.Package' } @@ -99,19 +103,19 @@ RSpec.shared_examples 'handling nuget metadata requests with package name' do packages.each { |pkg| create_dependencies_for(pkg) } end - context 'with valid project' do + context 'with valid target' do using RSpec::Parameterized::TableSyntax - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do + where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do 'PUBLIC' | :developer | true | true | 'process nuget metadata request at package name level' | :success 'PUBLIC' | :guest | true | true | 'process nuget metadata request at package name level' | :success - 'PUBLIC' | :developer | true | false | 'process nuget metadata request at package name level' | :success - 'PUBLIC' | :guest | true | false | 'process nuget metadata request at package name level' | :success + 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized 'PUBLIC' | :developer | false | true | 'process nuget metadata request at package name level' | :success 'PUBLIC' | :guest | false | true | 'process nuget metadata request at package name level' | :success - 'PUBLIC' | :developer | false | false | 'process nuget metadata request at package name level' | :success - 'PUBLIC' | :guest | false | false | 'process nuget metadata request at package name level' | :success - 'PUBLIC' | :anonymous | false | true | 'process nuget metadata request at package name level' | :success + 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :anonymous | false | true | anonymous_requests_example_name | anonymous_requests_status 'PRIVATE' | :developer | true | true | 'process nuget metadata request at package name level' | :success 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized @@ -130,21 +134,25 @@ RSpec.shared_examples 'handling nuget metadata requests with package name' do subject { get api(url), headers: headers } before do - project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) + update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false)) end it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] end - it_behaves_like 'deploy token for package GET requests' + it_behaves_like 'deploy token for package GET requests' do + before do + update_visibility_to(Gitlab::VisibilityLevel::PRIVATE) + end + end - it_behaves_like 'rejects nuget access with unknown project id' + it_behaves_like 'rejects nuget access with unknown target id' - it_behaves_like 'rejects nuget access with invalid project id' + it_behaves_like 'rejects nuget access with invalid target id' end end -RSpec.shared_examples 'handling nuget metadata requests with package name and package version' do +RSpec.shared_examples 'handling nuget metadata requests with package name and package version' do |anonymous_requests_example_name: 'process nuget metadata request at package name and package version level', anonymous_requests_status: :success| include_context 'with expected presenters dependency groups' let_it_be(:package_name) { 'Dummy.Package' } @@ -157,19 +165,19 @@ RSpec.shared_examples 'handling nuget metadata requests with package name and pa create_dependencies_for(package) end - context 'with valid project' do + context 'with valid target' do using RSpec::Parameterized::TableSyntax - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do + where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do 'PUBLIC' | :developer | true | true | 'process nuget metadata request at package name and package version level' | :success 'PUBLIC' | :guest | true | true | 'process nuget metadata request at package name and package version level' | :success - 'PUBLIC' | :developer | true | false | 'process nuget metadata request at package name and package version level' | :success - 'PUBLIC' | :guest | true | false | 'process nuget metadata request at package name and package version level' | :success + 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized 'PUBLIC' | :developer | false | true | 'process nuget metadata request at package name and package version level' | :success 'PUBLIC' | :guest | false | true | 'process nuget metadata request at package name and package version level' | :success - 'PUBLIC' | :developer | false | false | 'process nuget metadata request at package name and package version level' | :success - 'PUBLIC' | :guest | false | false | 'process nuget metadata request at package name and package version level' | :success - 'PUBLIC' | :anonymous | false | true | 'process nuget metadata request at package name and package version level' | :success + 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :anonymous | false | true | anonymous_requests_example_name | anonymous_requests_status 'PRIVATE' | :developer | true | true | 'process nuget metadata request at package name and package version level' | :success 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized @@ -188,23 +196,25 @@ RSpec.shared_examples 'handling nuget metadata requests with package name and pa subject { get api(url), headers: headers } before do - project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) + update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false)) end it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] end end - it_behaves_like 'deploy token for package GET requests' + it_behaves_like 'deploy token for package GET requests' do + before do + update_visibility_to(Gitlab::VisibilityLevel::PRIVATE) + end + end - context 'with invalid package name' do - let_it_be(:package_name) { 'Unkown' } + it_behaves_like 'rejects nuget access with unknown target id' - it_behaves_like 'rejects nuget packages access', :developer, :not_found - end + it_behaves_like 'rejects nuget access with invalid target id' end -RSpec.shared_examples 'handling nuget search requests' do +RSpec.shared_examples 'handling nuget search requests' do |anonymous_requests_example_name: 'process nuget search request', anonymous_requests_status: :success| let_it_be(:package_a) { create(:nuget_package, :with_metadatum, name: 'Dummy.PackageA', project: project) } let_it_be(:tag) { create(:packages_tag, package: package_a, name: 'test') } let_it_be(:packages_b) { create_list(:nuget_package, 5, name: 'Dummy.PackageB', project: project) } @@ -219,19 +229,19 @@ RSpec.shared_examples 'handling nuget search requests' do subject { get api(url) } - context 'with valid project' do + context 'with valid target' do using RSpec::Parameterized::TableSyntax - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do + where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do 'PUBLIC' | :developer | true | true | 'process nuget search request' | :success 'PUBLIC' | :guest | true | true | 'process nuget search request' | :success - 'PUBLIC' | :developer | true | false | 'process nuget search request' | :success - 'PUBLIC' | :guest | true | false | 'process nuget search request' | :success + 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized 'PUBLIC' | :developer | false | true | 'process nuget search request' | :success 'PUBLIC' | :guest | false | true | 'process nuget search request' | :success - 'PUBLIC' | :developer | false | false | 'process nuget search request' | :success - 'PUBLIC' | :guest | false | false | 'process nuget search request' | :success - 'PUBLIC' | :anonymous | false | true | 'process nuget search request' | :success + 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized + 'PUBLIC' | :anonymous | false | true | anonymous_requests_example_name | anonymous_requests_status 'PRIVATE' | :developer | true | true | 'process nuget search request' | :success 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized @@ -250,16 +260,20 @@ RSpec.shared_examples 'handling nuget search requests' do subject { get api(url), headers: headers } before do - project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) + update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false)) end it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] end end - it_behaves_like 'deploy token for package GET requests' + it_behaves_like 'deploy token for package GET requests' do + before do + update_visibility_to(Gitlab::VisibilityLevel::PRIVATE) + end + end - it_behaves_like 'rejects nuget access with unknown project id' + it_behaves_like 'rejects nuget access with unknown target id' - it_behaves_like 'rejects nuget access with invalid project id' + it_behaves_like 'rejects nuget access with invalid target id' end diff --git a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb index dc6ac5f0371..8b60857cdaf 100644 --- a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb @@ -3,7 +3,7 @@ RSpec.shared_examples 'rejects nuget packages access' do |user_type, status, add_member = true| context "for user type #{user_type}" do before do - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous + target.send("add_#{user_type}", user) if add_member && user_type != :anonymous end it_behaves_like 'returning response status', status @@ -21,7 +21,7 @@ end RSpec.shared_examples 'process nuget service index request' do |user_type, status, add_member = true| context "for user type #{user_type}" do before do - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous + target.send("add_#{user_type}", user) if add_member && user_type != :anonymous end it_behaves_like 'returning response status', status @@ -37,7 +37,7 @@ RSpec.shared_examples 'process nuget service index request' do |user_type, statu end context 'with invalid format' do - let(:url) { "/projects/#{project.id}/packages/nuget/index.xls" } + let(:url) { "/#{target_type}/#{target.id}/packages/nuget/index.xls" } it_behaves_like 'rejects nuget packages access', :anonymous, :not_found end @@ -57,7 +57,7 @@ end RSpec.shared_examples 'process nuget metadata request at package name level' do |user_type, status, add_member = true| context "for user type #{user_type}" do before do - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous + target.send("add_#{user_type}", user) if add_member && user_type != :anonymous end it_behaves_like 'returning response status', status @@ -65,7 +65,7 @@ RSpec.shared_examples 'process nuget metadata request at package name level' do it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/packages_metadata' context 'with invalid format' do - let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.xls" } + let(:url) { "/#{target_type}/#{target.id}/packages/nuget/metadata/#{package_name}/index.xls" } it_behaves_like 'rejects nuget packages access', :anonymous, :not_found end @@ -83,7 +83,7 @@ end RSpec.shared_examples 'process nuget metadata request at package name and package version level' do |user_type, status, add_member = true| context "for user type #{user_type}" do before do - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous + target.send("add_#{user_type}", user) if add_member && user_type != :anonymous end it_behaves_like 'returning response status', status @@ -91,7 +91,7 @@ RSpec.shared_examples 'process nuget metadata request at package name and packag it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/package_metadata' context 'with invalid format' do - let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/#{package.version}.xls" } + let(:url) { "/#{target_type}/#{target.id}/packages/nuget/metadata/#{package_name}/#{package.version}.xls" } it_behaves_like 'rejects nuget packages access', :anonymous, :not_found end @@ -109,7 +109,7 @@ end RSpec.shared_examples 'process nuget workhorse authorization' do |user_type, status, add_member = true| context "for user type #{user_type}" do before do - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous + target.send("add_#{user_type}", user) if add_member && user_type != :anonymous end it_behaves_like 'returning response status', status @@ -128,7 +128,7 @@ RSpec.shared_examples 'process nuget workhorse authorization' do |user_type, sta end before do - project.add_maintainer(user) + target.add_maintainer(user) end it_behaves_like 'returning response status', :forbidden @@ -141,18 +141,18 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member = it 'creates package files' do expect(::Packages::Nuget::ExtractionWorker).to receive(:perform_async).once expect { subject } - .to change { project.packages.count }.by(1) + .to change { target.packages.count }.by(1) .and change { Packages::PackageFile.count }.by(1) expect(response).to have_gitlab_http_status(status) - package_file = project.packages.last.package_files.reload.last + package_file = target.packages.last.package_files.reload.last expect(package_file.file_name).to eq('package.nupkg') end end context "for user type #{user_type}" do before do - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous + target.send("add_#{user_type}", user) if add_member && user_type != :anonymous end context 'with object storage disabled' do @@ -206,7 +206,7 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member = context 'with crafted package.path param' do let(:crafted_file) { Tempfile.new('nuget.crafted.package.path') } - let(:url) { "/projects/#{project.id}/packages/nuget?package.path=#{crafted_file.path}" } + let(:url) { "/#{target_type}/#{target.id}/packages/nuget?package.path=#{crafted_file.path}" } let(:params) { { file: temp_file(file_name) } } let(:file_key) { :file } @@ -255,7 +255,7 @@ RSpec.shared_examples 'process nuget download versions request' do |user_type, s context "for user type #{user_type}" do before do - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous + target.send("add_#{user_type}", user) if add_member && user_type != :anonymous end it_behaves_like 'returning response status', status @@ -263,7 +263,7 @@ RSpec.shared_examples 'process nuget download versions request' do |user_type, s it_behaves_like 'returns a valid nuget download versions json response' context 'with invalid format' do - let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package_name}/index.xls" } + let(:url) { "/#{target_type}/#{target.id}/packages/nuget/download/#{package_name}/index.xls" } it_behaves_like 'rejects nuget packages access', :anonymous, :not_found end @@ -281,7 +281,7 @@ end RSpec.shared_examples 'process nuget download content request' do |user_type, status, add_member = true| context "for user type #{user_type}" do before do - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous + target.send("add_#{user_type}", user) if add_member && user_type != :anonymous end it_behaves_like 'returning response status', status @@ -295,7 +295,7 @@ RSpec.shared_examples 'process nuget download content request' do |user_type, st end context 'with invalid format' do - let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.xls" } + let(:url) { "/#{target_type}/#{target.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.xls" } it_behaves_like 'rejects nuget packages access', :anonymous, :not_found end @@ -331,7 +331,7 @@ RSpec.shared_examples 'process nuget search request' do |user_type, status, add_ context "for user type #{user_type}" do before do - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous + target.send("add_#{user_type}", user) if add_member && user_type != :anonymous end it_behaves_like 'returns a valid json search response', status, 4, [1, 5, 5, 1] @@ -370,20 +370,20 @@ RSpec.shared_examples 'process nuget search request' do |user_type, status, add_ end end -RSpec.shared_examples 'rejects nuget access with invalid project id' do - context 'with a project id with invalid integers' do +RSpec.shared_examples 'rejects nuget access with invalid target id' do + context 'with a target id with invalid integers' do using RSpec::Parameterized::TableSyntax - let(:project) { OpenStruct.new(id: id) } + let(:target) { OpenStruct.new(id: id) } where(:id, :status) do - '/../' | :unauthorized + '/../' | :bad_request '' | :not_found - '%20' | :unauthorized - '%2e%2e%2f' | :unauthorized - 'NaN' | :unauthorized + '%20' | :bad_request + '%2e%2e%2f' | :bad_request + 'NaN' | :bad_request 00002345 | :unauthorized - 'anything25' | :unauthorized + 'anything25' | :bad_request end with_them do @@ -392,9 +392,9 @@ RSpec.shared_examples 'rejects nuget access with invalid project id' do end end -RSpec.shared_examples 'rejects nuget access with unknown project id' do - context 'with an unknown project' do - let(:project) { OpenStruct.new(id: 1234567890) } +RSpec.shared_examples 'rejects nuget access with unknown target id' do + context 'with an unknown target' do + let(:target) { OpenStruct.new(id: 1234567890) } context 'as anonymous' do it_behaves_like 'rejects nuget packages access', :anonymous, :unauthorized diff --git a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb new file mode 100644 index 00000000000..b2970fd265d --- /dev/null +++ b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb @@ -0,0 +1,219 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'repository_storage_moves API' do |container_type| + include AccessMatchersForRequest + + let_it_be(:user) { create(:admin) } + + shared_examples 'get single container repository storage move' do + let(:repository_storage_move_id) { storage_move.id } + + def get_container_repository_storage_move + get api(url, user) + end + + it 'returns a container repository storage move', :aggregate_failures do + get_container_repository_storage_move + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema("public_api/v4/#{container_type.singularize}_repository_storage_move") + expect(json_response['id']).to eq(storage_move.id) + expect(json_response['state']).to eq(storage_move.human_state_name) + end + + context 'non-existent container repository storage move' do + let(:repository_storage_move_id) { non_existing_record_id } + + it 'returns not found' do + get_container_repository_storage_move + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + describe 'permissions' do + it { expect { get_container_repository_storage_move }.to be_allowed_for(:admin) } + it { expect { get_container_repository_storage_move }.to be_denied_for(:user) } + end + end + + shared_examples 'get container repository storage move list' do + def get_container_repository_storage_moves + get api(url, user) + end + + it 'returns container repository storage moves', :aggregate_failures do + get_container_repository_storage_moves + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(response).to match_response_schema("public_api/v4/#{container_type.singularize}_repository_storage_moves") + expect(json_response.size).to eq(1) + expect(json_response.first['id']).to eq(storage_move.id) + expect(json_response.first['state']).to eq(storage_move.human_state_name) + end + + it 'avoids N+1 queries', :request_store do + # prevent `let` from polluting the control + get_container_repository_storage_moves + + control = ActiveRecord::QueryRecorder.new { get_container_repository_storage_moves } + + create(repository_storage_move_factory, :scheduled, container: container) + + expect { get_container_repository_storage_moves }.not_to exceed_query_limit(control) + end + + it 'returns the most recently created first' do + storage_move_oldest = create(repository_storage_move_factory, :scheduled, container: container, created_at: 2.days.ago) + storage_move_middle = create(repository_storage_move_factory, :scheduled, container: container, created_at: 1.day.ago) + + get_container_repository_storage_moves + + json_ids = json_response.map {|storage_move| storage_move['id'] } + expect(json_ids).to eq([ + storage_move.id, + storage_move_middle.id, + storage_move_oldest.id + ]) + end + + describe 'permissions' do + it { expect { get_container_repository_storage_moves }.to be_allowed_for(:admin) } + it { expect { get_container_repository_storage_moves }.to be_denied_for(:user) } + end + end + + describe "GET /#{container_type}/:id/repository_storage_moves" do + it_behaves_like 'get container repository storage move list' do + let(:url) { "/#{container_type}/#{container.id}/repository_storage_moves" } + end + end + + describe "GET /#{container_type}/:id/repository_storage_moves/:repository_storage_move_id" do + it_behaves_like 'get single container repository storage move' do + let(:url) { "/#{container_type}/#{container.id}/repository_storage_moves/#{repository_storage_move_id}" } + end + end + + describe "GET /#{container_type.singularize}_repository_storage_moves" do + it_behaves_like 'get container repository storage move list' do + let(:url) { "/#{container_type.singularize}_repository_storage_moves" } + end + end + + describe "GET /#{container_type.singularize}_repository_storage_moves/:repository_storage_move_id" do + it_behaves_like 'get single container repository storage move' do + let(:url) { "/#{container_type.singularize}_repository_storage_moves/#{repository_storage_move_id}" } + end + end + + describe "POST /#{container_type}/:id/repository_storage_moves" do + let(:url) { "/#{container_type}/#{container.id}/repository_storage_moves" } + let(:destination_storage_name) { 'test_second_storage' } + + def create_container_repository_storage_move + post api(url, user), params: { destination_storage_name: destination_storage_name } + end + + before do + stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' }) + end + + it 'schedules a container repository storage move', :aggregate_failures do + create_container_repository_storage_move + + storage_move = container.repository_storage_moves.last + + expect(response).to have_gitlab_http_status(:created) + expect(response).to match_response_schema("public_api/v4/#{container_type.singularize}_repository_storage_move") + expect(json_response['id']).to eq(storage_move.id) + expect(json_response['state']).to eq('scheduled') + expect(json_response['source_storage_name']).to eq('default') + expect(json_response['destination_storage_name']).to eq(destination_storage_name) + end + + describe 'permissions' do + it { expect { create_container_repository_storage_move }.to be_allowed_for(:admin) } + it { expect { create_container_repository_storage_move }.to be_denied_for(:user) } + end + + context 'destination_storage_name is missing', :aggregate_failures do + let(:destination_storage_name) { nil } + + it 'schedules a container repository storage move' do + create_container_repository_storage_move + + storage_move = container.repository_storage_moves.last + + expect(response).to have_gitlab_http_status(:created) + expect(response).to match_response_schema("public_api/v4/#{container_type.singularize}_repository_storage_move") + expect(json_response['id']).to eq(storage_move.id) + expect(json_response['state']).to eq('scheduled') + expect(json_response['source_storage_name']).to eq('default') + expect(json_response['destination_storage_name']).to be_present + end + end + end + + describe "POST /#{container_type.singularize}_repository_storage_moves" do + let(:url) { "/#{container_type.singularize}_repository_storage_moves" } + let(:source_storage_name) { 'default' } + let(:destination_storage_name) { 'test_second_storage' } + + def create_container_repository_storage_moves + post api(url, user), params: { + source_storage_name: source_storage_name, + destination_storage_name: destination_storage_name + } + end + + before do + stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' }) + end + + it 'schedules the worker' do + expect(bulk_worker_klass).to receive(:perform_async).with(source_storage_name, destination_storage_name) + + create_container_repository_storage_moves + + expect(response).to have_gitlab_http_status(:accepted) + end + + context 'source_storage_name is invalid' do + let(:destination_storage_name) { 'not-a-real-storage' } + + it 'gives an error' do + create_container_repository_storage_moves + + expect(response).to have_gitlab_http_status(:bad_request) + end + end + + context 'destination_storage_name is missing' do + let(:destination_storage_name) { nil } + + it 'schedules the worker' do + expect(bulk_worker_klass).to receive(:perform_async).with(source_storage_name, destination_storage_name) + + create_container_repository_storage_moves + + expect(response).to have_gitlab_http_status(:accepted) + end + end + + context 'destination_storage_name is invalid' do + let(:destination_storage_name) { 'not-a-real-storage' } + + it 'gives an error' do + create_container_repository_storage_moves + + expect(response).to have_gitlab_http_status(:bad_request) + end + end + + describe 'normal user' do + it { expect { create_container_repository_storage_moves }.to be_denied_for(:user) } + end + end +end diff --git a/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb b/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb index 5748e873fd4..460e8d57a2b 100644 --- a/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb @@ -9,6 +9,7 @@ RSpec.shared_examples 'resolvable discussions API' do |parent_type, noteable_typ expect(response).to have_gitlab_http_status(:ok) expect(json_response['notes'].size).to eq(1) expect(json_response['notes'][0]['resolved']).to eq(true) + expect(Time.parse(json_response['notes'][0]['resolved_at'])).to be_like_time(note.reload.resolved_at) end it "unresolves discussion if resolved is false" do @@ -18,6 +19,7 @@ RSpec.shared_examples 'resolvable discussions API' do |parent_type, noteable_typ expect(response).to have_gitlab_http_status(:ok) expect(json_response['notes'].size).to eq(1) expect(json_response['notes'][0]['resolved']).to eq(false) + expect(json_response['notes'][0]['resolved_at']).to be_nil end it "returns a 400 bad request error if resolved parameter is not passed" do diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb index 5d300d38e4a..3b039049ca9 100644 --- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb +++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb @@ -154,10 +154,11 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do end def make_request(args) + path, options = args if request_method == 'POST' - post(*args) + post(path, **options) else - get(*args) + get(path, **options) end end end diff --git a/spec/support/shared_examples/requests/releases_shared_examples.rb b/spec/support/shared_examples/requests/releases_shared_examples.rb new file mode 100644 index 00000000000..b835947e497 --- /dev/null +++ b/spec/support/shared_examples/requests/releases_shared_examples.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'correct release milestone order' do + let_it_be_with_reload(:milestone_1) { create(:milestone, project: project) } + let_it_be_with_reload(:milestone_2) { create(:milestone, project: project) } + + shared_examples 'correct sort order' do + it 'sorts milestonee_1 before milestone_2' do + freeze_time do + expect(actual_milestone_title_order).to eq([milestone_1.title, milestone_2.title]) + end + end + end + + context 'due_date' do + before do + milestone_1.update!(due_date: Time.zone.now, start_date: 1.day.ago, title: 'z') + milestone_2.update!(due_date: 1.day.from_now, start_date: 2.days.ago, title: 'a') + end + + context 'when both milestones have a due_date' do + it_behaves_like 'correct sort order' + end + + context 'when one milestone does not have a due_date' do + before do + milestone_2.update!(due_date: nil) + end + + it_behaves_like 'correct sort order' + end + end + + context 'start_date' do + before do + milestone_1.update!(due_date: 1.day.from_now, start_date: 1.day.ago, title: 'z' ) + milestone_2.update!(due_date: 1.day.from_now, start_date: milestone_2_start_date, title: 'a' ) + end + + context 'when both milestones have a start_date' do + let(:milestone_2_start_date) { Time.zone.now } + + it_behaves_like 'correct sort order' + end + + context 'when one milestone does not have a start_date' do + let(:milestone_2_start_date) { nil } + + it_behaves_like 'correct sort order' + end + end + + context 'title' do + before do + milestone_1.update!(due_date: 1.day.from_now, start_date: Time.zone.now, title: 'a' ) + milestone_2.update!(due_date: 1.day.from_now, start_date: Time.zone.now, title: 'z' ) + end + + it_behaves_like 'correct sort order' + end +end diff --git a/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb b/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb new file mode 100644 index 00000000000..d82da1b01e1 --- /dev/null +++ b/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'authenticates sessionless user for the request spec' do |params| + params ||= {} + + before do + stub_authentication_activity_metrics(debug: false) + end + + let(:user) { create(:user) } + let(:personal_access_token) { create(:personal_access_token, user: user) } + let(:default_params) { params.except(:public) || {} } + + context "when the 'personal_access_token' param is populated with the personal access token" do + it 'logs the user in' do + expect(authentication_metrics) + .to increment(:user_authenticated_counter) + .and increment(:user_session_override_counter) + .and increment(:user_sessionless_authentication_counter) + + get url, params: default_params.merge(private_token: personal_access_token.token) + + expect(response).to have_gitlab_http_status(:ok) + expect(controller.current_user).to eq(user) + end + + it 'does not log the user in if page is public', if: params[:public] do + get url, params: default_params + + expect(response).to have_gitlab_http_status(:ok) + expect(controller.current_user).to be_nil + end + end + + context 'when the personal access token has no api scope', unless: params[:public] do + it 'does not log the user in' do + # Several instances of where these specs are shared route the request + # through ApplicationController#route_not_found which does not involve + # the usual auth code from Devise, so does not increment the + # :user_unauthenticated_counter + # + unless params[:ignore_incrementing] + expect(authentication_metrics) + .to increment(:user_unauthenticated_counter) + end + + personal_access_token.update!(scopes: [:read_user]) + + get url, params: default_params.merge(private_token: personal_access_token.token) + + expect(response).not_to have_gitlab_http_status(:ok) + end + end + + context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do + it 'logs the user in' do + expect(authentication_metrics) + .to increment(:user_authenticated_counter) + .and increment(:user_session_override_counter) + .and increment(:user_sessionless_authentication_counter) + + headers = { 'PRIVATE-TOKEN': personal_access_token.token } + get url, params: default_params, headers: headers + + expect(response).to have_gitlab_http_status(:ok) + end + end + + it "doesn't log the user in otherwise", unless: params[:public] do + # Several instances of where these specs are shared route the request + # through ApplicationController#route_not_found which does not involve + # the usual auth code from Devise, so does not increment the + # :user_unauthenticated_counter + # + unless params[:ignore_incrementing] + expect(authentication_metrics) + .to increment(:user_unauthenticated_counter) + end + + get url, params: default_params.merge(private_token: 'token') + + expect(response).not_to have_gitlab_http_status(:ok) + end +end |